From 5e6ffcea31b7d7ddef8ef1f3f81ff5ecd06dc266 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 4 Dec 2021 04:21:23 +0900 Subject: [PATCH 001/369] KeyOf Type Reference Support (#133) --- changelog.md | 12 +- example/index.ts | 11 +- package.json | 2 +- readme.md | 461 ++++++++++++++++++---------------------- spec/schema/keyof.ts | 13 +- spec/types/typebox.d.ts | 36 ++-- src/typebox.ts | 59 ++--- typebox.png | Bin 0 -> 342043 bytes 8 files changed, 284 insertions(+), 310 deletions(-) create mode 100644 typebox.png diff --git a/changelog.md b/changelog.md index 1c65a1ae7..fcd52a1ef 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,16 @@ +## [0.23.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.1) + +Updates: + +- The `Type.KeyOf(...)` type can now accept references of `Type.Ref(TObject)` + ## [0.23.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.0) Updates: -- The types `Type.Namespace()` and `Type.Ref()` are promoted to `Standard`. -- TypeBox adds a new type named `TRef` that is returned on calls to `Type.Ref(...)`. The `TRef` includes a `RefKind` symbol for introspection of the reference type. -- TypeBox now maintains an internal dictionary of all schemas passed that contain an `$id` property. This dictionary is checked whenever a user attempts to reference a type and will throw if attempting to reference a target schema with no $id. +- The types `Type.Namespace(...)` and `Type.Ref(...)` are promoted to `Standard`. +- TypeBox now includes an additional type named `TRef<...>` that is returned on calls to `Type.Ref(...)`. The `TRef<...>` includes a new `RefKind` symbol for introspection of the reference type. +- TypeBox now maintains an internal dictionary of all schemas passed that contain an `$id` property. This dictionary is checked whenever a user attempts to reference a type and will throw if attempting to reference a target schema with no `$id`. - The types `Type.Partial(...)`, `Type.Required(...)`, `Type.Omit()` and `Type.Pick(...)` now support reference types. Note that when using these functions with references, TypeBox will replicate the source schema and apply the nessasary modifiers to the replication. ## [0.22.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.22.0) diff --git a/example/index.ts b/example/index.ts index 922c43620..7f3a03024 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,15 +1,16 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Object({ - name: Type.Optional(Type.String()), - order: Type.Number() + x: Type.String(), + y: Type.Number(), + z: Type.String() }, { $id: 'T' }) const R = Type.Ref(T) -const P = Type.Omit(T, ['name']) +const K = Type.KeyOf(R) -console.log(P) +type T = Static -type T = Static +console.log(K) diff --git a/package.json b/package.json index 81f0bf75e..1b774bf59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.23.0", + "version": "0.23.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "json-schema", diff --git a/readme.md b/readme.md index 92b344759..241d936c7 100644 --- a/readme.md +++ b/readme.md @@ -2,8 +2,12 @@

TypeBox

+ +

JSON Schema Type Builder with Static Type Resolution for TypeScript

+ + [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) @@ -24,7 +28,7 @@ $ npm install @sinclair/typebox --save import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts' ``` -## Usage +## Example ```typescript import { Static, Type } from '@sinclair/typebox' @@ -38,7 +42,7 @@ type T = Static // type T = string ## Overview -TypeBox is a library that builds in-memory JSON Schema objects that can be statically resolved to 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 create a unified type that can be statically checked by the TypeScript compiler and runtime asserted using standard JSON Schema validation. +TypeBox is a 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 create a unified type that can be both statically asserted by the TypeScript compiler and runtime asserted using industry standard JSON Schema validation. 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. TypeBox does not provide any JSON schema validation. Please use libraries such as AJV to validate schemas built with this library. @@ -49,7 +53,7 @@ License MIT ## Contents - [Install](#Install) - [Overview](#Overview) -- [Example](#Example) +- [Usage](#Usage) - [Types](#Types) - [Modifiers](#Modifiers) - [Options](#Options) @@ -63,7 +67,7 @@ License MIT -## Example +## Usage The following demonstrates TypeBox's general usage. @@ -89,25 +93,25 @@ type T = { // //-------------------------------------------------------------------------------------------- -const T = Type.Object({ // const T = { - id: Type.String(), // type: 'object', - name: Type.String(), // properties: { - timestamp: Type.Integer() // id: { -}) // type: 'string' - // }, - // name: { - // type: 'string' - // }, - // timestamp: { - // type: 'integer' - // } - // }, - // required: [ - // "id", - // "name", - // "timestamp" - // ] - // } +const T = Type.Object({ // const T = { + id: Type.String(), // type: 'object', + name: Type.String(), // properties: { + timestamp: Type.Integer() // id: { +}) // type: 'string' + // }, + // name: { + // type: 'string' + // }, + // timestamp: { + // type: 'integer' + // } + // }, + // required: [ + // "id", + // "name", + // "timestamp" + // ] + // } //-------------------------------------------------------------------------------------------- // @@ -115,11 +119,11 @@ const T = Type.Object({ // const T = { // //-------------------------------------------------------------------------------------------- -type T = Static // type T = { - // id: string, - // name: string, - // timestamp: number - // } +type T = Static // type T = { + // id: string, + // name: string, + // timestamp: number + // } //-------------------------------------------------------------------------------------------- // @@ -127,9 +131,9 @@ type T = Static // type T = { // //-------------------------------------------------------------------------------------------- -function receive(value: T) { // ... as a Type +function receive(value: T) { // ... as a Type - if(JSON.validate(T, value)) { // ... as a Schema + if(JSON.validate(T, value)) { // ... as a Schema // ok... } @@ -145,50 +149,50 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ TypeBox │ TypeScript │ JSON Schema │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Any() │ type T = any │ const T = { } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Unknown() │ type T = unknown │ const T = { } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.String() │ type T = string │ const T = { │ │ │ │ type: 'string' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Number() │ type T = number │ const T = { │ │ │ │ type: 'number' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Integer() │ type T = number │ const T = { │ │ │ │ type: 'integer' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Boolean() │ type T = boolean │ const T = { │ │ │ │ type: 'boolean' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Null() │ type T = null │ const T = { │ │ │ │ type: 'null' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │ +│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │ │ │ │ type: 'string', │ │ │ │ pattern: 'foo' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Literal(42) │ type T = 42 │ const T = { │ │ │ │ const: 42 │ │ │ │ type: 'number' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Array( │ type T = number[] │ const T = { │ │ Type.Number() │ │ type: 'array', │ @@ -196,36 +200,36 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ type: 'number' │ │ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ │ x: Type.Number(), │ x: number, │ type: 'object', │ │ y: Type.Number() │ y: number │ properties: { │ │ }) │ } │ x: { │ │ │ │ type: 'number' │ -│ │ │ }, │ -│ │ │ y: { │ -│ │ │ type: 'number' │ +│ │ │ }, │ +│ │ │ y: { │ +│ │ │ type: 'number' │ │ │ │ } │ -│ │ │ }, │ -│ │ │ required: ['x', 'y'] │ +│ │ │ }, │ +│ │ │ required: ['x', 'y'] │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │ │ Type.Number(), │ │ type: 'array', │ │ Type.Number() │ │ items: [ │ │ ]) │ │ { │ -│ │ │ type: 'number' │ -│ │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ ], │ -│ │ │ additionalItems: false, │ -│ │ │ minItems: 2, │ -│ │ │ maxItems: 2, │ -│ │ │ } │ -│ │ │ │ +│ │ │ type: 'number' │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ ], │ +│ │ │ additionalItems: false, │ +│ │ │ minItems: 2, │ +│ │ │ maxItems: 2, │ +│ │ │ } │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ enum Foo { │ enum Foo { │ const T = { │ │ A, │ A, │ anyOf: [{ │ @@ -244,7 +248,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ y: Type.Number() │ } │ } │ │ }) │ │ │ │ ) │ │ │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Union([ │ type T = string | number │ const T = { │ │ Type.String(), │ │ anyOf: [{ │ @@ -253,14 +257,14 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ type: 'number' │ │ │ │ }] │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Intersect([ │ type T = { │ const T = { │ │ Type.Object({ │ x: number │ allOf: [{ │ │ x: Type.Number() │ } & { │ type: 'object', │ │ }), │ y: number │ properties: { │ │ Type.Object({ │ } │ a: { │ -│ y: Type.Number() │ │ type: 'number' │ +│ y: Type.Number() │ │ type: 'number' │ │ }) │ │ } │ │ }) │ │ }, │ │ │ │ required: ['a'] │ @@ -268,23 +272,23 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ type: 'object', │ │ │ │ properties: { │ │ │ │ b: { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ }, │ -│ │ │ required: ['b'] │ -│ │ │ }] │ -│ │ │ } │ -│ │ │ │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ }, │ +│ │ │ required: ['b'] │ +│ │ │ }] │ +│ │ │ } │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Record( │ type T = { │ const T = { │ │ Type.String(), │ [key: string]: number │ type: 'object', │ │ Type.Number() │ } │ patternProperties: { │ -│ ) │ │ '^.*$': { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ +│ ) │ │ '^.*$': { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Partial( │ type T = Partial<{ │ const T = { │ │ Type.Object({ │ x: number, │ type: 'object', │ @@ -297,7 +301,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ } │ │ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Required( │ type T = Required<{ │ const T = { │ │ Type.Object({ │ x?: number, │ type: 'object', │ @@ -311,7 +315,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ ) │ │ }, │ │ │ │ required: ['x', 'y'] │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │ │ Type.Object({ │ x: number, │ type: 'object', │ @@ -345,38 +349,38 @@ TypeBox provides modifiers that can be applied to an objects properties. This al ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ TypeBox │ TypeScript │ JSON Schema │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ │ name: Type.Optional( │ name?: string, │ type: 'object', │ │ Type.String(), │ } │ properties: { │ -│ ) │ │ name: { │ +│ ) │ │ name: { │ │ }) │ │ type: 'string' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │ │ Type.String(), │ } │ properties: { │ -│ ) │ │ name: { │ +│ ) │ │ name: { │ │ }) │ │ type: 'string' │ -│ │ │ } │ -│ │ │ }, │ +│ │ │ } │ +│ │ │ }, │ │ │ │ required: ['name'] │ -│ │ │ } │ -│ │ │ │ +│ │ │ } │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ │ name: Type.ReadonlyOptional( │ readonly name?: string, │ type: 'object', │ │ Type.String(), │ } │ properties: { │ -│ ) │ │ name: { │ +│ ) │ │ name: { │ │ }) │ │ type: 'string' │ -│ │ │ } │ -│ │ │ } │ +│ │ │ } │ +│ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` @@ -409,25 +413,25 @@ import { Type, Static, TSchema } from '@sinclair/typebox' const Nullable = (type: T) => Type.Union([type, Type.Null()]) -const T = Nullable(Type.String()) // const T = { - // "anyOf": [{ - // type: 'string' - // }, { - // type: 'null' - // }] - // } - -type T = Static // type T = string | null - -const U = Nullable(Type.Number()) // const U = { - // "anyOf": [{ - // type: 'number' - // }, { - // type: 'null' - // }] - // } - -type U = Static // type U = number | null +const T = Nullable(Type.String()) // const T = { + // "anyOf": [{ + // type: 'string' + // }, { + // type: 'null' + // }] + // } + +type T = Static // type T = string | null + +const U = Nullable(Type.Number()) // const U = { + // "anyOf": [{ + // type: 'number' + // }, { + // type: 'null' + // }] + // } + +type U = Static // type U = number | null ``` @@ -437,61 +441,61 @@ type U = Static // type U = number | null Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`. ```typescript -const T = Type.String({ $id: 'T' }) // const T = { - // $id: 'T', - // type: 'string' - // } +const T = Type.String({ $id: 'T' }) // const T = { + // $id: 'T', + // type: 'string' + // } -const R = Type.Ref(T) // const R = { - // $ref: 'T' - // } +const R = Type.Ref(T) // const R = { + // $ref: 'T' + // } ``` -It can be helpful to organize shared referenced types under a common namespace. The `Type.Namespace(...)` function can be used to create a shared definition container for related types. The following creates a `Math3D` container and a `Vertex` structure that references types in the container. +It can sometimes be helpful to organize shared referenced types under a common namespace. The `Type.Namespace(...)` function can be used to create a shared definition container for related types. The following creates a `Math3D` container and a `Vertex` structure that references types in the container. ```typescript -const Math3D = Type.Namespace({ // const Math3D = { - Vector4: Type.Object({ // $id: 'Math3D', - x: Type.Number(), // $defs: { - y: Type.Number(), // Vector4: { - z: Type.Number(), // type: 'object', - w: Type.Number() // properties: { - }), // x: { type: 'number' }, - Vector3: Type.Object({ // y: { type: 'number' }, - x: Type.Number(), // z: { type: 'number' }, - y: Type.Number(), // w: { type: 'number' } - z: Type.Number() // }, - }), // required: ['x', 'y', 'z', 'w'] - Vector2: Type.Object({ // }, - x: Type.Number(), // Vector3: { - y: Type.Number() // type: 'object', - }) // properties: { -}, { $id: 'Math3D' }) // x: { 'type': 'number' }, - // y: { 'type': 'number' }, - // z: { 'type': 'number' } - // }, - // required: ['x', 'y', 'z'] - // }, - // Vector2: { - // type: 'object', - // properties: { - // x: { 'type': 'number' }, - // y: { 'type': 'number' }, - // }, - // required: ['x', 'y'] - // } - // } - // } +const Math3D = Type.Namespace({ // const Math3D = { + Vector4: Type.Object({ // $id: 'Math3D', + x: Type.Number(), // $defs: { + y: Type.Number(), // Vector4: { + z: Type.Number(), // type: 'object', + w: Type.Number() // properties: { + }), // x: { type: 'number' }, + Vector3: Type.Object({ // y: { type: 'number' }, + x: Type.Number(), // z: { type: 'number' }, + y: Type.Number(), // w: { type: 'number' } + z: Type.Number() // }, + }), // required: ['x', 'y', 'z', 'w'] + Vector2: Type.Object({ // }, + x: Type.Number(), // Vector3: { + y: Type.Number() // type: 'object', + }) // properties: { +}, { $id: 'Math3D' }) // x: { 'type': 'number' }, + // y: { 'type': 'number' }, + // z: { 'type': 'number' } + // }, + // required: ['x', 'y', 'z'] + // }, + // Vector2: { + // type: 'object', + // properties: { + // x: { 'type': 'number' }, + // y: { 'type': 'number' }, + // }, + // required: ['x', 'y'] + // } + // } + // } -const Vertex = Type.Object({ // const Vertex = { - position: Type.Ref(Math3D, 'Vector4'), // type: 'object', - normal: Type.Ref(Math3D, 'Vector3'), // properties: { - uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'Math3D#/$defs/Vector4' }, -}) // normal: { $ref: 'Math3D#/$defs/Vector3' }, - // uv: { $ref: 'Math3D#/$defs/Vector2' } - // }, - // required: ['position', 'normal', 'uv'] - // } +const Vertex = Type.Object({ // const Vertex = { + position: Type.Ref(Math3D, 'Vector4'), // type: 'object', + normal: Type.Ref(Math3D, 'Vector3'), // properties: { + uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'Math3D#/$defs/Vector4' }, +}) // normal: { $ref: 'Math3D#/$defs/Vector3' }, + // uv: { $ref: 'Math3D#/$defs/Vector2' } + // }, + // required: ['position', 'normal', 'uv'] + // } ``` @@ -501,30 +505,30 @@ const Vertex = Type.Object({ // const Vertex = { Recursive types can be created with the `Type.Rec(...)` function. The following creates a `Node` type that contains an array of inner Nodes. Note that due to current restrictions on TypeScript inference, it is not possible for TypeBox to statically infer for recursive types. TypeBox will infer the inner recursive type as `any`. ```typescript -const Node = Type.Rec(Self => Type.Object({ // const Node = { - id: Type.String(), // $id: 'Node', - nodes: Type.Array(Self), // $ref: 'Node#/$defs/self', -}), { $id: 'Node' }) // $defs: { - // self: { - // type: 'object', - // properties: { - // id: { - // type: 'string' - // }, - // nodes: { - // type: 'array', - // items: { - // $ref: 'Node#/$defs/self' - // } - // } - // } - // } - // } - -type Node = Static // type Node = { - // id: string - // nodes: any[] - // +const Node = Type.Rec(Self => Type.Object({ // const Node = { + id: Type.String(), // $id: 'Node', + nodes: Type.Array(Self), // $ref: 'Node#/$defs/self', +}), { $id: 'Node' }) // $defs: { + // self: { + // type: 'object', + // properties: { + // id: { + // type: 'string' + // }, + // nodes: { + // type: 'array', + // items: { + // $ref: 'Node#/$defs/self' + // } + // } + // } + // } + // } + +type Node = Static // type Node = { + // id: string + // nodes: any[] + // } function visit(node: Node) { for(const inner of node.nodes) { @@ -542,7 +546,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ │ TypeBox │ TypeScript │ Extended Schema │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Constructor([ │ type T = new ( │ const T = { │ │ Type.String(), │ arg0: string, │ type: 'constructor' │ @@ -555,7 +559,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a │ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Function([ │ type T = ( │ const T = { │ | Type.String(), │ arg0: string, │ type : 'function', │ @@ -568,7 +572,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a │ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Promise( │ type T = Promise │ const T = { │ │ Type.String() │ │ type: 'promise', │ @@ -576,17 +580,17 @@ In addition to JSON schema types, TypeBox provides several extended types that a │ │ │ type: 'string' │ │ │ │ } │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Undefined() │ type T = undefined │ const T = { │ │ │ │ type: 'undefined' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Void() │ type T = void │ const T = { │ │ │ │ type: 'void' │ │ │ │ } │ -│ │ │ │ +│ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` @@ -594,7 +598,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a ### Strict -TypeBox includes the properties `kind` and `modifier` on each underlying schema. These properties are used to help TypeBox statically resolve the schemas to the appropriate TypeScript type as well as apply the appropriate modifiers to an objects properties (such as optional). These properties are not strictly valid JSON schema so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if nessasary. +TypeBox schemas contain the properties `kind` and `modifier`. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox apply the appropriate static type inference rules. These properties are not strictly valid JSON schema so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if nessasary. ```typescript const T = Type.Object({ // const T = { @@ -623,7 +627,7 @@ const U = Type.Strict(T) // const U = { ### Validation -TypeBox does not provide JSON schema validation functionality, so users will need to select an appropriate JSON Schema validator for their language or framework. TypeBox targets JSON Schema draft `2019-09` so any validator capable of draft `2019-09` 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. +TypeBox does not provide JSON schema validation so users will need to select an appropriate JSON Schema validator for their needs. TypeBox schemas target JSON Schema draft `2019-09` so any validator capable of draft `2019-09` 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. ```bash $ npm install ajv ajv-formats --save @@ -689,64 +693,11 @@ const ok = ajv.validate(User, { }) // -> ok ``` -#### Reference Types - -Referenced types can be added to AJV with the `ajv.addSchema(...)` function. The following moves the `userId` and `email` property types into a `Type.Namespace(...)` and registers the box with AJV. - -```typescript -//-------------------------------------------------------------------------------------------- -// -// Shared Types -// -//-------------------------------------------------------------------------------------------- - -const Shared = Type.Namespace({ - UserId: Type.String({ format: 'uuid' }), - Email: Type.String({ format: 'email' }) -}, { $id: 'Shared' }) - -//-------------------------------------------------------------------------------------------- -// -// Setup Validator and Register Shared Types -// -//-------------------------------------------------------------------------------------------- - -const ajv = addFormats(new Ajv({}), [...]) - .addKeyword('kind') - .addKeyword('modifier') - .addSchema(Shared) // <-- Register Shared Types - -//-------------------------------------------------------------------------------------------- -// -// Create a TypeBox type -// -//-------------------------------------------------------------------------------------------- - -const User = Type.Object({ - userId: Type.Ref(Shared, 'UserId'), - email: Type.Ref(Shared, 'Email'), - online: Type.Boolean() -}, { additionalProperties: false }) - -//-------------------------------------------------------------------------------------------- -// -// Validate Data -// -//-------------------------------------------------------------------------------------------- - -const ok = ajv.validate(User, { - userId: '68b4b1d8-0db6-468d-b551-02069a692044', - email: 'dave@domain.com', - online: true -}) // -> ok - -``` - -Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information. +Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV. ### OpenAPI -TypeBox can be used to create schemas for OpenAPI, however users should be aware of the various differences between the JSON Schema and OpenAPI specifications. Two common instances where OpenAPI diverges from the JSON Schema specification is OpenAPI's handling of `string enum` and `nullable`. The following shows how you can use TypeBox to construct these types. +TypeBox can be used to create schemas for OpenAPI, however users should be aware of the various disparities between the JSON Schema and OpenAPI schema specifications. Two common instances where OpenAPI diverges from the JSON Schema specification is OpenAPI's handling of string enums and nullable schemas. The following shows how you can use TypeBox to construct these types. ```typescript import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox' @@ -761,12 +712,12 @@ function Nullable(schema: T): TUnion<[T, TNull]> { return { ...schema, nullable: true } as any } -const T = Nullable(Type.String()) // const T = { - // type: 'string', - // nullable: true - // } +const T = Nullable(Type.String()) // const T = { + // type: 'string', + // nullable: true + // } -type T = Static // type T = string | null +type T = Static // type T = string | null //-------------------------------------------------------------------------------------------- // @@ -780,9 +731,9 @@ function StringUnion(values: [...T]): TUnion // type T = 'A' | 'B' | 'C' +type T = Static // type T = 'A' | 'B' | 'C' ``` diff --git a/spec/schema/keyof.ts b/spec/schema/keyof.ts index baa1ec940..7ef2b0810 100644 --- a/spec/schema/keyof.ts +++ b/spec/schema/keyof.ts @@ -1,5 +1,6 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' describe("KeyOf", () => { it('Should validate with all object keys as a kind of union', () => { @@ -36,4 +37,12 @@ describe("KeyOf", () => { fail(T, 'y') ok(T, 'z') }) + + it('Should construct new object when targetting reference', () => { + const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) + const R = Type.Ref(T) + const P = Type.KeyOf(R, []) + strictEqual(P.enum[0], 'a') + strictEqual(P.enum[1], 'b') + }) }) diff --git a/spec/types/typebox.d.ts b/spec/types/typebox.d.ts index df723db3a..2c2f46d9b 100644 --- a/spec/types/typebox.d.ts +++ b/spec/types/typebox.d.ts @@ -142,6 +142,11 @@ export interface TEnum extends TSchema, CustomOptions { kind: typeof EnumKind; anyOf: T; } +export interface TRef extends TSchema, CustomOptions { + $static: Static; + kind: typeof RefKind; + $ref: string; +} export interface TString extends TSchema, StringOptions { $static: string; kind: typeof StringKind; @@ -175,11 +180,6 @@ export interface TAny extends TSchema, CustomOptions { $static: any; kind: typeof AnyKind; } -export interface TRef extends TSchema, CustomOptions { - $static: Static; - kind: typeof RefKind; - $ref: string; -} export declare const ConstructorKind: unique symbol; export declare const FunctionKind: unique symbol; export declare const PromiseKind: unique symbol; @@ -215,9 +215,9 @@ export interface TVoid extends TSchema, CustomOptions { kind: typeof VoidKind; type: 'void'; } -export declare type Pickable = TObject | TRef>; -export declare type PickablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never; -export declare type PickableProperties = T extends TObject ? U : T extends TRef> ? U : never; +export declare type Selectable = TObject | TRef>; +export declare type SelectablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never; +export declare type SelectableProperties = T extends TObject ? U : T extends TRef> ? U : never; export declare type UnionToIntersect = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; export declare type StaticReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never; @@ -271,7 +271,7 @@ export declare type StaticFunction = Promise>; export declare type Static = T['$static']; export declare class TypeBuilder { - private readonly schemas; + protected readonly schemas: Map; /** `Standard` Modifies an object property to be both readonly and optional */ ReadonlyOptional(item: T): TReadonlyOptional; /** `Standard` Modifies an object property to be readonly */ @@ -308,18 +308,18 @@ export declare class TypeBuilder { Unknown(options?: CustomOptions): TUnknown; /** `Standard` Creates an any type */ Any(options?: CustomOptions): TAny; - /** `Standard` Creates a keyof type from the given object */ - KeyOf>(object: T, options?: CustomOptions): TKeyOf<(keyof T['properties'])[]>; /** `Standard` Creates a record type */ Record(key: K, value: T, options?: ObjectOptions): TRecord; + /** `Standard` Creates a keyof type from the given object */ + KeyOf | TRef>>(object: T, options?: CustomOptions): TKeyOf[]>; /** `Standard` Makes all properties in the given object type required */ Required | TRef>>(object: T, options?: ObjectOptions): TObject>; /** `Standard` Makes all properties in the given object type optional */ Partial | TRef>>(object: T, options?: ObjectOptions): TObject>; /** `Standard` Picks property keys from the given object type */ - Pick | TRef>, K extends PickablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; + Pick | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; /** `Standard` Omits property keys from the given object type */ - Omit | TRef>, K extends PickablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; + Omit | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */ Strict(schema: T, options?: CustomOptions): T; /** `Extended` Creates a constructor type */ @@ -335,14 +335,14 @@ export declare class TypeBuilder { /** `Standard` Creates a namespace for a set of related types */ Namespace($defs: T, options?: CustomOptions): TNamespace; /** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */ - Ref, K extends keyof T['$defs']>(box: T, key: K): TRef; + Ref, K extends keyof T['$defs']>(namespace: T, key: K): TRef; /** `Standard` References type. The referenced type must specify an `$id` */ Ref(schema: T): TRef; /** `Experimental` Creates a recursive type */ Rec(callback: (self: TAny) => T, options?: CustomOptions): T; - /** Stores this schema if it contains an $id. This function is used for later referencing. */ - private Store; - /** Resolves a schema by $id. May resolve recursively if the target is a TRef. */ - private Resolve; + /** Conditionally stores and schema if it contains an $id and returns. */ + protected Store(schema: any): any; + /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ + protected Deref(schema: any): any; } export declare const Type: TypeBuilder; diff --git a/src/typebox.ts b/src/typebox.ts index db18c4cc4..3c47cc9e3 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -62,6 +62,10 @@ export const UnknownKind = Symbol('UnknownKind') export const AnyKind = Symbol('AnyKind') export const RefKind = Symbol('RefKind') +// -------------------------------------------------------------------------- +// Options +// -------------------------------------------------------------------------- + export interface CustomOptions { $id?: string title?: string @@ -109,7 +113,7 @@ export type ObjectOptions = { } & CustomOptions // -------------------------------------------------------------------------- -// Namespacing +// Namespace // -------------------------------------------------------------------------- export type TDefinitions = { [key: string]: TSchema } @@ -141,6 +145,7 @@ export interface TKeyOf extends TS export interface TArray extends TSchema, ArrayOptions { $static: StaticArray, kind: typeof ArrayKind, type: 'array', items: T } export interface TLiteral extends TSchema, CustomOptions { $static: StaticLiteral, kind: typeof LiteralKind, const: T } export interface TEnum extends TSchema, CustomOptions { $static: StaticEnum, kind: typeof EnumKind, anyOf: T } +export interface TRef extends TSchema, CustomOptions { $static: Static, kind: typeof RefKind, $ref: string } export interface TString extends TSchema, StringOptions { $static: string, kind: typeof StringKind, type: 'string' } export interface TNumber extends TSchema, NumberOptions { $static: number, kind: typeof NumberKind, type: 'number' } export interface TInteger extends TSchema, NumberOptions { $static: number, kind: typeof IntegerKind, type: 'integer' } @@ -148,7 +153,7 @@ export interface TBoolean extends TS export interface TNull extends TSchema, CustomOptions { $static: null, kind: typeof NullKind, type: 'null' } export interface TUnknown extends TSchema, CustomOptions { $static: unknown, kind: typeof UnknownKind } export interface TAny extends TSchema, CustomOptions { $static: any, kind: typeof AnyKind } -export interface TRef extends TSchema, CustomOptions { $static: Static, kind: typeof RefKind, $ref: string } + // -------------------------------------------------------------------------- // Extended Schema Types @@ -168,10 +173,10 @@ export interface TVoid extends T // -------------------------------------------------------------------------- // Utility Types // -------------------------------------------------------------------------- -export type Pickable = TObject | TRef> -export type PickablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never -export type PickableProperties = T extends TObject ? U : T extends TRef> ? U : never +export type Selectable = TObject | TRef> +export type SelectablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never +export type SelectableProperties = T extends TObject ? U : T extends TRef> ? U : never export type UnionToIntersect = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never export type StaticReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] export type StaticReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] @@ -246,7 +251,8 @@ function clone(object: any): any { // -------------------------------------------------------------------------- export class TypeBuilder { - private readonly schemas = new Map() + + protected readonly schemas = new Map() /** `Standard` Modifies an object property to be both readonly and optional */ public ReadonlyOptional(item: T): TReadonlyOptional { @@ -356,12 +362,6 @@ export class TypeBuilder { public Any(options: CustomOptions = {}): TAny { return this.Store({ ...options, kind: AnyKind }) } - - /** `Standard` Creates a keyof type from the given object */ - public KeyOf>(object: T, options: CustomOptions = {}): TKeyOf<(keyof T['properties'])[]> { - const keys = Object.keys(object.properties) - return this.Store({...options, kind: KeyOfKind, type: 'string', enum: keys }) - } /** `Standard` Creates a record type */ public Record(key: K, value: T, options: ObjectOptions = {}): TRecord { @@ -376,10 +376,17 @@ export class TypeBuilder { })() return this.Store({ ...options, kind: RecordKind, type: 'object', patternProperties: { [pattern]: value } }) } - + + /** `Standard` Creates a keyof type from the given object */ + public KeyOf | TRef>>(object: T, options: CustomOptions = {}): TKeyOf[]> { + const source = this.Deref(object) + const keys = Object.keys(source.properties) + return this.Store({...options, kind: KeyOfKind, type: 'string', enum: keys }) + } + /** `Standard` Makes all properties in the given object type required */ public Required | TRef>>(object: T, options: ObjectOptions = {}): TObject> { - const source = this.Resolve(object) + const source = this.Deref(object) const schema = { ...clone(source), ...options } schema.required = Object.keys(schema.properties) for(const key of Object.keys(schema.properties)) { @@ -396,7 +403,7 @@ export class TypeBuilder { /** `Standard` Makes all properties in the given object type optional */ public Partial | TRef>>(object: T, options: ObjectOptions = {}): TObject> { - const source = this.Resolve(object) + const source = this.Deref(object) const schema = { ...clone(source), ...options } delete schema.required for(const key of Object.keys(schema.properties)) { @@ -412,8 +419,8 @@ export class TypeBuilder { } /** `Standard` Picks property keys from the given object type */ - public Pick | TRef>, K extends PickablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}): TObject, K[number]>> { - const source = this.Resolve(object) + public Pick | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}): TObject, K[number]>> { + const source = this.Deref(object) const schema = { ...clone(source), ...options } schema.required = schema.required ? schema.required.filter((key: K) => keys.includes(key as any)) : undefined for(const key of Object.keys(schema.properties)) { @@ -423,8 +430,8 @@ export class TypeBuilder { } /** `Standard` Omits property keys from the given object type */ - public Omit | TRef>, K extends PickablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}):TObject, K[number]>> { - const source = this.Resolve(object) + public Omit | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}):TObject, K[number]>> { + const source = this.Deref(object) const schema = { ...clone(source), ...options } schema.required = schema.required ? schema.required.filter((key: string) => !keys.includes(key as any)) : undefined for(const key of Object.keys(schema.properties)) { @@ -469,7 +476,7 @@ export class TypeBuilder { } /** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */ - public Ref, K extends keyof T['$defs']>(box: T, key: K): TRef + public Ref, K extends keyof T['$defs']>(namespace: T, key: K): TRef /** `Standard` References type. The referenced type must specify an `$id` */ public Ref(schema: T): TRef @@ -498,18 +505,18 @@ export class TypeBuilder { return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } }) } - /** Stores this schema if it contains an $id. This function is used for later referencing. */ - private Store(schema: any): any { + /** Conditionally stores and schema if it contains an $id and returns. */ + protected Store(schema: any): any { if(!schema.$id) return schema - this.schemas.set(schema.$id, schema) + this.schemas.set(schema.$id, schema as any) return schema } - /** Resolves a schema by $id. May resolve recursively if the target is a TRef. */ - private Resolve(schema: any): any { + /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ + protected Deref(schema: any): any { if(schema.kind !== RefKind) return schema if(!this.schemas.has(schema.$ref)) throw Error(`Unable to locate schema with $id '${schema.$ref}'`) - return this.Resolve(this.schemas.get(schema.$ref)!) + return this.Deref(this.schemas.get(schema.$ref)!) } } diff --git a/typebox.png b/typebox.png new file mode 100644 index 0000000000000000000000000000000000000000..d55615fcea711801022a6d577bf0d3988e63bd7b GIT binary patch literal 342043 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

#iDtj&ka3tH=i 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

Lp6Wlc(kY(u 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 Date: Sat, 4 Dec 2021 04:23:11 +0900 Subject: [PATCH 002/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 241d936c7..aa474d8d3 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@

TypeBox

- +

JSON Schema Type Builder with Static Type Resolution for TypeScript

From ecb7d85c68e60d8511d13f7c5d30ffafbbdba69b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 4 Dec 2021 04:39:19 +0900 Subject: [PATCH 003/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index aa474d8d3..66589972d 100644 --- a/readme.md +++ b/readme.md @@ -42,7 +42,7 @@ type T = Static // type T = string ## Overview -TypeBox is a 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 create a unified type that can be both statically asserted by the TypeScript compiler and runtime asserted using industry standard JSON Schema validation. +TypeBox is a 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 create a unified type that can be both statically asserted by the TypeScript compiler and runtime asserted using standard JSON Schema validation. 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. TypeBox does not provide any JSON schema validation. Please use libraries such as AJV to validate schemas built with this library. From 00de70a118c40934d49d941e683a5c65168f4705 Mon Sep 17 00:00:00 2001 From: Doni Rubiagatra Date: Sun, 19 Dec 2021 14:28:52 +0800 Subject: [PATCH 004/369] Documentation --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 66589972d..a2e4aed56 100644 --- a/readme.md +++ b/readme.md @@ -263,19 +263,19 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ Type.Object({ │ x: number │ allOf: [{ │ │ x: Type.Number() │ } & { │ type: 'object', │ │ }), │ y: number │ properties: { │ -│ Type.Object({ │ } │ a: { │ +│ Type.Object({ │ } │ x: { │ │ y: Type.Number() │ │ type: 'number' │ │ }) │ │ } │ │ }) │ │ }, │ -│ │ │ required: ['a'] │ +│ │ │ required: ['x'] │ │ │ │ }, { │ │ │ │ type: 'object', │ │ │ │ properties: { │ -│ │ │ b: { │ +│ │ │ y: { │ │ │ │ type: 'number' │ │ │ │ } │ │ │ │ }, │ -│ │ │ required: ['b'] │ +│ │ │ required: ['y'] │ │ │ │ }] │ │ │ │ } │ │ │ │ │ From 62c85bac0fde54314e4b48ad6b870880f8aeeb82 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 22 Dec 2021 23:51:37 +0900 Subject: [PATCH 005/369] Strict Typing on Store and Deref --- package.json | 2 +- spec/types/typebox.d.ts | 4 ++-- src/typebox.ts | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 1b774bf59..cef6d5b2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.23.1", + "version": "0.23.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "json-schema", diff --git a/spec/types/typebox.d.ts b/spec/types/typebox.d.ts index 2c2f46d9b..de83d985b 100644 --- a/spec/types/typebox.d.ts +++ b/spec/types/typebox.d.ts @@ -341,8 +341,8 @@ export declare class TypeBuilder { /** `Experimental` Creates a recursive type */ Rec(callback: (self: TAny) => T, options?: CustomOptions): T; /** Conditionally stores and schema if it contains an $id and returns. */ - protected Store(schema: any): any; + protected Store, S = Omit>(schema: S): T; /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ - protected Deref(schema: any): any; + protected Deref(schema: T): any; } export declare const Type: TypeBuilder; diff --git a/src/typebox.ts b/src/typebox.ts index 3c47cc9e3..aeeb30849 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -506,17 +506,19 @@ export class TypeBuilder { } /** Conditionally stores and schema if it contains an $id and returns. */ - protected Store(schema: any): any { - if(!schema.$id) return schema - this.schemas.set(schema.$id, schema as any) - return schema + protected Store, S = Omit>(schema: S): T { + const $schema: any = schema + if(!$schema['$id']) return $schema + this.schemas.set($schema['$id'], $schema) + return $schema } /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ - protected Deref(schema: any): any { - if(schema.kind !== RefKind) return schema - if(!this.schemas.has(schema.$ref)) throw Error(`Unable to locate schema with $id '${schema.$ref}'`) - return this.Deref(this.schemas.get(schema.$ref)!) + protected Deref(schema: T): any { + const $schema: any = schema + if($schema['kind'] !== RefKind) return schema + if(!this.schemas.has($schema['$ref'])) throw Error(`Unable to locate schema with $id '${$schema['$ref']}'`) + return this.Deref(this.schemas.get($schema['$ref'])!) } } From 4e44e691a2d53aaf8221af69fd70846fc2b1c165 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 22 Dec 2021 23:54:01 +0900 Subject: [PATCH 006/369] Strict Typing on Store and Deref --- src/typebox.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index aeeb30849..4dd4864a1 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -505,7 +505,7 @@ export class TypeBuilder { return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } }) } - /** Conditionally stores and schema if it contains an $id and returns. */ + /** Conditionally stores and schema if it contains an $id and returns */ protected Store, S = Omit>(schema: S): T { const $schema: any = schema if(!$schema['$id']) return $schema @@ -513,7 +513,7 @@ export class TypeBuilder { return $schema } - /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ + /** Conditionally dereferences a schema if RefKind. Otherwise return argument */ protected Deref(schema: T): any { const $schema: any = schema if($schema['kind'] !== RefKind) return schema From 5c690d681d1e55bf95415f48db3844c13bbd79d5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 22 Dec 2021 23:55:51 +0900 Subject: [PATCH 007/369] Strict Typing on Store and Deref --- spec/types/typebox.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/types/typebox.d.ts b/spec/types/typebox.d.ts index de83d985b..3eb849c20 100644 --- a/spec/types/typebox.d.ts +++ b/spec/types/typebox.d.ts @@ -340,9 +340,9 @@ export declare class TypeBuilder { Ref(schema: T): TRef; /** `Experimental` Creates a recursive type */ Rec(callback: (self: TAny) => T, options?: CustomOptions): T; - /** Conditionally stores and schema if it contains an $id and returns. */ + /** Conditionally stores and schema if it contains an $id and returns */ protected Store, S = Omit>(schema: S): T; - /** Conditionally dereferences a schema if RefKind. Otherwise return argument. */ + /** Conditionally dereferences a schema if RefKind. Otherwise return argument */ protected Deref(schema: T): any; } export declare const Type: TypeBuilder; From 2149b290d2a6870d1d6360fbe942ae271d987a5d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 17 Jan 2022 23:40:34 +0900 Subject: [PATCH 008/369] Updates --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a2e4aed56..1f31ffca7 100644 --- a/readme.md +++ b/readme.md @@ -697,7 +697,7 @@ Please refer to the official AJV [documentation](https://ajv.js.org/guide/gettin ### OpenAPI -TypeBox can be used to create schemas for OpenAPI, however users should be aware of the various disparities between the JSON Schema and OpenAPI schema specifications. Two common instances where OpenAPI diverges from the JSON Schema specification is OpenAPI's handling of string enums and nullable schemas. The following shows how you can use TypeBox to construct these types. +TypeBox can be used to create schemas for OpenAPI, however users should be mindful of some disparities between the JSON Schema and OpenAPI for versions prior to OpenAPI 3.1. Two common instances where OpenAPI diverges is the handling nullable and string enum schemas types. The following shows how you can use TypeBox to construct these types. ```typescript import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox' From b7a9c23bb9a1db9b5d61965ddf4f03d582d371cf Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 18 Jan 2022 01:53:53 +0900 Subject: [PATCH 009/369] Additional Union Test --- spec/schema/union.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/schema/union.ts b/spec/schema/union.ts index c1e3c859a..3d66dd9a0 100644 --- a/spec/schema/union.ts +++ b/spec/schema/union.ts @@ -21,6 +21,14 @@ describe('Union', () => { ok(T, { b: 'world' }) }) + it('Should fail to validate for descriminated union types', () => { + const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) + const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) + const T = Type.Union([A, B]) + fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } + fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } + }) + it('Should validate union of objects where properties overlap', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) From 62dc5f825aee482833c5a0a44ac34b17ed5b3f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Correa=20Casablanca?= Date: Sat, 22 Jan 2022 10:46:28 +0100 Subject: [PATCH 010/369] Documentation (#144) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 1f31ffca7..083da1b54 100644 --- a/readme.md +++ b/readme.md @@ -598,7 +598,7 @@ In addition to JSON schema types, TypeBox provides several extended types that a ### Strict -TypeBox schemas contain the properties `kind` and `modifier`. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox apply the appropriate static type inference rules. These properties are not strictly valid JSON schema so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if nessasary. +TypeBox schemas contain the properties `kind` and `modifier`. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox apply the appropriate static type inference rules. These properties are not strictly valid JSON schema so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. ```typescript const T = Type.Object({ // const T = { From 4acd67064c2218a2674a63d81b3e82598563063b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 23 Jan 2022 09:07:34 +0900 Subject: [PATCH 011/369] Next --- .github/.keep | 0 .github/workflows/ci.yml | 23 +- .gitignore | 3 - .vscode/settings.json | 7 +- benchmark/compression/index.ts | 25 + .../compression/module/typebox-compiler.ts | 4 + .../compression/module/typebox-conditional.ts | 4 + .../compression/module/typebox-format.ts | 4 + benchmark/compression/module/typebox-guard.ts | 4 + benchmark/compression/module/typebox-value.ts | 4 + benchmark/compression/module/typebox.ts | 3 + benchmark/index.ts | 2 + benchmark/measurement/index.ts | 5 + benchmark/measurement/module/benchmark.ts | 7 + benchmark/measurement/module/cases.ts | 114 + benchmark/measurement/module/check.ts | 42 + benchmark/measurement/module/compile.ts | 36 + benchmark/measurement/module/index.ts | 36 + benchmark/measurement/module/result.ts | 15 + changelog.md | 98 +- codegen/codegen.ts | 43 + codegen/index.ts | 29 + codegen/tsconfig.json | 4 + codegen/typebox.ts | 364 +++ codegen/typescript.ts | 225 ++ example/index.ts | 24 +- hammer.mjs | 65 +- license | 2 +- package-lock.json | 2752 +++++------------ package.json | 36 +- readme.md | 1191 ++++--- spec/schema/any.ts | 33 - spec/schema/array.ts | 77 - spec/schema/boolean.ts | 41 - spec/schema/box.ts | 74 - spec/schema/enum.ts | 57 - spec/schema/intersect.ts | 97 - spec/schema/keyof.ts | 48 - spec/schema/literal.ts | 44 - spec/schema/modifier.ts | 22 - spec/schema/null.ts | 39 - spec/schema/number.ts | 33 - spec/schema/object.ts | 94 - spec/schema/omit.ts | 44 - spec/schema/optional.ts | 21 - spec/schema/partial.ts | 52 - spec/schema/pick.ts | 43 - spec/schema/readonly-optional.ts | 21 - spec/schema/readonly.ts | 23 - spec/schema/rec.ts | 101 - spec/schema/record.ts | 100 - spec/schema/ref.ts | 87 - spec/schema/regex.ts | 31 - spec/schema/required.ts | 52 - spec/schema/strict.ts | 33 - spec/schema/string.ts | 49 - spec/schema/tuple.ts | 70 - spec/schema/union.ts | 65 - spec/schema/unknown.ts | 33 - spec/schema/validate.ts | 70 - spec/types/any.ts | 4 - spec/types/array.ts | 22 - spec/types/boolean.ts | 4 - spec/types/enum.ts | 8 - spec/types/intersect.ts | 38 - spec/types/keyof.ts | 49 - spec/types/literal.ts | 8 - spec/types/modifier.ts | 18 - spec/types/namespace.ts | 16 - spec/types/null.ts | 4 - spec/types/number.ts | 5 - spec/types/object.ts | 54 - spec/types/omit.ts | 17 - spec/types/optional.ts | 11 - spec/types/partial.ts | 18 - spec/types/pick.ts | 17 - spec/types/readonly-optional.ts | 11 - spec/types/readonly.ts | 12 - spec/types/rec.ts | 14 - spec/types/record.ts | 85 - spec/types/ref.ts | 26 - spec/types/regex.ts | 4 - spec/types/required.ts | 18 - spec/types/spec.ts | 8 - spec/types/strict.ts | 16 - spec/types/string.ts | 4 - spec/types/tuple.ts | 12 - spec/types/typebox.d.ts | 348 --- spec/types/union.ts | 28 - spec/types/unknown.ts | 4 - src/compiler/compiler.ts | 416 +++ src/compiler/index.ts | 30 + src/conditional/conditional.ts | 114 + src/conditional/index.ts | 30 + src/conditional/structural.ts | 558 ++++ src/errors/errors.ts | 411 +++ src/errors/index.ts | 29 + src/format/format.ts | 54 + src/format/index.ts | 29 + src/guard/guard.ts | 362 +++ src/guard/index.ts | 29 + src/tsconfig.json | 16 +- src/typebox.ts | 1316 +++++--- src/value/cast.ts | 370 +++ src/value/check.ts | 331 ++ src/value/clone.ts | 62 + src/value/create.ts | 342 ++ src/value/delta.ts | 182 ++ src/value/equal.ts | 67 + src/value/index.ts | 31 + src/value/is.ts | 50 + src/value/pointer.ts | 122 + src/value/value.ts | 97 + test/runtime/assert/assert.ts | 59 + test/runtime/assert/index.ts | 1 + test/runtime/compiler/any.ts | 33 + test/runtime/compiler/array.ts | 97 + test/runtime/compiler/boolean.ts | 40 + test/runtime/compiler/enum.ts | 56 + .../schema => test/runtime/compiler}/index.ts | 9 +- test/runtime/compiler/intersect.ts | 83 + test/runtime/compiler/keyof.ts | 52 + test/runtime/compiler/literal.ts | 43 + test/runtime/compiler/modifier.ts | 3 + test/runtime/compiler/never.ts | 36 + test/runtime/compiler/null.ts | 39 + test/runtime/compiler/number.ts | 33 + test/runtime/compiler/object.ts | 142 + test/runtime/compiler/omit.ts | 60 + test/runtime/compiler/optional.ts | 27 + test/runtime/compiler/partial.ts | 52 + test/runtime/compiler/pick.ts | 60 + test/runtime/compiler/readonly-optional.ts | 27 + test/runtime/compiler/readonly.ts | 28 + test/runtime/compiler/record.ts | 101 + test/runtime/compiler/recursive.ts | 83 + test/runtime/compiler/ref.ts | 70 + test/runtime/compiler/regex.ts | 35 + test/runtime/compiler/required.ts | 57 + test/runtime/compiler/string.ts | 65 + test/runtime/compiler/tuple.ts | 62 + test/runtime/compiler/uint8array.ts | 56 + test/runtime/compiler/union.ts | 62 + test/runtime/compiler/unknown.ts | 33 + test/runtime/compiler/validate.ts | 107 + test/runtime/compiler/void.ts | 39 + test/runtime/conditional/any.ts | 92 + test/runtime/conditional/array.ts | 310 ++ test/runtime/conditional/boolean.ts | 107 + test/runtime/conditional/constructor.ts | 243 ++ test/runtime/conditional/function.ts | 245 ++ test/runtime/conditional/index.ts | 19 + test/runtime/conditional/integer.ts | 112 + test/runtime/conditional/literal.ts | 324 ++ test/runtime/conditional/null.ts | 113 + test/runtime/conditional/number.ts | 111 + test/runtime/conditional/object.ts | 155 + test/runtime/conditional/promise.ts | 245 ++ test/runtime/conditional/record.ts | 182 ++ test/runtime/conditional/string.ts | 143 + test/runtime/conditional/tuple.ts | 196 ++ test/runtime/conditional/uint8array.ts | 112 + test/runtime/conditional/undefined.ts | 100 + test/runtime/conditional/union.ts | 142 + test/runtime/conditional/unknown.ts | 119 + test/runtime/conditional/void.ts | 119 + test/runtime/format/format.ts | 25 + test/runtime/format/index.ts | 1 + test/runtime/guard/any.ts | 19 + test/runtime/guard/array.ts | 63 + test/runtime/guard/boolean.ts | 19 + test/runtime/guard/constructor.ts | 35 + test/runtime/guard/function.ts | 35 + test/runtime/guard/index.ts | 21 + test/runtime/guard/integer.ts | 44 + test/runtime/guard/literal.ts | 32 + test/runtime/guard/null.ts | 19 + test/runtime/guard/number.ts | 44 + test/runtime/guard/object.ts | 99 + test/runtime/guard/promise.ts | 45 + test/runtime/guard/record.ts | 53 + test/runtime/guard/ref.ts | 25 + test/runtime/guard/self.ts | 22 + test/runtime/guard/string.ts | 39 + test/runtime/guard/tuple.ts | 23 + test/runtime/guard/uint8array.ts | 33 + test/runtime/guard/undefined.ts | 19 + test/runtime/guard/union.ts | 66 + test/runtime/guard/unknown.ts | 19 + test/runtime/guard/void.ts | 19 + test/runtime/index.ts | 6 + test/runtime/schema/any.ts | 33 + test/runtime/schema/array.ts | 96 + test/runtime/schema/boolean.ts | 40 + test/runtime/schema/enum.ts | 56 + .../runtime/schema/index.ts | 10 +- test/runtime/schema/intersect.ts | 94 + test/runtime/schema/keyof.ts | 52 + test/runtime/schema/literal.ts | 43 + test/runtime/schema/modifier.ts | 3 + test/runtime/schema/never.ts | 33 + test/runtime/schema/null.ts | 39 + test/runtime/schema/number.ts | 33 + test/runtime/schema/object.ts | 142 + test/runtime/schema/omit.ts | 73 + test/runtime/schema/optional.ts | 27 + test/runtime/schema/partial.ts | 52 + test/runtime/schema/pick.ts | 73 + test/runtime/schema/readonly-optional.ts | 27 + test/runtime/schema/readonly.ts | 28 + test/runtime/schema/record.ts | 101 + test/runtime/schema/recursive.ts | 83 + test/runtime/schema/ref.ts | 69 + test/runtime/schema/regex.ts | 35 + test/runtime/schema/required.ts | 57 + test/runtime/schema/string.ts | 65 + test/runtime/schema/tuple.ts | 62 + test/runtime/schema/union.ts | 62 + test/runtime/schema/unknown.ts | 33 + test/runtime/schema/unsafe.ts | 18 + test/runtime/schema/validate.ts | 64 + test/runtime/schema/void.ts | 39 + test/runtime/value/cast/any.ts | 47 + test/runtime/value/cast/array.ts | 137 + test/runtime/value/cast/boolean.ts | 53 + test/runtime/value/cast/convert.ts | 281 ++ test/runtime/value/cast/enum.ts | 62 + test/runtime/value/cast/index.ts | 24 + test/runtime/value/cast/integer.ts | 47 + test/runtime/value/cast/intersect.ts | 90 + test/runtime/value/cast/keyof.ts | 62 + test/runtime/value/cast/literal.ts | 53 + test/runtime/value/cast/namespace.ts | 0 test/runtime/value/cast/never.ts | 35 + test/runtime/value/cast/null.ts | 53 + test/runtime/value/cast/number.ts | 53 + test/runtime/value/cast/object.ts | 112 + test/runtime/value/cast/rec.ts | 104 + test/runtime/value/cast/record.ts | 81 + test/runtime/value/cast/regex.ts | 53 + test/runtime/value/cast/string.ts | 55 + test/runtime/value/cast/tuple.ts | 80 + test/runtime/value/cast/uint8array.ts | 53 + test/runtime/value/cast/undefined.ts | 53 + test/runtime/value/cast/union.ts | 125 + test/runtime/value/cast/unknown.ts | 50 + test/runtime/value/cast/void.ts | 53 + test/runtime/value/check/any.ts | 42 + test/runtime/value/check/array.ts | 33 + test/runtime/value/check/boolean.ts | 42 + test/runtime/value/check/enum.ts | 29 + test/runtime/value/check/index.ts | 22 + test/runtime/value/check/integer.ts | 19 + test/runtime/value/check/intersect.ts | 82 + test/runtime/value/check/keyof.ts | 37 + test/runtime/value/check/literal.ts | 27 + test/runtime/value/check/never.ts | 42 + test/runtime/value/check/null.ts | 42 + test/runtime/value/check/number.ts | 42 + test/runtime/value/check/object.ts | 101 + test/runtime/value/check/rec.ts | 69 + test/runtime/value/check/record.ts | 83 + test/runtime/value/check/regex.ts | 19 + test/runtime/value/check/string.ts | 42 + test/runtime/value/check/tuple.ts | 40 + test/runtime/value/check/uint8array.ts | 33 + test/runtime/value/check/undefined.ts | 42 + test/runtime/value/check/union.ts | 77 + test/runtime/value/check/unknown.ts | 42 + test/runtime/value/check/void.ts | 42 + test/runtime/value/clone/clone.ts | 177 ++ test/runtime/value/clone/index.ts | 1 + test/runtime/value/create/any.ts | 14 + test/runtime/value/create/array.ts | 18 + test/runtime/value/create/boolean.ts | 14 + test/runtime/value/create/constructor.ts | 38 + test/runtime/value/create/enum.ts | 22 + test/runtime/value/create/function.ts | 19 + test/runtime/value/create/index.ts | 24 + test/runtime/value/create/integer.ts | 14 + test/runtime/value/create/intersect.ts | 66 + test/runtime/value/create/keyof.ts | 25 + test/runtime/value/create/literal.ts | 18 + test/runtime/value/create/never.ts | 10 + test/runtime/value/create/null.ts | 10 + test/runtime/value/create/number.ts | 14 + test/runtime/value/create/object.ts | 75 + test/runtime/value/create/rec.ts | 30 + test/runtime/value/create/record.ts | 18 + test/runtime/value/create/regex.ts | 16 + test/runtime/value/create/string.ts | 14 + test/runtime/value/create/tuple.ts | 22 + test/runtime/value/create/uint8array.ts | 28 + test/runtime/value/create/undefined.ts | 10 + test/runtime/value/create/union.ts | 51 + test/runtime/value/create/unknown.ts | 14 + test/runtime/value/create/void.ts | 10 + test/runtime/value/delta/diff.ts | 424 +++ test/runtime/value/delta/index.ts | 2 + test/runtime/value/delta/patch.ts | 447 +++ test/runtime/value/equal/equal.ts | 111 + test/runtime/value/equal/index.ts | 1 + test/runtime/value/index.ts | 7 + test/runtime/value/pointer/index.ts | 1 + test/runtime/value/pointer/pointer.ts | 397 +++ test/static/any.ts | 4 + test/static/array.ts | 24 + test/static/assert.ts | 8 + test/static/boolean.ts | 4 + test/static/enum.ts | 12 + test/static/index.ts | 35 + test/static/intersect.ts | 51 + test/static/keyof.ts | 57 + test/static/literal.ts | 8 + test/static/modifier.ts | 18 + test/static/never.ts | 7 + test/static/null.ts | 4 + test/static/number.ts | 4 + test/static/object.ts | 52 + test/static/omit.ts | 37 + test/static/optional.ts | 14 + test/static/partial.ts | 19 + test/static/pick.ts | 40 + test/static/readonly-optional.ts | 11 + test/static/readonly.ts | 14 + test/static/record.ts | 61 + test/static/recursive.ts | 15 + test/static/ref.ts | 13 + test/static/regex.ts | 4 + test/static/required.ts | 22 + test/static/return-type.ts | 18 + test/static/strict.ts | 20 + test/static/string.ts | 4 + test/static/tsconfig.json | 4 + test/static/tuple.ts | 10 + test/static/union.ts | 73 + test/static/unknown.ts | 4 + tsconfig.json | 26 +- typebox.png | Bin 342043 -> 706199 bytes 339 files changed, 20443 insertions(+), 5343 deletions(-) create mode 100644 .github/.keep create mode 100644 benchmark/compression/index.ts create mode 100644 benchmark/compression/module/typebox-compiler.ts create mode 100644 benchmark/compression/module/typebox-conditional.ts create mode 100644 benchmark/compression/module/typebox-format.ts create mode 100644 benchmark/compression/module/typebox-guard.ts create mode 100644 benchmark/compression/module/typebox-value.ts create mode 100644 benchmark/compression/module/typebox.ts create mode 100644 benchmark/index.ts create mode 100644 benchmark/measurement/index.ts create mode 100644 benchmark/measurement/module/benchmark.ts create mode 100644 benchmark/measurement/module/cases.ts create mode 100644 benchmark/measurement/module/check.ts create mode 100644 benchmark/measurement/module/compile.ts create mode 100644 benchmark/measurement/module/index.ts create mode 100644 benchmark/measurement/module/result.ts create mode 100644 codegen/codegen.ts create mode 100644 codegen/index.ts create mode 100644 codegen/tsconfig.json create mode 100644 codegen/typebox.ts create mode 100644 codegen/typescript.ts delete mode 100644 spec/schema/any.ts delete mode 100644 spec/schema/array.ts delete mode 100644 spec/schema/boolean.ts delete mode 100644 spec/schema/box.ts delete mode 100644 spec/schema/enum.ts delete mode 100644 spec/schema/intersect.ts delete mode 100644 spec/schema/keyof.ts delete mode 100644 spec/schema/literal.ts delete mode 100644 spec/schema/modifier.ts delete mode 100644 spec/schema/null.ts delete mode 100644 spec/schema/number.ts delete mode 100644 spec/schema/object.ts delete mode 100644 spec/schema/omit.ts delete mode 100644 spec/schema/optional.ts delete mode 100644 spec/schema/partial.ts delete mode 100644 spec/schema/pick.ts delete mode 100644 spec/schema/readonly-optional.ts delete mode 100644 spec/schema/readonly.ts delete mode 100644 spec/schema/rec.ts delete mode 100644 spec/schema/record.ts delete mode 100644 spec/schema/ref.ts delete mode 100644 spec/schema/regex.ts delete mode 100644 spec/schema/required.ts delete mode 100644 spec/schema/strict.ts delete mode 100644 spec/schema/string.ts delete mode 100644 spec/schema/tuple.ts delete mode 100644 spec/schema/union.ts delete mode 100644 spec/schema/unknown.ts delete mode 100644 spec/schema/validate.ts delete mode 100644 spec/types/any.ts delete mode 100644 spec/types/array.ts delete mode 100644 spec/types/boolean.ts delete mode 100644 spec/types/enum.ts delete mode 100644 spec/types/intersect.ts delete mode 100644 spec/types/keyof.ts delete mode 100644 spec/types/literal.ts delete mode 100644 spec/types/modifier.ts delete mode 100644 spec/types/namespace.ts delete mode 100644 spec/types/null.ts delete mode 100644 spec/types/number.ts delete mode 100644 spec/types/object.ts delete mode 100644 spec/types/omit.ts delete mode 100644 spec/types/optional.ts delete mode 100644 spec/types/partial.ts delete mode 100644 spec/types/pick.ts delete mode 100644 spec/types/readonly-optional.ts delete mode 100644 spec/types/readonly.ts delete mode 100644 spec/types/rec.ts delete mode 100644 spec/types/record.ts delete mode 100644 spec/types/ref.ts delete mode 100644 spec/types/regex.ts delete mode 100644 spec/types/required.ts delete mode 100644 spec/types/spec.ts delete mode 100644 spec/types/strict.ts delete mode 100644 spec/types/string.ts delete mode 100644 spec/types/tuple.ts delete mode 100644 spec/types/typebox.d.ts delete mode 100644 spec/types/union.ts delete mode 100644 spec/types/unknown.ts create mode 100644 src/compiler/compiler.ts create mode 100644 src/compiler/index.ts create mode 100644 src/conditional/conditional.ts create mode 100644 src/conditional/index.ts create mode 100644 src/conditional/structural.ts create mode 100644 src/errors/errors.ts create mode 100644 src/errors/index.ts create mode 100644 src/format/format.ts create mode 100644 src/format/index.ts create mode 100644 src/guard/guard.ts create mode 100644 src/guard/index.ts create mode 100644 src/value/cast.ts create mode 100644 src/value/check.ts create mode 100644 src/value/clone.ts create mode 100644 src/value/create.ts create mode 100644 src/value/delta.ts create mode 100644 src/value/equal.ts create mode 100644 src/value/index.ts create mode 100644 src/value/is.ts create mode 100644 src/value/pointer.ts create mode 100644 src/value/value.ts create mode 100644 test/runtime/assert/assert.ts create mode 100644 test/runtime/assert/index.ts create mode 100644 test/runtime/compiler/any.ts create mode 100644 test/runtime/compiler/array.ts create mode 100644 test/runtime/compiler/boolean.ts create mode 100644 test/runtime/compiler/enum.ts rename {spec/schema => test/runtime/compiler}/index.ts (82%) create mode 100644 test/runtime/compiler/intersect.ts create mode 100644 test/runtime/compiler/keyof.ts create mode 100644 test/runtime/compiler/literal.ts create mode 100644 test/runtime/compiler/modifier.ts create mode 100644 test/runtime/compiler/never.ts create mode 100644 test/runtime/compiler/null.ts create mode 100644 test/runtime/compiler/number.ts create mode 100644 test/runtime/compiler/object.ts create mode 100644 test/runtime/compiler/omit.ts create mode 100644 test/runtime/compiler/optional.ts create mode 100644 test/runtime/compiler/partial.ts create mode 100644 test/runtime/compiler/pick.ts create mode 100644 test/runtime/compiler/readonly-optional.ts create mode 100644 test/runtime/compiler/readonly.ts create mode 100644 test/runtime/compiler/record.ts create mode 100644 test/runtime/compiler/recursive.ts create mode 100644 test/runtime/compiler/ref.ts create mode 100644 test/runtime/compiler/regex.ts create mode 100644 test/runtime/compiler/required.ts create mode 100644 test/runtime/compiler/string.ts create mode 100644 test/runtime/compiler/tuple.ts create mode 100644 test/runtime/compiler/uint8array.ts create mode 100644 test/runtime/compiler/union.ts create mode 100644 test/runtime/compiler/unknown.ts create mode 100644 test/runtime/compiler/validate.ts create mode 100644 test/runtime/compiler/void.ts create mode 100644 test/runtime/conditional/any.ts create mode 100644 test/runtime/conditional/array.ts create mode 100644 test/runtime/conditional/boolean.ts create mode 100644 test/runtime/conditional/constructor.ts create mode 100644 test/runtime/conditional/function.ts create mode 100644 test/runtime/conditional/index.ts create mode 100644 test/runtime/conditional/integer.ts create mode 100644 test/runtime/conditional/literal.ts create mode 100644 test/runtime/conditional/null.ts create mode 100644 test/runtime/conditional/number.ts create mode 100644 test/runtime/conditional/object.ts create mode 100644 test/runtime/conditional/promise.ts create mode 100644 test/runtime/conditional/record.ts create mode 100644 test/runtime/conditional/string.ts create mode 100644 test/runtime/conditional/tuple.ts create mode 100644 test/runtime/conditional/uint8array.ts create mode 100644 test/runtime/conditional/undefined.ts create mode 100644 test/runtime/conditional/union.ts create mode 100644 test/runtime/conditional/unknown.ts create mode 100644 test/runtime/conditional/void.ts create mode 100644 test/runtime/format/format.ts create mode 100644 test/runtime/format/index.ts create mode 100644 test/runtime/guard/any.ts create mode 100644 test/runtime/guard/array.ts create mode 100644 test/runtime/guard/boolean.ts create mode 100644 test/runtime/guard/constructor.ts create mode 100644 test/runtime/guard/function.ts create mode 100644 test/runtime/guard/index.ts create mode 100644 test/runtime/guard/integer.ts create mode 100644 test/runtime/guard/literal.ts create mode 100644 test/runtime/guard/null.ts create mode 100644 test/runtime/guard/number.ts create mode 100644 test/runtime/guard/object.ts create mode 100644 test/runtime/guard/promise.ts create mode 100644 test/runtime/guard/record.ts create mode 100644 test/runtime/guard/ref.ts create mode 100644 test/runtime/guard/self.ts create mode 100644 test/runtime/guard/string.ts create mode 100644 test/runtime/guard/tuple.ts create mode 100644 test/runtime/guard/uint8array.ts create mode 100644 test/runtime/guard/undefined.ts create mode 100644 test/runtime/guard/union.ts create mode 100644 test/runtime/guard/unknown.ts create mode 100644 test/runtime/guard/void.ts create mode 100644 test/runtime/index.ts create mode 100644 test/runtime/schema/any.ts create mode 100644 test/runtime/schema/array.ts create mode 100644 test/runtime/schema/boolean.ts create mode 100644 test/runtime/schema/enum.ts rename spec/types/typebox.test-d.ts => test/runtime/schema/index.ts (83%) create mode 100644 test/runtime/schema/intersect.ts create mode 100644 test/runtime/schema/keyof.ts create mode 100644 test/runtime/schema/literal.ts create mode 100644 test/runtime/schema/modifier.ts create mode 100644 test/runtime/schema/never.ts create mode 100644 test/runtime/schema/null.ts create mode 100644 test/runtime/schema/number.ts create mode 100644 test/runtime/schema/object.ts create mode 100644 test/runtime/schema/omit.ts create mode 100644 test/runtime/schema/optional.ts create mode 100644 test/runtime/schema/partial.ts create mode 100644 test/runtime/schema/pick.ts create mode 100644 test/runtime/schema/readonly-optional.ts create mode 100644 test/runtime/schema/readonly.ts create mode 100644 test/runtime/schema/record.ts create mode 100644 test/runtime/schema/recursive.ts create mode 100644 test/runtime/schema/ref.ts create mode 100644 test/runtime/schema/regex.ts create mode 100644 test/runtime/schema/required.ts create mode 100644 test/runtime/schema/string.ts create mode 100644 test/runtime/schema/tuple.ts create mode 100644 test/runtime/schema/union.ts create mode 100644 test/runtime/schema/unknown.ts create mode 100644 test/runtime/schema/unsafe.ts create mode 100644 test/runtime/schema/validate.ts create mode 100644 test/runtime/schema/void.ts create mode 100644 test/runtime/value/cast/any.ts create mode 100644 test/runtime/value/cast/array.ts create mode 100644 test/runtime/value/cast/boolean.ts create mode 100644 test/runtime/value/cast/convert.ts create mode 100644 test/runtime/value/cast/enum.ts create mode 100644 test/runtime/value/cast/index.ts create mode 100644 test/runtime/value/cast/integer.ts create mode 100644 test/runtime/value/cast/intersect.ts create mode 100644 test/runtime/value/cast/keyof.ts create mode 100644 test/runtime/value/cast/literal.ts create mode 100644 test/runtime/value/cast/namespace.ts create mode 100644 test/runtime/value/cast/never.ts create mode 100644 test/runtime/value/cast/null.ts create mode 100644 test/runtime/value/cast/number.ts create mode 100644 test/runtime/value/cast/object.ts create mode 100644 test/runtime/value/cast/rec.ts create mode 100644 test/runtime/value/cast/record.ts create mode 100644 test/runtime/value/cast/regex.ts create mode 100644 test/runtime/value/cast/string.ts create mode 100644 test/runtime/value/cast/tuple.ts create mode 100644 test/runtime/value/cast/uint8array.ts create mode 100644 test/runtime/value/cast/undefined.ts create mode 100644 test/runtime/value/cast/union.ts create mode 100644 test/runtime/value/cast/unknown.ts create mode 100644 test/runtime/value/cast/void.ts create mode 100644 test/runtime/value/check/any.ts create mode 100644 test/runtime/value/check/array.ts create mode 100644 test/runtime/value/check/boolean.ts create mode 100644 test/runtime/value/check/enum.ts create mode 100644 test/runtime/value/check/index.ts create mode 100644 test/runtime/value/check/integer.ts create mode 100644 test/runtime/value/check/intersect.ts create mode 100644 test/runtime/value/check/keyof.ts create mode 100644 test/runtime/value/check/literal.ts create mode 100644 test/runtime/value/check/never.ts create mode 100644 test/runtime/value/check/null.ts create mode 100644 test/runtime/value/check/number.ts create mode 100644 test/runtime/value/check/object.ts create mode 100644 test/runtime/value/check/rec.ts create mode 100644 test/runtime/value/check/record.ts create mode 100644 test/runtime/value/check/regex.ts create mode 100644 test/runtime/value/check/string.ts create mode 100644 test/runtime/value/check/tuple.ts create mode 100644 test/runtime/value/check/uint8array.ts create mode 100644 test/runtime/value/check/undefined.ts create mode 100644 test/runtime/value/check/union.ts create mode 100644 test/runtime/value/check/unknown.ts create mode 100644 test/runtime/value/check/void.ts create mode 100644 test/runtime/value/clone/clone.ts create mode 100644 test/runtime/value/clone/index.ts create mode 100644 test/runtime/value/create/any.ts create mode 100644 test/runtime/value/create/array.ts create mode 100644 test/runtime/value/create/boolean.ts create mode 100644 test/runtime/value/create/constructor.ts create mode 100644 test/runtime/value/create/enum.ts create mode 100644 test/runtime/value/create/function.ts create mode 100644 test/runtime/value/create/index.ts create mode 100644 test/runtime/value/create/integer.ts create mode 100644 test/runtime/value/create/intersect.ts create mode 100644 test/runtime/value/create/keyof.ts create mode 100644 test/runtime/value/create/literal.ts create mode 100644 test/runtime/value/create/never.ts create mode 100644 test/runtime/value/create/null.ts create mode 100644 test/runtime/value/create/number.ts create mode 100644 test/runtime/value/create/object.ts create mode 100644 test/runtime/value/create/rec.ts create mode 100644 test/runtime/value/create/record.ts create mode 100644 test/runtime/value/create/regex.ts create mode 100644 test/runtime/value/create/string.ts create mode 100644 test/runtime/value/create/tuple.ts create mode 100644 test/runtime/value/create/uint8array.ts create mode 100644 test/runtime/value/create/undefined.ts create mode 100644 test/runtime/value/create/union.ts create mode 100644 test/runtime/value/create/unknown.ts create mode 100644 test/runtime/value/create/void.ts create mode 100644 test/runtime/value/delta/diff.ts create mode 100644 test/runtime/value/delta/index.ts create mode 100644 test/runtime/value/delta/patch.ts create mode 100644 test/runtime/value/equal/equal.ts create mode 100644 test/runtime/value/equal/index.ts create mode 100644 test/runtime/value/index.ts create mode 100644 test/runtime/value/pointer/index.ts create mode 100644 test/runtime/value/pointer/pointer.ts create mode 100644 test/static/any.ts create mode 100644 test/static/array.ts create mode 100644 test/static/assert.ts create mode 100644 test/static/boolean.ts create mode 100644 test/static/enum.ts create mode 100644 test/static/index.ts create mode 100644 test/static/intersect.ts create mode 100644 test/static/keyof.ts create mode 100644 test/static/literal.ts create mode 100644 test/static/modifier.ts create mode 100644 test/static/never.ts create mode 100644 test/static/null.ts create mode 100644 test/static/number.ts create mode 100644 test/static/object.ts create mode 100644 test/static/omit.ts create mode 100644 test/static/optional.ts create mode 100644 test/static/partial.ts create mode 100644 test/static/pick.ts create mode 100644 test/static/readonly-optional.ts create mode 100644 test/static/readonly.ts create mode 100644 test/static/record.ts create mode 100644 test/static/recursive.ts create mode 100644 test/static/ref.ts create mode 100644 test/static/regex.ts create mode 100644 test/static/required.ts create mode 100644 test/static/return-type.ts create mode 100644 test/static/strict.ts create mode 100644 test/static/string.ts create mode 100644 test/static/tsconfig.json create mode 100644 test/static/tuple.ts create mode 100644 test/static/union.ts create mode 100644 test/static/unknown.ts diff --git a/.github/.keep b/.github/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4604aa7a8..d64a14027 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,24 +3,21 @@ name: GitHub CI on: [push, pull_request] jobs: - npm: + TypeBox: runs-on: ${{ matrix.os }} strategy: matrix: - node-version: [14.x] + node: [14.x, 16.x, 18.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v2 - - name: Use Node.js + - name: Install Node uses: actions/setup-node@v1 with: - node-version: ${{ matrix.node-version }} - - name: Install with npm - run: | - npm install - - name: Build - run: | - npm run build - - name: Run tests - run: | - npm run test + node-version: ${{ matrix.node }} + - name: Install Packages + run: npm install + - name: Build Library + run: npm run build + - name: Test Library + run: npm run test diff --git a/.gitignore b/.gitignore index 46029743a..2c085d1d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ node_modules target -index.js -spec.js - diff --git a/.vscode/settings.json b/.vscode/settings.json index e4c4348f5..a9232115d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,8 @@ { "files.exclude": { "node_modules": true, - "package-lock.json": true, - "target": false - } + "package-lock.json": true + }, + "typescript.tsdk": "node_modules\\typescript\\lib", + "editor.suggest.showStatusBar": false } \ No newline at end of file diff --git a/benchmark/compression/index.ts b/benchmark/compression/index.ts new file mode 100644 index 000000000..9454d15ce --- /dev/null +++ b/benchmark/compression/index.ts @@ -0,0 +1,25 @@ +import { shell } from '@sinclair/hammer' +import { statSync, readdirSync } from 'fs' +import { basename, extname } from 'path' + +export async function measure(test: string) { + await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression`) + const compiled = statSync(`target/benchmark/compression/${test}.js`) + await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`) + const minified = statSync(`target/benchmark/compression/${test}.js`) + return { + test: test.padEnd(20), + compiled: `${Math.floor(compiled.size / 1000)} kb`.padStart(8), + minified: `${Math.floor(minified.size / 1000)} kb`.padStart(8), + ratio: compiled.size / minified.size, + } +} + +export async function compression() { + const tests = readdirSync('benchmark/compression/module').map((name) => basename(name, extname(name))) + const results = await Promise.all(tests.map((test) => measure(test))) + const present = results.reduce((acc, c) => { + return { ...acc, [c.test.replace('-', '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } } + }, {}) + console.table(present) +} diff --git a/benchmark/compression/module/typebox-compiler.ts b/benchmark/compression/module/typebox-compiler.ts new file mode 100644 index 000000000..97a54ba07 --- /dev/null +++ b/benchmark/compression/module/typebox-compiler.ts @@ -0,0 +1,4 @@ +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Type } from '@sinclair/typebox' + +const T = TypeCompiler.Compile(Type.String()) diff --git a/benchmark/compression/module/typebox-conditional.ts b/benchmark/compression/module/typebox-conditional.ts new file mode 100644 index 000000000..3a12edfa2 --- /dev/null +++ b/benchmark/compression/module/typebox-conditional.ts @@ -0,0 +1,4 @@ +import { Conditional } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' + +const T = Conditional.Extends(Type.String(), Type.String(), Type.String(), Type.String()) diff --git a/benchmark/compression/module/typebox-format.ts b/benchmark/compression/module/typebox-format.ts new file mode 100644 index 000000000..360a2ce85 --- /dev/null +++ b/benchmark/compression/module/typebox-format.ts @@ -0,0 +1,4 @@ +import { Format } from 'src/format' +import { Type } from '@sinclair/typebox' + +Format.Set('custom', (value) => true) diff --git a/benchmark/compression/module/typebox-guard.ts b/benchmark/compression/module/typebox-guard.ts new file mode 100644 index 000000000..9c453c2d5 --- /dev/null +++ b/benchmark/compression/module/typebox-guard.ts @@ -0,0 +1,4 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' + +const T = TypeGuard.TSchema(Type.String()) diff --git a/benchmark/compression/module/typebox-value.ts b/benchmark/compression/module/typebox-value.ts new file mode 100644 index 000000000..f616f8256 --- /dev/null +++ b/benchmark/compression/module/typebox-value.ts @@ -0,0 +1,4 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +const T = Value.Create(Type.String()) diff --git a/benchmark/compression/module/typebox.ts b/benchmark/compression/module/typebox.ts new file mode 100644 index 000000000..0b01566e5 --- /dev/null +++ b/benchmark/compression/module/typebox.ts @@ -0,0 +1,3 @@ +import { Type } from '@sinclair/typebox' + +const T = Type.String() diff --git a/benchmark/index.ts b/benchmark/index.ts new file mode 100644 index 000000000..db1450ec6 --- /dev/null +++ b/benchmark/index.ts @@ -0,0 +1,2 @@ +export * from './compression/index' +export * from './measurement/index' diff --git a/benchmark/measurement/index.ts b/benchmark/measurement/index.ts new file mode 100644 index 000000000..6175663af --- /dev/null +++ b/benchmark/measurement/index.ts @@ -0,0 +1,5 @@ +import { shell } from '@sinclair/hammer' + +export async function measurement() { + await shell(`hammer run benchmark/measurement/module/index.ts --dist target/benchmark/measurement`) +} diff --git a/benchmark/measurement/module/benchmark.ts b/benchmark/measurement/module/benchmark.ts new file mode 100644 index 000000000..c4b4de381 --- /dev/null +++ b/benchmark/measurement/module/benchmark.ts @@ -0,0 +1,7 @@ +export namespace Benchmark { + export function Measure(execute: Function, iterations: number = 16_000_000) { + const start = Date.now() + for (let i = 0; i < iterations; i++) execute() + return { iterations, completed: Date.now() - start } + } +} diff --git a/benchmark/measurement/module/cases.ts b/benchmark/measurement/module/cases.ts new file mode 100644 index 000000000..fb88ceb36 --- /dev/null +++ b/benchmark/measurement/module/cases.ts @@ -0,0 +1,114 @@ +import { Type } from '@sinclair/typebox' + +export namespace Cases { + export const Number = Type.Number() + + export const String = Type.String() + + export const Boolean = Type.Boolean() + + export const Null = Type.Null() + + export const RegEx = Type.RegEx(/foo/, { default: 'foo' }) + + export const ObjectA = Type.Object({ + p0: Type.String(), + p1: Type.Number(), + p2: Type.Number(), + p3: Type.Array(Type.Number(), { minItems: 4 }), + p4: Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + p5: Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + }), + }) + + export const ObjectB = Type.Object(ObjectA.properties, { + additionalProperties: false, + }) + + export const Tuple = Type.Tuple([Type.String(), Type.Number(), Type.Boolean()]) + + export const Union = Type.Union([Type.Object({ x: Type.Number(), y: Type.Number() }), Type.Object({ a: Type.String(), b: Type.String() })], { default: { a: 'a', b: 'b' } }) + + export const Recursive = Type.Recursive( + (Recursive) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Recursive), + }), + { + default: { + id: '', + nodes: [ + { + id: '', + nodes: [ + { id: '', nodes: [] }, + { id: '', nodes: [] }, + { id: '', nodes: [] }, + ], + }, + { + id: '', + nodes: [ + { id: '', nodes: [] }, + { id: '', nodes: [] }, + { id: '', nodes: [] }, + ], + }, + { + id: '', + nodes: [ + { id: '', nodes: [] }, + { id: '', nodes: [] }, + { id: '', nodes: [] }, + ], + }, + ], + }, + }, + ) + + export const Vector4 = Type.Tuple([Type.Number(), Type.Number(), Type.Number(), Type.Number()]) + + export const Matrix4 = Type.Array(Type.Array(Type.Number()), { + default: [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ], + }) + + export const Literal_String = Type.Literal('foo') + + export const Literal_Number = Type.Literal(1) + + export const Literal_Boolean = Type.Literal(true) + + export const Array_Number = Type.Array(Type.Number(), { minItems: 16 }) + + export const Array_String = Type.Array(Type.String(), { minItems: 16 }) + + export const Array_Boolean = Type.Array(Type.Boolean(), { minItems: 16 }) + + export const Array_ObjectA = Type.Array(ObjectA, { minItems: 16 }) + + export const Array_ObjectB = Type.Array(ObjectB, { minItems: 16 }) + + export const Array_Tuple = Type.Array(Tuple, { minItems: 16 }) + + export const Array_Union = Type.Array(Union, { minItems: 16 }) + + export const Array_Recursive = Type.Array(Recursive, { minItems: 16 }) + + export const Array_Vector4 = Type.Array(Vector4, { minItems: 16 }) + + export const Array_Matrix4 = Type.Array(Matrix4, { minItems: 16 }) +} diff --git a/benchmark/measurement/module/check.ts b/benchmark/measurement/module/check.ts new file mode 100644 index 000000000..3b8f975a4 --- /dev/null +++ b/benchmark/measurement/module/check.ts @@ -0,0 +1,42 @@ +import { Cases } from './cases' +import { Benchmark } from './benchmark' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { TypeGuard } from '@sinclair/typebox/guard' +import { TSchema } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +import Ajv from 'ajv' + +const ajv = new Ajv() // ensure single instance + +export namespace CheckBenchmark { + function Measure(type: string, schema: T) { + console.log('CheckBenchmark.Measure(', type, ')') + + const iterations = 1_000_000 + const V = Value.Create(schema) + + const AC = ajv.compile(schema) + const A = Benchmark.Measure(() => { + if (!AC(V)) throw Error() + }, iterations) + + const CC = TypeCompiler.Compile(schema) + const T = Benchmark.Measure(() => { + if (!CC.Check(V)) throw Error() + }, iterations) + + const VC = Benchmark.Measure(() => { + if (!Value.Check(schema, V)) throw Error() + }, iterations) + + return { type, ajv: A, compiler: T, value: VC } + } + + export function* Execute() { + for (const [type, schema] of Object.entries(Cases)) { + if (!TypeGuard.TSchema(schema)) throw Error('Invalid TypeBox schema') + yield Measure(type, schema) + } + } +} diff --git a/benchmark/measurement/module/compile.ts b/benchmark/measurement/module/compile.ts new file mode 100644 index 000000000..1dd039601 --- /dev/null +++ b/benchmark/measurement/module/compile.ts @@ -0,0 +1,36 @@ +import { Cases } from './cases' +import { Benchmark } from './benchmark' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { TypeGuard } from '@sinclair/typebox/guard' +import { TSchema } from '@sinclair/typebox' +import Ajv from 'ajv' + +const ajv = new Ajv() // ensure single instance + +export namespace CompileBenchmark { + function Measure(type: string, schema: T) { + const iterations = 2000 + console.log('CompileBenchmark.Measure(', type, ')') + // ------------------------------------------------------------------------------- + // Note: Ajv caches schemas by reference. To ensure we measure actual + // compilation times, we must pass a new reference via { ...schema } + // ------------------------------------------------------------------------------- + const AC = Benchmark.Measure(() => ajv.compile({ ...schema }), iterations) + const CC = Benchmark.Measure(() => TypeCompiler.Compile({ ...schema }), iterations) + return { type, ajv: AC, compiler: CC } + } + + export function* Execute() { + for (const [type, schema] of Object.entries(Cases)) { + if (!TypeGuard.TSchema(schema)) throw Error('Invalid TypeBox schema') + // ------------------------------------------------------------------------------- + // Note: it is not possible to benchmark recursive schemas as ajv will cache and + // track duplicate $id (resulting in compile error). It is not possible to ammend + // recursive $id's without potentially biasing results, so we omit on this case. + // ------------------------------------------------------------------------------- + if (type === 'Recursive' || type === 'Array_Recursive') continue + + yield Measure(type, schema) + } + } +} diff --git a/benchmark/measurement/module/index.ts b/benchmark/measurement/module/index.ts new file mode 100644 index 000000000..5b6d43480 --- /dev/null +++ b/benchmark/measurement/module/index.ts @@ -0,0 +1,36 @@ +import { CompileBenchmark } from './compile' +import { CheckBenchmark } from './check' +import { Result } from './result' + +export function present(results: Result[]) { + console.table( + results.reduce((acc, result) => { + const ratio = result.ajv.completed / result.compiler.completed + if (result.value) { + return { + ...acc, + [result.type.padStart(16, ' ')]: { + Iterations: result.compiler.iterations, + ValueCheck: `${result.value.completed} ms`.padStart(10), + Ajv: `${result.ajv.completed} ms`.padStart(10), + TypeCompiler: `${result.compiler.completed} ms`.padStart(10), + Performance: `${ratio.toFixed(2)} x`.padStart(10, ' '), + }, + } + } else { + return { + ...acc, + [result.type.padStart(16, ' ')]: { + Iterations: result.compiler.iterations, + Ajv: `${result.ajv.completed} ms`.padStart(10), + TypeCompiler: `${result.compiler.completed} ms`.padStart(10), + Performance: `${ratio.toFixed(2)} x`.padStart(10, ' '), + }, + } + } + }, {}), + ) +} + +present([...CompileBenchmark.Execute()]) +present([...CheckBenchmark.Execute()]) diff --git a/benchmark/measurement/module/result.ts b/benchmark/measurement/module/result.ts new file mode 100644 index 000000000..c74a1aa5d --- /dev/null +++ b/benchmark/measurement/module/result.ts @@ -0,0 +1,15 @@ +export type Result = { + type: string + ajv: { + iterations: number + completed: number + } + compiler: { + iterations: number + completed: number + } + value?: { + iterations: number + completed: number + } +} diff --git a/changelog.md b/changelog.md index fcd52a1ef..7c8020b0c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,99 @@ +## [0.24.44](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.44) + +Updates: +- [189](https://github.com/sinclairzx81/typebox/pull/189) Both `Value.Error(T, value)` and `TypeCheck.Error(value)` now return an iterator for validation errors. +- [191](https://github.com/sinclairzx81/typebox/pull/191) TypeBox now provides a `TypeGuard` API that can be used to check the structural validity of TypeBox type. The TypeGuard can be used in reflection / code generation scenarios to resolve the appropriate inner `TSchema` type while traversing a outer type. +- [197](https://github.com/sinclairzx81/typebox/pull/197) TypeBox now implements conditional runtime type mapping. This functionality is offered as seperate import for the `0.24.0` release but may be provided as standard type in later releases. This API enables `type T = Foo extends Bar ? true : false` conditional checks to be implemented at runtime. This API also provides the `Exclude` and `Extract` utility types which are implemented through conditional types in TypeScript. +- [199](https://github.com/sinclairzx81/typebox/pull/199) TypeBox now provides better support for varidiac function and constructor signatures. Currently Variadics are mapped through `Tuple` types. +- [200](https://github.com/sinclairzx81/typebox/pull/200) The types `TPick` and `TOmit` now support types of `TUnion[]>` to be used to select properties. Additionally, `KeyOf` now returns `TUnion[]>`, allowing `KeyOf` schemas to be passed to `TPick` and `TOmit`. +- [214](https://github.com/sinclairzx81/typebox/pull/214) TypeBox now provides better support for i18n. To achieve this, TypeBox includes fixed mappable error codes on the `ValueError` type. These codes can be used by external implementors to create localized error messages. TypeBox may include localized error codes as an optional import in future releases. +- [288](https://github.com/sinclairzx81/typebox/pull/228) TypeBox now allows users to implement custom string validator formats. These formats are internally shared between the `Value` and `TypeCompiler` API's. TypeBox does not currently provide any built in formats, however the standard expected set (email, uuid, uri, etc) may be provided via optional import (inline with ajv-formats usage) +- [229](https://github.com/sinclairzx81/typebox/pull/229) The `Value.Cast()` function now implements automatic coersion of string, number and boolean types. +- [231](https://github.com/sinclairzx81/typebox/pull/231) TypeBox provides a new `Value.Diff()` and `Value.Patch()` utility API for JavaScript values. This API is intended to provide a basis for the efficient transmission of state updates across a network. This API can diff any JavaScript value (typed or untyped) but is recommended to be used in conjunction with a formal static type. +- [236](https://github.com/sinclairzx81/typebox/pull/236) TypeBox now implements the `TNever` type. This type is analogous to TypeScript's `never` type and is used in instances a composition results in a non-reconcilable type. Currently this type is implemented for empty `TUnion<[]>` types only. Future releases may utilize this type for planned updates to `TIntersect` (for example `string & number` resolves to `never`) +- [241](https://github.com/sinclairzx81/typebox/pull/241) [247](https://github.com/sinclairzx81/typebox/pull/247) TypeBox now exposes a ValuePointer API that can be used to mutate a value via an RFC6901 JSON Pointer. Previously this functionality was internally used by `Value.Diff()` and `Value.Patch()` functions but is now offered as an optional import for implementations that need to update values manually through pointer references. + +Additional: + +- This project now includes two reference code generation utilities that can be used in custom build tooling. The first is `TypeScriptCodeGen` which will remap TypeScript `interface` and `type` definitions to TypeBox types. The second is `TypeBoxCodeGen` which will map existing TypeBox types into TypeScript type definitions. These implementations are not expected to be part of the TypeBox package, but users are free to clone and enhance them in their existing tool chains. Reference implementations can be found https://github.com/sinclairzx81/typebox/tree/master/codegen + + + +## [0.24.15](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.15) + +Added: +- `Conditional.Extends(...)` This enables TypeBox to conditionally map types inline with TypeScripts structural equivelence checks. Tested against TypeScript 4.7.4. +- `Conditional.Extract(...)` Which analogs TypeScripts `Extract<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) +- `Conditional.Exclude(...)` Which analogs TypeScripts `Exclude<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) +- `Type.Parameters(...)` Returns the parameters of a `TFunction` as a `TTuple` +- `Type.ReturnType(...)` Returns the return type schema of a `TFunction` +- `Type.ConstructorParameters(...)` Returns the parameters of a `TConstructor` as a `TTuple` +- `Type.InstanceType(...)` Returns the instance type schema of a `TConstructor` + +## [0.24.8](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.8) + +Added: +- `Value.Cast(T, value)` structurally casts a value into another form while retaining information within the original value. +- `Value.Check(T, value)` provides slow dynamic type checking for values. For performance, one should consider the `TypeCompiler` or `Ajv` validator. +- `Value.Errors(T, value)` returns an iterable iterator errors found in a given value. + +## [0.24.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.6) + +Added: + +- TypeBox now offers a `TypeGuard` module for structurally checking TypeBox schematics. This module can be used in runtime type reflection scenarios where it's helpful to test a schema is of a particular form. This module can be imported under the `@sinclair/typebox/guard` import path. + +Example: + +```typescript +import { TypeGuard } from '@sinclair/typebox/guard' + +const T: any = {} // T is any + +const { type } = T // unsafe: type is any + +if(TypeGuard.TString(T)) { + + const { type } = T // safe: type is 'string' +} + +``` + +## [0.24.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.0) + +Changes: + +- The `kind` and `modifier` keywords are now expressed as symbol keys. This change allows AJV to leverage TypeBox schemas directly without explicit configuration of `kind` and `modifier` in strict mode. +- `Type.Intersect([...])` now returns a composite `TObject` instead of a `allOf` schema representation. This change allows intersected types to be leveraged in calls to `Omit`, `Pick`, `Partial`, `Required`. +- `Type.Void(...)` now generates a `{ type: null }` schema representation. This is principally used for RPC implementations where a RPC target function needs to respond with a serializable value for `void` return. +- `Type.Rec(...)` renamed to `Type.Recursive(...)` and now supports non-mutual recursive type inference. + +Added: + +- `Type.Unsafe(...)`. This type enables custom schema representations whose static type is informed by generic type T. +- `Type.Uint8Array(...)`. This is a non-standard schema that can be configured on AJV to enable binary buffer range validation. +- Added optional extended `design` property on all schema options. This property can be used to specify design time metadata when rendering forms. + +Compiler: + +- TypeBox now provides an optional experimental type compiler that can be used to validate types without AJV. This compiler is not a standard JSON schema compiler and will only compile TypeBox's known schema representations. For full JSON schema validation, AJV should still be the preference. This compiler is a work in progress. + +Value: + +- TypeBox now provides a value generator that can generate default values from TypeBox types. + +Breaking Changes: + +- `Type.Intersect(...)` is constrained to accept types of `TObject` only. +- `Type.Namespace(...)` has been removed. +- The types `TUnion`, `TEnum`, `KeyOf` and `TLiteral[]` are all now expressed via `allOf`. For Open API users, Please consider `Type.Unsafe()` to express `enum` string union representations. Documentation on using `Type.Unsafe()` can be found [here](https://github.com/sinclairzx81/typebox#Unsafe-Types) + +## [0.23.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.3) + +Updates: + +- Fix: Rename BoxKind to NamespaceKind + ## [0.23.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.1) Updates: @@ -240,4 +336,4 @@ const T = Type.Object({ b: Type.Number() }, { additionalProperties: false -}) \ No newline at end of file +}) diff --git a/codegen/codegen.ts b/codegen/codegen.ts new file mode 100644 index 000000000..ebbdab70d --- /dev/null +++ b/codegen/codegen.ts @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema } from '@sinclair/typebox' +import { TypeBoxCodegen } from './typebox' +import { TypeScriptCodeGen } from './typescript' + +export namespace CodeGen { + /** Generates TypeScript type definitions from TypeBox types */ + export function TypeScript(schema: TSchema, references: TSchema[] = []): string { + return TypeScriptCodeGen.Generate(schema, references) + } + + /** Generates TypeBox type definitions from TypeScript code */ + export function TypeBox(code: string): string { + return TypeBoxCodegen.Generate(code) + } +} diff --git a/codegen/index.ts b/codegen/index.ts new file mode 100644 index 000000000..d72a0bbe4 --- /dev/null +++ b/codegen/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './codegen' diff --git a/codegen/tsconfig.json b/codegen/tsconfig.json new file mode 100644 index 000000000..9f4a7e200 --- /dev/null +++ b/codegen/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "files": ["index.ts"] +} diff --git a/codegen/typebox.ts b/codegen/typebox.ts new file mode 100644 index 000000000..d18637bd2 --- /dev/null +++ b/codegen/typebox.ts @@ -0,0 +1,364 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ts from 'typescript' + +/** Generates TypeBox types from TypeScript interface and type definitions */ +export namespace TypeBoxCodegen { + function isReadonlyProperty(node: ts.PropertySignature): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined + } + + function isOptionalProperty(node: ts.PropertySignature) { + return node.questionToken !== undefined + } + + function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined + } + + function* SourceFile(node: ts.SourceFile): IterableIterator { + for (const next of node.getChildren()) { + yield* Visit(next) + } + } + + function* PropertySignature(node: ts.PropertySignature): IterableIterator { + const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] + const type = Collect(node.type) + if (readonly && optional) { + return yield `${node.name.getText()}: Type.ReadonlyOptional(${type})` + } else if (readonly) { + return yield `${node.name.getText()}: Type.Readonly(${type})` + } else if (optional) { + return yield `${node.name.getText()}: Type.Optional(${type})` + } else { + return yield `${node.name.getText()}: ${type}` + } + } + + function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { + const type = Collect(node.elementType) + yield `Type.Array(${type})` + } + + function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { + const types = node.elements.map((type) => Collect(type)).join(',\n') + yield `Type.Tuple([\n${types}\n])` + } + + function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { + const types = node.types.map((type) => Collect(type)).join(',\n') + yield `Type.Union([\n${types}\n])` + } + + function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { + const types = node.types.map((type) => Collect(type)).join(',\n') + yield `Type.Intersect([\n${types}\n])` + } + + function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { + if (node.operator === ts.SyntaxKind.KeyOfKeyword) { + const type = Collect(node.type) + yield `Type.KeyOf(${type})` + } + } + + function* Parameter(node: ts.ParameterDeclaration): IterableIterator { + yield Collect(node.type) + } + + function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { + const parameters = node.parameters.map((param) => Collect(param)).join(', ') + const returns = Collect(node.type) + yield `Type.Function([${parameters}], ${returns})` + } + + function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { + const parameters = node.parameters.map((param) => Collect(param)).join(', ') + const returns = Collect(node.type) + yield `Type.Constructor([${parameters}], ${returns})` + } + + function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { + const exports = isExport(node) ? 'export ' : '' + const name = node.name.getText() + const members = node.members.map((member) => member.getText()).join(', ') + const enumType = `${exports}enum ${name}Enum { ${members} }` + const type = `${exports}const ${name} = Type.Enum(${name}Enum)` + yield [enumType, '', type].join('\n') + } + + function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { + useImports = true + if (node.typeParameters) { + useGenerics = true + const exports = isExport(node) ? 'export ' : '' + const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') + const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') + const names = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const members = node.members.map((member) => Collect(member)).join(',\n') + const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` + const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Object({\n${members}\n})` + yield `${staticDeclaration}\n${typeDeclaration}` + } else { + const exports = isExport(node) ? 'export ' : '' + const members = node.members.map((member) => Collect(member)).join(',\n') + const staticDeclaration = `${exports}type ${node.name.getText()} = Static` + const typeDeclaration = `${exports}const ${node.name.getText()} = Type.Object({\n${members}\n})` + yield `${staticDeclaration}\n${typeDeclaration}` + } + } + + function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { + useImports = true + if (node.typeParameters) { + useGenerics = true + const exports = isExport(node) ? 'export ' : '' + const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') + const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') + const names = node.typeParameters.map((param) => Collect(param)).join(', ') + const type = Collect(node.type) + const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` + const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${type}` + yield `${staticDeclaration}\n${typeDeclaration}` + } else { + const exports = isExport(node) ? 'export ' : '' + const type = Collect(node.type) + const staticDeclaration = `${exports}type ${node.name.getText()} = Static` + const typeDeclaration = `${exports}const ${node.name.getText()} = ${type}` + yield `${staticDeclaration}\n${typeDeclaration}` + } + } + + function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { + yield node.name.getText() + } + + function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { + yield Collect(node.type) + } + + function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { + yield `Type.Rest()` + } + + function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { + useConditional = true + const checkType = Collect(node.checkType) + const extendsType = Collect(node.extendsType) + const trueType = Collect(node.trueType) + const falseType = Collect(node.falseType) + yield `Conditional.Extends(${checkType}, ${extendsType}, ${trueType}, ${falseType})` + } + + function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { + const name = node.typeName.getText() + const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' + if (name === 'Array') { + return yield `Type.Array${args}` + } else if (name === 'Record') { + return yield `Type.Record${args}` + } else if (name === 'Partial') { + return yield `Type.Partial${args}` + } else if (name === 'Uint8Array') { + return yield `Type.Uint8Array()` + } else if (name === 'Required') { + return yield `Type.Required${args}` + } else if (name === 'Omit') { + return yield `Type.Omit${args}` + } else if (name === 'Pick') { + return yield `Type.Pick${args}` + } else if (name === 'Promise') { + return yield `Type.Promise${args}` + } else if (name === 'ReturnType') { + return yield `Type.ReturnType${args}` + } else if (name === 'InstanceType') { + return yield `Type.InstanceType${args}` + } else if (name === 'Parameters') { + return yield `Type.Parameters${args}` + } else if (name === 'ConstructorParameters') { + return yield `Type.ConstructorParameters${args}` + } else if (name === 'Exclude') { + return yield `Conditional.Exclude${args}` + } else if (name === 'Extract') { + return yield `Conditional.Extract${args}` + } else { + return yield `${name}${args}` + } + } + + function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { + const members = node.members.map((member) => Collect(member)).join(',\n') + yield `Type.Object({\n${members}\n})` + } + + function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { + const text = node.getText() + if (text === 'null') return yield `Type.Null()` + yield `Type.Literal(${node.getText()})` + } + + function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { + yield node.getText() + } + + function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { + yield node.getText() + } + + function Collect(node: ts.Node | undefined): string { + return `${[...Visit(node)].join('')}` + } + + function CollectNewLine(node: ts.Node | undefined): string { + return [...Visit(node)].join('\n\n') + } + + function* Visit(node: ts.Node | undefined): IterableIterator { + if (node === undefined) return + if (ts.isSourceFile(node)) { + return yield* SourceFile(node) + } else if (ts.isInterfaceDeclaration(node)) { + return yield* InterfaceDeclaration(node) + } else if (ts.isTypeAliasDeclaration(node)) { + return yield* TypeAliasDeclaration(node) + } else if (ts.isParameter(node)) { + return yield* Parameter(node) + } else if (ts.isFunctionTypeNode(node)) { + return yield* FunctionTypeNode(node) + } else if (ts.isConstructorTypeNode(node)) { + return yield* ConstructorTypeNode(node) + } else if (ts.isEnumDeclaration(node)) { + return yield* EnumDeclaration(node) + } else if (ts.isPropertySignature(node)) { + return yield* PropertySignature(node) + } else if (ts.isTypeReferenceNode(node)) { + return yield* TypeReferenceNode(node) + } else if (ts.isTypeLiteralNode(node)) { + return yield* TypeLiteralNode(node) + } else if (ts.isLiteralTypeNode(node)) { + return yield* LiteralTypeNode(node) + } else if (ts.isArrayTypeNode(node)) { + return yield* ArrayTypeNode(node) + } else if (ts.isTupleTypeNode(node)) { + return yield* TupleTypeNode(node) + } else if (ts.isIntersectionTypeNode(node)) { + return yield* IntersectionTypeNode(node) + } else if (ts.isUnionTypeNode(node)) { + return yield* UnionTypeNode(node) + } else if (ts.isTypeOperatorNode(node)) { + return yield* TypeOperatorNode(node) + } else if (ts.isTypeParameterDeclaration(node)) { + return yield* TypeParameterDeclaration(node) + } else if (ts.isParenthesizedTypeNode(node)) { + return yield* ParenthesizedTypeNode(node) + } else if (ts.isRestTypeNode(node)) { + return yield* RestTypeNode(node) + } else if (ts.isFunctionDeclaration(node)) { + return yield* FunctionDeclaration(node) + } else if (ts.isClassDeclaration(node)) { + return yield* ClassDeclaration(node) + } else if (ts.isConditionalTypeNode(node)) { + return yield* ConditionalTypeNode(node) + } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { + return yield `Type.KeyOf()` + } else if (node.kind === ts.SyntaxKind.NumberKeyword) { + return yield `Type.Number()` + } else if (node.kind === ts.SyntaxKind.StringKeyword) { + return yield `Type.String()` + } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { + return yield `Type.Boolean()` + } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { + return yield `Type.Undefined()` + } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { + return yield `Type.Unknown()` + } else if (node.kind === ts.SyntaxKind.AnyKeyword) { + return yield `Type.Any()` + } else if (node.kind === ts.SyntaxKind.NeverKeyword) { + return yield `Type.Never()` + } else if (node.kind === ts.SyntaxKind.NullKeyword) { + return yield `Type.Null()` + } else if (node.kind === ts.SyntaxKind.VoidKeyword) { + return yield `Type.Void()` + } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { + return + } else if (node.kind === ts.SyntaxKind.SyntaxList) { + for (const child of node.getChildren()) { + yield* Visit(child) + } + return + } else { + console.log('Unhandled:', ts.SyntaxKind[node.kind]) + return yield node.getText() + } + } + + function Format(input: string): string { + function count(line: string, opens: string[]) { + const codes = opens.map((open) => open.charCodeAt(0)) + return line + .split('') + .map((char) => char.charCodeAt(0)) + .reduce((acc, current) => { + return codes.includes(current) ? acc + 1 : acc + }, 0) + } + let indent = 0 + const output: string[] = [] + for (const line of input.split('\n').map((n) => n.trim())) { + indent -= count(line, ['}', ']']) + output.push(`${''.padStart(indent * 2, ' ')}${line}`) + indent += count(line, ['{', '[']) + } + return output.join('\n') + } + + let useImports = false + let useConditional = false + let useGenerics = false + + /** Generates TypeBox types from TypeScript interface and type definitions */ + export function Generate(typescriptCode: string) { + useImports = false + useConditional = false + useGenerics = false + + const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) + const typeDeclarations = CollectNewLine(source) + const importStatments: string[] = [] + if (useImports) { + if (useConditional) importStatments.push(`import { Conditional } from '@sinclair/typebox/conditional'`) + if (useGenerics) importStatments.push(`import { Type, Static, TSchema } from '@sinclair/typebox'`) + if (!useGenerics) importStatments.push(`import { Type, Static } from '@sinclair/typebox'`) + } + const imports = importStatments.join('\n') + const types = Format(typeDeclarations) + return [imports, '', types].join('\n') + } +} diff --git a/codegen/typescript.ts b/codegen/typescript.ts new file mode 100644 index 000000000..6087ece23 --- /dev/null +++ b/codegen/typescript.ts @@ -0,0 +1,225 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeGuard } from '@sinclair/typebox/guard' +import * as Types from '@sinclair/typebox' + +export namespace TypeScriptCodeGen { + function Any(schema: Types.TAny) { + return 'any' + } + + function Array(schema: Types.TArray) { + const items = Visit(schema.items) + return `Array<${items}>` + } + + function Boolean(schema: Types.TBoolean) { + return 'boolean' + } + + function Constructor(schema: Types.TConstructor) { + const params = schema.parameters.map((param) => Visit(param)).join(', ') + const returns = Visit(schema.returns) + return `new (${params}) => ${returns}` + } + + function Function(schema: Types.TFunction) { + const params = schema.parameters.map((param) => Visit(param)).join(', ') + const returns = Visit(schema.returns) + return `(${params}) => ${returns}` + } + + function Integer(schema: Types.TInteger) { + return 'number' + } + + function Literal(schema: Types.TLiteral) { + if (typeof schema.const === 'string') { + return `'${schema.const}'` + } else { + return `${schema.const}` + } + } + + function Never(schema: Types.TNever) { + return 'never' + } + + function Null(schema: Types.TNull) { + return 'null' + } + + function String(schema: Types.TString) { + return 'string' + } + + function Number(schema: Types.TNumber) { + return 'number' + } + + function Object(schema: Types.TObject) { + const properties: string = globalThis.Object.entries(schema.properties) + .map(([key, value]) => { + return `${key}: ${Visit(value)}` + }) + .join(',\n') + return `{\n${properties}\n}` + } + + function Promise(schema: Types.TPromise) { + const item = Visit(schema.item) + return `Promise<${item}>` + } + + function Record(schema: Types.TRecord) { + for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { + const type = Visit(value) + if (key === '^(0|[1-9][0-9]*)$') { + return `Record` + } else { + return `Record` + } + } + throw Error('TypeScriptCodeGen: Unreachable') + } + + function Ref(schema: Types.TRef) { + return schema.$ref + } + + function Self(schema: Types.TSelf) { + return schema.$ref + } + + function Tuple(schema: Types.TTuple) { + if (schema.items === undefined) return `[]` + const items = schema.items.map((schema) => Visit(schema)).join(', ') + return `[${items}]` + } + + function UInt8Array(schema: Types.TUint8Array) { + return `Uint8Array` + } + + function Undefined(schema: Types.TUndefined) { + return `undefined` + } + + function Union(schema: Types.TUnion) { + return schema.anyOf.map((schema) => Visit(schema)).join(' | ') + } + + function Unknown(schema: Types.TUnknown) { + return `unknown` + } + + function Void(schema: Types.TVoid) { + return `void` + } + + function Visit(schema: Types.TSchema): string { + if (TypeGuard.TAny(schema)) { + return Any(schema) + } else if (TypeGuard.TArray(schema)) { + return Array(schema) + } else if (TypeGuard.TBoolean(schema)) { + return Boolean(schema) + } else if (TypeGuard.TConstructor(schema)) { + return Constructor(schema) + } else if (TypeGuard.TFunction(schema)) { + return Function(schema) + } else if (TypeGuard.TInteger(schema)) { + return Integer(schema) + } else if (TypeGuard.TLiteral(schema)) { + return Literal(schema) + } else if (TypeGuard.TNever(schema)) { + return Never(schema) + } else if (TypeGuard.TNull(schema)) { + return Null(schema) + } else if (TypeGuard.TNumber(schema)) { + return Number(schema) + } else if (TypeGuard.TObject(schema)) { + return Object(schema) + } else if (TypeGuard.TPromise(schema)) { + return Promise(schema) + } else if (TypeGuard.TRecord(schema)) { + return Record(schema) + } else if (TypeGuard.TRef(schema)) { + return Ref(schema) + } else if (TypeGuard.TSelf(schema)) { + return Self(schema) + } else if (TypeGuard.TString(schema)) { + return String(schema) + } else if (TypeGuard.TTuple(schema)) { + return Tuple(schema) + } else if (TypeGuard.TUint8Array(schema)) { + return UInt8Array(schema) + } else if (TypeGuard.TUndefined(schema)) { + return Undefined(schema) + } else if (TypeGuard.TUnion(schema)) { + return Union(schema) + } else if (TypeGuard.TUnknown(schema)) { + return Unknown(schema) + } else if (TypeGuard.TVoid(schema)) { + return Void(schema) + } else { + throw Error('TypeScriptCodeGen: Unknown type') + } + } + + function Format(input: string): string { + function count(line: string, opens: string[]) { + const codes = opens.map((open) => open.charCodeAt(0)) + return line + .split('') + .map((char) => char.charCodeAt(0)) + .reduce((acc, current) => { + return codes.includes(current) ? acc + 1 : acc + }, 0) + } + let indent = 0 + const output: string[] = [] + for (const line of input.split('\n').map((n) => n.trim())) { + indent -= count(line, ['}']) + output.push(`${''.padStart(indent * 2, ' ')}${line}`) + indent += count(line, ['{']) + } + return output.join('\n') + } + + /** Generates TypeScript code from TypeBox types */ + export function Generate(schema: Types.TSchema, references: Types.TSchema[] = []) { + const result: string[] = [] + for (const reference of references) { + result.push(`type ${reference.$id} = ${Format([...Visit(reference)].join(''))}`) + } + result.push(`type ${schema.$id || 'T'} = ${Format([...Visit(schema)].join(''))}`) + return result.join('\n\n') + } +} diff --git a/example/index.ts b/example/index.ts index 7f3a03024..1d6e207c4 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,16 +1,18 @@ +import { CodeGen } from '@sinclair/typebox/codegen' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Conditional } from '@sinclair/typebox/conditional' +import { TypeGuard } from '@sinclair/typebox/guard' +import { Format } from '@sinclair/typebox/format' +import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Static } from '@sinclair/typebox' -const T = Type.Object({ - x: Type.String(), - y: Type.Number(), - z: Type.String() -}, { $id: 'T' }) - -const R = Type.Ref(T) -const K = Type.KeyOf(R) - -type T = Static +const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}) -console.log(K) +type T = Static +console.log(T) \ No newline at end of file diff --git a/hammer.mjs b/hammer.mjs index f46ddc1b6..34b412fca 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -1,3 +1,7 @@ +import { compression, measurement } from './benchmark' +import { readFileSync } from 'fs' + + // ------------------------------------------------------------------------------- // Clean // ------------------------------------------------------------------------------- @@ -7,29 +11,46 @@ export async function clean() { } // ------------------------------------------------------------------------------- -// Specs +// Format // ------------------------------------------------------------------------------- -export async function spec_types() { - await shell(`tsc -p ./src/tsconfig.json --outDir spec/types --emitDeclarationOnly`) - await shell(`tsd spec/types`) +export async function format() { + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test benchmark codegen') } -export async function spec_schemas() { - await shell(`hammer build ./spec/schema/index.ts --dist target/spec/schema --platform node`) - await shell(`mocha target/spec/schema/index.js`) +// ------------------------------------------------------------------------------- +// Start +// ------------------------------------------------------------------------------- + +export async function start(example = 'index') { + await shell(`hammer run example/${example}.ts --dist target/example/${example}`) } -export async function spec() { - await spec_types() - await spec_schemas() +// ------------------------------------------------------------------------------- +// Benchmark +// ------------------------------------------------------------------------------- + +export async function benchmark() { + await compression() + await measurement() } // ------------------------------------------------------------------------------- -// Example +// Test // ------------------------------------------------------------------------------- -export async function example(target = 'target/example') { - await shell(`hammer run example/index.ts --dist ${target}`) + +export async function test_static() { + await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) +} + +export async function test_runtime(filter) { + await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) + await shell(`mocha target/test/runtime/index.js -g "${filter}"`) +} + +export async function test(filter = '') { + await test_static() + await test_runtime(filter) } // ------------------------------------------------------------------------------- @@ -37,15 +58,23 @@ export async function example(target = 'target/example') { // ------------------------------------------------------------------------------- export async function build(target = 'target/build') { - await spec() + await test() await folder(target).delete() await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) await folder(target).add('package.json') await folder(target).add('readme.md') await folder(target).add('license') await shell(`cd ${target} && npm pack`) - - // $ npm publish sinclair-typebox-0.x.x.tgz --access=public - // $ git tag - // $ git push origin } + + +// ------------------------------------------------------------- +// Publish +// ------------------------------------------------------------- + +export async function publish(otp, target = 'target/build') { + const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) + await shell(`git tag ${version}`) + await shell(`git push origin ${version}`) +} \ No newline at end of file diff --git a/license b/license index 2e5d87302..cd929d327 100644 --- a/license +++ b/license @@ -2,7 +2,7 @@ TypeBox: JSON Schema Type Builder with Static Type Resolution for TypeScript The MIT License (MIT) -Copyright (c) 2021 Haydn Paterson (sinclair) +Copyright (c) 2022 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package-lock.json b/package-lock.json index 2a7201ff4..0754cb51b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,239 +1,71 @@ { "name": "@sinclair/typebox", - "version": "0.22.0", + "version": "0.24.39", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.22.0", + "version": "0.24.39", "license": "MIT", "devDependencies": { - "@sinclair/hammer": "^0.15.8", - "@types/chai": "^4.2.22", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.9", - "ajv": "^8.8.2", + "@sinclair/hammer": "^0.17.1", + "@types/chai": "^4.3.3", + "@types/mocha": "^9.1.1", + "@types/node": "^18.7.13", + "ajv": "^8.11.0", "ajv-formats": "^2.1.1", - "chai": "^4.3.4", - "mocha": "^9.1.3", - "tsd": "^0.19.0", - "typescript": "^4.5.2" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.16.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "chai": "^4.3.6", + "mocha": "^9.2.2", + "prettier": "^2.7.1", + "typescript": "^4.8.2" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz", + "integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==", + "cpu": [ + "loong64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, "node_modules/@sinclair/hammer": { - "version": "0.15.8", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.15.8.tgz", - "integrity": "sha512-XM6SOQkF8HusX6nCZrlwpU1O+QtjNyhvrF0B6loZReJAuAZJJuTd4/fFDErCrp8uGauG8alYNXcpEOmQRvLARw==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.1.tgz", + "integrity": "sha512-/M7KXwCaXdDmBJXQHpn1NWSTUg7Rmx6vzseVOax7IXhsBYj3vEGU5yt/Xh3XdRLVOEcyl4vGWgJ0pL9cmyrTJA==", "dev": true, "dependencies": { - "esbuild": "^0.12.24" + "@sinclair/hammer": "^0.17.0", + "esbuild": "^0.14.53" }, "bin": { "hammer": "hammer" } }, - "node_modules/@tsd/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-K778wcPuAsJ9Ch0/FlhQcaIMFEi+TfxNi5NSUbgZd3RucaktYUpR++1Ox2mW2G25oyxWb8gfgg+JUhulRbI6eQ==", - "dev": true, - "bin": { - "tsc": "typescript/bin/tsc", - "tsserver": "typescript/bin/tsserver" - } - }, "node_modules/@types/chai": { - "version": "4.2.22", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", - "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==", - "dev": true - }, - "node_modules/@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "node_modules/@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", "dev": true }, "node_modules/@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "node_modules/@types/node": { - "version": "16.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", - "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "version": "18.7.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", + "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", "dev": true }, "node_modules/@ungap/promise-all-settled": { @@ -243,9 +75,9 @@ "dev": true }, "node_modules/ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -284,21 +116,6 @@ "node": ">=6" } }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -342,24 +159,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -413,9 +212,9 @@ "dev": true }, "node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" @@ -424,42 +223,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-keys/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" }, @@ -498,17 +272,23 @@ "node_modules/check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true, "engines": { "node": "*" } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -557,13 +337,13 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -595,37 +375,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -647,138 +396,410 @@ "node": ">=0.3.1" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/esbuild": { - "version": "0.12.29", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", - "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz", + "integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==", "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.53", + "esbuild-android-64": "0.14.53", + "esbuild-android-arm64": "0.14.53", + "esbuild-darwin-64": "0.14.53", + "esbuild-darwin-arm64": "0.14.53", + "esbuild-freebsd-64": "0.14.53", + "esbuild-freebsd-arm64": "0.14.53", + "esbuild-linux-32": "0.14.53", + "esbuild-linux-64": "0.14.53", + "esbuild-linux-arm": "0.14.53", + "esbuild-linux-arm64": "0.14.53", + "esbuild-linux-mips64le": "0.14.53", + "esbuild-linux-ppc64le": "0.14.53", + "esbuild-linux-riscv64": "0.14.53", + "esbuild-linux-s390x": "0.14.53", + "esbuild-netbsd-64": "0.14.53", + "esbuild-openbsd-64": "0.14.53", + "esbuild-sunos-64": "0.14.53", + "esbuild-windows-32": "0.14.53", + "esbuild-windows-64": "0.14.53", + "esbuild-windows-arm64": "0.14.53" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz", + "integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/esbuild-android-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz", + "integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/esbuild-darwin-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz", + "integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/eslint-formatter-pretty": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz", - "integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==", + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz", + "integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/eslint": "^7.2.13", - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "eslint-rule-docs": "^1.1.5", - "log-symbols": "^4.0.0", - "plur": "^4.0.0", - "string-width": "^4.2.0", - "supports-hyperlinks": "^2.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/eslint-rule-docs": { - "version": "1.1.231", - "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.231.tgz", - "integrity": "sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA==", - "dev": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "node_modules/esbuild-freebsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz", + "integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz", + "integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/esbuild-linux-32": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz", + "integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==", + "cpu": [ + "ia32" + ], "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "node_modules/esbuild-linux-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz", + "integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz", + "integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz", + "integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz", + "integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz", + "integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz", + "integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz", + "integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz", + "integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz", + "integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz", + "integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz", + "integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz", + "integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz", + "integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { @@ -804,7 +825,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { @@ -821,12 +842,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -839,16 +854,16 @@ "node_modules/get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true, "engines": { "node": "*" } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -877,24 +892,16 @@ "node": ">= 6" } }, - "node_modules/globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, "node_modules/growl": { @@ -906,27 +913,6 @@ "node": ">=4.x" } }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -945,40 +931,10 @@ "he": "bin/he" } }, - "node_modules/hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -991,21 +947,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "node_modules/irregular-plurals": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", - "integrity": "sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1018,22 +959,10 @@ "node": ">=8" } }, - "node_modules/is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1049,9 +978,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { "is-extglob": "^2.1.1" @@ -1093,13 +1022,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/js-yaml": { @@ -1114,33 +1037,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1172,170 +1074,54 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" + "get-func-name": "^2.0.0" } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { - "node": "*" - } - }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -1359,9 +1145,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -1370,21 +1156,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1397,7 +1168,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -1433,33 +1204,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1472,27 +1216,12 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -1503,9 +1232,9 @@ } }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" @@ -1514,19 +1243,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/plur": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", - "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true, - "dependencies": { - "irregular-plurals": "^3.2.0" + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/punycode": { @@ -1538,35 +1267,6 @@ "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1576,135 +1276,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1717,81 +1288,22 @@ "node": ">=8.10.0" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { - "iojs": ">=1.0.0", "node": ">=0.10.0" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "engines": { + "node": ">=0.10.0" } }, "node_modules/safe-buffer": { @@ -1814,21 +1326,6 @@ } ] }, - "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -1838,47 +1335,6 @@ "randombytes": "^2.1.0" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1905,18 +1361,6 @@ "node": ">=8" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1944,31 +1388,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1981,35 +1400,6 @@ "node": ">=8.0" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsd": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.19.0.tgz", - "integrity": "sha512-DlYCjjRVspfSAC+9X1NUDqPj0PtsVjUSyC4/OJpCGnY2Mtg0ddc+opckmnLV3RhUBpVuAAOrz661v+QSJwINkQ==", - "dev": true, - "dependencies": { - "@tsd/typescript": "~4.5.2", - "eslint-formatter-pretty": "^4.1.0", - "globby": "^11.0.1", - "meow": "^9.0.0", - "path-exists": "^4.0.0", - "read-pkg-up": "^7.0.0" - }, - "bin": { - "tsd": "dist/cli.js" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -2019,22 +1409,10 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2053,16 +1431,6 @@ "punycode": "^2.1.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2079,9 +1447,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -2104,7 +1472,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/y18n": { @@ -2116,12 +1484,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -2178,181 +1540,39 @@ } }, "dependencies": { - "@babel/code-frame": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", - "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.16.0" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.15.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", - "dev": true - }, - "@babel/highlight": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", - "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.15.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "@esbuild/linux-loong64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz", + "integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==", "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } + "optional": true }, "@sinclair/hammer": { - "version": "0.15.8", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.15.8.tgz", - "integrity": "sha512-XM6SOQkF8HusX6nCZrlwpU1O+QtjNyhvrF0B6loZReJAuAZJJuTd4/fFDErCrp8uGauG8alYNXcpEOmQRvLARw==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.1.tgz", + "integrity": "sha512-/M7KXwCaXdDmBJXQHpn1NWSTUg7Rmx6vzseVOax7IXhsBYj3vEGU5yt/Xh3XdRLVOEcyl4vGWgJ0pL9cmyrTJA==", "dev": true, "requires": { - "esbuild": "^0.12.24" + "@sinclair/hammer": "^0.17.0", + "esbuild": "^0.14.53" } }, - "@tsd/typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-K778wcPuAsJ9Ch0/FlhQcaIMFEi+TfxNi5NSUbgZd3RucaktYUpR++1Ox2mW2G25oyxWb8gfgg+JUhulRbI6eQ==", - "dev": true - }, "@types/chai": { - "version": "4.2.22", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", - "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==", - "dev": true - }, - "@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", + "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", "dev": true }, "@types/mocha": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", - "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", "dev": true }, "@types/node": { - "version": "16.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", - "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "version": "18.7.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", + "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", "dev": true }, "@ungap/promise-all-settled": { @@ -2362,9 +1582,9 @@ "dev": true }, "ajv": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.8.2.tgz", - "integrity": "sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -2388,15 +1608,6 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2428,18 +1639,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2484,40 +1683,22 @@ "dev": true }, "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" } @@ -2546,13 +1727,13 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -2594,13 +1775,13 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "requires": { "ms": "2.1.2" @@ -2620,30 +1801,6 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, - "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - } - } - }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -2659,35 +1816,180 @@ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "esbuild": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz", + "integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==", + "dev": true, + "requires": { + "@esbuild/linux-loong64": "0.14.53", + "esbuild-android-64": "0.14.53", + "esbuild-android-arm64": "0.14.53", + "esbuild-darwin-64": "0.14.53", + "esbuild-darwin-arm64": "0.14.53", + "esbuild-freebsd-64": "0.14.53", + "esbuild-freebsd-arm64": "0.14.53", + "esbuild-linux-32": "0.14.53", + "esbuild-linux-64": "0.14.53", + "esbuild-linux-arm": "0.14.53", + "esbuild-linux-arm64": "0.14.53", + "esbuild-linux-mips64le": "0.14.53", + "esbuild-linux-ppc64le": "0.14.53", + "esbuild-linux-riscv64": "0.14.53", + "esbuild-linux-s390x": "0.14.53", + "esbuild-netbsd-64": "0.14.53", + "esbuild-openbsd-64": "0.14.53", + "esbuild-sunos-64": "0.14.53", + "esbuild-windows-32": "0.14.53", + "esbuild-windows-64": "0.14.53", + "esbuild-windows-arm64": "0.14.53" + } + }, + "esbuild-android-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz", + "integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz", + "integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz", + "integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz", + "integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz", + "integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz", + "integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz", + "integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz", + "integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz", + "integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz", + "integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz", + "integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz", + "integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz", + "integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz", + "integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz", + "integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz", + "integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz", + "integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==", "dev": true, - "requires": { - "path-type": "^4.0.0" - } + "optional": true }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "esbuild-windows-32": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz", + "integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==", + "dev": true, + "optional": true }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "esbuild-windows-64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz", + "integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } + "optional": true }, - "esbuild": { - "version": "0.12.29", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz", - "integrity": "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g==", - "dev": true + "esbuild-windows-arm64": { + "version": "0.14.53", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz", + "integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==", + "dev": true, + "optional": true }, "escalade": { "version": "3.1.1", @@ -2701,56 +2003,12 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "eslint-formatter-pretty": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz", - "integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==", - "dev": true, - "requires": { - "@types/eslint": "^7.2.13", - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "eslint-rule-docs": "^1.1.5", - "log-symbols": "^4.0.0", - "plur": "^4.0.0", - "string-width": "^4.2.0", - "supports-hyperlinks": "^2.0.0" - } - }, - "eslint-rule-docs": { - "version": "1.1.231", - "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.231.tgz", - "integrity": "sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA==", - "dev": true - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2779,7 +2037,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { @@ -2789,12 +2047,6 @@ "dev": true, "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2804,13 +2056,13 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -2819,6 +2071,17 @@ "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "glob-parent": { @@ -2830,41 +2093,12 @@ "is-glob": "^4.0.1" } }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2877,31 +2111,10 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "hosted-git-info": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz", - "integrity": "sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -2914,18 +2127,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "irregular-plurals": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", - "integrity": "sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2935,19 +2136,10 @@ "binary-extensions": "^2.0.0" } }, - "is-core-module": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", - "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -2957,9 +2149,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -2986,13 +2178,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "js-yaml": { @@ -3004,30 +2190,12 @@ "argparse": "^2.0.1" } }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3047,132 +2215,51 @@ "is-unicode-supported": "^0.1.0" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "meow": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", - "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize": "^1.2.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - } - } - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", "dev": true, "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "get-func-name": "^2.0.0" } }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "dependencies": { - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - } - } - }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -3185,23 +2272,11 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3211,7 +2286,7 @@ "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -3235,24 +2310,6 @@ "p-limit": "^3.0.2" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3262,19 +2319,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "pathval": { @@ -3284,19 +2329,16 @@ "dev": true }, "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "plur": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", - "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", - "dev": true, - "requires": { - "irregular-plurals": "^3.2.0" - } + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true }, "punycode": { "version": "2.1.1", @@ -3304,18 +2346,6 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3325,106 +2355,6 @@ "safe-buffer": "^5.1.0" } }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3434,20 +2364,10 @@ "picomatch": "^2.2.1" } }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-from-string": { @@ -3456,46 +2376,12 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -3505,44 +2391,6 @@ "randombytes": "^2.1.0" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", - "dev": true - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3563,15 +2411,6 @@ "ansi-regex": "^5.0.1" } }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3587,27 +2426,6 @@ "has-flag": "^4.0.0" } }, - "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3617,42 +2435,16 @@ "is-number": "^7.0.0" } }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "tsd": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.19.0.tgz", - "integrity": "sha512-DlYCjjRVspfSAC+9X1NUDqPj0PtsVjUSyC4/OJpCGnY2Mtg0ddc+opckmnLV3RhUBpVuAAOrz661v+QSJwINkQ==", - "dev": true, - "requires": { - "@tsd/typescript": "~4.5.2", - "eslint-formatter-pretty": "^4.1.0", - "globby": "^11.0.1", - "meow": "^9.0.0", - "path-exists": "^4.0.0", - "read-pkg-up": "^7.0.0" - } - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, "typescript": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", - "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", "dev": true }, "uri-js": { @@ -3664,16 +2456,6 @@ "punycode": "^2.1.0" } }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3684,9 +2466,9 @@ } }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { @@ -3703,7 +2485,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "y18n": { @@ -3712,12 +2494,6 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index cef6d5b2f..44cc0bab1 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.23.2", + "version": "0.24.44", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ - "json-schema", "typescript", - "static-types", - "runtime-typechecking" + "json-schema", + "validate", + "typecheck" ], "author": "sinclairzx81", "license": "MIT", @@ -18,23 +18,23 @@ }, "scripts": { "clean": "hammer task clean", + "format": "hammer task format", + "start": "hammer task start", + "test": "hammer task test", + "benchmark": "hammer task benchmark", "build": "hammer task build", - "example": "hammer task example", - "spec": "hammer task spec", - "spec:types": "hammer task spec_types", - "spec:schemas": "hammer task spec_schemas", - "test": "npm run spec" + "publish": "hammer task publish" }, "devDependencies": { - "@sinclair/hammer": "^0.15.8", - "@types/chai": "^4.2.22", - "@types/mocha": "^9.0.0", - "@types/node": "^16.11.9", - "ajv": "^8.8.2", + "@sinclair/hammer": "^0.17.1", + "@types/chai": "^4.3.3", + "@types/mocha": "^9.1.1", + "@types/node": "^18.7.13", + "ajv": "^8.11.0", "ajv-formats": "^2.1.1", - "chai": "^4.3.4", - "mocha": "^9.1.3", - "tsd": "^0.19.0", - "typescript": "^4.5.2" + "chai": "^4.3.6", + "mocha": "^9.2.2", + "prettier": "^2.7.1", + "typescript": "^4.8.2" } } diff --git a/readme.md b/readme.md index 083da1b54..6d77e6a33 100644 --- a/readme.md +++ b/readme.md @@ -2,13 +2,16 @@

TypeBox

- -

JSON Schema Type Builder with Static Type Resolution for TypeScript

+ + +
+
- -[![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) +[![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) +[![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) +[![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) @@ -16,16 +19,16 @@ ## Install -#### Node +Node ```bash $ npm install @sinclair/typebox --save ``` -#### Deno +Deno and ESM ```typescript -import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts' +import { Static, Type } from 'https://esm.sh/@sinclair/typebox' ``` ## Example @@ -33,7 +36,7 @@ import { Static, Type } from 'https://deno.land/x/typebox/src/typebox.ts' ```typescript import { Static, Type } from '@sinclair/typebox' -const T = Type.String() // const T = { "type": "string" } +const T = Type.String() // const T = { type: 'string' } type T = Static // type T = string ``` @@ -42,28 +45,47 @@ type T = Static // type T = string ## Overview -TypeBox is a 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 create a unified type that can be both statically asserted by the TypeScript compiler and runtime asserted using standard JSON Schema validation. - -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. TypeBox does not provide any JSON schema validation. Please use libraries such as AJV to validate schemas built with this library. +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 a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. -Requires TypeScript 4.3.5 and above. +TypeBox is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used either as a simple tool to build up complex schemas or integrated into REST and RPC services to help validate data received over the wire. License MIT ## Contents -- [Install](#Install) -- [Overview](#Overview) -- [Usage](#Usage) -- [Types](#Types) -- [Modifiers](#Modifiers) -- [Options](#Options) -- [Generic Types](#Generic-Types) -- [Reference Types](#Reference-Types) -- [Recursive Types](#Recursive-Types) -- [Extended Types](#Extended-Types) -- [Strict](#Strict) -- [Validation](#Validation) -- [OpenAPI](#OpenAPI) +- [Install](#install) +- [Overview](#overview) +- [Usage](#usage) +- [Types](#types) + - [Standard](#types-standard) + - [Modifiers](#types-modifiers) + - [Options](#types-options) + - [Extended](#types-extended) + - [Reference](#types-reference) + - [Recursive](#types-recursive) + - [Generic](#types-generic) + - [Conditional](#types-conditional) + - [Unsafe](#types-unsafe) + - [Guards](#types-guards) + - [Strict](#types-strict) +- [Values](#values) + - [Create](#values-create) + - [Clone](#values-clone) + - [Check](#values-check) + - [Cast](#values-cast) + - [Equal](#values-equal) + - [Diff](#values-diff) + - [Patch](#values-patch) + - [Errors](#values-errors) + - [Pointer](#values-pointer) +- [TypeCheck](#typecheck) + - [Ajv](#typecheck-ajv) + - [Compiler](#typecheck-compiler) + - [Formats](#typecheck-formats) +- [Benchmark](#benchmark) + - [Compile](#benchmark-compile) + - [Validate](#benchmark-validate) + - [Compression](#benchmark-compression) +- [Contribute](#contribute)
@@ -82,9 +104,9 @@ import { Static, Type } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- type T = { - id: string, - name: string, - timestamp: number + id: string, + name: string, + timestamp: number } //-------------------------------------------------------------------------------------------- @@ -93,25 +115,25 @@ type T = { // //-------------------------------------------------------------------------------------------- -const T = Type.Object({ // const T = { - id: Type.String(), // type: 'object', - name: Type.String(), // properties: { - timestamp: Type.Integer() // id: { -}) // type: 'string' - // }, - // name: { - // type: 'string' - // }, - // timestamp: { - // type: 'integer' - // } - // }, - // required: [ - // "id", - // "name", - // "timestamp" - // ] - // } +const T = Type.Object({ // const T = { + id: Type.String(), // type: 'object', + name: Type.String(), // properties: { + timestamp: Type.Integer() // id: { +}) // type: 'string' + // }, + // name: { + // type: 'string' + // }, + // timestamp: { + // type: 'integer' + // } + // }, + // required: [ + // 'id', + // 'name', + // 'timestamp' + // ] + // } //-------------------------------------------------------------------------------------------- // @@ -119,11 +141,11 @@ const T = Type.Object({ // const T = { // //-------------------------------------------------------------------------------------------- -type T = Static // type T = { - // id: string, - // name: string, - // timestamp: number - // } +type T = Static // type T = { + // id: string, + // name: string, + // timestamp: number + // } //-------------------------------------------------------------------------------------------- // @@ -131,20 +153,26 @@ type T = Static // type T = { // //-------------------------------------------------------------------------------------------- -function receive(value: T) { // ... as a Type +function receive(value: T) { // ... as a Type - if(JSON.validate(T, value)) { // ... as a Schema - - // ok... - } + if(JSON.validate(T, value)) { // ... as a Schema + + // ok... + } } ``` - + ## Types -The following table outlines the TypeBox mappings between TypeScript and JSON schema. +TypeBox provides a set of functions that allow you to compose JSON Schema similar to how you would compose static types with TypeScript. Each function creates a JSON schema fragment which can compose into more complex types. The schemas produced by TypeBox can be passed directly to any JSON Schema compliant validator, or used to reflect runtime metadata for a type. + + + +### Standard + +The following table lists the standard TypeBox types. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -158,22 +186,22 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.String() │ type T = string │ const T = { │ -│ │ │ type: 'string' │ +│ │ │ type: 'string' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Number() │ type T = number │ const T = { │ -│ │ │ type: 'number' │ +│ │ │ type: 'number' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Integer() │ type T = number │ const T = { │ -│ │ │ type: 'integer' │ +│ │ │ type: 'integer' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Boolean() │ type T = boolean │ const T = { │ -│ │ │ type: 'boolean' │ +│ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -189,22 +217,22 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Literal(42) │ type T = 42 │ const T = { │ -│ │ │ const: 42 │ +│ │ │ const: 42, │ │ │ │ type: 'number' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Array( │ type T = number[] │ const T = { │ -│ Type.Number() │ │ type: 'array', │ -│ ) │ │ items: { │ -│ │ │ type: 'number' │ -│ │ │ } │ +│ Type.Number() │ │ type: 'array', │ +│ ) │ │ items: { │ +│ │ │ type: 'number' │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ x: Type.Number(), │ x: number, │ type: 'object', │ -│ y: Type.Number() │ y: number │ properties: { │ +│ x: Type.Number(), │ x: number, │ type: 'object', │ +│ y: Type.Number() │ y: number │ properties: { │ │ }) │ } │ x: { │ │ │ │ type: 'number' │ │ │ │ }, │ @@ -217,19 +245,18 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │ -│ Type.Number(), │ │ type: 'array', │ -│ Type.Number() │ │ items: [ │ -│ ]) │ │ { │ -│ │ │ type: 'number' │ -│ │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ ], │ +│ Type.Number(), │ │ type: 'array', │ +│ Type.Number() │ │ items: [{ │ +│ ]) │ │ type: 'number' │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ }], │ │ │ │ additionalItems: false, │ │ │ │ minItems: 2, │ -│ │ │ maxItems: 2, │ +│ │ │ maxItems: 2 │ │ │ │ } │ │ │ │ │ +│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ enum Foo { │ enum Foo { │ const T = { │ │ A, │ A, │ anyOf: [{ │ @@ -243,85 +270,92 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.KeyOf( │ type T = keyof { │ const T = { │ -│ Type.Object({ │ x: number, │ enum: ['x', 'y'], │ -│ x: Type.Number(), │ y: number │ type: 'string' │ -│ y: Type.Number() │ } │ } │ -│ }) │ │ │ -│ ) │ │ │ +│ Type.Object({ │ x: number, │ anyOf: [{ │ +│ x: Type.Number(), │ y: number │ type: 'string', │ +│ y: Type.Number() │ } │ const: 'x' │ +│ }) │ │ }, { │ +│ ) │ │ type: 'string', │ +│ │ │ const: 'y' │ +│ │ │ }] │ +│ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Union([ │ type T = string | number │ const T = { │ -│ Type.String(), │ │ anyOf: [{ │ -│ Type.Number() │ │ type: 'string' │ -│ ]) │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ }] │ +│ Type.String(), │ │ anyOf: [{ │ +│ Type.Number() │ │ type: 'string' │ +│ ]) │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ }] │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Intersect([ │ type T = { │ const T = { │ -│ Type.Object({ │ x: number │ allOf: [{ │ -│ x: Type.Number() │ } & { │ type: 'object', │ -│ }), │ y: number │ properties: { │ -│ Type.Object({ │ } │ x: { │ -│ y: Type.Number() │ │ type: 'number' │ -│ }) │ │ } │ -│ }) │ │ }, │ -│ │ │ required: ['x'] │ -│ │ │ }, { │ -│ │ │ type: 'object', │ -│ │ │ properties: { │ -│ │ │ y: { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ }, │ -│ │ │ required: ['y'] │ -│ │ │ }] │ +│ Type.Object({ │ x: number │ type: 'object', │ +│ x: Type.Number() │ } & { │ properties: { │ +│ }), │ y: number │ x: { │ +│ Type.Object({ │ } │ type: 'number' │ +│ y: Type.Number() │ │ }, │ +│ }) │ │ y: { │ +│ ]) │ │ type: 'number' │ +│ │ │ } │ +│ │ │ }, │ +│ │ │ required: ['x', 'y'] │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Record( │ type T = { │ const T = { │ -│ Type.String(), │ [key: string]: number │ type: 'object', │ -│ Type.Number() │ } │ patternProperties: { │ -│ ) │ │ '^.*$': { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ } │ +│ const T = Type.Never() │ type T = never │ const T = { │ +│ │ │ allOf: [{ │ +│ │ │ type: 'boolean', │ +│ │ │ const: false │ +│ │ │ }, { │ +│ │ │ type: 'boolean', │ +│ │ │ const: true │ +│ │ │ }] │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Record( │ type T = Record< │ const T = { │ +│ Type.String(), │ string, │ type: 'object', │ +│ Type.Number() │ number, │ patternProperties: { │ +│ ) │ > │ '^.*$': { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Partial( │ type T = Partial<{ │ const T = { │ -│ Type.Object({ │ x: number, │ type: 'object', │ -│ x: Type.Number(), │ y: number │ properties: { │ -│ y: Type.Number() | }> │ x: { │ -│ }) │ │ type: 'number' │ +│ Type.Object({ │ x: number, │ type: 'object', │ +│ x: Type.Number(), │ y: number │ properties: { │ +│ y: Type.Number() | }> │ x: { │ +│ }) │ │ type: 'number' │ │ ) │ │ }, │ │ │ │ y: { │ -│ │ │ type: 'number' │ +│ │ │ type: 'number' │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Required( │ type T = Required<{ │ const T = { │ -│ Type.Object({ │ x?: number, │ type: 'object', │ -│ x: Type.Optional( │ y?: number │ properties: { │ -│ Type.Number() | }> │ x: { │ -│ ), │ │ type: 'number' │ -│ y: Type.Optional( │ │ }, │ -│ Type.Number() │ │ y: { │ -│ ) │ │ type: 'number' │ -│ }) │ │ } │ +│ Type.Object({ │ x?: number, │ type: 'object', │ +│ x: Type.Optional( │ y?: number │ properties: { │ +│ Type.Number() | }> │ x: { │ +│ ), │ │ type: 'number' │ +│ y: Type.Optional( │ │ }, │ +│ Type.Number() │ │ y: { │ +│ ) │ │ type: 'number' │ +│ }) │ │ } │ │ ) │ │ }, │ │ │ │ required: ['x', 'y'] │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │ -│ Type.Object({ │ x: number, │ type: 'object', │ -│ x: Type.Number(), │ y: number │ properties: { │ -│ y: Type.Number(), | }, 'x'> │ x: { │ -│ }), ['x'] │ │ type: 'number' │ +│ Type.Object({ │ x: number, │ type: 'object', │ +│ x: Type.Number(), │ y: number │ properties: { │ +│ y: Type.Number() | }, 'x'> │ x: { │ +│ }), ['x'] │ │ type: 'number' │ │ ) │ │ } │ │ │ │ }, │ │ │ │ required: ['x'] │ @@ -329,10 +363,10 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Omit( │ type T = Omit<{ │ const T = { │ -│ Type.Object({ │ x: number, │ type: 'object', │ -│ x: Type.Number(), │ y: number │ properties: { │ -│ y: Type.Number(), | }, 'x'> │ y: { │ -│ }), ['x'] │ │ type: 'number' │ +│ Type.Object({ │ x: number, │ type: 'object', │ +│ x: Type.Number(), │ y: number │ properties: { │ +│ y: Type.Number() | }, 'x'> │ y: { │ +│ }), ['x'] │ │ type: 'number' │ │ ) │ │ } │ │ │ │ }, │ │ │ │ required: ['y'] │ @@ -340,7 +374,8 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - + + ### Modifiers @@ -352,8 +387,8 @@ TypeBox provides modifiers that can be applied to an objects properties. This al │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.Optional( │ name?: string, │ type: 'object', │ -│ Type.String(), │ } │ properties: { │ +│ name: Type.Optional( │ name?: string │ type: 'object', │ +│ Type.String() │ } │ properties: { │ │ ) │ │ name: { │ │ }) │ │ type: 'string' │ │ │ │ } │ @@ -362,29 +397,29 @@ TypeBox provides modifiers that can be applied to an objects properties. This al │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.Readonly( │ readonly name: string, │ type: 'object', │ -│ Type.String(), │ } │ properties: { │ -│ ) │ │ name: { │ -│ }) │ │ type: 'string' │ -│ │ │ } │ +│ name: Type.Readonly( │ readonly name: string │ type: 'object', │ +│ Type.String() │ } │ properties: { │ +│ ) │ │ name: { │ +│ }) │ │ type: 'string' │ +│ │ │ } │ │ │ │ }, │ │ │ │ required: ['name'] │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.ReadonlyOptional( │ readonly name?: string, │ type: 'object', │ -│ Type.String(), │ } │ properties: { │ -│ ) │ │ name: { │ -│ }) │ │ type: 'string' │ -│ │ │ } │ +│ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │ +│ Type.String() │ } │ properties: { │ +│ ) │ │ name: { │ +│ }) │ │ type: 'string' │ +│ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - + ### Options @@ -400,148 +435,12 @@ const T = Type.Number({ multipleOf: 2 }) // array must have at least 5 integer values const T = Type.Array(Type.Integer(), { minItems: 5 }) ``` - - -### Generic Types - -Generic types can be created using functions. The following creates a generic `Nullable` type. - -```typescript -import { Type, Static, TSchema } from '@sinclair/typebox' - -// type Nullable = T | null - -const Nullable = (type: T) => Type.Union([type, Type.Null()]) - -const T = Nullable(Type.String()) // const T = { - // "anyOf": [{ - // type: 'string' - // }, { - // type: 'null' - // }] - // } - -type T = Static // type T = string | null - -const U = Nullable(Type.Number()) // const U = { - // "anyOf": [{ - // type: 'number' - // }, { - // type: 'null' - // }] - // } - -type U = Static // type U = number | null -``` - - - -### Reference Types - -Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`. - -```typescript -const T = Type.String({ $id: 'T' }) // const T = { - // $id: 'T', - // type: 'string' - // } - -const R = Type.Ref(T) // const R = { - // $ref: 'T' - // } -``` - -It can sometimes be helpful to organize shared referenced types under a common namespace. The `Type.Namespace(...)` function can be used to create a shared definition container for related types. The following creates a `Math3D` container and a `Vertex` structure that references types in the container. - -```typescript -const Math3D = Type.Namespace({ // const Math3D = { - Vector4: Type.Object({ // $id: 'Math3D', - x: Type.Number(), // $defs: { - y: Type.Number(), // Vector4: { - z: Type.Number(), // type: 'object', - w: Type.Number() // properties: { - }), // x: { type: 'number' }, - Vector3: Type.Object({ // y: { type: 'number' }, - x: Type.Number(), // z: { type: 'number' }, - y: Type.Number(), // w: { type: 'number' } - z: Type.Number() // }, - }), // required: ['x', 'y', 'z', 'w'] - Vector2: Type.Object({ // }, - x: Type.Number(), // Vector3: { - y: Type.Number() // type: 'object', - }) // properties: { -}, { $id: 'Math3D' }) // x: { 'type': 'number' }, - // y: { 'type': 'number' }, - // z: { 'type': 'number' } - // }, - // required: ['x', 'y', 'z'] - // }, - // Vector2: { - // type: 'object', - // properties: { - // x: { 'type': 'number' }, - // y: { 'type': 'number' }, - // }, - // required: ['x', 'y'] - // } - // } - // } - -const Vertex = Type.Object({ // const Vertex = { - position: Type.Ref(Math3D, 'Vector4'), // type: 'object', - normal: Type.Ref(Math3D, 'Vector3'), // properties: { - uv: Type.Ref(Math3D, 'Vector2') // position: { $ref: 'Math3D#/$defs/Vector4' }, -}) // normal: { $ref: 'Math3D#/$defs/Vector3' }, - // uv: { $ref: 'Math3D#/$defs/Vector2' } - // }, - // required: ['position', 'normal', 'uv'] - // } -``` - - - -### Recursive Types - -Recursive types can be created with the `Type.Rec(...)` function. The following creates a `Node` type that contains an array of inner Nodes. Note that due to current restrictions on TypeScript inference, it is not possible for TypeBox to statically infer for recursive types. TypeBox will infer the inner recursive type as `any`. - -```typescript -const Node = Type.Rec(Self => Type.Object({ // const Node = { - id: Type.String(), // $id: 'Node', - nodes: Type.Array(Self), // $ref: 'Node#/$defs/self', -}), { $id: 'Node' }) // $defs: { - // self: { - // type: 'object', - // properties: { - // id: { - // type: 'string' - // }, - // nodes: { - // type: 'array', - // items: { - // $ref: 'Node#/$defs/self' - // } - // } - // } - // } - // } - -type Node = Static // type Node = { - // id: string - // nodes: any[] - // } - -function visit(node: Node) { - for(const inner of node.nodes) { - visit(inner as Node) // Assert inner as Node - } -} -``` - + -### Extended Types +### Extended -In addition to JSON schema types, TypeBox provides several extended types that allow for `function` and `constructor` types to be composed. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows. +In addition to JSON schema types, TypeBox provides several extended types that allow for the composition of `function` and `constructor` types. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -549,124 +448,487 @@ In addition to JSON schema types, TypeBox provides several extended types that a │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Constructor([ │ type T = new ( │ const T = { │ -│ Type.String(), │ arg0: string, │ type: 'constructor' │ -│ Type.Number(), │ arg1: number │ arguments: [{ │ -│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ +│ Type.String(), │ arg0: string, │ type: 'constructor' │ +│ Type.Number() │ arg1: number │ parameters: [{ │ +│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ -│ │ │ type: 'number' │ +│ │ │ type: 'number' │ │ │ │ }], │ -│ │ │ returns: { │ -│ │ │ type: 'boolean' │ +│ │ │ return: { │ +│ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Function([ │ type T = ( │ const T = { │ -| Type.String(), │ arg0: string, │ type : 'function', │ -│ Type.Number(), │ arg1: number │ arguments: [{ │ -│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ +| Type.String(), │ arg0: string, │ type : 'function', │ +│ Type.Number() │ arg1: number │ parameters: [{ │ +│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ -│ │ │ type: 'number' │ +│ │ │ type: 'number' │ │ │ │ }], │ -│ │ │ returns: { │ -│ │ │ type: 'boolean' │ +│ │ │ return: { │ +│ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │ +│ │ │ type: 'object', │ +│ │ │ specialized: 'Uint8Array' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Promise( │ type T = Promise │ const T = { │ -│ Type.String() │ │ type: 'promise', │ +│ Type.String() │ │ type: 'promise', │ │ ) │ │ item: { │ -│ │ │ type: 'string' │ +│ │ │ type: 'string' │ │ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Undefined() │ type T = undefined │ const T = { │ -│ │ │ type: 'undefined' │ +│ │ │ type: 'object', │ +│ │ │ specialized: 'Undefined' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Void() │ type T = void │ const T = { │ -│ │ │ type: 'void' │ +│ │ │ type: 'null' │ │ │ │ } │ │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - + -### Strict +### Reference + +Use `Type.Ref(...)` to create referenced types. The target type must specify an `$id`. -TypeBox schemas contain the properties `kind` and `modifier`. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox apply the appropriate static type inference rules. These properties are not strictly valid JSON schema so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. +```typescript +const T = Type.String({ $id: 'T' }) // const T = { + // $id: 'T', + // type: 'string' + // } + +const R = Type.Ref(T) // const R = { + // $ref: 'T' + // } +``` + + + +### Recursive + +Use `Type.Recursive(...)` to create recursive types. ```typescript -const T = Type.Object({ // const T = { - name: Type.Optional(Type.String()) // kind: Symbol(ObjectKind), -}) // type: 'object', - // properties: { - // name: { - // kind: Symbol(StringKind), - // type: 'string', - // modifier: Symbol(OptionalModifier) - // } - // } - // } +const Node = Type.Recursive(Node => Type.Object({ // const Node = { + id: Type.String(), // $id: 'Node', + nodes: Type.Array(Node) // type: 'object', +}), { $id: 'Node' }) // properties: { + // id: { + // type: 'string' + // }, + // nodes: { + // type: 'array', + // items: { + // $ref: 'Node' + // } + // } + // }, + // required: [ + // 'id', + // 'nodes' + // ] + // } + +type Node = Static // type Node = { + // id: string + // nodes: Node[] + // } + +function test(node: Node) { + const id = node.nodes[0].nodes[0] // id is string + .nodes[0].nodes[0] + .id +} +``` + + + +### Generic -const U = Type.Strict(T) // const U = { - // type: 'object', - // properties: { - // name: { - // type: 'string' - // } - // } - // } +Use functions to create generic types. The following creates a generic `Nullable` type. + +```typescript +import { Type, Static, TSchema } from '@sinclair/typebox' + +const Nullable = (type: T) => Type.Union([type, Type.Null()]) + +const T = Nullable(Type.String()) // const T = { + // anyOf: [{ + // type: 'string' + // }, { + // type: 'null' + // }] + // } + +type T = Static // type T = string | null + +const U = Nullable(Type.Number()) // const U = { + // anyOf: [{ + // type: 'number' + // }, { + // type: 'null' + // }] + // } + +type U = Static // type U = number | null ``` - + -### Validation +### Conditional -TypeBox does not provide JSON schema validation so users will need to select an appropriate JSON Schema validator for their needs. TypeBox schemas target JSON Schema draft `2019-09` so any validator capable of draft `2019-09` 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. +Use the conditional module to create [Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html). This module implements TypeScript's structural equivalence checks to enable TypeBox types to be conditionally inferred at runtime. This module also provides the [Extract](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) and [Exclude](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) utility types which are expressed as conditional types in TypeScript. -```bash -$ npm install ajv ajv-formats --save +The conditional module is provided as an optional import. + +```typescript +import { Conditional } from '@sinclair/typebox/conditional' ``` +The following table shows the TypeBox mappings between TypeScript and JSON schema. ```typescript +┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ +│ TypeBox │ TypeScript │ JSON Schema │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Conditional.Extends( │ type T = │ const T = { │ +│ Type.String(), │ string extends number │ const: false, │ +│ Type.Number(), │ true : false │ type: 'boolean' │ +│ Type.Literal(true), │ │ } │ +│ Type.Literal(false) │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Conditional.Extract( │ type T = Extract< │ const T = { │ +│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │ +│ Type.Literal('a'), │ 'a' | 'f' │ const: 'a' │ +│ Type.Literal('b'), │ > │ type: 'string' │ +│ Type.Literal('c') │ │ }] │ +│ ]), │ │ } │ +│ Type.Union([ │ │ │ +│ Type.Literal('a'), │ │ │ +│ Type.Literal('f') │ │ │ +│ ]) │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Conditional.Exclude( │ type T = Exclude< │ const T = { │ +│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │ +│ Type.Literal('a'), │ 'a' │ const: 'b', │ +│ Type.Literal('b'), │ > │ type: 'string' │ +│ Type.Literal('c') │ │ }, { │ +│ ]), │ │ const: 'c', │ +│ Type.Union([ │ │ type: 'string' │ +│ Type.Literal('a') │ │ }] │ +│ ]) │ │ } │ +│ ) │ │ │ +│ │ │ │ +└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ +``` + + + +### Unsafe + +Use `Type.Unsafe(...)` to create custom schemas with user defined inference rules. + +```typescript +const T = Type.Unsafe({ type: 'number' }) // const T = { + // type: 'number' + // } + +type T = Static // type T = string +``` + +This function can be used to create custom schemas for validators that require specific schema representations. An example of this might be OpenAPI's `nullable` and `enum` schemas which are not provided by TypeBox. The following demonstrates using `Type.Unsafe(...)` to create these types. + +```typescript +import { Type, Static, TSchema } from '@sinclair/typebox' + +//-------------------------------------------------------------------------------------------- +// +// Nullable +// +//-------------------------------------------------------------------------------------------- + +function Nullable(schema: T) { + return Type.Unsafe | null>({ ...schema, nullable: true }) +} + +const T = Nullable(Type.String()) // const T = { + // type: 'string', + // nullable: true + // } + +type T = Static // type T = string | null + + //-------------------------------------------------------------------------------------------- // -// Import the 2019 compliant validator from AJV +// StringEnum // //-------------------------------------------------------------------------------------------- +function StringEnum(values: [...T]) { + return Type.Unsafe({ type: 'string', enum: values }) +} + +const T = StringEnum(['A', 'B', 'C']) // const T = { + // enum: ['A', 'B', 'C'] + // } + +type T = Static // type T = 'A' | 'B' | 'C' +``` + + + +### Guards + +Use the guard module to test if values are TypeBox types. + +```typescript +import { TypeGuard } from '@sinclair/typebox/guard' + +const T = Type.String() + +if(TypeGuard.TString(T)) { + + // T is TString +} +``` + + + +### Strict + +TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox internally compose types. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. + +```typescript +const T = Type.Object({ // const T = { + name: Type.Optional(Type.String()) // [Kind]: 'Object', +}) // type: 'object', + // properties: { + // name: { + // [Kind]: 'String', + // type: 'string', + // [Modifier]: 'Optional' + // } + // } + // } + +const U = Type.Strict(T) // const U = { + // type: 'object', + // properties: { + // name: { + // type: 'string' + // } + // } + // } +``` + + + +## Values + +TypeBox includes an optional values module that can be used to perform common operations on JavaScript values. This module enables one to create, check and cast values from types. It also provides functionality to check equality, clone and diff and patch JavaScript values. The value module is provided as an optional import. + +```typescript +import { Value } from '@sinclair/typebox/value' +``` + + + +### Create + +Use the Create function to create a value from a TypeBox type. TypeBox will use default values if specified. + +```typescript +const T = Type.Object({ x: Type.Number(), y: Type.Number({ default: 42 }) }) + +const A = Value.Create(T) // const A = { x: 0, y: 42 } +``` + + + +### Clone + +Use the Clone function to deeply clone a value + +```typescript +const A = Value.Clone({ x: 1, y: 2, z: 3 }) // const A = { x: 1, y: 2, z: 3 } +``` + + + +### Check + +Use the Check function to type check a value + +```typescript +const T = Type.Object({ x: Type.Number() }) + +const R = Value.Check(T, { x: 1 }) // const R = true +``` + + + +### Cast + +Use the Cast function to cast a value into a type. The cast function will retain as much information as possible from the original value. + +```typescript +const T = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) + +const X = Value.Cast(T, null) // const X = { x: 0, y: 0 } + +const Y = Value.Cast(T, { x: 1 }) // const Y = { x: 1, y: 0 } + +const Z = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const Z = { x: 1, y: 2 } +``` + + + +### Equal + +Use the Equal function to deeply check for value equality. + +```typescript +const R = Value.Equal( // const R = true + { x: 1, y: 2, z: 3 }, + { x: 1, y: 2, z: 3 } +) +``` + + + +### Diff + +Use the Diff function to produce a sequence of edits to transform one value into another. + +```typescript +const E = Value.Diff( // const E = [ + { x: 1, y: 2, z: 3 }, // { type: 'update', path: '/y', value: 4 }, + { y: 4, z: 5, w: 6 } // { type: 'update', path: '/z', value: 5 }, +) // { type: 'insert', path: '/w', value: 6 }, + // { type: 'delete', path: '/x' } + // ] +``` + + + +### Patch + +Use the Patch function to apply edits + +```typescript +const A = { x: 1, y: 2 } + +const B = { x: 3 } + +const E = Value.Diff(A, B) // const E = [ + // { type: 'update', path: '/x', value: 3 }, + // { type: 'delete', path: '/y' } + // ] + +const C = Value.Patch(A, E) // const C = { x: 3 } +``` + + + + +### Errors + +Use the Errors function enumerate validation errors. + +```typescript +const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + +const R = [...Value.Errors(T, { x: '42' })] // const R = [{ + // schema: { type: 'number' }, + // path: '/x', + // value: '42', + // message: 'Expected number' + // }, { + // schema: { type: 'number' }, + // path: '/y', + // value: undefined, + // message: 'Expected number' + // }] +``` + + + +### Pointer + +Use ValuePointer to perform mutable updates on existing values using [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) Json Pointers. + +```typescript +import { ValuePointer } from '@sinclair/typebox/value' + +const A = { x: 0, y: 0, z: 0 } + +ValuePointer.Set(A, '/x', 1) // const A = { x: 1, y: 0, z: 0 } +ValuePointer.Set(A, '/y', 1) // const A = { x: 1, y: 1, z: 0 } +ValuePointer.Set(A, '/z', 1) // const A = { x: 1, y: 1, z: 1 } +``` + + +## TypeCheck + +TypeBox is written to target JSON Schema Draft 6 and can be used with any Draft 6 compliant validator. TypeBox is developed and tested against Ajv and can be used in any application already making use of this validator. Additionally, TypeBox also provides an optional type compiler that can be used to attain improved compilation and validation performance for certain application types. + + + +### Ajv + +The following example shows setting up Ajv to work with TypeBox. + +```bash +$ npm install ajv ajv-formats --save +``` + +```typescript import { Type } from '@sinclair/typebox' import addFormats from 'ajv-formats' -import Ajv from 'ajv/dist/2019' +import Ajv from 'ajv' //-------------------------------------------------------------------------------------------- // -// Setup AJV validator with the following options and formats +// Setup Ajv validator with the following options and formats // //-------------------------------------------------------------------------------------------- const ajv = addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' -]).addKeyword('kind') - .addKeyword('modifier') + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' +]) //-------------------------------------------------------------------------------------------- // @@ -674,11 +936,11 @@ const ajv = addFormats(new Ajv({}), [ // //-------------------------------------------------------------------------------------------- -const User = Type.Object({ - userId: Type.String({ format: 'uuid' }), - email: Type.String({ format: 'email' }), - online: Type.Boolean(), -}, { additionalProperties: false }) +const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}) //-------------------------------------------------------------------------------------------- // @@ -686,54 +948,205 @@ const User = Type.Object({ // //-------------------------------------------------------------------------------------------- -const ok = ajv.validate(User, { - userId: '68b4b1d8-0db6-468d-b551-02069a692044', - email: 'dave@domain.com', - online: true -}) // -> ok +const R = ajv.validate(T, { x: 1, y: 2, z: 3 }) // const R = true ``` -Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV. + + +### Compiler -### OpenAPI +TypeBox provides an optional high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. If defining custom types with `Type.Unsafe` please consider Ajv. -TypeBox can be used to create schemas for OpenAPI, however users should be mindful of some disparities between the JSON Schema and OpenAPI for versions prior to OpenAPI 3.1. Two common instances where OpenAPI diverges is the handling nullable and string enum schemas types. The following shows how you can use TypeBox to construct these types. +The compiler module is provided as an optional import. ```typescript -import { Type, Static, TNull, TLiteral, TUnion, TSchema } from '@sinclair/typebox' +import { TypeCompiler } from '@sinclair/typebox/compiler' +``` -//-------------------------------------------------------------------------------------------- -// -// Nullable -// -//-------------------------------------------------------------------------------------------- +Use the `Compile(...)` function to compile a type. -function Nullable(schema: T): TUnion<[T, TNull]> { - return { ...schema, nullable: true } as any -} +```typescript +const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> -const T = Nullable(Type.String()) // const T = { - // type: 'string', - // nullable: true - // } +const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true +``` -type T = Static // type T = string | null +Validation errors can be read with the `Errors(...)` function. -//-------------------------------------------------------------------------------------------- -// -// StringUnion<[...]> -// -//-------------------------------------------------------------------------------------------- +```typescript +const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> + +const value = { } + +const errors = [...C.Errors(value)] // const errors = [{ + // schema: { type: 'number' }, + // path: '/x', + // value: undefined, + // message: 'Expected number' + // }, { + // schema: { type: 'number' }, + // path: '/y', + // value: undefined, + // message: 'Expected number' + // }, { + // schema: { type: 'number' }, + // path: '/z', + // value: undefined, + // message: 'Expected number' + // }] +``` -type IntoStringUnion = {[K in keyof T]: T[K] extends string ? TLiteral: never } +Compiled routines can be inspected with the `.Code()` function. -function StringUnion(values: [...T]): TUnion> { - return { enum: values } as any -} +```typescript +const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck + +console.log(C.Code()) // return function check(value) { + // return ( + // (typeof value === 'string') + // ) + // } +``` + + + +### Formats + +Use the format module to create user defined string formats. The format module is used by the Value and TypeCompiler modules only. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). -const T = StringUnion(['A', 'B', 'C']) // const T = { - // enum: ['A', 'B', 'C'] - // } +The format module is an optional import. -type T = Static // type T = 'A' | 'B' | 'C' +```typescript +import { Format } from '@sinclair/typebox/format' +``` + +The following creates a `palindrome` string format. + +```typescript +Format.Set('palindrome', value => value === value.split('').reverse().join('')) +``` + +Once set, this format can then be used by the TypeCompiler and Value modules. + +```typescript +const T = Type.String({ format: 'palindrome' }) + +const A = TypeCompiler.Compile(T).Check('engine') // const A = false + +const B = Value.Check(T, 'kayak') // const B = true +``` + + + +## Benchmark + +This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.11.0. + +For additional comparative benchmarks, please refer to [typescript-runtime-type-benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/). + + + +### Compile + +This benchmark measures compilation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/compile.ts). + +```typescript +┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ +│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ +├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ +│ Number │ 2000 │ ' 384 ms' │ ' 9 ms' │ ' 42.67 x' │ +│ String │ 2000 │ ' 322 ms' │ ' 8 ms' │ ' 40.25 x' │ +│ Boolean │ 2000 │ ' 310 ms' │ ' 6 ms' │ ' 51.67 x' │ +│ Null │ 2000 │ ' 271 ms' │ ' 6 ms' │ ' 45.17 x' │ +│ RegEx │ 2000 │ ' 491 ms' │ ' 12 ms' │ ' 40.92 x' │ +│ ObjectA │ 2000 │ ' 2935 ms' │ ' 44 ms' │ ' 66.70 x' │ +│ ObjectB │ 2000 │ ' 3076 ms' │ ' 30 ms' │ ' 102.53 x' │ +│ Tuple │ 2000 │ ' 1274 ms' │ ' 22 ms' │ ' 57.91 x' │ +│ Union │ 2000 │ ' 1281 ms' │ ' 22 ms' │ ' 58.23 x' │ +│ Vector4 │ 2000 │ ' 1602 ms' │ ' 17 ms' │ ' 94.24 x' │ +│ Matrix4 │ 2000 │ ' 959 ms' │ ' 10 ms' │ ' 95.90 x' │ +│ Literal_String │ 2000 │ ' 337 ms' │ ' 6 ms' │ ' 56.17 x' │ +│ Literal_Number │ 2000 │ ' 376 ms' │ ' 5 ms' │ ' 75.20 x' │ +│ Literal_Boolean │ 2000 │ ' 370 ms' │ ' 5 ms' │ ' 74.00 x' │ +│ Array_Number │ 2000 │ ' 733 ms' │ ' 9 ms' │ ' 81.44 x' │ +│ Array_String │ 2000 │ ' 764 ms' │ ' 10 ms' │ ' 76.40 x' │ +│ Array_Boolean │ 2000 │ ' 818 ms' │ ' 6 ms' │ ' 136.33 x' │ +│ Array_ObjectA │ 2000 │ ' 3744 ms' │ ' 35 ms' │ ' 106.97 x' │ +│ Array_ObjectB │ 2000 │ ' 3893 ms' │ ' 34 ms' │ ' 114.50 x' │ +│ Array_Tuple │ 2000 │ ' 2229 ms' │ ' 16 ms' │ ' 139.31 x' │ +│ Array_Union │ 2000 │ ' 1705 ms' │ ' 18 ms' │ ' 94.72 x' │ +│ Array_Vector4 │ 2000 │ ' 2261 ms' │ ' 17 ms' │ ' 133.00 x' │ +│ Array_Matrix4 │ 2000 │ ' 1574 ms' │ ' 14 ms' │ ' 112.43 x' │ +└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ +``` + + + +### Validate + +This benchmark measures validation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/check.ts). + +```typescript +┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ +│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ +├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ +│ Number │ 1000000 │ ' 37 ms' │ ' 5 ms' │ ' 5 ms' │ ' 1.00 x' │ +│ String │ 1000000 │ ' 31 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ +│ Boolean │ 1000000 │ ' 25 ms' │ ' 25 ms' │ ' 11 ms' │ ' 2.27 x' │ +│ Null │ 1000000 │ ' 32 ms' │ ' 26 ms' │ ' 10 ms' │ ' 2.60 x' │ +│ RegEx │ 1000000 │ ' 170 ms' │ ' 48 ms' │ ' 35 ms' │ ' 1.37 x' │ +│ ObjectA │ 1000000 │ ' 564 ms' │ ' 36 ms' │ ' 23 ms' │ ' 1.57 x' │ +│ ObjectB │ 1000000 │ ' 1000 ms' │ ' 56 ms' │ ' 40 ms' │ ' 1.40 x' │ +│ Tuple │ 1000000 │ ' 125 ms' │ ' 27 ms' │ ' 13 ms' │ ' 2.08 x' │ +│ Union │ 1000000 │ ' 316 ms' │ ' 27 ms' │ ' 14 ms' │ ' 1.93 x' │ +│ Recursive │ 1000000 │ ' 3308 ms' │ ' 415 ms' │ ' 183 ms' │ ' 2.27 x' │ +│ Vector4 │ 1000000 │ ' 135 ms' │ ' 27 ms' │ ' 12 ms' │ ' 2.25 x' │ +│ Matrix4 │ 1000000 │ ' 658 ms' │ ' 44 ms' │ ' 30 ms' │ ' 1.47 x' │ +│ Literal_String │ 1000000 │ ' 48 ms' │ ' 26 ms' │ ' 10 ms' │ ' 2.60 x' │ +│ Literal_Number │ 1000000 │ ' 49 ms' │ ' 31 ms' │ ' 9 ms' │ ' 3.44 x' │ +│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 28 ms' │ ' 10 ms' │ ' 2.80 x' │ +│ Array_Number │ 1000000 │ ' 418 ms' │ ' 38 ms' │ ' 17 ms' │ ' 2.24 x' │ +│ Array_String │ 1000000 │ ' 466 ms' │ ' 35 ms' │ ' 22 ms' │ ' 1.59 x' │ +│ Array_Boolean │ 1000000 │ ' 403 ms' │ ' 41 ms' │ ' 24 ms' │ ' 1.71 x' │ +│ Array_ObjectA │ 1000000 │ ' 13596 ms' │ ' 2861 ms' │ ' 1759 ms' │ ' 1.63 x' │ +│ Array_ObjectB │ 1000000 │ ' 15767 ms' │ ' 2798 ms' │ ' 1991 ms' │ ' 1.41 x' │ +│ Array_Tuple │ 1000000 │ ' 1666 ms' │ ' 103 ms' │ ' 73 ms' │ ' 1.41 x' │ +│ Array_Union │ 1000000 │ ' 4738 ms' │ ' 229 ms' │ ' 87 ms' │ ' 2.63 x' │ +│ Array_Recursive │ 1000000 │ ' 56371 ms' │ ' 7315 ms' │ ' 2813 ms' │ ' 2.60 x' │ +│ Array_Vector4 │ 1000000 │ ' 2180 ms' │ ' 106 ms' │ ' 53 ms' │ ' 2.00 x' │ +│ Array_Matrix4 │ 1000000 │ ' 11795 ms' │ ' 384 ms' │ ' 313 ms' │ ' 1.23 x' │ +└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` + + + +### Compression + +The following table lists esbuild compiled and minified sizes for each TypeBox module. + +```typescript +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ ' 49 kb' │ ' 24 kb' │ '2.00 x' │ +│ typebox/conditional │ ' 42 kb' │ ' 17 kb' │ '2.46 x' │ +│ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ +│ typebox/guard │ ' 21 kb' │ ' 10 kb' │ '2.07 x' │ +│ typebox/value │ ' 72 kb' │ ' 33 kb' │ '2.16 x' │ +│ typebox │ ' 11 kb' │ ' 6 kb' │ '1.91 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ +``` + + + +## Contribute + +TypeBox is open to community contribution. Please ensure you submit an open issue before submitting your pull request. The TypeBox project preferences open community discussion prior to accepting new features. diff --git a/spec/schema/any.ts b/spec/schema/any.ts deleted file mode 100644 index decdc2667..000000000 --- a/spec/schema/any.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Any", () => { - it('Should validate number', () => { - const T = Type.Any() - ok(T, 1) - }) - it('Should validate string', () => { - const T = Type.Any() - ok(T, 'hello') - }) - it('Should validate boolean', () => { - const T = Type.Any() - ok(T, true) - }) - it('Should validate array', () => { - const T = Type.Any() - ok(T, [1, 2, 3]) - }) - it('Should validate object', () => { - const T = Type.Any() - ok(T, { a: 1, b: 2 }) - }) - it('Should validate null', () => { - const T = Type.Any() - ok(T, null) - }) - it('Should validate undefined', () => { - const T = Type.Any() - ok(T, undefined) - }) -}) diff --git a/spec/schema/array.ts b/spec/schema/array.ts deleted file mode 100644 index 6c4fcbded..000000000 --- a/spec/schema/array.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Array", () => { - it('Should validate an array of any', () => { - const T = Type.Array(Type.Any()) - ok(T, [0, true, 'hello', {}]) - }) - - it('Should not validate varying array when item is number', () => { - const T = Type.Array(Type.Number()) - fail(T, [1, 2, 3, 'hello']) - }) - - it('Should validate for an array of unions', () => { - const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, [1, 'hello', 3, 'world']) - }) - - it('Should not validate for an array of unions where item is not in union.', () => { - const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - fail(T, [1, 'hello', 3, 'world', true]) - }) - - it('Should validate for an empty array', () => { - const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, []) - }) - - it('Should validate for an array of intersection types', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.String() }) - const C = Type.Intersect([A, B], { unevaluatedProperties: false }) - const T = Type.Array(C) - ok(T, [ - { a: 'hello', b: 'hello' }, - { a: 'hello', b: 'hello' }, - { a: 'hello', b: 'hello' }, - ]) - }) - - it('Should not validate for an array of intersection types when passing unevaluated property', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.String() }) - const C = Type.Intersect([A, B], { unevaluatedProperties: false }) - const T = Type.Array(C) - fail(T, [ - { a: 'hello', b: 'hello' }, - { a: 'hello', b: 'hello' }, - { a: 'hello', b: 'hello', c: 'additional' }, - ]) - }) - - it('Should validate an array of tuples', () => { - const A = Type.String() - const B = Type.Number() - const C = Type.Tuple([A, B]) - const T = Type.Array(C) - ok(T, [ - ['hello', 1], - ['hello', 1], - ['hello', 1], - ]) - }) - - it('Should not validate an array of tuples when tuple values are incorrect', () => { - const A = Type.String() - const B = Type.Number() - const C = Type.Tuple([A, B]) - const T = Type.Array(C) - fail(T, [ - [1, 'hello'], - [1, 'hello'], - [1, 'hello'], - ]) - }) -}) diff --git a/spec/schema/boolean.ts b/spec/schema/boolean.ts deleted file mode 100644 index 6ce216837..000000000 --- a/spec/schema/boolean.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Boolean", () => { - - it('Should validate a boolean', () => { - const T = Type.Boolean() - ok(T, true) - ok(T, false) - }) - - it('Should not validate a number', () => { - const T = Type.Boolean() - fail(T, 1) - }) - - it('Should not validate a string', () => { - const T = Type.Boolean() - fail(T, 'true') - }) - - it('Should not validate an array', () => { - const T = Type.Boolean() - fail(T, [true]) - }) - - it('Should not validate an object', () => { - const T = Type.Boolean() - fail(T, {}) - }) - - it('Should not validate an null', () => { - const T = Type.Boolean() - fail(T, null) - }) - - it('Should not validate an undefined', () => { - const T = Type.Boolean() - fail(T, undefined) - }) -}) diff --git a/spec/schema/box.ts b/spec/schema/box.ts deleted file mode 100644 index a605d00ba..000000000 --- a/spec/schema/box.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Box", () => { - - it('Should should validate Vertex structure', () => { - const Vector2 = Type.Object({ x: Type.Number(), y: Type.Number() }) - const Vector3 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() }) - const Vector4 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number(), w: Type.Number() }) - const Math3D = Type.Namespace({ Vector2, Vector3, Vector4 }, { $id: 'Math3D' }) - const Vertex = Type.Object({ - position: Type.Ref(Math3D, 'Vector4'), - normal: Type.Ref(Math3D, 'Vector3'), - uv: Type.Ref(Math3D, 'Vector2'), - }) - - ok(Vertex, { - position: { x: 1, y: 1, z: 1, w: 1 }, - normal: { x: 1, y: 1, z: 1 }, - uv: { x: 1, y: 1 }, - }, [Math3D]) - }) - - it('Should not validate when Vertex structure is missing properties', () => { - const Vector2 = Type.Object({ x: Type.Number(), y: Type.Number() }) - const Vector3 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() }) - const Vector4 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number(), w: Type.Number() }) - const Math3D = Type.Namespace({ Vector2, Vector3, Vector4 }, { $id: 'Math3D' }) - const Vertex = Type.Object({ - position: Type.Ref(Math3D, 'Vector4'), - normal: Type.Ref(Math3D, 'Vector3'), - uv: Type.Ref(Math3D, 'Vector2'), - }) - - fail(Vertex, { - position: { x: 1, y: 1, z: 1, w: 1 }, - normal: { x: 1, y: 1, z: 1 }, - }, [Math3D]) - }) - - it('Should not validate when Vertex structure contains invalid property values.', () => { - const Vector2 = Type.Object({ x: Type.Number(), y: Type.Number() }) - const Vector3 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() }) - const Vector4 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number(), w: Type.Number() }) - const Math3D = Type.Namespace({ Vector2, Vector3, Vector4 }, { $id: 'Math3D' }) - const Vertex = Type.Object({ - position: Type.Ref(Math3D, 'Vector4'), - normal: Type.Ref(Math3D, 'Vector3'), - uv: Type.Ref(Math3D, 'Vector2'), - }) - fail(Vertex, { - position: { x: 1, y: 1, z: 1, w: 1 }, - normal: { x: 1, y: 1, z: 1 }, - uv: { x: 1, y: 'not a number'}, - }, [Math3D]) - }) - - it('Should not validate when Box has not been registered with validator (AJV)', () => { - const Vector2 = Type.Object({ x: Type.Number(), y: Type.Number() }) - const Vector3 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() }) - const Vector4 = Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number(), w: Type.Number() }) - const Math3D = Type.Namespace({ Vector2, Vector3, Vector4 }, { $id: 'Math3D' }) - const Vertex = Type.Object({ - position: Type.Ref(Math3D, 'Vector4'), - normal: Type.Ref(Math3D, 'Vector3'), - uv: Type.Ref(Math3D, 'Vector2'), - }) - fail(Vertex, { - position: { x: 1, y: 1, z: 1, w: 1 }, - normal: { x: 1, y: 1, z: 1 }, - uv: { x: 1, y: 1}, - }, []) - }) -}) diff --git a/spec/schema/enum.ts b/spec/schema/enum.ts deleted file mode 100644 index 2a6356c3c..000000000 --- a/spec/schema/enum.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Enum', () => { - - it('Should validate when emum uses default numeric values', () => { - enum Kind { - Foo, // = 0 - Bar // = 1 - } - const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 1) - }) - it('Should not validate when given enum values are not numeric', () => { - enum Kind { - Foo, // = 0 - Bar // = 1 - } - const T = Type.Enum(Kind) - fail(T, 'Foo') - fail(T, 'Bar') - }) - - it('Should validate when emum has defined string values', () => { - enum Kind { - Foo = 'foo', - Bar = 'bar' - } - const T = Type.Enum(Kind) - ok(T, 'foo') - ok(T, 'bar') - }) - - it('Should not validate when emum has defined string values and user passes numeric', () => { - enum Kind { - Foo = 'foo', - Bar = 'bar' - } - const T = Type.Enum(Kind) - fail(T, 0) - fail(T, 1) - }) - - it('Should validate when enum has one or more string values', () => { - enum Kind { - Foo, - Bar = 'bar' - } - const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 'bar') - fail(T, 'baz') - fail(T, 'Foo') - fail(T, 1) - }) -}) diff --git a/spec/schema/intersect.ts b/spec/schema/intersect.ts deleted file mode 100644 index 4a7f9cd5d..000000000 --- a/spec/schema/intersect.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Intersect', () => { - - it('Should intersect two objects', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Intersect([A, B]) - ok(T, { a: 'hello', b: 42 }) - }) - - it('Should allow additional properties if not using unevaluatedProperties', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Intersect([A, B]) - ok(T, { a: 'hello', b: 42, c: true }) - }) - - it('Should not allow additional properties if using unevaluatedProperties', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Intersect([A, B], { unevaluatedProperties: false }) - fail(T, { a: 'hello', b: 42, c: true }) - }) - - describe('Should not allow unevaluatedProperties with record intersection', () => { - const A = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String() - }) - const B = Type.Record(Type.Number(), Type.Number()) - const T = Type.Intersect([A, B]) - ok(T, { - a: 'a', b: 'b', c: 'c', - 0: 1, 1: 2, 2: 3 - }) - }) - - describe('Should intersect object with number record', () => { - const A = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String() - }) - const B = Type.Record(Type.Number(), Type.Number()) - const T = Type.Intersect([A, B]) - ok(T, { - a: 'a', b: 'b', c: 'c', - 0: 1, 1: 2, 2: 3 - }) - }) - - describe('Should not intersect object with string record', () => { - const A = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String() - }) - const B = Type.Record(Type.String(), Type.Number()) - const T = Type.Intersect([A, B]) - fail(T, { - a: 'a', b: 'b', c: 'c', - x: 1, y: 2, z: 3 - }) - }) - - describe('Should intersect object with union literal record', () => { - const A = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String() - }) - const K = Type.Union([ - Type.Literal('x'), - Type.Literal('y'), - Type.Literal('z') - ]) - const B = Type.Record(K, Type.Number()) - const T = Type.Intersect([A, B]) - ok(T, { - a: 'a', b: 'b', c: 'c', - x: 1, y: 2, z: 3 - }) - }) - - describe('Should intersect with partial', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ b: Type.Number() }) - const P = Type.Intersect([Type.Partial(A), Type.Partial(B)], { unevaluatedProperties: false }) - ok(P, { a: 1, b: 2 }) - ok(P, { a: 1 }) - ok(P, { b: 1 }) - fail(P, { c: 1 }) - }) -}) diff --git a/spec/schema/keyof.ts b/spec/schema/keyof.ts deleted file mode 100644 index 7ef2b0810..000000000 --- a/spec/schema/keyof.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe("KeyOf", () => { - it('Should validate with all object keys as a kind of union', () => { - const T = Type.KeyOf(Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - })) - ok(T, 'x') - ok(T, 'y') - ok(T, 'z') - fail(T, 'w') - }) - - it('Should validate when using pick', () => { - const T = Type.KeyOf(Type.Pick(Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }), ['x', 'y'])) - ok(T, 'x') - ok(T, 'y') - fail(T, 'z') - }) - - it('Should validate when using omit', () => { - const T = Type.KeyOf(Type.Omit(Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }), ['x', 'y'])) - - fail(T, 'x') - fail(T, 'y') - ok(T, 'z') - }) - - it('Should construct new object when targetting reference', () => { - const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) - const R = Type.Ref(T) - const P = Type.KeyOf(R, []) - strictEqual(P.enum[0], 'a') - strictEqual(P.enum[1], 'b') - }) -}) diff --git a/spec/schema/literal.ts b/spec/schema/literal.ts deleted file mode 100644 index 72d190c1b..000000000 --- a/spec/schema/literal.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import * as assert from 'assert' - -describe("Literal", () => { - it('Should validate literal number', () => { - const T = Type.Literal(42) - ok(T, 42) - }) - it('Should validate literal string', () => { - const T = Type.Literal('hello') - ok(T, 'hello') - }) - - it('Should validate literal boolean', () => { - const T = Type.Literal(true) - ok(T, true) - }) - - it('Should not validate invalid literal number', () => { - const T = Type.Literal(42) - fail(T, 43) - }) - it('Should not validate invalid literal string', () => { - const T = Type.Literal('hello') - fail(T, 'world') - }) - it('Should not validate invalid literal boolean', () => { - const T = Type.Literal(false) - fail(T, true) - }) - - it('Should validate literal union', () => { - const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - ok(T, 42) - ok(T, 'hello') - }) - - it('Should not validate invalid literal union', () => { - const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - fail(T, 43) - fail(T, 'world') - }) -}) diff --git a/spec/schema/modifier.ts b/spec/schema/modifier.ts deleted file mode 100644 index 456b72513..000000000 --- a/spec/schema/modifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Type, ReadonlyModifier, OptionalModifier } from '@sinclair/typebox' -import * as assert from 'assert' - -describe('Modifier', () => { - it('Omit modifier', () => { - const T = Type.Object({ - a: Type.Readonly(Type.String()), - b: Type.Optional(Type.String()), - }) - - const S = JSON.stringify(T) - const P = JSON.parse(S) as any - - // check assignment on Type - assert.equal(T.properties.a['modifier'], ReadonlyModifier) - assert.equal(T.properties.b['modifier'], OptionalModifier) - - // check deserialized - assert.equal(P.properties.a['modifier'], undefined) - assert.equal(P.properties.b['modifier'], undefined) - }) -}) diff --git a/spec/schema/null.ts b/spec/schema/null.ts deleted file mode 100644 index 3ea7e8867..000000000 --- a/spec/schema/null.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Null", () => { - it('Should not validate number', () => { - const T = Type.Null() - fail(T, 1) - }) - - it('Should not validate string', () => { - const T = Type.Null() - fail(T, 'hello') - }) - - it('Should not validate boolean', () => { - const T = Type.Null() - fail(T, true) - }) - - it('Should not validate array', () => { - const T = Type.Null() - fail(T, [1, 2, 3]) - }) - - it('Should not validate object', () => { - const T = Type.Null() - fail(T, { a: 1, b: 2 }) - }) - - it('Should not validate null', () => { - const T = Type.Null() - ok(T, null) - }) - - it('Should not validate undefined', () => { - const T = Type.Null() - fail(T, undefined) - }) -}) diff --git a/spec/schema/number.ts b/spec/schema/number.ts deleted file mode 100644 index a37af99a1..000000000 --- a/spec/schema/number.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Number", () => { - it('Should validate number', () => { - const T = Type.Number() - ok(T, 1) - }) - it('Should not validate string', () => { - const T = Type.Number() - fail(T, 'hello') - }) - it('Should not validate boolean', () => { - const T = Type.Number() - fail(T, true) - }) - it('Should not validate array', () => { - const T = Type.Number() - fail(T, [1, 2, 3]) - }) - it('Should not validate object', () => { - const T = Type.Number() - fail(T, { a: 1, b: 2 }) - }) - it('Should not validate null', () => { - const T = Type.Number() - fail(T, null) - }) - it('Should not validate undefined', () => { - const T = Type.Number() - fail(T, undefined) - }) -}) diff --git a/spec/schema/object.ts b/spec/schema/object.ts deleted file mode 100644 index e1e0285a7..000000000 --- a/spec/schema/object.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Object', () => { - - it('Should not validate a number', () => { - const T = Type.Object({}) - fail(T, 42) - }) - - it('Should not validate a string', () => { - const T = Type.Object({}) - fail(T, 'hello') - }) - - it('Should not validate a boolean', () => { - const T = Type.Object({}) - fail(T, true) - }) - - it('Should not validate a null', () => { - const T = Type.Object({}) - fail(T, null) - }) - - it('Should not validate an array', () => { - const T = Type.Object({}) - fail(T, [1, 2]) - }) - - it('Should validate with correct property values', () => { - const T = Type.Object({ - a: Type.Number(), - b: Type.String(), - c: Type.Boolean(), - d: Type.Array(Type.Number()), - e: Type.Object({ x: Type.Number(), y: Type.Number() }) - }) - ok(T, { - a: 10, - b: 'hello', - c: true, - d: [1, 2, 3], - e: { x: 10, y: 20 } - }) - }) - - it('Should not validate with incorrect property values', () => { - const T = Type.Object({ - a: Type.Number(), - b: Type.String(), - c: Type.Boolean(), - d: Type.Array(Type.Number()), - e: Type.Object({ x: Type.Number(), y: Type.Number() }) - }) - fail(T, { - a: 'not a number', // error - b: 'hello', - c: true, - d: [1, 2, 3], - e: { x: 10, y: 20 } - }) - }) - - it('Should allow additionalProperties by default', () => { - const T = Type.Object({ - a: Type.Number(), - b: Type.String() - }) - ok(T, { - a: 1, - b: 'hello', - c: true - }) - }) - - it('Should not allow additionalProperties if additionalProperties is false', () => { - const T = Type.Object({ - a: Type.Number(), - b: Type.String() - }, { additionalProperties: false }) - fail(T, { - a: 1, - b: 'hello', - c: true - }) - }) - - it('Should not allow properties for an empty object when additionalProperties is false', () => { - const T = Type.Object({}, { additionalProperties: false }) - ok(T, {}) - fail(T, { a: 10 }) - }) -}) diff --git a/spec/schema/omit.ts b/spec/schema/omit.ts deleted file mode 100644 index e4cb69d26..000000000 --- a/spec/schema/omit.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe('Omit', () => { - it('Should omit properties on the source schema', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Omit(A, ['z']) - ok(T, { x: 1, y: 1 }) - }) - - it('Should remove required properties on the target schema', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Omit(A, ['z']) - strictEqual(T.required!.includes('z'), false) - }) - - it('Should inherit options from the source object', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Omit(A, ['z']) - strictEqual(A.additionalProperties, false) - strictEqual(T.additionalProperties, false) - }) - - it('Should construct new object when targetting reference', () => { - const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) - const R = Type.Ref(T) - const P = Type.Omit(R, []) - strictEqual(P.properties.a.type, 'string') - strictEqual(P.properties.b.type, 'string') - }) -}) diff --git a/spec/schema/optional.ts b/spec/schema/optional.ts deleted file mode 100644 index 07168d943..000000000 --- a/spec/schema/optional.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { deepStrictEqual, strictEqual } from 'assert' -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Optional', () => { - it('Should validate object with optional', () => { - const T = Type.Object({ - a: Type.Optional(Type.String()), - b: Type.String() - }, { additionalProperties: false }) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) - }) - it('Should remove required value from schema', () => { - const T = Type.Object({ - a: Type.Optional(Type.String()), - b: Type.String() - }, { additionalProperties: false }) - strictEqual(T.required!.includes('a'), false) - }) -}) diff --git a/spec/schema/partial.ts b/spec/schema/partial.ts deleted file mode 100644 index 0e3af59f1..000000000 --- a/spec/schema/partial.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { OptionalModifier, ReadonlyOptionalModifier, Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe('Partial', () => { - - it('Should convert a required object into a partial.', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Partial(A) - ok(T, { x: 1, y: 1, z: 1 }) - ok(T, { x: 1, y: 1 }) - ok(T, { x: 1 }) - ok(T, {}) - }) - - it('Should update modifier types correctly when converting to partial', () => { - const A = Type.Object({ - x: Type.ReadonlyOptional(Type.Number()), - y: Type.Readonly(Type.Number()), - z: Type.Optional(Type.Number()), - w: Type.Number() - }, { additionalProperties: false }) - const T = Type.Partial(A) - strictEqual(T.properties.x.modifier, ReadonlyOptionalModifier) - strictEqual(T.properties.y.modifier, ReadonlyOptionalModifier) - strictEqual(T.properties.z.modifier, OptionalModifier) - strictEqual(T.properties.w.modifier, OptionalModifier) - }) - - it('Should inherit options from the source object', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Partial(A) - strictEqual(A.additionalProperties, false) - strictEqual(T.additionalProperties, false) - }) - - it('Should construct new object when targetting reference', () => { - const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) - const R = Type.Ref(T) - const P = Type.Partial(R) - strictEqual(P.properties.a.type, 'string') - strictEqual(P.properties.b.type, 'string') - }) -}) diff --git a/spec/schema/pick.ts b/spec/schema/pick.ts deleted file mode 100644 index 5318c3555..000000000 --- a/spec/schema/pick.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe('Pick', () => { - it('Should pick properties from the source schema', () => { - const Vector3 = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Pick(Vector3, ['x', 'y']) - ok(T, { x: 1, y: 1 }) - }) - - it('Should remove required properties on the target schema', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Pick(A, ['x', 'y']) - strictEqual(T.required!.includes('z'), false) - }) - - it('Should inherit options from the source object', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { additionalProperties: false }) - const T = Type.Pick(A, ['x', 'y']) - strictEqual(A.additionalProperties, false) - strictEqual(T.additionalProperties, false) - }) - it('Should construct new object when targetting reference', () => { - const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) - const R = Type.Ref(T) - const P = Type.Pick(R, ['a', 'b']) - strictEqual(P.properties.a.type, 'string') - strictEqual(P.properties.b.type, 'string') - }) -}) diff --git a/spec/schema/readonly-optional.ts b/spec/schema/readonly-optional.ts deleted file mode 100644 index 4edb7c9e2..000000000 --- a/spec/schema/readonly-optional.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe('ReadonlyOptional', () => { - it('Should validate object with optional', () => { - const T = Type.Object({ - a: Type.ReadonlyOptional(Type.String()), - b: Type.String() - }, { additionalProperties: false }) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) - }) - it('Should remove required value from schema', () => { - const T = Type.Object({ - a: Type.ReadonlyOptional(Type.String()), - b: Type.String() - }, { additionalProperties: false }) - strictEqual(T.required!.includes('a'), false) - }) -}) diff --git a/spec/schema/readonly.ts b/spec/schema/readonly.ts deleted file mode 100644 index 8cdc0f855..000000000 --- a/spec/schema/readonly.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { deepStrictEqual, strictEqual } from 'assert' -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Readonly', () => { - - it('Should validate object with readonly', () => { - const T = Type.Object({ - a: Type.Readonly(Type.String()), - b: Type.Readonly(Type.String()) - }, { additionalProperties: false }) - ok(T, { a: 'hello', b: 'world' }) - }) - - it('Should retain required array on object', () => { - const T = Type.Object({ - a: Type.Readonly(Type.String()), - b: Type.Readonly(Type.String()), - }, { additionalProperties: false }) - strictEqual(T.required!.includes('a'), true) - strictEqual(T.required!.includes('b'), true) - }) -}) \ No newline at end of file diff --git a/spec/schema/rec.ts b/spec/schema/rec.ts deleted file mode 100644 index 51a4801e9..000000000 --- a/spec/schema/rec.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Rec", () => { - - it('Should validate recursive Node type', () => { - const Node = Type.Rec(Self => Type.Object({ - nodeId: Type.String(), - nodes: Type.Array(Self) - }, { additionalProperties: false })) - ok(Node, { nodeId: '1', nodes: [] }) - ok(Node, { - nodeId: '1', - nodes: [ - { nodeId: '2', nodes: [] }, - { nodeId: '3', nodes: [] }, - { nodeId: '4', nodes: [] }, - { nodeId: '5', nodes: [] } - ] - }) - }) - it('Should validate recursive Node type with an $id', () => { - const Node = Type.Rec(Self => Type.Object({ - nodeId: Type.String(), - nodes: Type.Array(Self) - }, { additionalProperties: false }), - { $id: 'Node' }) - ok(Node, { nodeId: '1', nodes: [] }) - ok(Node, { - nodeId: '1', - nodes: [ - { nodeId: '2', nodes: [] }, - { nodeId: '3', nodes: [] }, - { nodeId: '4', nodes: [] }, - { nodeId: '5', nodes: [] } - ] - }) - }) - it('Should not validate recursive Node type with missing properties', () => { - const Node = Type.Rec(Self => Type.Object({ - nodeId: Type.String(), - nodes: Type.Array(Self) - }, { additionalProperties: false })) - fail(Node, { - nodes: [ - { nodeId: '2', nodes: [] }, - { nodeId: '3', nodes: [] }, - { nodeId: '4', nodes: [] }, - { nodeId: '5', nodes: [] } - ] - }) - }) - - it('Should not validate recursive Node type with additionalProperties', () => { - const Node = Type.Rec(Self => Type.Object({ - nodeId: Type.String(), - nodes: Type.Array(Self) - }, { additionalProperties: false })) - fail(Node, { - nodeId: '1', - nodes: [ - { nodeId: '2', nodes: [] }, - { nodeId: '3', nodes: [] }, - { nodeId: '4', nodes: [] }, - { nodeId: '5', nodes: [] } - ], - additional: 1 - }) - }) - - - // it('Should validate with JSON pointer references to sub schema', () => { - // const Element = Type.Rec('Element', Self => Type.Object({ - // elementId: Type.String(), - // elements: Type.Array(Self) - // }, { additionalProperties: false })) - - // const Node = Type.Rec('Node', Self => Type.Object({ - // nodeId: Type.String(), - // nodes: Type.Array(Self), - // element: Element - // }, { additionalProperties: false })) - - // ok(Node, { - // nodeId: '1', - // nodes: [ - // { nodeId: '2', nodes: [] }, - // { nodeId: '3', nodes: [] }, - // { nodeId: '4', nodes: [] }, - // { nodeId: '5', nodes: [] } - // ], - // element: { - // elementId: '1', - // elements: [{ - // elementId: '1', - // elements: [] - // }] - // } - // }) - // }) -}) diff --git a/spec/schema/record.ts b/spec/schema/record.ts deleted file mode 100644 index 187f2bcd6..000000000 --- a/spec/schema/record.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Record', () => { - - it('Should validate when all property values are numbers', () => { - const T = Type.Record(Type.String(), Type.Number()) - ok(T, { 'a': 1, 'b': 2, 'c': 3 }) - }) - - it('Should validate when all property keys are strings', () => { - const T = Type.Record(Type.String(), Type.Number()) - ok(T, { 'a': 1, 'b': 2, 'c': 3, '0': 4 }) - }) - - it('Should validate when all property keys are numbers', () => { - const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) - }) - - it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4, 'a': 'hello' }) - }) - - it('Should not validate if passing a leading zeros for numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) - fail(T, { - '00': 1, - '01': 2, - '02': 3, - '03': 4 - }) - }) - - it('Should not validate if passing a signed numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) - fail(T, { - '-0': 1, - '-1': 2, - '-2': 3, - '-3': 4 - }) - }) - - it('Should not validate when all property keys are numbers, but one property is a string with varying type with additionalProperties false', () => { - const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, 'a': 'hello' }) - }) - - it('Should validate when specifying union literals for the known keys', () => { - const K = Type.Union([ - Type.Literal('a'), - Type.Literal('b'), - Type.Literal('c'), - ]) - const T = Type.Record(K, Type.Number()) - ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) - }) - - it('Should not validate when specifying union literals for the known keys and with additionalProperties: false', () => { - const K = Type.Union([ - Type.Literal('a'), - Type.Literal('b'), - Type.Literal('c'), - ]) - const T = Type.Record(K, Type.Number(), { additionalProperties: false }) - fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) - }) - - it('Should validate for keyof records', () => { - const T = Type.Object({ - a: Type.String(), - b: Type.Number(), - c: Type.String() - }) - const R = Type.Record(Type.KeyOf(T), Type.Number()) - ok(R, { a: 1, b: 2, c: 3 }) - }) - - it('Should should validate when specifying regular expressions', () => { - const K = Type.RegEx(/^op_.*$/) - const T = Type.Record(K, Type.Number(), { additionalProperties: false }) - ok(T, { - 'op_a': 1, - 'op_b': 2, - 'op_c': 3, - }) - }) - - it('Should should not validate when specifying regular expressions and passing invalid property', () => { - const K = Type.RegEx(/^op_.*$/) - const T = Type.Record(K, Type.Number(), { additionalProperties: false }) - fail(T, { - 'op_a': 1, - 'op_b': 2, - 'aop_c': 3, - }) - }) -}) diff --git a/spec/schema/ref.ts b/spec/schema/ref.ts deleted file mode 100644 index 0eeee897b..000000000 --- a/spec/schema/ref.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Ref', () => { - - it('Should should validate when referencing a type', () => { - const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { $id: 'T' }) - const R = Type.Ref(T) - ok(R, { - x: 1, - y: 2, - z: 3 - }, [T]) - }) - - it('Should not validate when passing invalid data', () => { - const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { $id: 'T' }) - const R = Type.Ref(T) - fail(R, { - x: 1, - y: 2 - }, [T]) - }) - - it('Should throw when not specifying an $id on target schema', () => { - try { - const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { }) - const R = Type.Ref(T) - } catch { - return - } - throw Error('Expected throw') - }) - - it('Should not validate when not adding additional schema', () => { - const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { $id: 'T' }) - const R = Type.Ref(T) - fail(R, { - x: 1, - y: 2, - z: 3 - }, []) - }) - - it('Should validate as a Box, and as a Ref', () => { - const Vertex = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }, { $id: 'Vertex' }) - - const Box = Type.Namespace({ - Vertex - }, { $id: 'Box' }) - - const R1 = Type.Ref(Vertex) - - const R2 = Type.Ref(Box, 'Vertex') - - ok(R1, { - x: 1, - y: 2, - z: 3 - }, [Box]) - ok(R2, { - x: 1, - y: 2, - z: 3 - }, [Box]) - }) -}) \ No newline at end of file diff --git a/spec/schema/regex.ts b/spec/schema/regex.ts deleted file mode 100644 index af2d52311..000000000 --- a/spec/schema/regex.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('RegEx', () => { - - it('Should validate numeric value', () => { - const T = Type.RegEx(/[012345]/) - ok(T, '0') - ok(T, '1') - ok(T, '2') - ok(T, '3') - ok(T, '4') - ok(T, '5') - }) - - it('Should validate true or false string value', () => { - const T = Type.RegEx(/true|false/) - ok(T, 'true') - ok(T, 'true') - ok(T, 'true') - ok(T, 'false') - ok(T, 'false') - ok(T, 'false') - fail(T, '6') - }) - - it('Should not validate failed regex test', () => { - const T = Type.RegEx(/true|false/) - fail(T, 'unknown') - }) -}) diff --git a/spec/schema/required.ts b/spec/schema/required.ts deleted file mode 100644 index 2b9eb8cd8..000000000 --- a/spec/schema/required.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Type, ReadonlyModifier, ReadonlyOptionalModifier, OptionalModifier } from '@sinclair/typebox' -import { ok, fail } from './validate' -import { strictEqual } from 'assert' - -describe('Required', () => { - - it('Should convert a partial object into a required object', () => { - const A = Type.Object({ - x: Type.Optional(Type.Number()), - y: Type.Optional(Type.Number()), - z: Type.Optional(Type.Number()) - }, { additionalProperties: false }) - const T = Type.Required(A) - ok(T, { x: 1, y: 1, z: 1 }) - fail(T, { x: 1, y: 1 }) - fail(T, { x: 1 }) - fail(T, {}) - }) - - it('Should update modifier types correctly when converting to required', () => { - const A = Type.Object({ - x: Type.ReadonlyOptional(Type.Number()), - y: Type.Readonly(Type.Number()), - z: Type.Optional(Type.Number()), - w: Type.Number() - }) - const T = Type.Required(A) - strictEqual(T.properties.x.modifier, ReadonlyModifier) - strictEqual(T.properties.y.modifier, ReadonlyModifier) - strictEqual(T.properties.z.modifier, undefined) - strictEqual(T.properties.w.modifier, undefined) - }) - - it('Should inherit options from the source object', () => { - const A = Type.Object({ - x: Type.Optional(Type.Number()), - y: Type.Optional(Type.Number()), - z: Type.Optional(Type.Number()) - }, { additionalPropeties: false }) - const T = Type.Required(A) - strictEqual(A.additionalPropeties, false) - strictEqual(T.additionalPropeties, false) - }) - - it('Should construct new object when targetting reference', () => { - const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) - const R = Type.Ref(T) - const P = Type.Required(R) - strictEqual(P.properties.a.type, 'string') - strictEqual(P.properties.b.type, 'string') - }) -}) diff --git a/spec/schema/strict.ts b/spec/schema/strict.ts deleted file mode 100644 index 4329cd079..000000000 --- a/spec/schema/strict.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Required', () => { - - it('Should validate after applying strict', () => { - const A = Type.Object({ - x: Type.Optional(Type.Number()), - y: Type.Optional(Type.Number()), - z: Type.Optional(Type.Number()) - }, { additionalProperties: false }) - const T = Type.Strict(Type.Required(A)) - ok(T, { x: 1, y: 1, z: 1 }) - fail(T, { x: 1, y: 1 }) - fail(T, { x: 1 }) - fail(T, {}) - }) - - it('Should validate if applying $schema.', () => { - const A = Type.Object({ - x: Type.Optional(Type.Number()), - y: Type.Optional(Type.Number()), - z: Type.Optional(Type.Number()) - }, { additionalProperties: false }) - const $schema = 'https://json-schema.org/draft/2019-09/schema' - const T = Type.Strict(Type.Required(A), { $schema }) - ok(T, { x: 1, y: 1, z: 1 }) - fail(T, { x: 1, y: 1 }) - fail(T, { x: 1 }) - fail(T, {}) - }) - -}) diff --git a/spec/schema/string.ts b/spec/schema/string.ts deleted file mode 100644 index c51bb1c2c..000000000 --- a/spec/schema/string.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("String", () => { - it('Should not validate number', () => { - const T = Type.String() - fail(T, 1) - }) - it('Should validate string', () => { - const T = Type.String() - ok(T, 'hello') - }) - it('Should not validate boolean', () => { - const T = Type.String() - fail(T, true) - }) - it('Should not validate array', () => { - const T = Type.String() - fail(T, [1, 2, 3]) - }) - it('Should not validate object', () => { - const T = Type.String() - fail(T, { a: 1, b: 2 }) - }) - it('Should not validate null', () => { - const T = Type.String() - fail(T, null) - }) - it('Should not validate undefined', () => { - const T = Type.String() - fail(T, undefined) - }) - - it('Should validate string format as email', () => { - const T = Type.String({ format: 'email' }) - ok(T, 'name@domain.com') - }) - - it('Should validate string format as uuid', () => { - const T = Type.String({ format: 'uuid' }) - ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') - }) - - it('Should validate string format as iso8601 date', () => { - const T = Type.String({ format: 'date-time' }) - ok(T, '2021-06-11T20:30:00-04:00') - }) -}) - diff --git a/spec/schema/tuple.ts b/spec/schema/tuple.ts deleted file mode 100644 index 659de6565..000000000 --- a/spec/schema/tuple.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { isRegExp } from 'util' -import { ok, fail } from './validate' - -describe('Tuple', () => { - - it('Should validate tuple of [string, number]', () => { - const A = Type.String() - const B = Type.Number() - const T = Type.Tuple([A, B]) - ok(T, ['hello', 42]) - }) - - it('Should not validate tuple of [string, number] when reversed', () => { - const A = Type.String() - const B = Type.Number() - const T = Type.Tuple([A, B]) - fail(T, [42, 'hello']) - }) - - it('Should validate empty tuple []', () => { - const T = Type.Tuple([]) - ok(T, []) - }) - - it('Should not validate empty tuple [] with one or more elements', () => { - const T = Type.Tuple([]) - fail(T, [1]) - }) - - it('Should validate tuple of objects', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Tuple([A, B]) - ok(T, [ - { a: 'hello' }, - { b: 42 }, - ]) - }) - - it('Should not validate tuple of objects when reversed', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Tuple([A, B]) - fail(T, [ - { b: 42 }, - { a: 'hello' }, - ]) - }) - - it('Should not validate tuple when array is less than tuple length', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Tuple([A, B]) - fail(T, [ - { a: 'hello' }, - ]) - }) - - it('Should not validate tuple when array is greater than tuple length', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Tuple([A, B]) - fail(T, [ - { a: 'hello' }, - { b: 42 }, - { b: 42 }, - ]) - }) -}) diff --git a/spec/schema/union.ts b/spec/schema/union.ts deleted file mode 100644 index 3d66dd9a0..000000000 --- a/spec/schema/union.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe('Union', () => { - - it('Should validate union of string, number and boolean', () => { - const A = Type.String() - const B = Type.Number() - const C = Type.Boolean() - const T = Type.Union([A, B, C]) - ok(T, 'hello') - ok(T, true) - ok(T, 42) - }) - - it('Should validate union of objects', () => { - const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) - const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) - const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) - ok(T, { b: 'world' }) - }) - - it('Should fail to validate for descriminated union types', () => { - const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) - const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) - const T = Type.Union([A, B]) - fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } - fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } - }) - - it('Should validate union of objects where properties overlap', () => { - const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) - const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) - const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) // A - ok(T, { a: 'hello', b: 'world' }) // B - }) - - - it('Should validate union of overlapping property of varying type', () => { - const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) - const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) - const T = Type.Union([A, B]) - ok(T, { a: 'hello', b: 42 }) // A - ok(T, { a: 'hello', b: 'world' }) // B - }) - - it('Should validate union of literal strings', () => { - const A = Type.Literal('hello') - const B = Type.Literal('world') - const T = Type.Union([A, B]) - ok(T, 'hello') // A - ok(T, 'world') // B - }) - - it('Should not validate union of literal strings for unknown string', () => { - const A = Type.Literal('hello') - const B = Type.Literal('world') - const T = Type.Union([A, B]) - fail(T, 'foo') // A - fail(T, 'bar') // B - }) -}) - diff --git a/spec/schema/unknown.ts b/spec/schema/unknown.ts deleted file mode 100644 index aea8b4035..000000000 --- a/spec/schema/unknown.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' - -describe("Unknown", () => { - it('Should validate number', () => { - const T = Type.Any() - ok(T, 1) - }) - it('Should validate string', () => { - const T = Type.Any() - ok(T, 'hello') - }) - it('Should validate boolean', () => { - const T = Type.Any() - ok(T, true) - }) - it('Should validate array', () => { - const T = Type.Any() - ok(T, [1, 2, 3]) - }) - it('Should validate object', () => { - const T = Type.Any() - ok(T, { a: 1, b: 2 }) - }) - it('Should validate null', () => { - const T = Type.Any() - ok(T, null) - }) - it('Should validate undefined', () => { - const T = Type.Any() - ok(T, undefined) - }) -}) diff --git a/spec/schema/validate.ts b/spec/schema/validate.ts deleted file mode 100644 index 50dd77f37..000000000 --- a/spec/schema/validate.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { TSchema } from '@sinclair/typebox' -import addFormats from 'ajv-formats' -import Ajv, {AnySchema} from 'ajv/dist/2019' - -export function validator(additional: AnySchema[]) { - return addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' - ]) - .addKeyword('kind') - .addKeyword('modifier') - .addSchema(additional) -} - - -export function ok(type: T, data: unknown, additional: AnySchema[] = []) { - const ajv = validator(additional) - function execute() { // required as ajv will throw if referenced schema is not found - try { return ajv.validate(type, data) as boolean } catch { return false } - } - if (execute() === false) { - console.log('---------------------------') - console.log('type') - console.log('---------------------------') - console.log(JSON.stringify(type, null, 2)) - console.log('---------------------------') - console.log('data') - console.log('---------------------------') - console.log(JSON.stringify(data, null, 2)) - console.log('---------------------------') - console.log('errors') - console.log('---------------------------') - console.log(ajv.errorsText(ajv.errors)) - throw Error('expected ok') - } -} - -export function fail(type: T, data: unknown, additional: AnySchema[] = []) { - const ajv = validator(additional) - function execute() { // required as ajv will throw if referenced schema is not found - try { return ajv.validate(type, data) as boolean } catch { return false } - } - if (execute() === true) { - console.log('---------------------------') - console.log('type') - console.log('---------------------------') - console.log(JSON.stringify(type, null, 2)) - console.log('---------------------------') - console.log('data') - console.log('---------------------------') - console.log(JSON.stringify(data, null, 2)) - console.log('---------------------------') - console.log('errors') - console.log('---------------------------') - console.log(ajv.errorsText(ajv.errors)) - throw Error('expected ok') - } -} \ No newline at end of file diff --git a/spec/types/any.ts b/spec/types/any.ts deleted file mode 100644 index 00dcbbca0..000000000 --- a/spec/types/any.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.Any())) diff --git a/spec/types/array.ts b/spec/types/array.ts deleted file mode 100644 index 20e21336b..000000000 --- a/spec/types/array.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -const T0 = Type.Array(Type.String()) - -Spec.expectType(Spec.infer(T0)) - -const T1 = Type.Array(Type.Object({ - x: Type.Number(), - y: Type.Boolean(), - z: Type.String() -})) - -Spec.expectType<{x: number, y: boolean, z: string}[]>(Spec.infer(T1)) - -const T2 = Type.Array(Type.Array(Type.String())) - -Spec.expectType(Spec.infer(T2)) - -const T3 = Type.Array(Type.Tuple([Type.String(), Type.Number()])) - -Spec.expectType<[string, number][]>(Spec.infer(T3)) diff --git a/spec/types/boolean.ts b/spec/types/boolean.ts deleted file mode 100644 index 5f468bae3..000000000 --- a/spec/types/boolean.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.Boolean())) diff --git a/spec/types/enum.ts b/spec/types/enum.ts deleted file mode 100644 index cfeef50de..000000000 --- a/spec/types/enum.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -enum E { A, B = 'hello', C = 42 } - -const T = Type.Enum(E) - -Spec.expectType(Spec.infer(T)) \ No newline at end of file diff --git a/spec/types/intersect.ts b/spec/types/intersect.ts deleted file mode 100644 index f3fc6f9fd..000000000 --- a/spec/types/intersect.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as Spec from './spec' -import { Type, Static } from './typebox' - -{ - const A = Type.String() - const B = Type.Number() - const T = Type.Intersect([A, B]) - Spec.expectType(Spec.infer(T)) -} -{ - const A = Type.Object({ - A: Type.String(), - B: Type.String() - }) - const B = Type.Object({ - X: Type.Number(), - Y: Type.Number() - }) - const T = Type.Intersect([A, B]) - Spec.expectType<{ - A: string, - B: string - } & { - X: number, - Y: number - }>(Spec.infer(T)) -} -{ // https://github.com/sinclairzx81/typebox/issues/113 - const A = Type.Object({ A: Type.String() }) - const B = Type.Object({ B: Type.String() }) - const C = Type.Object({ C: Type.String() }) - const T = Type.Intersect([A, Type.Union([B, C])]) - type T = Static - const _0: T = { A: '', B: '' } - const _1: T = { A: '', C: '' } - const _3: T = { A: '', B: '', C: '' } - Spec.expectType<{ A: string } & ({ B: string, } | { C: string })>(Spec.infer(T)) -} diff --git a/spec/types/keyof.ts b/spec/types/keyof.ts deleted file mode 100644 index b9a830ff3..000000000 --- a/spec/types/keyof.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const K = Type.KeyOf( - Type.Object({ - A: Type.Null(), - B: Type.Null(), - C: Type.Null(), - }) - ) - - Spec.expectType<'A' | 'B' | 'C'>(Spec.infer(K)) -} - -{ - const Q = Type.Pick( - Type.Object({ - A: Type.Null(), - B: Type.Null(), - C: Type.Null(), - }), ['A', 'B'] - ) - - const K = Type.KeyOf( - Type.Pick( - Type.Object({ - A: Type.Null(), - B: Type.Null(), - C: Type.Null(), - }), ['A', 'B'] - ) - ) - - Spec.expectType<'A' | 'B'>(Spec.infer(K)) -} - -{ - const K = Type.KeyOf( - Type.Omit( - Type.Object({ - A: Type.Null(), - B: Type.Null(), - C: Type.Null(), - }), ['A', 'B'] - ) - ) - Spec.expectType<'C'>(Spec.infer(K)) -} \ No newline at end of file diff --git a/spec/types/literal.ts b/spec/types/literal.ts deleted file mode 100644 index 982584675..000000000 --- a/spec/types/literal.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType<'hello'>(Spec.infer(Type.Literal('hello'))) - -Spec.expectType(Spec.infer(Type.Literal(true))) - -Spec.expectType<42>(Spec.infer(Type.Literal(42))) \ No newline at end of file diff --git a/spec/types/modifier.ts b/spec/types/modifier.ts deleted file mode 100644 index 4b1ae11fc..000000000 --- a/spec/types/modifier.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -// Asserts combinatory modifiers -{ - const T = Type.Object({ - A: Type.ReadonlyOptional(Type.String()), - B: Type.Readonly(Type.String()), - C: Type.Optional(Type.String()), - D: Type.String() - }) - Spec.expectType<{ - readonly A?: string, - readonly B: string, - C?: string, - D: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/namespace.ts b/spec/types/namespace.ts deleted file mode 100644 index 5e580ed82..000000000 --- a/spec/types/namespace.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Namespace({ - Vector2: Type.Object({ - X: Type.Number(), - Y: Type.Number(), - }) - }, { $id: 'Math' }) - - Spec.expectType<{ - X: number, - Y: number - }>(Spec.infer(T['definitions']['Vector2'])) -} \ No newline at end of file diff --git a/spec/types/null.ts b/spec/types/null.ts deleted file mode 100644 index e3a49fa0e..000000000 --- a/spec/types/null.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.Null())) diff --git a/spec/types/number.ts b/spec/types/number.ts deleted file mode 100644 index 9c3a761b5..000000000 --- a/spec/types/number.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.Number())) - diff --git a/spec/types/object.ts b/spec/types/object.ts deleted file mode 100644 index 3b112a608..000000000 --- a/spec/types/object.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }) - - Spec.expectType<{ - A: string, - B: string, - C: string - }>(Spec.infer(T)) -} -{ - const T = Type.Object({ - A: Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }), - B: Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }), - C: Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }) - }) - Spec.expectType<{ - A: { - A: string, - B: string, - C: string - }, - B: { - A: string, - B: string, - C: string - }, - C: { - A: string, - B: string, - C: string - } - }>(Spec.infer(T)) -} - - diff --git a/spec/types/omit.ts b/spec/types/omit.ts deleted file mode 100644 index 5614036b2..000000000 --- a/spec/types/omit.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Omit( - Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }), - ['C']) - - Spec.expectType<{ - A: string, - B: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/optional.ts b/spec/types/optional.ts deleted file mode 100644 index 1de0fd3c4..000000000 --- a/spec/types/optional.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Object({ - A: Type.Optional(Type.String()) - }) - Spec.expectType<{ - A?: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/partial.ts b/spec/types/partial.ts deleted file mode 100644 index 63ee73fbc..000000000 --- a/spec/types/partial.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Partial( - Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }), - ) - - Spec.expectType<{ - A?: string, - B?: string, - C?: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/pick.ts b/spec/types/pick.ts deleted file mode 100644 index 73c4ac527..000000000 --- a/spec/types/pick.ts +++ /dev/null @@ -1,17 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Pick( - Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - }), - ['A', 'B']) - - Spec.expectType<{ - A: string, - B: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/readonly-optional.ts b/spec/types/readonly-optional.ts deleted file mode 100644 index 59b460adf..000000000 --- a/spec/types/readonly-optional.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Object({ - A: Type.ReadonlyOptional(Type.String()) - }) - Spec.expectType<{ - readonly A?: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/readonly.ts b/spec/types/readonly.ts deleted file mode 100644 index 9b1ff7f24..000000000 --- a/spec/types/readonly.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Object({ - A: Type.Readonly(Type.String()) - }) - - Spec.expectType<{ - readonly A: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/rec.ts b/spec/types/rec.ts deleted file mode 100644 index 131c68049..000000000 --- a/spec/types/rec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Rec(Self => Type.Object({ - id: Type.String(), - nodes: Type.Array(Self) - })) - - Spec.expectType<{ - id: string, - nodes: { id: string, nodes: [] }[] - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/record.ts b/spec/types/record.ts deleted file mode 100644 index 0d02cd207..000000000 --- a/spec/types/record.ts +++ /dev/null @@ -1,85 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' -{ - // type K = string - const K = Type.String() - - Spec.expectType< - Record - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} -{ - // type K = string - const K = Type.RegEx(/foo|bar/) - - Spec.expectType< - Record - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} -{ - // type K = number - const K = Type.Number() - - Spec.expectType< - Record - >(Spec.infer( - Type.Record(K, Type.Number())) - ) - - Spec.expectType< - Record - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} -{ - // type K = 'A' | 'B' | 'C' - const K = Type.Union([ - Type.Literal('A'), - Type.Literal('B'), - Type.Literal('C') - ]) - - Spec.expectType< - Record<'A' | 'B' | 'C', number> - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} -{ - // type K = keyof { A: number, B: number, C: number } - const K = Type.KeyOf( - Type.Object({ - A: Type.Number(), - B: Type.Number(), - C: Type.Number() - }) - ) - Spec.expectType< - Record<'A' | 'B' | 'C', number> - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} -{ - // type K = keyof Omit<{ A: number, B: number, C: number }, 'C'> - const K = Type.KeyOf( - Type.Omit( - Type.Object({ - A: Type.Number(), - B: Type.Number(), - C: Type.Number() - }), - ['C'] - ) - ) - Spec.expectType< - Record<'A' | 'B', number> - >(Spec.infer( - Type.Record(K, Type.Number())) - ) -} \ No newline at end of file diff --git a/spec/types/ref.ts b/spec/types/ref.ts deleted file mode 100644 index 2089da146..000000000 --- a/spec/types/ref.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.String({ $id: 'T' }) - - const R = Type.Ref(T) - - Spec.expectType(Spec.infer(R)) - -} -{ - const T = Type.Namespace({ - Vector2: Type.Object({ - X: Type.Number(), - Y: Type.Number(), - }) - }, { $id: 'Math' }) - - const R = Type.Ref(T, 'Vector2') - - Spec.expectType<{ - X: number, - Y: number - }>(Spec.infer(R)) -} \ No newline at end of file diff --git a/spec/types/regex.ts b/spec/types/regex.ts deleted file mode 100644 index 4bfabc9af..000000000 --- a/spec/types/regex.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.RegEx(/foo/))) diff --git a/spec/types/required.ts b/spec/types/required.ts deleted file mode 100644 index f4f0dc5fe..000000000 --- a/spec/types/required.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as Spec from './spec' -import { Type, Static } from './typebox' - -{ - const T = Type.Required( - Type.Object({ - A: Type.Optional(Type.String()), - B: Type.Optional(Type.String()), - C: Type.Optional(Type.String()) - }), - ) - - Spec.expectType<{ - A: string, - B: string, - C: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/spec.ts b/spec/types/spec.ts deleted file mode 100644 index 2f7f31bea..000000000 --- a/spec/types/spec.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TSchema, Static } from './typebox' - -export const infer = (_: T): Static => null as any as Static - -export * from 'tsd' - - - diff --git a/spec/types/strict.ts b/spec/types/strict.ts deleted file mode 100644 index 24dba3044..000000000 --- a/spec/types/strict.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Strict(Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String() - })) - - Spec.expectType<{ - A: string, - B: string, - C: string - }>(Spec.infer(T)) -} \ No newline at end of file diff --git a/spec/types/string.ts b/spec/types/string.ts deleted file mode 100644 index f4e8578bb..000000000 --- a/spec/types/string.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.String())) diff --git a/spec/types/tuple.ts b/spec/types/tuple.ts deleted file mode 100644 index db323731d..000000000 --- a/spec/types/tuple.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const T = Type.Tuple([ - Type.Number(), - Type.String(), - Type.Boolean() - ]) - - Spec.expectType<[number, string, boolean]>(Spec.infer(T)) -} diff --git a/spec/types/typebox.d.ts b/spec/types/typebox.d.ts deleted file mode 100644 index 3eb849c20..000000000 --- a/spec/types/typebox.d.ts +++ /dev/null @@ -1,348 +0,0 @@ -export declare const ReadonlyOptionalModifier: unique symbol; -export declare const OptionalModifier: unique symbol; -export declare const ReadonlyModifier: unique symbol; -export declare type TModifier = TReadonlyOptional | TOptional | TReadonly; -export declare type TReadonlyOptional = T & { - modifier: typeof ReadonlyOptionalModifier; -}; -export declare type TOptional = T & { - modifier: typeof OptionalModifier; -}; -export declare type TReadonly = T & { - modifier: typeof ReadonlyModifier; -}; -export declare const BoxKind: unique symbol; -export declare const KeyOfKind: unique symbol; -export declare const IntersectKind: unique symbol; -export declare const UnionKind: unique symbol; -export declare const TupleKind: unique symbol; -export declare const ObjectKind: unique symbol; -export declare const RecordKind: unique symbol; -export declare const ArrayKind: unique symbol; -export declare const EnumKind: unique symbol; -export declare const LiteralKind: unique symbol; -export declare const StringKind: unique symbol; -export declare const NumberKind: unique symbol; -export declare const IntegerKind: unique symbol; -export declare const BooleanKind: unique symbol; -export declare const NullKind: unique symbol; -export declare const UnknownKind: unique symbol; -export declare const AnyKind: unique symbol; -export declare const RefKind: unique symbol; -export interface CustomOptions { - $id?: string; - title?: string; - description?: string; - default?: any; - examples?: any; - [prop: string]: any; -} -export declare type StringFormatOption = 'date-time' | 'time' | 'date' | 'email' | 'idn-email' | 'hostname' | 'idn-hostname' | 'ipv4' | 'ipv6' | 'uri' | 'uri-reference' | 'iri' | 'uuid' | 'iri-reference' | 'uri-template' | 'json-pointer' | 'relative-json-pointer' | 'regex'; -export declare type StringOptions = { - minLength?: number; - maxLength?: number; - pattern?: string; - format?: TFormat; - contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64'; - contentMediaType?: string; -} & CustomOptions; -export declare type ArrayOptions = { - uniqueItems?: boolean; - minItems?: number; - maxItems?: number; -} & CustomOptions; -export declare type NumberOptions = { - exclusiveMaximum?: number; - exclusiveMinimum?: number; - maximum?: number; - minimum?: number; - multipleOf?: number; -} & CustomOptions; -export declare type IntersectOptions = { - unevaluatedProperties?: boolean; -} & CustomOptions; -export declare type ObjectOptions = { - additionalProperties?: boolean; -} & CustomOptions; -export declare type TDefinitions = { - [key: string]: TSchema; -}; -export declare type TNamespace = { - kind: typeof BoxKind; - $defs: T; -} & CustomOptions; -export interface TSchema { - $static: unknown; -} -export declare type TEnumType = Record; -export declare type TKey = string | number | symbol; -export declare type TValue = string | number | boolean; -export declare type TRecordKey = TString | TNumber | TKeyOf | TUnion; -export declare type TEnumKey = { - type: 'number' | 'string'; - const: T; -}; -export interface TProperties { - [key: string]: TSchema; -} -export interface TRecord extends TSchema, ObjectOptions { - $static: StaticRecord; - kind: typeof RecordKind; - type: 'object'; - patternProperties: { - [pattern: string]: T; - }; -} -export interface TTuple extends TSchema, CustomOptions { - $static: StaticTuple; - kind: typeof TupleKind; - type: 'array'; - items?: T; - additionalItems?: false; - minItems: number; - maxItems: number; -} -export interface TObject extends TSchema, ObjectOptions { - $static: StaticObject; - kind: typeof ObjectKind; - type: 'object'; - properties: T; - required?: string[]; -} -export interface TUnion extends TSchema, CustomOptions { - $static: StaticUnion; - kind: typeof UnionKind; - anyOf: T; -} -export interface TIntersect extends TSchema, IntersectOptions { - $static: StaticIntersect; - kind: typeof IntersectKind; - type: 'object'; - allOf: T; -} -export interface TKeyOf extends TSchema, CustomOptions { - $static: StaticKeyOf; - kind: typeof KeyOfKind; - type: 'string'; - enum: T; -} -export interface TArray extends TSchema, ArrayOptions { - $static: StaticArray; - kind: typeof ArrayKind; - type: 'array'; - items: T; -} -export interface TLiteral extends TSchema, CustomOptions { - $static: StaticLiteral; - kind: typeof LiteralKind; - const: T; -} -export interface TEnum extends TSchema, CustomOptions { - $static: StaticEnum; - kind: typeof EnumKind; - anyOf: T; -} -export interface TRef extends TSchema, CustomOptions { - $static: Static; - kind: typeof RefKind; - $ref: string; -} -export interface TString extends TSchema, StringOptions { - $static: string; - kind: typeof StringKind; - type: 'string'; -} -export interface TNumber extends TSchema, NumberOptions { - $static: number; - kind: typeof NumberKind; - type: 'number'; -} -export interface TInteger extends TSchema, NumberOptions { - $static: number; - kind: typeof IntegerKind; - type: 'integer'; -} -export interface TBoolean extends TSchema, CustomOptions { - $static: boolean; - kind: typeof BooleanKind; - type: 'boolean'; -} -export interface TNull extends TSchema, CustomOptions { - $static: null; - kind: typeof NullKind; - type: 'null'; -} -export interface TUnknown extends TSchema, CustomOptions { - $static: unknown; - kind: typeof UnknownKind; -} -export interface TAny extends TSchema, CustomOptions { - $static: any; - kind: typeof AnyKind; -} -export declare const ConstructorKind: unique symbol; -export declare const FunctionKind: unique symbol; -export declare const PromiseKind: unique symbol; -export declare const UndefinedKind: unique symbol; -export declare const VoidKind: unique symbol; -export interface TConstructor extends TSchema, CustomOptions { - $static: StaticConstructor; - kind: typeof ConstructorKind; - type: 'constructor'; - arguments: TSchema[]; - returns: TSchema; -} -export interface TFunction extends TSchema, CustomOptions { - $static: StaticFunction; - kind: typeof FunctionKind; - type: 'function'; - arguments: TSchema[]; - returns: TSchema; -} -export interface TPromise extends TSchema, CustomOptions { - $static: StaticPromise; - kind: typeof PromiseKind; - type: 'promise'; - item: TSchema; -} -export interface TUndefined extends TSchema, CustomOptions { - $static: undefined; - kind: typeof UndefinedKind; - type: 'undefined'; -} -export interface TVoid extends TSchema, CustomOptions { - $static: void; - kind: typeof VoidKind; - type: 'void'; -} -export declare type Selectable = TObject | TRef>; -export declare type SelectablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never; -export declare type SelectableProperties = T extends TObject ? U : T extends TRef> ? U : never; -export declare type UnionToIntersect = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; -export declare type StaticReadonlyOptionalPropertyKeys = { - [K in keyof T]: T[K] extends TReadonlyOptional ? K : never; -}[keyof T]; -export declare type StaticReadonlyPropertyKeys = { - [K in keyof T]: T[K] extends TReadonly ? K : never; -}[keyof T]; -export declare type StaticOptionalPropertyKeys = { - [K in keyof T]: T[K] extends TOptional ? K : never; -}[keyof T]; -export declare type StaticRequiredPropertyKeys = keyof Omit | StaticReadonlyPropertyKeys | StaticOptionalPropertyKeys>; -export declare type StaticIntersectEvaluate = { - [K in keyof T]: T[K] extends TSchema ? Static : never; -}; -export declare type StaticIntersectReduce = T extends [infer A, ...infer B] ? StaticIntersectReduce : I; -export declare type StaticRequired = { - [K in keyof T]: T[K] extends TReadonlyOptional ? TReadonly : T[K] extends TReadonly ? TReadonly : T[K] extends TOptional ? U : T[K]; -}; -export declare type StaticPartial = { - [K in keyof T]: T[K] extends TReadonlyOptional ? TReadonlyOptional : T[K] extends TReadonly ? TReadonlyOptional : T[K] extends TOptional ? TOptional : TOptional; -}; -export declare type StaticProperties = { - readonly [K in StaticReadonlyOptionalPropertyKeys]?: Static; -} & { - readonly [K in StaticReadonlyPropertyKeys]: Static; -} & { - [K in StaticOptionalPropertyKeys]?: Static; -} & { - [K in StaticRequiredPropertyKeys]: Static; -}; -export declare type StaticRecord = K extends TString ? Record> : K extends TNumber ? Record> : K extends TKeyOf ? Record> : K extends TUnion ? Record> : never; -export declare type StaticEnum = T extends TEnumKey[] ? U : never; -export declare type StaticKeyOf = T extends Array ? K : never; -export declare type StaticIntersect = StaticIntersectReduce>; -export declare type StaticUnion = { - [K in keyof T]: T[K] extends TSchema ? Static : never; -}[number]; -export declare type StaticTuple = { - [K in keyof T]: T[K] extends TSchema ? Static : never; -}; -export declare type StaticObject = StaticProperties extends infer I ? { - [K in keyof I]: I[K]; -} : never; -export declare type StaticArray = Array>; -export declare type StaticLiteral = T; -export declare type StaticParameters = { - [K in keyof T]: T[K] extends TSchema ? Static : never; -}; -export declare type StaticConstructor = new (...args: [...StaticParameters]) => Static; -export declare type StaticFunction = (...args: [...StaticParameters]) => Static; -export declare type StaticPromise = Promise>; -export declare type Static = T['$static']; -export declare class TypeBuilder { - protected readonly schemas: Map; - /** `Standard` Modifies an object property to be both readonly and optional */ - ReadonlyOptional(item: T): TReadonlyOptional; - /** `Standard` Modifies an object property to be readonly */ - Readonly(item: T): TReadonly; - /** `Standard` Modifies an object property to be optional */ - Optional(item: T): TOptional; - /** `Standard` Creates a type type */ - Tuple(items: [...T], options?: CustomOptions): TTuple; - /** `Standard` Creates an object type with the given properties */ - Object(properties: T, options?: ObjectOptions): TObject; - /** `Standard` Creates an intersect type. */ - Intersect(items: [...T], options?: IntersectOptions): TIntersect; - /** `Standard` Creates a union type */ - Union(items: [...T], options?: CustomOptions): TUnion; - /** `Standard` Creates an array type */ - Array(items: T, options?: ArrayOptions): TArray; - /** `Standard` Creates an enum type from a TypeScript enum */ - Enum(item: T, options?: CustomOptions): TEnum[]>; - /** `Standard` Creates a literal type. Supports string, number and boolean values only */ - Literal(value: T, options?: CustomOptions): TLiteral; - /** `Standard` Creates a string type */ - String(options?: StringOptions): TString; - /** `Standard` Creates a string type from a regular expression */ - RegEx(regex: RegExp, options?: CustomOptions): TString; - /** `Standard` Creates a number type */ - Number(options?: NumberOptions): TNumber; - /** `Standard` Creates an integer type */ - Integer(options?: NumberOptions): TInteger; - /** `Standard` Creates a boolean type */ - Boolean(options?: CustomOptions): TBoolean; - /** `Standard` Creates a null type */ - Null(options?: CustomOptions): TNull; - /** `Standard` Creates an unknown type */ - Unknown(options?: CustomOptions): TUnknown; - /** `Standard` Creates an any type */ - Any(options?: CustomOptions): TAny; - /** `Standard` Creates a record type */ - Record(key: K, value: T, options?: ObjectOptions): TRecord; - /** `Standard` Creates a keyof type from the given object */ - KeyOf | TRef>>(object: T, options?: CustomOptions): TKeyOf[]>; - /** `Standard` Makes all properties in the given object type required */ - Required | TRef>>(object: T, options?: ObjectOptions): TObject>; - /** `Standard` Makes all properties in the given object type optional */ - Partial | TRef>>(object: T, options?: ObjectOptions): TObject>; - /** `Standard` Picks property keys from the given object type */ - Pick | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; - /** `Standard` Omits property keys from the given object type */ - Omit | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options?: ObjectOptions): TObject, K[number]>>; - /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */ - Strict(schema: T, options?: CustomOptions): T; - /** `Extended` Creates a constructor type */ - Constructor(args: [...T], returns: U, options?: CustomOptions): TConstructor; - /** `Extended` Creates a function type */ - Function(args: [...T], returns: U, options?: CustomOptions): TFunction; - /** `Extended` Creates a promise type */ - Promise(item: T, options?: CustomOptions): TPromise; - /** `Extended` Creates a undefined type */ - Undefined(options?: CustomOptions): TUndefined; - /** `Extended` Creates a void type */ - Void(options?: CustomOptions): TVoid; - /** `Standard` Creates a namespace for a set of related types */ - Namespace($defs: T, options?: CustomOptions): TNamespace; - /** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */ - Ref, K extends keyof T['$defs']>(namespace: T, key: K): TRef; - /** `Standard` References type. The referenced type must specify an `$id` */ - Ref(schema: T): TRef; - /** `Experimental` Creates a recursive type */ - Rec(callback: (self: TAny) => T, options?: CustomOptions): T; - /** Conditionally stores and schema if it contains an $id and returns */ - protected Store, S = Omit>(schema: S): T; - /** Conditionally dereferences a schema if RefKind. Otherwise return argument */ - protected Deref(schema: T): any; -} -export declare const Type: TypeBuilder; diff --git a/spec/types/union.ts b/spec/types/union.ts deleted file mode 100644 index 892a25e74..000000000 --- a/spec/types/union.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -{ - const A = Type.String() - const B = Type.Number() - const T = Type.Union([A, B]) - Spec.expectType(Spec.infer(T)) -} -{ - const A = Type.Object({ - A: Type.String(), - B: Type.String() - }) - const B = Type.Object({ - X: Type.Number(), - Y: Type.Number() - }) - const T = Type.Union([A, B]) - Spec.expectType<{ - A: string, - B: string - } | { - X: number, - Y: number - }>(Spec.infer(T)) -} - diff --git a/spec/types/unknown.ts b/spec/types/unknown.ts deleted file mode 100644 index 45a99a05e..000000000 --- a/spec/types/unknown.ts +++ /dev/null @@ -1,4 +0,0 @@ -import * as Spec from './spec' -import { Type } from './typebox' - -Spec.expectType(Spec.infer(Type.Unknown())) \ No newline at end of file diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts new file mode 100644 index 000000000..716b0974b --- /dev/null +++ b/src/compiler/compiler.ts @@ -0,0 +1,416 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/compiler + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { ValueErrors, ValueError } from '../errors/index' +import { TypeGuard } from '../guard/index' +import { Format } from '../format/index' +import * as Types from '../typebox' + +// ------------------------------------------------------------------- +// CheckFunction +// ------------------------------------------------------------------- + +export type CheckFunction = (value: unknown) => boolean + +// ------------------------------------------------------------------- +// TypeCheck +// ------------------------------------------------------------------- + +export class TypeCheck { + constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) {} + + /** Returns the generated validation code used to validate this type. */ + public Code(): string { + return this.code + } + + /** Returns an iterator for each error in this value. */ + public Errors(value: unknown): IterableIterator { + return ValueErrors.Errors(this.schema, this.references, value) + } + + /** Returns true if the value matches the given type. */ + public Check(value: unknown): value is Types.Static { + return this.checkFunc(value) + } +} + +// ------------------------------------------------------------------- +// Property +// ------------------------------------------------------------------- + +export namespace Property { + function DollarSign(code: number) { + return code === 36 + } + function Underscore(code: number) { + return code === 95 + } + function Numeric(code: number) { + return code >= 48 && code <= 57 + } + function Alpha(code: number) { + return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) + } + + export function Check(propertyName: string) { + if (propertyName.length === 0) return false + { + const code = propertyName.charCodeAt(0) + if (!(DollarSign(code) || Underscore(code) || Alpha(code))) { + return false + } + } + for (let i = 1; i < propertyName.length; i++) { + const code = propertyName.charCodeAt(i) + if (!(DollarSign(code) || Underscore(code) || Alpha(code) || Numeric(code))) { + return false + } + } + return true + } +} + +// ------------------------------------------------------------------- +// TypeCompiler +// ------------------------------------------------------------------- + +export class TypeCompilerUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('TypeCompiler: Unknown type') + } +} + +/** Compiles Types for Runtime Type Checking */ +export namespace TypeCompiler { + // ------------------------------------------------------------------- + // Types + // ------------------------------------------------------------------- + + function* Any(schema: Types.TAny, value: string): IterableIterator { + yield '(true)' + } + + function* Array(schema: Types.TArray, value: string): IterableIterator { + const expression = CreateExpression(schema.items, 'value') + if (schema.minItems !== undefined) yield `(${value}.length >= ${schema.minItems})` + if (schema.maxItems !== undefined) yield `(${value}.length <= ${schema.maxItems})` + if (schema.uniqueItems !== undefined) yield `(new Set(${value}).size === ${value}.length)` + yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))` + } + + function* Boolean(schema: Types.TBoolean, value: string): IterableIterator { + yield `(typeof ${value} === 'boolean')` + } + + function* Constructor(schema: Types.TConstructor, value: string): IterableIterator { + yield* Visit(schema.returns, `${value}.prototype`) + } + + function* Function(schema: Types.TFunction, value: string): IterableIterator { + yield `(typeof ${value} === 'function')` + } + + function* Integer(schema: Types.TNumeric, value: string): IterableIterator { + yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` + if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` + if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` + if (schema.exclusiveMaximum !== undefined) yield `(${value} < ${schema.exclusiveMaximum})` + if (schema.minimum !== undefined) yield `(${value} >= ${schema.minimum})` + if (schema.maximum !== undefined) yield `(${value} <= ${schema.maximum})` + } + + function* Literal(schema: Types.TLiteral, value: string): IterableIterator { + if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { + yield `(${value} === ${schema.const})` + } else { + yield `(${value} === '${schema.const}')` + } + } + + function* Never(schema: Types.TNull, value: string): IterableIterator { + yield `(false)` + } + + function* Null(schema: Types.TNull, value: string): IterableIterator { + yield `(${value} === null)` + } + + function* Number(schema: Types.TNumeric, value: string): IterableIterator { + yield `(typeof ${value} === 'number')` + if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` + if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` + if (schema.exclusiveMaximum !== undefined) yield `(${value} < ${schema.exclusiveMaximum})` + if (schema.minimum !== undefined) yield `(${value} >= ${schema.minimum})` + if (schema.maximum !== undefined) yield `(${value} <= ${schema.maximum})` + } + + function* Object(schema: Types.TObject, value: string): IterableIterator { + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` + if (schema.minProperties !== undefined) yield `(Object.keys(${value}).length >= ${schema.minProperties})` + if (schema.maxProperties !== undefined) yield `(Object.keys(${value}).length <= ${schema.maxProperties})` + const propertyKeys = globalThis.Object.keys(schema.properties) + if (schema.additionalProperties === false) { + // Optimization: If the property key length matches the required keys length + // then we only need check that the values property key length matches that + // of the property key length. This is because exhaustive testing for values + // will occur in subsequent property tests. + if (schema.required && schema.required.length === propertyKeys.length) { + yield `(Object.keys(${value}).length === ${propertyKeys.length})` + } else { + const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` + yield `(Object.keys(${value}).every(key => ${keys}.includes(key)))` + } + } + for (const propertyKey of propertyKeys) { + const memberExpression = Property.Check(propertyKey) ? `${value}.${propertyKey}` : `${value}['${propertyKey}']` + const propertySchema = schema.properties[propertyKey] + if (schema.required && schema.required.includes(propertyKey)) { + yield* Visit(propertySchema, memberExpression) + } else { + const expression = CreateExpression(propertySchema, memberExpression) + yield `(${memberExpression} === undefined ? true : (${expression}))` + } + } + } + + function* Promise(schema: Types.TPromise, value: string): IterableIterator { + yield `(typeof value === 'object' && typeof ${value}.then === 'function')` + } + + function* Record(schema: Types.TRecord, value: string): IterableIterator { + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` + const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const local = PushLocal(`new RegExp(/${keyPattern}/)`) + yield `(Object.keys(${value}).every(key => ${local}.test(key)))` + const expression = CreateExpression(valueSchema, 'value') + yield `(Object.values(${value}).every(value => ${expression}))` + } + + function* Ref(schema: Types.TRef, value: string): IterableIterator { + // Reference: If we have seen this reference before we can just yield and return + // the function call. If this isn't the case we defer to visit to generate and + // set the function for subsequent passes. Consider for refactor. + if (names.has(schema.$ref)) return yield `(${CreateFunctionName(schema.$ref)}(${value}))` + if (!referenceMap.has(schema.$ref)) throw Error(`TypeCompiler.Ref: Cannot de-reference schema with $id '${schema.$ref}'`) + const reference = referenceMap.get(schema.$ref)! + yield* Visit(reference, value) + } + + function* Self(schema: Types.TSelf, value: string): IterableIterator { + const func = CreateFunctionName(schema.$ref) + yield `(${func}(${value}))` + } + + function* String(schema: Types.TString, value: string): IterableIterator { + yield `(typeof ${value} === 'string')` + if (schema.minLength !== undefined) { + yield `(${value}.length >= ${schema.minLength})` + } + if (schema.maxLength !== undefined) { + yield `(${value}.length <= ${schema.maxLength})` + } + if (schema.pattern !== undefined) { + const local = PushLocal(`new RegExp(/${schema.pattern}/);`) + yield `(${local}.test(${value}))` + } + if (schema.format !== undefined) { + yield `(format('${schema.format}', ${value}))` + } + } + + function* Tuple(schema: Types.TTuple, value: string): IterableIterator { + yield `(Array.isArray(${value}))` + if (schema.items === undefined) return yield `(${value}.length === 0)` + yield `(${value}.length === ${schema.maxItems})` + for (let i = 0; i < schema.items.length; i++) { + const expression = CreateExpression(schema.items[i], `${value}[${i}]`) + yield `(${expression})` + } + } + + function* Undefined(schema: Types.TUndefined, value: string): IterableIterator { + yield `(${value} === undefined)` + } + + function* Union(schema: Types.TUnion, value: string): IterableIterator { + const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, value)) + yield `(${expressions.join(' || ')})` + } + + function* Uint8Array(schema: Types.TUint8Array, value: string): IterableIterator { + yield `(${value} instanceof Uint8Array)` + if (schema.maxByteLength) yield `(${value}.length <= ${schema.maxByteLength})` + if (schema.minByteLength) yield `(${value}.length >= ${schema.minByteLength})` + } + + function* Unknown(schema: Types.TUnknown, value: string): IterableIterator { + yield '(true)' + } + + function* Void(schema: Types.TVoid, value: string): IterableIterator { + yield `(${value} === null)` + } + + function* Visit(schema: T, value: string): IterableIterator { + // Reference: Referenced schemas can originate from either additional schemas + // or inline in the schema itself. Ideally the recursive path should align to + // reference path. Consider for refactor. + if (schema.$id && !names.has(schema.$id)) { + names.add(schema.$id) + const name = CreateFunctionName(schema.$id) + const body = CreateFunction(name, schema, 'value') + PushFunction(body) + yield `(${name}(${value}))` + return + } + const anySchema = schema as any + switch (anySchema[Types.Kind]) { + case 'Any': + return yield* Any(anySchema, value) + case 'Array': + return yield* Array(anySchema, value) + case 'Boolean': + return yield* Boolean(anySchema, value) + case 'Constructor': + return yield* Constructor(anySchema, value) + case 'Function': + return yield* Function(anySchema, value) + case 'Integer': + return yield* Integer(anySchema, value) + case 'Literal': + return yield* Literal(anySchema, value) + case 'Never': + return yield* Never(anySchema, value) + case 'Null': + return yield* Null(anySchema, value) + case 'Number': + return yield* Number(anySchema, value) + case 'Object': + return yield* Object(anySchema, value) + case 'Promise': + return yield* Promise(anySchema, value) + case 'Record': + return yield* Record(anySchema, value) + case 'Ref': + return yield* Ref(anySchema, value) + case 'Self': + return yield* Self(anySchema, value) + case 'String': + return yield* String(anySchema, value) + case 'Tuple': + return yield* Tuple(anySchema, value) + case 'Undefined': + return yield* Undefined(anySchema, value) + case 'Union': + return yield* Union(anySchema, value) + case 'Uint8Array': + return yield* Uint8Array(anySchema, value) + case 'Unknown': + return yield* Unknown(anySchema, value) + case 'Void': + return yield* Void(anySchema, value) + default: + throw new TypeCompilerUnknownTypeError(schema) + } + } + + // ------------------------------------------------------------------- + // Compile State + // ------------------------------------------------------------------- + + const referenceMap = new Map() + const locals = new Set() // local variables and functions + const names = new Set() // cache of local functions + + function ResetCompiler() { + referenceMap.clear() + locals.clear() + names.clear() + } + + function AddReferences(schemas: Types.TSchema[] = []) { + for (const schema of schemas) { + if (!schema.$id) throw new Error(`TypeCompiler: Referenced schemas must specify an $id.`) + if (referenceMap.has(schema.$id)) throw new Error(`TypeCompiler: Duplicate schema $id found for '${schema.$id}'`) + referenceMap.set(schema.$id, schema) + } + } + + function CreateExpression(schema: Types.TSchema, value: string): string { + return [...Visit(schema, value)].join(' && ') + } + + function CreateFunctionName($id: string) { + return `check_${$id.replace(/-/g, '_')}` + } + + function CreateFunction(name: string, schema: Types.TSchema, value: string): string { + const expression = [...Visit(schema, value)].map((condition) => ` ${condition}`).join(' &&\n') + return `function ${name}(value) {\n return (\n${expression}\n )\n}` + } + + function PushFunction(functionBody: string) { + locals.add(functionBody) + } + + function PushLocal(expression: string) { + const local = `local_${locals.size}` + locals.add(`const ${local} = ${expression}`) + return local + } + + function GetLocals() { + return [...locals.values()] + } + + // ------------------------------------------------------------------- + // Compile + // ------------------------------------------------------------------- + + function Build(schema: T, references: Types.TSchema[] = []): string { + ResetCompiler() + AddReferences(references) + const check = CreateFunction('check', schema, 'value') + const locals = GetLocals() + return `${locals.join('\n')}\nreturn ${check}` + } + + /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */ + export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { + TypeGuard.Assert(schema, references) + const code = Build(schema, references) + const func1 = globalThis.Function('format', code) + const func2 = func1((format: string, value: string) => { + if (!Format.Has(format)) return false + const func = Format.Get(format)! + return func(value) + }) + return new TypeCheck(schema, references, func2, code) + } +} diff --git a/src/compiler/index.ts b/src/compiler/index.ts new file mode 100644 index 000000000..2785e30e6 --- /dev/null +++ b/src/compiler/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/compiler + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export { ValueError, ValueErrorType } from '../errors/index' +export * from './compiler' diff --git a/src/conditional/conditional.ts b/src/conditional/conditional.ts new file mode 100644 index 000000000..dd3e71180 --- /dev/null +++ b/src/conditional/conditional.ts @@ -0,0 +1,114 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/conditional + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { Structural, StructuralResult } from './structural' +import { TypeGuard } from '../guard/index' + +// ------------------------------------------------------------------------ +// Extends +// ------------------------------------------------------------------------ + +export type TExtends = Types.Static extends Types.Static ? T : U + +// ------------------------------------------------------------------------ +// Exclude +// ------------------------------------------------------------------------ + +export interface TExclude extends Types.TUnion { + static: Exclude, Types.Static> +} + +// ------------------------------------------------------------------------ +// Extract +// ------------------------------------------------------------------------ + +export interface TExtract extends Types.TUnion { + static: Extract, Types.Static> +} + +/** Conditional Types */ +export namespace Conditional { + /** (Experimental) Creates a conditional expression type */ + export function Extends(left: L, right: R, ok: T, fail: U): TExtends { + switch (Structural.Check(left, right)) { + case StructuralResult.Union: + return Types.Type.Union([Clone(ok), Clone(fail)]) as any as TExtends + case StructuralResult.True: + return Clone(ok) + case StructuralResult.False: + return Clone(fail) + } + } + + /** (Experimental) Constructs a type by excluding from UnionType all union members that are assignable to ExcludedMembers. */ + export function Exclude(unionType: T, excludedMembers: U, options: Types.SchemaOptions = {}): TExclude { + const anyOf = unionType.anyOf + .filter((schema) => { + const check = Structural.Check(schema, excludedMembers) + return !(check === StructuralResult.True || check === StructuralResult.Union) + }) + .map((schema) => Clone(schema)) + return { ...options, [Types.Kind]: 'Union', anyOf } as any + } + + /** (Experimental) Constructs a type by extracting from Type all union members that are assignable to Union. */ + export function Extract(type: T, union: U, options: Types.SchemaOptions = {}): TExtract { + if (TypeGuard.TUnion(type)) { + const anyOf = type.anyOf.filter((schema: Types.TSchema) => Structural.Check(schema, union) === StructuralResult.True).map((schema: Types.TSchema) => Clone(schema)) + return { ...options, [Types.Kind]: 'Union', anyOf } as any + } else { + const anyOf = union.anyOf.filter((schema) => Structural.Check(type, schema) === StructuralResult.True).map((schema) => Clone(schema)) + return { ...options, [Types.Kind]: 'Union', anyOf } as any + } + } + + function Clone(value: any): any { + const isObject = (object: any): object is Record => typeof object === 'object' && object !== null && !Array.isArray(object) + const isArray = (object: any): object is any[] => typeof object === 'object' && object !== null && Array.isArray(object) + if (isObject(value)) { + return Object.keys(value).reduce( + (acc, key) => ({ + ...acc, + [key]: Clone(value[key]), + }), + Object.getOwnPropertySymbols(value).reduce( + (acc, key) => ({ + ...acc, + [key]: Clone(value[key]), + }), + {}, + ), + ) + } else if (isArray(value)) { + return value.map((item: any) => Clone(item)) + } else { + return value + } + } +} diff --git a/src/conditional/index.ts b/src/conditional/index.ts new file mode 100644 index 000000000..93afa1daa --- /dev/null +++ b/src/conditional/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/conditional + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './conditional' +export * from './structural' diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts new file mode 100644 index 000000000..18790d858 --- /dev/null +++ b/src/conditional/structural.ts @@ -0,0 +1,558 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/conditional + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { TypeGuard } from '../guard' + +// -------------------------------------------------------------------------- +// StructuralResult +// -------------------------------------------------------------------------- + +export enum StructuralResult { + Union, + True, + False, +} + +// -------------------------------------------------------------------------- +// Structural +// -------------------------------------------------------------------------- + +/** Performs structural equivalence checks against TypeBox types. */ +export namespace Structural { + const referenceMap = new Map() + + // ------------------------------------------------------------------------ + // Rules + // ------------------------------------------------------------------------ + + function AnyOrUnknownRule(right: Types.TSchema) { + // https://github.com/microsoft/TypeScript/issues/40049 + if (right[Types.Kind] === 'Union' && right.anyOf.some((schema: Types.TSchema) => schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown')) return true + if (right[Types.Kind] === 'Unknown') return true + if (right[Types.Kind] === 'Any') return true + return false + } + + function ObjectRightRule(left: Types.TAnySchema, right: Types.TObject) { + // type A = boolean extends {} ? 1 : 2 // additionalProperties: false + // type B = boolean extends object ? 1 : 2 // additionalProperties: true + const additionalProperties = right.additionalProperties + const propertyLength = globalThis.Object.keys(right.properties).length + return additionalProperties === false && propertyLength === 0 + } + + function UnionRightRule(left: Types.TAnySchema, right: Types.TUnion): StructuralResult { + const result = right.anyOf.some((right: Types.TSchema) => Visit(left, right) !== StructuralResult.False) + return result ? StructuralResult.True : StructuralResult.False + } + + // ------------------------------------------------------------------------ + // Records + // ------------------------------------------------------------------------ + + function RecordPattern(schema: Types.TRecord) { + return globalThis.Object.keys(schema.patternProperties)[0] as string + } + + function RecordNumberOrStringKey(schema: Types.TRecord) { + const pattern = RecordPattern(schema) + return pattern === '^.*$' || pattern === '^(0|[1-9][0-9]*)$' + } + + function RecordValue(schema: Types.TRecord) { + const pattern = RecordPattern(schema) + return schema.patternProperties[pattern] + } + + function RecordKey(schema: Types.TRecord) { + const pattern = RecordPattern(schema) + if (pattern === '^.*$') { + return Types.Type.String() + } else if (pattern === '^(0|[1-9][0-9]*)$') { + return Types.Type.Number() + } else { + const keys = pattern.slice(1, pattern.length - 1).split('|') + const schemas = keys.map((key) => (isNaN(+key) ? Types.Type.Literal(key) : Types.Type.Literal(parseFloat(key)))) + return Types.Type.Union(schemas) + } + } + + function PropertyMap(schema: Types.TObject | Types.TRecord) { + const comparable = new Map() + if (TypeGuard.TRecord(schema)) { + const propertyPattern = RecordPattern(schema as Types.TRecord) + if (propertyPattern === '^.*$' || propertyPattern === '^(0|[1-9][0-9]*)$') throw Error('Cannot extract record properties without property constraints') + const propertySchema = schema.patternProperties[propertyPattern] as Types.TSchema + const propertyKeys = propertyPattern.slice(1, propertyPattern.length - 1).split('|') + propertyKeys.forEach((propertyKey) => { + comparable.set(propertyKey, propertySchema) + }) + } else { + globalThis.Object.entries(schema.properties).forEach(([propertyKey, propertySchema]) => { + comparable.set(propertyKey, propertySchema as Types.TSchema) + }) + } + return comparable + } + + // ------------------------------------------------------------------------ + // Indexable + // ------------------------------------------------------------------------ + + function Indexable(left: Left, right: Types.TSchema): StructuralResult { + if (TypeGuard.TUnion(right)) { + return StructuralResult.False + } else { + return Visit(left, right) + } + } + + // ------------------------------------------------------------------------ + // Checks + // ------------------------------------------------------------------------ + + function Any(left: Types.TAny, right: Types.TSchema): StructuralResult { + return AnyOrUnknownRule(right) ? StructuralResult.True : StructuralResult.Union + } + + function Array(left: Types.TArray, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True + if (globalThis.Object.keys(right.properties).length === 0) return StructuralResult.True + return StructuralResult.False + } else if (!TypeGuard.TArray(right)) { + return StructuralResult.False + } else if (left.items === undefined && right.items !== undefined) { + return StructuralResult.False + } else if (left.items !== undefined && right.items === undefined) { + return StructuralResult.False + } else if (left.items === undefined && right.items === undefined) { + return StructuralResult.False + } else { + const result = Visit(left.items, right.items) !== StructuralResult.False + return result ? StructuralResult.True : StructuralResult.False + } + } + + function Boolean(left: Types.TBoolean, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TBoolean(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Constructor(left: Types.TConstructor, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && globalThis.Object.keys(right.properties).length === 0) { + return StructuralResult.True + } else if (!TypeGuard.TConstructor(right)) { + return StructuralResult.False + } else if (right.parameters.length < left.parameters.length) { + return StructuralResult.False + } else { + if (Visit(left.returns, right.returns) === StructuralResult.False) { + return StructuralResult.False + } + for (let i = 0; i < left.parameters.length; i++) { + const result = Visit(right.parameters[i], left.parameters[i]) + if (result === StructuralResult.False) return StructuralResult.False + } + return StructuralResult.True + } + } + + function Function(left: Types.TFunction, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True + if (globalThis.Object.keys(right.properties).length === 0) return StructuralResult.True + return StructuralResult.False + } else if (!TypeGuard.TFunction(right)) { + return StructuralResult.False + } else if (right.parameters.length < left.parameters.length) { + return StructuralResult.False + } else if (Visit(left.returns, right.returns) === StructuralResult.False) { + return StructuralResult.False + } else { + for (let i = 0; i < left.parameters.length; i++) { + const result = Visit(right.parameters[i], left.parameters[i]) + if (result === StructuralResult.False) return StructuralResult.False + } + return StructuralResult.True + } + } + + function Integer(left: Types.TInteger, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TInteger(right) || TypeGuard.TNumber(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Literal(left: Types.TLiteral, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TRecord(right)) { + if (typeof left.const === 'string') { + return Indexable(left, RecordValue(right as Types.TRecord)) + } else { + return StructuralResult.False + } + } else if (TypeGuard.TLiteral(right) && left.const === right.const) { + return StructuralResult.True + } else if (TypeGuard.TString(right) && typeof left.const === 'string') { + return StructuralResult.True + } else if (TypeGuard.TNumber(right) && typeof left.const === 'number') { + return StructuralResult.True + } else if (TypeGuard.TInteger(right) && typeof left.const === 'number') { + return StructuralResult.True + } else if (TypeGuard.TBoolean(right) && typeof left.const === 'boolean') { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Number(left: Types.TNumber, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TNumber(right)) { + return StructuralResult.True + } else if (TypeGuard.TInteger(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Null(left: Types.TNull, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TNull(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Properties(left: Map, right: Map) { + if (right.size > left.size) return StructuralResult.False + if (![...right.keys()].every((rightKey) => left.has(rightKey))) return StructuralResult.False + for (const rightKey of right.keys()) { + const leftProp = left.get(rightKey)! + const rightProp = right.get(rightKey)! + if (Visit(leftProp, rightProp) === StructuralResult.False) { + return StructuralResult.False + } + } + return StructuralResult.True + } + + function Object(left: Types.TObject, right: Types.TAnySchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + return Properties(PropertyMap(left), PropertyMap(right)) + } else if (TypeGuard.TRecord(right)) { + if (!RecordNumberOrStringKey(right as Types.TRecord)) { + return Properties(PropertyMap(left), PropertyMap(right)) + } else { + return StructuralResult.True + } + } else { + return StructuralResult.False + } + } + + function Promise(left: Types.TPromise, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + if (ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0) { + return StructuralResult.True + } else { + return StructuralResult.False + } + } else if (!TypeGuard.TPromise(right)) { + return StructuralResult.False + } else { + const result = Visit(left.item, right.item) !== StructuralResult.False + return result ? StructuralResult.True : StructuralResult.False + } + } + + function Record(left: Types.TRecord, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + if (RecordPattern(left) === '^.*$' && right[Types.Hint] === 'Record') { + return StructuralResult.True + } else if (RecordPattern(left) === '^.*$') { + return StructuralResult.False + } else { + return globalThis.Object.keys(right.properties).length === 0 ? StructuralResult.True : StructuralResult.False + } + } else if (TypeGuard.TRecord(right)) { + if (!RecordNumberOrStringKey(left as Types.TRecord) && !RecordNumberOrStringKey(right as Types.TRecord)) { + return Properties(PropertyMap(left), PropertyMap(right)) + } else if (RecordNumberOrStringKey(left as Types.TRecord) && !RecordNumberOrStringKey(right as Types.TRecord)) { + const leftKey = RecordKey(left as Types.TRecord) + const rightKey = RecordKey(right as Types.TRecord) + if (Visit(rightKey, leftKey) === StructuralResult.False) { + return StructuralResult.False + } else { + return StructuralResult.True + } + } else { + return StructuralResult.True + } + } else { + return StructuralResult.False + } + } + + function Ref(left: Types.TRef, right: Types.TSchema): StructuralResult { + if (!referenceMap.has(left.$ref)) throw Error(`Cannot locate referenced $id '${left.$ref}'`) + const resolved = referenceMap.get(left.$ref)! + return Visit(resolved, right) + } + + function Self(left: Types.TSelf, right: Types.TSchema): StructuralResult { + if (!referenceMap.has(left.$ref)) throw Error(`Cannot locate referenced self $id '${left.$ref}'`) + const resolved = referenceMap.get(left.$ref)! + return Visit(resolved, right) + } + + function String(left: Types.TString, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TRecord(right)) { + return Indexable(left, RecordValue(right)) + } else if (TypeGuard.TString(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Tuple(left: Types.TTuple, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right)) { + const result = ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0 + return result ? StructuralResult.True : StructuralResult.False + } else if (TypeGuard.TRecord(right)) { + return Indexable(left, RecordValue(right)) + } else if (TypeGuard.TArray(right)) { + if (right.items === undefined) { + return StructuralResult.False + } else if (TypeGuard.TUnion(right.items) && left.items) { + const result = left.items.every((left: Types.TSchema) => UnionRightRule(left, right.items as Types.TUnion) !== StructuralResult.False) + return result ? StructuralResult.True : StructuralResult.False + } else if (TypeGuard.TAny(right.items)) { + return StructuralResult.True + } else { + return StructuralResult.False + } + } + if (!TypeGuard.TTuple(right)) return StructuralResult.False + if (left.items === undefined && right.items === undefined) return StructuralResult.True + if (left.items === undefined && right.items !== undefined) return StructuralResult.False + if (left.items !== undefined && right.items === undefined) return StructuralResult.False + if (left.items === undefined && right.items === undefined) return StructuralResult.True + if (left.minItems !== right.minItems || left.maxItems !== right.maxItems) return StructuralResult.False + for (let i = 0; i < left.items!.length; i++) { + if (Visit(left.items![i], right.items![i]) === StructuralResult.False) return StructuralResult.False + } + return StructuralResult.True + } + + function Uint8Array(left: Types.TUint8Array, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TRecord(right)) { + return Indexable(left, RecordValue(right as Types.TRecord)) + } else if (TypeGuard.TUint8Array(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Undefined(left: Types.TUndefined, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TUndefined(right)) { + return StructuralResult.True + } else if (TypeGuard.TVoid(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + + function Union(left: Types.TUnion, right: Types.TSchema): StructuralResult { + if (left.anyOf.some((left: Types.TSchema) => TypeGuard.TAny(left))) { + return StructuralResult.Union + } else if (TypeGuard.TUnion(right)) { + const result = left.anyOf.every((left: Types.TSchema) => right.anyOf.some((right: Types.TSchema) => Visit(left, right) !== StructuralResult.False)) + return result ? StructuralResult.True : StructuralResult.False + } else { + const result = left.anyOf.every((left: Types.TSchema) => Visit(left, right) !== StructuralResult.False) + return result ? StructuralResult.True : StructuralResult.False + } + } + + function Unknown(left: Types.TUnknown, right: Types.TSchema): StructuralResult { + if (TypeGuard.TUnion(right)) { + const result = right.anyOf.some((right: Types.TSchema) => TypeGuard.TAny(right) || TypeGuard.TUnknown(right)) + return result ? StructuralResult.True : StructuralResult.False + } else if (TypeGuard.TAny(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnknown(right)) { + return StructuralResult.True + } else { + return StructuralResult.False + } + } + + function Void(left: Types.TVoid, right: Types.TSchema): StructuralResult { + if (TypeGuard.TUnion(right)) { + const result = right.anyOf.some((right: Types.TSchema) => TypeGuard.TAny(right) || TypeGuard.TUnknown(right)) + return result ? StructuralResult.True : StructuralResult.False + } else if (TypeGuard.TAny(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnknown(right)) { + return StructuralResult.True + } else if (TypeGuard.TVoid(right)) { + return StructuralResult.True + } else { + return StructuralResult.False + } + } + + let recursionDepth = 0 + function Visit(left: Left, right: Types.TSchema): StructuralResult { + recursionDepth += 1 + if (recursionDepth >= 1000) return StructuralResult.True + if (left.$id !== undefined) referenceMap.set(left.$id!, left) + if (right.$id !== undefined) referenceMap.set(right.$id!, right) + const resolvedRight = right[Types.Kind] === 'Self' ? referenceMap.get(right.$ref)! : right + if (TypeGuard.TAny(left)) { + return Any(left, resolvedRight) + } else if (TypeGuard.TArray(left)) { + return Array(left, resolvedRight) + } else if (TypeGuard.TBoolean(left)) { + return Boolean(left, resolvedRight) + } else if (TypeGuard.TConstructor(left)) { + return Constructor(left, resolvedRight) + } else if (TypeGuard.TFunction(left)) { + return Function(left, resolvedRight) + } else if (TypeGuard.TInteger(left)) { + return Integer(left, resolvedRight) + } else if (TypeGuard.TLiteral(left)) { + return Literal(left, resolvedRight) + } else if (TypeGuard.TNull(left)) { + return Null(left, resolvedRight) + } else if (TypeGuard.TNumber(left)) { + return Number(left, resolvedRight) + } else if (TypeGuard.TObject(left)) { + return Object(left, resolvedRight) + } else if (TypeGuard.TPromise(left)) { + return Promise(left, resolvedRight) + } else if (TypeGuard.TRecord(left)) { + return Record(left, resolvedRight) + } else if (TypeGuard.TRef(left)) { + return Ref(left, resolvedRight) + } else if (TypeGuard.TSelf(left)) { + return Self(left, resolvedRight) + } else if (TypeGuard.TString(left)) { + return String(left, resolvedRight) + } else if (TypeGuard.TTuple(left)) { + return Tuple(left, resolvedRight) + } else if (TypeGuard.TUndefined(left)) { + return Undefined(left, resolvedRight) + } else if (TypeGuard.TUint8Array(left)) { + return Uint8Array(left, resolvedRight) + } else if (TypeGuard.TUnion(left)) { + return Union(left, resolvedRight) + } else if (TypeGuard.TUnknown(left)) { + return Unknown(left, resolvedRight) + } else if (TypeGuard.TVoid(left)) { + return Void(left, resolvedRight) + } else { + throw Error(`Structural: Unknown left operand '${left[Types.Kind]}'`) + } + } + + /** Structurally tests if the left schema extends the right. */ + export function Check(left: Types.TSchema, right: Types.TSchema): StructuralResult { + referenceMap.clear() + recursionDepth = 0 + return Visit(left, right) + } +} diff --git a/src/errors/errors.ts b/src/errors/errors.ts new file mode 100644 index 000000000..e4dea5907 --- /dev/null +++ b/src/errors/errors.ts @@ -0,0 +1,411 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/errors + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { Format } from '../format/index' + +// ------------------------------------------------------------------- +// ValueErrorType +// ------------------------------------------------------------------- + +export enum ValueErrorType { + Array, + ArrayMinItems, + ArrayMaxItems, + ArrayUniqueItems, + Boolean, + Function, + Integer, + IntegerMultipleOf, + IntegerExclusiveMinimum, + IntegerExclusiveMaximum, + IntegerMinimum, + IntegerMaximum, + Literal, + Never, + Null, + Number, + NumberMultipleOf, + NumberExclusiveMinimum, + NumberExclusiveMaximum, + NumberMinumum, + NumberMaximum, + Object, + ObjectMinProperties, + ObjectMaxProperties, + ObjectAdditionalProperties, + Promise, + RecordKeyNumeric, + RecordKeyString, + String, + StringMinLength, + StringMaxLength, + StringPattern, + StringFormatUnknown, + StringFormat, + TupleZeroLength, + TupleLength, + Undefined, + Union, + Uint8Array, + Uint8ArrayMinByteLength, + Uint8ArrayMaxByteLength, + Void, +} + +// ------------------------------------------------------------------- +// ValueError +// ------------------------------------------------------------------- + +export interface ValueError { + type: ValueErrorType + schema: Types.TSchema + path: string + value: unknown + message: string +} + +// ------------------------------------------------------------------- +// ValueErrors +// ------------------------------------------------------------------- + +export class ValueErrorsUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueErrors: Unknown type') + } +} + +export namespace ValueErrors { + function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} + + function* Array(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!globalThis.Array.isArray(value)) { + return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } + } + if (schema.minItems !== undefined && !(value.length >= schema.minItems)) { + yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } + } + if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) { + yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } + } + if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { + yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } + } + for (let i = 0; i < value.length; i++) { + yield* Visit(schema.items, references, `${path}/${i}`, value[i]) + } + } + + function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'boolean')) { + return yield { type: ValueErrorType.Boolean, schema, path, value, message: `Expected boolean` } + } + } + + function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { + yield* Visit(schema.returns, references, path, value.prototype) + } + + function* Function(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'function')) { + return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } + } + } + + function* Integer(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'number')) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } + } + if (!globalThis.Number.isInteger(value)) { + yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } + } + if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } + } + if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } + } + if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } + } + if (schema.minimum && !(value >= schema.minimum)) { + yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } + } + if (schema.maximum && !(value <= schema.maximum)) { + yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } + } + } + + function* Literal(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === schema.const)) { + const error = typeof schema.const === 'string' ? `'${schema.const}'` : schema.const + return yield { type: ValueErrorType.Literal, schema, path, value, message: `Expected ${error}` } + } + } + + function* Never(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { + yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } + } + + function* Null(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === null)) { + return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } + } + } + + function* Number(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'number')) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } + } + if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } + } + if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } + } + if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } + } + if (schema.minimum && !(value >= schema.minimum)) { + yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } + } + if (schema.maximum && !(value <= schema.maximum)) { + yield { type: ValueErrorType.NumberMinumum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } + } + } + + function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } + if (schema.minProperties !== undefined && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + } + if (schema.maxProperties !== undefined && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } + } + const propertyKeys = globalThis.Object.keys(schema.properties) + if (schema.additionalProperties === false) { + for (const propKey of globalThis.Object.keys(value)) { + if (!propertyKeys.includes(propKey)) { + yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${propKey}`, value: value[propKey], message: 'Unexpected property' } + } + } + } + for (const propertyKey of propertyKeys) { + const propertySchema = schema.properties[propertyKey] + if (schema.required && schema.required.includes(propertyKey)) { + yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) + } else { + if (value[propertyKey] !== undefined) { + yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) + } + } + } + } + + function* Promise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'object' && typeof value.then === 'function')) { + yield { type: ValueErrorType.Promise, schema, path, value, message: `Expected Promise` } + } + } + + function* Record(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } + const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const regex = new RegExp(keyPattern) + if (!globalThis.Object.keys(value).every((key) => regex.test(key))) { + const numeric = keyPattern === '^(0|[1-9][0-9]*)$' + const type = numeric ? ValueErrorType.RecordKeyNumeric : ValueErrorType.RecordKeyString + const message = numeric ? 'Expected all object property keys to be numeric' : 'Expected all object property keys to be strings' + return yield { type, schema, path, value, message } + } + for (const [propKey, propValue] of globalThis.Object.entries(value)) { + yield* Visit(valueSchema, references, `${path}/${propKey}`, propValue) + } + } + + function* Ref(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueErrors.Ref: Cannot find schema with $id '${schema.$ref}'.`) + yield* Visit(reference, references, path, value) + } + + function* Self(schema: Types.TSelf, references: Types.TSchema[], path: string, value: any): IterableIterator { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueErrors.Self: Cannot find schema with $id '${schema.$ref}'.`) + yield* Visit(reference, references, path, value) + } + + function* String(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'string')) { + return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } + } + if (schema.minLength !== undefined && !(value.length >= schema.minLength)) { + yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } + } + if (schema.maxLength !== undefined && !(value.length <= schema.maxLength)) { + yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } + } + if (schema.pattern !== undefined) { + const regex = new RegExp(schema.pattern) + if (!regex.test(value)) { + yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } + } + } + if (schema.format !== undefined) { + if (!Format.Has(schema.format)) { + yield { type: ValueErrorType.StringFormatUnknown, schema, path, value, message: `Unknown string format '${schema.format}'` } + } else { + const format = Format.Get(schema.format)! + if (!format(value)) { + yield { type: ValueErrorType.StringFormat, schema, path, value, message: `Expected string to match format '${schema.format}'` } + } + } + } + } + + function* Tuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!globalThis.Array.isArray(value)) { + return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } + } + if (schema.items === undefined && !(value.length === 0)) { + return yield { type: ValueErrorType.TupleZeroLength, schema, path, value, message: 'Expected tuple to have 0 elements' } + } + if (!(value.length === schema.maxItems)) { + yield { type: ValueErrorType.TupleLength, schema, path, value, message: `Expected tuple to have ${schema.maxItems} elements` } + } + if (!schema.items) { + return + } + for (let i = 0; i < schema.items.length; i++) { + yield* Visit(schema.items[i], references, `${path}/${i}`, value[i]) + } + } + + function* Undefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === undefined)) { + yield { type: ValueErrorType.Undefined, schema, path, value, message: `Expected undefined` } + } + } + + function* Union(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { + const errors: ValueError[] = [] + for (const inner of schema.anyOf) { + const variantErrors = [...Visit(inner, references, path, value)] + if (variantErrors.length === 0) return + errors.push(...variantErrors) + } + for (const error of errors) { + yield error + } + if (errors.length > 0) { + yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } + } + } + + function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value instanceof globalThis.Uint8Array)) { + return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } + } + + if (schema.maxByteLength && !(value.length <= schema.maxByteLength)) { + yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } + } + if (schema.minByteLength && !(value.length >= schema.minByteLength)) { + yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } + } + } + + function* Unknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} + + function* Void(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === null)) { + return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected null` } + } + } + + function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { + const anyReferences = schema.$id === undefined ? references : [schema, ...references] + const anySchema = schema as any + switch (anySchema[Types.Kind]) { + case 'Any': + return yield* Any(anySchema, anyReferences, path, value) + case 'Array': + return yield* Array(anySchema, anyReferences, path, value) + case 'Boolean': + return yield* Boolean(anySchema, anyReferences, path, value) + case 'Constructor': + return yield* Constructor(anySchema, anyReferences, path, value) + case 'Function': + return yield* Function(anySchema, anyReferences, path, value) + case 'Integer': + return yield* Integer(anySchema, anyReferences, path, value) + case 'Literal': + return yield* Literal(anySchema, anyReferences, path, value) + case 'Never': + return yield* Never(anySchema, anyReferences, path, value) + case 'Null': + return yield* Null(anySchema, anyReferences, path, value) + case 'Number': + return yield* Number(anySchema, anyReferences, path, value) + case 'Object': + return yield* Object(anySchema, anyReferences, path, value) + case 'Promise': + return yield* Promise(anySchema, anyReferences, path, value) + case 'Record': + return yield* Record(anySchema, anyReferences, path, value) + case 'Ref': + return yield* Ref(anySchema, anyReferences, path, value) + case 'Self': + return yield* Self(anySchema, anyReferences, path, value) + case 'String': + return yield* String(anySchema, anyReferences, path, value) + case 'Tuple': + return yield* Tuple(anySchema, anyReferences, path, value) + case 'Undefined': + return yield* Undefined(anySchema, anyReferences, path, value) + case 'Union': + return yield* Union(anySchema, anyReferences, path, value) + case 'Uint8Array': + return yield* Uint8Array(anySchema, anyReferences, path, value) + case 'Unknown': + return yield* Unknown(anySchema, anyReferences, path, value) + case 'Void': + return yield* Void(anySchema, anyReferences, path, value) + default: + throw new ValueErrorsUnknownTypeError(schema) + } + } + + export function* Errors(schema: T, references: Types.TSchema[], value: any): IterableIterator { + yield* Visit(schema, references, '', value) + } +} diff --git a/src/errors/index.ts b/src/errors/index.ts new file mode 100644 index 000000000..eae491eeb --- /dev/null +++ b/src/errors/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/errors + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './errors' diff --git a/src/format/format.ts b/src/format/format.ts new file mode 100644 index 000000000..3dc6de9f4 --- /dev/null +++ b/src/format/format.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type FormatValidationFunction = (value: string) => boolean + +/** Shared string formats used by the TypeCompiler and Value modules */ +export namespace Format { + const formats = new Map() + + /** Clears all formats */ + export function Clear() { + return formats.clear() + } + + /** Returns true if the string format exists */ + export function Has(format: string) { + return formats.has(format) + } + + /** Sets a string format validation function */ + export function Set(format: string, func: FormatValidationFunction) { + formats.set(format, func) + } + + /** Gets a string format validation function */ + export function Get(format: string) { + return formats.get(format) + } +} diff --git a/src/format/index.ts b/src/format/index.ts new file mode 100644 index 000000000..f3153aa36 --- /dev/null +++ b/src/format/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './format' diff --git a/src/guard/guard.ts b/src/guard/guard.ts new file mode 100644 index 000000000..01b653566 --- /dev/null +++ b/src/guard/guard.ts @@ -0,0 +1,362 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/guard + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, dTribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' + +export class TypeGuardInvalidTypeError extends Error { + constructor(public readonly schema: unknown) { + super('TypeGuard: Invalid type') + } +} + +/** TypeGuard tests that values conform to a known TypeBox type specification */ +export namespace TypeGuard { + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value) + } + + function IsArray(value: unknown): value is any[] { + return typeof value === 'object' && value !== null && Array.isArray(value) + } + + function IsPattern(value: unknown): value is string { + try { + new RegExp(value as string) + return true + } catch { + return false + } + } + + function IsControlCharacterFree(value: unknown): value is string { + if (typeof value !== 'string') return false + for (let i = 0; i < value.length; i++) { + const code = value.charCodeAt(i) + if ((code >= 7 && code <= 13) || code === 27 || code === 127) { + return false + } + } + return true + } + + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + + function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } + + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + + function IsOptionalNumber(value: unknown): value is number | undefined { + return value === undefined || (value !== undefined && IsNumber(value)) + } + + function IsOptionalBoolean(value: unknown): value is boolean | undefined { + return value === undefined || (value !== undefined && IsBoolean(value)) + } + + function IsOptionalString(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value)) + } + + function IsOptionalPattern(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) + } + function IsOptionalFormat(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) + } + /** Returns true if the given schema is TAny */ + export function TAny(schema: unknown): schema is Types.TAny { + return IsObject(schema) && schema[Types.Kind] === 'Any' && IsOptionalString(schema.$id) + } + + /** Returns true if the given schema is TArray */ + export function TArray(schema: unknown): schema is Types.TArray { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Array' && + schema.type === 'array' && + IsOptionalString(schema.$id) && + TSchema(schema.items) && + IsOptionalNumber(schema.minItems) && + IsOptionalNumber(schema.maxItems) && + IsOptionalBoolean(schema.uniqueItems) + ) + } + + /** Returns true if the given schema is TBoolean */ + export function TBoolean(schema: unknown): schema is Types.TBoolean { + return IsObject(schema) && schema[Types.Kind] === 'Boolean' && schema.type === 'boolean' && IsOptionalString(schema.$id) + } + + /** Returns true if the given schema is TConstructor */ + export function TConstructor(schema: unknown): schema is Types.TConstructor { + if (!(IsObject(schema) && schema[Types.Kind] === 'Constructor' && schema.type === 'constructor' && IsOptionalString(schema.$id) && IsArray(schema.parameters) && TSchema(schema.returns))) { + return false + } + for (const parameter of schema.parameters) { + if (!TSchema(parameter)) return false + } + return true + } + + /** Returns true if the given schema is TFunction */ + export function TFunction(schema: unknown): schema is Types.TFunction { + if (!(IsObject(schema) && schema[Types.Kind] === 'Function' && schema.type === 'function' && IsOptionalString(schema.$id) && IsArray(schema.parameters) && TSchema(schema.returns))) { + return false + } + for (const parameter of schema.parameters) { + if (!TSchema(parameter)) return false + } + return true + } + + /** Returns true if the given schema is TInteger */ + export function TInteger(schema: unknown): schema is Types.TInteger { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Integer' && + schema.type === 'integer' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.multipleOf) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMinimum) && + IsOptionalNumber(schema.exclusiveMaximum) + ) + } + + /** Returns true if the given schema is TLiteral */ + export function TLiteral(schema: unknown): schema is Types.TLiteral { + return IsObject(schema) && schema[Types.Kind] === 'Literal' && IsOptionalString(schema.$id) && (IsString(schema.const) || IsNumber(schema.const) || IsBoolean(schema.const)) + } + + /** Returns true if the given schema is TNever */ + export function TNever(schema: unknown): schema is Types.TNever { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Never' && + IsArray(schema.allOf) && + schema.allOf.length === 2 && + IsObject(schema.allOf[0]) && + IsString(schema.allOf[0].type) && + schema.allOf[0].type === 'boolean' && + schema.allOf[0].const === false && + IsObject(schema.allOf[1]) && + IsString(schema.allOf[1].type) && + schema.allOf[1].type === 'boolean' && + schema.allOf[1].const === true + ) + } + + /** Returns true if the given schema is TNull */ + export function TNull(schema: unknown): schema is Types.TNull { + return IsObject(schema) && schema[Types.Kind] === 'Null' && schema.type === 'null' && IsOptionalString(schema.$id) + } + + /** Returns true if the given schema is TNumber */ + export function TNumber(schema: unknown): schema is Types.TNumber { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Number' && + schema.type === 'number' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.multipleOf) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMinimum) && + IsOptionalNumber(schema.exclusiveMaximum) + ) + } + + /** Returns true if the given schema is TObject */ + export function TObject(schema: unknown): schema is Types.TObject { + if ( + !( + IsObject(schema) && + schema[Types.Kind] === 'Object' && + schema.type === 'object' && + IsOptionalString(schema.$id) && + IsObject(schema.properties) && + IsOptionalBoolean(schema.additionalProperties) && + IsOptionalNumber(schema.minProperties) && + IsOptionalNumber(schema.maxProperties) + ) + ) { + return false + } + for (const [key, value] of Object.entries(schema.properties)) { + if (!IsControlCharacterFree(key)) return false + if (!TSchema(value)) return false + } + return true + } + + /** Returns true if the given schema is TPromise */ + export function TPromise(schema: unknown): schema is Types.TPromise { + return IsObject(schema) && schema[Types.Kind] === 'Promise' && schema.type === 'promise' && IsOptionalString(schema.$id) && TSchema(schema.item) + } + + /** Returns true if the given schema is TRecord */ + export function TRecord(schema: unknown): schema is Types.TRecord { + if (!(IsObject(schema) && schema[Types.Kind] === 'Record' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.additionalProperties === false && IsObject(schema.patternProperties))) { + return false + } + const keys = Object.keys(schema.patternProperties) + if (keys.length !== 1) { + return false + } + if (!IsPattern(keys[0])) { + return false + } + if (!TSchema(schema.patternProperties[keys[0]])) { + return false + } + return true + } + + /** Returns true if the given schema is TSelf */ + export function TSelf(schema: unknown): schema is Types.TSelf { + return IsObject(schema) && schema[Types.Kind] === 'Self' && IsOptionalString(schema.$id) && IsString(schema.$ref) + } + + /** Returns true if the given schema is TRef */ + export function TRef(schema: unknown): schema is Types.TRef { + return IsObject(schema) && schema[Types.Kind] === 'Ref' && IsOptionalString(schema.$id) && IsString(schema.$ref) + } + + /** Returns true if the given schema is TString */ + export function TString(schema: unknown): schema is Types.TString { + return ( + IsObject(schema) && + schema[Types.Kind] === 'String' && + schema.type === 'string' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minLength) && + IsOptionalNumber(schema.maxLength) && + IsOptionalPattern(schema.pattern) && + IsOptionalFormat(schema.format) + ) + } + + /** Returns true if the given schema is TTuple */ + export function TTuple(schema: unknown): schema is Types.TTuple { + if (!(IsObject(schema) && schema[Types.Kind] === 'Tuple' && schema.type === 'array' && IsOptionalString(schema.$id) && IsNumber(schema.minItems) && IsNumber(schema.maxItems) && schema.minItems === schema.maxItems)) { + return false + } + if (schema.items === undefined && schema.additionalItems === undefined && schema.minItems === 0) { + return true + } + if (!IsArray(schema.items)) { + return false + } + for (const inner of schema.items) { + if (!TSchema(inner)) return false + } + return true + } + + /** Returns true if the given schema is TUndefined */ + export function TUndefined(schema: unknown): schema is Types.TUndefined { + return IsObject(schema) && schema[Types.Kind] === 'Undefined' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.specialized === 'Undefined' + } + + /** Returns true if the given schema is TUnion */ + export function TUnion(schema: unknown): schema is Types.TUnion { + if (!(IsObject(schema) && schema[Types.Kind] === 'Union' && IsArray(schema.anyOf) && IsOptionalString(schema.$id))) { + return false + } + for (const inner of schema.anyOf) { + if (!TSchema(inner)) return false + } + return true + } + + /** Returns true if the given schema is TUint8Array */ + export function TUint8Array(schema: unknown): schema is Types.TUint8Array { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Uint8Array' && + schema.type === 'object' && + IsOptionalString(schema.$id) && + schema.specialized === 'Uint8Array' && + IsOptionalNumber(schema.minByteLength) && + IsOptionalNumber(schema.maxByteLength) + ) + } + + /** Returns true if the given schema is TUnknown */ + export function TUnknown(schema: unknown): schema is Types.TUnknown { + return IsObject(schema) && schema[Types.Kind] === 'Unknown' && IsOptionalString(schema.$id) + } + + /** Returns true if the given schema is TVoid */ + export function TVoid(schema: unknown): schema is Types.TVoid { + return IsObject(schema) && schema[Types.Kind] === 'Void' && schema.type === 'null' && IsOptionalString(schema.$id) + } + + /** Returns true if the given schema is TSchema */ + export function TSchema(schema: unknown): schema is Types.TSchema { + return ( + TAny(schema) || + TArray(schema) || + TBoolean(schema) || + TConstructor(schema) || + TFunction(schema) || + TInteger(schema) || + TLiteral(schema) || + TNever(schema) || + TNull(schema) || + TNumber(schema) || + TObject(schema) || + TPromise(schema) || + TRecord(schema) || + TSelf(schema) || + TRef(schema) || + TString(schema) || + TTuple(schema) || + TUndefined(schema) || + TUnion(schema) || + TUint8Array(schema) || + TUnknown(schema) || + TVoid(schema) + ) + } + + /** Asserts if this schema and associated references are valid. */ + export function Assert(schema: T, references: Types.TSchema[] = []) { + if (!TSchema(schema)) throw new TypeGuardInvalidTypeError(schema) + for (const schema of references) { + if (!TSchema(schema)) throw new TypeGuardInvalidTypeError(schema) + } + } +} diff --git a/src/guard/index.ts b/src/guard/index.ts new file mode 100644 index 000000000..d39efe4ec --- /dev/null +++ b/src/guard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/guards + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './guard' diff --git a/src/tsconfig.json b/src/tsconfig.json index 8440ad2e5..3bca8a311 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,14 +1,4 @@ { - "compilerOptions": { - "strict": true, - "module": "CommonJS", - "target": "ESNext", - "moduleResolution": "node", - "lib": ["ESNext"], - "declaration": true, - "outDir": "../dist/", - }, - "files": [ - "typebox.ts" - ] -} \ No newline at end of file + "extends": "../tsconfig.json", + "files": ["compiler/index.ts", "conditional/index.ts", "format/index.ts", "guard/index.ts", "value/index.ts", "typebox.ts"] +} diff --git a/src/typebox.ts b/src/typebox.ts index 4dd4864a1..0f6211749 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1,10 +1,10 @@ /*-------------------------------------------------------------------------- -TypeBox: JSON Schema Type Builder with Static Type Resolution for TypeScript +@sinclair/typebox The MIT License (MIT) -Copyright (c) 2021 Haydn Paterson (sinclair) +Copyright (c) 2022 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -26,501 +26,947 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +// -------------------------------------------------------------------------- +// Symbols +// -------------------------------------------------------------------------- + +export const Kind = Symbol.for('TypeBox.Kind') +export const Hint = Symbol.for('TypeBox.Hint') +export const Modifier = Symbol.for('TypeBox.Modifier') + // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- -export const ReadonlyOptionalModifier = Symbol('ReadonlyOptionalModifier') -export const OptionalModifier = Symbol('OptionalModifier') -export const ReadonlyModifier = Symbol('ReadonlyModifier') +export type TModifier = TReadonlyOptional | TOptional | TReadonly +export type TReadonly = T & { [Modifier]: 'Readonly' } +export type TOptional = T & { [Modifier]: 'Optional' } +export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOptional' } -export type TModifier = TReadonlyOptional | TOptional | TReadonly -export type TReadonlyOptional = T & { modifier: typeof ReadonlyOptionalModifier } -export type TOptional = T & { modifier: typeof OptionalModifier } -export type TReadonly = T & { modifier: typeof ReadonlyModifier } +// -------------------------------------------------------------------------- +// Schema +// -------------------------------------------------------------------------- + +export interface SchemaOptions { + $schema?: string + /** Id for this schema */ + $id?: string + /** Title of this schema */ + title?: string + /** Description of this schema */ + description?: string + /** Default value for this schema */ + default?: any + /** Example values matching this schema. */ + examples?: any + [prop: string]: any +} + +export interface TSchema extends SchemaOptions { + [Kind]: string + [Hint]?: string + [Modifier]?: string + params: unknown[] + static: unknown +} // -------------------------------------------------------------------------- -// Schema Standard +// TAnySchema // -------------------------------------------------------------------------- -export const BoxKind = Symbol('BoxKind') -export const KeyOfKind = Symbol('KeyOfKind') -export const IntersectKind = Symbol('IntersectKind') -export const UnionKind = Symbol('UnionKind') -export const TupleKind = Symbol('TupleKind') -export const ObjectKind = Symbol('ObjectKind') -export const RecordKind = Symbol('RecordKind') -export const ArrayKind = Symbol('ArrayKind') -export const EnumKind = Symbol('EnumKind') -export const LiteralKind = Symbol('LiteralKind') -export const StringKind = Symbol('StringKind') -export const NumberKind = Symbol('NumberKind') -export const IntegerKind = Symbol('IntegerKind') -export const BooleanKind = Symbol('BooleanKind') -export const NullKind = Symbol('NullKind') -export const UnknownKind = Symbol('UnknownKind') -export const AnyKind = Symbol('AnyKind') -export const RefKind = Symbol('RefKind') +export type TAnySchema = + | TSchema + | TAny + | TArray + | TBoolean + | TConstructor + | TEnum + | TFunction + | TInteger + | TLiteral + | TNull + | TNumber + | TObject + | TPromise + | TRecord + | TSelf + | TRef + | TString + | TTuple + | TUndefined + | TUnion + | TUint8Array + | TUnknown + | TVoid // -------------------------------------------------------------------------- -// Options +// TNumeric // -------------------------------------------------------------------------- -export interface CustomOptions { - $id?: string - title?: string - description?: string - default?: any - examples?: any - [prop: string]: any +export interface NumericOptions extends SchemaOptions { + exclusiveMaximum?: number + exclusiveMinimum?: number + maximum?: number + minimum?: number + multipleOf?: number } -export type StringFormatOption = - | 'date-time' | 'time' | 'date' | 'email' | 'idn-email' | 'hostname' - | 'idn-hostname' | 'ipv4' | 'ipv6' | 'uri' | 'uri-reference' | 'iri' - | 'uuid' | 'iri-reference' | 'uri-template' | 'json-pointer' | 'relative-json-pointer' - | 'regex' +export type TNumeric = TInteger | TNumber -export declare type StringOptions = { - minLength?: number - maxLength?: number - pattern?: string - format?: TFormat - contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64' - contentMediaType?: string -} & CustomOptions - -export type ArrayOptions = { - uniqueItems?: boolean - minItems?: number - maxItems?: number -} & CustomOptions - -export type NumberOptions = { - exclusiveMaximum?: number - exclusiveMinimum?: number - maximum?: number - minimum?: number - multipleOf?: number -} & CustomOptions - -export type IntersectOptions = { - unevaluatedProperties?: boolean -} & CustomOptions - -export type ObjectOptions = { - additionalProperties?: boolean -} & CustomOptions - -// -------------------------------------------------------------------------- -// Namespace -// -------------------------------------------------------------------------- - -export type TDefinitions = { [key: string]: TSchema } -export type TNamespace = { kind: typeof BoxKind, $defs: T } & CustomOptions - -// -------------------------------------------------------------------------- -// TSchema -// -------------------------------------------------------------------------- - -export interface TSchema { $static: unknown } - -// -------------------------------------------------------------------------- -// Standard Schema Types -// -------------------------------------------------------------------------- - -export type TEnumType = Record -export type TKey = string | number | symbol -export type TValue = string | number | boolean -export type TRecordKey = TString | TNumber | TKeyOf | TUnion -export type TEnumKey = { type: 'number' | 'string', const: T } - -export interface TProperties { [key: string]: TSchema } -export interface TRecord extends TSchema, ObjectOptions { $static: StaticRecord, kind: typeof RecordKind, type: 'object', patternProperties: { [pattern: string]: T } } -export interface TTuple extends TSchema, CustomOptions { $static: StaticTuple, kind: typeof TupleKind, type: 'array', items?: T, additionalItems?: false, minItems: number, maxItems: number } -export interface TObject extends TSchema, ObjectOptions { $static: StaticObject, kind: typeof ObjectKind, type: 'object', properties: T, required?: string[] } -export interface TUnion extends TSchema, CustomOptions { $static: StaticUnion, kind: typeof UnionKind, anyOf: T } -export interface TIntersect extends TSchema, IntersectOptions { $static: StaticIntersect, kind: typeof IntersectKind, type: 'object', allOf: T } -export interface TKeyOf extends TSchema, CustomOptions { $static: StaticKeyOf, kind: typeof KeyOfKind, type: 'string', enum: T } -export interface TArray extends TSchema, ArrayOptions { $static: StaticArray, kind: typeof ArrayKind, type: 'array', items: T } -export interface TLiteral extends TSchema, CustomOptions { $static: StaticLiteral, kind: typeof LiteralKind, const: T } -export interface TEnum extends TSchema, CustomOptions { $static: StaticEnum, kind: typeof EnumKind, anyOf: T } -export interface TRef extends TSchema, CustomOptions { $static: Static, kind: typeof RefKind, $ref: string } -export interface TString extends TSchema, StringOptions { $static: string, kind: typeof StringKind, type: 'string' } -export interface TNumber extends TSchema, NumberOptions { $static: number, kind: typeof NumberKind, type: 'number' } -export interface TInteger extends TSchema, NumberOptions { $static: number, kind: typeof IntegerKind, type: 'integer' } -export interface TBoolean extends TSchema, CustomOptions { $static: boolean, kind: typeof BooleanKind, type: 'boolean' } -export interface TNull extends TSchema, CustomOptions { $static: null, kind: typeof NullKind, type: 'null' } -export interface TUnknown extends TSchema, CustomOptions { $static: unknown, kind: typeof UnknownKind } -export interface TAny extends TSchema, CustomOptions { $static: any, kind: typeof AnyKind } +// -------------------------------------------------------------------------- +// Any +// -------------------------------------------------------------------------- +export interface TAny extends TSchema { + [Kind]: 'Any' + static: any +} // -------------------------------------------------------------------------- -// Extended Schema Types +// Array // -------------------------------------------------------------------------- -export const ConstructorKind = Symbol('ConstructorKind') -export const FunctionKind = Symbol('FunctionKind') -export const PromiseKind = Symbol('PromiseKind') -export const UndefinedKind = Symbol('UndefinedKind') -export const VoidKind = Symbol('VoidKind') -export interface TConstructor extends TSchema, CustomOptions { $static: StaticConstructor, kind: typeof ConstructorKind, type: 'constructor', arguments: TSchema[], returns: TSchema } -export interface TFunction extends TSchema, CustomOptions { $static: StaticFunction, kind: typeof FunctionKind, type: 'function', arguments: TSchema[], returns: TSchema } -export interface TPromise extends TSchema, CustomOptions { $static: StaticPromise, kind: typeof PromiseKind, type: 'promise', item: TSchema } -export interface TUndefined extends TSchema, CustomOptions { $static: undefined, kind: typeof UndefinedKind, type: 'undefined' } -export interface TVoid extends TSchema, CustomOptions { $static: void, kind: typeof VoidKind, type: 'void' } +export interface ArrayOptions extends SchemaOptions { + uniqueItems?: boolean + minItems?: number + maxItems?: number +} + +export interface TArray extends TSchema, ArrayOptions { + [Kind]: 'Array' + static: Array> + type: 'array' + items: T +} // -------------------------------------------------------------------------- -// Utility Types +// Boolean // -------------------------------------------------------------------------- -export type Selectable = TObject | TRef> -export type SelectablePropertyKeys = T extends TObject ? keyof U : T extends TRef> ? keyof U : never -export type SelectableProperties = T extends TObject ? U : T extends TRef> ? U : never -export type UnionToIntersect = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never -export type StaticReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] -export type StaticReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] -export type StaticOptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? K : never }[keyof T] -export type StaticRequiredPropertyKeys = keyof Omit | StaticReadonlyPropertyKeys | StaticOptionalPropertyKeys> -export type StaticIntersectEvaluate = {[K in keyof T]: T[K] extends TSchema ? Static : never } -export type StaticIntersectReduce = T extends [infer A, ...infer B] ? StaticIntersectReduce : I -export type StaticRequired = { - [K in keyof T]: - T[K] extends TReadonlyOptional ? TReadonly : - T[K] extends TReadonly ? TReadonly : - T[K] extends TOptional ? U : - T[K] +export interface TBoolean extends TSchema { + [Kind]: 'Boolean' + static: boolean + type: 'boolean' } -export type StaticPartial = { - [K in keyof T]: - T[K] extends TReadonlyOptional ? TReadonlyOptional : - T[K] extends TReadonly ? TReadonlyOptional : - T[K] extends TOptional ? TOptional : - TOptional + +// -------------------------------------------------------------------------- +// Constructor +// -------------------------------------------------------------------------- + +export type TConstructorParameters> = TTuple + +export type TInstanceType> = T['returns'] + +export type StaticContructorParameters = [...{ [K in keyof T]: T[K] extends TSchema ? Static : never }] + +export interface TConstructor extends TSchema { + [Kind]: 'Constructor' + static: new (...param: StaticContructorParameters) => Static + type: 'constructor' + parameters: T + returns: U } -// ------------------------------------------------------------------------ -// Static Schema -// ------------------------------------------------------------------------ +// -------------------------------------------------------------------------- +// Enum +// -------------------------------------------------------------------------- -export type StaticProperties = - { readonly [K in StaticReadonlyOptionalPropertyKeys]?: Static } & - { readonly [K in StaticReadonlyPropertyKeys]: Static } & - { [K in StaticOptionalPropertyKeys]?: Static } & - { [K in StaticRequiredPropertyKeys]: Static } -export type StaticRecord = - K extends TString ? Record> : - K extends TNumber ? Record> : - K extends TKeyOf ? Record> : - K extends TUnion ? Record> : - never -export type StaticEnum = T extends TEnumKey[] ? U : never -export type StaticKeyOf = T extends Array ? K : never -export type StaticIntersect = StaticIntersectReduce> -export type StaticUnion = { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] -export type StaticTuple = { [K in keyof T]: T[K] extends TSchema ? Static : never } -export type StaticObject = StaticProperties extends infer I ? {[K in keyof I]: I[K]}: never -export type StaticArray = Array> -export type StaticLiteral = T -export type StaticParameters = {[K in keyof T]: T[K] extends TSchema ? Static: never } -export type StaticConstructor = new (...args: [...StaticParameters]) => Static -export type StaticFunction = (...args: [...StaticParameters]) => Static -export type StaticPromise = Promise> -export type Static = T['$static'] - -// -------------------------------------------------------------------------- -// Utility -// -------------------------------------------------------------------------- - -function isObject(object: any) { - return typeof object === 'object' && object !== null && !Array.isArray(object) -} - -function isArray(object: any) { - return typeof object === 'object' && object !== null && Array.isArray(object) -} - -function clone(object: any): any { - if(isObject(object)) return Object.keys(object).reduce((acc, key) => ({...acc, [key]: clone(object[key]) }), {}) - if(isArray(object)) return object.map((item: any) => clone(item)) - return object +export interface TEnumOption { + type: 'number' | 'string' + const: T +} + +export interface TEnum = Record> extends TSchema { + [Kind]: 'Union' + static: T[keyof T] + anyOf: TLiteral[] } // -------------------------------------------------------------------------- -// TypeBuilder +// Function // -------------------------------------------------------------------------- -export class TypeBuilder { +export type TParameters = TTuple - protected readonly schemas = new Map() +export type TReturnType = T['returns'] - /** `Standard` Modifies an object property to be both readonly and optional */ - public ReadonlyOptional(item: T): TReadonlyOptional { - return { ...item, modifier: ReadonlyOptionalModifier } - } +export type StaticFunctionParameters = [...{ [K in keyof T]: T[K] extends TSchema ? Static : never }] - /** `Standard` Modifies an object property to be readonly */ - public Readonly(item: T): TReadonly { - return { ...item, modifier: ReadonlyModifier } - } +export interface TFunction extends TSchema { + [Kind]: 'Function' + static: (...param: StaticFunctionParameters) => Static + type: 'function' + parameters: T + returns: U +} - /** `Standard` Modifies an object property to be optional */ - public Optional(item: T): TOptional { - return { ...item, modifier: OptionalModifier } - } +// -------------------------------------------------------------------------- +// Integer +// -------------------------------------------------------------------------- - /** `Standard` Creates a type type */ - public Tuple(items: [...T], options: CustomOptions = {}): TTuple { - const additionalItems = false - const minItems = items.length - const maxItems = items.length - const schema = ((items.length > 0) - ? { ...options, kind: TupleKind, type: 'array', items, additionalItems, minItems, maxItems } - : { ...options, kind: TupleKind, type: 'array', minItems, maxItems }) as any - return this.Store(schema) - } +export interface TInteger extends TSchema, NumericOptions { + [Kind]: 'Integer' + static: number + type: 'integer' +} - /** `Standard` Creates an object type with the given properties */ - public Object(properties: T, options: ObjectOptions = {}): TObject { - const property_names = Object.keys(properties) - const optional = property_names.filter(name => { - const candidate = properties[name] as TModifier - return (candidate.modifier && - (candidate.modifier === OptionalModifier || - candidate.modifier === ReadonlyOptionalModifier)) - }) - const required_names = property_names.filter(name => !optional.includes(name)) - const required = (required_names.length > 0) ? required_names : undefined - return this.Store(((required) - ? { ...options, kind: ObjectKind, type: 'object', properties, required } - : { ...options, kind: ObjectKind, type: 'object', properties })) - } +// -------------------------------------------------------------------------- +// Intersect +// -------------------------------------------------------------------------- - /** `Standard` Creates an intersect type. */ - public Intersect(items: [...T], options: IntersectOptions = {}): TIntersect { - return this.Store({ ...options, kind: IntersectKind, type: 'object', allOf: items }) - } - - /** `Standard` Creates a union type */ - public Union(items: [...T], options: CustomOptions = {}): TUnion { - return this.Store({ ...options, kind: UnionKind, anyOf: items }) - } +export type IntersectReduce = T extends [infer A, ...infer B] ? IntersectReduce : I extends object ? I : {} - /** `Standard` Creates an array type */ - public Array(items: T, options: ArrayOptions = {}): TArray { - return this.Store({ ...options, kind: ArrayKind, type: 'array', items }) - } - - /** `Standard` Creates an enum type from a TypeScript enum */ - public Enum(item: T, options: CustomOptions = {}): TEnum[]> { - const values = Object.keys(item).filter(key => isNaN(key as any)).map(key => item[key]) as T[keyof T][] - const anyOf = values.map(value => typeof value === 'string' ? { type: 'string' as const, const: value } : { type: 'number' as const, const: value }) - return this.Store({ ...options, kind: EnumKind, anyOf }) - } +// note: rename to IntersectStatic in next minor release +export type IntersectEvaluate = { [K in keyof T]: T[K] extends TSchema ? Static : never } - /** `Standard` Creates a literal type. Supports string, number and boolean values only */ - public Literal(value: T, options: CustomOptions = {}): TLiteral { - return this.Store({ ...options, kind: LiteralKind, const: value, type: typeof value as 'string' | 'number' | 'boolean' }) - } +export type IntersectProperties = { + [K in keyof T]: T[K] extends TObject ? P : {} +} - /** `Standard` Creates a string type */ - public String(options: StringOptions = {}): TString { - return this.Store({ ...options, kind: StringKind, type: 'string' }) - } +export interface TIntersect extends TObject { + static: IntersectReduce> + properties: IntersectReduce> +} - /** `Standard` Creates a string type from a regular expression */ - public RegEx(regex: RegExp, options: CustomOptions = {}): TString { - return this.String({ ...options, pattern: regex.source }) - } +// -------------------------------------------------------------------------- +// KeyOf: Implemented by way of Union> +// -------------------------------------------------------------------------- - /** `Standard` Creates a number type */ - public Number(options: NumberOptions = {}): TNumber { - return this.Store({ ...options, kind: NumberKind, type: 'number' }) - } +export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never - /** `Standard` Creates an integer type */ - public Integer(options: NumberOptions = {}): TInteger { - return this.Store({ ...options, kind: IntegerKind, type: 'integer' }) - } +export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never - /** `Standard` Creates a boolean type */ - public Boolean(options: CustomOptions = {}): TBoolean { - return this.Store({ ...options, kind: BooleanKind, type: 'boolean' }) - } +export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] - /** `Standard` Creates a null type */ - public Null(options: CustomOptions = {}): TNull { - return this.Store({ ...options, kind: NullKind, type: 'null' }) - } +export type UnionStringLiteralToTuple = T extends TUnion ? { [I in keyof L]: L[I] extends TLiteral ? C : never } : never - /** `Standard` Creates an unknown type */ - public Unknown(options: CustomOptions = {}): TUnknown { - return this.Store({ ...options, kind: UnknownKind }) - } +export type UnionLiteralsFromObject = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never - /** `Standard` Creates an any type */ - public Any(options: CustomOptions = {}): TAny { - return this.Store({ ...options, kind: AnyKind }) - } - - /** `Standard` Creates a record type */ - public Record(key: K, value: T, options: ObjectOptions = {}): TRecord { - const pattern = (() => { - switch(key.kind) { - case UnionKind: return `^${key.anyOf.map((literal: any) => literal.const as TValue).join('|')}$` - case KeyOfKind: return `^${key.enum.join('|')}$` - case NumberKind: return '^(0|[1-9][0-9]*)$' - case StringKind: return key.pattern ? key.pattern : '^.*$' - default: throw Error('Invalid Record Key') - } - })() - return this.Store({ ...options, kind: RecordKind, type: 'object', patternProperties: { [pattern]: value } }) - } +// export type TKeyOf = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never - /** `Standard` Creates a keyof type from the given object */ - public KeyOf | TRef>>(object: T, options: CustomOptions = {}): TKeyOf[]> { - const source = this.Deref(object) - const keys = Object.keys(source.properties) - return this.Store({...options, kind: KeyOfKind, type: 'string', enum: keys }) - } +export interface TKeyOf extends TUnion> {} - /** `Standard` Makes all properties in the given object type required */ - public Required | TRef>>(object: T, options: ObjectOptions = {}): TObject> { - const source = this.Deref(object) - const schema = { ...clone(source), ...options } - schema.required = Object.keys(schema.properties) - for(const key of Object.keys(schema.properties)) { - const property = schema.properties[key] - switch(property.modifier) { - case ReadonlyOptionalModifier: property.modifier = ReadonlyModifier; break; - case ReadonlyModifier: property.modifier = ReadonlyModifier; break; - case OptionalModifier: delete property.modifier; break; - default: delete property.modifier; break; - } - } - return this.Store(schema) - } - - /** `Standard` Makes all properties in the given object type optional */ - public Partial | TRef>>(object: T, options: ObjectOptions = {}): TObject> { - const source = this.Deref(object) - const schema = { ...clone(source), ...options } - delete schema.required - for(const key of Object.keys(schema.properties)) { - const property = schema.properties[key] - switch(property.modifier) { - case ReadonlyOptionalModifier: property.modifier = ReadonlyOptionalModifier; break; - case ReadonlyModifier: property.modifier = ReadonlyOptionalModifier; break; - case OptionalModifier: property.modifier = OptionalModifier; break; - default: property.modifier = OptionalModifier; break; - } - } - return this.Store(schema) - } - - /** `Standard` Picks property keys from the given object type */ - public Pick | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}): TObject, K[number]>> { - const source = this.Deref(object) - const schema = { ...clone(source), ...options } - schema.required = schema.required ? schema.required.filter((key: K) => keys.includes(key as any)) : undefined - for(const key of Object.keys(schema.properties)) { - if(!keys.includes(key as any)) delete schema.properties[key] - } - return this.Store(schema) - } - - /** `Standard` Omits property keys from the given object type */ - public Omit | TRef>, K extends SelectablePropertyKeys[]>(object: T, keys: [...K], options: ObjectOptions = {}):TObject, K[number]>> { - const source = this.Deref(object) - const schema = { ...clone(source), ...options } - schema.required = schema.required ? schema.required.filter((key: string) => !keys.includes(key as any)) : undefined - for(const key of Object.keys(schema.properties)) { - if(keys.includes(key as any)) delete schema.properties[key] - } - return this.Store(schema) - } +// -------------------------------------------------------------------------- +// Literal +// -------------------------------------------------------------------------- - /** `Standard` Omits the `kind` and `modifier` properties from the underlying schema */ - public Strict(schema: T, options: CustomOptions = {}): T { - return JSON.parse(JSON.stringify({ ...options, ...schema })) as T - } - - /** `Extended` Creates a constructor type */ - public Constructor(args: [...T], returns: U, options: CustomOptions = {}): TConstructor { - return this.Store({ ...options, kind: ConstructorKind, type: 'constructor', arguments: args, returns }) - } +export type TLiteralValue = string | number | boolean - /** `Extended` Creates a function type */ - public Function(args: [...T], returns: U, options: CustomOptions = {}): TFunction { - return this.Store({ ...options, kind: FunctionKind, type: 'function', arguments: args, returns }) - } +export interface TLiteral extends TSchema { + [Kind]: 'Literal' + static: T + const: T +} - /** `Extended` Creates a promise type */ - public Promise(item: T, options: CustomOptions = {}): TPromise { - return this.Store({ ...options, type: 'promise', kind: PromiseKind, item }) - } +// -------------------------------------------------------------------------- +// Never +// -------------------------------------------------------------------------- - /** `Extended` Creates a undefined type */ - public Undefined(options: CustomOptions = {}): TUndefined { - return this.Store({ ...options, type: 'undefined', kind: UndefinedKind }) - } +export interface TNever extends TSchema { + [Kind]: 'Never' + static: never + allOf: [{ type: 'boolean'; const: false }, { type: 'boolean'; const: true }] +} - /** `Extended` Creates a void type */ - public Void(options: CustomOptions = {}): TVoid { - return this.Store({ ...options, type: 'void', kind: VoidKind }) - } - - /** `Standard` Creates a namespace for a set of related types */ - public Namespace($defs: T, options: CustomOptions = {}): TNamespace { - return this.Store({ ...options, kind: BoxKind, $defs }) - } - - /** `Standard` References a type within a namespace. The referenced namespace must specify an `$id` */ - public Ref, K extends keyof T['$defs']>(namespace: T, key: K): TRef - - /** `Standard` References type. The referenced type must specify an `$id` */ - public Ref(schema: T): TRef - - public Ref(...args: any[]): any { - if(args.length === 2) { - const namespace = args[0] as TNamespace - const targetKey = args[1] as string - if(namespace.$id === undefined) throw new Error(`Referenced namespace has no $id`) - if(!this.schemas.has(namespace.$id)) throw new Error(`Unable to locate namespace with $id '${namespace.$id}'`) - return this.Store({ kind: RefKind, $ref: `${namespace.$id}#/$defs/${targetKey}` }) - } else if(args.length === 1) { - const target = args[0] as any - if(target.$id === undefined) throw new Error(`Referenced schema has no $id`) - if(!this.schemas.has(target.$id)) throw new Error(`Unable to locate schema with $id '${target.$id}'`) - return this.Store({ kind: RefKind, $ref: target.$id }) - } else { - throw new Error('Type.Ref: Invalid arguments') - } - } +// -------------------------------------------------------------------------- +// Null +// -------------------------------------------------------------------------- - /** `Experimental` Creates a recursive type */ - public Rec(callback: (self: TAny) => T, options: CustomOptions = {}): T { - const $id = options.$id || '' - const self = callback({ $ref: `${$id}#/$defs/self` } as any) - return this.Store({ ...options, $ref: `${$id}#/$defs/self`, $defs: { self } }) - } +export interface TNull extends TSchema { + [Kind]: 'Null' + static: null + type: 'null' +} - /** Conditionally stores and schema if it contains an $id and returns */ - protected Store, S = Omit>(schema: S): T { - const $schema: any = schema - if(!$schema['$id']) return $schema - this.schemas.set($schema['$id'], $schema) - return $schema - } +// -------------------------------------------------------------------------- +// Number +// -------------------------------------------------------------------------- - /** Conditionally dereferences a schema if RefKind. Otherwise return argument */ - protected Deref(schema: T): any { - const $schema: any = schema - if($schema['kind'] !== RefKind) return schema - if(!this.schemas.has($schema['$ref'])) throw Error(`Unable to locate schema with $id '${$schema['$ref']}'`) - return this.Deref(this.schemas.get($schema['$ref'])!) - } +export interface TNumber extends TSchema, NumericOptions { + [Kind]: 'Number' + static: number + type: 'number' +} + +// -------------------------------------------------------------------------- +// Object +// -------------------------------------------------------------------------- + +export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] + +export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] + +export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? K : never }[keyof T] + +export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> + +export type PropertiesReduce = { readonly [K in ReadonlyOptionalPropertyKeys]?: Static } & { + readonly [K in ReadonlyPropertyKeys]: Static +} & { + [K in OptionalPropertyKeys]?: Static +} & { [K in RequiredPropertyKeys]: Static } extends infer R + ? { [K in keyof R]: R[K] } + : never + +export type TRecordProperties, T extends TSchema> = Static extends string ? { [X in Static]: T } : never + +export interface TProperties { + [key: string]: TSchema +} + +export type ObjectProperties = T extends TObject ? U : never + +export type ObjectPropertyKeys = T extends TObject ? keyof U : never + +export interface ObjectOptions extends SchemaOptions { + additionalProperties?: boolean + minProperties?: number + maxProperties?: number +} + +export interface TObject extends TSchema, ObjectOptions { + [Kind]: 'Object' + static: PropertiesReduce + type: 'object' + properties: T + required?: string[] +} + +// -------------------------------------------------------------------------- +// Omit +// -------------------------------------------------------------------------- + +export interface TOmit[]> extends TObject, ObjectOptions { + static: Omit, Properties[number]> + properties: T extends TObject ? Omit : never +} + +// -------------------------------------------------------------------------- +// Partial +// -------------------------------------------------------------------------- + +export interface TPartial extends TObject { + static: Partial> +} + +// -------------------------------------------------------------------------- +// Pick +// -------------------------------------------------------------------------- + +// export interface TPick[]> extends TObject, ObjectOptions { +// static: Pick, Properties[number]> +// properties: ObjectProperties +// } + +export type TPick[]> = TObject<{ + [K in Properties[number]]: T['properties'][K] +}> + +// -------------------------------------------------------------------------- +// Promise +// -------------------------------------------------------------------------- + +export interface TPromise extends TSchema { + [Kind]: 'Promise' + static: Promise> + type: 'promise' + item: TSchema +} + +// -------------------------------------------------------------------------- +// Record +// -------------------------------------------------------------------------- + +export type TRecordKey = TString | TNumber | TUnion[]> + +export interface TRecord extends TSchema { + [Kind]: 'Record' + static: Record, Static> + type: 'object' + patternProperties: { [pattern: string]: T } + additionalProperties: false +} + +// -------------------------------------------------------------------------- +// Rec +// -------------------------------------------------------------------------- + +export interface TSelf extends TSchema { + [Kind]: 'Self' + static: this['params'][0] + $ref: string +} + +export type TRecursiveReduce = Static]> + +export interface TRecursive extends TSchema { + static: TRecursiveReduce +} + +// -------------------------------------------------------------------------- +// Ref +// -------------------------------------------------------------------------- + +export interface TRef extends TSchema { + [Kind]: 'Ref' + static: Static + $ref: string +} + +// -------------------------------------------------------------------------- +// Required +// -------------------------------------------------------------------------- + +export interface TRequired> extends TObject { + static: Required> +} + +// -------------------------------------------------------------------------- +// String +// -------------------------------------------------------------------------- + +export type StringFormatOption = + | 'date-time' + | 'time' + | 'date' + | 'email' + | 'idn-email' + | 'hostname' + | 'idn-hostname' + | 'ipv4' + | 'ipv6' + | 'uri' + | 'uri-reference' + | 'iri' + | 'uuid' + | 'iri-reference' + | 'uri-template' + | 'json-pointer' + | 'relative-json-pointer' + | 'regex' + +export interface StringOptions extends SchemaOptions { + minLength?: number + maxLength?: number + pattern?: string + format?: Format + contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64' + contentMediaType?: string } +export interface TString extends TSchema, StringOptions { + [Kind]: 'String' + static: string + type: 'string' +} + +// -------------------------------------------------------------------------- +// Tuple +// -------------------------------------------------------------------------- + +export type TupleToArray> = T extends TTuple ? R : never + +export interface TTuple extends TSchema { + [Kind]: 'Tuple' + static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } + type: 'array' + items?: T + additionalItems?: false + minItems: number + maxItems: number +} + +// -------------------------------------------------------------------------- +// Undefined +// -------------------------------------------------------------------------- + +export interface TUndefined extends TSchema { + [Kind]: 'Undefined' + specialized: 'Undefined' + static: undefined + type: 'object' +} + +// -------------------------------------------------------------------------- +// Union +// -------------------------------------------------------------------------- + +export interface TUnion extends TSchema { + [Kind]: 'Union' + static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] + anyOf: T +} + +// ------------------------------------------------------------------------- +// Uint8Array +// ------------------------------------------------------------------------- + +export interface Uint8ArrayOptions extends SchemaOptions { + maxByteLength?: number + minByteLength?: number +} + +export interface TUint8Array extends TSchema, Uint8ArrayOptions { + [Kind]: 'Uint8Array' + static: Uint8Array + specialized: 'Uint8Array' + type: 'object' +} + +// -------------------------------------------------------------------------- +// Unknown +// -------------------------------------------------------------------------- + +export interface TUnknown extends TSchema { + [Kind]: 'Unknown' + static: unknown +} + +// -------------------------------------------------------------------------- +// Unsafe +// -------------------------------------------------------------------------- + +export interface UnsafeOptions extends SchemaOptions { + [Kind]?: string +} + +export interface TUnsafe extends TSchema { + [Kind]: string + static: T +} + +// -------------------------------------------------------------------------- +// Void +// -------------------------------------------------------------------------- + +export interface TVoid extends TSchema { + [Kind]: 'Void' + static: void + type: 'null' +} + +// -------------------------------------------------------------------------- +// Static +// -------------------------------------------------------------------------- + +/** Creates a static type from a TypeBox type */ +export type Static = (T & { params: P })['static'] + +// -------------------------------------------------------------------------- +// TypeBuilder +// -------------------------------------------------------------------------- + +let TypeOrdinal = 0 + +export class TypeBuilder { + // ---------------------------------------------------------------------- + // Modifiers + // ---------------------------------------------------------------------- + + /** Creates a readonly optional property */ + public ReadonlyOptional(item: T): TReadonlyOptional { + return { [Modifier]: 'ReadonlyOptional', ...item } + } + + /** Creates a readonly property */ + public Readonly(item: T): TReadonly { + return { [Modifier]: 'Readonly', ...item } + } + + /** Creates a optional property */ + public Optional(item: T): TOptional { + return { [Modifier]: 'Optional', ...item } + } + + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- + + /** Creates a any type */ + public Any(options: SchemaOptions = {}): TAny { + return this.Create({ ...options, [Kind]: 'Any' }) + } + + /** Creates a array type */ + public Array(items: T, options: ArrayOptions = {}): TArray { + return this.Create({ ...options, [Kind]: 'Array', type: 'array', items }) + } + + /** Creates a boolean type */ + public Boolean(options: SchemaOptions = {}): TBoolean { + return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) + } + + /** Creates a tuple type from this constructors parameters */ + public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + return this.Tuple([...schema.parameters], { ...options }) + } + + /** Creates a constructor type */ + public Constructor, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor, U> + + /** Creates a constructor type */ + public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor + + /** Creates a constructor type */ + public Constructor(parameters: any, returns: any, options: SchemaOptions = {}) { + if (parameters[Kind] === 'Tuple') { + const inner = parameters.items === undefined ? [] : parameters.items + return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters: inner, returns }) + } else if (globalThis.Array.isArray(parameters)) { + return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters, returns }) + } else { + throw new Error('TypeBuilder.Constructor: Invalid parameters') + } + } + + /** Creates a enum type */ + public Enum>(item: T, options: SchemaOptions = {}): TEnum { + const values = Object.keys(item) + .filter((key) => isNaN(key as any)) + .map((key) => item[key]) as T[keyof T][] + const anyOf = values.map((value) => (typeof value === 'string' ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) + return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'Enum', anyOf }) + } + + /** Creates a function type */ + public Function, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction, U> + + /** Creates a function type */ + public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction + + /** Creates a function type */ + public Function(parameters: any, returns: any, options: SchemaOptions = {}) { + if (parameters[Kind] === 'Tuple') { + const inner = parameters.items === undefined ? [] : parameters.items + return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters: inner, returns }) + } else if (globalThis.Array.isArray(parameters)) { + return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters, returns }) + } else { + throw new Error('TypeBuilder.Function: Invalid parameters') + } + } + + /** Creates a type from this constructors instance type */ + public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + return { ...options, ...this.Clone(schema.returns) } + } + + /** Creates a integer type */ + public Integer(options: NumericOptions = {}): TInteger { + return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) + } + + /** Creates a intersect type. */ + public Intersect(objects: [...T], options: ObjectOptions = {}): TIntersect { + const isOptional = (schema: TSchema) => (schema[Modifier] && schema[Modifier] === 'Optional') || schema[Modifier] === 'ReadonlyOptional' + const [required, optional] = [new Set(), new Set()] + for (const object of objects) { + for (const [key, schema] of Object.entries(object.properties)) { + if (isOptional(schema)) optional.add(key) + } + } + for (const object of objects) { + for (const key of Object.keys(object.properties)) { + if (!optional.has(key)) required.add(key) + } + } + const properties = {} as Record + for (const object of objects) { + for (const [key, schema] of Object.entries(object.properties)) { + properties[key] = properties[key] === undefined ? schema : { [Kind]: 'Union', anyOf: [properties[key], { ...schema }] } + } + } + if (required.size > 0) { + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] }) + } else { + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) + } + } + + /** Creates a keyof type */ + public KeyOf(object: T, options: SchemaOptions = {}): TKeyOf { + const items = Object.keys(object.properties).map((key) => this.Create({ ...options, [Kind]: 'Literal', type: 'string', const: key })) + return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'KeyOf', anyOf: items }) + } + + /** Creates a literal type. */ + public Literal(value: T, options: SchemaOptions = {}): TLiteral { + return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) + } + + /** Creates a never type */ + public Never(options: SchemaOptions = {}): TNever { + return this.Create({ + ...options, + [Kind]: 'Never', + allOf: [ + { type: 'boolean', const: false }, + { type: 'boolean', const: true }, + ], + }) + } + + /** Creates a null type */ + public Null(options: SchemaOptions = {}): TNull { + return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) + } + + /** Creates a number type */ + public Number(options: NumericOptions = {}): TNumber { + return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) + } + + /** Creates an object type with the given properties */ + public Object(properties: T, options: ObjectOptions = {}): TObject { + const property_names = Object.keys(properties) + const optional = property_names.filter((name) => { + const property = properties[name] as TModifier + const modifier = property[Modifier] + return modifier && (modifier === 'Optional' || modifier === 'ReadonlyOptional') + }) + const required = property_names.filter((name) => !optional.includes(name)) + if (required.length > 0) { + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required }) + } else { + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) + } + } + + /** Creates a new object whose properties are omitted from the given object */ + public Omit[]>>(schema: T, keys: K, options?: ObjectOptions): TOmit> + + /** Creates a new object whose properties are omitted from the given object */ + public Omit[]>(schema: T, keys: [...K], options?: ObjectOptions): TOmit + + /** Creates a new object whose properties are omitted from the given object */ + public Omit(schema: any, keys: any, options: ObjectOptions = {}) { + const select: string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys + const next = { ...this.Clone(schema), ...options, [Hint]: 'Omit' } + if (next.required) { + next.required = next.required.filter((key: string) => !select.includes(key as any)) + if (next.required.length === 0) delete next.required + } + for (const key of Object.keys(next.properties)) { + if (select.includes(key as any)) delete next.properties[key] + } + return this.Create(next) + } + + /** Creates a tuple type from this functions parameters */ + public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + return Type.Tuple(schema.parameters, { ...options }) + } + + /** Creates an object type whose properties are all optional */ + public Partial(schema: T, options: ObjectOptions = {}): TPartial { + const next = { ...this.Clone(schema), ...options, [Hint]: 'Partial' } + delete next.required + for (const key of Object.keys(next.properties)) { + const property = next.properties[key] + const modifer = property[Modifier] + switch (modifer) { + case 'ReadonlyOptional': + property[Modifier] = 'ReadonlyOptional' + break + case 'Readonly': + property[Modifier] = 'ReadonlyOptional' + break + case 'Optional': + property[Modifier] = 'Optional' + break + default: + property[Modifier] = 'Optional' + break + } + } + return this.Create(next) + } + + /** Creates a object whose properties are picked from the given object */ + public Pick[]>>(schema: T, keys: K, options?: ObjectOptions): TPick> + + /** Creates a object whose properties are picked from the given object */ + public Pick[]>(schema: T, keys: [...K], options?: ObjectOptions): TPick + + /** Creates a object whose properties are picked from the given object */ + public Pick[]>(schema: any, keys: any, options: ObjectOptions = {}) { + const select: string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys + const next = { ...this.Clone(schema), ...options, [Hint]: 'Pick' } + if (next.required) { + next.required = next.required.filter((key: any) => select.includes(key)) + if (next.required.length === 0) delete next.required + } + for (const key of Object.keys(next.properties)) { + if (!select.includes(key as any)) delete next.properties[key] + } + return this.Create(next) + } + + /** Creates a promise type. This type cannot be represented in schema. */ + public Promise(item: T, options: SchemaOptions = {}): TPromise { + return this.Create({ ...options, [Kind]: 'Promise', type: 'promise', item }) + } + + /** Creates an object whose properties are derived from the given string literal union. */ + public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> + + /** Creates a record type */ + public Record(key: K, schema: T, options?: ObjectOptions): TRecord + + /** Creates a record type */ + public Record(key: any, value: any, options: ObjectOptions = {}) { + // If string literal union return TObject with properties extracted from union. + if (key[Kind] === 'Union') { + return this.Object( + key.anyOf.reduce((acc: any, literal: any) => { + return { ...acc, [literal.const]: value } + }, {}), + { ...options, [Hint]: 'Record' }, + ) + } + // otherwise return TRecord with patternProperties + const pattern = key[Kind] === 'Number' ? '^(0|[1-9][0-9]*)$' : key[Kind] === 'String' && key.pattern ? key.pattern : '^.*$' + return this.Create({ + ...options, + [Kind]: 'Record', + type: 'object', + patternProperties: { [pattern]: value }, + additionalProperties: false, + }) + } + + /** Creates a recursive object type */ + public Recursive(callback: (self: TSelf) => T, options: SchemaOptions = {}): TRecursive { + if (options.$id === undefined) options.$id = `T${TypeOrdinal++}` + const self = callback({ [Kind]: 'Self', $ref: `${options.$id}` } as any) + self.$id = options.$id + return this.Create({ ...options, ...self } as any) + } + + /** Creates a reference schema */ + public Ref(schema: T, options: SchemaOptions = {}): TRef { + if (schema.$id === undefined) throw Error('TypeBuilder.Ref: Referenced schema must specify an $id') + return this.Create({ ...options, [Kind]: 'Ref', $ref: schema.$id! }) + } + + /** Creates a string type from a regular expression */ + public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { + return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern: regex.source }) + } + + /** Creates an object type whose properties are all required */ + public Required(schema: T, options: SchemaOptions = {}): TRequired { + const next = { ...this.Clone(schema), ...options, [Hint]: 'Required' } + next.required = Object.keys(next.properties) + for (const key of Object.keys(next.properties)) { + const property = next.properties[key] + const modifier = property[Modifier] + switch (modifier) { + case 'ReadonlyOptional': + property[Modifier] = 'Readonly' + break + case 'Readonly': + property[Modifier] = 'Readonly' + break + case 'Optional': + delete property[Modifier] + break + default: + delete property[Modifier] + break + } + } + return this.Create(next) + } + + /** Creates a type from this functions return type */ + public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return { ...options, ...this.Clone(schema.returns) } + } + + /** Removes Kind and Modifier symbol property keys from this schema */ + public Strict(schema: T): T { + return JSON.parse(JSON.stringify(schema)) + } + + /** Creates a string type */ + public String(options: StringOptions = {}): TString { + return this.Create({ ...options, [Kind]: 'String', type: 'string' }) + } + + /** Creates a tuple type */ + public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { + const additionalItems = false + const minItems = items.length + const maxItems = items.length + const schema = (items.length > 0 ? { ...options, [Kind]: 'Tuple', type: 'array', items, additionalItems, minItems, maxItems } : { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any + return this.Create(schema) + } + + /** Creates a undefined type */ + public Undefined(options: SchemaOptions = {}): TUndefined { + return this.Create({ ...options, [Kind]: 'Undefined', type: 'object', specialized: 'Undefined' }) + } + + /** Creates a union type */ + + public Union(items: [], options?: SchemaOptions): TNever + public Union(items: [...T], options?: SchemaOptions): TUnion + public Union(items: [...T], options: SchemaOptions = {}) { + return items.length === 0 ? Type.Never({ ...options }) : this.Create({ ...options, [Kind]: 'Union', anyOf: items }) + } + + /** Creates a Uint8Array type */ + public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', specialized: 'Uint8Array' }) + } + + /** Creates an unknown type */ + public Unknown(options: SchemaOptions = {}): TUnknown { + return this.Create({ ...options, [Kind]: 'Unknown' }) + } + + /** Creates a user defined schema that infers as type T */ + public Unsafe(options: UnsafeOptions = {}): TUnsafe { + return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) + } + + /** Creates a void type */ + public Void(options: SchemaOptions = {}): TVoid { + return this.Create({ ...options, [Kind]: 'Void', type: 'null' }) + } + + /** Use this function to return TSchema with static and params omitted */ + protected Create(schema: Omit): T { + return schema as any + } + + /** Clones the given value */ + protected Clone(value: any): any { + const isObject = (object: any): object is Record => typeof object === 'object' && object !== null && !Array.isArray(object) + const isArray = (object: any): object is any[] => typeof object === 'object' && object !== null && Array.isArray(object) + if (isObject(value)) { + return Object.keys(value).reduce( + (acc, key) => ({ + ...acc, + [key]: this.Clone(value[key]), + }), + Object.getOwnPropertySymbols(value).reduce( + (acc, key) => ({ + ...acc, + [key]: this.Clone(value[key]), + }), + {}, + ), + ) + } else if (isArray(value)) { + return value.map((item: any) => this.Clone(item)) + } else { + return value + } + } +} +/** JSON Schema Type Builder with Static Type Resolution for TypeScript */ export const Type = new TypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast.ts new file mode 100644 index 000000000..4478b5ff5 --- /dev/null +++ b/src/value/cast.ts @@ -0,0 +1,370 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { ValueCreate } from './create' +import { ValueCheck } from './check' +import { ValueClone } from './clone' + +namespace UnionValueCast { + // ---------------------------------------------------------------------------------------------- + // The following will score a schema against a value. For objects, the score is the tally of + // points awarded for each property of the value. Property points are (1.0 / propertyCount) + // to prevent large property counts biasing results. Properties that match literal values are + // maximally awarded as literals are typically used as union discriminator fields. + // ---------------------------------------------------------------------------------------------- + function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { + if (schema[Types.Kind] === 'Object' && typeof value === 'object' && value !== null) { + const object = schema as Types.TObject + const keys = Object.keys(value) + const entries = globalThis.Object.entries(object.properties) + const [point, max] = [1 / entries.length, entries.length] + return entries.reduce((acc, [key, schema]) => { + const literal = schema[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0 + const checks = ValueCheck.Check(schema, references, value[key]) ? point : 0 + const exists = keys.includes(key) ? point : 0 + return acc + (literal + checks + exists) + }, 0) + } else { + return ValueCheck.Check(schema, references, value) ? 1 : 0 + } + } + function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { + let [select, best] = [union.anyOf[0], 0] + for (const schema of union.anyOf) { + const score = Score(schema, references, value) + if (score > best) { + select = schema + best = score + } + } + return select + } + + export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { + return ValueCheck.Check(union, references, value) ? ValueClone.Clone(value) : ValueCast.Cast(Select(union, references, value), references, value) + } +} + +// ----------------------------------------------------------- +// Errors +// ----------------------------------------------------------- + +export class ValueCastReferenceTypeError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) + } +} + +export class ValueCastArrayUniqueItemsTypeError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { + super('ValueCast: Array cast produced invalid data due to uniqueItems constraint') + } +} + +export class ValueCastNeverTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast: Never types cannot be cast') + } +} + +export class ValueCastRecursiveTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast.Recursive: Cannot cast recursive schemas') + } +} +export class ValueCastUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast: Unknown type') + } +} + +export namespace ValueCast { + // ----------------------------------------------------------- + // Guards + // ----------------------------------------------------------- + + function IsArray(value: unknown): value is unknown[] { + return typeof value === 'object' && globalThis.Array.isArray(value) + } + + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + + function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } + + function IsStringNumeric(value: unknown): value is string { + return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) + } + + function IsValueToString(value: unknown): value is { toString: () => string } { + return IsBigInt(value) || IsBoolean(value) || IsNumber(value) + } + + function IsValueTrue(value: unknown): value is true { + return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === 1n) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) + } + + function IsValueFalse(value: unknown): value is true { + return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === 0n) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) + } + + // ----------------------------------------------------------- + // Convert + // ----------------------------------------------------------- + + function TryConvertString(value: unknown) { + return IsValueToString(value) ? value.toString() : value + } + + function TryConvertNumber(value: unknown) { + return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : value + } + + function TryConvertInteger(value: unknown) { + return IsStringNumeric(value) ? parseInt(value) : IsValueTrue(value) ? 1 : value + } + + function TryConvertBoolean(value: unknown) { + return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value + } + + // ----------------------------------------------------------- + // Cast + // ----------------------------------------------------------- + + function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + const created = IsArray(value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...globalThis.Array.from({ length: schema.minItems - created.length }, () => null)] : created + const maximum = IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum + const casted = maximum.map((value: unknown) => Visit(schema.items, references, value)) + if (schema.uniqueItems !== true) return casted + const unique = [...new Set(casted)] + if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) + return unique + } + + function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { + const conversion = TryConvertBoolean(value) + return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + } + + function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) + const required = new Set(schema.returns.required || []) + const result = function () {} + for (const [key, property] of globalThis.Object.entries(schema.returns.properties)) { + if (!required.has(key) && value.prototype[key] === undefined) continue + result.prototype[key] = Visit(property as Types.TSchema, references, value.prototype[key]) + } + return result + } + + function Enum(schema: Types.TEnum, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): any { + const conversion = TryConvertInteger(value) + return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + } + + function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Never(schema: Types.TNever, references: Types.TSchema[], value: any): any { + throw new ValueCastNeverTypeError(schema) + } + + function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): any { + const conversion = TryConvertNumber(value) + return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + } + + function Object(schema: Types.TObject, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) + const required = new Set(schema.required || []) + const result = {} as Record + for (const [key, property] of globalThis.Object.entries(schema.properties)) { + if (!required.has(key) && value[key] === undefined) continue + result[key] = Visit(property, references, value[key]) + } + return result + } + + function Promise(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) + const subschemaKey = globalThis.Object.keys(schema.patternProperties)[0] + const subschema = schema.patternProperties[subschemaKey] + const result = {} as Record + for (const [propKey, propValue] of globalThis.Object.entries(value)) { + result[propKey] = Visit(subschema, references, propValue) + } + return result + } + + function Recursive(schema: Types.TRecursive, references: Types.TSchema[], value: any): any { + throw new ValueCastRecursiveTypeError(schema) + } + + function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): any { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new ValueCastReferenceTypeError(schema) + return Visit(reference, references, value) + } + + function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): any { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new ValueCastReferenceTypeError(schema) + return Visit(reference, references, value) + } + + function String(schema: Types.TString, references: Types.TSchema[], value: any): any { + const conversion = TryConvertString(value) + return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + } + + function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (!globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) + if (schema.items === undefined) return [] + return schema.items.map((schema, index) => Visit(schema, references, value[index])) + } + + function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): any { + return UnionValueCast.Create(schema, references, value) + } + + function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + + export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + const anyReferences = schema.$id === undefined ? references : [schema, ...references] + const anySchema = schema as any + switch (schema[Types.Kind]) { + case 'Any': + return Any(anySchema, anyReferences, value) + case 'Array': + return Array(anySchema, anyReferences, value) + case 'Boolean': + return Boolean(anySchema, anyReferences, value) + case 'Constructor': + return Constructor(anySchema, anyReferences, value) + case 'Enum': + return Enum(anySchema, anyReferences, value) + case 'Function': + return Function(anySchema, anyReferences, value) + case 'Integer': + return Integer(anySchema, anyReferences, value) + case 'Literal': + return Literal(anySchema, anyReferences, value) + case 'Never': + return Never(anySchema, anyReferences, value) + case 'Null': + return Null(anySchema, anyReferences, value) + case 'Number': + return Number(anySchema, anyReferences, value) + case 'Object': + return Object(anySchema, anyReferences, value) + case 'Promise': + return Promise(anySchema, anyReferences, value) + case 'Record': + return Record(anySchema, anyReferences, value) + case 'Rec': + return Recursive(anySchema, anyReferences, value) + case 'Ref': + return Ref(anySchema, anyReferences, value) + case 'Self': + return Self(anySchema, anyReferences, value) + case 'String': + return String(anySchema, anyReferences, value) + case 'Tuple': + return Tuple(anySchema, anyReferences, value) + case 'Undefined': + return Undefined(anySchema, anyReferences, value) + case 'Union': + return Union(anySchema, anyReferences, value) + case 'Uint8Array': + return Uint8Array(anySchema, anyReferences, value) + case 'Unknown': + return Unknown(anySchema, anyReferences, value) + case 'Void': + return Void(anySchema, anyReferences, value) + default: + throw new ValueCastUnknownTypeError(anySchema) + } + } + + export function Cast(schema: T, references: [...R], value: any): Types.Static { + return schema.$id === undefined ? Visit(schema, references, value) : Visit(schema, [schema, ...references], value) + } +} diff --git a/src/value/check.ts b/src/value/check.ts new file mode 100644 index 000000000..b4f7bafcf --- /dev/null +++ b/src/value/check.ts @@ -0,0 +1,331 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { Format } from '../format' + +export class ValueCheckUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCheck: Unknown type') + } +} + +export namespace ValueCheck { + function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { + return true + } + function Array(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { + if (!globalThis.Array.isArray(value)) { + return false + } + if (schema.minItems !== undefined && !(value.length >= schema.minItems)) { + return false + } + if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) { + return false + } + if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { + return false + } + return value.every((val) => Visit(schema.items, references, val)) + } + + function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { + return typeof value === 'boolean' + } + + function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { + return Visit(schema.returns, references, value.prototype) + } + + function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { + return typeof value === 'function' + } + + function Integer(schema: Types.TNumeric, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'number')) { + return false + } + if (!globalThis.Number.isInteger(value)) { + return false + } + if (schema.multipleOf !== undefined && !(value % schema.multipleOf === 0)) { + return false + } + if (schema.exclusiveMinimum !== undefined && !(value > schema.exclusiveMinimum)) { + return false + } + if (schema.exclusiveMaximum !== undefined && !(value < schema.exclusiveMaximum)) { + return false + } + if (schema.minimum !== undefined && !(value >= schema.minimum)) { + return false + } + if (schema.maximum !== undefined && !(value <= schema.maximum)) { + return false + } + return true + } + + function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { + return value === schema.const + } + + function Never(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { + return false + } + + function Null(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { + return value === null + } + + function Number(schema: Types.TNumeric, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'number')) { + return false + } + if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + return false + } + if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + return false + } + if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + return false + } + if (schema.minimum && !(value >= schema.minimum)) { + return false + } + if (schema.maximum && !(value <= schema.maximum)) { + return false + } + return true + } + + function Object(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return false + } + if (schema.minProperties !== undefined && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + return false + } + if (schema.maxProperties !== undefined && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + return false + } + const propertyKeys = globalThis.Object.keys(schema.properties) + if (schema.additionalProperties === false) { + // optimization: If the property key length matches the required keys length + // then we only need check that the values property key length matches that + // of the property key length. This is because exhaustive testing for values + // will occur in subsequent property tests. + if (schema.required && schema.required.length === propertyKeys.length && !(globalThis.Object.keys(value).length === propertyKeys.length)) { + return false + } else { + if (!globalThis.Object.keys(value).every((key) => propertyKeys.includes(key))) { + return false + } + } + } + for (const propertyKey of propertyKeys) { + const propertySchema = schema.properties[propertyKey] + if (schema.required && schema.required.includes(propertyKey)) { + if (!Visit(propertySchema, references, value[propertyKey])) { + return false + } + } else { + if (value[propertyKey] !== undefined) { + if (!Visit(propertySchema, references, value[propertyKey])) { + return false + } + } + } + } + return true + } + + function Promise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { + return typeof value === 'object' && typeof value.then === 'function' + } + + function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return false + } + const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const regex = new RegExp(keyPattern) + if (!globalThis.Object.keys(value).every((key) => regex.test(key))) { + return false + } + for (const propValue of globalThis.Object.values(value)) { + if (!Visit(valueSchema, references, propValue)) return false + } + return true + } + + function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueCheck.Ref: Cannot find schema with $id '${schema.$ref}'.`) + return Visit(reference, references, value) + } + + function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): boolean { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueCheck.Self: Cannot find schema with $id '${schema.$ref}'.`) + return Visit(reference, references, value) + } + + function String(schema: Types.TString, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'string')) { + return false + } + if (schema.minLength !== undefined) { + if (!(value.length >= schema.minLength)) return false + } + if (schema.maxLength !== undefined) { + if (!(value.length <= schema.maxLength)) return false + } + if (schema.pattern !== undefined) { + const regex = new RegExp(schema.pattern) + if (!regex.test(value)) return false + } + if (schema.format !== undefined) { + if (!Format.Has(schema.format)) return false + const func = Format.Get(schema.format)! + return func(value) + } + return true + } + + function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { + if (!globalThis.Array.isArray(value)) { + return false + } + if (schema.items === undefined && !(value.length === 0)) { + return false + } + if (!(value.length === schema.maxItems)) { + return false + } + if (!schema.items) { + return true + } + for (let i = 0; i < schema.items.length; i++) { + if (!Visit(schema.items[i], references, value[i])) return false + } + return true + } + + function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { + return value === undefined + } + + function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { + return schema.anyOf.some((inner) => Visit(inner, references, value)) + } + + function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { + if (!(value instanceof globalThis.Uint8Array)) { + return false + } + if (schema.maxByteLength && !(value.length <= schema.maxByteLength)) { + return false + } + if (schema.minByteLength && !(value.length >= schema.minByteLength)) { + return false + } + return true + } + + function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { + return true + } + + function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { + return value === null + } + + function Visit(schema: T, references: Types.TSchema[], value: any): boolean { + const anyReferences = schema.$id === undefined ? references : [schema, ...references] + const anySchema = schema as any + switch (anySchema[Types.Kind]) { + case 'Any': + return Any(anySchema, anyReferences, value) + case 'Array': + return Array(anySchema, anyReferences, value) + case 'Boolean': + return Boolean(anySchema, anyReferences, value) + case 'Constructor': + return Constructor(anySchema, anyReferences, value) + case 'Function': + return Function(anySchema, anyReferences, value) + case 'Integer': + return Integer(anySchema, anyReferences, value) + case 'Literal': + return Literal(anySchema, anyReferences, value) + case 'Never': + return Never(anySchema, anyReferences, value) + case 'Null': + return Null(anySchema, anyReferences, value) + case 'Number': + return Number(anySchema, anyReferences, value) + case 'Object': + return Object(anySchema, anyReferences, value) + case 'Promise': + return Promise(anySchema, anyReferences, value) + case 'Record': + return Record(anySchema, anyReferences, value) + case 'Ref': + return Ref(anySchema, anyReferences, value) + case 'Self': + return Self(anySchema, anyReferences, value) + case 'String': + return String(anySchema, anyReferences, value) + case 'Tuple': + return Tuple(anySchema, anyReferences, value) + case 'Undefined': + return Undefined(anySchema, anyReferences, value) + case 'Union': + return Union(anySchema, anyReferences, value) + case 'Uint8Array': + return Uint8Array(anySchema, anyReferences, value) + case 'Unknown': + return Unknown(anySchema, anyReferences, value) + case 'Void': + return Void(anySchema, anyReferences, value) + default: + throw new ValueCheckUnknownTypeError(anySchema) + } + } + + // ------------------------------------------------------------------------- + // Check + // ------------------------------------------------------------------------- + + export function Check(schema: T, references: [...R], value: any): boolean { + return schema.$id === undefined ? Visit(schema, references, value) : Visit(schema, [schema, ...references], value) + } +} diff --git a/src/value/clone.ts b/src/value/clone.ts new file mode 100644 index 000000000..03b06c913 --- /dev/null +++ b/src/value/clone.ts @@ -0,0 +1,62 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' + +export namespace ValueClone { + function Object(value: ObjectType): any { + const keys = [...globalThis.Object.keys(value), ...globalThis.Object.getOwnPropertySymbols(value)] + return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) + } + + function Array(value: ArrayType): any { + return value.map((element: any) => Clone(element)) + } + + function TypedArray(value: TypedArrayType): any { + return value.slice() + } + + function Value(value: ValueType): any { + return value + } + + export function Clone(value: T): T { + if (Is.Object(value)) { + return Object(value) + } else if (Is.Array(value)) { + return Array(value) + } else if (Is.TypedArray(value)) { + return TypedArray(value) + } else if (Is.Value(value)) { + return Value(value) + } else { + throw new Error('ValueClone: Unable to clone value') + } + } +} diff --git a/src/value/create.ts b/src/value/create.ts new file mode 100644 index 000000000..5cd549096 --- /dev/null +++ b/src/value/create.ts @@ -0,0 +1,342 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' + +export class ValueCreateUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCreate: Unknown type') + } +} + +export class ValueCreateNeverTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCreate: Never types cannot be created') + } +} + +export namespace ValueCreate { + function Any(schema: Types.TAny, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + return {} + } + } + + function Array(schema: Types.TArray, references: Types.TSchema[]): any { + if (schema.uniqueItems === true && schema.default === undefined) { + throw new Error('ValueCreate.Array: Arrays with uniqueItems require a default value') + } else if (schema.default !== undefined) { + return schema.default + } else if (schema.minItems !== undefined) { + return globalThis.Array.from({ length: schema.minItems }).map((item) => { + return ValueCreate.Create(schema.items, references) + }) + } else { + return [] + } + } + + function Boolean(schema: Types.TBoolean, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + return false + } + } + + function Constructor(schema: Types.TConstructor, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + const value = ValueCreate.Create(schema.returns, references) as any + if (typeof value === 'object' && !globalThis.Array.isArray(value)) { + return class { + constructor() { + for (const [key, val] of globalThis.Object.entries(value)) { + const self = this as any + self[key] = val + } + } + } + } else { + return class {} + } + } + } + + function Enum(schema: Types.TEnum, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.anyOf.length === 0) { + throw new Error('ValueCreate.Enum: Cannot create default enum value as this enum has no items') + } else { + return schema.anyOf[0].const + } + } + + function Function(schema: Types.TFunction, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + return () => ValueCreate.Create(schema.returns, references) + } + } + + function Integer(schema: Types.TInteger, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.minimum !== undefined) { + return schema.minimum + } else { + return 0 + } + } + + function Literal(schema: Types.TLiteral, references: Types.TSchema[]): any { + return schema.const + } + + function Never(schema: Types.TNever, references: Types.TSchema[]): any { + throw new ValueCreateNeverTypeError(schema) + } + + function Null(schema: Types.TNull, references: Types.TSchema[]): any { + return null + } + + function Number(schema: Types.TNumber, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.minimum !== undefined) { + return schema.minimum + } else { + return 0 + } + } + + function Object(schema: Types.TObject, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + const required = new Set(schema.required) + return ( + schema.default || + globalThis.Object.entries(schema.properties).reduce((acc, [key, schema]) => { + return required.has(key) ? { ...acc, [key]: ValueCreate.Create(schema, references) } : { ...acc } + }, {}) + ) + } + } + + function Promise(schema: Types.TPromise, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + return globalThis.Promise.resolve(ValueCreate.Create(schema.item, references)) + } + } + + function Record(schema: Types.TRecord, references: Types.TSchema[]): any { + const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] + if (schema.default !== undefined) { + return schema.default + } else if (!(keyPattern === '^.*$' || keyPattern === '^(0|[1-9][0-9]*)$')) { + const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') + return propertyKeys.reduce((acc, key) => { + return { ...acc, [key]: Create(valueSchema, references) } + }, {}) + } else { + return {} + } + } + + function Recursive(schema: Types.TRecursive, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + throw new Error('ValueCreate.Recursive: Recursive types require a default value') + } + } + + function Ref(schema: Types.TRef, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueCreate.Ref: Cannot find schema with $id '${schema.$ref}'.`) + return Visit(reference, references) + } + } + + function Self(schema: Types.TSelf, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + const reference = references.find((reference) => reference.$id === schema.$ref) + if (reference === undefined) throw new Error(`ValueCreate.Self: Cannot locate schema with $id '${schema.$ref}'`) + return Visit(reference, references) + } + } + + function String(schema: Types.TString, references: Types.TSchema[]): any { + if (schema.pattern !== undefined) { + if (schema.default === undefined) { + throw new Error('ValueCreate.String: String types with patterns must specify a default value') + } else { + return schema.default + } + } else if (schema.format !== undefined) { + if (schema.default === undefined) { + throw new Error('ValueCreate.String: String types with formats must specify a default value') + } else { + return schema.default + } + } else { + if (schema.default !== undefined) { + return schema.default + } else if (schema.minLength !== undefined) { + return globalThis.Array.from({ length: schema.minLength }) + .map(() => '.') + .join('') + } else { + return '' + } + } + } + + function Tuple(schema: Types.TTuple, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } + if (schema.items === undefined) { + return [] + } else { + return globalThis.Array.from({ length: schema.minItems }).map((_, index) => ValueCreate.Create((schema.items as any[])[index], references)) + } + } + + function Undefined(schema: Types.TUndefined, references: Types.TSchema[]): any { + return undefined + } + + function Union(schema: Types.TUnion, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.anyOf.length === 0) { + throw new Error('ValueCreate.Union: Cannot create Union with zero variants') + } else { + return ValueCreate.Create(schema.anyOf[0], references) + } + } + function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.minByteLength !== undefined) { + return new globalThis.Uint8Array(schema.minByteLength) + } else { + return new globalThis.Uint8Array(0) + } + } + + function Unknown(schema: Types.TUnknown, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + return {} + } + } + + function Void(schema: Types.TVoid, references: Types.TSchema[]): any { + return null + } + + /** Creates a value from the given schema. If the schema specifies a default value, then that value is returned. */ + export function Visit(schema: T, references: Types.TSchema[]): Types.Static { + const anyReferences = schema.$id === undefined ? references : [schema, ...references] + const anySchema = schema as any + + switch (anySchema[Types.Kind]) { + case 'Any': + return Any(anySchema, anyReferences) + case 'Array': + return Array(anySchema, anyReferences) + case 'Boolean': + return Boolean(anySchema, anyReferences) + case 'Constructor': + return Constructor(anySchema, anyReferences) + case 'Enum': + return Enum(anySchema, anyReferences) + case 'Function': + return Function(anySchema, anyReferences) + case 'Integer': + return Integer(anySchema, anyReferences) + case 'Literal': + return Literal(anySchema, anyReferences) + case 'Never': + return Never(anySchema, anyReferences) + case 'Null': + return Null(anySchema, anyReferences) + case 'Number': + return Number(anySchema, anyReferences) + case 'Object': + return Object(anySchema, anyReferences) + case 'Promise': + return Promise(anySchema, anyReferences) + case 'Record': + return Record(anySchema, anyReferences) + case 'Rec': + return Recursive(anySchema, anyReferences) + case 'Ref': + return Ref(anySchema, anyReferences) + case 'Self': + return Self(anySchema, anyReferences) + case 'String': + return String(anySchema, anyReferences) + case 'Tuple': + return Tuple(anySchema, anyReferences) + case 'Undefined': + return Undefined(anySchema, anyReferences) + case 'Union': + return Union(anySchema, anyReferences) + case 'Uint8Array': + return Uint8Array(anySchema, anyReferences) + case 'Unknown': + return Unknown(anySchema, anyReferences) + case 'Void': + return Void(anySchema, anyReferences) + default: + throw new ValueCreateUnknownTypeError(anySchema) + } + } + + export function Create(schema: T, references: [...R]): Types.Static { + return Visit(schema, references) + } +} diff --git a/src/value/delta.ts b/src/value/delta.ts new file mode 100644 index 000000000..371a11f2c --- /dev/null +++ b/src/value/delta.ts @@ -0,0 +1,182 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' +import { ValueClone } from './clone' +import { ValuePointer } from './pointer' + +export type Edit = Insert | Update | Delete + +export interface Insert { + brand: T + type: 'insert' + path: string + value: any +} + +export interface Update { + brand: T + type: 'update' + path: string + value: any +} + +export interface Delete { + brand: T + type: 'delete' + path: string +} + +export namespace ValueDelta { + // --------------------------------------------------------------------- + // Edits + // --------------------------------------------------------------------- + + function Update(path: string, value: unknown): Edit { + return { type: 'update', path, value } as any + } + + function Insert(path: string, value: unknown): Edit { + return { type: 'insert', path, value } as any + } + + function Delete(path: string): Edit { + return { type: 'delete', path } as any + } + + // --------------------------------------------------------------------- + // Diff + // --------------------------------------------------------------------- + + function* Object(path: string, current: ObjectType, next: unknown): IterableIterator> { + if (!Is.Object(next)) return yield Update(path, next) + const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] + const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] + for (const key of currentKeys) { + if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (next[key] === undefined && nextKeys.includes(key)) yield Update(`${path}/${String(key)}`, undefined) + } + for (const key of nextKeys) { + if (current[key] === undefined || next[key] === undefined) continue + if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + yield* Visit(`${path}/${String(key)}`, current[key], next[key]) + } + for (const key of nextKeys) { + if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (current[key] === undefined) yield Insert(`${path}/${String(key)}`, next[key]) + } + for (const key of currentKeys.reverse()) { + if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (next[key] === undefined && !nextKeys.includes(key)) yield Delete(`${path}/${String(key)}`) + } + } + + function* Array(path: string, current: ArrayType, next: unknown): IterableIterator> { + if (!Is.Array(next)) return yield Update(path, next) + for (let i = 0; i < Math.min(current.length, next.length); i++) { + yield* Visit(`${path}/${i}`, current[i], next[i]) + } + for (let i = 0; i < next.length; i++) { + if (i < current.length) continue + yield Insert(`${path}/${i}`, next[i]) + } + for (let i = current.length - 1; i >= 0; i--) { + if (i < next.length) continue + yield Delete(`${path}/${i}`) + } + } + + function* TypedArray(path: string, current: TypedArrayType, next: unknown): IterableIterator> { + if (!Is.TypedArray(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield Update(path, next) + for (let i = 0; i < Math.min(current.length, next.length); i++) { + yield* Visit(`${path}/${i}`, current[i], next[i]) + } + } + + function* Value(path: string, current: ValueType, next: unknown): IterableIterator> { + if (current === next) return + yield Update(path, next) + } + + function* Visit(path: string, current: unknown, next: unknown): IterableIterator> { + if (Is.Object(current)) { + return yield* Object(path, current, next) + } else if (Is.Array(current)) { + return yield* Array(path, current, next) + } else if (Is.TypedArray(current)) { + return yield* TypedArray(path, current, next) + } else if (Is.Value(current)) { + return yield* Value(path, current, next) + } else { + throw new Error('ValueDelta: Cannot produce edits for value') + } + } + + export function Diff(current: T, next: T): Edit[] { + return [...Visit('', current, next)] + } + + // --------------------------------------------------------------------- + // Patch + // --------------------------------------------------------------------- + + function IsRootUpdate(edits: Edit[]): edits is [Update] { + return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' + } + + function IsIdentity(edits: Edit[]) { + return edits.length === 0 + } + + export function Patch(current: T, edits: Edit[]): T { + if (IsRootUpdate(edits)) { + return ValueClone.Clone(edits[0].value) + } + if (IsIdentity(edits)) { + return ValueClone.Clone(current) + } + const clone = ValueClone.Clone(current) + for (const edit of edits) { + switch (edit.type) { + case 'insert': { + ValuePointer.Set(clone, edit.path, edit.value) + break + } + case 'update': { + ValuePointer.Set(clone, edit.path, edit.value) + break + } + case 'delete': { + ValuePointer.Delete(clone, edit.path) + break + } + } + } + return clone + } +} diff --git a/src/value/equal.ts b/src/value/equal.ts new file mode 100644 index 000000000..773f2d3c1 --- /dev/null +++ b/src/value/equal.ts @@ -0,0 +1,67 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' + +export namespace ValueEqual { + function Object(left: ObjectType, right: unknown): boolean { + if (!Is.Object(right)) return false + const leftKeys = [...globalThis.Object.keys(left), ...globalThis.Object.getOwnPropertySymbols(left)] + const rightKeys = [...globalThis.Object.keys(right), ...globalThis.Object.getOwnPropertySymbols(right)] + if (leftKeys.length !== rightKeys.length) return false + return leftKeys.every((key) => Equal(left[key], right[key])) + } + + function Array(left: ArrayType, right: unknown): any { + if (!Is.Array(right) || left.length !== right.length) return false + return left.every((value, index) => Equal(value, right[index])) + } + + function TypedArray(left: TypedArrayType, right: unknown): any { + if (!Is.TypedArray(right) || left.length !== right.length || globalThis.Object.getPrototypeOf(left).constructor.name !== globalThis.Object.getPrototypeOf(right).constructor.name) return false + return left.every((value, index) => Equal(value, right[index])) + } + + function Value(left: ValueType, right: unknown): any { + return left === right + } + + export function Equal(left: T, right: unknown): right is T { + if (Is.Object(left)) { + return Object(left, right) + } else if (Is.TypedArray(left)) { + return TypedArray(left, right) + } else if (Is.Array(left)) { + return Array(left, right) + } else if (Is.Value(left)) { + return Value(left, right) + } else { + throw new Error('ValueEquals: Unable to compare value') + } + } +} diff --git a/src/value/index.ts b/src/value/index.ts new file mode 100644 index 000000000..f96cee562 --- /dev/null +++ b/src/value/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export { ValueError, ValueErrorType } from '../errors/index' +export * from './pointer' +export * from './value' diff --git a/src/value/is.ts b/src/value/is.ts new file mode 100644 index 000000000..729197d59 --- /dev/null +++ b/src/value/is.ts @@ -0,0 +1,50 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type ValueType = null | undefined | Function | symbol | bigint | number | boolean | string +export type ObjectType = Record +export type TypedArrayType = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array +export type ArrayType = unknown[] + +export namespace Is { + export function Object(value: unknown): value is ObjectType { + return value !== null && typeof value === 'object' && !globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) + } + + export function Array(value: unknown): value is ArrayType { + return globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) + } + + export function Value(value: unknown): value is ValueType { + return value === null || value === undefined || typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint' || typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string' + } + + export function TypedArray(value: unknown): value is TypedArrayType { + return ArrayBuffer.isView(value) + } +} diff --git a/src/value/pointer.ts b/src/value/pointer.ts new file mode 100644 index 000000000..4b7c5bf9c --- /dev/null +++ b/src/value/pointer.ts @@ -0,0 +1,122 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export class ValuePointerRootSetError extends Error { + constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { + super('ValuePointer: Cannot set root value') + } +} + +export class ValuePointerRootDeleteError extends Error { + constructor(public readonly value: unknown, public readonly path: string) { + super('ValuePointer: Cannot delete root value') + } +} + +/** ValuePointer performs mutable operations on values using RFC6901 Json Pointers */ +export namespace ValuePointer { + function Escape(component: string) { + return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') + } + + /** Formats the given pointer into navigable key components */ + export function* Format(pointer: string): IterableIterator { + if (pointer === '') return + let [start, end] = [0, 0] + for (let i = 0; i < pointer.length; i++) { + const char = pointer.charAt(i) + if (char === '/') { + if (i === 0) { + start = i + 1 + } else { + end = i + yield Escape(pointer.slice(start, end)) + start = i + 1 + } + } else { + end = i + } + } + yield Escape(pointer.slice(start)) + } + + /** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ + export function Set(value: any, pointer: string, update: unknown): void { + if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) + let [owner, next, key] = [null as any, value, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) next[component] = {} + owner = next + next = next[component] + key = component + } + owner[key] = update + } + + /** Deletes a value at the given pointer */ + export function Delete(value: any, pointer: string): void { + if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined || next[component] === null) return + owner = next + next = next[component] + key = component + } + if (globalThis.Array.isArray(owner)) { + const index = parseInt(key) + owner.splice(index, 1) + } else { + delete owner[key] + } + } + + /** Returns true if a value exists at the given pointer */ + export function Has(value: any, pointer: string): boolean { + if (pointer === '') return true + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) return false + owner = next + next = next[component] + key = component + } + return globalThis.Object.getOwnPropertyNames(owner).includes(key) + } + + /** Gets the value at the given pointer */ + export function Get(value: any, pointer: string): any { + if (pointer === '') return value + let current = value + for (const component of Format(pointer)) { + if (current[component] === undefined) return undefined + current = current[component] + } + return current + } +} diff --git a/src/value/value.ts b/src/value/value.ts new file mode 100644 index 000000000..ff0f27872 --- /dev/null +++ b/src/value/value.ts @@ -0,0 +1,97 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { ValueErrors, ValueError } from '../errors/index' +import { ValueEqual } from './equal' +import { ValueCast } from './cast' +import { ValueClone } from './clone' +import { ValueCreate } from './create' +import { ValueCheck } from './check' +import { ValueDelta, Edit } from './delta' + +export type { Edit } from './delta' + +/** Value performs immutable operations on values */ +export namespace Value { + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ + export function Cast(schema: T, references: [...R], value: unknown): Types.Static + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ + export function Cast(schema: T, value: unknown): Types.Static + export function Cast(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + return ValueCast.Cast(schema, references, value) + } + + /** Creates a value from the given type */ + export function Create(schema: T, references: [...R]): Types.Static + /** Creates a value from the given type */ + export function Create(schema: T): Types.Static + export function Create(...args: any[]) { + const [schema, references] = args.length === 2 ? [args[0], args[1]] : [args[0], []] + return ValueCreate.Create(schema, references) + } + + /** Returns true if the value matches the given type. */ + export function Check(schema: T, references: [...R], value: unknown): value is Types.Static + /** Returns true if the value matches the given type. */ + export function Check(schema: T, value: unknown): value is Types.Static + export function Check(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + return ValueCheck.Check(schema, references, value) + } + + /** Returns an iterator for each error in this value. */ + export function Errors(schema: T, references: [...R], value: unknown): IterableIterator + /** Returns an iterator for each error in this value. */ + export function Errors(schema: T, value: unknown): IterableIterator + export function* Errors(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + yield* ValueErrors.Errors(schema, references, value) + } + + /** Returns true if left and right values are structurally equal */ + export function Equal(left: T, right: unknown): right is T { + return ValueEqual.Equal(left, right) + } + + /** Returns a structural clone of the given value */ + export function Clone(value: T): T { + return ValueClone.Clone(value) + } + + /** Returns edits to transform the current value into the next value */ + export function Diff(current: T, next: T): Edit[] { + return ValueDelta.Diff(current, next) + } + + /** Returns a new value with edits applied to the given value */ + export function Patch(current: T, edits: Edit[]): T { + return ValueDelta.Patch(current, edits) as T + } +} diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts new file mode 100644 index 000000000..19543636d --- /dev/null +++ b/test/runtime/assert/assert.ts @@ -0,0 +1,59 @@ +import * as assert from 'assert' + +export namespace Assert { + let port = 9000 + /** Generates a new port used for host binding */ + export function nextPort() { + const next = port++ + return next + } + + export function equal(actual: unknown, expect: unknown) { + return assert.equal(actual, expect) + } + + export function notEqual(actual: unknown, expect: unknown) { + return assert.notEqual(actual, expect) + } + + export function deepEqual(actual: unknown, expect: unknown) { + if (actual instanceof Uint8Array && expect instanceof Uint8Array) { + assert.equal(actual.length, expect.length) + for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i]) + } + return assert.deepEqual(actual, expect) + } + + let nextIdOrdinal = 0 + export function nextId() { + return `nextID${nextIdOrdinal++}` + } + + export function throws(callback: Function) { + try { + callback() + } catch { + return + } + throw Error('Expected throw') + } + + export async function throwsAsync(callback: Function) { + try { + await callback() + } catch { + return + } + throw Error('Expected throw') + } + + export function isTypeOf(value: any, type: any) { + if (typeof value === type) return + throw Error(`Value is not typeof ${type}`) + } + + export function isInstanceOf(value: any, constructor: any) { + if (value instanceof constructor) return + throw Error(`Value is not instance of ${constructor}`) + } +} diff --git a/test/runtime/assert/index.ts b/test/runtime/assert/index.ts new file mode 100644 index 000000000..c3bd55138 --- /dev/null +++ b/test/runtime/assert/index.ts @@ -0,0 +1 @@ +export * from './assert' diff --git a/test/runtime/compiler/any.ts b/test/runtime/compiler/any.ts new file mode 100644 index 000000000..fdccc5f61 --- /dev/null +++ b/test/runtime/compiler/any.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok } from './validate' + +describe('type/compiler/Any', () => { + it('Should validate number', () => { + const T = Type.Any() + ok(T, 1) + }) + it('Should validate string', () => { + const T = Type.Any() + ok(T, 'hello') + }) + it('Should validate boolean', () => { + const T = Type.Any() + ok(T, true) + }) + it('Should validate array', () => { + const T = Type.Any() + ok(T, [1, 2, 3]) + }) + it('Should validate object', () => { + const T = Type.Any() + ok(T, { a: 1, b: 2 }) + }) + it('Should validate null', () => { + const T = Type.Any() + ok(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Any() + ok(T, undefined) + }) +}) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts new file mode 100644 index 000000000..f74b7b4d6 --- /dev/null +++ b/test/runtime/compiler/array.ts @@ -0,0 +1,97 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Array', () => { + it('Should validate an array of any', () => { + const T = Type.Array(Type.Any()) + ok(T, [0, true, 'hello', {}]) + }) + + it('Should not validate varying array when item is number', () => { + const T = Type.Array(Type.Number()) + fail(T, [1, 2, 3, 'hello']) + }) + + it('Should validate for an array of unions', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + ok(T, [1, 'hello', 3, 'world']) + }) + + it('Should not validate for an array of unions where item is not in union.', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + fail(T, [1, 'hello', 3, 'world', true]) + }) + + it('Should validate for an empty array', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + ok(T, []) + }) + + it('Should validate for an array of intersection types', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Intersect([A, B]) + const T = Type.Array(C) + ok(T, [ + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + ]) + }) + + it('Should not validate for an array of intersection types when passing additionalProperties false', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Intersect([A, B], { additionalProperties: false }) + const T = Type.Array(C) + fail(T, [ + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello', c: 'additional' }, + ]) + }) + + it('Should validate an array of tuples', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Tuple([A, B]) + const T = Type.Array(C) + ok(T, [ + ['hello', 1], + ['hello', 1], + ['hello', 1], + ]) + }) + + it('Should not validate an array of tuples when tuple values are incorrect', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Tuple([A, B]) + const T = Type.Array(C) + fail(T, [ + [1, 'hello'], + [1, 'hello'], + [1, 'hello'], + ]) + }) + + it('Should not validate array with failed minItems', () => { + const T = Type.Array(Type.Number(), { minItems: 3 }) + fail(T, [0, 1]) + }) + it('Should not validate array with failed maxItems', () => { + const T = Type.Array(Type.Number(), { maxItems: 3 }) + fail(T, [0, 1, 2, 3]) + }) + it('Should validate array with uniqueItems when items are references', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + ok(T, [ + { x: 0, y: 1 }, + { x: 1, y: 0 }, + ]) // references distinct + }) + it('Should not validate array with non uniqueItems', () => { + const T = Type.Array(Type.Number(), { uniqueItems: true }) + fail(T, [0, 0]) + }) +}) diff --git a/test/runtime/compiler/boolean.ts b/test/runtime/compiler/boolean.ts new file mode 100644 index 000000000..07293041f --- /dev/null +++ b/test/runtime/compiler/boolean.ts @@ -0,0 +1,40 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Boolean', () => { + it('Should validate a boolean', () => { + const T = Type.Boolean() + ok(T, true) + ok(T, false) + }) + + it('Should not validate a number', () => { + const T = Type.Boolean() + fail(T, 1) + }) + + it('Should not validate a string', () => { + const T = Type.Boolean() + fail(T, 'true') + }) + + it('Should not validate an array', () => { + const T = Type.Boolean() + fail(T, [true]) + }) + + it('Should not validate an object', () => { + const T = Type.Boolean() + fail(T, {}) + }) + + it('Should not validate an null', () => { + const T = Type.Boolean() + fail(T, null) + }) + + it('Should not validate an undefined', () => { + const T = Type.Boolean() + fail(T, undefined) + }) +}) diff --git a/test/runtime/compiler/enum.ts b/test/runtime/compiler/enum.ts new file mode 100644 index 000000000..736cee47b --- /dev/null +++ b/test/runtime/compiler/enum.ts @@ -0,0 +1,56 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Enum', () => { + it('Should validate when emum uses default numeric values', () => { + enum Kind { + Foo, // = 0 + Bar, // = 1 + } + const T = Type.Enum(Kind) + ok(T, 0) + ok(T, 1) + }) + it('Should not validate when given enum values are not numeric', () => { + enum Kind { + Foo, // = 0 + Bar, // = 1 + } + const T = Type.Enum(Kind) + fail(T, 'Foo') + fail(T, 'Bar') + }) + + it('Should validate when emum has defined string values', () => { + enum Kind { + Foo = 'foo', + Bar = 'bar', + } + const T = Type.Enum(Kind) + ok(T, 'foo') + ok(T, 'bar') + }) + + it('Should not validate when emum has defined string values and user passes numeric', () => { + enum Kind { + Foo = 'foo', + Bar = 'bar', + } + const T = Type.Enum(Kind) + fail(T, 0) + fail(T, 1) + }) + + it('Should validate when enum has one or more string values', () => { + enum Kind { + Foo, + Bar = 'bar', + } + const T = Type.Enum(Kind) + ok(T, 0) + ok(T, 'bar') + fail(T, 'baz') + fail(T, 'Foo') + fail(T, 1) + }) +}) diff --git a/spec/schema/index.ts b/test/runtime/compiler/index.ts similarity index 82% rename from spec/schema/index.ts rename to test/runtime/compiler/index.ts index a4a273c52..a77b5456e 100644 --- a/spec/schema/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,12 +1,12 @@ import './any' import './array' import './boolean' -import './box' import './enum' import './intersect' import './keyof' import './literal' import './modifier' +import './never' import './null' import './number' import './object' @@ -16,13 +16,14 @@ import './partial' import './pick' import './readonly-optional' import './readonly' -import './rec' +import './recursive' import './record' import './ref' import './regex' import './required' -import './strict' import './string' import './tuple' +import './uint8array' import './union' -import './unknown' \ No newline at end of file +import './unknown' +import './void' diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts new file mode 100644 index 000000000..f59d9adae --- /dev/null +++ b/test/runtime/compiler/intersect.ts @@ -0,0 +1,83 @@ +import { Type, Static } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Intersect', () => { + it('Should intersect two objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Intersect([A, B], { additionalProperties: false }) + ok(T, { a: 'hello', b: 42 }) + }) + + it('Should intersect with partial', () => { + const A = Type.Partial(Type.Object({ a: Type.Number() })) + const B = Type.Partial(Type.Object({ b: Type.Number() })) + const P = Type.Intersect([A, B], { additionalProperties: false }) + ok(P, { a: 1, b: 2 }) + // ok(P, { a: 1 }) + // ok(P, { b: 1 }) + // ok(P, {}) + // fail(P, { c: 1 }) + }) + + it('Should intersect with overlapping same type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Number() }) + const P = Type.Intersect([A, B]) + ok(P, { a: 1 }) + fail(P, { a: 'hello' }) + fail(P, {}) + }) + + it('Should intersect with overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const T = Type.Intersect([A, B]) + ok(T, { a: 1 }) + ok(T, { a: 'hello' }) + fail(T, {}) + }) + + it('Should intersect with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const C = Type.Object({ a: Type.Boolean() }) + const D = Type.Object({ a: Type.Null() }) + const T = Type.Intersect([A, B, C, D]) + ok(T, { a: 1 }) + ok(T, { a: 'hello' }) + ok(T, { a: false }) + ok(T, { a: null }) + fail(T, { a: [] }) + fail(T, {}) + }) + + it('Should pick from intersected type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Intersect([A, B, C]) + const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) + ok(P, { x: 1, y: 1 }) + fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should omit from intersected type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Intersect([A, B, C]) + const P = Type.Omit(T, ['z'], { additionalProperties: false }) + ok(P, { x: 1, y: 1 }) + fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should intersect nested object properties', () => { + const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) + const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const T = Type.Intersect([A, B]) + ok(T, { x: { x: 1 } }) + ok(T, { x: { x: 'hello' } }) + fail(T, { x: { x: false } }) + }) +}) diff --git a/test/runtime/compiler/keyof.ts b/test/runtime/compiler/keyof.ts new file mode 100644 index 000000000..3920b1485 --- /dev/null +++ b/test/runtime/compiler/keyof.ts @@ -0,0 +1,52 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/KeyOf', () => { + it('Should validate with all object keys as a kind of union', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + ok(T, 'x') + ok(T, 'y') + ok(T, 'z') + fail(T, 'w') + }) + + it('Should validate when using pick', () => { + const T = Type.KeyOf( + Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ['x', 'y'], + ), + ) + ok(T, 'x') + ok(T, 'y') + fail(T, 'z') + }) + + it('Should validate when using omit', () => { + const T = Type.KeyOf( + Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ['x', 'y'], + ), + ) + + fail(T, 'x') + fail(T, 'y') + ok(T, 'z') + }) +}) diff --git a/test/runtime/compiler/literal.ts b/test/runtime/compiler/literal.ts new file mode 100644 index 000000000..1aa46921f --- /dev/null +++ b/test/runtime/compiler/literal.ts @@ -0,0 +1,43 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Literal', () => { + it('Should validate literal number', () => { + const T = Type.Literal(42) + ok(T, 42) + }) + it('Should validate literal string', () => { + const T = Type.Literal('hello') + ok(T, 'hello') + }) + + it('Should validate literal boolean', () => { + const T = Type.Literal(true) + ok(T, true) + }) + + it('Should not validate invalid literal number', () => { + const T = Type.Literal(42) + fail(T, 43) + }) + it('Should not validate invalid literal string', () => { + const T = Type.Literal('hello') + fail(T, 'world') + }) + it('Should not validate invalid literal boolean', () => { + const T = Type.Literal(false) + fail(T, true) + }) + + it('Should validate literal union', () => { + const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) + ok(T, 42) + ok(T, 'hello') + }) + + it('Should not validate invalid literal union', () => { + const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) + fail(T, 43) + fail(T, 'world') + }) +}) diff --git a/test/runtime/compiler/modifier.ts b/test/runtime/compiler/modifier.ts new file mode 100644 index 000000000..39f16c447 --- /dev/null +++ b/test/runtime/compiler/modifier.ts @@ -0,0 +1,3 @@ +import { Type } from '@sinclair/typebox' + +describe('type/compiler/Modifier', () => {}) diff --git a/test/runtime/compiler/never.ts b/test/runtime/compiler/never.ts new file mode 100644 index 000000000..8be660cdf --- /dev/null +++ b/test/runtime/compiler/never.ts @@ -0,0 +1,36 @@ +import { Type } from '@sinclair/typebox' +import { fail } from './validate' + +describe('type/compiler/Never', () => { + it('Should not validate number', () => { + const T = Type.Never() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Never() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Never() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Never() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Never() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Never() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Never() + fail(T, undefined) + }) +}) diff --git a/test/runtime/compiler/null.ts b/test/runtime/compiler/null.ts new file mode 100644 index 000000000..a3e6009c4 --- /dev/null +++ b/test/runtime/compiler/null.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Null', () => { + it('Should not validate number', () => { + const T = Type.Null() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Null() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Null() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Null() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Null() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Null() + ok(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Null() + fail(T, undefined) + }) +}) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts new file mode 100644 index 000000000..0a9e05b08 --- /dev/null +++ b/test/runtime/compiler/number.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Number', () => { + it('Should validate number', () => { + const T = Type.Number() + ok(T, 1) + }) + it('Should not validate string', () => { + const T = Type.Number() + fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.Number() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.Number() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Number() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Number() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Number() + fail(T, undefined) + }) +}) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts new file mode 100644 index 000000000..845855cd7 --- /dev/null +++ b/test/runtime/compiler/object.ts @@ -0,0 +1,142 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Object', () => { + it('Should not validate a number', () => { + const T = Type.Object({}) + fail(T, 42) + }) + + it('Should not validate a string', () => { + const T = Type.Object({}) + fail(T, 'hello') + }) + + it('Should not validate a boolean', () => { + const T = Type.Object({}) + fail(T, true) + }) + + it('Should not validate a null', () => { + const T = Type.Object({}) + fail(T, null) + }) + + it('Should not validate an array', () => { + const T = Type.Object({}) + fail(T, [1, 2]) + }) + + it('Should validate with correct property values', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + c: Type.Boolean(), + d: Type.Array(Type.Number()), + e: Type.Object({ x: Type.Number(), y: Type.Number() }), + }) + ok(T, { + a: 10, + b: 'hello', + c: true, + d: [1, 2, 3], + e: { x: 10, y: 20 }, + }) + }) + + it('Should not validate with incorrect property values', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + c: Type.Boolean(), + d: Type.Array(Type.Number()), + e: Type.Object({ x: Type.Number(), y: Type.Number() }), + }) + fail(T, { + a: 'not a number', // error + b: 'hello', + c: true, + d: [1, 2, 3], + e: { x: 10, y: 20 }, + }) + }) + + it('Should allow additionalProperties by default', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + }) + ok(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow an empty object if minProperties is set to 1', () => { + const T = Type.Object( + { + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.String()), + }, + { additionalProperties: false, minProperties: 1 }, + ) + ok(T, { a: 1 }) + ok(T, { b: 'hello' }) + fail(T, {}) + }) + + it('Should not allow 3 properties if maxProperties is set to 2', () => { + const T = Type.Object( + { + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.String()), + c: Type.Optional(Type.Boolean()), + }, + { additionalProperties: false, maxProperties: 2 }, + ) + ok(T, { a: 1 }) + ok(T, { a: 1, b: 'hello' }) + fail(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow additionalProperties if additionalProperties is false', () => { + const T = Type.Object( + { + a: Type.Number(), + b: Type.String(), + }, + { additionalProperties: false }, + ) + fail(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow properties for an empty object when additionalProperties is false', () => { + const T = Type.Object({}, { additionalProperties: false }) + ok(T, {}) + fail(T, { a: 10 }) + }) + + it('Should validate with non-syntax property keys', () => { + const T = Type.Object({ + 'with-hyphen': Type.Literal(1), + '0-leading': Type.Literal(2), + '$-leading': Type.Literal(3), + '!@#$%^&*(': Type.Literal(4), + }) + ok(T, { + 'with-hyphen': 1, + '0-leading': 2, + '$-leading': 3, + '!@#$%^&*(': 4, + }) + }) +}) diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts new file mode 100644 index 000000000..29b65bab7 --- /dev/null +++ b/test/runtime/compiler/omit.ts @@ -0,0 +1,60 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/Omit', () => { + it('Should omit properties on the source schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + ok(T, { x: 1, y: 1 }) + }) + + it('Should remove required properties on the target schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + strictEqual(T.required!.includes('z'), false) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) + + it('Should omit with keyof object', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const T = Type.Omit(A, Type.KeyOf(B), { additionalProperties: false }) + ok(T, { z: 0 }) + fail(T, { x: 0, y: 0, z: 0 }) + }) +}) diff --git a/test/runtime/compiler/optional.ts b/test/runtime/compiler/optional.ts new file mode 100644 index 000000000..abbcb86c3 --- /dev/null +++ b/test/runtime/compiler/optional.ts @@ -0,0 +1,27 @@ +import { strictEqual } from 'assert' +import { Type } from '@sinclair/typebox' +import { ok } from './validate' + +describe('type/compiler/Optional', () => { + it('Should validate object with optional', () => { + const T = Type.Object( + { + a: Type.Optional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + ok(T, { b: 'world' }) + }) + it('Should remove required value from schema', () => { + const T = Type.Object( + { + a: Type.Optional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), false) + }) +}) diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts new file mode 100644 index 000000000..c060a0e20 --- /dev/null +++ b/test/runtime/compiler/partial.ts @@ -0,0 +1,52 @@ +import { Type, Modifier } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/Partial', () => { + it('Should convert a required object into a partial.', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + ok(T, { x: 1, y: 1, z: 1 }) + ok(T, { x: 1, y: 1 }) + ok(T, { x: 1 }) + ok(T, {}) + }) + + it('Should update modifier types correctly when converting to partial', () => { + const A = Type.Object( + { + x: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Number()), + z: Type.Optional(Type.Number()), + w: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + strictEqual(T.properties.x[Modifier], 'ReadonlyOptional') + strictEqual(T.properties.y[Modifier], 'ReadonlyOptional') + strictEqual(T.properties.z[Modifier], 'Optional') + strictEqual(T.properties.w[Modifier], 'Optional') + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) +}) diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts new file mode 100644 index 000000000..c66cbb912 --- /dev/null +++ b/test/runtime/compiler/pick.ts @@ -0,0 +1,60 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/Pick', () => { + it('Should pick properties from the source schema', () => { + const Vector3 = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(Vector3, ['x', 'y']) + ok(T, { x: 1, y: 1 }) + }) + + it('Should remove required properties on the target schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, ['x', 'y']) + strictEqual(T.required!.includes('z'), false) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, ['x', 'y']) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) + + it('Should pick with keyof object', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const T = Type.Pick(A, Type.KeyOf(B), { additionalProperties: false }) + ok(T, { x: 0, y: 0 }) + fail(T, { x: 0, y: 0, z: 0 }) + }) +}) diff --git a/test/runtime/compiler/readonly-optional.ts b/test/runtime/compiler/readonly-optional.ts new file mode 100644 index 000000000..33b78d762 --- /dev/null +++ b/test/runtime/compiler/readonly-optional.ts @@ -0,0 +1,27 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/ReadonlyOptional', () => { + it('Should validate object with optional', () => { + const T = Type.Object( + { + a: Type.ReadonlyOptional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + ok(T, { b: 'world' }) + }) + it('Should remove required value from schema', () => { + const T = Type.Object( + { + a: Type.ReadonlyOptional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), false) + }) +}) diff --git a/test/runtime/compiler/readonly.ts b/test/runtime/compiler/readonly.ts new file mode 100644 index 000000000..1ad7ff703 --- /dev/null +++ b/test/runtime/compiler/readonly.ts @@ -0,0 +1,28 @@ +import { deepStrictEqual, strictEqual } from 'assert' +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Readonly', () => { + it('Should validate object with readonly', () => { + const T = Type.Object( + { + a: Type.Readonly(Type.String()), + b: Type.Readonly(Type.String()), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + }) + + it('Should retain required array on object', () => { + const T = Type.Object( + { + a: Type.Readonly(Type.String()), + b: Type.Readonly(Type.String()), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), true) + strictEqual(T.required!.includes('b'), true) + }) +}) diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts new file mode 100644 index 000000000..aa3384805 --- /dev/null +++ b/test/runtime/compiler/record.ts @@ -0,0 +1,101 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Record', () => { + it('Should validate when all property values are numbers', () => { + const T = Type.Record(Type.String(), Type.Number()) + ok(T, { a: 1, b: 2, c: 3 }) + }) + + it('Should validate when all property keys are strings', () => { + const T = Type.Record(Type.String(), Type.Number()) + ok(T, { a: 1, b: 2, c: 3, '0': 4 }) + }) + + it('Should validate when all property keys are numbers', () => { + const T = Type.Record(Type.Number(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should validate when specifying string union literals when additionalProperties is true', () => { + const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) + const T = Type.Record(K, Type.Number()) + ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) + }) + + it('Should not validate when specifying string union literals when additionalProperties is false', () => { + const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) + const T = Type.Record(K, Type.Number(), { additionalProperties: false }) + fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) + }) + + it('Should validate for keyof records', () => { + const T = Type.Object({ + a: Type.String(), + b: Type.Number(), + c: Type.String(), + }) + const R = Type.Record(Type.KeyOf(T), Type.Number()) + ok(R, { a: 1, b: 2, c: 3 }) + }) + + it('Should not validate for unknown key via keyof', () => { + const T = Type.Object({ + a: Type.String(), + b: Type.Number(), + c: Type.String(), + }) + const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) + fail(R, { a: 1, b: 2, c: 3, d: 4 }) + }) + + it('Should should validate when specifying regular expressions', () => { + const K = Type.RegEx(/^op_.*$/) + const T = Type.Record(K, Type.Number()) + ok(T, { + op_a: 1, + op_b: 2, + op_c: 3, + }) + }) + + it('Should should not validate when specifying regular expressions and passing invalid property', () => { + const K = Type.RegEx(/^op_.*$/) + const T = Type.Record(K, Type.Number()) + fail(T, { + op_a: 1, + op_b: 2, + aop_c: 3, + }) + }) +}) diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts new file mode 100644 index 000000000..0bd48baf0 --- /dev/null +++ b/test/runtime/compiler/recursive.ts @@ -0,0 +1,83 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' +import { ok, fail } from './validate' + +describe('type/compiler/Recursive', () => { + it('Should generate default ordinal $id if not specified', () => { + const Node = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + Assert.equal(Node.$id === undefined, false) + }) + + it('Should override default ordinal $id if specified', () => { + const Node = Type.Recursive( + (Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + { $id: 'Node' }, + ) + Assert.equal(Node.$id === 'Node', true) + }) + + it('Should validate recursive node type', () => { + const Node = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + ok(Node, { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + ], + }) + }) + + it('Should validate wrapped recursive node type', () => { + const Node = Type.Tuple([ + Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ), + ]) + ok(Node, [ + { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + ], + }, + ]) + }) + + it('Should not validate wrapped recursive node type with invalid id', () => { + const Node = Type.Tuple([ + Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ), + ]) + fail(Node, [ + { + id: 'A', + nodes: [ + { id: 1, nodes: [] }, + { id: 'C', nodes: [] }, + ], + }, + ]) + }) +}) diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts new file mode 100644 index 000000000..b816f895c --- /dev/null +++ b/test/runtime/compiler/ref.ts @@ -0,0 +1,70 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { Assert } from '../assert/index' + +describe('type/compiler/Ref', () => { + it('Should should validate when referencing a type', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: Assert.nextId() }, + ) + const R = Type.Ref(T) + ok( + R, + { + x: 1, + y: 2, + z: 3, + }, + [T], + ) + }) + + it('Should not validate when passing invalid data', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: Assert.nextId() }, + ) + const R = Type.Ref(T) + fail( + R, + { + x: 1, + y: 2, + }, + [T], + ) + }) + + it('Should de-reference object property schema', () => { + const R = Type.Object( + { + name: Type.String(), + }, + { $id: 'R' }, + ) + + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + r: Type.Optional(Type.Ref(R)), + }, + { $id: 'T' }, + ) + + ok(T, { x: 1, y: 2, z: 3 }, [R]) + ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) + fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) + fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) + }) +}) diff --git a/test/runtime/compiler/regex.ts b/test/runtime/compiler/regex.ts new file mode 100644 index 000000000..0213a8216 --- /dev/null +++ b/test/runtime/compiler/regex.ts @@ -0,0 +1,35 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/RegEx', () => { + it('Should validate numeric value', () => { + const T = Type.RegEx(/[012345]/) + ok(T, '0') + ok(T, '1') + ok(T, '2') + ok(T, '3') + ok(T, '4') + ok(T, '5') + }) + + it('Should validate true or false string value', () => { + const T = Type.RegEx(/true|false/) + ok(T, 'true') + ok(T, 'true') + ok(T, 'true') + ok(T, 'false') + ok(T, 'false') + ok(T, 'false') + fail(T, '6') + }) + + it('Should not validate failed regex test', () => { + const T = Type.RegEx(/true|false/) + fail(T, 'unknown') + }) + + it('Should pass numeric 5 digit test', () => { + const T = Type.RegEx(/[\d]{5}/) + ok(T, '12345') + }) +}) diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts new file mode 100644 index 000000000..56d27bc5a --- /dev/null +++ b/test/runtime/compiler/required.ts @@ -0,0 +1,57 @@ +import { Type, Modifier } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/compiler/compiler/Required', () => { + it('Should convert a partial object into a required object', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }, + { additionalProperties: false }, + ) + const T = Type.Required(A) + ok(T, { x: 1, y: 1, z: 1 }) + fail(T, { x: 1, y: 1 }) + fail(T, { x: 1 }) + fail(T, {}) + }) + + it('Should update modifier types correctly when converting to required', () => { + const A = Type.Object({ + x: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Number()), + z: Type.Optional(Type.Number()), + w: Type.Number(), + }) + const T = Type.Required(A) + strictEqual(T.properties.x[Modifier], 'Readonly') + strictEqual(T.properties.y[Modifier], 'Readonly') + strictEqual(T.properties.z[Modifier], undefined) + strictEqual(T.properties.w[Modifier], undefined) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }, + { additionalPropeties: false }, + ) + const T = Type.Required(A) + strictEqual(A.additionalPropeties, false) + strictEqual(T.additionalPropeties, false) + }) + + // it('Should construct new object when targetting reference', () => { + // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) + // const R = Type.Ref(T) + // const P = Type.Required(R) + // strictEqual(P.properties.a.type, 'string') + // strictEqual(P.properties.b.type, 'string') + // }) +}) diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts new file mode 100644 index 000000000..f90398e7b --- /dev/null +++ b/test/runtime/compiler/string.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/String', () => { + it('Should not validate number', () => { + const T = Type.String() + fail(T, 1) + }) + it('Should validate string', () => { + const T = Type.String() + ok(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.String() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.String() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.String() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.String() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.String() + fail(T, undefined) + }) + + it('Should validate string format as email', () => { + const T = Type.String({ format: 'email' }) + ok(T, 'name@domain.com') + }) + + it('Should validate string format as uuid', () => { + const T = Type.String({ format: 'uuid' }) + ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') + }) + + it('Should validate string format as iso8601 date', () => { + const T = Type.String({ format: 'date-time' }) + ok(T, '2021-06-11T20:30:00-04:00') + }) + + it('Should validate minLength', () => { + const T = Type.String({ minLength: 4 }) + ok(T, '....') + fail(T, '...') + }) + + it('Should validate maxLength', () => { + const T = Type.String({ maxLength: 4 }) + ok(T, '....') + fail(T, '.....') + }) + + it('Should pass numeric 5 digit test', () => { + const T = Type.String({ pattern: '[\\d]{5}' }) + ok(T, '12345') + }) +}) diff --git a/test/runtime/compiler/tuple.ts b/test/runtime/compiler/tuple.ts new file mode 100644 index 000000000..574410d8b --- /dev/null +++ b/test/runtime/compiler/tuple.ts @@ -0,0 +1,62 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { Assert } from '../assert' + +describe('type/compiler/Tuple', () => { + it('Should validate tuple of [string, number]', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Tuple([A, B]) + ok(T, ['hello', 42]) + }) + + it('Should not validate tuple of [string, number] when reversed', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Tuple([A, B]) + fail(T, [42, 'hello']) + }) + + it('Should validate with empty tuple', () => { + const T = Type.Tuple([]) + ok(T, []) + }) + + it('Should not validate with empty tuple with more items', () => { + const T = Type.Tuple([]) + fail(T, [1]) + }) + + it('Should not validate with empty tuple with less items', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + fail(T, [1]) + }) + + it('Should validate tuple of objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + ok(T, [{ a: 'hello' }, { b: 42 }]) + }) + + it('Should not validate tuple of objects when reversed', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ b: 42 }, { a: 'hello' }]) + }) + + it('Should not validate tuple when array is less than tuple length', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ a: 'hello' }]) + }) + + it('Should not validate tuple when array is greater than tuple length', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) + }) +}) diff --git a/test/runtime/compiler/uint8array.ts b/test/runtime/compiler/uint8array.ts new file mode 100644 index 000000000..9a552e53d --- /dev/null +++ b/test/runtime/compiler/uint8array.ts @@ -0,0 +1,56 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Uint8Array', () => { + it('Should not validate number', () => { + const T = Type.Uint8Array() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Uint8Array() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Uint8Array() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Uint8Array() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Uint8Array() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Uint8Array() + fail(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Uint8Array() + fail(T, undefined) + }) + + it('Should validate Uint8Array', () => { + const T = Type.Uint8Array() + ok(T, new Uint8Array(100)) + }) + + it('Should validate minByteLength', () => { + const T = Type.Uint8Array({ minByteLength: 4 }) + ok(T, new Uint8Array(4)) + fail(T, new Uint8Array(3)) + }) + + it('Should validate maxByteLength', () => { + const T = Type.Uint8Array({ maxByteLength: 4 }) + ok(T, new Uint8Array(4)) + fail(T, new Uint8Array(5)) + }) +}) diff --git a/test/runtime/compiler/union.ts b/test/runtime/compiler/union.ts new file mode 100644 index 000000000..fcf751910 --- /dev/null +++ b/test/runtime/compiler/union.ts @@ -0,0 +1,62 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Union', () => { + it('Should validate union of string, number and boolean', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Boolean() + const T = Type.Union([A, B, C]) + ok(T, 'hello') + ok(T, true) + ok(T, 42) + }) + + it('Should validate union of objects', () => { + const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) + const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello' }) + ok(T, { b: 'world' }) + }) + + it('Should fail to validate for descriminated union types', () => { + const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) + const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) + const T = Type.Union([A, B]) + fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } + fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } + }) + + it('Should validate union of objects where properties overlap', () => { + const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) + const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello' }) // A + ok(T, { a: 'hello', b: 'world' }) // B + }) + + it('Should validate union of overlapping property of varying type', () => { + const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello', b: 42 }) // A + ok(T, { a: 'hello', b: 'world' }) // B + }) + + it('Should validate union of literal strings', () => { + const A = Type.Literal('hello') + const B = Type.Literal('world') + const T = Type.Union([A, B]) + ok(T, 'hello') // A + ok(T, 'world') // B + }) + + it('Should not validate union of literal strings for unknown string', () => { + const A = Type.Literal('hello') + const B = Type.Literal('world') + const T = Type.Union([A, B]) + fail(T, 'foo') // A + fail(T, 'bar') // B + }) +}) diff --git a/test/runtime/compiler/unknown.ts b/test/runtime/compiler/unknown.ts new file mode 100644 index 000000000..7f3cb116d --- /dev/null +++ b/test/runtime/compiler/unknown.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Unknown', () => { + it('Should validate number', () => { + const T = Type.Any() + ok(T, 1) + }) + it('Should validate string', () => { + const T = Type.Any() + ok(T, 'hello') + }) + it('Should validate boolean', () => { + const T = Type.Any() + ok(T, true) + }) + it('Should validate array', () => { + const T = Type.Any() + ok(T, [1, 2, 3]) + }) + it('Should validate object', () => { + const T = Type.Any() + ok(T, { a: 1, b: 2 }) + }) + it('Should validate null', () => { + const T = Type.Any() + ok(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Any() + ok(T, undefined) + }) +}) diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts new file mode 100644 index 000000000..985184e4c --- /dev/null +++ b/test/runtime/compiler/validate.ts @@ -0,0 +1,107 @@ +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Value } from '@sinclair/typebox/value' +import { TSchema } from '@sinclair/typebox' +import { Format } from 'src/format' + +// ------------------------------------------------------------------------- +// Test Formats: https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts +// +// - date-time +// - email +// - uuid +// +// ------------------------------------------------------------------------- + +const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i +const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i +const DATE_TIME_SEPARATOR = /t|\s/i +const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + +function isLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) +} + +function isDate(str: string): boolean { + const matches: string[] | null = DATE.exec(str) + if (!matches) return false + const year: number = +matches[1] + const month: number = +matches[2] + const day: number = +matches[3] + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]) +} + +function isTime(str: string, strictTimeZone?: boolean): boolean { + const matches: string[] | null = TIME.exec(str) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tz: string | undefined = matches[4] + const tzSign: number = matches[5] === '-' ? -1 : 1 + const tzH: number = +(matches[6] || 0) + const tzM: number = +(matches[7] || 0) + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false + if (hr <= 23 && min <= 59 && sec < 60) return true + // leap second + const utcMin = min - tzM * tzSign + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 +} + +function isDateTime(str: string, strictTimeZone?: boolean): boolean { + const dateTime: string[] = str.split(DATE_TIME_SEPARATOR) + return dateTime.length === 2 && isDate(dateTime[0]) && isTime(dateTime[1], strictTimeZone) +} + +// ------------------------------------------------------------------------- +// Use Formats +// ------------------------------------------------------------------------- + +Format.Set('email', (value) => EMAIL.test(value)) +Format.Set('uuid', (value) => UUID.test(value)) +Format.Set('date-time', (value) => isDateTime(value, true)) + +export function ok(schema: T, data: unknown, references: any[] = []) { + const C = TypeCompiler.Compile(schema, references) + const result = C.Check(data) + if (result !== Value.Check(schema, references, data)) { + throw Error('Compiler and Value Check disparity') + } + if (!result) { + console.log('---------------------------') + console.log('type') + console.log('---------------------------') + console.log(JSON.stringify(schema, null, 2)) + console.log('---------------------------') + console.log('data') + console.log('---------------------------') + console.log(JSON.stringify(data, null, 2)) + console.log('---------------------------') + console.log('errors') + console.log('---------------------------') + console.log(result) + throw Error('expected ok') + } +} + +export function fail(schema: T, data: unknown, additional: any[] = []) { + const C = TypeCompiler.Compile(schema, additional) + const result = C.Check(data) + if (result) { + console.log('---------------------------') + console.log('type') + console.log('---------------------------') + console.log(JSON.stringify(schema, null, 2)) + console.log('---------------------------') + console.log('data') + console.log('---------------------------') + console.log(JSON.stringify(data, null, 2)) + console.log('---------------------------') + console.log('errors') + console.log('---------------------------') + console.log('none') + throw Error('expected ok') + } +} diff --git a/test/runtime/compiler/void.ts b/test/runtime/compiler/void.ts new file mode 100644 index 000000000..3beefc21d --- /dev/null +++ b/test/runtime/compiler/void.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Void', () => { + it('Should not validate number', () => { + const T = Type.Void() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Void() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Void() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Void() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Void() + fail(T, { a: 1, b: 2 }) + }) + + it('Should validate null', () => { + const T = Type.Null() + ok(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Void() + fail(T, undefined) + }) +}) diff --git a/test/runtime/conditional/any.ts b/test/runtime/conditional/any.ts new file mode 100644 index 000000000..a8a11d46a --- /dev/null +++ b/test/runtime/conditional/any.ts @@ -0,0 +1,92 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Any', () => { + it('Should extend Any', () => { + type T = any extends any ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Unknown', () => { + type T = any extends unknown ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = any extends string ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.String()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Boolean', () => { + type T = any extends boolean ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Number', () => { + type T = any extends number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Number()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Integer', () => { + type T = any extends number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Array 1', () => { + type T = any extends Array ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Array 2', () => { + type T = any extends Array ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Tuple', () => { + type T = any extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Object 1', () => { + type T = any extends object ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Object 2', () => { + type T = any extends {} ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Object 3', () => { + type T = any extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Union 1', () => { + type T = any extends number | string ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Union 2', () => { + type T = any extends any | number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Null', () => { + type T = any extends null ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Null()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Undefined', () => { + type T = any extends undefined ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.Union) + }) + it('Should extend Void', () => { + type T = any extends void ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Void()) + Assert.deepEqual(R, StructuralResult.Union) + }) +}) diff --git a/test/runtime/conditional/array.ts b/test/runtime/conditional/array.ts new file mode 100644 index 000000000..2f28914d0 --- /dev/null +++ b/test/runtime/conditional/array.ts @@ -0,0 +1,310 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Array', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + + it('Should extend Array Varying 1', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array Varying 2', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array Varying 3', () => { + type T = Array extends Array ? 1 : 2 // 1 + const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array Varying 4', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Number()), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ---------------------------------------------- + // Any + // ---------------------------------------------- + it('Should extend Any', () => { + type T = Array extends any ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = Array extends unknown ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = Array extends string ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = Array extends boolean ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = Array extends number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = Array extends number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array 2', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array 3', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Tuple', () => { + type T = Array extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = Array extends object ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = Array extends {} ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 3', () => { + type T = Array extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 4', () => { + type T = Array extends { length: '1' } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 5', () => { + type T = Array extends { length: number } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 1', () => { + type T = Array extends number | string ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = Array extends any | number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 4', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 5', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = Array extends null ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = Array extends undefined ? 1 : 2 + const R = Structural.Check(Type.Array(Type.Any()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ---------------------------------------------- + // Constrained + // ---------------------------------------------- + + it('Should extend constrained Any', () => { + type T = Array extends any ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Unknown', () => { + type T = Array extends unknown ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained String', () => { + type T = Array extends string ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Boolean', () => { + type T = Array extends boolean ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Number', () => { + type T = Array extends number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Integer', () => { + type T = Array extends number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Array 1', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Array 2', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Array 3', () => { + type T = Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Tuple', () => { + type T = Array extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Object 1', () => { + type T = Array extends object ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Object 2', () => { + type T = Array extends {} ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Object 3', () => { + type T = Array extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Object 4', () => { + type T = Array extends { length: '1' } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Object 5', () => { + type T = Array extends { length: number } ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 1', () => { + type T = Array extends number | string ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Union 2', () => { + type T = Array extends any | number ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 3', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 4', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 5', () => { + type T = Array extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Null', () => { + type T = Array extends null ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Undefined', () => { + type T = Array extends undefined ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Void', () => { + type T = Array extends void ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/boolean.ts b/test/runtime/conditional/boolean.ts new file mode 100644 index 000000000..df089789c --- /dev/null +++ b/test/runtime/conditional/boolean.ts @@ -0,0 +1,107 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Boolean', () => { + it('Should extend Any', () => { + type T = boolean extends any ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = boolean extends string ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = boolean extends boolean ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Number', () => { + type T = boolean extends number ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = boolean extends number ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = boolean extends Array ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = boolean extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = boolean extends Record ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = boolean extends {} ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = boolean extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = boolean extends object ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = boolean extends number | string ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = boolean extends any | number ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = boolean extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = boolean extends null ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = boolean extends undefined ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = boolean extends void ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/constructor.ts b/test/runtime/conditional/constructor.ts new file mode 100644 index 000000000..de607b660 --- /dev/null +++ b/test/runtime/conditional/constructor.ts @@ -0,0 +1,243 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Constructor', () => { + it('Should extend Function', () => { + type T = (new () => number) extends () => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Constructor 1', () => { + type T = (new () => number) extends new () => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 2', () => { + type T = (new () => any) extends new () => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 3', () => { + type T = (new () => number) extends new () => any ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 4', () => { + type T = (new (a: number) => number) extends new () => any ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Constructor 5', () => { + type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 6', () => { + type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Constructor 7', () => { + type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Constructor 8', () => { + type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 9', () => { + type T = (new () => number) extends new () => any ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 9', () => { + type T = (new () => any) extends new () => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 10', () => { + type T = (new () => Array) extends new () => object ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Constructor 11', () => { + type T = (new () => Array) extends new () => object ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 12', () => { + type T = (new () => object) extends new () => Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Constructor 13', () => { + type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 14', () => { + type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 + const R = Structural.Check(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 15', () => { + type T = (new () => any) extends new () => unknown ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Constructor 16', () => { + type T = (new () => unknown) extends new () => any ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Any', () => { + type T = (new () => number) extends any ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = (new () => number) extends string ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = (new () => number) extends boolean ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = (new () => number) extends number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = (new () => number) extends number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 2', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 3', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = (new () => number) extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = (() => number) extends Record ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = (new () => number) extends object ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = (new () => number) extends {} ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 3', () => { + type T = (new () => number) extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 4', () => { + type T = (new () => number) extends { length: '1' } ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = (new () => number) extends number | string ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = (new () => number) extends any | number ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 4', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 5', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = (new () => number) extends null ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = (new () => number) extends undefined ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = (new () => number) extends void ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/function.ts b/test/runtime/conditional/function.ts new file mode 100644 index 000000000..2729aa40b --- /dev/null +++ b/test/runtime/conditional/function.ts @@ -0,0 +1,245 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Function', () => { + it('Should extend Constructor 1', () => { + type T = (() => number) extends new () => number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Function 1', () => { + type T = (() => number) extends () => number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Function([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 2', () => { + type T = (() => any) extends () => number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 3', () => { + type T = (() => number) extends () => any ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 4', () => { + type T = ((a: number) => number) extends () => any ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Function 5', () => { + type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 6', () => { + type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Function 7', () => { + type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Function 8', () => { + type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 9', () => { + type T = (() => number) extends () => any ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Function([], Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 9', () => { + type T = (() => any) extends () => number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 10', () => { + type T = (() => Array) extends () => object ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 11', () => { + type T = (() => Array) extends () => object ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 12', () => { + type T = (() => object) extends () => Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Function 13', () => { + type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 14', () => { + type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 + const R = Structural.Check(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 15', () => { + type T = (() => any) extends () => unknown ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Function 16', () => { + type T = (() => unknown) extends () => any ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Any', () => { + type T = (() => number) extends any ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = (() => number) extends string ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = (() => number) extends boolean ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = (() => number) extends number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = (() => number) extends number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 2', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 3', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = (() => number) extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = (() => number) extends Record ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = (() => number) extends object ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = (() => number) extends {} ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 3', () => { + type T = (() => number) extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 4', () => { + type T = (() => number) extends { length: '1' } ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 5', () => { + type T = (() => number) extends { length: number } ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 1', () => { + type T = (() => number) extends number | string ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = (() => number) extends any | number ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 4', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 5', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = (() => number) extends null ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = (() => number) extends undefined ? 1 : 2 + const R = Structural.Check(Type.Function([], Type.Number()), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/index.ts b/test/runtime/conditional/index.ts new file mode 100644 index 000000000..0686b6d9d --- /dev/null +++ b/test/runtime/conditional/index.ts @@ -0,0 +1,19 @@ +import './any' +import './array' +import './boolean' +import './constructor' +import './function' +import './integer' +import './literal' +import './null' +import './number' +import './object' +import './promise' +import './record' +import './string' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/conditional/integer.ts b/test/runtime/conditional/integer.ts new file mode 100644 index 000000000..3c384c776 --- /dev/null +++ b/test/runtime/conditional/integer.ts @@ -0,0 +1,112 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Integer', () => { + it('Should extend Any', () => { + type T = number extends any ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = number extends unknown ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = number extends string ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = number extends boolean ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = number extends number ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Number()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Integer', () => { + type T = number extends number ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array', () => { + type T = number extends Array ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = number extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = number extends Record ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = number extends {} ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = number extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = number extends object ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Void', () => { + type T = number extends undefined ? 1 : 2 + const R = Structural.Check(Type.Integer(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/literal.ts b/test/runtime/conditional/literal.ts new file mode 100644 index 000000000..994597c2f --- /dev/null +++ b/test/runtime/conditional/literal.ts @@ -0,0 +1,324 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Literal', () => { + // ------------------------------------------------------------------- + // String Literal + // ------------------------------------------------------------------- + it('Should extend Any (String)', () => { + type T = 'hello' extends any ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown (String)', () => { + type T = 'hello' extends unknown ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String (String)', () => { + type T = 'hello' extends string ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.String()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Boolean (String)', () => { + type T = 'hello' extends boolean ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number (String)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer (String)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array (String)', () => { + type T = 'hello' extends Array ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple (String)', () => { + type T = 'hello' extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Object 1 (String)', () => { + type T = 'hello' extends {} ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2 (String)', () => { + type T = 'hello' extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3 (String)', () => { + type T = 'hello' extends object ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1 (String)', () => { + type T = 'hello' extends number | string ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2 (String)', () => { + type T = 'hello' extends any | number ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3 (String)', () => { + type T = 'hello' extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null (String)', () => { + type T = 'hello' extends null ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined (String)', () => { + type T = 'hello' extends undefined ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ------------------------------------------------------------------- + // Number Literal + // ------------------------------------------------------------------- + + it('Should extend Any (Number)', () => { + type T = 10 extends any ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown (Number)', () => { + type T = 10 extends unknown ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String (Number)', () => { + type T = 10 extends string ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean (Number)', () => { + type T = 10 extends boolean ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number (Number)', () => { + type T = 10 extends number ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Number()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Integer (Number)', () => { + type T = 10 extends number ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Integer()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array (Number)', () => { + type T = 10 extends Array ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple (Number)', () => { + type T = 10 extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Object 1 (Number)', () => { + type T = 10 extends {} ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2 (Number)', () => { + type T = 10 extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3 (Number)', () => { + type T = 10 extends object ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1 (Number)', () => { + type T = 10 extends number | string ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2 (Number)', () => { + type T = 10 extends any | number ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3 (Number)', () => { + type T = 10 extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null (Number)', () => { + type T = 10 extends null ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined (Number)', () => { + type T = 10 extends undefined ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ------------------------------------------------------------------- + // Boolean Literal + // ------------------------------------------------------------------- + + it('Should extend Any (Boolean)', () => { + type T = true extends any ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown (Boolean)', () => { + type T = true extends unknown ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String (Boolean)', () => { + type T = true extends string ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean (Boolean)', () => { + type T = true extends boolean ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Number (Boolean)', () => { + type T = true extends number ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer (Boolean)', () => { + type T = true extends number ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array (Boolean)', () => { + type T = true extends Array ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple (Boolean)', () => { + type T = true extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 1', () => { + type T = 'hello' extends Record ? 1 : 2 + const R = Structural.Check(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 2', () => { + type T = 10 extends Record ? 1 : 2 + const R = Structural.Check(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 3', () => { + type T = true extends Record ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1 (Boolean)', () => { + type T = true extends {} ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2 (Boolean)', () => { + type T = true extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3 (Boolean)', () => { + type T = true extends object ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1 (Boolean)', () => { + type T = true extends number | string ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2 (Boolean)', () => { + type T = true extends any | number ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3 (Boolean)', () => { + type T = true extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null (Boolean)', () => { + type T = true extends null ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined (Boolean)', () => { + type T = true extends undefined ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = true extends void ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/null.ts b/test/runtime/conditional/null.ts new file mode 100644 index 000000000..d8b91e504 --- /dev/null +++ b/test/runtime/conditional/null.ts @@ -0,0 +1,113 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Null', () => { + it('Should extend Any', () => { + type T = null extends any ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = null extends unknown ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = null extends string ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = null extends boolean ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = null extends number ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = null extends number ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = null extends Array ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = null extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = null extends Record ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = null extends {} ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 2', () => { + type T = null extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = null extends object ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = null extends number | string ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = null extends any | number ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = null extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = null extends null ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Null()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Undefined', () => { + type T = null extends undefined ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = null extends void ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/number.ts b/test/runtime/conditional/number.ts new file mode 100644 index 000000000..35e6f7c68 --- /dev/null +++ b/test/runtime/conditional/number.ts @@ -0,0 +1,111 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Number', () => { + it('Should extend Any', () => { + type T = number extends any ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Unknown', () => { + type T = number extends unknown ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend String', () => { + type T = number extends string ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = number extends boolean ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = number extends number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Number()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Integer', () => { + type T = number extends number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array', () => { + type T = number extends Array ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = number extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = number extends Record ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = number extends {} ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = number extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = number extends object ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = number extends void ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/object.ts b/test/runtime/conditional/object.ts new file mode 100644 index 000000000..3452661ef --- /dev/null +++ b/test/runtime/conditional/object.ts @@ -0,0 +1,155 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Object', () => { + // ---------------------------------------------------------- + // Record + // ---------------------------------------------------------- + + it('Should extend Record 1', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 2', () => { + type T = { a: number; b: number } extends Record ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.String(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 3', () => { + type T = { a: number; b: number } extends Record ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Number(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 4', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 5', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.String(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 6', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Number(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + // ---------------------------------------------------------- + // Standard + // ---------------------------------------------------------- + + it('Should extend Any', () => { + type T = { a: number } extends any ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = { a: number } extends string ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = { a: number } extends boolean ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = { a: number } extends number ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = { a: number } extends number ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = { a: number } extends Array ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple', () => { + type T = { a: number } extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Object 1', () => { + type T = { a: number } extends {} ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = { a: number } extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = { a: number } extends object ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 1', () => { + type T = { a: number } extends number | string ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = { a: number } extends any | number ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = { a: number } extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = { a: number } extends null ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = { a: number } extends undefined ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = { a: number } extends void ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/promise.ts b/test/runtime/conditional/promise.ts new file mode 100644 index 000000000..77de0358d --- /dev/null +++ b/test/runtime/conditional/promise.ts @@ -0,0 +1,245 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Promise', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + + it('Should extend Promise Varying 1', () => { + type T = Promise extends Promise ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Promise(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Promise Varying 2', () => { + type T = Promise extends Promise ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.String()), Type.Promise(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Promise Varying 3', () => { + type T = Promise extends Promise ? 1 : 2 // 1 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Promise(Type.String())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Promise Varying 4', () => { + type T = Promise extends Promise ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Promise(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ---------------------------------------------- + // Any + // ---------------------------------------------- + + it('Should extend Any', () => { + type T = Promise extends any ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = Promise extends unknown ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = Promise extends string ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = Promise extends boolean ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = Promise extends number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = Promise extends number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = Promise extends Array ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = Promise extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = Promise extends Record ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = Promise extends {} ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = Promise extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = Promise extends object ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 1', () => { + type T = Promise extends number | string ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = Promise extends any | number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = Promise extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = Promise extends null ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = Promise extends undefined ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + // ---------------------------------------------- + // Constrained + // ---------------------------------------------- + + it('Should extend constrained Any', () => { + type T = Promise extends any ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Unknown', () => { + type T = Promise extends unknown ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained String', () => { + type T = Promise extends string ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Boolean', () => { + type T = Promise extends boolean ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Number', () => { + type T = Promise extends number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Any()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Integer', () => { + type T = Promise extends number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Array', () => { + type T = Promise extends Array ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Tuple', () => { + type T = Promise extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Object 1', () => { + type T = Promise extends {} ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Object 2', () => { + type T = Promise extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Object 3', () => { + type T = Promise extends object ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 1', () => { + type T = Promise extends number | string ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Union 2', () => { + type T = Promise extends any | number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend constrained Union 2', () => { + type T = Promise extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Null', () => { + type T = Promise extends null ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend constrained Undefined', () => { + type T = Promise extends undefined ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = Promise extends void ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/record.ts b/test/runtime/conditional/record.ts new file mode 100644 index 000000000..4e4e0cb7c --- /dev/null +++ b/test/runtime/conditional/record.ts @@ -0,0 +1,182 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Record', () => { + it('Should extend Record 1', () => { + type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 + const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const B = Type.Object({ a: Type.Number(), b: Type.Number() }) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 2', () => { + type T = Record extends { a: number; b: number } ? 1 : 2 + const A = Type.Record(Type.String(), Type.Number()) + const B = Type.Object({ a: Type.Number(), b: Type.Number() }) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 3', () => { + type T = Record extends { a: number; b: number } ? 1 : 2 + const A = Type.Record(Type.Number(), Type.Number()) + const B = Type.Object({ a: Type.Number(), b: Type.Number() }) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 4', () => { + type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 + const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const B = Type.Object({ a: Type.Number(), b: Type.Number() }) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 5', () => { + type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 + const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const B = Type.Object({ a: Type.Number(), b: Type.Number() }) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 6', () => { + type T = Record extends Record<'a' | 'b', number> ? true : false + const A = Type.Record(Type.String(), Type.Number()) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 7', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.String(), Type.Number()) + const B = Type.Record(Type.String(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Record 8', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.String(), Type.Number()) + const B = Type.Record(Type.Number(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Record 9', () => { + type T = Record extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Record(Type.Number(), Type.Number()) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Record 10', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.Number(), Type.Number()) + const B = Type.Record(Type.String(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Record 11', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.Number(), Type.Number()) + const B = Type.Record(Type.Number(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + // ------------------------------------------------------------------- + // Standard + // ------------------------------------------------------------------- + + it('Should extend Any', () => { + type T = Record extends any ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = Record extends unknown ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = Record extends string ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Boolean', () => { + type T = Record extends boolean ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Number', () => { + type T = Record extends number ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Integer', () => { + type T = Record extends number ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Array 1', () => { + type T = Record extends Array ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Array 2', () => { + type T = Record extends Array ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple', () => { + type T = Record extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Object 1', () => { + type T = Record extends object ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Object 2', () => { + type T = Record extends {} ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Object 3', () => { + type T = Record extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Union 1', () => { + type T = Record extends number | string ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Union 2', () => { + type T = Record extends any | number ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Null', () => { + type T = Record extends null ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Undefined', () => { + type T = Record extends undefined ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = Record extends void ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/string.ts b/test/runtime/conditional/string.ts new file mode 100644 index 000000000..42c02aa1e --- /dev/null +++ b/test/runtime/conditional/string.ts @@ -0,0 +1,143 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/String', () => { + it('Should extend Any', () => { + type T = string extends any ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = string extends unknown ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = string extends string ? 1 : 2 + const R = Structural.Check(Type.String(), Type.String()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Boolean', () => { + type T = string extends boolean ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = string extends number ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = string extends number ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = string extends Array ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = string extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 1', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 2', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 3', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 4', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 5', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Union([Type.Number(), Type.String()]))) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 6', () => { + type T = string extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = string extends {} ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = string extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = string extends object ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = number extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = number extends void ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/tuple.ts b/test/runtime/conditional/tuple.ts new file mode 100644 index 000000000..6c2d025e2 --- /dev/null +++ b/test/runtime/conditional/tuple.ts @@ -0,0 +1,196 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Tuple', () => { + it('Should extend Any', () => { + type T = [string, number] extends any ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = [string, number] extends string ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = [string, number] extends boolean ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = [string, number] extends number ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = [string, number] extends number ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Array 2', () => { + type T = [string, number] extends Array ? 1 : 2 // 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 3', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Array 4', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Tuple 1', () => { + type T = [string, number] extends [string, number] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Tuple 2', () => { + type T = [string, number] extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple 3', () => { + type T = [string, any] extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple 4', () => { + type T = [string, number] extends [string, any] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Tuple 5', () => { + type T = [string, unknown] extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple 6', () => { + type T = [string, number] extends [string, unknown] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Tuple 7', () => { + type T = [] extends [string, number] ? 1 : 2 + const R = Structural.Check(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple 8', () => { + type T = [string, number] extends [] ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 1', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 2', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 3', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 4', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 5', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Union([Type.Number(), Type.String()]))) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record 6', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = Structural.Check(Type.String(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = [string, number] extends {} ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = [string, number] extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = [string, number] extends object ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 1', () => { + type T = [string, number] extends number | string ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = [string, number] extends any | number ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = [string, number] extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = [string, number] extends null ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = [string, number] extends undefined ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = [string, number] extends void ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/uint8array.ts b/test/runtime/conditional/uint8array.ts new file mode 100644 index 000000000..ddfef9031 --- /dev/null +++ b/test/runtime/conditional/uint8array.ts @@ -0,0 +1,112 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Uint8Array', () => { + it('Should extend Any', () => { + type T = Uint8Array extends any ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = Uint8Array extends unknown ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = Uint8Array extends string ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = Uint8Array extends boolean ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = Uint8Array extends number ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = Uint8Array extends number ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = Uint8Array extends Array ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple', () => { + type T = Uint8Array extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = Uint8Array extends Record ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 1', () => { + type T = Uint8Array extends {} ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = Uint8Array extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = Uint8Array extends object ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = Uint8Array extends number | string ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = Uint8Array extends any | number ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = Uint8Array extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = Uint8Array extends null ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = Uint8Array extends undefined ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = Uint8Array extends void ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/undefined.ts b/test/runtime/conditional/undefined.ts new file mode 100644 index 000000000..3bacbf798 --- /dev/null +++ b/test/runtime/conditional/undefined.ts @@ -0,0 +1,100 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Undefined', () => { + it('Should extend Any', () => { + type T = undefined extends any ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = undefined extends string ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = undefined extends boolean ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = undefined extends number ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = undefined extends number ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = undefined extends Array ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = undefined extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = undefined extends {} ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 2', () => { + type T = undefined extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = undefined extends object ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = undefined extends number | string ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = undefined extends any | number ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = undefined extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = undefined extends null ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = undefined extends undefined ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Void', () => { + type T = undefined extends void ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Void()) + Assert.deepEqual(R, StructuralResult.True) + }) +}) diff --git a/test/runtime/conditional/union.ts b/test/runtime/conditional/union.ts new file mode 100644 index 000000000..26e06a812 --- /dev/null +++ b/test/runtime/conditional/union.ts @@ -0,0 +1,142 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Union', () => { + it('Should extend Any', () => { + type T = number | string extends any ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = number | string extends string ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = number | string extends boolean ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = number | string extends number ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = number | string extends number ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = number | string extends Array ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple', () => { + type T = number | string extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = number | string extends {} ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = number | string extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = number | string extends object ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = number | string extends number | string ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 2', () => { + type T = number | string extends any | number ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = number | string extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 4', () => { + type T = any | boolean extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + + it('Should extend Union 5', () => { + type T = any | string extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + + it('Should extend Union 6', () => { + type T = any | {} extends {} ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + + it('Should extend Union 7', () => { + type T = any extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.Union) + }) + + it('Should extend Union 8', () => { + type T = unknown | string extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 9', () => { + type T = unknown extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = number | string extends null ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = number | string extends undefined ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = number | string extends void ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void 2', () => { + type T = number | string | void extends void ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/unknown.ts b/test/runtime/conditional/unknown.ts new file mode 100644 index 000000000..8025a6b77 --- /dev/null +++ b/test/runtime/conditional/unknown.ts @@ -0,0 +1,119 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Unknown', () => { + it('Should extend Any', () => { + type T = unknown extends any ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = unknown extends unknown ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = unknown extends string ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = unknown extends boolean ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = unknown extends number ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = unknown extends number ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = unknown extends Array ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 2', () => { + type T = unknown extends Array ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = unknown extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = unknown extends object ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 2', () => { + type T = unknown extends {} ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = unknown extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = unknown extends number | string ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = unknown extends any | number ? 1 : 2 // 1 + const R = Structural.Check(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = unknown extends unknown | number ? 1 : 2 // 1 + const R = Structural.Check(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 4', () => { + type T = unknown extends unknown | any ? 1 : 2 // 1 + const R = Structural.Check(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = unknown extends null ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = unknown extends undefined ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = unknown extends void ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) +}) diff --git a/test/runtime/conditional/void.ts b/test/runtime/conditional/void.ts new file mode 100644 index 000000000..289f3dfdc --- /dev/null +++ b/test/runtime/conditional/void.ts @@ -0,0 +1,119 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Void', () => { + it('Should extend Any', () => { + type T = void extends any ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = void extends unknown ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = void extends string ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = void extends boolean ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = void extends number ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = void extends number ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 1', () => { + type T = void extends Array ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array 2', () => { + type T = void extends Array ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Array(Type.String())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Tuple', () => { + type T = void extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = void extends object ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 2', () => { + type T = void extends {} ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = void extends { a: number } ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = void extends number | string ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = void extends any | number ? 1 : 2 // 1 + const R = Structural.Check(Type.Void(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = void extends unknown | number ? 1 : 2 // 1 + const R = Structural.Check(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 4', () => { + type T = void extends unknown | any ? 1 : 2 // 1 + const R = Structural.Check(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Null', () => { + type T = void extends null ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = void extends undefined ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = void extends void ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Void()) + Assert.deepEqual(R, StructuralResult.True) + }) +}) diff --git a/test/runtime/format/format.ts b/test/runtime/format/format.ts new file mode 100644 index 000000000..e867d5a41 --- /dev/null +++ b/test/runtime/format/format.ts @@ -0,0 +1,25 @@ +import { Format } from '@sinclair/typebox/format' +import { Assert } from '../assert/index' + +describe('format/Format', () => { + it('Should set format', () => { + Format.Set('test#format1', () => true) + }) + + it('Should get format', () => { + Format.Set('test#format2', () => true) + const format = Format.Get('test#format2') + Assert.equal(typeof format, 'function') + }) + + it('Should return true if exists', () => { + Format.Set('test#format3', () => true) + Assert.equal(Format.Has('test#format3'), true) + }) + + it('Should clear formats', () => { + Format.Set('test#format4', () => true) + Format.Clear() + Assert.equal(Format.Has('test#format4'), false) + }) +}) diff --git a/test/runtime/format/index.ts b/test/runtime/format/index.ts new file mode 100644 index 000000000..78c71b23b --- /dev/null +++ b/test/runtime/format/index.ts @@ -0,0 +1 @@ +import './format' diff --git a/test/runtime/guard/any.ts b/test/runtime/guard/any.ts new file mode 100644 index 000000000..e2b5a7b74 --- /dev/null +++ b/test/runtime/guard/any.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TAny', () => { + it('should guard for TAny', () => { + const R = TypeGuard.TAny(Type.Any()) + Assert.equal(R, true) + }) + it('should not guard for TAny', () => { + const R = TypeGuard.TAny(null) + Assert.equal(R, false) + }) + it('should not guard for TAny with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TAny(Type.Any({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/array.ts b/test/runtime/guard/array.ts new file mode 100644 index 000000000..088f39653 --- /dev/null +++ b/test/runtime/guard/array.ts @@ -0,0 +1,63 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TArray', () => { + it('should guard for TArray', () => { + const R = TypeGuard.TArray(Type.Array(Type.Number())) + Assert.equal(R, true) + }) + + it('should not guard for TArray', () => { + const R = TypeGuard.TArray(null) + Assert.equal(R, false) + }) + + it('should guard for nested object TArray', () => { + const R = TypeGuard.TArray( + Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + ) + Assert.equal(R, true) + }) + + it('should not guard for nested object TArray', () => { + const R = TypeGuard.TArray( + Type.Array( + Type.Object({ + x: Type.Number(), + y: {} as any, + }), + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TArray with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TArray(Type.Array(Type.Number(), { $id: 1 })) + Assert.equal(R, false) + }) + + it('should not guard for TArray with invalid minItems', () => { + // @ts-ignore + const R = TypeGuard.TArray(Type.Array(Type.String(), { minItems: '1' })) + Assert.equal(R, false) + }) + + it('should not guard for TArray with invalid maxItems', () => { + // @ts-ignore + const R = TypeGuard.TArray(Type.Array(Type.String(), { maxItems: '1' })) + Assert.equal(R, false) + }) + + it('should not guard for TArray with invalid uniqueItems', () => { + // @ts-ignore + const R = TypeGuard.TArray(Type.Array(Type.String(), { uniqueItems: '1' })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/boolean.ts b/test/runtime/guard/boolean.ts new file mode 100644 index 000000000..52c94c877 --- /dev/null +++ b/test/runtime/guard/boolean.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TBoolean', () => { + it('should guard for TBoolean', () => { + const R = TypeGuard.TBoolean(Type.Boolean()) + Assert.equal(R, true) + }) + it('should not guard for TBoolean', () => { + const R = TypeGuard.TBoolean(null) + Assert.equal(R, false) + }) + it('should not guard for TBoolean with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TBoolean(Type.Boolean({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/constructor.ts b/test/runtime/guard/constructor.ts new file mode 100644 index 000000000..e584c0609 --- /dev/null +++ b/test/runtime/guard/constructor.ts @@ -0,0 +1,35 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TConstructor', () => { + it('should guard for TConstructor', () => { + const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number())) + Assert.equal(R, true) + }) + it('should not guard for TConstructor', () => { + const R = TypeGuard.TConstructor(null) + Assert.equal(R, false) + }) + it('should not guard for TConstructor with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) + Assert.equal(R, false) + }) + it('should not guard for TConstructor with invalid Params', () => { + const R = TypeGuard.TConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) + Assert.equal(R, false) + }) + it('should not guard for TConstructor with invalid Return', () => { + const R = TypeGuard.TConstructor(Type.Constructor([], {} as any)) + Assert.equal(R, false) + }) + it('should guard for TConstructor with empty TTuple', () => { + const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([]), Type.Number())) + Assert.equal(R, true) + }) + it('should guard for TConstructor with array TTuple', () => { + const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) + Assert.equal(R, true) + }) +}) diff --git a/test/runtime/guard/function.ts b/test/runtime/guard/function.ts new file mode 100644 index 000000000..3cff158a8 --- /dev/null +++ b/test/runtime/guard/function.ts @@ -0,0 +1,35 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TFunction', () => { + it('should guard for TFunction', () => { + const R = TypeGuard.TFunction(Type.Function([], Type.Number())) + Assert.equal(R, true) + }) + it('should not guard for TFunction', () => { + const R = TypeGuard.TFunction(null) + Assert.equal(R, false) + }) + it('should not guard for TFunction with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TFunction(Type.Function([], Type.Number(), { $id: 1 })) + Assert.equal(R, false) + }) + it('should not guard for TFunction with invalid Params', () => { + const R = TypeGuard.TFunction(Type.Function([{} as any, {} as any], Type.Number())) + Assert.equal(R, false) + }) + it('should not guard for TFunction with invalid Return', () => { + const R = TypeGuard.TFunction(Type.Function([], {} as any)) + Assert.equal(R, false) + }) + it('should guard for TFunction with empty TTuple', () => { + const R = TypeGuard.TFunction(Type.Function(Type.Tuple([]), Type.Number())) + Assert.equal(R, true) + }) + it('should guard for TFunction with array TTuple', () => { + const R = TypeGuard.TFunction(Type.Function(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) + Assert.equal(R, true) + }) +}) diff --git a/test/runtime/guard/index.ts b/test/runtime/guard/index.ts new file mode 100644 index 000000000..804c4865c --- /dev/null +++ b/test/runtime/guard/index.ts @@ -0,0 +1,21 @@ +import './any' +import './array' +import './boolean' +import './constructor' +import './function' +import './integer' +import './literal' +import './null' +import './number' +import './object' +import './promise' +import './record' +import './ref' +import './self' +import './string' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/guard/integer.ts b/test/runtime/guard/integer.ts new file mode 100644 index 000000000..d53536b6e --- /dev/null +++ b/test/runtime/guard/integer.ts @@ -0,0 +1,44 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TInteger', () => { + it('should guard for TInteger', () => { + const R = TypeGuard.TInteger(Type.Integer()) + Assert.equal(R, true) + }) + it('should not guard for TInteger', () => { + const R = TypeGuard.TInteger(null) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ $id: 1 })) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid multipleOf', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ multipleOf: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid minimum', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ minimum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid maximum', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ maximum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid exclusiveMinimum', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ exclusiveMinimum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TInteger with invalid exclusiveMaximum', () => { + // @ts-ignore + const R = TypeGuard.TInteger(Type.Integer({ exclusiveMaximum: '1' })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/literal.ts b/test/runtime/guard/literal.ts new file mode 100644 index 000000000..efe372437 --- /dev/null +++ b/test/runtime/guard/literal.ts @@ -0,0 +1,32 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TLiteral', () => { + it('should guard for TLiteral of String', () => { + const R = TypeGuard.TLiteral(Type.Literal('hello')) + Assert.equal(R, true) + }) + it('should guard for TLiteral of Number', () => { + const R = TypeGuard.TLiteral(Type.Literal(42)) + Assert.equal(R, true) + }) + it('should guard for TLiteral of Boolean', () => { + const R = TypeGuard.TLiteral(Type.Literal(true)) + Assert.equal(R, true) + }) + it('should not guard for TLiteral of Null', () => { + // @ts-ignore + const R = TypeGuard.TLiteral(Type.Literal(null)) + Assert.equal(R, false) + }) + it('should not guard for TLiteral', () => { + const R = TypeGuard.TLiteral(null) + Assert.equal(R, false) + }) + it('should not guard for TLiteral with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TLiteral(Type.Literal(42, { $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/null.ts b/test/runtime/guard/null.ts new file mode 100644 index 000000000..36f8df40b --- /dev/null +++ b/test/runtime/guard/null.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TNull', () => { + it('should guard for TNull', () => { + const R = TypeGuard.TNull(Type.Null()) + Assert.equal(R, true) + }) + it('should not guard for TNull', () => { + const R = TypeGuard.TNull(null) + Assert.equal(R, false) + }) + it('should not guard for TNull with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TNull(Type.Null({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/number.ts b/test/runtime/guard/number.ts new file mode 100644 index 000000000..9ea80b538 --- /dev/null +++ b/test/runtime/guard/number.ts @@ -0,0 +1,44 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TNumber', () => { + it('should guard for TNumber', () => { + const R = TypeGuard.TNumber(Type.Number()) + Assert.equal(R, true) + }) + it('should not guard for TNumber', () => { + const R = TypeGuard.TNumber(null) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ $id: 1 })) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid multipleOf', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ multipleOf: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid minimum', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ minimum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid maximum', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ maximum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid exclusiveMinimum', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ exclusiveMinimum: '1' })) + Assert.equal(R, false) + }) + it('should not guard for TNumber with invalid exclusiveMaximum', () => { + // @ts-ignore + const R = TypeGuard.TNumber(Type.Number({ exclusiveMaximum: '1' })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/object.ts b/test/runtime/guard/object.ts new file mode 100644 index 000000000..a4503e2c5 --- /dev/null +++ b/test/runtime/guard/object.ts @@ -0,0 +1,99 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TObject', () => { + it('should guard for TObject', () => { + const R = TypeGuard.TObject( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + Assert.equal(R, true) + }) + + it('should not guard for TObject', () => { + const R = TypeGuard.TObject(null) + Assert.equal(R, false) + }) + + it('should not guard for TObject with escape characters in property key', () => { + const R = TypeGuard.TObject( + Type.Object({ + 'hello\nworld': Type.Number(), + }), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with invalid property values', () => { + const R = TypeGuard.TObject( + Type.Object({ + x: Type.Number(), + y: {} as any, + }), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with invalid additionalProperties', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + }, + { + // @ts-ignore + additionalProperties: 'true', + }, + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with invalid $id', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + }, + { + // @ts-ignore + $id: 1, + }, + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with invalid minProperties', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + }, + { + // @ts-ignore + minProperties: '1', + }, + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with invalid maxProperties', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + }, + { + // @ts-ignore + maxProperties: '1', + }, + ), + ) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/promise.ts b/test/runtime/guard/promise.ts new file mode 100644 index 000000000..0610bc319 --- /dev/null +++ b/test/runtime/guard/promise.ts @@ -0,0 +1,45 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TPromise', () => { + it('should guard for TPromise', () => { + const R = TypeGuard.TPromise(Type.Promise(Type.Number())) + Assert.equal(R, true) + }) + + it('should not guard for TPromise', () => { + const R = TypeGuard.TPromise(null) + Assert.equal(R, false) + }) + + it('should not guard for TPromise with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TPromise(Type.Promise(Type.Number(), { $id: 1 })) + Assert.equal(R, false) + }) + + it('should guard for TPromise with nested TObject', () => { + const R = TypeGuard.TPromise( + Type.Promise( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + ) + Assert.equal(R, true) + }) + + it('should not guard for TPromise with nested TObject', () => { + const R = TypeGuard.TPromise( + Type.Promise( + Type.Object({ + x: Type.Number(), + y: {} as any, + }), + ), + ) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/record.ts b/test/runtime/guard/record.ts new file mode 100644 index 000000000..718f56e65 --- /dev/null +++ b/test/runtime/guard/record.ts @@ -0,0 +1,53 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TRecord', () => { + it('should guard for TRecord', () => { + const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) + Assert.equal(R, true) + }) + + it('should guard for TRecord with TObject value', () => { + const R = TypeGuard.TRecord( + Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + ) + Assert.equal(R, true) + }) + + it('should not guard for TRecord', () => { + const R = TypeGuard.TRecord(null) + Assert.equal(R, false) + }) + + it('should not guard for TRecord with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) + Assert.equal(R, false) + }) + + it('should not guard for TRecord with TObject value with invalid Property', () => { + const R = TypeGuard.TRecord( + Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: {} as any, + }), + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TRecord with invalid literal key', () => { + const K = Type.Union([Type.Literal('hello\nworld')]) + const R = TypeGuard.TRecord(Type.Record(K, Type.Number())) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/ref.ts b/test/runtime/guard/ref.ts new file mode 100644 index 000000000..ecc260cb6 --- /dev/null +++ b/test/runtime/guard/ref.ts @@ -0,0 +1,25 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TRef', () => { + it('should guard for TRef', () => { + const T = Type.Number({ $id: 'T' }) + const R = TypeGuard.TRef(Type.Ref(T)) + Assert.equal(R, true) + }) + + it('should not guard for TRef', () => { + const R = TypeGuard.TRef(null) + Assert.equal(R, false) + }) + + it('should not guard for TRef with invalid $ref', () => { + const T = Type.Number({ $id: 'T' }) + const S = Type.Ref(T) + // @ts-ignore + S.$ref = 1 + const R = TypeGuard.TRef(S) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/self.ts b/test/runtime/guard/self.ts new file mode 100644 index 000000000..8777789de --- /dev/null +++ b/test/runtime/guard/self.ts @@ -0,0 +1,22 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TSelf', () => { + it('should guard for TSelf', () => { + Type.Recursive((Node) => { + const R = TypeGuard.TSelf(Node) + Assert.equal(R, true) + return Type.Object({ nodes: Type.Array(Node) }) + }) + }) + it('should guard for TSelf with invalid $ref', () => { + Type.Recursive((Node) => { + // @ts-ignore + Node.$ref = 1 + const R = TypeGuard.TSelf(Node) + Assert.equal(R, false) + return Type.Object({ nodes: Type.Array(Node) }) + }) + }) +}) diff --git a/test/runtime/guard/string.ts b/test/runtime/guard/string.ts new file mode 100644 index 000000000..598b9bf15 --- /dev/null +++ b/test/runtime/guard/string.ts @@ -0,0 +1,39 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TString', () => { + it('should guard for TString', () => { + const R = TypeGuard.TString(Type.String()) + Assert.equal(R, true) + }) + + it('should not guard for TString', () => { + const R = TypeGuard.TString(null) + Assert.equal(R, false) + }) + + it('should not guard for TString with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TString(Type.String({ $id: 1 })) + Assert.equal(R, false) + }) + + it('should not guard for TString with invalid minLength', () => { + // @ts-ignore + const R = TypeGuard.TString(Type.String({ minLength: '1' })) + Assert.equal(R, false) + }) + + it('should not guard for TString with invalid maxLength', () => { + // @ts-ignore + const R = TypeGuard.TString(Type.String({ maxLength: '1' })) + Assert.equal(R, false) + }) + + it('should not guard for TString with invalid pattern', () => { + // @ts-ignore + const R = TypeGuard.TString(Type.String({ pattern: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/tuple.ts b/test/runtime/guard/tuple.ts new file mode 100644 index 000000000..765714caf --- /dev/null +++ b/test/runtime/guard/tuple.ts @@ -0,0 +1,23 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TTuple', () => { + it('should guard for TTuple', () => { + const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()])) + Assert.equal(R, true) + }) + it('should not guard for TTuple', () => { + const R = TypeGuard.TTuple(null) + Assert.equal(R, false) + }) + it('should not guard for TTuple with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) + Assert.equal(R, false) + }) + it('should not guard for TTuple with invalid Items', () => { + const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), {} as any])) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/uint8array.ts b/test/runtime/guard/uint8array.ts new file mode 100644 index 000000000..963d7ad4d --- /dev/null +++ b/test/runtime/guard/uint8array.ts @@ -0,0 +1,33 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TUint8Array', () => { + it('should guard for TUint8Array', () => { + const R = TypeGuard.TUint8Array(Type.Uint8Array()) + Assert.equal(R, true) + }) + + it('should not guard for TUint8Array', () => { + const R = TypeGuard.TUint8Array(null) + Assert.equal(R, false) + }) + + it('should not guard for TUint8Array with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TUint8Array(Type.Uint8Array({ $id: 1 })) + Assert.equal(R, false) + }) + + it('should not guard for TUint8Array with invalid minByteLength', () => { + // @ts-ignore + const R = TypeGuard.TUint8Array(Type.Uint8Array({ minByteLength: '1' })) + Assert.equal(R, false) + }) + + it('should not guard for TUint8Array with invalid maxByteLength', () => { + // @ts-ignore + const R = TypeGuard.TUint8Array(Type.Uint8Array({ maxByteLength: '1' })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/undefined.ts b/test/runtime/guard/undefined.ts new file mode 100644 index 000000000..90c854759 --- /dev/null +++ b/test/runtime/guard/undefined.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TUndefined', () => { + it('should guard for TUndefined', () => { + const R = TypeGuard.TUndefined(Type.Undefined()) + Assert.equal(R, true) + }) + it('should not guard for TUndefined', () => { + const R = TypeGuard.TUndefined(null) + Assert.equal(R, false) + }) + it('should not guard for TUndefined with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TUndefined(Type.Undefined({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/union.ts b/test/runtime/guard/union.ts new file mode 100644 index 000000000..614a16654 --- /dev/null +++ b/test/runtime/guard/union.ts @@ -0,0 +1,66 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TUnion', () => { + it('should guard for TUnion', () => { + const R = TypeGuard.TUnion( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.equal(R, true) + }) + it('should not guard for TUnion', () => { + const R = TypeGuard.TUnion(null) + Assert.equal(R, false) + }) + it('should guard for TUnion with invalid $id', () => { + const R = TypeGuard.TUnion( + Type.Union( + [ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ], + { + // @ts-ignore + $id: 1, + }, + ), + ) + Assert.equal(R, false) + }) + it('should not guard for TUnion with invalid variant', () => { + const R = TypeGuard.TUnion( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + {} as any, + ]), + ) + Assert.equal(R, false) + }) + it('should not guard for TUnion with invalid object variant', () => { + const R = TypeGuard.TUnion( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: {} as any, + }), + ]), + ) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/unknown.ts b/test/runtime/guard/unknown.ts new file mode 100644 index 000000000..443698bd3 --- /dev/null +++ b/test/runtime/guard/unknown.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TUnknown', () => { + it('should guard for TUnknown', () => { + const R = TypeGuard.TUnknown(Type.Unknown()) + Assert.equal(R, true) + }) + it('should not guard for TUnknown', () => { + const R = TypeGuard.TUnknown(null) + Assert.equal(R, false) + }) + it('should not guard for TUnknown with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TUnknown(Type.Unknown({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/void.ts b/test/runtime/guard/void.ts new file mode 100644 index 000000000..b0565e69a --- /dev/null +++ b/test/runtime/guard/void.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TVoid', () => { + it('should guard for TVoid', () => { + const R = TypeGuard.TVoid(Type.Void()) + Assert.equal(R, true) + }) + it('should not guard for TVoid', () => { + const R = TypeGuard.TVoid(null) + Assert.equal(R, false) + }) + it('should not guard for TVoid with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TVoid(Type.Void({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/index.ts b/test/runtime/index.ts new file mode 100644 index 000000000..77bb736d2 --- /dev/null +++ b/test/runtime/index.ts @@ -0,0 +1,6 @@ +import './compiler/index' +import './conditional/index' +import './format/index' +import './guard/index' +import './schema/index' +import './value/index' diff --git a/test/runtime/schema/any.ts b/test/runtime/schema/any.ts new file mode 100644 index 000000000..6ffcb54c5 --- /dev/null +++ b/test/runtime/schema/any.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok } from './validate' + +describe('type/schema/Any', () => { + it('Should validate number', () => { + const T = Type.Any() + ok(T, 1) + }) + it('Should validate string', () => { + const T = Type.Any() + ok(T, 'hello') + }) + it('Should validate boolean', () => { + const T = Type.Any() + ok(T, true) + }) + it('Should validate array', () => { + const T = Type.Any() + ok(T, [1, 2, 3]) + }) + it('Should validate object', () => { + const T = Type.Any() + ok(T, { a: 1, b: 2 }) + }) + it('Should validate null', () => { + const T = Type.Any() + ok(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Any() + ok(T, undefined) + }) +}) diff --git a/test/runtime/schema/array.ts b/test/runtime/schema/array.ts new file mode 100644 index 000000000..bc3d1b1cf --- /dev/null +++ b/test/runtime/schema/array.ts @@ -0,0 +1,96 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Array', () => { + it('Should validate an array of any', () => { + const T = Type.Array(Type.Any()) + ok(T, [0, true, 'hello', {}]) + }) + + it('Should not validate varying array when item is number', () => { + const T = Type.Array(Type.Number()) + fail(T, [1, 2, 3, 'hello']) + }) + + it('Should validate for an array of unions', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + ok(T, [1, 'hello', 3, 'world']) + }) + + it('Should not validate for an array of unions where item is not in union.', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + fail(T, [1, 'hello', 3, 'world', true]) + }) + + it('Should validate for an empty array', () => { + const T = Type.Array(Type.Union([Type.Number(), Type.String()])) + ok(T, []) + }) + + it('Should validate for an array of intersection types', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Intersect([A, B]) + const T = Type.Array(C) + ok(T, [ + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + ]) + }) + + it('Should not validate for an array of intersection types when passing additionalProperties false', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Intersect([A, B], { additionalProperties: false }) + const T = Type.Array(C) + fail(T, [ + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello' }, + { a: 'hello', b: 'hello', c: 'additional' }, + ]) + }) + + it('Should validate an array of tuples', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Tuple([A, B]) + const T = Type.Array(C) + ok(T, [ + ['hello', 1], + ['hello', 1], + ['hello', 1], + ]) + }) + + it('Should not validate an array of tuples when tuple values are incorrect', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Tuple([A, B]) + const T = Type.Array(C) + fail(T, [ + [1, 'hello'], + [1, 'hello'], + [1, 'hello'], + ]) + }) + it('Should not validate array with failed minItems', () => { + const T = Type.Array(Type.Number(), { minItems: 3 }) + fail(T, [0, 1]) + }) + it('Should not validate array with failed maxItems', () => { + const T = Type.Array(Type.Number(), { maxItems: 3 }) + fail(T, [0, 1, 2, 3]) + }) + it('Should validate array with uniqueItems when items are references', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + ok(T, [ + { x: 0, y: 1 }, + { x: 1, y: 0 }, + ]) // references distinct + }) + it('Should not validate array with non uniqueItems', () => { + const T = Type.Array(Type.Number(), { uniqueItems: true }) + fail(T, [0, 0]) + }) +}) diff --git a/test/runtime/schema/boolean.ts b/test/runtime/schema/boolean.ts new file mode 100644 index 000000000..4402a1b60 --- /dev/null +++ b/test/runtime/schema/boolean.ts @@ -0,0 +1,40 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Boolean', () => { + it('Should validate a boolean', () => { + const T = Type.Boolean() + ok(T, true) + ok(T, false) + }) + + it('Should not validate a number', () => { + const T = Type.Boolean() + fail(T, 1) + }) + + it('Should not validate a string', () => { + const T = Type.Boolean() + fail(T, 'true') + }) + + it('Should not validate an array', () => { + const T = Type.Boolean() + fail(T, [true]) + }) + + it('Should not validate an object', () => { + const T = Type.Boolean() + fail(T, {}) + }) + + it('Should not validate an null', () => { + const T = Type.Boolean() + fail(T, null) + }) + + it('Should not validate an undefined', () => { + const T = Type.Boolean() + fail(T, undefined) + }) +}) diff --git a/test/runtime/schema/enum.ts b/test/runtime/schema/enum.ts new file mode 100644 index 000000000..dbdbacf4c --- /dev/null +++ b/test/runtime/schema/enum.ts @@ -0,0 +1,56 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Enum', () => { + it('Should validate when emum uses default numeric values', () => { + enum Kind { + Foo, // = 0 + Bar, // = 1 + } + const T = Type.Enum(Kind) + ok(T, 0) + ok(T, 1) + }) + it('Should not validate when given enum values are not numeric', () => { + enum Kind { + Foo, // = 0 + Bar, // = 1 + } + const T = Type.Enum(Kind) + fail(T, 'Foo') + fail(T, 'Bar') + }) + + it('Should validate when emum has defined string values', () => { + enum Kind { + Foo = 'foo', + Bar = 'bar', + } + const T = Type.Enum(Kind) + ok(T, 'foo') + ok(T, 'bar') + }) + + it('Should not validate when emum has defined string values and user passes numeric', () => { + enum Kind { + Foo = 'foo', + Bar = 'bar', + } + const T = Type.Enum(Kind) + fail(T, 0) + fail(T, 1) + }) + + it('Should validate when enum has one or more string values', () => { + enum Kind { + Foo, + Bar = 'bar', + } + const T = Type.Enum(Kind) + ok(T, 0) + ok(T, 'bar') + fail(T, 'baz') + fail(T, 'Foo') + fail(T, 1) + }) +}) diff --git a/spec/types/typebox.test-d.ts b/test/runtime/schema/index.ts similarity index 83% rename from spec/types/typebox.test-d.ts rename to test/runtime/schema/index.ts index 519bba3d5..401e007a9 100644 --- a/spec/types/typebox.test-d.ts +++ b/test/runtime/schema/index.ts @@ -1,12 +1,12 @@ import './any' import './array' import './boolean' -import './emum' +import './enum' import './intersect' import './keyof' import './literal' import './modifier' -import './namespace' +import './never' import './null' import './number' import './object' @@ -16,14 +16,14 @@ import './partial' import './pick' import './readonly-optional' import './readonly' -import './rec' +import './recursive' import './record' import './ref' import './regex' import './required' -import './strict' import './string' import './tuple' import './union' import './unknown' - +import './unsafe' +import './void' diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts new file mode 100644 index 000000000..24e3406ba --- /dev/null +++ b/test/runtime/schema/intersect.ts @@ -0,0 +1,94 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert' +import { ok, fail } from './validate' + +describe('type/schema/Intersect', () => { + it('Should intersect two objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Intersect([A, B], { additionalProperties: false }) + ok(T, { a: 'hello', b: 42 }) + }) + + it('Should intersect with partial', () => { + const A = Type.Partial(Type.Object({ a: Type.Number() })) + const B = Type.Partial(Type.Object({ b: Type.Number() })) + const P = Type.Intersect([A, B], { additionalProperties: false }) + ok(P, { a: 1, b: 2 }) + ok(P, { a: 1 }) + ok(P, { b: 1 }) + ok(P, {}) + fail(P, { c: 1 }) + }) + + it('Should intersect with overlapping same type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Number() }) + const P = Type.Intersect([A, B]) + ok(P, { a: 1 }) + fail(P, { a: 'hello' }) + fail(P, {}) + }) + + it('Should intersect with overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const T = Type.Intersect([A, B]) + ok(T, { a: 1 }) + ok(T, { a: 'hello' }) + fail(T, {}) + }) + + it('Should intersect with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const C = Type.Object({ a: Type.Boolean() }) + const D = Type.Object({ a: Type.Null() }) + const T = Type.Intersect([A, B, C, D]) + ok(T, { a: 1 }) + ok(T, { a: 'hello' }) + ok(T, { a: false }) + ok(T, { a: null }) + fail(T, { a: [] }) + fail(T, {}) + }) + + it('Should pick from intersected type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Intersect([A, B, C]) + const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) + ok(P, { x: 1, y: 1 }) + fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should omit from intersected type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Intersect([A, B, C]) + const P = Type.Omit(T, ['z'], { additionalProperties: false }) + ok(P, { x: 1, y: 1 }) + fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should intersect nested object properties', () => { + const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) + const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const T = Type.Intersect([A, B]) + ok(T, { x: { x: 1 } }) + ok(T, { x: { x: 'hello' } }) + fail(T, { x: { x: false } }) + }) + + // todo: move to composition / type guard spec + it('Should intersect and produce the same schema', () => { + const T = Type.Object({ + field: Type.Optional(Type.String()), + }) + const A = Type.Intersect([T]) + const B = Type.Intersect([T]) + Assert.deepEqual(A, B) + }) +}) diff --git a/test/runtime/schema/keyof.ts b/test/runtime/schema/keyof.ts new file mode 100644 index 000000000..d929b3770 --- /dev/null +++ b/test/runtime/schema/keyof.ts @@ -0,0 +1,52 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/KeyOf', () => { + it('Should validate with all object keys as a kind of union', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + ok(T, 'x') + ok(T, 'y') + ok(T, 'z') + fail(T, 'w') + }) + + it('Should validate when using pick', () => { + const T = Type.KeyOf( + Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ['x', 'y'], + ), + ) + ok(T, 'x') + ok(T, 'y') + fail(T, 'z') + }) + + it('Should validate when using omit', () => { + const T = Type.KeyOf( + Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ['x', 'y'], + ), + ) + + fail(T, 'x') + fail(T, 'y') + ok(T, 'z') + }) +}) diff --git a/test/runtime/schema/literal.ts b/test/runtime/schema/literal.ts new file mode 100644 index 000000000..92b065986 --- /dev/null +++ b/test/runtime/schema/literal.ts @@ -0,0 +1,43 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Literal', () => { + it('Should validate literal number', () => { + const T = Type.Literal(42) + ok(T, 42) + }) + it('Should validate literal string', () => { + const T = Type.Literal('hello') + ok(T, 'hello') + }) + + it('Should validate literal boolean', () => { + const T = Type.Literal(true) + ok(T, true) + }) + + it('Should not validate invalid literal number', () => { + const T = Type.Literal(42) + fail(T, 43) + }) + it('Should not validate invalid literal string', () => { + const T = Type.Literal('hello') + fail(T, 'world') + }) + it('Should not validate invalid literal boolean', () => { + const T = Type.Literal(false) + fail(T, true) + }) + + it('Should validate literal union', () => { + const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) + ok(T, 42) + ok(T, 'hello') + }) + + it('Should not validate invalid literal union', () => { + const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) + fail(T, 43) + fail(T, 'world') + }) +}) diff --git a/test/runtime/schema/modifier.ts b/test/runtime/schema/modifier.ts new file mode 100644 index 000000000..43e76c2b1 --- /dev/null +++ b/test/runtime/schema/modifier.ts @@ -0,0 +1,3 @@ +import { Type } from '@sinclair/typebox' + +describe('type/schema/Modifier', () => {}) diff --git a/test/runtime/schema/never.ts b/test/runtime/schema/never.ts new file mode 100644 index 000000000..a32e4b66b --- /dev/null +++ b/test/runtime/schema/never.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { fail } from './validate' + +describe('type/schema/Never', () => { + it('Should not validate number', () => { + const T = Type.Never() + fail(T, 1) + }) + it('Should not validate string', () => { + const T = Type.Never() + fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.Never() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.Never() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Never() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Never() + fail(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Never() + fail(T, undefined) + }) +}) diff --git a/test/runtime/schema/null.ts b/test/runtime/schema/null.ts new file mode 100644 index 000000000..8d5dca921 --- /dev/null +++ b/test/runtime/schema/null.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Null', () => { + it('Should not validate number', () => { + const T = Type.Null() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Null() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Null() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Null() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Null() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Null() + ok(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Null() + fail(T, undefined) + }) +}) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts new file mode 100644 index 000000000..62d509c0f --- /dev/null +++ b/test/runtime/schema/number.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Number', () => { + it('Should validate number', () => { + const T = Type.Number() + ok(T, 1) + }) + it('Should not validate string', () => { + const T = Type.Number() + fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.Number() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.Number() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Number() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Number() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Number() + fail(T, undefined) + }) +}) diff --git a/test/runtime/schema/object.ts b/test/runtime/schema/object.ts new file mode 100644 index 000000000..188dc1076 --- /dev/null +++ b/test/runtime/schema/object.ts @@ -0,0 +1,142 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Object', () => { + it('Should not validate a number', () => { + const T = Type.Object({}) + fail(T, 42) + }) + + it('Should not validate a string', () => { + const T = Type.Object({}) + fail(T, 'hello') + }) + + it('Should not validate a boolean', () => { + const T = Type.Object({}) + fail(T, true) + }) + + it('Should not validate a null', () => { + const T = Type.Object({}) + fail(T, null) + }) + + it('Should not validate an array', () => { + const T = Type.Object({}) + fail(T, [1, 2]) + }) + + it('Should validate with correct property values', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + c: Type.Boolean(), + d: Type.Array(Type.Number()), + e: Type.Object({ x: Type.Number(), y: Type.Number() }), + }) + ok(T, { + a: 10, + b: 'hello', + c: true, + d: [1, 2, 3], + e: { x: 10, y: 20 }, + }) + }) + + it('Should not validate with incorrect property values', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + c: Type.Boolean(), + d: Type.Array(Type.Number()), + e: Type.Object({ x: Type.Number(), y: Type.Number() }), + }) + fail(T, { + a: 'not a number', // error + b: 'hello', + c: true, + d: [1, 2, 3], + e: { x: 10, y: 20 }, + }) + }) + + it('Should allow additionalProperties by default', () => { + const T = Type.Object({ + a: Type.Number(), + b: Type.String(), + }) + ok(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow an empty object if minProperties is set to 1', () => { + const T = Type.Object( + { + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.String()), + }, + { additionalProperties: false, minProperties: 1 }, + ) + ok(T, { a: 1 }) + ok(T, { b: 'hello' }) + fail(T, {}) + }) + + it('Should not allow 3 properties if maxProperties is set to 2', () => { + const T = Type.Object( + { + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.String()), + c: Type.Optional(Type.Boolean()), + }, + { additionalProperties: false, maxProperties: 2 }, + ) + ok(T, { a: 1 }) + ok(T, { a: 1, b: 'hello' }) + fail(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow additionalProperties if additionalProperties is false', () => { + const T = Type.Object( + { + a: Type.Number(), + b: Type.String(), + }, + { additionalProperties: false }, + ) + fail(T, { + a: 1, + b: 'hello', + c: true, + }) + }) + + it('Should not allow properties for an empty object when additionalProperties is false', () => { + const T = Type.Object({}, { additionalProperties: false }) + ok(T, {}) + fail(T, { a: 10 }) + }) + + it('Should validate with non-syntax property keys', () => { + const T = Type.Object({ + 'with-hyphen': Type.Literal(1), + '0-leading': Type.Literal(2), + '$-leading': Type.Literal(3), + '!@#$%^&*(': Type.Literal(4), + }) + ok(T, { + 'with-hyphen': 1, + '0-leading': 2, + '$-leading': 3, + '!@#$%^&*(': 4, + }) + }) +}) diff --git a/test/runtime/schema/omit.ts b/test/runtime/schema/omit.ts new file mode 100644 index 000000000..b605646f5 --- /dev/null +++ b/test/runtime/schema/omit.ts @@ -0,0 +1,73 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/Omit', () => { + it('Should omit properties on the source schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + ok(T, { x: 1, y: 1 }) + }) + + it('Should remove required properties on the target schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + strictEqual(T.required!.includes('z'), false) + }) + + it('Should delete the required property if no required properties remain', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.ReadonlyOptional(Type.Number()), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + strictEqual(T.required, undefined) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, ['z']) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) + + it('Should omit with keyof object', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const T = Type.Omit(A, Type.KeyOf(B), { additionalProperties: false }) + ok(T, { z: 0 }) + fail(T, { x: 0, y: 0, z: 0 }) + }) +}) diff --git a/test/runtime/schema/optional.ts b/test/runtime/schema/optional.ts new file mode 100644 index 000000000..ba95bd7fb --- /dev/null +++ b/test/runtime/schema/optional.ts @@ -0,0 +1,27 @@ +import { strictEqual } from 'assert' +import { Type } from '@sinclair/typebox' +import { ok } from './validate' + +describe('type/schema/Optional', () => { + it('Should validate object with optional', () => { + const T = Type.Object( + { + a: Type.Optional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + ok(T, { b: 'world' }) + }) + it('Should remove required value from schema', () => { + const T = Type.Object( + { + a: Type.Optional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), false) + }) +}) diff --git a/test/runtime/schema/partial.ts b/test/runtime/schema/partial.ts new file mode 100644 index 000000000..6fafdd867 --- /dev/null +++ b/test/runtime/schema/partial.ts @@ -0,0 +1,52 @@ +import { Type, Modifier } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/Partial', () => { + it('Should convert a required object into a partial.', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + ok(T, { x: 1, y: 1, z: 1 }) + ok(T, { x: 1, y: 1 }) + ok(T, { x: 1 }) + ok(T, {}) + }) + + it('Should update modifier types correctly when converting to partial', () => { + const A = Type.Object( + { + x: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Number()), + z: Type.Optional(Type.Number()), + w: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + strictEqual(T.properties.x[Modifier], 'ReadonlyOptional') + strictEqual(T.properties.y[Modifier], 'ReadonlyOptional') + strictEqual(T.properties.z[Modifier], 'Optional') + strictEqual(T.properties.w[Modifier], 'Optional') + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Partial(A) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) +}) diff --git a/test/runtime/schema/pick.ts b/test/runtime/schema/pick.ts new file mode 100644 index 000000000..f96332bfc --- /dev/null +++ b/test/runtime/schema/pick.ts @@ -0,0 +1,73 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/Pick', () => { + it('Should pick properties from the source schema', () => { + const Vector3 = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(Vector3, ['x', 'y']) + ok(T, { x: 1, y: 1 }) + }) + + it('Should remove required properties on the target schema', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, ['x', 'y']) + strictEqual(T.required!.includes('z'), false) + }) + + it('Should delete the required property if no required properties remain', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.ReadonlyOptional(Type.Number()), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, ['x', 'y']) + strictEqual(T.required, undefined) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, ['x', 'y']) + strictEqual(A.additionalProperties, false) + strictEqual(T.additionalProperties, false) + }) + + it('Should pick with keyof object', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const T = Type.Pick(A, Type.KeyOf(B), { additionalProperties: false }) + ok(T, { x: 0, y: 0 }) + fail(T, { x: 0, y: 0, z: 0 }) + }) +}) diff --git a/test/runtime/schema/readonly-optional.ts b/test/runtime/schema/readonly-optional.ts new file mode 100644 index 000000000..8353010be --- /dev/null +++ b/test/runtime/schema/readonly-optional.ts @@ -0,0 +1,27 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/ReadonlyOptional', () => { + it('Should validate object with optional', () => { + const T = Type.Object( + { + a: Type.ReadonlyOptional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + ok(T, { b: 'world' }) + }) + it('Should remove required value from schema', () => { + const T = Type.Object( + { + a: Type.ReadonlyOptional(Type.String()), + b: Type.String(), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), false) + }) +}) diff --git a/test/runtime/schema/readonly.ts b/test/runtime/schema/readonly.ts new file mode 100644 index 000000000..c0b55dc85 --- /dev/null +++ b/test/runtime/schema/readonly.ts @@ -0,0 +1,28 @@ +import { deepStrictEqual, strictEqual } from 'assert' +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Readonly', () => { + it('Should validate object with readonly', () => { + const T = Type.Object( + { + a: Type.Readonly(Type.String()), + b: Type.Readonly(Type.String()), + }, + { additionalProperties: false }, + ) + ok(T, { a: 'hello', b: 'world' }) + }) + + it('Should retain required array on object', () => { + const T = Type.Object( + { + a: Type.Readonly(Type.String()), + b: Type.Readonly(Type.String()), + }, + { additionalProperties: false }, + ) + strictEqual(T.required!.includes('a'), true) + strictEqual(T.required!.includes('b'), true) + }) +}) diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts new file mode 100644 index 000000000..d88819c94 --- /dev/null +++ b/test/runtime/schema/record.ts @@ -0,0 +1,101 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Record', () => { + it('Should validate when all property values are numbers', () => { + const T = Type.Record(Type.String(), Type.Number()) + ok(T, { a: 1, b: 2, c: 3 }) + }) + + it('Should validate when all property keys are strings', () => { + const T = Type.Record(Type.String(), Type.Number()) + ok(T, { a: 1, b: 2, c: 3, '0': 4 }) + }) + + it('Should validate when all property keys are numbers', () => { + const T = Type.Record(Type.Number(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should validate when specifying string union literals when additionalProperties is true', () => { + const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) + const T = Type.Record(K, Type.Number()) + ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) + }) + + it('Should not validate when specifying string union literals when additionalProperties is false', () => { + const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) + const T = Type.Record(K, Type.Number(), { additionalProperties: false }) + fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) + }) + + it('Should validate for keyof records', () => { + const T = Type.Object({ + a: Type.String(), + b: Type.Number(), + c: Type.String(), + }) + const R = Type.Record(Type.KeyOf(T), Type.Number()) + ok(R, { a: 1, b: 2, c: 3 }) + }) + + it('Should not validate for unknown key via keyof', () => { + const T = Type.Object({ + a: Type.String(), + b: Type.Number(), + c: Type.String(), + }) + const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) + fail(R, { a: 1, b: 2, c: 3, d: 4 }) + }) + + it('Should should validate when specifying regular expressions', () => { + const K = Type.RegEx(/^op_.*$/) + const T = Type.Record(K, Type.Number()) + ok(T, { + op_a: 1, + op_b: 2, + op_c: 3, + }) + }) + + it('Should should not validate when specifying regular expressions and passing invalid property', () => { + const K = Type.RegEx(/^op_.*$/) + const T = Type.Record(K, Type.Number()) + fail(T, { + op_a: 1, + op_b: 2, + aop_c: 3, + }) + }) +}) diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts new file mode 100644 index 000000000..a0b132232 --- /dev/null +++ b/test/runtime/schema/recursive.ts @@ -0,0 +1,83 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' +import { ok, fail } from './validate' + +describe('type/schema/Recursive', () => { + it('Should generate default ordinal $id if not specified', () => { + const Node = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + Assert.equal(Node.$id === undefined, false) + }) + + it('Should override default ordinal $id if specified', () => { + const Node = Type.Recursive( + (Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + { $id: 'Node' }, + ) + Assert.equal(Node.$id === 'Node', true) + }) + + it('Should validate recursive node type', () => { + const Node = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + ok(Node, { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + ], + }) + }) + + it('Should validate wrapped recursive node type', () => { + const Node = Type.Tuple([ + Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ), + ]) + ok(Node, [ + { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + ], + }, + ]) + }) + + it('Should not validate wrapped recursive node type with invalid id', () => { + const Node = Type.Tuple([ + Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ), + ]) + fail(Node, [ + { + id: 'A', + nodes: [ + { id: 1, nodes: [] }, + { id: 'C', nodes: [] }, + ], + }, + ]) + }) +}) diff --git a/test/runtime/schema/ref.ts b/test/runtime/schema/ref.ts new file mode 100644 index 000000000..ade5e054a --- /dev/null +++ b/test/runtime/schema/ref.ts @@ -0,0 +1,69 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { Assert } from '../assert/index' + +describe('type/schema/Ref', () => { + it('Should should validate when referencing a type', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: 'T' }, + ) + const R = Type.Ref(T) + ok( + R, + { + x: 1, + y: 2, + z: 3, + }, + [T], + ) + }) + + it('Should not validate when passing invalid data', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: 'T' }, + ) + const R = Type.Ref(T) + fail( + R, + { + x: 1, + y: 2, + }, + [T], + ) + }) + it('Should de-reference object property schema', () => { + const R = Type.Object( + { + name: Type.String(), + }, + { $id: 'R' }, + ) + + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + r: Type.Optional(Type.Ref(R)), + }, + { $id: 'T' }, + ) + + ok(T, { x: 1, y: 2, z: 3 }, [R]) + ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) + fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) + fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) + }) +}) diff --git a/test/runtime/schema/regex.ts b/test/runtime/schema/regex.ts new file mode 100644 index 000000000..0c5c56faa --- /dev/null +++ b/test/runtime/schema/regex.ts @@ -0,0 +1,35 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/RegEx', () => { + it('Should validate numeric value', () => { + const T = Type.RegEx(/[012345]/) + ok(T, '0') + ok(T, '1') + ok(T, '2') + ok(T, '3') + ok(T, '4') + ok(T, '5') + }) + + it('Should validate true or false string value', () => { + const T = Type.RegEx(/true|false/) + ok(T, 'true') + ok(T, 'true') + ok(T, 'true') + ok(T, 'false') + ok(T, 'false') + ok(T, 'false') + fail(T, '6') + }) + + it('Should not validate failed regex test', () => { + const T = Type.RegEx(/true|false/) + fail(T, 'unknown') + }) + + it('Should pass numeric 5 digit test', () => { + const T = Type.RegEx(/[\d]{5}/) + ok(T, '12345') + }) +}) diff --git a/test/runtime/schema/required.ts b/test/runtime/schema/required.ts new file mode 100644 index 000000000..f3090fcdd --- /dev/null +++ b/test/runtime/schema/required.ts @@ -0,0 +1,57 @@ +import { Type, Modifier } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { strictEqual } from 'assert' + +describe('type/schema/Required', () => { + it('Should convert a partial object into a required object', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }, + { additionalProperties: false }, + ) + const T = Type.Required(A) + ok(T, { x: 1, y: 1, z: 1 }) + fail(T, { x: 1, y: 1 }) + fail(T, { x: 1 }) + fail(T, {}) + }) + + it('Should update modifier types correctly when converting to required', () => { + const A = Type.Object({ + x: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Number()), + z: Type.Optional(Type.Number()), + w: Type.Number(), + }) + const T = Type.Required(A) + strictEqual(T.properties.x[Modifier], 'Readonly') + strictEqual(T.properties.y[Modifier], 'Readonly') + strictEqual(T.properties.z[Modifier], undefined) + strictEqual(T.properties.w[Modifier], undefined) + }) + + it('Should inherit options from the source object', () => { + const A = Type.Object( + { + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }, + { additionalPropeties: false }, + ) + const T = Type.Required(A) + strictEqual(A.additionalPropeties, false) + strictEqual(T.additionalPropeties, false) + }) + + // it('Should construct new object when targetting reference', () => { + // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) + // const R = Type.Ref(T) + // const P = Type.Required(R) + // strictEqual(P.properties.a.type, 'string') + // strictEqual(P.properties.b.type, 'string') + // }) +}) diff --git a/test/runtime/schema/string.ts b/test/runtime/schema/string.ts new file mode 100644 index 000000000..9061de762 --- /dev/null +++ b/test/runtime/schema/string.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/String', () => { + it('Should not validate number', () => { + const T = Type.String() + fail(T, 1) + }) + it('Should validate string', () => { + const T = Type.String() + ok(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.String() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.String() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.String() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.String() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.String() + fail(T, undefined) + }) + + it('Should validate string format as email', () => { + const T = Type.String({ format: 'email' }) + ok(T, 'name@domain.com') + }) + + it('Should validate string format as uuid', () => { + const T = Type.String({ format: 'uuid' }) + ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') + }) + + it('Should validate string format as iso8601 date', () => { + const T = Type.String({ format: 'date-time' }) + ok(T, '2021-06-11T20:30:00-04:00') + }) + + it('Should validate minLength', () => { + const T = Type.String({ minLength: 4 }) + ok(T, '....') + fail(T, '...') + }) + + it('Should validate maxLength', () => { + const T = Type.String({ maxLength: 4 }) + ok(T, '....') + fail(T, '.....') + }) + + it('Should pass numeric 5 digit test', () => { + const T = Type.String({ pattern: '[\\d]{5}' }) + ok(T, '12345') + }) +}) diff --git a/test/runtime/schema/tuple.ts b/test/runtime/schema/tuple.ts new file mode 100644 index 000000000..60ba0be00 --- /dev/null +++ b/test/runtime/schema/tuple.ts @@ -0,0 +1,62 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' +import { Assert } from '../assert' + +describe('type/schema/Tuple', () => { + it('Should validate tuple of [string, number]', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Tuple([A, B]) + ok(T, ['hello', 42]) + }) + + it('Should not validate tuple of [string, number] when reversed', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Tuple([A, B]) + fail(T, [42, 'hello']) + }) + + it('Should validate with empty tuple', () => { + const T = Type.Tuple([]) + ok(T, []) + }) + + it('Should not validate with empty tuple with more items', () => { + const T = Type.Tuple([]) + fail(T, [1]) + }) + + it('Should not validate with empty tuple with less items', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + fail(T, [1]) + }) + + it('Should validate tuple of objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + ok(T, [{ a: 'hello' }, { b: 42 }]) + }) + + it('Should not validate tuple of objects when reversed', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ b: 42 }, { a: 'hello' }]) + }) + + it('Should not validate tuple when array is less than tuple length', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ a: 'hello' }]) + }) + + it('Should not validate tuple when array is greater than tuple length', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Tuple([A, B]) + fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) + }) +}) diff --git a/test/runtime/schema/union.ts b/test/runtime/schema/union.ts new file mode 100644 index 000000000..45d371abe --- /dev/null +++ b/test/runtime/schema/union.ts @@ -0,0 +1,62 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Union', () => { + it('Should validate union of string, number and boolean', () => { + const A = Type.String() + const B = Type.Number() + const C = Type.Boolean() + const T = Type.Union([A, B, C]) + ok(T, 'hello') + ok(T, true) + ok(T, 42) + }) + + it('Should validate union of objects', () => { + const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) + const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello' }) + ok(T, { b: 'world' }) + }) + + it('Should fail to validate for descriminated union types', () => { + const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) + const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) + const T = Type.Union([A, B]) + fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } + fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } + }) + + it('Should validate union of objects where properties overlap', () => { + const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) + const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello' }) // A + ok(T, { a: 'hello', b: 'world' }) // B + }) + + it('Should validate union of overlapping property of varying type', () => { + const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) + const T = Type.Union([A, B]) + ok(T, { a: 'hello', b: 42 }) // A + ok(T, { a: 'hello', b: 'world' }) // B + }) + + it('Should validate union of literal strings', () => { + const A = Type.Literal('hello') + const B = Type.Literal('world') + const T = Type.Union([A, B]) + ok(T, 'hello') // A + ok(T, 'world') // B + }) + + it('Should not validate union of literal strings for unknown string', () => { + const A = Type.Literal('hello') + const B = Type.Literal('world') + const T = Type.Union([A, B]) + fail(T, 'foo') // A + fail(T, 'bar') // B + }) +}) diff --git a/test/runtime/schema/unknown.ts b/test/runtime/schema/unknown.ts new file mode 100644 index 000000000..c9b94f339 --- /dev/null +++ b/test/runtime/schema/unknown.ts @@ -0,0 +1,33 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Unknown', () => { + it('Should validate number', () => { + const T = Type.Any() + ok(T, 1) + }) + it('Should validate string', () => { + const T = Type.Any() + ok(T, 'hello') + }) + it('Should validate boolean', () => { + const T = Type.Any() + ok(T, true) + }) + it('Should validate array', () => { + const T = Type.Any() + ok(T, [1, 2, 3]) + }) + it('Should validate object', () => { + const T = Type.Any() + ok(T, { a: 1, b: 2 }) + }) + it('Should validate null', () => { + const T = Type.Any() + ok(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Any() + ok(T, undefined) + }) +}) diff --git a/test/runtime/schema/unsafe.ts b/test/runtime/schema/unsafe.ts new file mode 100644 index 000000000..d21fe3411 --- /dev/null +++ b/test/runtime/schema/unsafe.ts @@ -0,0 +1,18 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Unsafe', () => { + it('Should validate an unsafe type', () => { + const T = Type.Unsafe({ + type: 'object', + properties: { + x: { type: 'number' }, + y: { type: 'number' }, + z: { type: 'number' }, + }, + additionalProperties: false, + }) + ok(T, { x: 1, y: 2, z: 3 }) + fail(T, { x: 1, y: 2, z: 3, w: 4 }) + }) +}) diff --git a/test/runtime/schema/validate.ts b/test/runtime/schema/validate.ts new file mode 100644 index 000000000..f12181907 --- /dev/null +++ b/test/runtime/schema/validate.ts @@ -0,0 +1,64 @@ +import { TSchema } from '@sinclair/typebox' +import addFormats from 'ajv-formats' +import Ajv, { AnySchema } from 'ajv/dist/2019' + +export function validator(additional: AnySchema[]) { + return addFormats(new Ajv({}), ['date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex']) + .addKeyword('kind') + .addKeyword('modifier') + .addSchema(additional) +} + +export function ok(type: T, data: unknown, additional: AnySchema[] = []) { + const ajv = validator(additional) + function execute() { + // required as ajv will throw if referenced schema is not found + try { + return ajv.validate(type, data) as boolean + } catch { + return false + } + } + if (execute() === false) { + console.log('---------------------------') + console.log('type') + console.log('---------------------------') + console.log(JSON.stringify(type, null, 2)) + console.log('---------------------------') + console.log('data') + console.log('---------------------------') + console.log(JSON.stringify(data, null, 2)) + console.log('---------------------------') + console.log('errors') + console.log('---------------------------') + console.log(ajv.errorsText(ajv.errors)) + throw Error('expected ok') + } +} + +export function fail(type: T, data: unknown, additional: AnySchema[] = []) { + const ajv = validator(additional) + function execute() { + // required as ajv will throw if referenced schema is not found + try { + return ajv.validate(type, data) as boolean + } catch { + return false + } + } + if (execute() === true) { + console.log('---------------------------') + console.log('type') + console.log('---------------------------') + console.log(JSON.stringify(type, null, 2)) + console.log('---------------------------') + console.log('data') + console.log('---------------------------') + console.log(JSON.stringify(data, null, 2)) + console.log('---------------------------') + console.log('errors') + console.log('---------------------------') + console.log(ajv.errorsText(ajv.errors)) + throw Error('expected ok') + } +} diff --git a/test/runtime/schema/void.ts b/test/runtime/schema/void.ts new file mode 100644 index 000000000..8d5c13e21 --- /dev/null +++ b/test/runtime/schema/void.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Void', () => { + it('Should not validate number', () => { + const T = Type.Void() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Void() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Void() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Void() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Void() + fail(T, { a: 1, b: 2 }) + }) + + it('Should validate null', () => { + const T = Type.Null() + ok(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Void() + fail(T, undefined) + }) +}) diff --git a/test/runtime/value/cast/any.ts b/test/runtime/value/cast/any.ts new file mode 100644 index 000000000..5fcace73f --- /dev/null +++ b/test/runtime/value/cast/any.ts @@ -0,0 +1,47 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Any', () => { + const T = Type.Any() + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from boolean', () => { + const value = false + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should preserve', () => { + const value = { a: 1, b: 2 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { a: 1, b: 2 }) + }) +}) diff --git a/test/runtime/value/cast/array.ts b/test/runtime/value/cast/array.ts new file mode 100644 index 000000000..d23c59218 --- /dev/null +++ b/test/runtime/value/cast/array.ts @@ -0,0 +1,137 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Array', () => { + const T = Type.Array(Type.Number(), { default: [1, 2, 3] }) + const E = [1, 2, 3] + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [1]) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = [6, 7, 8] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [6, 7, 8]) + }) + + it('Should preserve with invalid element set to default', () => { + const value = [6, 7, 8, 'hello', 9] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [6, 7, 8, 0, 9]) + }) + + // ----------------------------------------------------------------- + // Constraints: Ranges + // ----------------------------------------------------------------- + + it('Should cast array and truncate to maxItems from value', () => { + const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3 }), [0, 1, 2, 4, 5, 6]) + Assert.deepEqual(result, [0, 1, 2]) + }) + + it('Should cast arrays and append array to minItems from value', () => { + const result = Value.Cast(Type.Array(Type.Number(), { minItems: 6 }), [0, 1, 2]) + Assert.deepEqual(result, [0, 1, 2, 0, 0, 0]) + }) + + it('Should cast array and truncate to maxItems from default value', () => { + const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3, default: [0, 1, 2, 4, 5, 6] }), null) + Assert.deepEqual(result, [0, 1, 2]) + }) + + it('Should cast arrays and append array to minItems from default value', () => { + const result = Value.Cast(Type.Array(Type.Number({ default: 1 }), { minItems: 6, default: [0, 1, 2] }), null) + Assert.deepEqual(result, [0, 1, 2, 1, 1, 1]) + }) + + // ----------------------------------------------------------------- + // Constraints: Unique + // ----------------------------------------------------------------- + + it('Should cast arrays with uniqueItems with unique default value', () => { + const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true, default: [0, 1, 2] }), null) + Assert.deepEqual(result, [0, 1, 2]) + }) + + it('Should cast arrays with uniqueItems with unique value', () => { + const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), [0, 1, 2]) + Assert.deepEqual(result, [0, 1, 2]) + }) + + it('Should throw when casting arrays with uniqueItems and no value or default value', () => { + Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), null)) + }) + + it('Should throw when casting arrays with uniqueItems and not enough values to populate set', () => { + Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true }), [0, 1])) + }) + + it('Should throw when casting arrays with uniqueItems and not enough default values to populate set', () => { + Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true, default: [0, 1] }), null)) + }) + + // ----------------------------------------------------------------- + // Suggestion: https://github.com/sinclairzx81/typebox/issues/239 + // ----------------------------------------------------------------- + + it('Should remove duplicates if uniqueItems is true', () => { + const T = Type.Array(Type.Number(), { uniqueItems: true }) + const value = [1, 1, 2, 2] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [1, 2]) + }) + + it('Should should fill up with defaults to minItems', () => { + const T = Type.Array(Type.Number(), { minItems: 3 }) + const value = [1, 2] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [1, 2, 0]) + }) + + it('Should should truncate to maxItems', () => { + const T = Type.Array(Type.Number(), { maxItems: 3 }) + const value = [1, 2, 3, 4] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [1, 2, 3]) + }) +}) diff --git a/test/runtime/value/cast/boolean.ts b/test/runtime/value/cast/boolean.ts new file mode 100644 index 000000000..e7a3c8023 --- /dev/null +++ b/test/runtime/value/cast/boolean.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Boolean', () => { + const T = Type.Boolean() + const E = false + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 0 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, true) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, true) + }) +}) diff --git a/test/runtime/value/cast/convert.ts b/test/runtime/value/cast/convert.ts new file mode 100644 index 000000000..ba63f16a8 --- /dev/null +++ b/test/runtime/value/cast/convert.ts @@ -0,0 +1,281 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/String', () => { + it('Should convert string', () => { + const value = 'hello' + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, 'hello') + }) + + it('Should convert number #1', () => { + const value = 42 + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, '42') + }) + + it('Should convert number #2', () => { + const value = 42n + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, '42') + }) + + it('Should convert true', () => { + const value = true + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, 'true') + }) + + it('Should convert false', () => { + const value = false + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, 'false') + }) + + it('Should convert object', () => { + const value = {} + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, '') + }) + + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, '') + }) +}) + +describe('value/convert/Number', () => { + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert string #2', () => { + const value = '3.14' + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 3.14) + }) + + it('Should convert string #3', () => { + const value = '-0' + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert string #4', () => { + const value = '-100' + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, -100) + }) + + it('Should convert number', () => { + const value = 42 + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 42) + }) + + it('Should convert true', () => { + const value = true + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 1) + }) + + it('Should convert false', () => { + const value = false + const result = Value.Cast(Type.Number(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert object', () => { + const value = {} + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Cast(Type.String(), value) + Assert.deepEqual(result, 0) + }) +}) + +describe('value/convert/Integer', () => { + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert string #2', () => { + const value = '3.14' + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 3) + }) + + it('Should convert string #3', () => { + const value = '-0' + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert string #4', () => { + const value = '-100' + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, -100) + }) + + it('Should convert number', () => { + const value = 42 + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 42) + }) + + it('Should convert true', () => { + const value = true + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 1) + }) + + it('Should convert false', () => { + const value = false + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert object', () => { + const value = {} + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Cast(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) +}) + +describe('value/convert/Boolean', () => { + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert string #2', () => { + const value = 'true' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert string #3', () => { + const value = 'TRUE' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert string #4', () => { + const value = 'false' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert string #5', () => { + const value = '0' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert string #6', () => { + const value = '1' + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert string #7', () => { + const value = '0' + const result = Value.Cast(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, false) + }) + + it('Should convert string #8', () => { + const value = '1' + const result = Value.Cast(Type.Boolean({ default: false }), value) + Assert.deepEqual(result, true) + }) + + it('Should convert string #8', () => { + const value = '2' + const result = Value.Cast(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, true) + }) + + it('Should convert number #1', () => { + const value = 0 + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert number #2', () => { + const value = 1n + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert number #3', () => { + const value = 1 + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert number #4', () => { + const value = 2 + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert number #5', () => { + const value = 0 + const result = Value.Cast(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, false) + }) + + it('Should convert number #6', () => { + const value = 1 + const result = Value.Cast(Type.Boolean({ default: false }), value) + Assert.deepEqual(result, true) + }) + + it('Should convert number #7', () => { + const value = 2 + const result = Value.Cast(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, true) + }) + + it('Should convert true', () => { + const value = true + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + + it('Should convert false', () => { + const value = false + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert object', () => { + const value = {} + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Cast(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) +}) diff --git a/test/runtime/value/cast/enum.ts b/test/runtime/value/cast/enum.ts new file mode 100644 index 000000000..232505ac7 --- /dev/null +++ b/test/runtime/value/cast/enum.ts @@ -0,0 +1,62 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Boolean', () => { + enum Foo { + A, + B, + } + const T = Type.Enum(Foo) + const E = Foo.A + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 123 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from enum A', () => { + const value = Foo.A + const result = Value.Cast(T, value) + Assert.deepEqual(result, Foo.A) + }) + + it('Should upcast from enum B', () => { + const value = Foo.B + const result = Value.Cast(T, value) + Assert.deepEqual(result, Foo.B) + }) +}) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts new file mode 100644 index 000000000..14b81b055 --- /dev/null +++ b/test/runtime/value/cast/index.ts @@ -0,0 +1,24 @@ +import './any' +import './array' +import './boolean' +import './convert' +import './enum' +import './integer' +import './intersect' +import './keyof' +import './literal' +import './namespace' +import './never' +import './null' +import './number' +import './object' +import './rec' +import './record' +import './regex' +import './string' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/cast/integer.ts b/test/runtime/value/cast/integer.ts new file mode 100644 index 000000000..1f026ac8e --- /dev/null +++ b/test/runtime/value/cast/integer.ts @@ -0,0 +1,47 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Integer', () => { + const T = Type.Integer() + const E = 0 + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, 1) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, 1) // conversion + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) +}) diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts new file mode 100644 index 000000000..78e2c0bb5 --- /dev/null +++ b/test/runtime/value/cast/intersect.ts @@ -0,0 +1,90 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Intersect', () => { + const A = Type.Object({ + x: Type.Number({ default: 0 }), + y: Type.Number({ default: 1 }), + z: Type.Number({ default: 2 }), + }) + const B = Type.Object({ + a: Type.Number({ default: 'a' }), + b: Type.Number({ default: 'b' }), + c: Type.Number({ default: 'c' }), + }) + const T = Type.Intersect([A, B]) + const E = { + x: 0, + y: 1, + z: 2, + a: 'a', + b: 'b', + c: 'c', + } + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = E + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast and preserve object', () => { + const value = { x: 7, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 7, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) + + it('Should upcast and preserve from incorrect properties', () => { + const value = { x: {}, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 0, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) +}) diff --git a/test/runtime/value/cast/keyof.ts b/test/runtime/value/cast/keyof.ts new file mode 100644 index 000000000..c9c2b9c9c --- /dev/null +++ b/test/runtime/value/cast/keyof.ts @@ -0,0 +1,62 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/KeyOf', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const E = 'x' + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = 'y' + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'y') + }) +}) diff --git a/test/runtime/value/cast/literal.ts b/test/runtime/value/cast/literal.ts new file mode 100644 index 000000000..bf0174a77 --- /dev/null +++ b/test/runtime/value/cast/literal.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Literal', () => { + const T = Type.Literal('hello') + const E = 'hello' + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preseve', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'hello') + }) +}) diff --git a/test/runtime/value/cast/namespace.ts b/test/runtime/value/cast/namespace.ts new file mode 100644 index 000000000..e69de29bb diff --git a/test/runtime/value/cast/never.ts b/test/runtime/value/cast/never.ts new file mode 100644 index 000000000..d8ec459fe --- /dev/null +++ b/test/runtime/value/cast/never.ts @@ -0,0 +1,35 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Never', () => { + const T = Type.Never() + it('Should throw from string', () => { + const value = 'hello' + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from number', () => { + const value = 1 + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from boolean', () => { + const value = false + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from object', () => { + const value = {} + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from array', () => { + const value = [1] + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from undefined', () => { + const value = undefined + Assert.throws(() => Value.Cast(T, value)) + }) + it('Should throw from null', () => { + const value = null + Assert.throws(() => Value.Cast(T, value)) + }) +}) diff --git a/test/runtime/value/cast/null.ts b/test/runtime/value/cast/null.ts new file mode 100644 index 000000000..5857121e2 --- /dev/null +++ b/test/runtime/value/cast/null.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Null', () => { + const T = Type.Null() + const E = null + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preseve', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, null) + }) +}) diff --git a/test/runtime/value/cast/number.ts b/test/runtime/value/cast/number.ts new file mode 100644 index 000000000..273f1f4ab --- /dev/null +++ b/test/runtime/value/cast/number.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Number', () => { + const T = Type.Number() + const E = 0 + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, 1) + }) + it('Should upcast from boolean', () => { + const value = true // convert + const result = Value.Cast(T, value) + Assert.deepEqual(result, 1) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preseve', () => { + const value = 123 + const result = Value.Cast(T, value) + Assert.deepEqual(result, 123) + }) +}) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts new file mode 100644 index 000000000..bf821147b --- /dev/null +++ b/test/runtime/value/cast/object.ts @@ -0,0 +1,112 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Object', () => { + const T = Type.Object({ + a: Type.Number({ default: 'a' }), + b: Type.Number({ default: 'b' }), + c: Type.Number({ default: 'c' }), + x: Type.Number({ default: 0 }), + y: Type.Number({ default: 1 }), + z: Type.Number({ default: 2 }), + }) + const E = { + x: 0, + y: 1, + z: 2, + a: 'a', + b: 'b', + c: 'c', + } + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = E + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { + const value = { x: 7, y: 8, z: 9, a: 10, b: 11, c: 12 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 7, + y: 8, + z: 9, + a: 10, + b: 11, + c: 12, + }) + }) + it('Should upcast and preserve partial object', () => { + const value = { x: 7, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 7, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) + + it('Should upcast and preserve partial object with incorrect properties', () => { + const value = { x: {}, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 0, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) + + it('Should upcast and preserve partial object and omit unknown properties', () => { + const value = { x: 7, y: 8, z: 9, unknown: 'foo' } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 7, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) +}) diff --git a/test/runtime/value/cast/rec.ts b/test/runtime/value/cast/rec.ts new file mode 100644 index 000000000..478a5fc87 --- /dev/null +++ b/test/runtime/value/cast/rec.ts @@ -0,0 +1,104 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Recursive', () => { + const T = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + + const E = { id: '', nodes: [] } + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = E + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + { id: 'D', nodes: [] }, + ], + } + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should upcast from varying types', () => { + const TypeA = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + + const TypeB = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + name: Type.String({ default: 'test' }), + nodes: Type.Array(Self), + }), + ) + + const ValueA = { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + { id: 'D', nodes: [] }, + ], + } + const ValueB = Value.Cast(TypeB, ValueA) + + Assert.deepEqual(ValueB, { + id: 'A', + name: 'test', + nodes: [ + { id: 'B', name: 'test', nodes: [] }, + { id: 'C', name: 'test', nodes: [] }, + { id: 'D', name: 'test', nodes: [] }, + ], + }) + }) +}) diff --git a/test/runtime/value/cast/record.ts b/test/runtime/value/cast/record.ts new file mode 100644 index 000000000..c8e9dd350 --- /dev/null +++ b/test/runtime/value/cast/record.ts @@ -0,0 +1,81 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Record', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const E = {} + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = E + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = { + a: { x: 1, y: 2, z: 3 }, + b: { x: 4, y: 5, z: 6 }, + } + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should preserve and patch invalid records', () => { + const value = { + a: { x: 1, y: 2, z: 3 }, + b: { x: 4, y: 5, z: {} }, + c: [1, 2, 3], + d: 1, + e: { x: 1, y: 2, w: 9000 }, + } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + a: { x: 1, y: 2, z: 3 }, + b: { x: 4, y: 5, z: 0 }, + c: { x: 0, y: 0, z: 0 }, + d: { x: 0, y: 0, z: 0 }, + e: { x: 1, y: 2, z: 0 }, + }) + }) +}) diff --git a/test/runtime/value/cast/regex.ts b/test/runtime/value/cast/regex.ts new file mode 100644 index 000000000..c973dba37 --- /dev/null +++ b/test/runtime/value/cast/regex.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/RegEx', () => { + const T = Type.RegEx(/foo/, { default: 'foo' }) + const E = 'foo' + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'foo') + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = 'foo' + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) +}) diff --git a/test/runtime/value/cast/string.ts b/test/runtime/value/cast/string.ts new file mode 100644 index 000000000..9b5fa1ef7 --- /dev/null +++ b/test/runtime/value/cast/string.ts @@ -0,0 +1,55 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/String', () => { + const T = Type.String() + const E = '' + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'hello') + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, '1') // conversion + }) + + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'true') // conversion + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = 'foo' + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) +}) diff --git a/test/runtime/value/cast/tuple.ts b/test/runtime/value/cast/tuple.ts new file mode 100644 index 000000000..4ecb10d96 --- /dev/null +++ b/test/runtime/value/cast/tuple.ts @@ -0,0 +1,80 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Tuple', () => { + const T = Type.Tuple([Type.Number(), Type.String()]) + const E = [0, ''] + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [1, '']) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = [42, 'world'] + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should upcast with empty', () => { + const value = [] as any[] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should append with less than tuple length', () => { + const value = [42] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [42, '']) + }) + + it('Should truncate with greater than tuple length', () => { + const value = [42, '', true] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [42, '']) + }) + + it('Should preserve and patch invalid element', () => { + const value = [{}, 'hello'] + const result = Value.Cast(T, value) + Assert.deepEqual(result, [0, 'hello']) + }) +}) diff --git a/test/runtime/value/cast/uint8array.ts b/test/runtime/value/cast/uint8array.ts new file mode 100644 index 000000000..edcdef5de --- /dev/null +++ b/test/runtime/value/cast/uint8array.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Uint8Array', () => { + const T = Type.Uint8Array({ default: new Uint8Array([0, 1, 2, 3]) }) + const E = new Uint8Array([0, 1, 2, 3]) + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = new Uint8Array([6, 7, 8, 9, 10]) + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) +}) diff --git a/test/runtime/value/cast/undefined.ts b/test/runtime/value/cast/undefined.ts new file mode 100644 index 000000000..62de89959 --- /dev/null +++ b/test/runtime/value/cast/undefined.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Undefined', () => { + const T = Type.Undefined() + const E = undefined + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preseve', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, undefined) + }) +}) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts new file mode 100644 index 000000000..5998527da --- /dev/null +++ b/test/runtime/value/cast/union.ts @@ -0,0 +1,125 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Union', () => { + const A = Type.Object( + { + type: Type.Literal('A'), + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const B = Type.Object( + { + type: Type.Literal('B'), + a: Type.String(), + b: Type.String(), + c: Type.String(), + }, + { additionalProperties: false }, + ) + const T = Type.Union([A, B]) + + const E = { + type: 'A', + x: 0, + y: 0, + z: 0, + } + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve A', () => { + const value = { type: 'A', x: 1, y: 2, z: 3 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should preserve B', () => { + const value = { type: 'B', a: 'a', b: 'b', c: 'c' } + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should infer through heuristics #1', () => { + const value = { type: 'A', a: 'a', b: 'b', c: 'c' } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + }) + + it('Should infer through heuristics #2', () => { + const value = { type: 'B', x: 1, y: 2, z: 3 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + }) + + it('Should infer through heuristics #3', () => { + const value = { type: 'A', a: 'a', b: 'b', c: null } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + }) + + it('Should infer through heuristics #4', () => { + const value = { type: 'B', x: 1, y: 2, z: {} } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + }) + + it('Should infer through heuristics #5', () => { + const value = { type: 'B', x: 1, y: 2, z: null } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + }) + + it('Should infer through heuristics #6', () => { + const value = { x: 1 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) + }) + + it('Should infer through heuristics #7', () => { + const value = { a: null } // property existing should contribute + const result = Value.Cast(T, value) + Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + }) +}) diff --git a/test/runtime/value/cast/unknown.ts b/test/runtime/value/cast/unknown.ts new file mode 100644 index 000000000..5a98faa9c --- /dev/null +++ b/test/runtime/value/cast/unknown.ts @@ -0,0 +1,50 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Unknown', () => { + const T = Type.Unknown() + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from boolean', () => { + const value = false + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, value) + }) + + it('Should preserve', () => { + const value = { a: 1, b: 2 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { a: 1, b: 2 }) + }) +}) diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts new file mode 100644 index 000000000..33e53e02d --- /dev/null +++ b/test/runtime/value/cast/void.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Void', () => { + const T = Type.Void() + const E = null + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, null) + }) +}) diff --git a/test/runtime/value/check/any.ts b/test/runtime/value/check/any.ts new file mode 100644 index 000000000..15f31b510 --- /dev/null +++ b/test/runtime/value/check/any.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Any', () => { + const T = Type.Any() + it('Should pass string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts new file mode 100644 index 000000000..5b7b327e1 --- /dev/null +++ b/test/runtime/value/check/array.ts @@ -0,0 +1,33 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Array', () => { + it('Should pass number array', () => { + const T = Type.Array(Type.Number()) + const value = [1, 2, 3] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail number array', () => { + const T = Type.Array(Type.Number()) + const value = ['a', 'b', 'c'] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass object array', () => { + const T = Type.Array(Type.Object({ x: Type.Number() })) + const value = [{ x: 1 }, { x: 1 }, { x: 1 }] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail object array', () => { + const T = Type.Array(Type.Object({ x: Type.Number() })) + const value = [{ x: 1 }, { x: 1 }, 1] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/boolean.ts b/test/runtime/value/check/boolean.ts new file mode 100644 index 000000000..003bc296a --- /dev/null +++ b/test/runtime/value/check/boolean.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Boolean', () => { + const T = Type.Boolean() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/enum.ts b/test/runtime/value/check/enum.ts new file mode 100644 index 000000000..505625ca9 --- /dev/null +++ b/test/runtime/value/check/enum.ts @@ -0,0 +1,29 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Enum', () => { + enum Foo { + A = 1, + B = 2, + } + const T = Type.Enum(Foo) + + it('Should pass enum option A', () => { + const value = Foo.A + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should pass enum option B', () => { + const value = Foo.A + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail unknown value', () => { + const value = 'unknown' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts new file mode 100644 index 000000000..e617cfa64 --- /dev/null +++ b/test/runtime/value/check/index.ts @@ -0,0 +1,22 @@ +import './any' +import './array' +import './boolean' +import './enum' +import './integer' +import './intersect' +import './keyof' +import './literal' +import './never' +import './null' +import './number' +import './object' +import './rec' +import './record' +import './regex' +import './string' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts new file mode 100644 index 000000000..27ab4d396 --- /dev/null +++ b/test/runtime/value/check/integer.ts @@ -0,0 +1,19 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Integer', () => { + const T = Type.Integer() + + it('Should pass integer', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail integer', () => { + const value = 3.14 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/intersect.ts b/test/runtime/value/check/intersect.ts new file mode 100644 index 000000000..c5f747b35 --- /dev/null +++ b/test/runtime/value/check/intersect.ts @@ -0,0 +1,82 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Intersect', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + const T = Type.Intersect([A, B]) + + it('Should pass intersect', () => { + const value = { + x: 1, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail intersect with invalid property', () => { + const value = { + x: true, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail intersect with missing property', () => { + const value = { + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail intersect with primitive value', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass intersect with optional properties', () => { + const A = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + const T = Type.Intersect([A, B]) + const value = { + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/keyof.ts b/test/runtime/value/check/keyof.ts new file mode 100644 index 000000000..132446d60 --- /dev/null +++ b/test/runtime/value/check/keyof.ts @@ -0,0 +1,37 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/KeyOf', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + + it('Should pass keyof', () => { + const value = 'x' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail keyof', () => { + const value = 'w' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail keyof with undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail keyof with null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/literal.ts b/test/runtime/value/check/literal.ts new file mode 100644 index 000000000..84667b1e0 --- /dev/null +++ b/test/runtime/value/check/literal.ts @@ -0,0 +1,27 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Literal', () => { + const T = Type.Literal('hello') + it('Should pass literal', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail literal', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail literal with undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail literal with null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/never.ts b/test/runtime/value/check/never.ts new file mode 100644 index 000000000..4f4948d6a --- /dev/null +++ b/test/runtime/value/check/never.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Never', () => { + const T = Type.Never() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/null.ts b/test/runtime/value/check/null.ts new file mode 100644 index 000000000..f2cbc93ff --- /dev/null +++ b/test/runtime/value/check/null.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Null', () => { + const T = Type.Null() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts new file mode 100644 index 000000000..f6b34bebf --- /dev/null +++ b/test/runtime/value/check/number.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Number', () => { + const T = Type.Number() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts new file mode 100644 index 000000000..25ac7265f --- /dev/null +++ b/test/runtime/value/check/object.ts @@ -0,0 +1,101 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Object', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + + it('Should pass object', () => { + const value = { + x: 1, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail object with additional properties', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const value = { + x: 1, + y: 1, + z: 1, + a: 1, + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail object with invalid property', () => { + const value = { + x: true, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail object with missing property', () => { + const value = { + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass object with optional properties', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + const value = { + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail object with null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail object with undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/rec.ts b/test/runtime/value/check/rec.ts new file mode 100644 index 000000000..4659b7f8c --- /dev/null +++ b/test/runtime/value/check/rec.ts @@ -0,0 +1,69 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Recursive', () => { + const T = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + + it('Should pass recursive', () => { + const value = { + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] }, + { id: 'D', nodes: [] }, + ], + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail recursive with invalid id', () => { + const value = { + id: 'A', + nodes: [ + { id: 1, nodes: [] }, + { id: 'C', nodes: [] }, + { id: 'D', nodes: [] }, + ], + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail rec with invalid nodes', () => { + const value = { + id: 'A', + nodes: [ + { id: 'B', nodes: 1 }, + { id: 'C', nodes: [] }, + { id: 'D', nodes: [] }, + ], + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail recursive with missing id', () => { + const value = { + id: 'A', + nodes: [{ nodes: [] }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail rec with missing nodes', () => { + const value = { + id: 'A', + nodes: [{ id: 'B' }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts new file mode 100644 index 000000000..fe58df049 --- /dev/null +++ b/test/runtime/value/check/record.ts @@ -0,0 +1,83 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Record', () => { + it('Should pass record', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const value = { + position: { + x: 1, + y: 2, + z: 3, + }, + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail record with missing property', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const value = { + position: { + x: 1, + y: 2, + }, + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail record with invalid property', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const value = { + position: { + x: 1, + y: 2, + z: '3', + }, + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass record with optional property', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Optional(Type.Number()), + }), + ) + const value = { + position: { + x: 1, + y: 2, + }, + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/regex.ts b/test/runtime/value/check/regex.ts new file mode 100644 index 000000000..343e10980 --- /dev/null +++ b/test/runtime/value/check/regex.ts @@ -0,0 +1,19 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/RegEx', () => { + it('Should pass regex', () => { + const T = Type.RegEx(/foo/) + const value = 'foo' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail regex', () => { + const T = Type.RegEx(/foo/) + const value = 'bar' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/string.ts b/test/runtime/value/check/string.ts new file mode 100644 index 000000000..85737681a --- /dev/null +++ b/test/runtime/value/check/string.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/String', () => { + const T = Type.String() + it('Should pass string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/tuple.ts b/test/runtime/value/check/tuple.ts new file mode 100644 index 000000000..2d010d9aa --- /dev/null +++ b/test/runtime/value/check/tuple.ts @@ -0,0 +1,40 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Tuple', () => { + it('Should pass tuple', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail when tuple is less than length', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const value = [1] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail when tuple is greater than length', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const value = [1, 1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass empty tuple', () => { + const T = Type.Tuple([]) + const value = [] as any[] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail empty tuple', () => { + const T = Type.Tuple([]) + const value = [1] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/uint8array.ts b/test/runtime/value/check/uint8array.ts new file mode 100644 index 000000000..f792d8147 --- /dev/null +++ b/test/runtime/value/check/uint8array.ts @@ -0,0 +1,33 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Uint8Array', () => { + it('Should pass uint8array', () => { + const T = Type.Uint8Array() + const value = new Uint8Array(4) + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail uint8array', () => { + const T = Type.Uint8Array() + const value = [0, 1, 2, 3] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail uint8array with undefined', () => { + const T = Type.Uint8Array() + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail uint8array with null', () => { + const T = Type.Uint8Array() + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/undefined.ts b/test/runtime/value/check/undefined.ts new file mode 100644 index 000000000..162022385 --- /dev/null +++ b/test/runtime/value/check/undefined.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Undefined', () => { + const T = Type.Undefined() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/union.ts b/test/runtime/value/check/union.ts new file mode 100644 index 000000000..ff822c895 --- /dev/null +++ b/test/runtime/value/check/union.ts @@ -0,0 +1,77 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Union', () => { + const A = Type.Object({ + type: Type.Literal('A'), + x: Type.Number(), + y: Type.Number(), + }) + + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.Boolean(), + y: Type.Boolean(), + }) + + const T = Type.Union([A, B]) + + it('Should pass union A', () => { + const value = { type: 'A', x: 1, y: 1 } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should pass union B', () => { + const value = { type: 'B', x: true, y: false } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail union A', () => { + const value = { type: 'A', x: true, y: false } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail union B', () => { + const value = { type: 'B', x: 1, y: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass union A with optional properties', () => { + const A = Type.Object({ + type: Type.Literal('A'), + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.Boolean(), + y: Type.Boolean(), + }) + const T = Type.Union([A, B]) + const value = { type: 'A' } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail union A with invalid optional properties', () => { + const A = Type.Object({ + type: Type.Literal('A'), + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.Boolean(), + y: Type.Boolean(), + }) + const T = Type.Union([A, B]) + const value = { type: 'A', x: true, y: false } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/check/unknown.ts b/test/runtime/value/check/unknown.ts new file mode 100644 index 000000000..1bb814596 --- /dev/null +++ b/test/runtime/value/check/unknown.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Unknown', () => { + const T = Type.Any() + it('Should pass string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should pass array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/void.ts b/test/runtime/value/check/void.ts new file mode 100644 index 000000000..b9d01c1f3 --- /dev/null +++ b/test/runtime/value/check/void.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Void', () => { + const T = Type.Void() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) +}) diff --git a/test/runtime/value/clone/clone.ts b/test/runtime/value/clone/clone.ts new file mode 100644 index 000000000..4fdcf9e04 --- /dev/null +++ b/test/runtime/value/clone/clone.ts @@ -0,0 +1,177 @@ +import { Value } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +describe('value/clone/Clone', () => { + // -------------------------------------------- + // ValueType + // -------------------------------------------- + it('Should clone null', () => { + const R = Value.Clone(null) + Assert.deepEqual(R, null) + }) + + it('Should clone undefined', () => { + const R = Value.Clone(undefined) + Assert.deepEqual(R, undefined) + }) + + it('Should clone number', () => { + const R = Value.Clone(1) + Assert.deepEqual(R, 1) + }) + + it('Should clone bigint', () => { + const R = Value.Clone(1n) + Assert.deepEqual(R, 1n) + }) + + it('Should clone boolean', () => { + const R = Value.Clone(true) + Assert.deepEqual(R, true) + }) + + it('Should clone string', () => { + const R = Value.Clone('hello') + Assert.deepEqual(R, 'hello') + }) + + it('Should clone symbol', () => { + const S = Symbol('hello') + const R = Value.Clone(S) + Assert.deepEqual(R, S) + }) + + // -------------------------------------------- + // ObjectType + // -------------------------------------------- + + it('Should clone object #1', () => { + const V = { + x: 1, + y: 2, + z: 3, + } + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone object #2', () => { + const V = { + x: 1, + y: 2, + z: 3, + w: { + a: 1, + b: 2, + c: 3, + }, + } + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone object #3', () => { + const V = { + x: 1, + y: 2, + z: 3, + w: [0, 1, 2, 3, 4], + } + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + // -------------------------------------------- + // ArrayType + // -------------------------------------------- + it('Should clone array #1', () => { + const V = [1, 2, 3, 4] + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + it('Should clone array #2', () => { + const V = [ + [1, 2, 3], + [1, 2, 3], + [1, 2, 3], + [1, 2, 3], + ] + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + it('Should clone array #3', () => { + const V = [ + { x: 1, y: 2, z: 3 }, + { x: 1, y: 2, z: 3 }, + { x: 1, y: 2, z: 3 }, + { x: 1, y: 2, z: 3 }, + ] + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Int8Array', () => { + const V = new Int8Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Uint8Array', () => { + const V = new Uint8Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Uint8ClampedArray', () => { + const V = new Uint8ClampedArray([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Int16Array', () => { + const V = new Int16Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Uint16Array', () => { + const V = new Uint16Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Int32Array', () => { + const V = new Int32Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Uint32Array', () => { + const V = new Int32Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Float32Array', () => { + const V = new Float32Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone Float64Array', () => { + const V = new Float64Array([1, 2, 3, 4]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone BigInt64Array', () => { + const V = new BigInt64Array([1n, 2n, 3n, 4n]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) + + it('Should clone BigUint64Array', () => { + const V = new BigUint64Array([1n, 2n, 3n, 4n]) + const R = Value.Clone(V) + Assert.deepEqual(R, V) + }) +}) diff --git a/test/runtime/value/clone/index.ts b/test/runtime/value/clone/index.ts new file mode 100644 index 000000000..cd798158e --- /dev/null +++ b/test/runtime/value/clone/index.ts @@ -0,0 +1 @@ +import './clone' diff --git a/test/runtime/value/create/any.ts b/test/runtime/value/create/any.ts new file mode 100644 index 000000000..bbd59d47d --- /dev/null +++ b/test/runtime/value/create/any.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Any', () => { + it('Should create value', () => { + const T = Type.Any() + Assert.deepEqual(Value.Create(T), {}) + }) + it('Should create default', () => { + const T = Type.Any({ default: 1 }) + Assert.deepEqual(Value.Create(T), 1) + }) +}) diff --git a/test/runtime/value/create/array.ts b/test/runtime/value/create/array.ts new file mode 100644 index 000000000..1d97755a1 --- /dev/null +++ b/test/runtime/value/create/array.ts @@ -0,0 +1,18 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Any', () => { + it('Should create value', () => { + const T = Type.Array(Type.String()) + Assert.deepEqual(Value.Create(T), []) + }) + it('Should create default', () => { + const T = Type.Array(Type.String(), { default: ['1'] }) + Assert.deepEqual(Value.Create(T), ['1']) + }) + it('Should create with minItems', () => { + const T = Type.Array(Type.String(), { minItems: 4 }) + Assert.deepEqual(Value.Create(T), ['', '', '', '']) + }) +}) diff --git a/test/runtime/value/create/boolean.ts b/test/runtime/value/create/boolean.ts new file mode 100644 index 000000000..824641724 --- /dev/null +++ b/test/runtime/value/create/boolean.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Boolean', () => { + it('Should create value', () => { + const T = Type.Boolean() + Assert.deepEqual(Value.Create(T), false) + }) + it('Should create default', () => { + const T = Type.Any({ default: true }) + Assert.deepEqual(Value.Create(T), true) + }) +}) diff --git a/test/runtime/value/create/constructor.ts b/test/runtime/value/create/constructor.ts new file mode 100644 index 000000000..b4c9b7701 --- /dev/null +++ b/test/runtime/value/create/constructor.ts @@ -0,0 +1,38 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Constructor', () => { + it('Should create value', () => { + const T = Type.Constructor( + [], + Type.Object({ + test: Type.Function([], Type.Number({ default: 123 })), + }), + ) + const C = Value.Create(T) + const I = new C() + const R = I.test() + Assert.deepEqual(R, 123) + }) + + it('Should create default', () => { + const T = Type.Constructor( + [], + Type.Object({ + test: Type.Function([], Type.Number({ default: 123 })), + }), + { + default: class { + test() { + return 321 + } + }, + }, + ) + const C = Value.Create(T) + const I = new C() + const R = I.test() + Assert.deepEqual(R, 321) + }) +}) diff --git a/test/runtime/value/create/enum.ts b/test/runtime/value/create/enum.ts new file mode 100644 index 000000000..b6dcb55ca --- /dev/null +++ b/test/runtime/value/create/enum.ts @@ -0,0 +1,22 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Boolean', () => { + it('Should create value', () => { + enum Foo { + A, + B, + } + const T = Type.Enum(Foo) + Assert.deepEqual(Value.Create(T), Foo.A) + }) + it('Should create default', () => { + enum Foo { + A, + B, + } + const T = Type.Enum(Foo, { default: Foo.B }) + Assert.deepEqual(Value.Create(T), Foo.B) + }) +}) diff --git a/test/runtime/value/create/function.ts b/test/runtime/value/create/function.ts new file mode 100644 index 000000000..6315bd203 --- /dev/null +++ b/test/runtime/value/create/function.ts @@ -0,0 +1,19 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Function', () => { + it('Should create value', () => { + const T = Type.Function([], Type.Number({ default: 123 })) + const F = Value.Create(T) + const R = F() + Assert.deepEqual(R, 123) + }) + + it('Should create default', () => { + const T = Type.Function([], Type.Number({ default: 123 }), { default: () => 321 }) + const F = Value.Create(T) + const R = F() + Assert.deepEqual(R, 321) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts new file mode 100644 index 000000000..db38f47ad --- /dev/null +++ b/test/runtime/value/create/index.ts @@ -0,0 +1,24 @@ +import './any' +import './array' +import './boolean' +import './constructor' +import './enum' +import './function' +import './integer' +import './intersect' +import './keyof' +import './literal' +import './never' +import './null' +import './number' +import './object' +import './rec' +import './record' +import './regex' +import './string' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/create/integer.ts b/test/runtime/value/create/integer.ts new file mode 100644 index 000000000..3643a1f54 --- /dev/null +++ b/test/runtime/value/create/integer.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Integer', () => { + it('Should create value', () => { + const T = Type.Integer() + Assert.deepEqual(Value.Create(T), 0) + }) + it('Should create default', () => { + const T = Type.Integer({ default: 7 }) + Assert.deepEqual(Value.Create(T), 7) + }) +}) diff --git a/test/runtime/value/create/intersect.ts b/test/runtime/value/create/intersect.ts new file mode 100644 index 000000000..efa66c5b1 --- /dev/null +++ b/test/runtime/value/create/intersect.ts @@ -0,0 +1,66 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Intersect', () => { + it('Should create value', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.Number(), + b: Type.Number(), + c: Type.Number(), + }) + const T = Type.Intersect([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + a: 0, + b: 0, + c: 0, + }) + }) + it('Should create default', () => { + const A = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + z: Type.Number({ default: 3 }), + }) + const B = Type.Object({ + a: Type.Number({ default: 4 }), + b: Type.Number({ default: 5 }), + c: Type.Number({ default: 6 }), + }) + const T = Type.Intersect([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + a: 4, + b: 5, + c: 6, + }) + }) + it('Should create default and omit optionals', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.Number()), + c: Type.Optional(Type.Number()), + }) + const T = Type.Intersect([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + }) + }) +}) diff --git a/test/runtime/value/create/keyof.ts b/test/runtime/value/create/keyof.ts new file mode 100644 index 000000000..6b498bb15 --- /dev/null +++ b/test/runtime/value/create/keyof.ts @@ -0,0 +1,25 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/KeyOf', () => { + it('Should create value', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + Assert.deepEqual(Value.Create(T), 'x') + }) + it('Should create default', () => { + const T = Type.KeyOf( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + { default: 'y' }, + ) + Assert.deepEqual(Value.Create(T), 'y') + }) +}) diff --git a/test/runtime/value/create/literal.ts b/test/runtime/value/create/literal.ts new file mode 100644 index 000000000..591799b39 --- /dev/null +++ b/test/runtime/value/create/literal.ts @@ -0,0 +1,18 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/KeyOf', () => { + it('Should create literal string', () => { + const T = Type.Literal('hello') + Assert.deepEqual(Value.Create(T), 'hello') + }) + it('Should create literal number', () => { + const T = Type.Literal(1) + Assert.deepEqual(Value.Create(T), 1) + }) + it('Should create literal boolean', () => { + const T = Type.Literal(true) + Assert.deepEqual(Value.Create(T), true) + }) +}) diff --git a/test/runtime/value/create/never.ts b/test/runtime/value/create/never.ts new file mode 100644 index 000000000..437ff3ef3 --- /dev/null +++ b/test/runtime/value/create/never.ts @@ -0,0 +1,10 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Never', () => { + it('Should create value', () => { + const T = Type.Never() + Assert.throws(() => Value.Create(T)) + }) +}) diff --git a/test/runtime/value/create/null.ts b/test/runtime/value/create/null.ts new file mode 100644 index 000000000..4b11e6d1a --- /dev/null +++ b/test/runtime/value/create/null.ts @@ -0,0 +1,10 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Null', () => { + it('Should create value', () => { + const T = Type.Null() + Assert.deepEqual(Value.Create(T), null) + }) +}) diff --git a/test/runtime/value/create/number.ts b/test/runtime/value/create/number.ts new file mode 100644 index 000000000..7be03f37d --- /dev/null +++ b/test/runtime/value/create/number.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Number', () => { + it('Should create value', () => { + const T = Type.Number() + Assert.deepEqual(Value.Create(T), 0) + }) + it('Should create default', () => { + const T = Type.Number({ default: 7 }) + Assert.deepEqual(Value.Create(T), 7) + }) +}) diff --git a/test/runtime/value/create/object.ts b/test/runtime/value/create/object.ts new file mode 100644 index 000000000..50b41ee3b --- /dev/null +++ b/test/runtime/value/create/object.ts @@ -0,0 +1,75 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Object', () => { + it('Should create value', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + }) + }) + + it('Should create value with optional properties', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }) + Assert.deepEqual(Value.Create(T), {}) + }) + + it('Should create default with default properties', () => { + const T = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + z: Type.Number({ default: 3 }), + }) + Assert.deepEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + }) + }) + + it('Should create nested object', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + w: Type.Object({ + x: Type.Number({ default: 7 }), + y: Type.Number(), + z: Type.Number(), + }), + }) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + w: { x: 7, y: 0, z: 0 }, + }) + }) + + it('Should create with default', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { default: { x: 1, y: 2, z: 3 } }, + ) + Assert.deepEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + }) + }) +}) diff --git a/test/runtime/value/create/rec.ts b/test/runtime/value/create/rec.ts new file mode 100644 index 000000000..070169062 --- /dev/null +++ b/test/runtime/value/create/rec.ts @@ -0,0 +1,30 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Recursive', () => { + it('Should create value', () => { + const T = Type.Recursive((Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + ) + Assert.deepEqual(Value.Create(T), { + id: '', + nodes: [], + }) + }) + + it('Should create default', () => { + const T = Type.Recursive( + (Self) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Self), + }), + { default: 7 }, + ) + Assert.deepEqual(Value.Create(T), 7) + }) +}) diff --git a/test/runtime/value/create/record.ts b/test/runtime/value/create/record.ts new file mode 100644 index 000000000..c19d8555f --- /dev/null +++ b/test/runtime/value/create/record.ts @@ -0,0 +1,18 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Record', () => { + it('Should create value', () => { + const T = Type.Record(Type.String(), Type.Object({})) + Assert.deepEqual(Value.Create(T), {}) + }) + it('Should create default', () => { + const T = Type.Record(Type.String(), Type.Object({}), { + default: { + x: {}, + }, + }) + Assert.deepEqual(Value.Create(T), { x: {} }) + }) +}) diff --git a/test/runtime/value/create/regex.ts b/test/runtime/value/create/regex.ts new file mode 100644 index 000000000..78d535a2d --- /dev/null +++ b/test/runtime/value/create/regex.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/RegEx', () => { + it('Should throw without a default value', () => { + Assert.throws(() => { + const T = Type.RegEx(/foo/) + Value.Create(T) + }) + }) + it('Should create default', () => { + const T = Type.RegEx(/foo/, { default: 'foo' }) + Assert.deepEqual(Value.Create(T), 'foo') + }) +}) diff --git a/test/runtime/value/create/string.ts b/test/runtime/value/create/string.ts new file mode 100644 index 000000000..d5a7bcd77 --- /dev/null +++ b/test/runtime/value/create/string.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/String', () => { + it('Should create value', () => { + const T = Type.String() + Assert.deepEqual(Value.Create(T), 0) + }) + it('Should create default', () => { + const T = Type.String({ default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) +}) diff --git a/test/runtime/value/create/tuple.ts b/test/runtime/value/create/tuple.ts new file mode 100644 index 000000000..f2c91ccdb --- /dev/null +++ b/test/runtime/value/create/tuple.ts @@ -0,0 +1,22 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Tuple', () => { + it('Should create value', () => { + const T = Type.Tuple([Type.Number(), Type.String()]) + Assert.deepEqual(Value.Create(T), [0, '']) + }) + it('Should create default', () => { + const T = Type.Tuple([Type.Number(), Type.String()], { default: [7, 'hello'] }) + Assert.deepEqual(Value.Create(T), [7, 'hello']) + }) + it('Should create default elements', () => { + const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })]) + Assert.deepEqual(Value.Create(T), [7, 'hello']) + }) + it('Should create default by overriding elements', () => { + const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })], { default: [32, 'world'] }) + Assert.deepEqual(Value.Create(T), [32, 'world']) + }) +}) diff --git a/test/runtime/value/create/uint8array.ts b/test/runtime/value/create/uint8array.ts new file mode 100644 index 000000000..6effa6d4c --- /dev/null +++ b/test/runtime/value/create/uint8array.ts @@ -0,0 +1,28 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Uint8Array', () => { + it('Should create value', () => { + const T = Type.Uint8Array() + const value = Value.Create(T) + Assert.isInstanceOf(value, Uint8Array) + Assert.equal(value.length, 0) + }) + + it('Should create default', () => { + const T = Type.Uint8Array({ default: new Uint8Array([0, 1, 2, 3]) }) + const value = Value.Create(T) + Assert.isInstanceOf(value, Uint8Array) + Assert.equal(value.length, 4) + Assert.deepEqual([value[0], value[1], value[2], value[3]], [0, 1, 2, 3]) + }) + + it('Should create with minByteLength', () => { + const T = Type.Uint8Array({ minByteLength: 4 }) + const value = Value.Create(T) + Assert.isInstanceOf(value, Uint8Array) + Assert.equal(value.length, 4) + Assert.deepEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) + }) +}) diff --git a/test/runtime/value/create/undefined.ts b/test/runtime/value/create/undefined.ts new file mode 100644 index 000000000..45e369ad4 --- /dev/null +++ b/test/runtime/value/create/undefined.ts @@ -0,0 +1,10 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Undefined', () => { + it('Should create value', () => { + const T = Type.Undefined() + Assert.deepEqual(Value.Create(T), undefined) + }) +}) diff --git a/test/runtime/value/create/union.ts b/test/runtime/value/create/union.ts new file mode 100644 index 000000000..63e14202f --- /dev/null +++ b/test/runtime/value/create/union.ts @@ -0,0 +1,51 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Union', () => { + it('Should create union Object', () => { + const A = Type.Object({ + type: Type.Literal('A'), + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.String(), + y: Type.String(), + z: Type.String(), + }) + const T = Type.Union([A, B]) + Assert.deepEqual(Value.Create(T), { + type: 'A', + x: 0, + y: 0, + z: 0, + }) + }) + + it('Should create union Null', () => { + const A = Type.Null() + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.String(), + y: Type.String(), + z: Type.String(), + }) + const T = Type.Union([A, B]) + Assert.deepEqual(Value.Create(T), null) + }) + + it('Should create union Array', () => { + const A = Type.Array(Type.String()) + const B = Type.Object({ + type: Type.Literal('B'), + x: Type.String(), + y: Type.String(), + z: Type.String(), + }) + const T = Type.Union([A, B]) + Assert.deepEqual(Value.Create(T), []) + }) +}) diff --git a/test/runtime/value/create/unknown.ts b/test/runtime/value/create/unknown.ts new file mode 100644 index 000000000..d86946c6c --- /dev/null +++ b/test/runtime/value/create/unknown.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Unknown', () => { + it('Should create value', () => { + const T = Type.Unknown() + Assert.deepEqual(Value.Create(T), {}) + }) + it('Should create default', () => { + const T = Type.Unknown({ default: 1 }) + Assert.deepEqual(Value.Create(T), 1) + }) +}) diff --git a/test/runtime/value/create/void.ts b/test/runtime/value/create/void.ts new file mode 100644 index 000000000..eff8379a7 --- /dev/null +++ b/test/runtime/value/create/void.ts @@ -0,0 +1,10 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Void', () => { + it('Should create value', () => { + const T = Type.Void() + Assert.deepEqual(Value.Create(T), null) + }) +}) diff --git a/test/runtime/value/delta/diff.ts b/test/runtime/value/delta/diff.ts new file mode 100644 index 000000000..5a86fccb4 --- /dev/null +++ b/test/runtime/value/delta/diff.ts @@ -0,0 +1,424 @@ +import { Value, Edit } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +// ----------------------------------------------------------------- +// Diff Factory +// ----------------------------------------------------------------- + +function Update(path: string, value: unknown): Edit { + return { type: 'update', path, value } as any +} + +function Insert(path: string, value: unknown): Edit { + return { type: 'insert', path, value } as any +} + +function Delete(path: string): Edit { + return { type: 'delete', path } as any +} + +describe('value/delta/Diff', () => { + // ---------------------------------------------------- + // Null + // ---------------------------------------------------- + it('Should diff NULL null to null', () => { + const A = null + const B = null + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL undefined to undefined', () => { + const A = undefined + const B = undefined + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL string to string', () => { + const A = 'hello' + const B = 'hello' + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL number to number', () => { + const A = 1 + const B = 1 + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL boolean to boolean', () => { + const A = true + const B = true + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL symbol to symbol', () => { + const S = Symbol('A') + const A = S + const B = S + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL object to object', () => { + const A = { x: 1, y: 2, z: 3 } + const B = { x: 1, y: 2, z: 3 } + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + it('Should diff NULL array to array', () => { + const A = [1, 2, 3] + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const E = [] as Edit[] + Assert.deepEqual(D, E) + }) + + // ---------------------------------------------------- + // Type Change Root + // ---------------------------------------------------- + + it('Should diff TYPE change number to null', () => { + const A = 1 + const B = null + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + + it('Should diff TYPE change null to undefined', () => { + const A = null + const B = undefined + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to number', () => { + const A = null + const B = 1 + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to boolean', () => { + const A = null + const B = true + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to string', () => { + const A = null + const B = 'hello' + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to symbol', () => { + const A = null + const B = Symbol('A') + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to object', () => { + const A = null + const B = { x: 1, y: 1, z: 1 } + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + it('Should diff TYPE change null to array', () => { + const A = null + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + // ---------------------------------------------------- + // Value Change Root + // ---------------------------------------------------- + + it('Should diff VALUE change number', () => { + const A = 1 + const B = 2 + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + + it('Should diff VALUE change boolean', () => { + const A = false + const B = true + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + + it('Should diff VALUE change string', () => { + const A = 'hello' + const B = 'world' + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + + it('Should diff VALUE change symbol', () => { + const A = Symbol('A') + const B = Symbol('B') + const D = Value.Diff(A, B) + const E = [Update('', B)] + Assert.deepEqual(D, E) + }) + + // ---------------------------------------------------- + // Array + // ---------------------------------------------------- + + it('Should diff ELEMENT update', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 9] + const D = Value.Diff(A, B) + const E = [Update('/3', 9)] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT push', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 4, 5] + const D = Value.Diff(A, B) + const E = [Insert('/4', 5)] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT push twice', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 4, 5, 6] + const D = Value.Diff(A, B) + const E = [Insert('/4', 5), Insert('/5', 6)] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT pop', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const E = [Delete('/3')] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT pop twice', () => { + const A = [1, 2, 3, 4] + const B = [1, 2] + const D = Value.Diff(A, B) + const E = [Delete('/3'), Delete('/2')] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT unshift', () => { + const A = [1, 2, 3, 4] + const B = [2, 3, 4] + const D = Value.Diff(A, B) + const E = [Update('/0', 2), Update('/1', 3), Update('/2', 4), Delete('/3')] + Assert.deepEqual(D, E) + }) + + it('Should diff ELEMENT unshift twice', () => { + const A = [1, 2, 3, 4] + const B = [3, 4] + const D = Value.Diff(A, B) + const E = [Update('/0', 3), Update('/1', 4), Delete('/3'), Delete('/2')] + Assert.deepEqual(D, E) + }) + + // ---------------------------------------------------- + // Object + // ---------------------------------------------------- + + it('Should diff PROPERTY insert', () => { + const A = { x: 1, y: 1 } + const B = { x: 1, y: 1, z: 1 } + const D = Value.Diff(A, B) + const E = [Insert('/z', 1)] + Assert.deepEqual(D, E) + }) + it('Should diff PROPERTY delete', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 1, y: 1 } + const D = Value.Diff(A, B) + const E = [Delete('/z')] + Assert.deepEqual(D, E) + }) + it('Should diff PROPERTY update', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 1, y: 1, z: 2 } + const D = Value.Diff(A, B) + const E = [Update('/z', 2)] + Assert.deepEqual(D, E) + }) + it('Should diff PROPERTY all values', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 2, y: 2, z: 2 } + const D = Value.Diff(A, B) + const E = [Update('/x', 2), Update('/y', 2), Update('/z', 2)] + Assert.deepEqual(D, E) + }) + it('Should diff PROPERTY all delete, all insert', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { a: 2, b: 2, c: 2 } + const D = Value.Diff(A, B) + const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] + Assert.deepEqual(D, E) + }) + + it('Should diff PROPERTY update, insert and delete order preserved', () => { + const A = { x: 1, y: 1, z: 1, w: 1 } + const B = { a: 2, b: 2, c: 2, w: 2 } + const D = Value.Diff(A, B) + const E = [Update('/w', 2), Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] + Assert.deepEqual(D, E) + }) + + // ---------------------------------------------------- + // Object Nested + // ---------------------------------------------------- + + it('Should diff NESTED OBJECT diff type change update', () => { + const A = { v: 1 } + const B = { v: { x: 1, y: 1, z: 1 } } + const D = Value.Diff(A, B) + const E = [Update('/v', B.v)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED OBJECT diff value change update', () => { + const A = { v: 1 } + const B = { v: 2 } + const D = Value.Diff(A, B) + const E = [Update('/v', B.v)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED OBJECT diff partial property update', () => { + const A = { v: { x: 1, y: 1, z: 1 } } + const B = { v: { x: 2, y: 2, z: 2 } } + const D = Value.Diff(A, B) + const E = [Update('/v/x', B.v.x), Update('/v/y', B.v.y), Update('/v/z', B.v.z)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED OBJECT diff partial property insert', () => { + const A = { v: { x: 1, y: 1, z: 1 } } + const B = { v: { x: 1, y: 1, z: 1, w: 1 } } + const D = Value.Diff(A, B) + const E = [Insert('/v/w', B.v.w)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED OBJECT diff partial property delete', () => { + const A = { v: { x: 1, y: 1, z: 1 } } + const B = { v: { x: 1, y: 1 } } + const D = Value.Diff(A, B) + const E = [Delete('/v/z')] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED OBJECT ordered diff - update, insert and delete', () => { + const A = { v: { x: 1, y: 1 } } + const B = { v: { x: 2, w: 2 } } + const D = Value.Diff(A, B) + const E = [Update('/v/x', B.v.x), Insert('/v/w', B.v.w), Delete('/v/y')] + Assert.deepEqual(D, E) + }) + + // ---------------------------------------------------- + // Array Nested + // ---------------------------------------------------- + + it('Should diff NESTED ARRAY object diff type change update', () => { + const A = [{ v: 1 }] + const B = [{ v: { x: 1, y: 1, z: 1 } }] + const D = Value.Diff(A, B) + const E = [Update('/0/v', B[0].v)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED ARRAY object diff value change update', () => { + const A = [{ v: 1 }] + const B = [{ v: 2 }] + const D = Value.Diff(A, B) + const E = [Update('/0/v', B[0].v)] + Assert.deepEqual(D, E) + }) + it('Should diff NESTED ARRAY object diff partial property update', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 2, y: 2, z: 2 } }] + const D = Value.Diff(A, B) + const E = [Update('/0/v/x', B[0].v.x), Update('/0/v/y', B[0].v.y), Update('/0/v/z', B[0].v.z)] + Assert.deepEqual(D, E) + }) + it('Should diff NESTED ARRAY object diff partial property insert', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] + const D = Value.Diff(A, B) + const E = [Insert('/0/v/w', B[0].v.w)] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED ARRAY object diff partial property delete', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 1, y: 1 } }] + const D = Value.Diff(A, B) + const E = [Delete('/0/v/z')] + Assert.deepEqual(D, E) + }) + + it('Should diff NESTED ARRAY update, insert and delete order preserved', () => { + const A = [{ v: { x: 1, y: 1 } }] + const B = [{ v: { x: 2, w: 2 } }] + const D = Value.Diff(A, B) + const E = [Update('/0/v/x', B[0].v.x), Insert('/0/v/w', B[0].v.w), Delete('/0/v/y')] + Assert.deepEqual(D, E) + }) + + it('Should throw if attempting to diff a current value with symbol key', () => { + const A = [{ [Symbol('A')]: 1, v: { x: 1, y: 1 } }] + const B = [{ v: { x: 2, w: 2 } }] + Assert.throws(() => Value.Diff(A, B)) + }) + + it('Should throw if attempting to diff a next value with symbol key', () => { + const A = [{ v: { x: 1, y: 1 } }] + const B = [{ [Symbol('A')]: 1, v: { x: 2, w: 2 } }] + Assert.throws(() => Value.Diff(A, B)) + }) + + it('Should diff a Uint8Array (same size)', () => { + const A = new Uint8Array([0, 1, 2, 3]) + const B = new Uint8Array([0, 9, 2, 9]) + const D = Value.Diff(A, B) + const E = [Update('/1', 9), Update('/3', 9)] + Assert.deepEqual(D, E) + }) + + it('Should diff a Uint8Array (less than requires full update)', () => { + const A = new Uint8Array([0, 1, 2, 3]) + const B = new Uint8Array([0, 9, 2]) + const D = Value.Diff(A, B) + const E = [Update('', new Uint8Array([0, 9, 2]))] + Assert.deepEqual(D, E) + }) + + it('Should diff a Uint8Array (greater than requires full update)', () => { + const A = new Uint8Array([0, 1, 2, 3]) + const B = new Uint8Array([0, 9, 2, 3, 4]) + const D = Value.Diff(A, B) + const E = [Update('', new Uint8Array([0, 9, 2, 3, 4]))] + Assert.deepEqual(D, E) + }) +}) diff --git a/test/runtime/value/delta/index.ts b/test/runtime/value/delta/index.ts new file mode 100644 index 000000000..10b4755d9 --- /dev/null +++ b/test/runtime/value/delta/index.ts @@ -0,0 +1,2 @@ +import './diff' +import './patch' diff --git a/test/runtime/value/delta/patch.ts b/test/runtime/value/delta/patch.ts new file mode 100644 index 000000000..62a0c50e1 --- /dev/null +++ b/test/runtime/value/delta/patch.ts @@ -0,0 +1,447 @@ +import { Value } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +describe('value/delta/Patch', () => { + // ---------------------------------------------------- + // Null + // ---------------------------------------------------- + it('Should patch NULL null to null', () => { + const A = null + const B = null + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NULL undefined to undefined', () => { + const A = undefined + const B = undefined + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NULL string to string', () => { + const A = 'hello' + const B = 'hello' + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NULL number to number', () => { + const A = 1 + const B = 1 + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NULL boolean to boolean', () => { + const A = true + const B = true + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NULL symbol to symbol', () => { + const S = Symbol('A') + const A = S + const B = S + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NULL object to object', () => { + const A = { x: 1, y: 2, z: 3 } + const B = { x: 1, y: 2, z: 3 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NULL array to array', () => { + const A = [1, 2, 3] + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + // ---------------------------------------------------- + // Type Change Root + // ---------------------------------------------------- + + it('Should patch TYPE change number to null', () => { + const A = 1 + const B = null + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch TYPE change null to undefined', () => { + const A = null + const B = undefined + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to number', () => { + const A = null + const B = 1 + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to boolean', () => { + const A = null + const B = true + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to string', () => { + const A = null + const B = 'hello' + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to symbol', () => { + const A = null + const B = Symbol('A') + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to object', () => { + const A = null + const B = { x: 1, y: 1, z: 1 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change null to array', () => { + const A = null + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch TYPE change object to array', () => { + const A = { x: 1, y: 2 } + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch TYPE change array to object', () => { + const A = [1, 2, 3] + const B = { x: 1, y: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + // ---------------------------------------------------- + // Value Change Root + // ---------------------------------------------------- + + it('Should patch VALUE change number', () => { + const A = 1 + const B = 2 + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch VALUE change boolean', () => { + const A = false + const B = true + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch VALUE change string', () => { + const A = 'hello' + const B = 'world' + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch VALUE change symbol', () => { + const A = Symbol('A') + const B = Symbol('B') + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + // ---------------------------------------------------- + // Array + // ---------------------------------------------------- + + it('Should patch ELEMENT update', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 9] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT push', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 4, 5] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT push twice', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3, 4, 5, 6] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT pop', () => { + const A = [1, 2, 3, 4] + const B = [1, 2, 3] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT pop twice', () => { + const A = [1, 2, 3, 4] + const B = [1, 2] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT unshift', () => { + const A = [1, 2, 3, 4] + const B = [2, 3, 4] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch ELEMENT unshift twice', () => { + const A = [1, 2, 3, 4] + const B = [3, 4] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + // ---------------------------------------------------- + // Object + // ---------------------------------------------------- + + it('Should patch PROPERTY insert', () => { + const A = { x: 1, y: 1 } + const B = { x: 1, y: 1, z: 1 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch PROPERTY delete', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 1, y: 1 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch PROPERTY update', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 1, y: 1, z: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch PROPERTY all values', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { x: 2, y: 2, z: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch PROPERTY all delete, all insert', () => { + const A = { x: 1, y: 1, z: 1 } + const B = { a: 2, b: 2, c: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch PROPERTY update, insert and delete order preserved', () => { + const A = { x: 1, y: 1, z: 1, w: 1 } + const B = { a: 2, b: 2, c: 2, w: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + // ---------------------------------------------------- + // Object Nested + // ---------------------------------------------------- + + it('Should patch NESTED OBJECT diff type change update', () => { + const A = { v: 1 } + const B = { v: { x: 1, y: 1, z: 1 } } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NESTED OBJECT diff value change update', () => { + const A = { v: 1 } + const B = { v: 2 } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NESTED OBJECT diff partial property update', () => { + const A = { v: { x: 1, y: 1, z: 1 } } + const B = { v: { x: 2, y: 2, z: 2 } } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NESTED OBJECT update, insert and delete order preserved', () => { + const A = { v: { x: 1, y: 1 } } + const B = { v: { x: 2, w: 2 } } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + // ---------------------------------------------------- + // Array Nested + // ---------------------------------------------------- + + it('Should patch NESTED ARRAY object diff type change update', () => { + const A = [{ v: 1 }] + const B = [{ v: { x: 1, y: 1, z: 1 } }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NESTED ARRAY object diff value change update', () => { + const A = [{ v: 1 }] + const B = [{ v: 2 }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NESTED ARRAY object diff partial property update', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 2, y: 2, z: 2 } }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + it('Should patch NESTED ARRAY object diff partial property insert', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NESTED ARRAY object diff partial property delete', () => { + const A = [{ v: { x: 1, y: 1, z: 1 } }] + const B = [{ v: { x: 1, y: 1 } }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch NESTED ARRAY object ordered diff - update, insert and delete', () => { + const A = [{ v: { x: 1, y: 1 } }] + const B = [{ v: { x: 2, w: 2 } }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch Uint8Array (same size)', () => { + const A = [{ v: new Uint8Array([0, 1, 3]) }] + const B = [{ v: new Uint8Array([0, 1, 2]) }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch Uint8Array (less than size)', () => { + const A = [{ v: new Uint8Array([0, 1, 3]) }] + const B = [{ v: new Uint8Array([0, 1]) }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + + it('Should patch Uint8Array (greater than size)', () => { + const A = [{ v: new Uint8Array([0, 1, 3]) }] + const B = [{ v: new Uint8Array([0, 1, 2, 4]) }] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) + // ---------------------------------------------------- + // Mega Values + // ---------------------------------------------------- + + it('Should patch MEGA value', () => { + const A = [ + { a: { x: 1, y: 1 } }, + { b: { x: 1, y: 1, z: ['hello', 1, 2] } }, + { c: { x: 1, y: 1 } }, + { + d: [ + { a: 1 }, + { a: 1 }, + { a: 1 }, + [ + { a: { x: 1, y: 1 } }, + { b: { x: 1, y: 1, z: ['hello', true, true, 2] } }, + { c: { x: 1, y: 1 } }, + { + d: [{ a: 1 }, { a: 1 }, { a: 1 }], + }, + ], + ], + }, + ] + const B = [ + 1, + 2, + { a: { x: 1, y: 'a' } }, + { b: { x: true, y: 1, z: ['hello', true, true] } }, + { c: { x: 1, y: 1 } }, + { + d: [ + { a: 'hello' }, + 1, + 2, + { a: 1 }, + { a: 1 }, + [ + { a: { x: 1, y: 1 }, x: [1, 2, 3, 4] }, + { c: { x: 1, y: 1 } }, + { + d: [{ a: 1 }, { a: 2 }, 'hello'], + }, + ], + ], + }, + ] + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.deepEqual(B, P) + }) +}) diff --git a/test/runtime/value/equal/equal.ts b/test/runtime/value/equal/equal.ts new file mode 100644 index 000000000..b02583e6e --- /dev/null +++ b/test/runtime/value/equal/equal.ts @@ -0,0 +1,111 @@ +import { Value } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +describe('value/equal/Equal', () => { + it('Should equal null value', () => { + const R = Value.Equal(null, null) + Assert.deepEqual(R, true) + }) + + it('Should not equal null value', () => { + const R = Value.Equal(null, 'null') + Assert.deepEqual(R, false) + }) + + it('Should equal undefined value', () => { + const R = Value.Equal(undefined, undefined) + Assert.deepEqual(R, true) + }) + + it('Should not equal undefined value', () => { + const R = Value.Equal(undefined, 'undefined') + Assert.deepEqual(R, false) + }) + + it('Should equal symbol value', () => { + const S1 = Symbol.for('test') + const S2 = Symbol.for('test') + const R = Value.Equal(S1, S2) + Assert.deepEqual(R, true) + }) + + it('Should not equal symbol value', () => { + const S1 = Symbol('test') + const S2 = Symbol('test') + const R = Value.Equal(S1, S2) + Assert.deepEqual(R, false) + }) + + it('Should equal string value', () => { + const R = Value.Equal('hello', 'hello') + Assert.deepEqual(R, true) + }) + it('Should not equal string value', () => { + const R = Value.Equal('hello', 'world') + Assert.deepEqual(R, false) + }) + + it('Should equal number value', () => { + const R = Value.Equal(1, 1) + Assert.deepEqual(R, true) + }) + + it('Should not equal number value', () => { + const R = Value.Equal(1, 2) + Assert.deepEqual(R, false) + }) + + it('Should equal boolean value', () => { + const R = Value.Equal(true, true) + Assert.deepEqual(R, true) + }) + + it('Should not equal boolean value', () => { + const R = Value.Equal(true, false) + Assert.deepEqual(R, false) + }) + + it('Should equal array value', () => { + const R = Value.Equal([0, 1, 2], [0, 1, 2]) + Assert.deepEqual(R, true) + }) + + it('Should not equal array value', () => { + const R = Value.Equal([0, 1, 2], [0, 1, 3]) + Assert.deepEqual(R, false) + }) + + it('Should not equal array value with additional elements', () => { + const R = Value.Equal([0, 1, 2], [0, 1, 2, 3]) + Assert.deepEqual(R, false) + }) + + it('Should equal object value', () => { + const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3 }) + Assert.deepEqual(R, true) + }) + + it('Should not equal object value', () => { + const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 4 }) + Assert.deepEqual(R, false) + }) + + it('Should not equal object value with additional properties', () => { + const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3, w: 1 }) + Assert.deepEqual(R, false) + }) + + it('Should equal typed array value', () => { + const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 2])) + Assert.deepEqual(R, true) + }) + + it('Should not equal typed array value', () => { + const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 3])) + Assert.deepEqual(R, false) + }) + it('Should not equal typed array value with varying type', () => { + const R = Value.Equal(new Uint8Array([0, 1, 2]), new Int8Array([0, 1, 2])) + Assert.deepEqual(R, false) + }) +}) diff --git a/test/runtime/value/equal/index.ts b/test/runtime/value/equal/index.ts new file mode 100644 index 000000000..8175d94d7 --- /dev/null +++ b/test/runtime/value/equal/index.ts @@ -0,0 +1 @@ +import './equal' diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts new file mode 100644 index 000000000..2ca9e7172 --- /dev/null +++ b/test/runtime/value/index.ts @@ -0,0 +1,7 @@ +import './cast' +import './check' +import './clone' +import './create' +import './delta' +import './equal' +import './pointer' diff --git a/test/runtime/value/pointer/index.ts b/test/runtime/value/pointer/index.ts new file mode 100644 index 000000000..a4004f3be --- /dev/null +++ b/test/runtime/value/pointer/index.ts @@ -0,0 +1 @@ +import './pointer' diff --git a/test/runtime/value/pointer/pointer.ts b/test/runtime/value/pointer/pointer.ts new file mode 100644 index 000000000..a3854baa6 --- /dev/null +++ b/test/runtime/value/pointer/pointer.ts @@ -0,0 +1,397 @@ +import { ValuePointer } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +describe('value/pointer/Pointer', () => { + //----------------------------------------------- + // Format + //----------------------------------------------- + + it('Should produce correct format #1', () => { + const R = [...ValuePointer.Format('')] + Assert.deepEqual(R, []) + }) + + it('Should produce correct format #2', () => { + const R = [...ValuePointer.Format('a')] + Assert.deepEqual(R, ['a']) + }) + + it('Should produce correct format #3', () => { + const R = [...ValuePointer.Format('/')] + Assert.deepEqual(R, ['']) + }) + + it('Should produce correct format #4', () => { + const R = [...ValuePointer.Format('/x')] + Assert.deepEqual(R, ['x']) + }) + + it('Should produce correct format #5', () => { + const R = [...ValuePointer.Format('/x/')] + Assert.deepEqual(R, ['x', '']) + }) + + it('Should produce correct format #6', () => { + const R = [...ValuePointer.Format('/x//')] + Assert.deepEqual(R, ['x', '', '']) + }) + + it('Should produce correct format #7', () => { + const R = [...ValuePointer.Format('/x//y')] + Assert.deepEqual(R, ['x', '', 'y']) + }) + + it('Should produce correct format #8', () => { + const R = [...ValuePointer.Format('/x//y/')] + Assert.deepEqual(R, ['x', '', 'y', '']) + }) + + it('Should produce correct format #9', () => { + const R = [...ValuePointer.Format('/x/~0')] + Assert.deepEqual(R, ['x', '~']) + }) + + it('Should produce correct format #10', () => { + const R = [...ValuePointer.Format('/x/~1')] + Assert.deepEqual(R, ['x', '/']) + }) + + it('Should produce correct format #11', () => { + const R = [...ValuePointer.Format('/x/~0/')] + Assert.deepEqual(R, ['x', '~', '']) + }) + + it('Should produce correct format #12', () => { + const R = [...ValuePointer.Format('/x/~1/')] + Assert.deepEqual(R, ['x', '/', '']) + }) + + it('Should produce correct format #13', () => { + const R = [...ValuePointer.Format('/x/a~0b')] + Assert.deepEqual(R, ['x', 'a~b']) + }) + + it('Should produce correct format #14', () => { + const R = [...ValuePointer.Format('/x/a~1b')] + Assert.deepEqual(R, ['x', 'a/b']) + }) + + it('Should produce correct format #15', () => { + const R = [...ValuePointer.Format('/x/a~0b/')] + Assert.deepEqual(R, ['x', 'a~b', '']) + }) + + it('Should produce correct format #16', () => { + const R = [...ValuePointer.Format('/x/a~1b/')] + Assert.deepEqual(R, ['x', 'a/b', '']) + }) + + it('Should produce correct format #17', () => { + const R = [...ValuePointer.Format('/x/a~0b///y')] + Assert.deepEqual(R, ['x', 'a~b', '', '', 'y']) + }) + + it('Should produce correct format #18', () => { + const R = [...ValuePointer.Format('/x/a~1b///y')] + Assert.deepEqual(R, ['x', 'a/b', '', '', 'y']) + }) + + it('Should produce correct format #19', () => { + const R = [...ValuePointer.Format('/x/a~0b///')] + Assert.deepEqual(R, ['x', 'a~b', '', '', '']) + }) + + it('Should produce correct format #20', () => { + const R = [...ValuePointer.Format('/x/a~1b///')] + Assert.deepEqual(R, ['x', 'a/b', '', '', '']) + }) + + //----------------------------------------------- + // Get + //----------------------------------------------- + + it('Should get array #1', () => { + const V = [0, 1, 2, 3] + Assert.deepEqual(ValuePointer.Get(V, ''), [0, 1, 2, 3]) + Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) + Assert.deepEqual(ValuePointer.Get(V, '/0'), 0) + Assert.deepEqual(ValuePointer.Get(V, '/1'), 1) + Assert.deepEqual(ValuePointer.Get(V, '/2'), 2) + Assert.deepEqual(ValuePointer.Get(V, '/3'), 3) + }) + + it('Should get array #2', () => { + const V = [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }] + Assert.deepEqual(ValuePointer.Get(V, ''), [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]) + Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) + Assert.deepEqual(ValuePointer.Get(V, '/0'), { x: 0 }) + Assert.deepEqual(ValuePointer.Get(V, '/1'), { x: 1 }) + Assert.deepEqual(ValuePointer.Get(V, '/2'), { x: 2 }) + Assert.deepEqual(ValuePointer.Get(V, '/3'), { x: 3 }) + Assert.deepEqual(ValuePointer.Get(V, '/0/x'), 0) + Assert.deepEqual(ValuePointer.Get(V, '/1/x'), 1) + Assert.deepEqual(ValuePointer.Get(V, '/2/x'), 2) + Assert.deepEqual(ValuePointer.Get(V, '/3/x'), 3) + }) + + it('Should get object #1', () => { + const V = { x: 0, y: 1, z: 2 } + Assert.deepEqual(ValuePointer.Get(V, ''), { x: 0, y: 1, z: 2 }) + Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) + Assert.deepEqual(ValuePointer.Get(V, '/x'), 0) + Assert.deepEqual(ValuePointer.Get(V, '/y'), 1) + Assert.deepEqual(ValuePointer.Get(V, '/z'), 2) + }) + + it('Should get object #2', () => { + const V = { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } } + Assert.deepEqual(ValuePointer.Get(V, ''), { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } }) + Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) + Assert.deepEqual(ValuePointer.Get(V, '/x'), { x: 0 }) + Assert.deepEqual(ValuePointer.Get(V, '/y'), { x: 1 }) + Assert.deepEqual(ValuePointer.Get(V, '/z'), { x: 2 }) + }) + + it('Should get object #3', () => { + const V = { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } } + Assert.deepEqual(ValuePointer.Get(V, ''), { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } }) + Assert.deepEqual(ValuePointer.Get(V, '/'), { x: -1 }) + Assert.deepEqual(ValuePointer.Get(V, '/x'), { '': { x: 1 } }) + Assert.deepEqual(ValuePointer.Get(V, '/y'), { '': { x: 2 } }) + Assert.deepEqual(ValuePointer.Get(V, '/z'), { '': { x: 3 } }) + Assert.deepEqual(ValuePointer.Get(V, '/x/'), { x: 1 }) + Assert.deepEqual(ValuePointer.Get(V, '/y/'), { x: 2 }) + Assert.deepEqual(ValuePointer.Get(V, '/z/'), { x: 3 }) + Assert.deepEqual(ValuePointer.Get(V, '/x//x'), 1) + Assert.deepEqual(ValuePointer.Get(V, '/y//x'), 2) + Assert.deepEqual(ValuePointer.Get(V, '/z//x'), 3) + }) + + //----------------------------------------------- + // Has + //----------------------------------------------- + + it('Should return has true for undefined', () => { + const V = undefined + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for null', () => { + const V = null + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for object', () => { + const V = {} + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for array', () => { + const V: any[] = [] + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for string', () => { + const V = 'hello' + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for number', () => { + const V = 42 + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for boolean', () => { + const V = false + Assert.deepEqual(ValuePointer.Has(V, ''), true) + }) + + it('Should return has true for deeply nested', () => { + const V = { + '': { x: { y: { z: 1 } } }, + x: 1, + y: { x: 1 }, + z: [{ x: 1 }, { y: 1 }], + w: undefined, + n: null, + } + // exists + Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.deepEqual(ValuePointer.Has(V, '/'), true) + Assert.deepEqual(ValuePointer.Has(V, '//x'), true) + Assert.deepEqual(ValuePointer.Has(V, '//x/y'), true) + Assert.deepEqual(ValuePointer.Has(V, '//x/y/z'), true) + Assert.deepEqual(ValuePointer.Has(V, '/x'), true) + Assert.deepEqual(ValuePointer.Has(V, '/y/x'), true) + Assert.deepEqual(ValuePointer.Has(V, '/z'), true) + Assert.deepEqual(ValuePointer.Has(V, '/z/0'), true) + Assert.deepEqual(ValuePointer.Has(V, '/z/0/x'), true) + Assert.deepEqual(ValuePointer.Has(V, '/z/1'), true) + Assert.deepEqual(ValuePointer.Has(V, '/z/1/y'), true) + Assert.deepEqual(ValuePointer.Has(V, '/x'), true) + Assert.deepEqual(ValuePointer.Has(V, '/n'), true) + }) + + //----------------------------------------------- + // Set + //----------------------------------------------- + + it('Should throw when setting root', () => { + const V = {} + Assert.throws(() => ValuePointer.Set(V, '', { x: 1 })) + }) + + it('Should set array values', () => { + const V = [0, 1, 2] + ValuePointer.Set(V, '/0', 3) + ValuePointer.Set(V, '/1', 4) + ValuePointer.Set(V, '/2', 5) + Assert.deepEqual(V, [3, 4, 5]) + }) + + it('Should set object values', () => { + const V = { x: 0, y: 1, z: 2 } + ValuePointer.Set(V, '/x', 3) + ValuePointer.Set(V, '/y', 4) + ValuePointer.Set(V, '/z', 5) + Assert.deepEqual(V, { x: 3, y: 4, z: 5 }) + }) + + it('Should set object values recursively #1', () => { + const V = {} + ValuePointer.Set(V, '/x/y/z', 1) + Assert.deepEqual(V, { x: { y: { z: 1 } } }) + }) + + it('Should set object values recursively #2', () => { + const V = {} + ValuePointer.Set(V, '/x/0/y/z/', 1) + ValuePointer.Set(V, '/x/1/y/z/', 2) + Assert.deepEqual(V, { + x: { + 0: { + y: { + z: { + '': 1, + }, + }, + }, + 1: { + y: { + z: { + '': 2, + }, + }, + }, + }, + }) + }) + + //----------------------------------------------- + // Delete + //----------------------------------------------- + + it('Should throw when deleting root', () => { + const V = {} + Assert.throws(() => ValuePointer.Delete(V, '')) + }) + + it('Should delete object properties', () => { + const V = { + x: { x: 1, y: 2, z: 3 }, + y: { x: 3, y: 4, z: 5 }, + } + ValuePointer.Delete(V, '/x/y') + ValuePointer.Delete(V, '/y') + Assert.deepEqual(V, { x: { x: 1, z: 3 } }) + }) + + it('Should be a noop if property does not exist', () => { + const V = { + x: { x: 1, y: 2, z: 3 }, + y: { x: 3, y: 4, z: 5 }, + } + ValuePointer.Delete(V, '/x/w') + ValuePointer.Delete(V, '/w') + Assert.deepEqual(V, { + x: { x: 1, y: 2, z: 3 }, + y: { x: 3, y: 4, z: 5 }, + }) + }) + + it('Should not delete owner', () => { + const V = { x: { y: { z: 1 } } } + ValuePointer.Delete(V, '/x/y/z') + Assert.deepEqual(V, { x: { y: {} } }) + }) + + it('Should delete owner', () => { + const V = { x: { y: { z: 1 } } } + ValuePointer.Delete(V, '/x/y') + Assert.deepEqual(V, { x: {} }) + }) + + it('Should not throw if deleting null property', () => { + const V = { x: { y: null } } + ValuePointer.Delete(V, '/x/y/z') + Assert.deepEqual(V, { x: { y: null } }) + }) + + //----------------------------------------------- + // Escapes + //----------------------------------------------- + + it('Should support get ~0 pointer escape', () => { + const V = { + x: { '~': { x: 1 } }, + } + Assert.deepEqual(ValuePointer.Get(V, '/x/~0'), { x: 1 }) + }) + + it('Should support get ~1 pointer escape', () => { + const V = { + x: { '/': { x: 1 } }, + } + Assert.deepEqual(ValuePointer.Get(V, '/x/~1'), { x: 1 }) + }) + + it('Should support set ~0 pointer escape', () => { + const V = { + x: { '~': { x: 1 } }, + } + ValuePointer.Set(V, '/x/~0', { x: 2 }) + Assert.deepEqual(V, { + x: { '~': { x: 2 } }, + }) + }) + + it('Should support set ~1 pointer escape', () => { + const V = { + x: { '/': { x: 1 } }, + } + ValuePointer.Set(V, '/x/~1', { x: 2 }) + Assert.deepEqual(V, { + x: { '/': { x: 2 } }, + }) + }) + + it('Should support delete ~0 pointer escape', () => { + const V = { + x: { '~': { x: 1 } }, + } + ValuePointer.Delete(V, '/x/~0') + Assert.deepEqual(V, { + x: {}, + }) + }) + + it('Should support delete ~1 pointer escape', () => { + const V = { + x: { '/': { x: 1 } }, + } + ValuePointer.Delete(V, '/x/~1') + Assert.deepEqual(V, { + x: {}, + }) + }) +}) diff --git a/test/static/any.ts b/test/static/any.ts new file mode 100644 index 000000000..abe16019e --- /dev/null +++ b/test/static/any.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Any()).ToInfer() diff --git a/test/static/array.ts b/test/static/array.ts new file mode 100644 index 000000000..1b52abc7c --- /dev/null +++ b/test/static/array.ts @@ -0,0 +1,24 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Array(Type.String())).ToInfer() + +Expect( + Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Boolean(), + z: Type.String(), + }), + ), +).ToInfer< + { + x: number + y: boolean + z: string + }[] +>() + +Expect(Type.Array(Type.Array(Type.String()))).ToInfer() + +Expect(Type.Array(Type.Tuple([Type.String(), Type.Number()]))).ToInfer<[string, number][]>() diff --git a/test/static/assert.ts b/test/static/assert.ts new file mode 100644 index 000000000..b4ffbd33f --- /dev/null +++ b/test/static/assert.ts @@ -0,0 +1,8 @@ +import { Static, TSchema } from '@sinclair/typebox' + +export function Expect(schema: T) { + return { + ToInfer: >() => {}, + ToBe: () => {}, + } +} diff --git a/test/static/boolean.ts b/test/static/boolean.ts new file mode 100644 index 000000000..d1b9f4c79 --- /dev/null +++ b/test/static/boolean.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Boolean()).ToInfer() diff --git a/test/static/enum.ts b/test/static/enum.ts new file mode 100644 index 000000000..aee807c9f --- /dev/null +++ b/test/static/enum.ts @@ -0,0 +1,12 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +enum E { + A, + B = 'hello', + C = 42, +} + +const T = Type.Enum(E) + +Expect(T).ToBe>() // ? diff --git a/test/static/index.ts b/test/static/index.ts new file mode 100644 index 000000000..909c65591 --- /dev/null +++ b/test/static/index.ts @@ -0,0 +1,35 @@ +import './any' +import './array' +import './boolean' +import './constructor-parameters' +import './constructor' +import './emum' +import './function' +import './instance-type' +import './intersect' +import './keyof' +import './literal' +import './modifier' +import './namespace' +import './never' +import './null' +import './number' +import './object' +import './omit' +import './optional' +import './parameters' +import './partial' +import './pick' +import './readonly-optional' +import './readonly' +import './recursive' +import './record' +import './ref' +import './regex' +import './required' +import './return-type' +import './strict' +import './string' +import './tuple' +import './union' +import './unknown' diff --git a/test/static/intersect.ts b/test/static/intersect.ts new file mode 100644 index 000000000..fcbb1000b --- /dev/null +++ b/test/static/intersect.ts @@ -0,0 +1,51 @@ +import { Expect } from './assert' +import { Type, TOptional, TString } from '@sinclair/typebox' + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + const B = Type.Object({ + X: Type.Number(), + Y: Type.Number(), + }) + const T = Type.Intersect([A, B]) + + Expect(T).ToInfer< + { + A: string + B: string + } & { + X: number + Y: number + } + >() +} + +{ + const A = Type.Object({ + A: Type.Optional(Type.String()), + }) + const B = Type.Object({ + B: Type.String(), + }) + const T = Type.Intersect([A, B]) + + Expect(T.properties.A).ToBe>() + Expect(T.properties.B).ToBe() +} + +// https://github.com/sinclairzx81/typebox/issues/113 +// https://github.com/sinclairzx81/typebox/issues/187 +// { +// const A = Type.Object({ A: Type.String() }) +// const B = Type.Object({ B: Type.String() }) +// const C = Type.Object({ C: Type.String() }) +// const T = Type.Intersect([A, Type.Union([B, C])]) +// type T = Static +// const _0: T = { A: '', B: '' } +// const _1: T = { A: '', C: '' } +// const _3: T = { A: '', B: '', C: '' } +// Expect(T).ToBe<{ A: string } & ({ B: string, } | { C: string })>() +// } diff --git a/test/static/keyof.ts b/test/static/keyof.ts new file mode 100644 index 000000000..62d626057 --- /dev/null +++ b/test/static/keyof.ts @@ -0,0 +1,57 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + const K = Type.KeyOf( + Type.Object({ + A: Type.Null(), + B: Type.Null(), + C: Type.Null(), + }), + ) + Expect(K).ToInfer<'A' | 'B' | 'C'>() +} + +{ + const T = Type.Pick( + Type.Object({ + A: Type.Null(), + B: Type.Null(), + C: Type.Null(), + }), + ['A', 'B'], + ) + + const K = Type.KeyOf(T) + + Expect(K).ToInfer<'A' | 'B'>() +} + +{ + const T = Type.Omit( + Type.Object({ + A: Type.Null(), + B: Type.Null(), + C: Type.Null(), + }), + ['A', 'B'], + ) + + const K = Type.KeyOf(T) + + Expect(K).ToInfer<'C'>() +} + +{ + const T = Type.KeyOf( + Type.Omit( + Type.Object({ + A: Type.Null(), + B: Type.Null(), + C: Type.Null(), + }), + ['A', 'B'], + ), + ) + Expect(T).ToInfer<'C'>() +} diff --git a/test/static/literal.ts b/test/static/literal.ts new file mode 100644 index 000000000..461f43475 --- /dev/null +++ b/test/static/literal.ts @@ -0,0 +1,8 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Literal('hello')).ToInfer<'hello'>() + +Expect(Type.Literal(true)).ToInfer() + +Expect(Type.Literal(42)).ToInfer<42>() diff --git a/test/static/modifier.ts b/test/static/modifier.ts new file mode 100644 index 000000000..c0950bdf2 --- /dev/null +++ b/test/static/modifier.ts @@ -0,0 +1,18 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +// Asserts combinatory modifiers +{ + const T = Type.Object({ + A: Type.ReadonlyOptional(Type.String()), + B: Type.Readonly(Type.String()), + C: Type.Optional(Type.String()), + D: Type.String(), + }) + Expect(T).ToInfer<{ + readonly A?: string + readonly B: string + C?: string + D: string + }>() +} diff --git a/test/static/never.ts b/test/static/never.ts new file mode 100644 index 000000000..5eae99bec --- /dev/null +++ b/test/static/never.ts @@ -0,0 +1,7 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + const T = Type.Never() + Expect(T).ToInfer() +} diff --git a/test/static/null.ts b/test/static/null.ts new file mode 100644 index 000000000..36993ffe3 --- /dev/null +++ b/test/static/null.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Null()).ToInfer() diff --git a/test/static/number.ts b/test/static/number.ts new file mode 100644 index 000000000..2b1717110 --- /dev/null +++ b/test/static/number.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Number()).ToInfer() diff --git a/test/static/object.ts b/test/static/object.ts new file mode 100644 index 000000000..9ed03b64a --- /dev/null +++ b/test/static/object.ts @@ -0,0 +1,52 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + const T = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + + Expect(T).ToInfer<{ + A: string + B: string + C: string + }>() +} +{ + const T = Type.Object({ + A: Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }), + B: Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }), + C: Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }), + }) + Expect(T).ToInfer<{ + A: { + A: string + B: string + C: string + } + B: { + A: string + B: string + C: string + } + C: { + A: string + B: string + C: string + } + }>() +} diff --git a/test/static/omit.ts b/test/static/omit.ts new file mode 100644 index 000000000..669fbab6c --- /dev/null +++ b/test/static/omit.ts @@ -0,0 +1,37 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + + const T = Type.Omit(A, ['A', 'B']) + + type T = Static + + Expect(T).ToInfer<{ + C: string + }>() +} +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + const B = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + + const T = Type.Omit(A, Type.KeyOf(B)) + + type T = Static + + Expect(T).ToInfer<{ + C: string + }>() +} diff --git a/test/static/optional.ts b/test/static/optional.ts new file mode 100644 index 000000000..aba6c4e8c --- /dev/null +++ b/test/static/optional.ts @@ -0,0 +1,14 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Object({ + A: Type.Optional(Type.String()), + }) + + type T = Static + + Expect(T).ToInfer<{ + A?: string + }>() +} diff --git a/test/static/partial.ts b/test/static/partial.ts new file mode 100644 index 000000000..348c64613 --- /dev/null +++ b/test/static/partial.ts @@ -0,0 +1,19 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Partial( + Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }), + ) + type T = Static + + Expect(T).ToInfer<{ + A?: string + B?: string + C?: string + }>() +} diff --git a/test/static/pick.ts b/test/static/pick.ts new file mode 100644 index 000000000..bf1772bfd --- /dev/null +++ b/test/static/pick.ts @@ -0,0 +1,40 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + + const T = Type.Pick(A, ['A', 'B']) + + type T = Static + + Expect(T).ToInfer<{ + A: string + B: string + }>() +} + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + const B = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + + const T = Type.Pick(A, Type.KeyOf(B)) + + type T = Static + + Expect(T).ToInfer<{ + A: string + B: string + }>() +} diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts new file mode 100644 index 000000000..622e809e2 --- /dev/null +++ b/test/static/readonly-optional.ts @@ -0,0 +1,11 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Object({ + A: Type.ReadonlyOptional(Type.String()), + }) + Expect(T).ToInfer<{ + readonly A?: string + }>() +} diff --git a/test/static/readonly.ts b/test/static/readonly.ts new file mode 100644 index 000000000..6cce5ebb3 --- /dev/null +++ b/test/static/readonly.ts @@ -0,0 +1,14 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Object({ + A: Type.Readonly(Type.String()), + }) + + type T = Static + + Expect(T).ToInfer<{ + readonly A: string + }>() +} diff --git a/test/static/record.ts b/test/static/record.ts new file mode 100644 index 000000000..928c052fa --- /dev/null +++ b/test/static/record.ts @@ -0,0 +1,61 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' +{ + // type K = string + const K = Type.String() + const T = Type.Record(K, Type.Number()) + type T = Static + + Expect(T).ToInfer>() +} +{ + // type K = string + const K = Type.RegEx(/foo|bar/) + const T = Type.Record(K, Type.Number()) + type T = Static + Expect(T).ToInfer>() +} +{ + // type K = number + const K = Type.Number() + const T = Type.Record(K, Type.Number()) + type T = Static + Expect(T).ToInfer>() + Expect(T).ToInfer>() +} +{ + // type K = 'A' | 'B' | 'C' + const K = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Record(K, Type.Number()) + type T = Static + Expect(T).ToInfer>() +} +{ + // type K = keyof { A: number, B: number, C: number } + const K = Type.KeyOf( + Type.Object({ + A: Type.Number(), + B: Type.Number(), + C: Type.Number(), + }), + ) + const T = Type.Record(K, Type.Number()) + type T = Static + Expect(T).ToInfer>() +} +{ + // type K = keyof Omit<{ A: number, B: number, C: number }, 'C'> + const K = Type.KeyOf( + Type.Omit( + Type.Object({ + A: Type.Number(), + B: Type.Number(), + C: Type.Number(), + }), + ['C'], + ), + ) + const T = Type.Record(K, Type.Number()) + type T = Static + Expect(T).ToInfer>() +} diff --git a/test/static/recursive.ts b/test/static/recursive.ts new file mode 100644 index 000000000..777195109 --- /dev/null +++ b/test/static/recursive.ts @@ -0,0 +1,15 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + + type T = Static + + Expect(T).ToInfer() // ? how to test.... +} diff --git a/test/static/ref.ts b/test/static/ref.ts new file mode 100644 index 000000000..c4abe3675 --- /dev/null +++ b/test/static/ref.ts @@ -0,0 +1,13 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + + type T = Static + type R = Static + + Expect(T).ToInfer() + Expect(R).ToInfer() +} diff --git a/test/static/regex.ts b/test/static/regex.ts new file mode 100644 index 000000000..ea2769da7 --- /dev/null +++ b/test/static/regex.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.RegEx(/foo/)).ToInfer() diff --git a/test/static/required.ts b/test/static/required.ts new file mode 100644 index 000000000..c3b0ca726 --- /dev/null +++ b/test/static/required.ts @@ -0,0 +1,22 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +Expect(Type.RegEx(/foo/)).ToInfer() + +{ + const T = Type.Required( + Type.Object({ + A: Type.Optional(Type.String()), + B: Type.Optional(Type.String()), + C: Type.Optional(Type.String()), + }), + ) + + type T = Static + + Expect(T).ToInfer<{ + A: string + B: string + C: string + }>() +} diff --git a/test/static/return-type.ts b/test/static/return-type.ts new file mode 100644 index 000000000..d394484ee --- /dev/null +++ b/test/static/return-type.ts @@ -0,0 +1,18 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.ReturnType(Type.Function([], Type.String())) + + type T = Static + + Expect(T).ToInfer() +} + +{ + const T = Type.ReturnType(Type.Function([Type.Number()], Type.Number())) + + type T = Static + + Expect(T).ToInfer() +} diff --git a/test/static/strict.ts b/test/static/strict.ts new file mode 100644 index 000000000..a389c8963 --- /dev/null +++ b/test/static/strict.ts @@ -0,0 +1,20 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Strict( + Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }), + ) + + type T = Static + + Expect(T).ToInfer<{ + A: string + B: string + C: string + }>() +} diff --git a/test/static/string.ts b/test/static/string.ts new file mode 100644 index 000000000..5f21e625b --- /dev/null +++ b/test/static/string.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.String()).ToInfer() diff --git a/test/static/tsconfig.json b/test/static/tsconfig.json new file mode 100644 index 000000000..6710ecd93 --- /dev/null +++ b/test/static/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "files": ["index.ts"] +} diff --git a/test/static/tuple.ts b/test/static/tuple.ts new file mode 100644 index 000000000..2ced852bc --- /dev/null +++ b/test/static/tuple.ts @@ -0,0 +1,10 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) + + type T = Static + + Expect(T).ToInfer<[number, string, boolean]>() +} diff --git a/test/static/union.ts b/test/static/union.ts new file mode 100644 index 000000000..114d685fc --- /dev/null +++ b/test/static/union.ts @@ -0,0 +1,73 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const A = Type.String() + const B = Type.Number() + const T = Type.Union([A, B]) + + type T = Static + + Expect(T).ToInfer() +} +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + const B = Type.Object({ + X: Type.Number(), + Y: Type.Number(), + }) + const T = Type.Union([A, B]) + + type T = Static + + Expect(T).ToInfer< + | { + A: string + B: string + } + | { + X: number + Y: number + } + >() +} + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + const B = Type.Object({ + X: Type.Number(), + Y: Type.Number(), + }) + const T = Type.Union([A, B, Type.Intersect([A, B])]) + + type T = Static + + Expect(T).ToInfer< + | { + A: string + B: string + } + | { + X: number + Y: number + } + | ({ + A: string + B: string + } & { + X: number + Y: number + }) + >() +} + +{ + const T = Type.Union([]) + Expect(T).ToInfer() +} diff --git a/test/static/unknown.ts b/test/static/unknown.ts new file mode 100644 index 000000000..28168a2a4 --- /dev/null +++ b/test/static/unknown.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Unknown()).ToInfer() diff --git a/tsconfig.json b/tsconfig.json index 893bd6203..df793c134 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,33 @@ { "compilerOptions": { "strict": true, - "target": "ESNext", + "target": "ES2020", + "module": "CommonJS", "moduleResolution": "node", - "removeComments": true, - "lib": ["ESNext"], + "declaration": true, "baseUrl": ".", "paths": { + "@sinclair/typebox/codegen": [ + "codegen/index.ts" + ], + "@sinclair/typebox/compiler": [ + "src/compiler/index.ts" + ], + "@sinclair/typebox/conditional": [ + "src/conditional/index.ts" + ], + "@sinclair/typebox/format": [ + "src/format/index.ts" + ], + "@sinclair/typebox/guard": [ + "src/guard/index.ts" + ], + "@sinclair/typebox/value": [ + "src/value/index.ts" + ], "@sinclair/typebox": [ "src/typebox.ts" - ] + ], } } } \ No newline at end of file diff --git a/typebox.png b/typebox.png index d55615fcea711801022a6d577bf0d3988e63bd7b..0df53e5c5f5f2e423565e564ab3a01aa6a845c3e 100644 GIT binary patch literal 706199 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

j3mntg~O|MOBB#m&!nX+mGz`D}qPfT}@Y$ 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

    +d3m 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

    GGVBez91dH74;v+i7-pCDKWQtFM0Kchu=PP z;;(m}tx`@#l%S@WZy$=Kg>c?-F+7!)Dg z!D2#Skh&zx`L%iCBw*%gw_wTN=JPV#fLk)NP8A0~(3+NjuYWy|;RFiATioT`Gy z4cObouky>B^a#m3OXSMdLDIMUmM6TH{P2K!Zb(<%ulDQUD)?lw*T`>CH0iP#myraZ zlwFVyqQuYCe4Uu&aIUjrj!5Nm;!0Wlozv&DGt0=ZJs~B(BhnGt-(fcYoF)ik&343AN_t(#U_Vlb?;e} zFZ;2v7o)kBlhqurPVTvNAkPM2yrg!2I|K?I3%^yp>AU8-PF*Wh;0-Op-P zj=EvT%@c0P)e@!jTnM1G7a{F5uLi}6hF{|Y?{L%37mRpbkwTtJI4Wb~yrzVz``Gfd z*bJRbz|DOf8ZDCSkg>d*JyeVMqoQ?9`$`!%E8OoJ1 zN?REi2$6xR01@(D<^i#v=d1(DrKDi!e3CpQz~1DwHDtZ-6&amq8lNdRH%cl5xJOQm zOl!Y=nCVw`31a^?SWWE>|4aVaMO(jr6&}sZr>2^ejFC9u)-m05WPP(Zgr@>EFDD%Z*QtY_ zi0{;-*PUfN{8E%x)*r!6-vA$;2K2o)4QBFZ9Yh$Y-|`t+f@7jE;AaU zZt9ND&vrSvE7AMiBa(X=24bHzxT-2Mq`&hTWY-{0vkDVW zd%T$1EG3{jX${(b^RiIx!z8Uq_C}?9+YuY3GvbDWcp}CWUTcrq6AJT9)m?6TXkQ4& z)kL8cPWL_MKNxP(7QqZdA!Yu?H)=|>qdHZcGOE422 z?t3cPE^~@J@Kp+(UuG1Bx;U^Ji^S=WS3Z2@OH=xmzKNk`Oe4nWaBR*h`mIx9hsTzb$I_oTF=6y5`;YF4N&IUv^=8afBv z<_;n`&JR$^mu-(rP90QC+~gwO;tLG9W58os6Qs>ileP53c% zQ{%B4x;X9ODVH?rl^x4y)-6(A+#0=aqU~k11B$ge? z$Ei!No!9HmuqZbAK45G)OjgskDRjs{pOvW$(3c~}`-cQKt?e9>9||4|Rx#-ud_l)a ziEZXBdnqWw9w`YtDRDb>EOrVPaM}b0!%u$={TzRE|1S9t+FL0nEiRoZT|;Lb{rqB5 z9qdB7On>>CDla>11`vNc64yr%K0ncT<`47pC#(MFkR-Vaf}GjTDmHz^dD(+OXLdN0 zve%BhmxMp4cqJgK$oq6t&=fQPQDY&~{SoKK~;re7KO9I)(VqaVuqTe-xSy zW`lYYBA63OWN(&j_X`1~gzdH)2F$a$)LTQ*2M;xs40z{jxn*Mdb$a32r!j(J zxW($_a#8AfpSx4%?>{DpnH;ka`nUD^t+$p9yQ5GdP5#t+v_9|Yy9Xtw-YGak zvTH}<2xg6eJk1bOS%&=lcdly~Eu|71q{v_lP{&Spvt!k;mY^SMh18nraCR$x6**xQ zEsXoLMv8LPYcz=`4qPH8)gSCW>3aHy>b$t%$EXr;C*%2ƒn11F&)+RK_z2EQgi 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%0A?DVB*DsVJi&ocpa!u>6&!4&M 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 literal 342043 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

    #iDtj&ka3tH=i z8BoQ028cur&Vi9I2Fq9xQE=}0L)Mf{bWUzyWl?}`taW-$$HM}ff z4xDvny_na$ire6QH!2l6Up{3fm;g_ILdrL6d5~IqxV>lMYBxSdmj$mN;K-LH=`v}-~}#*V~c`{bC$=we9M>eN=tYh zy*P>F=?KOCfYAXeCm5@n#dSD!q-wzE45RG$(=pYNqsIu2b}(lf>7;{lcBJTKhLwWy zp)*yxqDHi86W}XbklqX91|DqcM*hTUowyt>&f7#=JaS~34wCmvC@0O!U@K?2>alG_ z*>o09FfdX#i{W#*%j+mFOq@5ADE&2TymAJ(Qw*%nQRdkf&gJ{e=Ycn0J@`^o8++V>-bS70Cg}0XCre7rAfw+A^mLlVP*$;`& z0lPHXdG}4F8~3P7ol)ykeljbb0SWaeFO*UK!jCS%)jPcUP0EWr6kqM^ByiL^ar6wq zu8-Wqk2qd{AGz@x)aO`Q%Mc~sQPNT7TTbiBZ!TRH7l|q3HxaZd{$Zoeh=ReLoRwdj z@fekB)dvrvOJ`4yQ*8Ee`os~w@j5=1F{s_Rc^3}DhLRn8SBKWU*ZFYuy?G7Wa97_q zOy_%z&cWUFdv%`ozx>O;Ja+HiJq{c=Fkbh%*JWd$dAPJu621|xd0v~Re@L&+xpc{^ zONKqR!CvKAD$9DN+A=88UbJ0jGMDqs+Ig11YE!(J&haJ}DesKuYj7@gIDPrxH<;GB zK^t;IptLLMT|WHwjtFfC`@sG^KhxP!?ZAzY)~;SPdoEeeWN9Ygu+3Dzk|b`ld7T$$ zq5u0I{|RPS4p}jwtu1S(kRp`Lxnk;h`pr10WZINM^^ zo0cx0wgAT)6`#X5(NRCuOsA1_001BWNkla;E&keO*$~GG$W;gud6pgPq{>W z+60q*0(oIqRXI4|M!mHh_Dzjf3PoFns8^QBpK)m_Iz0UJqim+MZ0xx6&EqC?d&A-- zB@b{tb}N3QC!VVB=1trgjEBCuux=eEB}dgq1SOx1VT9$T&Qi7i7Z!cH+5tV^bn|Au z?b?X*bdIIc`&nxD1PeF3lmc&g`bP%Hr7Qbp7yzH?;tF5l=SD}_ zBPtAj;*POn*H&itIhPo`Isyjy zG>BIm^3wD|+dTRMgEYH7MN)F8*Ba~xF>s~_E~N@LspiBH@!F2CAnkA!UHCwbWqiZu zZws@R&fxj&;iC^dIiCFbevZ3YnGI#u@EzZ4*p=4fvkcOVqZemKooGwiUA-viz5|Af z9m)t(4rr;r@t7_csgpjePwy+3PvO{zG{h}!rL0+0%d6AAwhh&Ncmddc)^SbiT*|m^QMBPJ961=Ibi7EDQ&>pA6)@MF#sJWl8-ItTwLBoQYk< z21OT{)wHfpoj5a|czoY@j+csV1htx<4yoUns&nfreQUQ7^cS7OH8Ys}CA48Hd1lQj zdHSme9jv4pM5nSy7P&!OR**s%S3&8}xyAqlO5UVkV53YEBdyt%7auXAQDDkc;c~pd zhY}YIu{>wJXay^}mUrd_UPVL*){2TggS*trBu}MPBS@KvZWk%8L{~;B52?byNzVv` zGHMLTP;L06po`$4EW$+&b!BKcb(kcKFR*~}Ke*LrcNR&jaw!pB&f5p^Kv=@ zN4~Vh>*GON6Nj|p*i87$x88)k#?Fg-d6h;SiRmg0b#HlL%ySk}nVpGpwou2;ba6TO z)Y%i>dmUGK(5XC+qh?uY_?V79xVNk`Uh35{4sI-?O}UHgB_4F9OFy)m)?C@Z10Zf4 zMR#zu#1-BY8&0r^k2=2u9lxt|mQpy+!<4poDIEUY7|6l2XJ{_tIFbu|Uv~Jyg>f2Z z^AxixZv3;7L9J!gqqB$3tQz{r6!p+=c&j2Em}@t@Xo3&hxQ??susR#6F4UD9LaEy< z5eC0?XT?bCIS276KP^2ZBYKy2;lx3l7E?A#Tg?u_&@i~HR2uB0GkckV0bvI`d`Y%k zcIO248fTW^g?EnGi036bd!0<%LT&7%Uj6>9&eRMG$m3(&Ww9BaqLUZF5q25KxDGolgtEEBu*roqH1_S4EDKnnp=24X;wGP-_+IpFXw`DbL9X@9ZGXtr|Iq5Vzc4Ds^+0FAH z&a*RNmsobSn1frdu=$TP&h2|(Jn-N{Pb}XXp#rEMbs)Hx_vX9AZBrg+AojNr> z|GED%e)@m-xAd{TjkYBNGzIO(e%j+*-upf88Snqz_i@&%=YF~jxSS*h=JlNKzKuSS zcAyh^^Y&Y^S=u-UT8P7Gvst++OL50oo)tG@i(!(El_9;>l zhdlVRZ5hJTR6g*{Z%~k8Vj&70E2d@gs-0e7Civi!k8teBeF0v3(~hz8&NpOt*lN$V zVX0Nf0JnMrc%t=7i>%_PY893f)USQM>^fWdJT{s!peq{ab3OCYJfQePpTQ+(TZW$U zil$yr=+LZMwJICN+`MfYvmP9Ga_BHKbq|lPeEutAJDb+**tLy8>9TxZtXSD35;wb7~z+q?1DXAE9hl}~EmR@cJHKd=i9z5wDV z*}KmKbXMND`j8*Xinr;SSM5jEXk*mjxwGfSeP4TcJkAcW*6XfYw~jmRz9qZ3T8_e# z%_y_{RoUXBWQ$#B#}mAO{wv3c0!g=^e9B;$j9ztct8Es~g|`STv|mxEFKim!$h-IA zZ^yHEo4Vsx z?k8TQmpVXKawShQ+n|t=crSeGIc4b={a1Nazrjpq^eQfW0j}O_W73VYM@-q8PQ$Cy zKHpb9gx41QUe+bC2lpLeDe1Fg%Z{7Io%igC!>z8=n{7vY;gNfG(pq ziiKpNH8@3#7=%gkqjC&ZlAS|UsufM5c%c~{CN|O*%~I)81J>mUeGw5#P6~uC6OtU5 zl83A*&3k_{{w-!&73re6(_Ad|W$N zZ+1H7i^_qt4vH`ix~*k5I`BI&VI4T=L>b!on$ehqO@}enu?Arykl%^b;*Ih;EAkt? zcm+Zltq;?+kfZ@<9r&G_4rO&pHEZTWOBgBM{QZ{AzS<~!IGzXR=SuYFC< z%XCKO5Z@a)qv?h~I-<@r>TEiYcG;jfoRN&Y=-I(~1`EnA?_9;3jexkSj~w#>@0qPc zwnaE)e*Y#toHE(81UfoyzBq9Q;E9{FPK*Kgj#+v$y&jncV|696yHUOf8P zpokG`)e+vRP8 z;_YRyUA$?!i{9gpKOUGa&KQ|ca9TB5AqG+Xkb6wnXB+|?iD%f?vhPi6>x z4Nl~2-n|;cJ&R)(dA@_&IuO5oM)Om=6wWKpUK)q?v1=>evz|M7Xe?cM}QJQxS$umAi%@?F=evHR9t>~gy<%kg>> zkzk;#;|E#;vbx)Zlz(U`sJw_ee!&QXM^L0lDqCHN}hvm?OKPm5J<V zf{c_M3t!_@l7yuZ)qUaU*-tU3=bq)%2A%Obc;L|Z630SZJbx)W#NPe7TXDW_j*}*> z*j}iY?!?h!YwIud3!kRTXvq$35=>Ig&`!LZ1LaKn07f(H@8PtWg?4Q@@oArhxAVN0 zp75P7thmCD&$5X(XQkbl^u~2-vcb)vgGaM7>E}QD)i__PJztaW`|8LOpY1hDQ$NMO zdIvYVvTMI-cXDPNV(Qnu1sWNzgl_ds-G*BJ z@PwT!yS8Bdb8O^u=$Ru~(tP~rX=a-_oB2(5j?LTZY*Jy0CkRnae!XVgKGAf!wBIg@ z*PnmV4@`21L%&72EawYM<*;>XUp=msj=WZVmObl;W{ion6ps$FBaT{$}>M*M*GbEBjY%`=X$)(hD|r-``pDWtB!6_gjaT| zuSrZ9s8GzR44GE(QCRU7MKt5W3)3MAG|dFdPGzRsNq`0XNuSCqw1(1U#0zA~2nV*8 z-OEENlpHH(o+qKLB=J>8Yl{46xJGiDKSpP1SLFp@;xC5NbQ+Fpa4Kx4S!h1aZP7Jx zA)Yk(<6r0ZHGmVW@kXbdGH;m#aa?FP&4UK1qvU|BXcr#;c3IPPG8IVK4LVH@ z>K*vEc=9#lNuViD+L?i~!IB)u@G9QL(LCxXh(Ajm?O5Tgd26Rm7$4`&w{gq&z47_b zI`dl2QR!BAgK5U<;aRxlQ!L_AZ#q`$L|!k_Dd`9q%$Hwr$(K4XF0JM>>A*H0;tn44 zYTV#enN63LWn{n&eauS-Xom{K`73PRW1T=lop^p_^J97IHT)jp( zYdP`TCpm}`J_BzF5L_Z?-(#9GTSNKQ((=4@r{Y*LiG#{4sHJlNJrPxy&bQFigG3zz zFPe8yW6qxtHXSr@vDP)I=$obxEua+pZnan^Ugab zT~Ep;({CF;^{G#dANrvm3TWf-rsLn}+Awc;!y9s(3J=EdAOG^t#((>7emqNLU7jk- z&$9OQBD0YW2-YmSf!$@VU?=Dfy~3u)PhA9-ZF|NY-bTd40oUAm}aDa_(U_06XeJA?cEANZm19pCx}>J!He z49<+7J$-uI`@lov&;R_-#}~i&MUeV(k3Iqd_Mf(e;KEV(XW#$* z4~_r)h5N?6U*`-_IRZWdVsXdVG&k%M*$v8^F!8@In4!j$$`XsQta?={ulAvp zb}iou8-(k0pUBQ~N-O1s1@9GBrJTX;_CC6o;}JL}hmCHw?r>+)joD1Z9iBZ7N1cby zNv|MD`5G50NF|uvY3ehM2z{~X(QhJEw#KexkXCl2rScCOwGJKMI##T-hM8o8T#rjF zD;;gH?HdZ=L%zB`ggh`oI$?b&02e0H>>TKqOXukx=F z{O7%OsjO|=ZO>k%EBvf&$PmTjEKTL|3_~~NKt>J0SLN$oI?`y^(rM{Hht>Ro(NU6a zwQZdt`Lb;Bfn$B?^0-Vt>&8INY+ky^a?(}H$EGdih%R1oL-bvEsqbKe53Vg+l1jXZ zu60WdO}{vj^q1C|^bN%y&nEPtW0rJqbiGEW>Sn?b4QtH6fu8c*f56}zSF9U@0zajdkZToqE_zNj55_w|ZbF_tz4zWY+HV!a$+p|3=*g^HKUgnqKyF|*$ zEzBy#(B&(+4=} zVOs`uC=KgOzG`q=j=DM)-mCf%&ZW05ofY>6HVRq>&E+_=@@w7ufONLftBJ}ctU+4F zTYhzCjWjPVy_L@(BgazC!Wc0wekV;Z*3xO-a>}>B)_jGuBifE-Lt31c!AVrl&M3-< zKeV4gP9AK)r&W_*={zWm%Y(c@A&Yi#Dz!Tv?W9nz?byTTqj@Hl5%UKlIZ-ZWONyE)&`-SC7t@GN!bSeAZ++`qGn?Xr)Nr1iFLkJ#aqXFhZ&ZGs&!*LB-}61+Gk)VYegh_793THb|9Jew z2Y-r}9&U`}H^+RN%ArD2C|T;Xo9mUX9DZ7t$+fBobb>U`=T`m(|q&=oAH-Mr(L z@%z8`J7X2cLs`Kmzq39 z_i*%p@lz*HjQbyZa$LE5DLTLsW4}6>u|{5y_>JZJ-uJ%oz3+SP*s+~**v>PDj*S27 z(_a|-kDLyVucSBmKv3ip>trvjyy@EItP}eW;h&>DpJx-PLr*_Gjz9ARI(vTH$nx2Z zTW{g~wl#bY?6E7|HVbp`NRQy(SQmi{8Z0W;I!lGLNDc5jriso*W&I%pe{|qzP$5|rnx0*MP4J^0w#ezru)WF)Zgmk)G zxhHfy zk-C%5SJPiak5ZGb)G5zM^28aNnE1_eSK8=4W2DJ$PO>w&OJesuaR_|Zj%_ zzb;vhT+$@1by(t8Tabt7DD~+hymVJIE3e1Y~obGFy< z#Mv|F8B?6e^)fG4uVLy_SoNgNX2E+y^1SBm<#1K$l@laLW&dW171)_g!S%)PKE5I%*wVWl*mzSP_tJy;x1{w~$ zDS-6bR(trMeDa%g^0|H4z+16(hGu$@7QdSHM33N(LrR*?qj>Ti;soQ8Qenm6?yOl7 z%f0l)DNha}FHLWaqC3j8yuz8*PMHH)aY-vPo#eMON;G)Yi~7&bWTZ8J>M2Wg$+zRu zQR>01vzO}f9D~9OIvT(%rGs0=pj3JW>6k8F)0#*1VNlP~QU}&i(-+Qr&?83;%B3Yf zb>L6loGCrZjGM=rcyIm|4vd!8Vjkk!kxOL_I*sZijve^DS5N6UCwkEvxV7Xgv7O3$ zZwM-2<=3{~%W(5>v7BpJSa;*vEcJPW*|uN!gs#OY z*0FQv&arjt)^YOW$!uct+0TA9-;&JZY+mNe8gH8O`KEbB=SM#Bk<9r0@t^$3__sg% zQ~9>i9bj#vwiP%2aYnLVm}od*K1;0+^bquESoFxAe&Z*yEdj91b-}J5H!~g65 zoNw()4O1UR`G+5VX#9(R`CpIYCr(0-euP1m8wG(X!Oeo6n>PIcIc>elCQjqHo&lHv zGU@k}&-lt^i^to)>mQHr`@ZiRx8Aaga@UR{M~{vF`@ej7Jhty>z7d`24SuyP@B29* z&g5eksTb{Dy90)Z=27jAYo5amKV5SS4tD9LKu7m~og+_{~j0?!Tlnu1D-~P=khjn*YH%xJcNO*hs zh4K-Cd#^oS=s}`*(_bk!vKk^cSJT4-wyL}al1lE}nMNBIH9-_=O&Chny-S~yYr3#? z6{T?6nmC*h^=VVEra4fhPhw?#Bh1M=58z;5`73El%hh zT%SL8e%$-jhxpd=>6E?Y_HFr2&mDK$No^-_zCMdi@z?ZXGfPI~l zdnv0t;*%dkmW?K+Ug<-WM|&Xjc-j>%$Bvv}BcI3l*6pY&>ee##GN2q@dDCIb^FqL>ejE#QIcfUq8A$O| zA28JgblRDez7DB$%Vx^aG~UZ{=U*6ztAji!N6VYf3nqM}S2&U{WQqLnbcva4olRyi zuM0O-#L?OA001BWNklZcVRYjPph*t#&Kp~0}*9^i4SrQRP$_93dbLr00 zN>KsQ$)E~GD-`p-1(@L=Lv+QrrCuIXmPTi=@`O`S;jNGam#i2}FD`{o(u`;C+fb#X zbfHLk^V2kDIFP|lYWhLkGzzX7c*$F|X5=v}U)m|Zj1*tcNh2%6ZCb);P|a|0R`~oY z+$;bVzNa+t29L5t^nxt|tx`=V_-70XEUpSwN?z-}`L(P`)W8)J$cUFnJ&D%iqE)Z ze53EPv31n5T$v#ZM(A&O@9^;^{!hPb2m!Z={RI z^3}bUV``o}o3AX=Rd=Rqi1|32Yn(D*oc)~PnaT?9u9W$P5qg#j-{8wPn({l*mzE%B zSpsX?MV2~UK{q;}I+fD#t(`Ndtpm_kTbX1sT^ZD=gJF4;UgRcSe!3pKIw;SK3~<&( z^apKeIuO=Itm|{UExrUl`3~W)Pkl zXHOkrH^Vh!>#n=l8E(UP6^^C+Dx;38_V4CDI;HY0v+}O4eX*mQ)}#0GE*w>thvYx{ zwtL1q-}>eZ6n#spqxs)&WKX(>f z)D?KWYTZVx19!Ep9cjxZwaZ2Da0#bYT%ND_Eb&F)Ubx`u>)~&rVU9rfpq>Qt>u~J zO!ARw`QQ|$^NFk5)A$)S-5F*stIJMP%GR7TvIPY}$GE z*tliKxPc>PG8m;z$+LQqe017;NN>UwNyB4}Z<0>k6s>xKL~`lR2971O{b`{rd95zx zu0Gnjm4|jL)j`F3DXhIwIhd9cr+vGss<^lOAq)6X<>6n(6msUoC z{iyh=&lOe{O_P6RpIClbdK6TKlv&GtPaPZ&adgE|9H(^~*Kl;jE&2A)@75ZZ?M?@x z^imFT=an2mSoEyB@S9d0-W$fNJi%$MZC_sVY-~BvRnu$7Ko|>IT-rU`Rk+P6yVZWI ze__mvzTqhw!Kft5bG1&(sVP3gQ1~lZge#gvrh14oe&HfJlb#;?o;=8^inA=KT#@hl zHf~yYM&(2UCnZlE>ImZNm8EU`|-Vl7EUR@pnko`G>T*WSHlEKA{V|0*)%F zVhU(hj!1PtGF}7F7F5PnT*}O7S471re1b_69@CAB-_AoNCvPG=TUI;qUT7wRoGK)E zDQ_@eTwtk(kBTF)&{a9^0$Y4ra3O!n2%_ME#tf;&g4+`C3n4oS(;|nF3HJ<8gi|@A zLSztlCK-`4tj!O1keySnp#3)-FJ>sp4Nu=;+ntXcpqt21=2I&cBTJ%I*Co1{^ zw)H9hIytgbk`b8#Md7WSANg{btUQUwd*S8H`wpfhP*}dgn{5qfEm*q#g`;~ryj ziP>Q{+7XjShAiPcMz3)ep&^{S_R9d5^~!*0?Hnqv^n{)19e#EH9hf;QsjPl089Npq zzvd^nCOwy^6!P6}FJ2ve)D~%PLvv(uQXIJx+N6X3(SqVC=;~RMEf6| z<+crhb#~=PI%$$#CtZm%vID1`MvKr2wa8hsow-*Y;rQjeGZOJB&^1_a60NZ+sXmy2 zns7T%_C*Nm1mn;}&n~qEPi%+uIG@PkuCdOTKCh$f!$x_zxBNvmoH)+LDu)m6<9o|< zoJZ)Aw5{2^=hbfB18CYlas)3V{oax%yyi~GpZW$9*|Zt;kxY0Lf%1p1%$`KB@n*g~ zdjEUgG1jkN7yI@b%@2L(L;3zstOn^Cl+AcwJ1v>~>Ab7&uP)#DRquWGayp&bIQ!b^ zUgy*1;~oR@iBEhYeE!*={PFnUPyYN4) zK==+im%T*R4S(MIj<<}T{pEi@Ze;g9K%;AUu#b5Bkq0^F@&`D#l3k)1u%dh#}fySkJINb zr>JxdT7Z46!M-$u2d)WaBq}X7VVl-A9L(Ex4AJ3qHLstgHQ>jdCR}kMh+BWg&z(Lw z4m|PjICW$n_s@^j8@7&ho3>{e_u?f>gWEsdexOve$JXDao2`Sf_!K!DggIq)EAwo=Y!@EUFA0=lwWhPSlX|L5<1r^fb z)>&G1_%xk*4i@`ic{hb;8Mf>(IleSW+015QQ*8btFK%RZt$DmCQ!j9sKGYtt^dhbx z3tn|Ba&60vgWRcd-%|(TeE6;AhD~d;q;lg;>$9t+JXiT>Ru-ab>&0K&X2-oYyyHOg zE3evcAPA`|o{ZJn`7R(Ad6v^SI}Ax8OY*$hHr0_z;cs}3{P5TMQ$BT^K89-t zm+?OS$dj2_)j8bEO!{qi?PepY8=_cL48F3q(vU|spjHz6BBRKfXOI7Q?FdO;^t9fuI5z zNTVnlK?f^GW!rqZf}6L%EP#ecRACyCjxVsYU?9m8jLhhAF7CZFp?jAO^`X_-n9c|o_PA+eDMe)9v!SU{LFrl zuEV2)A}@yE<0_tc{*=b!EF6gW6Q8)}8?W$|Zyx2NVa;<8DJ|0s>Pi?8WmeIdRQZY{ zNT(qFy8Hdh%IZ8TdZi zA6G7(9cwvmWCMfq)qL0WYF-q$v#tHEYEepM6rZz`*D{#TIh)j1W@*)zamr9Aa4;m} zQm&%5icyT&K~`me$C()c50GXJOl@!v69=!QESw?pMP@Yc>f zc0>o$KEvZl3@(8cMq6LKWYIWrzS@^Fo6j+$=+zm_Z+pvI#(((LUmY88FuPqim7&s-K0H_GGgxFiNLwar)?sA zhh#w)67o0cP23Ti4EfV8+&ttkN1g0{`Y^Ma$Hrp6P3Bw8n>TOBf$ul4Q?2x7fwIv( z*yq#5tj*9S%G1oitzoxR zXS=p;uWv2YW9hWoLe*zk2@M1?t-GbB_eKJCpjYrp~6$UFc0XeQ~8m#W16rJ8U@Z!zUrR@M*q5 zIp^D$yQS`b`rvr@{=GTdauLh2@3?z+j#-H#jkBc4@@@T#&~ZZBh&s|Si{q(|=7PK` zv!UU2mM>%1ez*97ee%2>fVa$y*HU28v`uN{xvu$t3S)-MX$k?a?OmKC)jvz|Dg!Z?1jJ(ww9*1bXAN?zn3o}Fl+Ir|1vpo!Pn)ii@b zO!Ah%5^u>CX@kp!Vt^dX&P6y{M z$#h0jKEy9?Hmc6|>Q7qHiAaHTa3*i!lb7qP4^n0g%slExhf8Ns{5pNJrGvzEwBq=2 zHAvesC4cGEe1kkV3n>p-#yjEdCAV=#$&*IoY&()i%X+WFW!Gw3u#9q<=kix^%Y#;K zy1Fy3btrFcq~or%a^)G8wlSB(DyKo1s*7?Ebx6&z<5o&%FrRa90s^hpww3lpUg*P< zPN=+gyR)oIhNUHp!F=1R%VkpzKE+>}th$kJ`4W#jS=Y|y?mKgK9KLuVcvkZr)7BLn z4(yDk&JhSKBbn;pt3A|AC7EP+gvfI|^u((?KFH+30hTgZ)=o5zsC<)F?bX4E&LB8Q z*P>r!=T3K&MU=<{VAAQWaVXgW99@Aqyz20o?$T5lowjDEWwAPwmL*SIFW`V$_C;nu zUkrZuf?pP^@Qvj;zH2%#j+{Bk<{_uq#c%!C!1BV?e%B=|G~9JIHAY<^uoalPtv;1o zIM7kfphWJVjmWVr>%e98(StmJV^+uH)k9n*kq3}%W9{Y!hC2AhkRlBn7IGCcX zF~$*Y<;s=gU2prg@%DHB({cA*cjO41L!4pvxi8#1zV_GwcDSX@gWvBC4P6WM4B`%b zihEu^=2_PPrn+q>)f%e37`L3bqI0e(?>*(T8`Ebqoy6;fN0kFC!SeQJJ}a+0dzq!B z$8x6T;U^y)i&^Hq?&e*w?G>xnW-vJa?W71&fAS8!S&&3Ho9t-1m%oIG@47clE-EOq zeGkGULAf266_4nI_9U;-C+U83c;t zr@yf7btLW2M2^`K;3L)V6UVaZLA)+ktruP8u-o5*k3ZIdc|l@%<(TeFOJ71i=uEHS z3eTd8tWeqi)S>a@-hDareIt&?Ew}F)>o=@rmdmsHir>hiT}2kL61Y@dyj*9kKSBDX zLdh-vHBNL4BTwxtS_dajo*wt{t)=`J{6_EgyV#Iwg&P#r441gO-4IQ^1eibl%#nt6 zB=5qOljchj`6)eE&+;&{$>>YRj{bid*h|ZNlGGk~^@2;B%|lS7&Wk+Cxcq`MFnv#bl{4#d)hlr}-Dt;M zXiuIvld~3Av21$F_D$K;CYu9MR^90Q7`i`cUc9#6%(Gt+KDveH{vPzPhn|XqxOC|a zbYY)Rm$2RrKZ4c2hF9K7}*>ay!JdesXS;J5unw+Hp!czx^ikGe^5t0YU8o9Nk=TC zWhtT%3f~PxieYN}^LQIxyc7T8k1_T=SL!Q+yqO$9CuBs5;0`#6 zWS}U;;;mqWyE>TDawHF4CSiilxHxKXEM@}tGaYbS6f0dF>R5KZSC=4qwOnRG#3?3` zk*qd#1~QBxPg^iprtQVJd}CGH>68+UHElAypfh^Iw;Cukg~7_@k>jm&7d5n#epSVIB_@|sS_!z&Z7M444O7` zC`nVdX{Z=OxB;deJt@Y~<0>aUFqB=I?Z8P(`tlQJmw3tne?0?9`O%ql112}k@eE7D zGG;ZG(iypftOF9nTDseemi)0`sEFVda8ya`XaaUT}FWycieetV>}J?`OBq1LMqvv+P28v1pms$UOo*nd- zbL@ySk7c)YJc&ES`r^dhWnISVa@xgVNq6V+HoABXG%0 ze7I^GU;XM=$B+GsA0_QM>PD-iZzRu6gADQ_{9?}BObufj|Kwfo8t;DhyT|Rf-=0B+ zo2&fYR~{N)VsIrtL1Hzfh9^CGvC`&(y3kJA3H07No=Qa5>Cdf;*v*3eO)tj_E1ud> z@DUf^e3;dl4Di4DK-<-VUARQ?BHy7NIq>8-c5rXbi(I$mwj6chd6A1;xuDI;Bjvhm z_>iV{%r#<8F!C_Vr|nrEQ33hlF^H$}YV)R6EhIaCO!sA!PSOQ6D;%W+Th+IkUL_?+Yponm-um*|E(n3~36)xI*5V6xKjKRArggUGuf zJWhRpD7=r;p$*fY*)3a+M^QRs5;5%ISRj;?&th*Yvgp%gI;UM5RZZ@F#5e7N`0GUKm63Wc?+Nw2+u=O}Kfy zRTtG}X7Q3DoUY#Yh*D>s(EuMkaMvaOLZi7Jw%rY=>^U z3M{|Ui!-QG2tsiNw*|cCB7Zub!j`j(?Lbdmsz+fgo0kWqse5sh9%>g@Cs@?8d^VnP zD4`emi(bXWSU9{&M||a{@#1uHz_XMYo^<%d*-kigU-j=YXhCjK{licV()1#=0HFd} z5mC?t6Ik-nF*(AV7*qi&NCryCnmx2L7N(SJ=?YcS3aQSb^G#14c%*OHO5t*KFk9$c zxd5+{V#pJ$X;xAoi5Jd-mDes?`A{=Sy7TlcR~V-9Q~p#)@Z<-70Qple-YLOCgCuUE z!Ez~6_?@95AwTgYl{5!*Dc||iwO|00KozUVlpkY=Ax<>KZC!8`!~v`{l1f4byXeZd z?)@Ne0Pov72mY-)d7Q}rMuYrGPh8?LKXpO+g6k(_Sk2Y+cF?>U6h+6W_2Rw3TzL?8 z!^($0H-7OdjPUZIW76^m*ObTQWq2&3)8^GdiTInQIHjd7yh>kKJr3agJjU342)$furm<3iUFX0&CZ3eRX zqKeCav#0Hx`M8iLVH7S-rwPtp0%ugaxxiB9r=Qt39@+cQICSVK4)wlqY-j1+p51qH zNIL^BWG#70R+qWjuEd*-e4=F&&?$q1%x*?5(jRX>QeLMu zoLAGd>-oH^<5%BX&N7Bq%6t9mUms_U%&~v(qvLb``O|2xju<)rYz@GCaM!nx-~rB= zNoOcsO6wA5cZ&5m5?@SQ$jqVVVDfT_Sx>{}E!+4$@*Oxyuc-kBIBXlXi_^!CjKBKi ze;SuuYDIPW-KF1c8syuTCvvZ?{NsP}uJPkP@e||y|MYvumd!VhvuDqZPkr{wRf1P+G_M={rL}?-uJW8I!!H;u3V|piEik(9Z8@3;5I(p3sdy!896N> z5&!@o07*naR3MtxwfGV&SFz%SJLT#8@B@F^^dgQ#^1H?Ch`4};#Fb(LvA+)rs61D0ri!?i#=Pz>M zR|;)CnY6zt_hA}n=_pLqlVrM%#1lNUX8m$SdEJYF1e*I2JOU?S`Q{)c?;pk2Z(zQ; zc%UYYvO~>W${V~8uKQewkS7gkra$9Ke$hcDl+{e5dDG9NBW?Z(=fL{fjT_i$b_d_l zyq=}eS7+DHhrjeF%V`fqg{xTl?^3|@JJW8UlfooNX_{j1dEP;RdDeBxfYFZ*^Es7A z9{L6!l;4+kb}rzdf4}wJtT$A|vG0hS=ByxzLK;)y0hD*HONvh!OM(D0_e| z0a04!0GpSS^2fB>nbY(dKz<1nkV`)?wGX$Aw0py}9M)fCB}3j#*K_~5>L9^S90VPq zk$><@-6Np*EosTGZA$XCgYkgDimCV*%rJh!e$rpXJDFRyY|8giw{G9e9s=jb!50sY z{d=F!`?}VTe&IWdk&7{nx=16wk1wS!0LD+`yvX~WU-`nLoL^*F`c>o3du|=K-oB^s zkwc#1cfA@wIm4}NksO`MZe4GM69td}tpjTy+ks&0k+QO>x~p@=U8S$IJ17*i+99-o zjh)H#+>}5Pc}%hKB{rS%NTVcPv@!KE#lOMPF4C5)Kd;5IZs5rj?N=Qb%(|Q zIrwIn3fHshyeyP_8yThfXW8t+F2x7DB5Y2I3FT#|GCK5iKy=i>yWzFd`&2{2m4sCR z5jn)9DSpco(W)v_1jB{l$PkPyX~}!JsGN2Jw0s)9V;3@T%vPzbxYfzCS4;4QIpV2RmX9W@aJK?`Iu+;FFDi!10Em zonEs~;97h7`4p!o)TQ)+E-=nq+Q#HnbpV{QlixQz%cPNf=wvVj9cj1bDd##$TEFr` zW^r17>r&hw`Ov$@1Mj#eOK08q!5Ph8`ITQ8eoyl>@6{^bH%kj1VN*W73~Ra9eY~8; z|6l$x+%k;k?|a|-#z#K#k$`;m|M>IqAN=E=sTpi?*0c5gp;H&KJl3(e_qwweUL9-6 z*YD5qUZys!zNqYQj`uUQfxEnBr)6d%-}xZB$o|%EjP(Q}1xr;y%K-H&4}Fn$F#rAd ziL;;X^6JuCXE4L_>eb^NY((_${p3%M`@i+R@ht>dXZeJ}=f3dB_?t)e=H1W;-wnt* z=``)EKQHdt$eTI@CiR6Mn8b>0g{6II8|zlswz=0XuS{U-xn5Pyv!L0&lX?qLi5H#l zS)!l1fIuuK9{{TD7Tt?%_;s3P;mK>N@JMGH%V%p-pt*c@$}jEZ_ACFEI|EAi z%BSGuB)QoqQ&zalCl~?=7+qUnyvU(KXoQY!@|GbXg zkx4$3n_O^R~ORpWXL--nBe?`dntX?t6e)%U#}z1K7mYA#?){3jmRl&={7HM zS9}NiOf|jF*mt)q4S&gavy4|Xu~D^4pA39KVYc zrIosCKW!wuaDDVUm%YqYnznfZQCmy_EYUEoL5=Vh-VJ>q$mN?MCsBX&&uj$FVfTy(zL@^-~(3_m9zN+CM^59f< zc#9YN0%V{ReJD3ja3J2giqblJp1KD|+qJ9?uBW0qUAWdJ0mw}_Mo+ET%l+)zr zIozewAk*co>eey}Z-<21mr<-^fT+{8anD7d@s`(&Bu=%8GDZ(ACIxQ$sIh^+|CW@x*2FnSf9zepInsv z9B6ewxndhTRrA{?mCv7T%;9Nn62K^}bmF33%yW4RCPfgL=;FVv-dB|hn=nc$kscTzh zTnUKvIc$&d^&HQIP0N6eaQ5fw6Im#i-XgFd*QD*0U41b`_~fQM2hQf?K7kbF1^5Sa zKlbVWXMB|32kyUX{Jrn}j^R$N262Ag^zTM*lpc1Q(yP7 z;XGcB|IJ{8Gr#G1y@`#ZytY;KP1HJ_eB{HvxL{^;gI9FR=jz*3&=g-9_rxz7;<2 z`}X&bpZM_~8~5IG&se{1-8jhZh+qEN!{cB6?PJkHF421)BPi#~{%gG~;MAS+wn1js z@#hRMwXwgp&(gU(GDw9`HieRhfo3GPU#NQn*|@cByL1d=@^VF9WJ&uGawcddzxkS7 z0yDV0W^Cm>ppTPAci+K zy~2)L9jF8ArtJ`x$)9bL4E^}@$>Za|Q(xutDo>BsUO7K@-ST!e@wsPg*tj|MRWxV+ zA!#caJ)&_~m4%d}Z6eP+fYR;;mO&wpaY-lJW#o(Dq-J}qfJQLO61bpGmhy+Ebqgf) z@*pi(P$a{D_5q=j0fV&g#kbgJ)gaPf@k(}%m5MUfJ(vCvpI*>_ZwA|Rf#xfRRb9ck zJ|LIrM&?gn@Ms4W|4zR-H3d0UqREtmGB^A2w|&R9%+SBcXHK4aY=44@J-1v(@Udy^ zzHU2pz9x1odC)^W;r}|Fqq^0xZ0f+fb{2hRJpIIhEXBO*o?FIUy!Tmj$_C<@lw*;s zJ1(q0@5hcM<(c{n&j_Du2jl+4@8ChW4)&U6{0?3v^RySVg4Z_VI%8UF{APlEEd(!2 zDa~rLj*t8+RsfG|)8p-t03^9pE!E$6|}S?!ASbiTSoJbd?DSy!N#q18=W|lXrX$#@eLcl?*O?yv{@#yffSC5nq*m`~`gVsUWoK zy=x@H4Da{0=@RgP`!{r~;Cs8=%o6dh z%*3t6XJu3XH@!>DOXx!|JftqQu;J)Mu05x)nynY)Px+EB!W1KoqCl3A&8W`OW8;cu zC0Vg6&a4g$_LlUw(sk-`6dN2gWQhZnOtH^7L?ZcD2=gN6k{G+#@ z%45fAAm+P>Ew{ms!Cg8?U=4b$2XUq8^JCxf(;m`J;02s?t-vekl?Fl*TM^DzInt9q@8P)usUUJXGHYX}Ce3T*M7GDuUN)U8M5E`QR`ykRh7NS&*bGNj#dZXaU`EOkQxSF{n} zZRbAuf87p+GGX4BJmgdFL2}bqN%GI~aA5Dj7sm_CTKaD3ZoXl73*QIaxOp2l7Ctv3 zO{oL_ssStK(s^CkwIDo<^3ZLdNCtS+qDbtwjbWO?SD>Jg@ic*)u->`Oin+Pygko#((zn|M@sW zV0kTpqB~DJ6X{N{rhIRbO@216xoR9a&3jwxuVE(LSxoE;Ep1%yVU|x{V4&x~>^nd3 zf$=~6kN?Biuwgyd)B{#ObeLtuU;4uM?| z|Mb_!`O`TfA~b*RF%>|nuXZ#TSlF>`OTKhg zY@i=WWY#NXT0@$h!k3v<2eP))NMFKFTJR@74}Zwd#y$Y)r#`%Rix1nuRcFatjZ$&e zvvl%)V7~RJ518NFITsdy^etqku6d{?uF1cpk5}?2TUl)I5di-nQ*BO!&XHeuiUY@B zUK;{$z;K9nH4nbXO!8CD6M$aL63Z>)x*K*4msze_#WGr8tw3$DoX#-Y|NL_=u_X3! zW_5UHcKfDr=i6>!R`ka3>HA9gK*?<%sD38vH)E4|?7Y@>(O+V#Ok|*!WlG{P{xOd$ zyySx&5H>+1a%&f7!=#soIU0c!`kqKr#~^#bTbq%TJ(E40JKi-oh(58S^;Jo{4pfvy zY-wtHAFOKUJkTUB!4Q3ybS(E?-uj4Vy|j&_Z{H`Zex=S4RQlS1f05BL@t`Hl<+fGj z?CuJldYnCTp3Qqsj2GF3R#-O!x@PsN@!Y*TZO4;PW|H!GH=}$Q-i}KqS z$94sja!;*`+`EBMV&Qu?S#7fX0F9#XiR|W)EVUo z##1lWL)m@NrqNlQ!lMr1);l1ppXch-`sW@$@%mg_gobs*Nws}#T(T&m@4i}S`9;4r zz(4Cr8Rel);ltD^FlB5jEJ-Yqhvae66%VMn3q>Sk$5?ex13x=R8JQ|Sr%Tt9pEGWx90^{GZZIS4rl zT%zR7CAbJ8pXEVSSdgi6wC(~$9{#GIWhIKzRbG{QY1xWhO9vgH8e_{WeCbxlNRrIT z_3!04WF<9j^SP9_0RYsfPY2J!IG|TYe-d5r1vCXNn(TRvg)v=rQ?-_6i#{Uw3K zl1!bK3MM~xU6NS7(y$wnhVv-Rvx5|AdT#KeERlcWlueMCYm))BfnVcTZs2j2&OzDR zPYmP?#;i98MurUbfw#=$b%I3rMt80SPY^_T2Vu%?Alk`mb&4KXR(KI(8wpqryu9-* zO|C8Ty-9hCFMQXLl!Z;l4~zk&?^YU=nhZ?0Ff$@danE}k9BV{5L60xcWs@` zR|e%O-UckxC+$M{EGvjazY2rn&V@Yp&EXKI|-o@;qq()LWiIBA&YPh2}fJ7V#q zX`VKvviRYX%F3B?W%;jSTMC>fww=Sgm-!MwrW*ojTfd=s8}9+`X0~&+L0foDoJpBO z>R2{eQZ((@1A^#_U63I7zH@1f^|j0BoY~ROlqVfC*K~8qt51}815x(+1k3(I2glbA zzJ!nRZ8&zGyzPct`HTe{Od#WHF0B?rRH775xyC}d+_HYn zcsJkC{O%9DZ*1PQk^cDA@#K?FW@gh*oOBxvb`dGtEuD9nu-0QA&A%M~^6SfCyf!`X zzyo~8^HXVefA$f+gZT@;fF{sK&vndRdY|yU%4=K-1z&^D>@0isVveWT{702HuUnOk zew+o>cYUa2(yrh2cfWi5cmM5wGgh;@;ONn#A7LpppW&f$t|j<< z*Mkp^pZL)q8TY*X9plQY30`@}@*CfHeEf_5`|-?@)=ge>mhNFN|D+Af^Z1N5N?ny4 z@e|6sY=NtNfweq-U_XIBcx}Ek7XIS(w7W{{2Yv0}DKi%0g-;E7Qx7vQ^u7sx;PE5x z#~EC!AOCEN_6ho29`JEg0cmC@S7!a)1nbaqPX%TZGycB6xslyxo&8iTQ_EOllzbBE z@|;j8&0~iTj(seLJswhuxyH){t*VQn75Qd;$LEU?b>FsF!Bh6M zp)gm#S(jXUrN21ntifRok^@S9x4V9jA$;uu>-~nS*pEz-oO^Q~prWEPlvN`AYP+SA z{k&x+dl-X z?FdKCvi}HMRsfQAV5+69;$h+09y_3LJX*d09QHPFDje^}#VdR`6<6xkLC(FvK%RF1 zqR!f^?(NW6@GLy@g;AD?zOa+NxX=l|6}JOi+pGxMumV1OW71j#J;3{JqY_xQNbqyE zPre31e%Ep@v(KHh!A-Zhbb7~Fw|;H>xr4;m9$L!kxq43Ven zTJt>k;8-5o;Iyx1+~m?*>}6W`Bj*OnD({k71Id=p*;*gq*w;(PHmOaWk*(RwdiOO1 z{ey1tg9y8zuC_)Xu)A)5Rxkmu(A(HUG zNXO=>9sKEZX|9~xxnz(p9KXgBCy4%;w4}JKr!dMaj)8+mVdRl(&Vx&tM*}2hew3)` z=iX&`KuhMxKwju`4BuJaG8pm+r#uEe^=Xmft>NVm%&<>&P*zc`iiH&xKpe_sI=OiQdN&^iUf>y zGRd=3g3p;RZ*_5&)1>{hmGIJbvmWVdPc66d$%TSTT_LaX1s6NkuB`RoLVL!)CT;o2 zzmrnQ&wTOYXA>uOp}h6e-`f8Dl+jL7*-oCnFrH-7nL}&_bQ0U$On`aYmaStapNepK zZP6-Qh+DFv5q#^(f9WoqNxH&lZF3Cd0_!>X!EvJ>WoBTUUIFmTTCx=KwQsprG*)4n;+ zEDSbxW>e{6H*Lt^kPbZbq6L27odAT$t~%ZKYlPGIhaY^`_>T8IIIg{RLk5D+JoC)>@P|J<{`#-~x=By@s_l2C zb~b7EKEM3B%ldeEx!0yHe~_T=Q=j@&cKmAibIUczGoosZob{sj*u83cMbo}dwpBX34Tu|IgQu!M;rq0zPfu!|_-5uQ9 zCQOT}ns)1c+&T#j>*w-Rs#mV%z_zV8;nIf@_@@sk;xj9@6zTs%chWGw+p2V=>%aIb zdbG?v$cn#2&bt4EcHn~=nS@oI^JmYDgU{_9M_!^`ojyJ`Y~DFGZo7^pv^S5d3F;j% z>8I{go8_s|u6S!B?RbLEj=apQ=yOkeiC3hrAyB<7%VF2Es-TtyPk8M?JJ2ceJ}Ned z%<*}sQ67f@Bv9y@_85CrpP0S`9}!klKrs4fqeY(dc|TIFO*8L2TIco?mWYe~SSaVg zmmEEVgU2fKVLF3X#e#lj3?s|Do=ae}(GOV-6B@XYESwo|Wrq4@Pzs%x02%Cs)Tcym z-vc&s)!0QM3mXAg)$dWfm7_4`Ip#FK^|^ffDeYHjJI9Zo82g?+koWdn9=M(N@UFXl zXFf5q_o-+3CL*83IDB&KzHxgtez{@Kbz>C+?H)@NKY2N+;&Naf>Um#VlB@l5Q4P^q zyCA#&Fs%L%9F3gnDqnAXG@${5w(Pb#0plAT=tW=j&di{sG!!ZRL%u0FRszPxe|i6Glf49nbrQ*WIv# zU1&FDg-FFK(1q5rY`KqY?8)2XxMo z>qm)Ipc>rt5k4%M^#k5(+&lbErvYGmLVdBLsryOmO*-aFQ=4{urXHcQTzA?}erOW7 z%)X+BxLHCge{GHe;>jlg=#*1@>%@(i^e^q8uD0(a$}d?WYYQ0ISO5SZ07*naRHjH} z)Yv>m)cE|5Fg7lUG^_@;@m!Lm0poUzAcHi~avA)Ir|`LkjFg(b7foB4>RNMDzIn=J zN}ha$<9~NzmRAFbpumn~I;)HWuM8PsA(;gZ_~$cPQ6ltoWI)p^DW zVcYg74NI4@v(6A+&ib>(BY889Njara_qe2885Og2`tk%TQADQ_q_^*l1>Lu>Z^?5%DJ>1 zmXZ~E6NTKu%S`|{0SpAw5pb=n@-To?bJV{i83+k$fRkVfczHV$+TVk0Kj}ctz);x? zehebJ9;BrX-9c8vubZGV6df#ytBrg&P<`zLw1Ii~hm5K3$Zcna589BKrK%ajl227K zn+cxs9%Bh*&){w(0P+1wc}i>9?&7JFKg;EtfE76HWWZ=LPn|8--jPW>V8oTTW#)VB zxi+ZxBui##m?8(~2Ji7R;5zWLedVb)dD#$$K1Q3!R4=ql9xbQ5giAU3a!@DfX>(IRk^-m_x|?>VkxMw9P?PFUrs zj^6$d8&Ee2)b*$g29>Ej$^%PzV3En>z^|U(doI2ItjJooP1o6np4>7V{-^e8^o=R&X+B}a8NHC!$(yY8|+HcTJg z<@S+>AEuAFV*J%#{`2uOKl^j(Q?4dhbUC3x+rE?c2v3f~$4+Lct%1&a*kI@ffAELKyB~ajPtaUD4jwr^ z9{TFzDWI@;>GiPgY(((>|o@OW*794& zR=zW9@UL8*Dxj}6(dR9b*(4axc~zfsT1)ek(^KIN0D#=}?M zyFbeN$&a(?%Nc?Z`CjBRLCSc?+in_n-F@3w$LwZi$|w_GI-E}0bsv#t<#zd_*<*Y4o^442 z>RkM#kIcv;P5Jw7W0+vF_ARfqU3fs!RC1Or{JvuAEv*W&!#cF0KRDXYpi9Gf-z_*b zD*BlfqyJOqF1JisJISYcDSNl=4(_|m`^7~z@^N|dQ+$3&d9U5LZrpn39^TL0lJUT) zlc&e2lV^B{z$Z`mzNVi%5oX7(Z3Iayo2I=RvouZlSvT4bDI!;ViEB5;@mxtQ^S?>i z)wAnVTH0DWd#>Lqt8fN%z6a{P%%#RI={H|okMd6d2p$=WyrwOTNxHp6w{6^IURMxV zZQr_4M}t9aY5dl*TmL?Yk-jn;{H|WZPUh$vc!OhPin{P@`zS-}EUzh6`JFSE%5RFe zD|H(KnjZz`pwnvw&>>ohz?iR5ASMH8hLNotTU9R-g z5pt|bs7fwU#mK)I5Y=Va>kQ&`igSQeG^zn=cp;R>q^!Zv>A-&#M_5i)P7idWq=+C$ zMR~l8V^(INl4-uF`L!(OMZd^pXHbCJ)w9@co(o&~VdAZXTSLh&J5eLMfJ;dl29*wE z)Lj?@GS8)F%3t~DOf%2GAvUFaMaGgVT26j}1Ey^!U1g8boag=n2dhGu%&u^3`sO+C za`vsd>(W!1I)D+T0tH~8sV>WMnrGnV?4vupx+Hd?ALqi#Lp}USuWYF%wUkp{Y2X~s z5oj2Emc3Y>#&W8rr`-N}lOOSTX66uFL&ct{)lSN!ywX)x$1~t&s0%eccKfxpRkQy>dk3U|>l6G%<1DAnZUDvylr{Hos!_DhgkMDZVJ>%Qnb3cLRCT`BJj;Ee_YW)1q|9rj?sXUjb(Eld+ z%Ww_j(bRe^pPRRH=g#q^FMcug|FMsLbo}bC{Bqzg5JXE=S>WSIe}ZNyvM1)lPI!*>SLWwpMWnfKqQ+y~$F?(ri({P)MZ-}9cak(u>V zyeIaz4?jNs)fXO1V5yC&KB`WYQ!D7}u?NT2PajOh`FEBF-+Cag;5n(vU9m&g9kj(; z;A+=GB(Lf+gAgd_2R-lzxp_a6dRb`7$5rkOO4~uh8px_?!?hjMLri6L zfLKrci<|^33PAPo^mV6B9Lsx}E}s=|-Nx->_bqQ_`Rw)tjxs)Wi0>~x@$fiyaNk(B zX(zLmw~XC4-7!|PWWD89kK#86`8G}|BD*xKlhR|QuZYb9C1-6x--XPz?9uunQBd|k z@0{0l;hI;vdi0k*Os4Z;DEXY=w8doqq^02NB(u+bx`UOTWNXpm8g^fs~nZ|Ic2E6ss`%P zGU#vyXk}2ZUY5&eWz1Kwu?0Bic^LcH*Pa^Rc;u;UjFRykgYFw{+L=$F?B@HH4*IX= zUTSi@+R1w8fj^qUgF3f<;p>>NXO1irc12$Y;>t@eI`v99dD@fAu*ki>nR%Rc_GsN$ z-mnP-xo&!b_qjG-@J0@Gl%M6w>)2X49k2=;nTt_iEmuD|>yt@c55mo~;5q4g?QC5K z(2c7Mq7}`OwQ#4;Bh#|Y+OERbhJ1))o-oz7ls@OrU0_-4{_(;Ama3Ds?$|VLW|!E_ zn>VKJY!BMaxWd^vca=TLF0@DazNSIdIv!v;)45^8x^WfnZaUt`cmjFENgM(`1`7Yo z{?fOb9KR^rHEcFzkgII`NgM2d3$)nUqp}#VX&F0|XOyr2NUD>UeN0S284?wI?aIweY54@5V3QahLsXPH}4A7hWY%nG( zb8$m7!*bx7W@pqj^fbbxFSw~H$%-iXE`=?6(lzB!p!1rXAe4NTN2V!gD%m{jVI_7F zPP^zd@?Y3xp36eLIw`mRg*Df5lX&LI&P)gYrl#4B;`MTB{LXEf9+h8sj~dfJCVVJU z-j-V$4%j36JVxy%J{h(w^D=rlKr#Snxxy0w@$F00!!8S> z+~BW+8u@Q!h7ds_XY9$j%S_b6B(8i6Cf)h9myuf50g{22%VaH6SNTX&9FsDuuh-H| z9m4S}ouq-Kfvth1OIp`6!{)j8O6w>$qP&Acj~FC=M|N=A2Rbl~tk9K4(^eJ*vJKe&q@z4`sPP#J z)49^gs&`}t-hRhqo~e9>x{*F~3=%s<6E1BN^0HP91dg+0b}uuU&-1wqci_F1C9}Sx zdE@5I3_h+7ddP$a1dvPmp=_Rk+H0?Hus)?qnK0^r4CYyn``Ov{;TKQ7W-E*Q>sP|pfiv`-t<-&-3luw~vP&dMLjCZ$AC!<3IW*KO0*(Q|Z#~tL$7zax!lNfu;4M-uj!t zrpa@IPG|4Ec`KUj%6xa4J^R|Xj0f+%bNna&_#chkx8BVsE;eMw?Qg#H_3@XVeI#`t zotCq>%BT9q8Yq-M7mR)DESq)^KQ>lClD^;fd#`H{QEkPtwu3zK@CQ1c2M-;zYyTQn zI-TMNu&O$)L7Rg&>$YW-XFXi@-c>RyWR`5uLMMkmU&{{jQdRWO z&?e^T)35P<^M&(g$MM52j~DlSW1Ks6Y+Sv19h(B(Jl>%He)+%?)YYqF`}Mc89QIxY zrF`PZ_AL$hhh*6xcEmo)s_Y#I^gT*{XnU7FWdnevvPjxY90jhrDZIAf0&t$hPMmwp zn1N&2Ksl;?>SI2<@RK9jrew4p3lBFAj@=Vu;N;8SO?_Tv?funPLK{DaVt#ZK9EFgl z^4r!GLVgBR_I+qF`N_9rD}FY0+kkn}QBQ5twyU7CWKZC&fKrTZZC`!2Y}di2a!5;> zRZ@fFZ2rSiSvP}G#5?b~nO#M9=NpUWJ%8ZfSi5dD0SY@0@7zAt@(BzB3}uq0DRePx z^q73T-_^Zyac10{~uNrDh)R-M5Ex_O_Fa`kn#t{+eURQ5?fj=hDo z9gx~yvbE0jF)ix3d9kauTgW6I9xnJW3mV=BEL2ZrGAUCBQ=Ui0Nke+hW@ZWO#BCiG zuv3Q+<4|KIF27Vl|Ij4^M*aU@~);cj6IIgkVS(#aV+XZfM_y#LZ!~x_Z_)2+OL&JS zvN#Era{E=_9V=aAd=WarsetAUhWCCl{FzO;ax_fWhrXXa8L;ZF^*P4v)V(^kJ%v|S ziEA@$cf~4pk(HpvmZ|SbG!dC<7=M;Vn3hZ&&bxt`9XUCPU@4!#DFk5YD2#$NY>1hs zAwBZX>WoyAW>Jf{q1Ceo%CU^2+(lcka*72nDNcCdTBwxItLUU`lYhZesDcrKC(l$w zDt7AOK-Y>WoT@mWXCoK4WK1V$fspdR339V30A$ly1Zc^HtyKC;hf?fhhh@^)3dgc( zS90pQNl1E^aLwW{|Ci8Tt*nm9C94wjA z;m&iRrIQ4l_#MERmvWA3r5?&3Ik}dO*G4j6tfM`X3y~CbRRSyW5)6V?^y0&zEv7E6mKSVIv@Snf1L);!k`!s-wQMnarHAB#yb0D9de4TcjS9ah~hb+SB|#rzmOJ*30^Ou5B}Lu?--9 zm**();sEWURe!Aq@MzC{KUuVaWiZaA6S@$vodpLzCi4vp>RjLK9xhq@DL!5m9sI&U zFz8;_yOQ%>RwN5?%DN)*lmS~SFQ`e83mux?d3I?&#M0TPAO7r=rQmkodf&M9?)!Q0 zQ!5H8cj@oc5rryqg@J;`be4qeEQ@`~i~SG)u`<4JVBcf0qzO#6t z3a+iD>{rSqkU?L|Q^(?GHqkoJ=3qo9`egcR13+3oW!x8e=TcfWSAm>`tbW_xfOEj# zaMLn`UIe4T=Q{6e8axzlC?siXYCH5IGv&G0suK`BhejJ#7b}DmJRIodmW!B**yCq+mY|BJHu_ApfAa`(?MtPQC91o-G>hzVfplNcAvhG z-G;aC@?Oj*&R9mPoWjXpd#jgyT?grH%jia7>m$6dWo-T1J6hF^u<9n=n(@^qY=_VV zF7=7*89Q*4hkjzccoBPp+kHp19qSEUr_H6I&XL=9{=uzHOG`$}Tn<}xSplxQ#CG2! zUMFoXPhnN6<+R-Vj^CUm&az_wf5id?_UE|@tAq+GT#OLwMdnT#&eO;)9I50j!?b*rSA^1GK)=$I z1OCFM0Vof0DM2LR+DeM!l4GOxr|fpX3re0?!8jsCCC5BQZa6e7kSQ;e(muA3gie@p z{>&5MJXM}f%HSp~HXMT|lLJKYT{fvhGx5XY5R~LU4d&Gci0`SNo)YZ@%&zN@Jm+Sq`zXJeH$)F+Q#7Avl^ zqXwS_nUR~LPn+b}L9YR(G<_%X2)jr6txn6N*vh<{_@h>(J!db8Vn4Z zg*QoMUBj}O-OOI@WZ9|~e~H~^pE-VP9A{apxS72&5R_Ii^lxN#@EhHcNuP8l&?jDc zY5dK;y<;CVHbMKrn{F9zzhMsn(*~Y|`aX#5AA=%??bI2}Y6o}=c?xfE8JTkpJlB~~ zK?Zty{A#ux=}f&LUhN|KYk{&&eG;YYo~5x<4p6 z+|P{f`{5rScf9p(O5Pj~Kl14Kpa1!1#=}oMN4qJbY2o&7kL}|il)+L@rEBZb`eFd= zS2k{)JW8Vn65=#`<1fQ@p8n~%|7K04zLsGl=OuQx{@^tSPwfYQBo%K@mESt6{Dxcb zQAd3&{-Tdz8-SPnL!U_={uh~nu{~?W(qZ!8S~=6+IQLzs6MPEj#eI*B3uk!;mRZa# zyqoDxy(bPmKhB&ykwN{H?BML|QrSQvla8`<;@tAsHu5>f5>}an$M*CuJ?oahq$Et9 zL?f$u=1w8u9-L|%2uNrKVqQ9qv;9e%|8znI3A7zSq2V$89)X}a+C=_u@+8PvN;rmR znyc_!hZ68%WzUiTRlNvE_?I1MDHIZl{FSYA^-p|yIzBRSJOm_^>U`N( zQcoz*ekC8zYtUWwTzz=i+;eSu`0&y3l`npMJpK5A@V)EaTgSccxQk$9OZsn!1kOOy zrH)&+Zf3XH9W0S%_U4q195pdC@)z{ipWVjpee!9eMfKIL0#CqrC6#g92e4TWcqm>t;E$zCf~E0GE}@bD6L9N!;n1d zZy%x^B`&jl769#rpkj3|0s&h&6oGe z?zB~R=5>%SPtV1-AJPAnCHD-<0!zIu`%VYMY825F)Sn$so_K|r?90J8_&?V{5v#FN z23$ySq4bL~)hXpBfyogN3oNTc8H)=QvC#6IdueYs0p%lzSz*M5MQ!Y+n}HcH=1-DT za&?y>URdE#IAJKll&DU_76P%67ckDv3w)2t=m6f-GB|)&-|EOq)~@g=ZP>^uGY(-n zZ3W#roBWk|Hb80Uc+ppW{5)3%ozR~!CI|R-E(QXDB;OJTL-LeP&;Et3bx*mpJnCv5 zn;!*JprW6009;;aGO$YbqRTBIgs?Qtht&%(uS8DZj-kGhZ0-`5#`9mooE& zwe$2SoHV4ToZ3dYT>87pU|0eqVWrX6!dnTxduCqI#L{FhbO6Wv&<_>sz2w(k%3rjl zQ8Gx^pKP^}=eF4z^i-g%?7h^jkIHc6tV1RCTwQ_>?PI(L_Y|{_d-;~;N$TikHUoOw zj-BH=mTO*Gi!Ir1QN1)cEWmV<)$FYpG7%9(SKxpuZw>tu7FP{1bARzC2&?Us*Ep%~MZHjnqdwfyR2Gy#KWBmk&dke9* zUcuHtvgK%*yslI}PyvzsP5so{JXO&DJW`ZY`m9?$3km=LAOJ~3K~&gd65`Nu;9Kg5 zu7MSC639$^f-47xZ1Q#b9ezV$bCYB7}4WS>=evknXlZC7TJx-EFUs25eC z%r?~R1BCo{u&yt9ude~0eUW}>-Bn*!q^sP|CkbD5QZ%^x<=VKT`c3<8`TNj}F%JZ! z?RjVsger103NM7Sk0BX^O2cb|l=xBVvE(YfLQ0JG5LKbje?v*l*?`0(!p8EaS6# zUto6mLiD-y&Kvk%DNADAkOsM(p_QOCZ4;)lN$J=6o3HLYPV2f?27@x|$^U0gpUrHx z?;2Wf>o=^8|F{IynZDY*it+cYn>MW5o<*^ZIj?`sbNar0k=jE-Oy1Uuj-(_UUqp)p-9ENa-uIh1lO73 z#L&-@W?=itR|2=mQ@+xO4b=sbpqJkByx3IzS(XH>=dH5?cXf*oKqZ6T7{FF|K4lf&&yAdeo=F+}q)38P=!h?zNqeLvU*en02iz+DQ9qQ)MvUqj;TK7k$?^XLPJvtue9ZIi#z;HJ~<8C&fS?fEx;GAQ($^%D_Z5R;cUsARGoLs40K3j^WP>KRuYcl+C&nNA z!5@r2`IA3MA0*B)FRzzrEthxxefQm$rMG_P@-IL2@$p~$;x8tUe3e~%NZG=A*Ie|&t`2R^_|%2o!l7sgk<{?zz4Uwm}D zeC%}9#gu7T&ekzU{qZvWm77d{D|TGQw>0)gw#a8)EZMxvZ379wXls^#bJ$t$$RNC2 zy{FgUSRX20E%W7a)wNK{Dy?dBty0Ss-pG-JEd2oKa=);=b<il?N&DMxr<$8Z)9N0tcUGt`s}N(TpXv49|ittW;CCp`Cc_P zZrwe$^KIGAw`%3tLxM?V6*WlXwVCkoA$yaynFwey;Tk7}FvV7Ket^xFz zHrw?LA!jiRW@RT%#tQ_Fui|$Oh6Qy7QX3l-I1A~yZOu)7l>f~)T;^?GE39o?ABjZt zq|5NS*y8W;hV--YM5NLOfXvt;Q&P(@bqqnyBa?kr&a3|9Sc!fx-IDpp zt*b#NeLdF>l#d)aHXixXqs%~^BnZ4_T)$`Mxc#nM*g2G)xPjHSna{3xf|)u-P<$!b+sGBaG#Mu-*@7N)<3k{H!Zh+ z^j+p2+*{TuIw4MigegL&(UUVGlxnN7n#^fUiSSD5ZMrCq+Fco=}>p?zq1V;T9! z)&Nu>C_Fkrvw4RucCkcz8+6E9yN0^N)tNE3eW3Ss{Z|`CW*jN)6CV^IwgxsjK~vvU zk5HrBWvfp=xj*&k#ylH0ug~)3Ei8#$y_)y^;O%a$ieCJShUW%G&akzPWTie556pAz zr*7(=0F$HFeeQz=^?doIBjY6Rd#dkRzSr!72J74%SJelRqk}xG-^Pzkty5(I*SfJ> zKS&@9KV|Iq7}Y|12_IW~4NdK%Pj)?MpNyHv7gryYr{i?%PFrb{)IG<3_$Un@hF#4L zx~~Bv9c8rs>VZK$3|0@*!Y)Yh7W$FwKx{sCS4QP&-Q^kidl%U#4}(A~9jA{7n_K`Q z>~enPO-JBjWl&HJxNtQN+QmW=fgXxnf;)L2ZvZsp@=Yti{8_nStPqPZliIkNm(96uSN_HE!wO*mo9Q%?EyOtdnYC*7X? zj1It?63lTFMjz?A_UN@q+R+6X)`i!~XP)%cNjk|3@2aN`;MFZlbZ43JnUvf1pxpjC zNyj87+kia8Nk=+q6ffl}|7)4FM|N)(Uvc>-jP&IiE)>{-8`O52mbNq$E&U27+RUHw z%g=xQ7lzARmnL3Y0C)8WJV)|;xO4K{+416ZYgLh-!%0@lT2!4&tdz|IG zj(05Y-o9<@B(Pk~jpSPh3`09Kt%tIA@(32|+m^6`2E$}@<_6EvnN8(Opb4D8VV;CK zSdoSUGJ{b18v{V;n1plqhZYG|)kR*?_b60gvD+f}%nh*oSzU#Rd>l=64o`3#)N1qC zbE#Ck0D1tyOXU@(VYR=7SJ3_gI{{ie9xQiMs^W)$*sYpyU706|J5&#qo>ca;nS64 zJ)84f@J5R~ZsudKDcmaz>=nkPv1?dQ^56KG^&a%{2lJ#Ad0-wK z%l*y!x_vt)=bYU)_|w>tx~Y zl$&P?{5)4*B}%(P&dAjL=!6l+Q(+RMO@GloRVLkPAM$8EIeMMa;;X;KR_2w12<(Qzx2CiiBylvUX$c3G(k8EZ{UG$7(WJ7L`+EKpNmG*F% z>S3189_Mo>mP^lfb)DIEW>dO8e9%tnZQ!U~eb6b5RRn=$*J^j6A+K4N2EOKrr!8Vt z?5v!Y8QfaG$wzhj=B@|nDvvUFu3jDRB?tt`{FKdpJbF!jp7;0^LK#;Xm>U$UOP9&R zbH`YmLa*x5ncl?=ssSctB!rI%$~i@VUv*&LBwrj2CY%k_|7J7|^E|I(7gr+{NkeH6 zd5ER3^i9?IFNjatfe7&svbY8sc2v?pu}P~dyQG^+Qx?R@Cx(Du2DB7SsiFdaTt}#q zT>*iKjuw*Cd0BoNjOB6>Q(ZE{p_7O}QZAPWT4qY4pwK0m4wmI9%_Lybe+wtAGE!jr zo17&(aFkoYI#bew_1ubeQ@6T}tk8-CQ&{m$N@{oIRarIJk++@R8O4Sd&T|8gqTrw( zhnp~#MMmJ%%E5mJ8l4}#fYUYxBH||8Ay1kK_{4)p2TtZWqvx_xgOX{n@CGG35=2Q0 zI>`ek^f{Ugl$>RBhETf7VTm%^U^*~VN&FdL8vHRuLVF<(eD2V}%c34`tog(b$5$eL!BlZZ8SlFPzI*woUOX=S`@jPautfF~%uZf8KKhZ5jDPy!U*!9!S7xIhCuHm^+#S{c(_Lgus|hl# zueCgM`00Q2)8qTT=R;%b)-4GvAAfT1`1t?z&^X0+GUeNYg63+TGF_hEx^=nIYjb@% zwg9inus`_z^iz}hl~!~XJF`moLc`TPb;5!VV(i(0zDp`iv9V?NxpGOqXvw?$NS=*1 zNzH5RU2RgFOYBcRSjltXZJSx#chM!V@>lclFCPeRm?*jJUYh{o|M-&(g+F=h@Ob7M zUtqZ_pEdw))Ak$3t@k`Qu31xUv~_9wfnKQ<;7Xme*DEZ4Km5{j;}o=x9o&yC-oj^0 z*g)on+nCu{%l7@uumn$>C0l_TIkBDhS(D`^>)f-t|C(=8r+(;zwM+MD>SZ!7_L&*t z%To25cGqdCAPxD5ATInlbugVlF#}3{`ZadWwQsFp1sImWdTrId$?Rtat#8(S(a)Gv zf2c28&Jmf#;ZJx(2F|PN6JL{GliCh`e1kU^TNQ?d;^w)FC7~D zo_>xUVh@ow#%*uCX}p#1T4qO4;DnUF`We`m!bg76Benen7GAFEl=^^Qw~qvk>9ATC4Dk-KbAyqyo#e!HB^`5}A_FW}Po>ZK2#A4Rq8e$Ij;$;15NeTCg)i`?FKo1 zSM%)Yb4bGX%?Wxpu#EOv-seo40aje)(U|I+bD*`6D+}ziw25|5huCxas4~VR;CB6M zC-DqoD^RaG(kGR@;L4x9&NAxAUB2&q^>TZN(|U0lJ+;3{d6i3V&<~W=S~6ez38!8B zscYmw{ww&-54~Mz@Mqb9wFCtqiXih!>CRyzlAS~i%7yV~igGzhg1iFF4WePpEa+L> z7a_o(dEK;H7K`l&>r zn>g1gO8>AZQOUCstpEodo#buj>;^&M+%)G=ef0=Aw^5jcQwgB}^>iV;*Otq_gE;93VxVA9(e-FR=y~{Xl$829s5Wr&TKW75 z7u`4t(|UT|L6@|3a?8w*j>5`k(pHA1C0&!S2EeP-lcTxe&9yX51zEOeIf8>7rKx?K zMU|= zP_UiZN@>Yk80AqcDNufWE9SF;iMz3gLm^Hzp(u5u)>>jSRKp1%iUJRh&=jJ_o*%9_@ zd_U8fY-cf@5j?_1K-<}n=^mEF-n@NhzO7b19#z6lz6N6kdA=XT^d_`zC&0kV^Eb>R zaJ2q8p-$)zm<4rdtO4n(1TlegW>gx`d(9b6tqmUD1QV{LC9hUgTQDF-u>_Oi0T{a{ zn2ikB9Q^8mrg5Yo5#-aJ?XR(+AmVv|UwLSOM||=lYk&)`fG0_CB%L%7JZLu|^oanp zC~_bVG^?f3(uKVIhUWUUtH!(UyK}t%+a6@s*$ph2x;UPB=9zrns z`WwLc>Q}#-8Oy)=i%*UZfB2Wi@iVWCE$dgM-0xxLfg?859!SKe4tB8=_9w>Q|Gp2g zoNj9Z%l-TIkN@?rzDQtsG0J-nY5B{?=HShNhe4lxY1&RnQ^A_b$HG)y4GvmXkLqj? z*go2MWk3ER-*Qk`fvI@9!t67-(4OX1TAT#G>r4P))L~(_RFRco{qr{iD=o5;gzb!+ zd#N*#ulyF9pz5CmtTZav(#AzIph2+zE&F9gioT;~rtDYFojEmLW;yK3&pyE>J}1V; z?R)s<<=e(4zBhXn?>Wo2llJpLQZdKJ88Fx2RrwTiNlx1!{rc$>M>DH=;_&kVjI|p! zjcwQ8Hnwcvjm$N8j4qRjb=j$OuCYVwD2*QQdjGa=EVJ(Hi!3*wtwDO(qUv0_SjGN< zmYx2N=A#ZKc}c5+9R{lm8ih~Gqf8t@ufEE}uR*7{2?W=4{n)8BZ-h40Djf9ARISsOH%MwIjyZ#}OI&vAOJJ7m}(#>If z=hGR@9qdNBn$-*CTk-A)^q8plOq)=5F{)>3PN1s`@%QS^8 zy$rPQ8DP!xTb28sdXD#Ak0uyGx~ z^<&+-HGxwm>%yb_tao{8XJx+%p5|+k1gF5Yk7yI|Onojd;nc1B2w|ivA8D$G^h9ku z*hM#K8!UQl{R`-Bx(2Q${lfq@Wn8N#W)HlyGIk5^%GU>!L!Fd^?_fHEIfa%BDN1UH?qL4+uO%LHD9W|k^ITgcLn(+LwwDS`^jGw=ltF-4y6C0F#I5)HPL z)g(Ow7LSE%Ca4lnnNGmx@mx3C| zIg4t3`~z5V4G^Vix%tjy3g<4coeYAdZIGxu28zyFO3VD^pk5qdvN;gtwKQ}kAhXVu zyLFYPa_Ck;X8TV{wSQOuhm=F4jg@Iyb52Ghn*XSiR|Kq=7bl& zb&9`mZMk(O=Pa)XCw;0q-+7sXxUv7VSxei2X|;v&k(RMxQpSb6^f^-xUJ)WP%QZJ& zV9YBT){i*B5QHMGrpzui)khujn17n(vCj}#K7EW2H?zd~x^?T=@Mp)^#*VN)WY~A~ z=y-w+fsPZ*Y+*LzZf37;W<#LW*R0{QIdC$7fPHu}0CiI*hmgu*of&LKUTpB%6%}|| z_B#14lQq4;ATmo`r)|Y~9r(<0IyfRf_LrA>3Kt&!t!rr}7z9t7MR9K6=7oPaS%3KY zEN}l2*Me*ii2AeNkA7TCI66DvwQ$+6`)ui?UxP+y0s*_w>7)6@S07rD&Ai6( z*l>MZF8BQ3{lUL8-uvJ?x%scn(j}M9{_!9G@%Y`}{arY&q{jE?45ptcxf>>leoES3EBr8~p!x#_0y4}Rze$9I45Lu2R8o!qD|vb^@IpgLi4~&7R1A(R&Z2u@Av?e((LbK_s zpFq87k9AVOW$(5FK)F|0yW%z<^IOKtVdWY2sV@g}^t)u(cT_-LI!Z%&2820RU*P(o z&WcRcH>Dp5aj6%N$%u=}3q5r4JC}!FJiv0+$H&Pd&yTemx96LdyY{#oc6F%=4cm11 zacrANQ)=le{W6xJKD3AJMQ@*GN}Y3VaOT6Lh!Y{rq#clF&jC_b#I_jpj;SMIN>01AQ!2?l|-L@{A^vo}PGOG#S$jl5U(oGrdLDf&b z>TfVZpQVh*({_=P1bf`$m`FIuxL3g8mk*C8AA4pTK6os%DFz`ov7_i(+O=aUX(?!A zja)_1zuGl*Oun{{ohOX-2x0;jrB<%Ge-ys*i5q;0t2YPRo`H$WXALqB?0Yc*(YBqN z$1dL0bY@Fl6-?5{!^d{ew6sG4CygRWjZ3Paq3qMahe3?x%92@GaO%N>_M-;qhhjTv zG98?()an4HA_r&08;94@>FKW0NUAQkrilZ1KKYFD7j`STLwL| zXni+*FWQV`)XO z+*v|q0cZU*o=0aNGn;zBcRYv=l_63-QZUVg2U-!dgBbT;!_E33D>ZAQ%$!x5v z=4PNd^{`A`l{d?Gxn8b6{YV<-si!<#8mn!u;T=~u^+_;He(BAG3=aX;5>7qAX6sUR;(p9t$8-NOb~9o(hm3Xn=HK-)o_DGfr|CjLU$hJ(?9Pm~b0I#CPS zF+5gdmXf^75pyxA7wweBC<@RjTn-3v7z!T@p0D9>$0DIRM5goJp+JX zqboWXC^ft=21de(?=gWAyoE{EIO$0ud0dNUcUgnn%2x^HmY)(B;5jR3FxN6^I|II| zt4R-7^IUc-jf-?{26%q6avgcfARqI>lWS!(=<5LP0ltRoh&6;Y8@RY@Rof9LW#Plrb_* zIhEI6(}=%JIP2MfI=&=%;JO10za@Qxr%h%jf2DyG(x{X5=e5$=P9$G+NQ7YH1DxAv{uwvC&1@*W9+CJkQmA&>)Z@vC|H{Hh`dwl#afA-1o>A(8R@j8L# zjy-Q1yKcTKyN_N&;L+)F8B0&?-uDMw#dZ~cD1R2W+lBfq>Hs+N+vhvqqj~y!2UD6k zK7r4gS2K52U%>;Of{2;H|EwF!#5IpYy8*|pJJIU?R_(0eg;oaoQ3L5oJNLmNxX8;r zRkX|NTz=tSvgO{|z7$ET{8a+Wv!_msXPR7fnc3W$C z<$H#ulgC*?dtm>I1WxD22Hw-#LZIQSrhQKHQ)%l^8XcfXC-tlRlq;(=J73G>-p7e} z$|1b+E4Kk+?53%q-NA}^+QT-fKCun;n|hL_{M3to@i$ z)-_9Ne^Pa$-11b8%wS4GLA#Rc%naSjGH(`bNK2O?`#J;`&K?{Hdo)$Uqk(u)q&-_n z$+Lmz>|ChCbO_~GL8{ItjVjoo82L4@_|nhdhVwKKGR(F-v?n#uJHJ?XBNVpi6y?H` zfgNTTiEkm7(kofO3-inQP#)rPBY|@f2Ta9rseSCAnpZD_HQ%5mWip%mWNhEwfn;(lOs%yS?p!6EoXnyL>Z#Pl*4oLlta2+E0433M+q)c zFI+oQ>5OK0b1i=Z$1QVtY}-*<+E_eO(^n2{Vqb4@JNeuQyOmyj6`!fG|C%YX&2t00*Lu zC~F_%jHY%fxUAiBb! zmdYBuD5FYxn7`CzY%Ye0zt*!d@=INj#eGQg6?*JggOe1*5A1)6M-RHK<@UMeUEF1{ zG(Zj?>6d>MelZhX8NxVxm1)Z9;gS^SDR&mMDLb?G2mjBVEW(den#{bLKoiuB5Wa(l4P*q4wnV1q| zVy>?0>gr~*G>}?oDHAoBWaLUK?PR8fRx%Tf`ZL^ft&PNmj53pC5*JdF?6C$E3R7Z8 zB!EOB0VHO<&wJwBd#jC}`Q3I`!Ls?0-Ixm+2`Z*YyC$?;v6&)#_ZwFJB$oj$*P^Ylpgc_QD~ypr8#+5d-q zkY%8ThjW=5B(i<^j4kwE`rr&?4IFodGQ@fx#RIma(b;j|OrN7qRwn42z%p_lpAcc5 zA34um+U7n{Yhj?eH{Wd)^mU*fxO)?n8jx<;%)r2zhqdxsAJ5K57Mdo*j0Z<`hx&@A z_zEjvF&h$^sWLXzFJvr`f&Y#7Kg_$9*<2~_JYISD!P6_5-Le$*%EOm~pXN{@Ga5&K zCoi}JRr=QQqz^pCndDMy{Yx+*P=QPyo*QHy_~4pAgMSVkz}m<94nJA{H_KxuQk&HX zj(z@QK6&wYmeM}fpa~uJIh#$(H9gSU22p4k(}DGC;82NiKYk{E+qa^gk5YC-V#N)?me7R=qK=fXcOYq8Ge){Iqv!J8NH{@ z=h=~6U}@u=FYkVxncmCM%cG}j&pjPn{B_zAT5Ly0wMGuaV zJsa5cAq<_?VH%kI+m#&q?Z$9 zUU}r9%*>}mYu{>T^9B^&bz==YrDTaNbnG5DF4J^IQ33VJ2_5KMNb*&Hw0 z0xtSw6M8D1#$@xH$+!#6ohktxs+V-Ey6yhi{TDs5((E#bsZC^8?x78oWiW{GbDkdS;@|)~@5X zw7|tseKHZ}kHZflR48`nE{lZU26zAYb|$_B4G;5HBgUk6WP z!rGiqfC^3%(+Jvd0e}gwf+fL+AOcU0i`#Me5-_^%GF<1m$l>_Tp z@b37_<`^?OXl9F^svgU^j*8SJk!6Zz(+pPpVyAo3?!>iYD=a8r?^))~(LCDr-e=#jOdaysU_s{<7^nd*-8^|QBzvIV%@$MI&oo>AI#_8rezdPOeID?Ic9yvXo8Lg+EeNg+sxXft7WppVZ_b?nkAIful}!JE}SES4#j&ZFlN9mVWs-+A$n* z3)kZEQk`q(cuMww4UaC$Z|kKrFdf_;*KMPzJF|Z0frx%XJ^6nd$Mz}i^&G669%8`F zN6i|3^ufDbo_gow_cLSgKsMa@X>^=TcQR`^JdZq>uCCl~%QnxIhX?(lLG<)vqrX%b zNy}sT3{Cfi;g`P7gARD)vwM#TO!4>OdvBjU`7pu8Cm&=Jpa)J@6KFp6#4}lPmd!!Z zo&`es%Xtvut}%nT1_hCQW>Y4Ad6=26aGw{Sj2{g2yw~O7hQ0~s;jS`SUh2D-T1%!R zQttket|UPnpE9G0_a7y&?DsJPNiX%y=!5^^$>(>r4R!`II1jgperyCKh;a|5wdqT{ z`X(6y+!??I6SniG4=^+My*GT!pw>*y_|cs^pPl~ZFJJBVD8I;iZck+v@W(%W;q=tg zPlm5)8*T6G6 z6uCE-pWy*Mfu@atZodCfcO&(#-n04K#lw$W$pbF4>H^Kdq2Cqk2n;ZS2R}5y$#-pX zf^cThz?Huc1H(64H-CyRJPA6KEiM3fMwi|N2;*ZTmuvaz(48`A`DtKOS;tlz{D3t! zY#@B_u3q(%e0Y6d^o=YfW>ZPf^~z6P=;vkd6>;i)Xmyv;EDhEEJ5~?Ry1~q$k6q}1&hVn` zvxzq4{ZbnQ-TLTuHwR+h%Xv`Z6FPPyCNbRm9%yZm8C^W<6T}X+Ltxn5eax&bzE_^w zKMc94S3cXqiTR%JWM2@zF;HMCMN@pk!-LFs3-E5Y`7o-2o zW}B7m4CX53_l{ZtYxIwcF$|>>yv(QExkPD&&2_u1DB(QsTu6ojQR}IgbGXK6?7%sb z8v)P)IycIpQ|twpd~p8lldq|)p|ol#_yd){g$|GCH23Vh05wL7kc)FB<1CM;Q%~VI z@5(rJ%^h5$iOI`(7W4Gu&^%~IT zNR}I)eRldJ!6&rb5B0nsYW5bN#Y4W7 z)9v~tOmMo%(*s`n&dY&A-Ss_mly6!-YOBz%u7d|YbOgcpK|{UedU>g=DN_$F`Ab7z z;#!gi7M)EO;nhd46+#59-eA>LCq)4%9XB915p|N2dWVk|M?ize=VyNN{(GmtzH#Gp zC-#0mfyYm-UhTIo1(s^!#|;F$AAH-dbtl!}9r?;o>wKL7+(&t8`&z!C`RdIZc`xtF z(_?u@^XCa#U&=DehqBDJgIm1iol3qLq^ECxlnrI*BeF!xgzAhY&T};Zr#w6u2<0aU z9>EtbGWQ(6f$c*pgQCFE{4CAQ1-_GCYrq$H^de;PLM{JmQF=?c`T?53ZI1Y;C5Q?LDV{jHmP{9bBaIm`eecKrOa zPfyoh`d_~kKfV0(fA{q0)u&ERX2AE~{GZ`Aw z{@FkKXQ%(^pZt@)3;!?w_kVu+?Qec{`j7v!|03`6J(+i3^1kgGZ=e48|M{;@Z{4_c zvf)oGWn+bj%J3ao)3thf>IiI!huAJ)dH05IM zb?3f+0FdwiIc(FMIj5`g!Y085$M7`mp}JHZnb4?Bunp(t!`?CjrDJf(PU%wyXX*tv z@Ky^A18uC+eQ_${yE*b7crNA}=nV@XLcPewM>^(^QB@S`C@0hSZX?3d+s~#t%RJj#dVa0vy4Zqf{#SW)K~uh~iKbxF4zzb}f8O`p9?E89k6(Q> zOYI*oUOd9T{UM~I|LB*0h2HAdK;JhZH$akK{>#V6lhgXAdJU|~&){rd8(xjMH+V4X z1@9*E(Nhl95g9j_B6owKz>nUPhOV(!>a32yRZq#cDbmgNKko8j0nW3}=TlzKJ{jZ{ zt#E9CUfUCVzj08g38(pJS94YPnA@b=1@g?DP6MODVpL!E0kIeM5 zGJ;>9!P`YY07F)Ii!c0ufsYI=i(LhE0!@DUL3Ger?F82XKC_zMYc<=rJMXf~+(*Wi z(+BW@kqa$>9=zbRhqH$Qp@#{)(T_TPNH8j^_}YwRwQMMn-RySjq>L{%ZXrj0@GD1) ztWc@@X_kcxp6^L?AGiV5F*sl6!NEENV$LzuKYI?NoCsd*IY)Flx0d)51`Hw?)!=m+ z^AzMSoWXOTYIeXvFGWta@W=yyv9W=`^BDE4=Q`tQWvHW}lL~dbqa#EY4*70<;Lw&& zEM-B@Hc8P>3VW(N02@f+6)ivf!B->>IhUv5Ut9)j@Q-);Qa17iJ~+&lDO+O29b6k| z;SY(W+q$WziZt>J3&}2x4G@%<$MPx&n@YK`s}7VIWitR_}D-Z zt*L*mBlW!l>j|s+$}h?p1Y@V*0&BL?@$&?TW+T4}9f2vj4`~AfdKO)zqSqj;*H$u;>Y#qcVN(GL!H(|{NT{3 zf9L32D*EF%8CoV4>0)0=Q?kR$bhj`$c6r@Zj}M9u~Zr4SIf= z8O!&x%=xL<;}_Wx^o6UB=UuyFw@LDc#?r$>Z#u{9qHAS1?*>}Y+ssI$z{WhWt6TS{ zxyihirL(_!KeH~ObuB^7&!2ta^kUxW^m&VWG7Gu8%#wB9sSNLy%96r{KRISpRk$vd zMLc#5g9$owo_f-^c$n7EzqnIZn`Vn`C!paLdHVMah`u_Ca{n!8C^!NY0zk7Nxb`WU7DL}pc=Ifp1 z{3?UbZOv{_4!mkeaXg*8nk4i*p7Z?yTu*RCta z-7HuCAa;H4jbC>8u?GgvzVh>Yko~C!88ZeEFzB1wS`J@h@8Av1m3M-;2^y9^baqMZ z(w?%B;esgP{elk``{-zGS8bQ%_=G^9ma*rwm`Cv)HIXAX~5v-Q@m~jeWFcvyM`{ zEq@FylN%k3?C|Lm+=B+rJyYGYhX&ds@$&2a)aOppU)49x2CBKQ<(?sre~wS2UT~p9 zraQOqoZfun`sp`+{d)c4nFJs&z4H9&i6`w2*AlE}tCMh2{oPUM*- zAD#q+0xrGe?8PeBg;2k-RjpT@3lz42}y z2z^>T+6d>_=bp?bNFF`6L6}rmpoJkiv>UGgpHO%dR5>Z144r&DfuXPRNM^$14}5}f z$B{Ra4w>b5t={-ed^Y@!tqz}0b@JJzw(TeK@2n)#@)zy% zzQ|M?BpCM)pO1Lh@M(Nt$0+h1yZUe+G~#&!LpmnC{tDmIwn?ZB>JP@|+UB0y*oVBv zX6j2bb{XGLhBrJ(@Y0JU5M7zsm8{;qw9Ho-yMp%(P%^y#-iOuozjX$4o!8NSMj2tE z#85xaAA!JNscd!ly&Cy%gyelwkd zJ~|OF3M8*2coY;mcSIcyvyQvW)iPN)R=>+%eIgC=0Z#@)M_u&rVdnGW>^OTX!KQRI zvzgB%*nr!Q4EQJx$8<0r*svd&=wizRk!wf!65XOFA3o>c(cikYZOT31*cs2Y$u8dx zcu3BT6;H~g;(|MM>u1S>&-n~UM(>Mn^{<{ZG9*aKKV|Ak<@=zaa|7R-LCR4ceABk- z0|p(*uU6o|;{lgL>Cm0>Ga7-=R=(^MAc*^OmZk+$7|+1to3BovWC_@BZ@hQ)6ZD1*&jjw*=hy?_d1>P(@Etvs5oAnHj-_g0e z@*kk|OJ}pKcslg6wptlOXz-`pc?f$JzNELwabAW8m!%USfI&;By{ym3A6ZDkkHJ0HJyy6?e0r{XJeMM$FG9xlaD_<{hMF@_Vh3Q?>|3X zIemHh^LOu*9rXOe%VZ2U&JWR=T^TCV!8iM5R>Suh-}&7yyJYv?OZS}~%KMxG;72l} zbtw;=cG+E#z!@exs7rfxqety5NRArFzFIs8(@c5n;8*zM;!6l83E(WgCpO^p87+UqBKu^66*iyf|Rhd~~U^(oKcTVrW z`RmyA{nLXFUrk{1TcLyi03ZNKL_t*c2U!yP<2=pJN6!zvEZb;L-VCx_!?*If{{0mH ziC)(i8=$72Ca1vYtlfv7prOja7kiSWe^bC8$};3<`kMUG4=(=wb(Z3P^5OdlHec_% zn)hD1zq6W8W>)i|^at?Zp*BzZ-ra_~DULR5d2G~1e|^wLUkA+}Sd~F0a4;0)-#@FVZPI=*{Be$^`zVJQxQZ1Z} zozZrW2td#o?3y+6UCUo)N$lM$;}h7t{0GmSo`3OL__Dj{)ZGDfaYcW)fnS1~ZpT(5 zhjTp02ge^t24Aw8&^DM?f0J_Kv&HS$nY}7=_+KAsuo611!GJA%@HNoVM}(NRjN##L z`0?=SlUtv52igxm__#D5%BML5UOsv8;DggwEx+w!!?yry+bKDiWgq0a_g)ORDsTi7 zobiSY&c+ra5FJhM9@&BrkB*zj)m=<;TshGhj~|(4CfQh5Eq~I-1^&i+KI8I9f~n8a z_ugB%UU>Q0`Vd^xXOE6LM$rBaeb#=#fma*jtgZM+#~6XbyCtJ&N!5LDV1b{G{YOXo zN_Mcccka z%rrjmU}WI$f$P2F!23l$VP%7$4?noocJWv?JE9A|!2jFV21nZ+{E;oquez?T0?V%6 z`CPupBtTrb=*Fz+{n<=N9rDnDarNkLf_M3C58<=t!E1@V#1HK-OIE?9-JCZa0p=K9 z<`P+e;5}nlUKQ%(V20AxAtqagbKEEnv@H*!3LT)|4=&YWsKL{dL4=sIl%Dg~L5S>D zh;WD|BG_-&4i_49&eRx)fDD1FOPo@UVtNe(K}KQv>BLOJ6n_=FmKW z1E=Clb*_|Irs=7!q+Wa>0Yd(yVs@-=@ZiaYPQk!_Z2`895oc;DA!4Ue=x{K>8dQ>_ zV1dJiJJN4VkDP*PkyxNQn7;lPI(JV)?nvzp3N z2hOHktE1c?-TBTe!oLIe@|p?)Ja}EhyMZG+2nyAy4pP2B@(>z+7a#gK5L~G<_Kq!b zE+4M$bQ``JplS5USEepF%V%$8fa#q~gY!qyF$u;5pN=FKysUod6;F}}3x}r^=jtC` z2@SUQeN4eCUg*X*GM~=UOgvQw$-`@nkb3-#ewD!`X#Y9`7W(B2l_6!gSec!#K66?> zF(BErwo-p(=R>X~^})9-gl6Fn8hF7+cXLgzx46)gQq)fKB0unGTiYfJm_J(w4(B>! zeaiZ^JU=>+(4JK6_0^UIhL57XeG<6U$LD!a@cPZX7n!B7*Ryo-mAqs5r&+Fg&91N+ zs1~-G#M4mMp4ZN(k$Gm%h8KLzz=D6p^0{t#k@1w_oi?|9>H|JYko(u~zH|Czf=zz# zNa+93b1$A=cp~rVTzNEj)W|^WhnLcKj7**3j6L*IHUZ*iHu|Y=wcm;E*ae^XKr=@Q z>3f*tBsgmwyoTAC!8~+UTx>Me$^?B%blQ{*JoGB{-3VRRDsWy%^rQL=jK(i!Mur~c z4{~r2vZHhVwjDu7r(5QlO*h%-<%7KM_SUceO$O{AW>E6r>A9c$;&e4LmX{xP(;ip!p9IXnqy@{LlZ}zdT*P@d@IgJu@E$2@?Lce$JYfU##(EJgPTA zQ{;{Q$V+}afA;AoryFnoE`i4HPG5Zb@#%7wPF{QEkIVOc$2-5vE8HEtpdZ}Q`t?B^ zQ`Ixg48-^*9yV3?M>|q~2Si)$*x)QM22L}eqk9`hAU$$z-Q~g3Rk)eS5?(rxL;vV^l&#HbcYS{H0$9A{M8$P+r!IZVLr?kmiF@MQ z55QH|bn1QT$oQ32Ew^rFMmyitO~Co}S6`eSed1a+2D+93`PK4kN8ARMs~3Ex?)1MT zMKfcGgZ}ibg13>O*I^7j?_Q3M$3N5Bf+rP&)2Da@a-##tQWhInCR=@m|9dZA6y?n^n6c0!+ixI2PCCZ3)G?@&1kn*it@;>v*4n-d@A|yT(sN_F+}GfV zoPr6wkAKqPyeo+XG<&ZK9J*%z+6HpnKnA|x+5j2f>I^Oz=39`y@oDp$uQP}j7Kz?d+2j@2uJG&pYb!<*-PM5dxkf5@jm2iP-7*@VWYsJDFEzls6mAedaFaY zooj12EichAzUZa4P60gRSAY9>3Od7sukbi+R=-Nm<2&Sn$1Jah4hYeq@4N~$@x}+( zIA}@-U;3v4=dboOv#v8{S6;s)e{j>u>dMh79XhH|2<44c@cssvKH@+^$bS@3~;Ue&<7bD2mhY%Exuh!rcW1?+|A9dWjR-^1O4E+#Q2;&`MP&H zRqiJb_Ep-&*~HpdS&}*e7re9+m3QRBA<-e6UEUIUXF+K41wk9w5dpsjYLzYJXjR@E zVZ#TsT_T!u!C-e}J!sIEVa|S5WP?w5@Q1cMH0{DFaJrb#s16Q%TMrE0z^5pI>&StA zgS7(*8uA=_PQKhMFfE`Yi=fiE0fvD0N(LnN=l4W{KQ!=gZwIUCgmT`FC;6+lmLGi6 zEjsuFSAR(zJh>JS3t&IU`+4u6|uF|8t`Xo;UeN0gy`EkE?UH-EZV_?1z; zEywH9EMK8Pw~H&qwY4${uX`@1u*z}**B2SgTMGMU`Gm-g1onUM$Rnpee(I^y(>XpI zeNVtc9=HbgP>XMsSE$GmA5b27&uD5BlDhF7WpkaIV>0PRe!a&a5onjr=BZ~oyE}KY z6~7&8^Pc8=w?EECQ4gH{FazkHKKuOXa+YigDg~29New2`ihYaHdy{sfwc*dV2{NS# zD&s2)k7G6o9NDyuDvl61Nj|T;Co(qJY%V*F-f}s51%4KR9 z5AEvdfOC-O&A;{~xn}p;uJ#%`!i~=O`M}CEXxc;s8=KtzDDUHC#`5DEZx_$gFa9i_ zhxmhRB9Zr-!W;b3`Ul=}ksP$xap+IrNA{Ejx6d+sVJVaV9*?cDoJE414o-QKz}4Q# zGLU!PbPjSrA~CX>&G4S4CB5&x@i$#Y`A}x)Y*=&knddV=n;9)UZ{Twfr;zeCgNnP&XMq@p1}6*=XXwT|Mss>w?23~-dN( zSI+rb(-id-ZuRkIQ=m_7zIVEn?{|KdcQ(z^UVY}J>^^(-bpL}_B6G0}4xH9ze4EVa zr=rInq}_w^ZOdANpeB_rhbquKAdHdbdZ-4cAW^Zgha^LBhESvnv z&wkwClRULU{6&4L{3gM2-ybbr=km4HP}BEB$76Rq2!3-<%ph7DfJ@NanWhNKetxwZ z&d`Z|@YX&lI6D9r*dKMzXM`eue8^zmk-gEYF9c_Cgy#*)ro9TNCMXHHGujORB7lIG zzThx;_X!WPnsoEW*L8jjsz3`Td-49>wYf6w`ydgZfprbE!kB@IBQxB~^6KBM3Bp~qn) zhEeW!u8Zh|jKKpcT}H3*ZcwgasYeDug&UT@%Iu`~A>mTm0k)1Wa4UBaCZip9;G~v^ zmQ7>UP0$t?_^DgS{7H7}ZZIQI01HRu%z2Nl58OGN)v2olru7b(>MQ4NyYRS{=n{^_ zBe+7>bpyxnwgD4-@&!N(gAck2L*-FmbQS~!_Omo3%Tm=uldPM_wEErvQP78{&JqUa z4+4Xpy8Ag2oUg8Tm)p)ZvRmNT319fYZ|#MbYkj%cK$fsNvHptcdws*{mz^9hs)V@_Gi~#JUyQvPo&nAO$3*d_nzEQ1CCg+xV5120Q^%IG(CjBd_+ zI`oIH@NonhEe|HwDXhQC8#*W)>CmxY3f|(WpG^5e;=#_dAH4HMXY9WC^rP&In)ZL~ zmAQdF>G0j=w|)Y@?G3D!d!G zIG?_M@E1>PrM#>Zp6w#$SRD)-0eF0&c) zj%K=>1eY@yMV^3lnRDCmGpHG0lJygA>By#B^@Rnk_koc`n& zf0XZ9Uda{u>T4q{?w_?K9nte#55W4@=t3ZBZ-CWF>=#`65Jx<1+vEe-!k<84+lYIL zGF+?ER2gy>rlzkN+6{>K8yewjXV~EKgBw56%*2g<8Q>e-gF$0-1pV|O4R(U>!`2?MJ%)h zJ>~1r18G<9?v-SL3!FejeLq3*Ewga>nep;oq0h<)R5q!nE%E1lFfiw%Yj&i6Gj4FO z7aj6x<4Jr(grSL-$c6WvWrm-;fNIMzb7eMDX=ymQH?V`BJ_NjEZ-0=wX!zm4%HkhTV4f?F2(8nf^j#B_HeF)loo0HBxV6ZGz`OO<2 zS05h6SUPJ5-XacQ{W>z!o#2m-M)vVPx?|7!Wbm?|M6Z1KzTi`?UVSg+9pA-9V3eUR zK$X0c>4BT?yw>OQ#Da`X7<)+#Q@ZG5MuUFUGH5wUd~Y>n?Mt+d^ATt6NKsX@UK8Q zyrAgL;I@1o^g1IPT&w%VuMRl;de`&)J9z;-{CpZ;crdnqKFc_t$~$2bEXSrt2SMKT z3%VUVFj=M)e~Ays2cyjS!79@AEj$UdSKr_yaOB*h@|C4D4|;_?@Pj?$A;4^RX7iM( z-y4KV5?ZZ0KHC6Kc8fwgW#tziIVbP(r@B1&;8^~g8t8tRo5LF)-aP$HHt%^S`g|_I z`X6R9otK}udb*q$LYUEkUxHiOj<`08{hGAToP$NbgU#55z1%|ha0j{p(3Zh`Z-z1h ztuAdco0!3;`U1J>MM66^&Rs_B&OCnaoy%|YpdmMvSKod6^mg9=yOGapJe=K2fAZMl zr|0u|iz^R2bo$=RQR?A1w<(>-e70l=6jzFJjP1#V=jtf_m`i+j2U!<6ho?M6m-obP zOhV*#6glwdI<^OtJUXw|=q9qNEpKd>b5Ml=4>Az7(VPQVTkf%N>YxF^k?P;kQ3sE$ z7kW8bR_eQl*I)fNkYz{Km$R(&=b71hK75AngWfQG0bz0E&p`)o{)5jUsOBcj(;Enq zttAwZv;5#MUl(ApTX;$;oE#WOH#3kRN9bI@0}A{E95WzanZL?AdN;G=_r}|=X6fX+ z4KyFk_Y|+a@<;uiVgt>P7?Q)+^7prA0N_-1F&(U8pk-i|-%(HUt9y2yn)v*Iz&(A2 z%*vzF-ZT|Ea55`gT{;Ng!9VqpJ@|$_ zZP8NRrs{w^d@2|Zy^pSjzw%pOfMr1er@NWi{_wp#aLW6{ly%ucfSpJ0>eQ(!9(W_Oj)s@+(@YR_+!Ob_>MfKg&tG{}qJE`8Ef$|G4J#%{T zl^4!t<)|v6*oGI9`EGw zRSBNf3oUZ7iPXN+N#NPl*s6g{%KHFIS#&7C5IoielC}2|^;S1KFfh) z-JC0T#Iy1VI_I?=*MhnAxsVCo@AAIohkcLGa!Rvi*L;p8!LakANfZ}eCAjfJm+gPa zU)#w~5R*V_gB?fmv4=XVAAHiGfDRr;NLG)lXL^KxXRnpkP6>|DBM%0_sKeyDaS>hC zcTxu)`RL417CasIcoodKW#%DV;I4^ z&4@mZ-*qN6_4d1->Y7a@S7tD?i);v#i6ERCcC-{E7}^x`RN3-6NK!^4#}QOSl)d^#4R1KcpQnn1qInOb${uzidF8IZLDV<+CPWTRa)WchK22sxOe27q7 zvZj1=L>IqTCs33XPV>ox>0IEDeAHb>wlg|)fR>a@@Qgh3{l}@}uRyGhGOC2WfrncQ z98pgLNtEeB9q{Uvw_GdhOka)-Ml>YO)dpXbg-vmns0zgJOqo- zzG11}*s0*fr7pRY`3#DgSA3IUgD||Yk0r0*HYvx~J_LZ|aW0R)!6SW?T}Hj5 zX}3{{9KXnt&Ic3t3N&ZtZDbw2j_vkboel2XsGP&C9$kSW7c0qkg z_WYt8VBmvCJ+u}UUUKqZZE9h_!?EzyMd~fx-{(s$Iy>i8eRZZDnWn?H)9>q9YHC;5 z52C+oHqc2>`NHGbr7tsrWE@=T&ke@RQXinSY-FJ=!J@2?DQyF=9k8}-Pid8BklBxW zSEqx!4;aEHyG}}e>U1e;aQEAl(JAC+G28Eu|dujy}V6g$qoV)>i54n11h& zZL)$a_0?gs@*lo?OkKFEI=u9H1BhPM?)gLQO=~-Od+s>4m#KZRO^%`C{@{4$_9v$g z-hK0Q{kMP7Pd(g!`H|BLKmA2_hP{-wJ3Odk;LMlwB+s{BS)!hS#=-1!*{_B)x#O>EKSPt={pIi-+$}X(|f=B z%f5r?{XFk!KK|r083bfN6Tye?L&2fF0}MH}eE`rYPr*MN;?h~6rcTbu#dqPDa&WPI zd`sC3VuLR*s?zJw8$PxUy3*(Y{008fpC%)`X=9nSvmEx-zszSx-ppX`?&-?oc`rJ% zmXBP`?y%82TxLW=KC~v#-MXa$Pvk4llTn?h&~Xo$>0lUY9`>d^2P);&&r!bmTYC>O z{E{Q^^{tV+vz_6o%R>)748YLRFN8|)m#O;XzLSX*d5RF9)wuy++OzsWKlskrIbF{3Z=YoGzNU9|4LHBf;9js~Fpa;Nu_W`*Moe&N{9-I5U!7bgFJpl$ zeUNYB1#N@dOZVm^7nZ-eHXAy;sf}m2>3wF8qmf)k;sk?aC-X4yDTl(a<-rW8uPwAj>#UP|mM*P)+Ih)K5V?4?~=; z?dGTq33$n4#SWYMq0=_0Jh-5e&tJx1?4pkvTJq==jn1|mQqL@T!R->kP{ra{MvU0+#jqiL0)x|u|xvS2GYT62`N37MlcGh=nm+f z>+zil9_90q>N)sE7g@dw8)MN7lSIr` z8)ERc^NLiJ14?`*?SNT+%g7s?aCfkoqhOeS8;6$KDli=_Ln*sXWG$N__!6_gR z$lGDHfejofR|npJ6rVgzvPYRo?M!k+yBotq2j$}^bFL13uoC>z;$NOuw>wL?*YsqN zjt}oxVg^2R%?`o`V*?b|2fRjr2s(m3FoIXOHkhgYk`IOs%+3k&;P!*VzYQ)OEhokH zpi7tl03ZNKL_t&=J|)+_K`2nXlc3T}u{^(rvs4mZ37>oJ8ZJp59oX4c5Juk0kj8fD zxM{E%n$_iTR2Sc{%U4dul||6*CLj;Kc1AfzkNxd7`}!xJbm{J;)U|`{PxF1o$FkH@ zpt-o#4zBzATXfJP8yXU++6n?(zH(idl&dd~Ja~(W+cC+PN=s(a-aOgJz-h3wC^&Mn z@ULaTd*E!ngO5U{yn0DFtZ1wAZ%b!zNBQdPPCDGTKFW@t@4w%`{N>F0{wx9cwMQTA zQdn^3INe+gLZS;o1s8Pn#Y3ZD<%N}`{K*OKQY-M$UVa@wDTSv;{p#EVQy3-Z$ze9= z>EN}p!4tsX70{_ANGo4-XVx><-}U{>(TTQtJMU|}{qCEm*D@pfQ9je)-M2r?a=8~W zqv^96c9ISA{j~+pvtq17e*xsDW-YkD4lGq~8?;^o)Dxp}>rrv1@R zZ`P4%>L2Zq40Knz`6E@%kOrGFe&Ij92FKW5r4D)ydj+KL;+f66{@cIGY~J;JkMgN} z^YHIxvzM2$0fP5u%RupX(meyJ#ak^SRsOM3+a|{i9IfTWaecTvvU6&I*Z0>>3Yzds zYO>NIS=PtKb|5!!X_d3O1#X@4a^yR|=-;ImQ2K%2w;w-x|LtxF^v#!FKmfK_&clpR4Fv#|vmOgC#10g&MY|I!rPFqO19;VIGw zfi|-;lwR^y4zyPOX%`a+c;8UJF@wGgl(LD>o4@+AzGrEM?(t_{Iz1ErwmYn}_H|C@ zXDPJk21DAiKuoZ=sd~d~gtBbYaR;*vJk*O1^xaJD6==}&^^u)_@PXt7cI28eFlP@k zq9=j=+Ucke_|a=;$WkAUrHho5tTTNzSPEVVEjq#!-TCpguVVMleOLAU*E`#L>HY*3 zmmf`_>06OmI+^9P=)eor*vtJE#pEZ`;QZd~G~4pfnR_ssyFXCD5#*i7W%=*$iHCsq zhqbjmdg?VmNiet;@{r6oEq%`5_Q$uo*^74xpL{Cs&m@O_3q za0azf^%i>IrP`!&fzi`paL#RxVvvkR+j(i^zMcCu--FD3L7!;Q&Cfd%=)8V44~-8} zp+!gT(dRx6uKf+2AABe`Cgr#2l0&cuwlk*Tsr(Zm^;Qq^@P)q}$JT14$(ICS9wL0( z*}czvhcJ5mEbr=}_jq=o6=*iN3y$&Q^WeEU3=VvdWAEMgr-z+MLub#y*1o~l;3~&j zIJ}#+^b@EEIJ|G^;Q_sqMcwI-gM$sjxBMBe&+On?Kps62(Ci}l1$Tm|<&&+)w&6cG z@B|;c+vo4Y0A*%1?LJGEwXX|f3w#ZK6GSTCKpYkID|d9#S;Ww>v61}t7qMkd8(ghl zg{1_Eg1Nc>;Q!2L13WXB^X*Bn>Z2i<;0%+%uDMp!#z3Wh&`N1wgW`7-)3yX&(z ze9!y4`g0F%d}EUxMpomr885T5RQ<)zJ*Zinnofh?z}o%yPTm8R*cSZ5`EE4-#Veb$ z2Om2BObNka5Q%_kFFDCC5~paCpYsmzj-oocIsm3>iEW+fz?4m4|K{A05zp{OIc>}l zstr#;61egdSdOCtVVdXy+>vkaRlLb`q!={Nb~FTv>-5}vZ56K-AQ_mRC9i{l@Z=g1 z^)G&AL_`kNw}1TWy>Ok^IJ!fWsg`24e+*Sm&6SUQ3Y^#n3Y&k-l&TVCK5q=AQ5 zfTqlhn(NKSr@9SHa=!JduLvwyM0;^p-@$9P51-XQjsiNk?N7k%cIJ>S;0*E35&)4v zZodjb;b?08(Iq%R7&=Rj{%qc}GOHs%doVcQn;A#Aa-+;O{R(#7-r-T+j_IJc?W24j z@c1`-sji=-d=otK*@(_PF%?`&U^cu_u>RwT^zLQGh{B3{6X^q-trUq z5Jz7c-*dAY-ITY~lP`gDHniO!4jGe2m!Bp8zkci1>CfJI=k!TB_!qJ{(oddv;`CC2 zaI?MVvjvL@TjzZNo;ybIzoc>ON#SZ0(`eavkG@BBS@ zqrbL}bhrj7cuFr=nY=%VGXW>`-+RZ-u{`mD>2G+-o!m@b`{2guwKsor`Y7+}UC!Y0 z$C=T5F@fgghaZh%CP20E5B%!jo$qzFQhszs&~{g$p{#Zb@42&6M;^R|&(zZf@rIQS zl<=5q{Z8xTeB_!wW_%%;!9N4@_*v>~00!IdU8l@7+@1Xm%@QPk{#hpa(T#UcZ~XHA z%cm)BoF2IH*y)*<{wN#0{P^@xc7~OBa=jL>_>>ns2LDQxm9Jd!q0)~I)B|6b$Sc2B zIj*hibGa^$>gL~2IL9@9>DtYI`r;Ze_0XknFxrgsrGwVi9h^f43|M8Uo;oBNzLOOf zn+<{9jSqZs^WE$``%wb!C-Z(@KCKcTu#~j)5lA9GzL!7OwGHGD=2$+QY5-HI@N*&m z@Xh*~3B>v?EL#MBXB)g)`iYN?0UnIQ~GFkksezbk;OEy#c97=I1& z4hGc)KT9)-eAqaAk#B7|K_eET>-d19y6DYnD0D|R-yYwIBm#-R;#aG>Cw+)OcztF4 zXCDGI9}m@SHU-N2nK!P#869L+D@*AwJ#eMp)bxp+2d_L*zUi@)Lc_)!_hwwP0g)q| zW+(A$W+oad{C);SS;p6~2OkJ+eOY-NBbpJ#zIROd|H^ac(@(RJ&pW3#UwbF`PMK|g z^z`zNpU=*i-Xji<${oBJhzly|Ym`usy+BY*6H-n1QhB4c;yWYi>lfYUzV?>IR{@PKddmiqJ$ z7T$$Xe*-Id8(?aueV7wH2E6cK(P&mE3MRl96w;m%HO25t+v6>7nk7AhDl&; zvV+%i-_JNC^20|jLD?V=P5pXK++6>PMyy z;*-%E%c!I@@um*_;K~2$3?G5t!9V{0cG76@*@0rp)f2ecWJSHT8Qg-E**pdh+jmJjGpjtfbX-;GQFnt_I#8x_mfU~^dwBJoN&%+kZYizVO7B;$Oj}ny zK`^-lxpYo8*J#tZJl7I>t|fWo*t$DQyEC!slKZRh1V$}%tCJj+;|IN!apgs0gCw7| zc*oZ6xp{vP-9MZLPM$wZKxH!^cvhbBdf0?+;f)}L*=sbbPqdXE2_F(3w1c%B{5q+# zO%&h@oP5bqUG=sDlcyApmgU@0zMDz;3PdiO*SbfUL0sItzQLx>;E^naeJjgH?Y#Qi z1m78}fpFTaGrLYgM?;$Vf<-z&lFTvoNKKJM&4%i$@bSRAv-_s?mET{dR%SopM z#H)M*-1c>W>@{2|6SUdwwH}*s`k9g+8xw#OceOBtM zj_JI8Rmy6=gX6J!tK?7k3UhsM<{~ucVtRBLhYX7HkNnkWuJo%crBXJg$CL9>+o) z{FHw1ZawmEdpp=Kt_?Wm0YhYo9n9w4%`E)&*Zw-+0R2su{63uJu+O|GK+fkW(x<^k zdmEIM&%=I0C%p2N`tJw5uZe7iv;{L1kinVh%33Y^;TbS5-q zd{H|Xoxm||cb0$7Ky7Dw1&?GNn}SWd)y{)oph|Dj9F2)`uL*v*`ThDSe&aFGp#l0a z@JpuIy}r|!fV#Bs9@x8gZe>=}cS7Ipra<_;nvKjJej=M9=?|il2@D1Lxu0Z~Q{YHd za0X5AOkbdHkJKqJTES}u`f1#;c8-G0=_>`6c>Fd?AvXBrvI69{-+1Tr`fp#4Y}t4# zvuaO2`^4$-tB)ORb^tbO zN)KSobei>=Adi2{7y>=z9thx@J_POP>{tqqc25;}#p2UO0`Gc*Q$F7rdCVs5Zmq^i zUuCREUt~fb9rPOLhamrEvkm-5_Shl+=mN%(cb1y7S7&IW%i>8ey&Lb4mmLIbc)Ks} zBoc}b&OJ2rGVW6*2)D_QcT4#RnH^_2I~<42Bb;E*Zv$NVBnMvc{=ftGp9QP(z_qUo z4v8<~kq`JjrWr>8t^UP(lg>8?wweJx^aYOadr(0xLH52!tKac0Q+;k{Sm`5l+lN{r zTg|adG^ld)+4D1JlgD6Z$0;8Z=6n;7CG$dE&Tz;GU#+lkDdf0n4X%T3t_^N?25AT79o(l89 zand3}^1#3H7@i3*)Q`#=*eRo12yjJDf+ZqJ>R4)thPs7Go}=F;;P{hAmb+;{;3WYT z8iIg%FHnb(yHen{jw2c)3;J}YaSzQ2NS%lODid$f8yfJ04jz`LWK}1%`@x4Lu`7Rd z68H@aHwbL~z@sh50_S$C+-#xuAj#*rpXHo9m~JYXFYCaiO7@b9Dkmrux2dpj~?)IF%@4j$m+Me zmG+Upy4Ve{)W>hTp9n&4?Aigd!X!NDZm^9vI0R_lodu$y2`G7hqoEu}=ba%m>q1Ee zQ#pR$ z@~5XKo_Qh5OLVrGk}N3AV^}5^%>VQa2-Eg-Zwx|R@?xORQ_{bpCRjU?zldRMs*Y$ zMx64dOsbB0-h$%UvI5Xd^w57f8i<(1jpnELz|$Le6xt*I$v-e>@B4=L&$H|3CpWV+ z{QbAGg!r8dS{^t(mW^?qc=n~vYSuU7FWU0X#w{;&KIP|pW1unyclA9SRnLVDeeyY% zv=w^CGCdlI4f|vB3AnPXFEi2~zW3JYMjjm8{p?m|+%D&PmOsrdxi3ZjtTsq+DFCI5 z;$_R=L~Caf$&R-TFzax+wj6TeL=l4=X_}BINv%zOFLy~PoHJb ziVnNSuPZ-0y6Xp={AsIj_kl<76_&r2u6hQ$24|s3Z@E^nwkd8p8{%v=0mm_b3~bw$ z@&Hv2$!i~-YrK7a=hHltxqkZU3qfbTshQ=|K6CR#cB1YMnxRX-_vYQo=?^W9Boj6S zk%x!TMR@M=y-N`+${2CoKf+If1_S8{if0+>XSpZ6d;PuByKmW$C>!@>*5oIDl#R)* zTrQ7hXwammNi(pfqv|qc^r9c`fcnrcUDIg;m5liW4rJMP>Qe2@Zfw22C{RMnD+P2} zwdRjJd^9=ay1~V9T%F^I4mRP9uD4Gxzz(Js>xbY`X9JjAHi6(HXb33aAAQe+h9EbiNM0rN$+d=#^?i?$$IMP$Ga(vT}YR2 z!3dl~%l%%!Vus&xUO^tv*d;XV0KB`2ni(@wYNuGsatDl^1{QDH%udwMMenK6kARE+ zfJJY{CFj0?DE;7xPSJJ(j=znLCTN9MKaWQy?JQvUuRkfv`L=(uwylH!`2@!o-n zZM$t6FF%a0p|kdY8}F8=+BLTN4__Wi(YNKPc&uD>mvi`bMwZMI{K7MGl95g}IOl`T zL$rPmm-jRY$%pU_o{G>qmorrYyGgJPkLAW|>s>xe9_Jps!0}o7i#xYJubjSJdS50l zZ0fTQP->4Lrsuu`o3_BFnZZne8BKK-07DFt)WKFtnvzesoK$KzgZrc0=~-$%gQoBT z3?%@Emr~E_Kof&(#?VDU)=*qrZdp1QgzF?ZZzqOU5_q_4LhVE!Gb{)(a|rC3wnSW&+7Yme+dBx121Kg2?(xO88-ib1qW|w1%CN- zYtl7fv(60{xbV~6WlLpkMQ3sB$eyAfZd>M^MZu-ddh$gH#4J& zAMigbb)`?-&AgZrM8 z#iJ{0dZ5;a1Fl&Po8alt?aXw>|8yw$1D9Xx!H?JA=q|CT?}5YH@4j_~blsO0R3==4YVd%%p%@jru+%D^rvCMh=%akVp2@c&+UV5^s@ zQ24FusO|*`OS?DwWa5WEI}WV#@N8y{eU<0fz$ARQU!v_dgNI;0^w&Traya6(JO=LT z1aOv}-gx`9&JO-4n?*hL^o#wxh1tQ*${lp@Ko>)E6hb-EDQWOqj254D6Eu1lF2#kU#C(c3X5G=>ZA`!3BrPn(Yz zNm*iQ*0MD9W9Wu@V6~<9u3taB`ZvD=nDqVW<)6gwo_#Vw$OIojlR>EEQMcZUv-aC9|Yri)f-)$3{KvpYh+Zk1?U36Ac4+IBMSmd~`3EwmYZsz|>pa=|gtl zuL|E8ee)%P!kGJh2KQu`@hxRcJJntr=p=_7C%~9{5}ge{;}7sC-~KeLG>B@@Rlh&R zxS8GC@hx5k9**fBiYL7AF|%yDsSNsN5CtuQ3xT0PL-2Ed0t(9~8yse4lfOypTkhE_ z$Mmf}opkr~*6Z(e<_awRJ(XE$pG)zOj~4hq;ln5X=*SPx8$78ixn8|bpp_s>FdH1; zn->}SuCRwr!C4|vV)1E*IbZzj17>VI4{O+&$-+u}%mbpppc~jen54JRz_WV$USZoE zdnds!fj3Lq^Fv?stK35tep3c1B1r&^o_zemX&G$&GPo;O{D_L<9!1Q#C2w(>Ptjne34m%rM{`ThjiU*2{9{jxrZAAz?e zwQPb1a`M-(R$oKo>>&Yvz5l-KB1>n70**tJfFUT8T6-GZH125>kg4nl#y|hJLxz@% z8DJC#T&{cbO6E~->MJV-CJ4(=-q+tQ+JjD{(#{dhP2l%xWm=xj2JZ_T z#__cevQ`g@tFM&duR5lqTo*TbhtNxFQGOQL-u&p}(_g%w0qSR;ov!AA!M}g)T9?9J z$*eTIXsX_RBlrhZa5sPa5G7>(c3-mBbR^Y&hs4u|jnB{>e4w;A9=kVZJTzD<;S{(h_(8H0yT*YXX;7k~Umr$@8vWQ-rY<8xI;&O4Zp7u>iVS+-7 zoQ`v{eRGZgqrP$0PoKAObyP)X{Rd87^-Xl)@qF{} zzSvpg+(FRc_hUw$vb*EcWn6IleB zAH4H=zY+S)S6}o!%_pz@I2!^z6IV;Wc=TCl;u)`!!K#1msUcXt(R2+4UlSxfa}2-p{92%&L9!<>%d?>DjzTdnJL6z;f&}^ea>JhJI02{;?rjZn~(0)z8{` z^cKj}#q;QnKTa^rPGjo~=90%Q4Gzf|z0DvmZO>Bm$kYddMK^XZ!`cCA{U!IP=&`e7 z>w{Gzjn)N%2j642`arHP@>Nlj2fi|dkNShWFwlNU9>&A3cG`C!b9Db#k?DT;gY>Hg z(gu8HaKpa`^+E-4GJpOdv&r5KhKvPg^Sn>V$`l;zh z1)kq!MwFX;_gz*i`wcD@zC0 zd?@wK+rumhcgkqn?|kzE>O1EE0JYZ{=(EerK8{}WEnN{f001BW zNkls{3t$^GQ4&!8 zFg^i>z(Vjc`kDa80KWVmyo_vU4gQf~_~}d$yN0eHa&%%=Ho--%XI6Ur9G?<>(cncq z>I+uxb`%=v@CJ5S-)Kul4W~J)5?MZd_f*Q;I(9x&u!2~H+Z5Kc*ucI{L<+@+yJlc>Tms2z-cM7CDM4Oe{uzF zbqlBWE-i^&-(xSZ@Y^iPQra&vqnXUs(Y9A+I+%WDYSp+&9Zk20`c_FPAiU&4zZAQ1HC02f-xV!9eC&g?qXf)BtV1{a5Mt^P1L zwBbk;qMpFd(Qg=<1LL7-KrWDA7|9@AXX0{khY_l2;ACb}w=(30AGFmg#bga-v<(V& z;6w*$^mt8uvMt{YqLRD1=`KZbj{&4?xRF&14mS z>fv7}AqYdKb;~OrAf9r3Y!H=PSVNzW4A9ay!|7X$dwU<4q8Xi~UTv9r=mmY2rluo= zhfFyN$_~<_%OGbP^ubl8K!9uKo}<}F_(p#d#3_Ro4|g)H59h}TKrDsze&u5skdj@1 ziLUbTvvOUVm2QzJjlOb@XYWVaET{C3^0gCxg>QLoqYs^>LB|`Y!YRMHNIpOD8){R= zr(E+za1uV%TV3&|KG>+t6Ci?Hn=Nn41O9Jh6QEz^^Bf<=AK-f_LFP-D>3jB(M>@kj zx|oi0&zDbl>g#Nsy3G&0(#esIly5$+_uNqeGiMj%Oj&UsWy*slc{xe?y|QGYjgG+b zcbT32%NsZPxe>d0w_2fgI#z7zwsJv zMB53nzkYh_)jvOdcI!q1kLR;9?A52A%Yba!FpyDIdVQAvFyrUbK z)rY#hK8`csS^*9?bojtQ5B$>MyA$LXSZ%L7lHmOee`GOu`0Vz_*$C#fYy$J)>GNy| z^kCl8w0!o_C$jTwz6X0Lza}ii=Q}tJ9yS#~jI4nuzeDeg2A?~$uAW5xFq?r>W>Y`@ zAn(!sx*HK)df?&HV^2NbO=#}TQtqJ3AkF}g&$m|c=r8BFo~${)N1x<=kmKq?eNrzk zd`#WiKxO>OJJVqqD?GWnV`=0?ZtZ#yEsdr1Z3eLty>}^j>K14H602jIplMrmZMiO4 z@Nl*q7H`$*Z1e#i;$<~wY(@X-sc%{5;tq$ACAK}v$vzJ)V9-9`*0&i+xSl|ne!l-M zyo9d7=og=Ukd0YBKHa(XL5lJXy+CEVQ^^M<42>O@EE)X)zv}D;QaE- z+;86d;Ph5@i8UjADGzgBeC4@(#^908jzy$UuY|!jwlL7AJAE40J-3baGC>_!x)5ye z=WhgVL1Zvrc~XBd&k>%L-;&;WP&5w#0t?^aJE>SYZN@c<3)1M z06F;G!v&-PE6$b$H-0Ohg5nu|+K)tmaJtX0k2QEoo%SiU*|aHsKt_SY=s7w|Cft&v zpsBix4g?W!4axDNwV!$ygCMr24?bjkW5(F~fVV!(PNA9E#NVX^fHvtdb4B-Nx<2yl z&Ag8%kd~f)_Uh@%BbOU|;KRDtZfG1I`?kyG@W47PD`2+sI{72U}?2OT8TN(`BhS!k9yCvF}hS zt1bdBIahCCS1#NNMg%=@TSDl!$xK&2)e#t-itDe^pxu7(<%ibN6{wmu9IB%Wc_b?x zavaDV@o_fu7F;$Ky5#1Vd_Q

    lvOL!LJ6L-b(W;-P$}PWd^G;PF8&x+2%w0WWNj|6N{fDRTWk z8Te~G$JAXryA|qSH(13#8jd=J2G*hNy0#8Yjdkd(pQa29UD937;cq(V=~VbO-6;WM z^BaNYUnkJKnIP?<(EV|O%op>1=98J()Y+pW(E*&L+no7>uCy{$Ue`xG*ISPa_#m&O zj`HOl+}2lqaduPPmZ!d(fOIFj!oK;@N2kBa=SDsX|Ib{$+`!WNr8cl3Ywdi}*_XHf=)W#`bplbMg_6a37Q zt)&kKTlN}GxRSPf?L4-a{4LwOg&BXO#o-m407Sy=_&grt%h4mh=?qu>a$rkd;Bd${ zycI?NpxMBp7zd2p^6U5VOBbASl+WAG)StoUmf>pxqWDDM7Y5fuYw&CUj;9H92Y2zu zmwZRjtmfOl{?}Pb``PKCN1i-g&hE2UpM5d!%Gud-mZI%I6#fwNm*@8AZv&v<)80q^ z;n#IHtciYZ-@2Kl&cE)?zds1xN3LGWdzLTvUBInZeMbo!(Eh&NqOS&@hkfutuH1(^ zTM<51Kdo0i4wsd>daA0+{pgH1v2S=kqn*Hsba+P&+UV2%fgl zw$d&r!#Yr4brlN@{(DS zu;o)G!@r<<`ZRofnvLdeW~Ib?*KcC!^yD*7rv8(sYtKENz{HG8>q$AFrczL?(WM1b461vOwMV`|WA@UHIJ6W-O0e%Q6;fMnXXOy6Io-!ahEe@pO7 zaJwhYPMXnmVDSJBe*awifz`G;>y`ZVWkC~qYyfUIoq>OsFv2BJXuuOV{Y&q|p*ORA zvl-0DLuPv9FJ|g8o^_3XK9~Aq@7!15DSy+ifvfB}2B>gs#&5d|E*m8ACUxx?>RnCm z>-v`EgO~5m1BZvo(}y42YJCCE6Hh(X;1W-0Y$BJ`c0qS!i%-*yaX8%~MxPVlOhDya z!aMj4zS^fGZ-Ua%8Je5;Bm0ibW0TbfUf4*msLuL9SZa{0oDOS8d?hI(!t?>Yl^LGn z8_O3M^}z63xWN?H1znIIF6G{7WCJ+(23Z?K=DahI^#e1|(+8-xHUo=39{D?cl$Y3@ zpP1?20CRONUtWDXxG0>TY|yN%-@840-mEDeEtRD^vRr=n!M1%fvEIX6`MkS}re(Bz z1C5TI!e?h*@tG)d)WfD_;85(}y2e^`SfZg22b_!<{N-Z)7X`*9#i*2<3&ZR(4Dd+3 zPPv>z_9~13$~Y^hsZ(dU(xs z2U7|Ena#Su0j$&)wVr?*D;EQjwn3igC-+>P=I;HT{>jQ_H z*g9J_n{%7~kOkZZk7Pv~TsH|y89EIVoCg|5>dDur?J&9t9=oOrntc1wAX6tGSc8Ks z=n1GEYOs=--(-+mYBRiWPIn>GWI!Ian$VHxhYiqBe5GsHOMnKi9Q$ zj+WZ`Y>Y&=W;A`rk_-Z0ygn4U9?jDhdXqNLGJ8uG-q~EcOkgs6mA90WADnCaI}Zcp z6F;?ej&%FCX-TbhBBzezq^?8Gb@8Y( z9FOi+CNrA9%Py;bli#Nq%m_4pns+mQoOc!KN+zv~J&QhjoXqA7*GaK;Pk-1ERJFd%fuE|qbdmgz`jvw|g;Nk7F zd~N8vuI2EfJio$%loww0tMSO56AFII-@ebgb6t|3It_vX2ZuV^=Jy8Qcnq%2T;{qP z_@rDLkYAV##=Z{V7g>gSJ>O4!C-1=B%4R@53GrN(DL(hy%csj(s%VDD&5Rul;3C?Y zuE`z7Mt1y!ceFRZGO_jQ=Q!5-b%68{f&VtUWq$I}&C~Vp@X>p(r*Hnz>6w@RINv+` zad$lZ(J^C#R{5Z#A(Yya*0*{EE{P85O@P(w>UV6jzE?y=OZSH?LEgc8_?^0Q6kH~& zwmPFXEERvrl56R&ypHjOas@%k=|mfn7T3&T&CKBHY;}c?+9B}EQ?MR(3BUSE_|Ps7 zPD`=J9W)OUQ#QUfZP9y!-=v*=oX?xw{xF{z$hQ>ld+3q8m-lo6&6gSgnmMZi!b5#8 zbUM&6kRVU&LWY(R3?EY1xRcq^5Az=I&9{GDegv`48bv zujMMvubeK6FFdwiNxN)+%?9WsAN%s8%A#$guC{Jg?}y%dmd7`TOzUj$tpDlKw?hxr zb6`8zP2lI<$d0|Vg<*2=qnE(aQdqmVe*dkFIkIDD2G4@aA10_AoeG{KM_teiQ}mE_ zXXjX-eYpF@?d&@GX=b+nf4=Sn{I~6_3;VwFz2}@e&+|<}=7A(YCLn+sNC8CfOKrP{3^wz-vPM6bzlVtcBZ9_baF1Q+?hu8oh>g`j?I91=e*e0)Z0T(eTkhwwbQicSJAATgfpGROpYWZn-q%EHCGTt6#OK-@u5Q~9 zxS==8bKQq#nI}7~_H_^>C6E%Fy0?-8eLpzpjIIP=vxK-J9Po%YuyiUQq7QA29CRjc z*%{0V;K>1lUC_mM$X0&iGrZ#yyt;7vtw33x4S>+r zR`Aq7Id$pF?zUzm!Ffm7XKl`$eztV|F>CD`m;BQGT#&iit8Z&cHvQ3mbcUNu;TIit zns+b@Hh;o<6QR~1ukAr@@-Xij60qxf)k}6Av*60X=2yoi6uk1I7_h@gpyX2k;G}@I z%w-405e~M193X{DS(O<9^S^Zpl(OgJJZq2xKZDJ*{f=U1t^@ZFu1w*JOmz;l^V58R zHpt6y$s#apfKd2!jNzrShL*GflRC)GEB-aA^MeH%Qbhw$2B!e4nv<_ml6*s@Hm;n{ zGQjEVXh%6nb%!s&`R&Q620dF+qZ ztwYLn>nD#N6s~_KV5S4G>cF$`FT9Hu+0-Xn{k(__dO!}ZZ6CpRpzpxq8;(=a-LE~G z_ZKs(c`5@0@1@?IC9|)-FZ3=DP-WCJzlhou#m5T-o`$@brf43Tr9s72%a$7Y@2ubnwg%mw%!$=Og>*A{^wu zd_PHjx>7j>@Zr5CMI0Mn_PW$4i?TO4jTz~D6?a8B0WpeazzL|FC_Vj5wnH$GL zPn_C*?vwe*a%Nte!oQi(&S*vlxl>g?_(Thx(Js+wfL}(Y*qP2wojS2S`0<+< zJhS*pm)v|Yauo%6*nec74Q~wQlegda16&%V6#|R@avhxRRnAMl{7n1OFC*a|ASvRf zd@e{zbMe-JZ}@;{=%};vw$HY$`a9#riI>u^Z1F672md*FqXB-Fd&BVrnaA?r;q-|o z6L>zGrL%W$*WYqimQLn<-J}Nn(E~bC2WfPNkNg{*8DQpnrNQ&$XFi$TOHXbWUv?-P z_`E*v`Q4Pkjqk68etri5GF&@_rCa2TTRmfo;Pm9n(7* z$;^IBmw4#;-VO%_s_}s^84k|zwq`Z(BRH3w<73S@TFy#Ovt%{>a_GGfU4oI|9vje^ zd*@uU5&Q!*2T#2l%u;yyr}Jq?WVs|WgKL?lzD0Gxbx?MYZafpKV2(iRdl!;hEe&K%#4K5?u^%is=QbtFMWW+t!6s+YX8D+qn;k;l>pJzZvR zz4MlQAMe_H0^{1+EQn*5v}GiNSBm-ktsT2zPwue-%jdJawy#!&arPNqgpZU97Vwz= zt#6=wX$IYG_s}H?V8B4X{H6ENy>9g|&1Gmuet3)xOQY?Nt%l~#XmG|4@~`jtMK8V? z3a>yxqDKJ$+6Me1-}ru%RZ1DJ_N3;6i)bN-J-90pc36j2rc-7U1 zyL=LF?D1lPvP%+};#&Zt-;}Rh|8ii5aEs=$ThF7v`ad5LC-jO2KAbPyt|fHqb7-Y_?V7A? z^_}FvFPqTeBM&{%vVqyQ>u=1bp_KE9-H*u2H}E9++zC46F>p4D;v4EoT_TG>Qv^bs zfS9844yxv2V0x|r>k^X!dytf!e?3+koMEDZ1=z8mn3i%lzmw=S`uU|=VarakmT_Iz z2&-W69t9Yn*GS3J5l9B~s9mz(nENz8<=9;_MtBmOB_-gxgE6o+6LEwf`A(p(>5_u^)bt4D9BH8DCvKv-l66 z_!gL)3qbHbIv6%8Ve)eHKXuoDLk3&_;Gg~S&iEL}sBN(SeEKWzkXmwSkfq>k20_QO z377YozUS|MsAYU>P@luUi(eULU^;XsNa4q!Q=LuQtnP80iu5VbleB^+W%c*u^_&j! zo4~?T{=BDo^64yT_34n~4K%OFvgNDuj^?#D-qrxIve#FGJMt_Wn9X@A-zoJS&NIiK z=;RP4nPnTFqC>orq3tmN9X#5# zH2$&ckX`Z*f0Zw=%B2ZpW)@}o1cPcb2=i?>0U|s44(Hk-cIil6wguZV!DG-KylWZ! zwP$J1fPMvU4Kf0MfqQEG0YChA_+*Y=hXz~BuCB_+KYliP1Evo$F!#=tbMgwP1x^O^ z2D*`_pt-goPd+;$7^MdJ<1^YwRuAR7cHCv`Nxv6#FnQ+GQ+fFCc;B=1(8-d?Cm+ju zn|r$TuDiC|@3=91_@5L|UZRum77CFY586xq?d_88b~BYf z*DvJW-*?)`8QTwR=%lW(7CbYaL7g3y9A#+!I0&#zJ0i6tMG5e z(zh)I6;B^KQ8+p<(ag9kqy|LPkA z^kI`;r~QORaNw6Oz@=O;NN$N7bSR}%<>-Q6O>k>AQV`i$#nf4WB>tV_1)m4+l9DUD zc-TUxR&tpoL;{>Vd{p<9FZKx8>DvVH)2E@22F~0+*+a02ziD@oW49k!1;Tt(TIK9{ z{&jsZ2;pA8K0vXwJ$08%?dL*=zxY%QJHn})?`YR%B=viJ*bo8wdSUp$BiwYOj#+Ns z!Q>BUIVy*nY%g<)GE-LbCxt4uaOJs%#5-tptYNV@;Y#JwE#91?g%gAH zHBjo{GS_%PE96z2fqc&4MK@I&*iG^12Clh@BR0I?87z0|(ir*q2hTy>k2Pz#x*_$DEeM@aRfJfIu0&DWCNv?skh`ur?YX%!w3G#d#(kXg5fjy`PS#D9MQij zd|sJ=!?}ibCS`b+mek{6LksWdH8>1@Y55v^)6dfDfu4g`-V2e{dz*034WDiR8otp2 z)5gBzDZb?ERMt7#r4gLx>t}VsaHik@27l`I0Rmg5wyd|)001BWNkl8!DUZHnbWuACzBYpCnN{?sPKTBsnop*59$&RzH$oq|VZzk zhwl01b$}4XLze;NNJngq7d$T<@_ffqVCfx8?}*-*_bOk#e{RICu!Rqt&QaniyzG2D z^vAOBuFki>d)}PW*SWT|c$mzZwsOEFkGwN*nR*^H$Wz=p4!P)ilkt-^D27)mExe|^ z=||eR(?R`U_$Z`c(i48TJ5Y_i1NYKJ@#!wBi>(ZB`grN(?a%&&Z`oe+uCEIG=%d8? zXXvKjT-Nz#WoW^iU$CElRynwZ=jH9`hd;XgzHj@1?V_}^dQ@Zt;YTO+!StDw)4?yF z&3Okq@hRWpd+gy)ZI68FHzHDQOoy_`(5v5?*|F>lo3@PBvU52MPSlr;szdsZ53D@I z%S9^$c!ytkXpfHsu6mDt9n&X)LBYVbb}O=lNWG{&H&t}`lX}e@GL=`mTs36B@3q%# zfBr9i*LLBh8EE|$_^AeZ=~Pax`9_vLK6g4xNu!_N|D8Y5z+eTr1}tQ#9EHcmE=T1_C|R^xkQj;xVq4X}o`zjzq7X5gkj%b+U39=tXL@-5Bd+41%4OBoDiCi0q_ z?#zz9cjl?};ozM?a5+2O3AzGMo0y zgB5&%qyM#3x`A@?EM;vVcOd`hWMnkka8Aeb9wi;lz*3-EoyYElt1dIhPPufTOXq=s z6I}BP3qP61=jB_HYvUiftvrFBdqQMNkZN`nzvrjLzu=$`k(7rNpPIok@67!@_q=&; zD*a6Oao)slqP7{Q@X_3Fxjh~E}I#TJOsQgvzM19h`cD9g{_&O>c&7V90-gn z*Y0f8YT)cWuFEcqeh9-2*tE8Rd3>PvozUY8_?s$R{GhP{Qn(*^_=)Z;yMn=MuDdczq_6IVF7zUKKUIA}h4(U@3lP1ZcjQP`DD*mC zF9>b+2-@lck90#;`4cq9(tCaQ%-$`&@hMMmT6^VM68wBnx|^PnL1wj1gns2CQL78z(>6 zm#<-0y#|lb1tT=*P63+q6QuPYypf>+<4%8#CD_O&JoGJ47SrJaU-Sm2x@f~m7E58Z zrS)kNyz^5VI?<1`w{k9~NNy6DdX9;;o>1~!ursY@?(Ngkgz@!qZ;o@X*gK4@yK1M< zn=2XtLIW(CaKAuc4OAnCKZ^6Y4!eE19{z`?Ts9E!PCV$vYZVJUFm;04vG{VPT#4Y2 z97`wBXB&A6n?O~r$j7jPDnUr;1&8Z33$}ujI_A#H&_@}6pN$*80#6N}_J^a918;&5 zdN<<;w*;1sJA1ibw;+ESz@<#R^-d+;$ku?fGN)5ZIXsd&uHh8Wl2y>@+UhAX0^P32bQkn<7N4s zSzdvpd~!Jl+u$My_+VHqxdEX1~e5UlHPhMF%$%lKaGQ6(q^1stnZ^=vFXs@(3es|>E(9T+>u0$_V@u!WQOK_h@lT7)C zHDHUMHMnstxk2do*LC5NKgZECym(eaJEQ~EU6-vL*M<90i6x_zFSZPkz|RrK{DvKi z1ecM?Ol6)i1~&cyX%I!1K~fH!d00<>Acju^REB@ zPCpAacowRle&Nhh+Xuhzze)SPBm;oU$~!sHydbi+UrxtJzV-*DBS1NxPh&i=J@D}l zZ;yTYHxgK0l?{B}yuIp8@5mCX8`D1K<~Y7WSJ5}!Rm+gDC5=b_ozV#`SAmh=vSf5t zYw2UpSH{K$`RuU#uegJ9?tR5pd~JiR8@(q@A6@9uA+DQ$@$!X2mF47Z+n2uK#_g@| zdQZM6W5>zZX4lWbu|6CmNGf-o;{|z;aLMIYZdcuS$M(%%_ulPYcV)nO?CAFK$L9mr z9n|q7)lzo`;PLr5c=fPz0xd=aluf&gXpBw^5b_fo8K{%RpuK(?JO*)PFofW@ob|}n zH)IC#6&VcW-KhjZCyqWcpBU-j_E0}7^Jr!}ANu&OW@p+bYvUVkdsXcG#=c9t9tte| zI-)rE40J}qkxg5fnZTcbZ%;Lwu9V- z8>q&%&p&UFugEOcv@h>sqN_hLi0lKl@a}=b@u!~No_OR@tQPk*2|CpC&ZbXY2u?d` z5qrpAfpORgZPz%-z09~TE=m@k)Og`}Z8KvGOAd4YSp)U<*0 zKYl=0HWYHscMY(K!m4nrV2*tJdY%Mc29-Rgze6@Yoq?pV#U4*YSTe+?aAEce3 zi~cG=>!GX8$U|20@#SMrS{D7(?thj`UehI$>?#RT>L0iX--25+UWYQXX_n1$T04CP~eU8%jb=N_aq;1`OmqMRTqO&VRGy>9Qlo6cF2G&m?8)=gKHfoegs~tp}`)} z>l~imltSnjJX?={S$-YFqwC+^V2dwsJ~yJ0vgYk9pwrX*!8W}3MVAhg<#avo%W*H0 z2M$aCz^E@Ucs@56_0Vy$Q)obz>?ZhI*uUoj9NT2i`-g)M6rOyaauGpKFilU&;IWaA zbx{@G70A>>%I4oVQoB<#Ux%*zD^W?4I49vo-Ts`&4*QTFigSp^e!I#-Z zb<|xPpf68ZWyyJE-DxTF(7?t$PbRQ5b4eEOS6-b#tpG%zXZVEIz8{$*ygFq^^aO{3 z-*t^wmp@aNZ0wSh|=sR3=y`HrANa-iawYMU*miz=LxvY~bm^xzv31^OF{?)fugPa)NNJjwAe6XNm0Xc^A`WNj3!f zOajgOp2`;Xv4sZ-J_Q19C3U66Q;_v~@#BJQ5}M9jd&ly=#~?cRy+Ur*8T=oW&eE)3V)wY}b@&Ptw^ihUT1DhZD*8m|ML%5hWe9#xA z&iS(7b0zutUslFS+KJM^U3|O#<+p>5f3;p<%1;>@n;HDf@GE^%&dXo0*^%|-E{lzC z!WSIioF9mk;O=bR?R@up-?UwSy8tugO^)k)&pJ3@_W!Hw0O+@zoL5KHF!228W7~Is z&-ZVS+<)(O^5lsO3NDW?r2jC00Fqxp)AxaYHp{Pl7Uh9Y{Mz>LeIJfEnHm3_&)x2R zjakdv_ja=UHZsx~E`n%UYOb8?_v8d8Jvgte_H#LLsu>^!;6NqGu5PvUq_(O3yulW` zCck}wdeL+3QRs3qwhTS(V|W@FSNjC&K4Z{;ueaZGbpp(HCvOI0MfKbM34SCh!w!J) z_<(oYZocc)+nZi<)Ao0N{FehW@}iGpdR%q+n)3!;Iq8Fm;Bn)}SA8$_Tc>2mLQX-) zr14MZJ6w38w-q?SZTThr9L~l(hx1L+OE0&hWR_Pw{y=sfJ(f*;KAq29JXJbJvN_P5 zv9Wh8efFa~?)q);n)5{yUw$i?qW{G+e#Xwb{HZq&nZjp$V*DbeZyVA^W{@`Yrf)dF z9bJrVLp#1wX@g@0pE;>sUd&8#5K%ynLu2}j6}$={6~o>BbU*_?_zKrHyUSVs1uxhQ z@KBHbCV)l{9sDa7h|)bSdEM%}v28F{T?GbhgVfPecnpr|domyo{n}74Dd5aM;}pS@ z9b^S_YxRTKL*KSs8w<|34^NRV{f$BX(?_$c{h`ln$I@rRdv!MadDUz0>E}$Yxjye} zC>M+dTcv!BHvw(O>FqWZ!W!L`mYAb;e*R|&>tVgJ9U%YQCo!nVB8fsBzjnb zZZ@L>flJ$6t_2DD_x13zV?QER-7{OZ2E+WoJ&azLufa{XU{qpTK~HClas(@oS^Un} z5^}tb@8NTNP|!2|I=;;2b`T!i_+I@@1G)&k?vHShU8-z>cTL8^?0Ry}o^*_MdCoQ3 za0+7RukVcR`1V8jI=JUm_oD-TW4Ys}Kk@Lvl2|<7bn6YBxfE0he(HbWqcl>!Qf(GH z2sAA@1t-81Y+8azZ**yPj6C#LokXW_qu=kcM^_SA$R}tci(~B{IPVk+yj-g{csyK~ zafv*2=zesab`xKrCo^#Z*2@wEJ)ehT+KGDFk-#WymTqa~q7R4KuJtox5BftxT4TMo zLrMI=+mdjo8+p*!aS>w>j4$PjXpc!+m11xx8dY)tc_k7M$w@Mj0 zx>Rt5iP7sEJ5J{uw6kG&44lBg5z{n^2 z47|_Dqi!2?%E#o>;hPzpevvB!YDDPEPuw*CpHNYHSS8TGk4e-yt1v@_i8AhAu}lFm2Eoyw_)D?Uu|~ zddJdDVa>{cSJ(TOU?-42r_U5sb`pWb%i>F!{N7~3tM6Pc`REX=-}x|X&|o^p+A)Q5 zoOXalX@$q-N8{RK3;4=M=;9l_^Alc5bB9v~UQ*`VkDnCJ&g0;nCzItdFz_$l)Nkj5 zxg;Ig!t0!W>_x2umX2>pka=5XG_Orie>^sMATybda)Ax~QC3Gb)Gpjf^gzn%tB~daQ<4YaL<^oKliy`h+G%iMq}C{2-Szm+1sUwL3|#7v^RkQ1Z1=q8o_wVK)b`SICo{X4UH+au zk@M5r3(w@8x#XSAdCQ+a6Z-^(<+eK~Sa>FUWhAcXeYT^bORd4z43(eP_1|z0x6B@$8__0t?QKZP}Trb^GPwCot& ztpF0n)(wmsrhGp)?hYJkdOjDwFk2M3g$^wj;N*7zlJmK_UyuOvn(b}x&J1Rs{uX-w zGyD}PM$b<>kLR+Y$>`>AW(~jWb%(Zp^v{32{H`UHbYPII4e~oYjIKsE2fVRIc?{;z zAhUY)tej8TNP(#4+Yc743A$ zwW$DS0+qRou6h-5hi@KsQg8HaAiM{}m@V&c^N*3U$7BiO8vulOga0zI^e{NQyJ;Lk zF8mm`z%@Js)?GighO;x)r9A^%d?YiJ_^9*fvoh(6^^@?#hL+vNP8VjG>I_8jIfHeI z7JS-RCsXc07aR#ZM;3$b1f6-u^vOr_+Cnyjd;ZzXrbnMQ-*)|W>m9de+?9uo8EpD~ zt|gJzWW~vm3{=eoop}1h_Ef&7Xj!~q%Zdks{O6xzx6n)Q+GV8~)9qr^Zsf@SjP=a^ zj1Jju1$Wxn$fm!XL30PUsbp+O2D;IgSDyh|eFa`axc=1KXdxj-QxhR@Sk9^~e}W9kA=~dKrOo6tIkLX4c3I6ufO8J59QPTfow? z@a3J&WXG?@H&r{gw<j`b?K!Mpk&}A3u)t*TAuQ zp8JDqe36-)Ye{P^IvygAGOHZ$w)`qt#pwtye(BgOolQXH(F;3|l`Q+!9*hYRUqp@x z-XkSm!(W%P2FGkh#n*yEeqLY{i)`39+& zhmP>7j(_yq^9Wt~fn7m6z0PMs#y7|g7us}aW_jcmwC6KK*`x_R?WOTh&a2nx+@@PP z6Pq?Qz>kgK!UK)<;Y)C&LhWa{EM3%B(e;@U#5_5`fXYn~q(lQO_2x;KXn;s~ z2r|_<;B&uTwBT6n%~4WzP^UTh6CBD%_j6+!n+{KlKXoOv%{G2C0}KJ-)9J*oNd32G zw((Wj(e;`H|59n?s1_XPO7nLsj&9zQWv~w>SiLDTr9L+zsPvBIZ^!H6y`11DI2S(X zcNVKR@}RbKP}<2`*YNipAISE~@>XY#bA!1acPc}F+T_5E-guLNR(k3}7G0^Mn$*F? z4E~09?F;`Ch`f;gX@CCJ8OzaCWQz!RB3OFk(y+7c1(`+YF?joJOS4bOqm2tPxa+_w zWZ&|}o40@So5!}dzW#b8+pqj)g2(vD=fCFK?LYYQe`dS<>RZZN2?hRag2*5H;lH{4 z;7@$4`AC1^=Ul!0+$T=Vd!=P4Z8h}zQG3_HdnVt@vr*5(pZfJ2XSv+%cfTP^cJJwp z_MuPx=63Wm_f_5-?|kic)9tU^uDkJ;@rz)fG&j|z;Dps5`mgV}OdmKwTZ>XwC_sBP zHW>f?EPqS+vTNHxgi_a_bKYspd3?HlPT-U+{TpaGFGnGxxIswk>kB5JYeD$yAm0F` zXvWt*@FjO{fA~B8T`;&hv;O#>1s$H6$_pZ2VyYP}cK@P6p znqJC7gTL@y-?RPruN^IKXa#UsNdLqyn-hF#)YWTcNIp3_&<%jRb9$mfhyoF~6@(e2 ztbuZ2;ZMD$9LOu*%$p5-jy-yRzMXldpSQX8=DRYmcy2p+^wHA2;>b1IHCgU^#i4Be zktMdtti9?>sB1sF-sq(HyZ#$6kSnytHp9OlYwT!19(yK0s|_$1KE21tw#`dj0nnOt zR8EqYwVl0v9z5Wv4o3I*pwshxXO?9P0uAg&=kyA9H`al3Py9uyFOw&HqCK)G$aUn! z%d&^#n6-aL7i15NKVy@nBX=VMx*7C!c5>HSXK131EXic!$k=gjwHCeIWts;tDlQ51j|O# zuL%Sye+Go7@-XuBsWbU>iFfnjH{roI6OZJZwnwhKJp6lQWY<;Y(Z1YQ^~r1%!Go8L z>5m@@l-!S%jqP&AuAytZx@MtfrYUVM17m2qys|n@y9*5i=2>Q$u}b_!yPg0i^mDEq zuveQ@c#*f>0fZYb-b=OQ7tLkI^-g7!5_uzA;HJ-*wkW^X!LbrqDns~O!3_UzP(il* z$k2eWfyRtUTq~n*1A6fI;Jb80hXN!4$4ayOnExfU?tF^Gj8>PT?g8#iw|Y=;xVXB9 z1MuK;pP9C~;)Agjns{mO9UkU=J^jk~wfipoW;6w{4T@4GAUT`|6IWe*WaMMpz)DL# zJd?-l>I9?l;}uxLA3baHtG$Ayk0pP33qE{zd2Grh{NPzVHd#w>)n)tfA$IWaA@c2f z(J?+6MCH6eV**xOKWoFAybB3$f2mWxN-=-Z?%bP}EzwXuLH<1CV6*Bt2=UGr3m?Au zIQo*{&bc5Ff8d*;(}1RZTiB7qy|8fM7Z)&H|eSs7M2zMxZ{AD-y~&gv_f&IL5-R$AAx*UQ>?W_f5g)WJ8Nf&;DzSR(Jvr(p83 z;6b|qY|ima2Jm)kwY(PYm29pf*u++NnZp&cHYw{XA2bSDs8Z|nw@G9ebb>?DZf_Vr*M(;w>gWYevM=8&*H?{~^od<`Y z5MFqeZR=w@&HzGOngVM}v5P10i+*($nAQtCS=%mxW0j%j2Up<&BP}0oxoa@{5kKg| zweppbf2Gzt4~8e}5N2?>{A_5eg?9-LaH9sl+_sPCqWmwIl+pFmr_XF3eeBULnLVDT zkvAtWeDlpW_tPPkD!Uos1%9}-xo7hv+IK8Jn%|?RPj9znkn-9DruX=cW$Z0*UhTa6 zgr>C4%W3c(@O++LtNWeafqr&*221`1!AN#augXZRJ(5>aKc1C;qnogv&USyQn%rDe zHrg!R)SKmq2^4xhgOchJp1{LoV1;*jA?Ly)K#YPCV7jR$e+^90!f|N7?0vW5f+=}6 z^l7k()8vpn$Bc7k#w@g)=S~zJ;m)8dkmUb6f9HF*E3UmGh3Aq0^ZWjj@7;dk6DNz$ z(1UE1F$B;E^Zj-vT`m99Rt)0x(@$gxY?sU?(DZ)Zg`w@ewyUnYdAs|SZ``iBA)C8o zps%ihAfyf2LM6l|zjXbX_MFV>gkHhEl-aQk=tHk^tCzCYFU;5kyU7fUn|Pkg$G1wC zep|Bu)eRbz&)@v7+<_}^;X`r`ANw@85G>#=^c$pxw!ukf{=nz=M}NmX+aEbUz?2#I z(?{>$zU&WvPwOV9d0+MB8x{5Z+rQ&mw(IVAT^v7tK{q>hbaMZPe|G!YKl4K&4VhG= z2e|NaP$%crjT~|m)#$U=$*0%Jj{n3a@Gc()v*_?|vuu@@?4kSglaFqXX9FMKFXbs$ zW;yJ22`&#`c})gg&u`D1K9QY9AI;9QCo!?{Uv3ZrekcKA%7J&euh482;x$!RpJ5X$-o}PUT#~MyPUzroNwF?M(GP5MEZ_ zP@JhZelfa*4<2Rusa?gTE6DcXU{xgmb6pyN6xef{ZVI8Uds3G z@{P=BDfMX4?#7WbIBYm|ET1Ym_ShrH_Y+8$=OKY_TiX1GO!$xTdc61|?N;!X{z(8$ zw~^(!e5S_6N6%zN^Y~N84>pat;pXe}DWa;M%H&jE`I&rX;>7V&-JHeD9@ty&$R~sZ9l<3CL5KcVAcYT1(iJ>- z0OOiI`VL>}l3AVd8p;iX_BwW8J(Y(KHiE&ACAI3A^~5uo;6X5_(XR}&0|iIhqDKxLIP6&4;m z;Pv?|iv`;D7dpmO^6>{|wO90#UAp`P5FQu5$!KuMAL@gtPbbUI>MOy`2PJ`d2?xe5 zz<$~o{w!l&?`>-D_?Pfg-;NyBedL`Dq-HrXe6Ah67Goy8oB4z%e4@dY@VIB@i%2@G z35E!FaRx8eC0nPD5lA_EyqWx>JLrK+h%Zn)Oc*t%Y>9qUVI21Zh^+Do}IhAK=l9r0rJw1GK! z=|4wu>8zv%9O}$>+fW+X))*2#!ABYv-5asxKdJc&PvGc-HK6>F&IY zByzAnGW0xu;D$+d`N;L6yZBr*(mX}Vsjr`vD3a@+2Gs{Fki8&?a^pDyu0g=))~X72rpqD z!(Bs$l4h4>8rUfa7vODbcx7@uQ zx#s%x9R^G3OVgKMmG>^ar+Mbo@oWV2XlA&xyz=3X_ju&meEKMX=%FjGtvrjKz~o>P zn~|vcq`Fw?K)0)H*EV>~&%jv_{8DD*$W9N^$Q-`nlLEKC?-)90dHBF)_@y)FFY@u~ zH+~RxF59x1SxWUU$!BKH1*5pr&ZA$lr*5qwJNB&J(-zuSg-`gfW{JHMHYf8O^~upe zcp%ouF~QXY-eekE3*?;Z7vSlJJOb0$=xlIv^Ow)sP1Njd_{xpE_cs2SF~SSk)aT+$ z%<{6g^1z2K?B2IL@pL{#a_sSJ%;mkgZM)&-8?#(8!C+pOh(~nL9(9D*TwQ2ToM7ce zzM?&`g`?MQqTe9L?KK&6UYu;Js(=18f4~-rK3iPmM zVyeq9xSJQ~%!j8x$XLQXsx3Hw12{StK*TQj zcMzxr%Ke=U1f>SgF9q1RQv%1X4pU3Q60 zkT*fk%+d|r@Ev*V5_3Rp z-@wlYW~{CaWGu1U$pa30qF)aX)}}@LN}2$c5Aq?up?guO&oYU*oCboW5G{hNlMUIpUoRD~Q(Sr1gMfuEv*7<{emk))*euRL z`$L4_oyoHJW8jrVrKM$NQV_p_VGoSbVOGcW+g%^{iAYKAhZ&c}U>{@{ZeAtjNMHi! z_!eM;ga}-tB)6%KntDM5$23%!_LMxtmF-khu)`()AO(SejKJ2euoXInauihHXDNqq z49U=HL*XPr)WJ{A91Ys>tBz}_H{6^H2*#PlsgIxsl-o*VP?~^ zS-SEAA9f0#L-jEFty=RgUp4_SW0{#zfHz!z#dckmO5)LbeG+)M+=hLBv$VnwdEl;3 z1jad8C3Sie4GcIpd%XCqGCEKe=DJK)eRUicydzm1*ZIPauEBBjnImq{M+1*IS+dp! zXvxp9G*TaZy00wpBeWKu{2V|0qvbm+IPusyF0*} z8*AE$K{7m07`SO;OUBY}2ez}c)2zixb8eD07J+;4kG;Y00*TD68h+rES{C|()afzO zrobx9pQ)?W>T8mb!J$oll)F*E*V#=v$$85`ASU;CxorH8HswWsi$0q#%!*#EKJ|CFb!BNyEK98ZH6B2_X7FDE$*9q(CwCf~BO zGwjJM{WLhe=QVHNuFq_inbQ+TAI@yoBY8r8EH~Lh)x8aYYP;y1Oz@Dk*wa3jkE1mM z@^kteTZgZ}O@-F#4ci%ebPyTdTR*s^+L}6I_okE=_gv*K_|)y-9*)%GSIX%kI>&1V zntTtR$-ob&l1usAXkXgC{OxyauY3DDW6tT%pnGEN?&DW=_~miAbC)fH1}}FO4GblEG-vHX6MUq zovWvY|o1pf-CQTNU?`M}N!L;zP+7G{V~lHp%5D=VlP254vrBtjw39Lh$YHb3L{Q zOm#bcP`tTk2};=|;xu*Rpde6}&RJuETXq_o))xFL<+Jn`&7oJHtO?z7*llz~Ppe%7 zby%5ulAyPP(ml}Pn`SZjtNcEA&@<#gg|T6{j}Bkh9=z}4X|s=I+4FN5gl1RC8*j=t zYHw*^)wVj~434zn7ZX@ED9gF_aY=#)17W%}sKvMbmn~@HioDNv#i0b6`DS7ioLwwW zCzw5%T``}_;FV4@W0cvO6==aFuQRf^q=V`~P^Z72;AsZ<u{9hfQrh?8JNSKWfqtF$KAj_?=v21U8$V#_*f%u6Hy8+=1~9Qf1EbJk zhw4A|%TM`=W>NhJ4Hxc617ddoy^vp-+{E5ZkO`me1*XSI2JlXazYea)U1+y@&11rB`s zj^HO>mv<+xc*2`JzoGBJz_h)mvKz3Cnb=+#JNtwT-pMady%lhid&$8MrY%i-W-p2E zd{)TA9BoN`N5Qw$KsoPj@~g(ZDK8<2n?jT@1_LUFUp+VDbdnM1`8qI=FdF03iSa@S$rBLOT|#G`nvKI61XV#hZ}_K3 zA1$QDBK4r#s@XX%x9`!w9^ftk=OAvsR|^SNQ^$ zd`m`CWgn!urq|F(8H#5377aL-W6-Z4aGG<)PiY!J@*9{0hu~JQDew~9HTcYpb~fmV zQ$~J4V(k~YIMJ!1Q9Q}>4&q3fKy}9f-Vcw%=gerDMU>FNnFz8@mW(Pvl64SLQ|M>k?=v7qFGboenmrM~4Ageq27(-E|wL4&cd&XFP&~ zy?uf6{N$gn$MTgtGnIE|sq2jiGVjkf(f-wg4{rD6srRSzZ}c9y=`UV#AP;KVW0%|s8l={CXau&w3my%^QwQyg?K111vk*X!uXSJ?8d7=c8O*oCF8*-clX+YTnlogbdaJF_rmIM`92HUm_A zu*w8w27S>Go2K5p=QnyjmM6y8^U&cS?Wgs_Fi&rj9t1MoWJKLIsIqu8)&PX_KCu%$iBjNjON z2E@Bg=(T<^zGaq^kF-w-j*b`7r@)tnmSYBk!H<{vNciEWot3BJz

    Lp6Wlc(kY(u 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 Date: Wed, 12 Oct 2022 16:51:31 +0900 Subject: [PATCH 012/369] Next --- example/index.ts | 1 - package.json | 2 +- src/typebox.ts | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/example/index.ts b/example/index.ts index 1d6e207c4..fe4d20dc7 100644 --- a/example/index.ts +++ b/example/index.ts @@ -6,7 +6,6 @@ import { Format } from '@sinclair/typebox/format' import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Static } from '@sinclair/typebox' - const T = Type.Object({ x: Type.Number(), y: Type.Number(), diff --git a/package.json b/package.json index 44cc0bab1..d88fbc251 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.44", + "version": "0.24.45", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 0f6211749..657fc5ba0 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -347,6 +347,7 @@ export interface TOmit extends TObject { static: Partial> + properties: T['properties'] } // -------------------------------------------------------------------------- @@ -419,6 +420,7 @@ export interface TRef extends TSchema { export interface TRequired> extends TObject { static: Required> + properties: T['properties'] } // -------------------------------------------------------------------------- From 694f01aa874c067c1fcf1107664f6e8012577a59 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 12 Oct 2022 17:57:33 +0900 Subject: [PATCH 013/369] Remap TSchema modifier types on Partial and Required --- package.json | 2 +- src/typebox.ts | 42 ++++++++++++++++++++++++------------------ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index d88fbc251..9adb40302 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.45", + "version": "0.24.46", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 657fc5ba0..b7ad22d14 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -229,17 +229,13 @@ export interface TIntersect extends TObject { } // -------------------------------------------------------------------------- -// KeyOf: Implemented by way of Union> +// KeyOf // -------------------------------------------------------------------------- export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never - export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never - export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] - export type UnionStringLiteralToTuple = T extends TUnion ? { [I in keyof L]: L[I] extends TLiteral ? C : never } : never - export type UnionLiteralsFromObject = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never // export type TKeyOf = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never @@ -293,20 +289,16 @@ export interface TNumber extends TSchema, NumericOptions { // -------------------------------------------------------------------------- export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] - export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] - export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? K : never }[keyof T] - export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> -export type PropertiesReduce = { readonly [K in ReadonlyOptionalPropertyKeys]?: Static } & { - readonly [K in ReadonlyPropertyKeys]: Static -} & { - [K in OptionalPropertyKeys]?: Static -} & { [K in RequiredPropertyKeys]: Static } extends infer R - ? { [K in keyof R]: R[K] } - : never +// prettier-ignore +export type PropertiesReduce = + { readonly [K in ReadonlyOptionalPropertyKeys]?: Static } & + { readonly [K in ReadonlyPropertyKeys]: Static } & + { [K in OptionalPropertyKeys]?: Static } & + { [K in RequiredPropertyKeys]: Static } extends infer R ? { [K in keyof R]: R[K] } : never export type TRecordProperties, T extends TSchema> = Static extends string ? { [X in Static]: T } : never @@ -347,7 +339,14 @@ export interface TOmit extends TObject { static: Partial> - properties: T['properties'] + properties: { + // prettier-ignore + [K in keyof T['properties']]: + T['properties'][K] extends TReadonlyOptional ? TReadonlyOptional : + T['properties'][K] extends TReadonly ? TReadonlyOptional : + T['properties'][K] extends TOptional ? TOptional : + TOptional + } } // -------------------------------------------------------------------------- @@ -420,7 +419,14 @@ export interface TRef extends TSchema { export interface TRequired> extends TObject { static: Required> - properties: T['properties'] + properties: { + // prettier-ignore + [K in keyof T['properties']]: + T['properties'][K] extends TReadonlyOptional ? TReadonly : + T['properties'][K] extends TReadonly ? TReadonly : + T['properties'][K] extends TOptional ? U : + T['properties'][K] + } } // -------------------------------------------------------------------------- @@ -955,7 +961,7 @@ export class TypeBuilder { [key]: this.Clone(value[key]), }), Object.getOwnPropertySymbols(value).reduce( - (acc, key) => ({ + (acc, key: any) => ({ ...acc, [key]: this.Clone(value[key]), }), From 707b8caf7b83a241b57d94205d069d4d8f9c59bf Mon Sep 17 00:00:00 2001 From: Noam Okman Date: Tue, 18 Oct 2022 17:42:31 +0300 Subject: [PATCH 014/369] Support readonly argument for Pick and Omit (#261) * support pick from const array * support omit from const array --- src/typebox.ts | 8 ++++---- test/static/omit.ts | 19 +++++++++++++++++++ test/static/pick.ts | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index b7ad22d14..ec08f871a 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -745,11 +745,11 @@ export class TypeBuilder { public Omit[]>>(schema: T, keys: K, options?: ObjectOptions): TOmit> /** Creates a new object whose properties are omitted from the given object */ - public Omit[]>(schema: T, keys: [...K], options?: ObjectOptions): TOmit + public Omit[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TOmit /** Creates a new object whose properties are omitted from the given object */ public Omit(schema: any, keys: any, options: ObjectOptions = {}) { - const select: string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys + const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys const next = { ...this.Clone(schema), ...options, [Hint]: 'Omit' } if (next.required) { next.required = next.required.filter((key: string) => !select.includes(key as any)) @@ -795,11 +795,11 @@ export class TypeBuilder { public Pick[]>>(schema: T, keys: K, options?: ObjectOptions): TPick> /** Creates a object whose properties are picked from the given object */ - public Pick[]>(schema: T, keys: [...K], options?: ObjectOptions): TPick + public Pick[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TPick /** Creates a object whose properties are picked from the given object */ public Pick[]>(schema: any, keys: any, options: ObjectOptions = {}) { - const select: string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys + const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys const next = { ...this.Clone(schema), ...options, [Hint]: 'Pick' } if (next.required) { next.required = next.required.filter((key: any) => select.includes(key)) diff --git a/test/static/omit.ts b/test/static/omit.ts index 669fbab6c..4ef02bdf5 100644 --- a/test/static/omit.ts +++ b/test/static/omit.ts @@ -16,6 +16,25 @@ import { Type, Static } from '@sinclair/typebox' C: string }>() } + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + + const keys = ['A', 'B'] as const; + + const T = Type.Omit(A, keys) + + type T = Static + + Expect(T).ToInfer<{ + C: string + }>() +} + { const A = Type.Object({ A: Type.String(), diff --git a/test/static/pick.ts b/test/static/pick.ts index bf1772bfd..0ae10cd93 100644 --- a/test/static/pick.ts +++ b/test/static/pick.ts @@ -18,6 +18,25 @@ import { Type, Static } from '@sinclair/typebox' }>() } +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + C: Type.String(), + }) + + const keys = ['A', 'B'] as const + + const T = Type.Pick(A, keys) + + type T = Static + + Expect(T).ToInfer<{ + A: string + B: string + }>() +} + { const A = Type.Object({ A: Type.String(), From 302914491d07f2ec0b99552cca3b7b55ca7d73a1 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 18 Oct 2022 23:47:20 +0900 Subject: [PATCH 015/369] Formatting --- test/static/omit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/static/omit.ts b/test/static/omit.ts index 4ef02bdf5..c3f726889 100644 --- a/test/static/omit.ts +++ b/test/static/omit.ts @@ -24,7 +24,7 @@ import { Type, Static } from '@sinclair/typebox' C: Type.String(), }) - const keys = ['A', 'B'] as const; + const keys = ['A', 'B'] as const const T = Type.Omit(A, keys) From 31a137aca7bea6f80d564ad270e0704dac3d7aa2 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 19 Oct 2022 17:08:44 +0900 Subject: [PATCH 016/369] Next --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9adb40302..1af6ec989 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.46", + "version": "0.24.47", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From 44d74706e69efde3540d221f18694a71ab17d750 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 22 Oct 2022 08:52:03 +0900 Subject: [PATCH 017/369] Required properties error reporting type --- package.json | 2 +- src/errors/errors.ts | 16 +++++++++++++--- test/runtime/compiler/validate.ts | 12 ++++++++++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1af6ec989..c16727829 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.47", + "version": "0.24.48", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index e4dea5907..b681aa313 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -59,6 +59,7 @@ export enum ValueErrorType { ObjectMinProperties, ObjectMaxProperties, ObjectAdditionalProperties, + ObjectRequiredProperties, Promise, RecordKeyNumeric, RecordKeyString, @@ -211,12 +212,21 @@ export namespace ValueErrors { } const propertyKeys = globalThis.Object.keys(schema.properties) if (schema.additionalProperties === false) { - for (const propKey of globalThis.Object.keys(value)) { - if (!propertyKeys.includes(propKey)) { - yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${propKey}`, value: value[propKey], message: 'Unexpected property' } + for (const objectKey of globalThis.Object.keys(value)) { + if (!propertyKeys.includes(objectKey)) { + yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${objectKey}`, value: value[objectKey], message: `Unexpected property` } } } } + + if (schema.required && schema.required.length > 0) { + const objectKeys = globalThis.Object.keys(value) + for (const requiredKey of schema.required) { + if (objectKeys.includes(requiredKey)) continue + yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } + } + } + for (const propertyKey of propertyKeys) { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index 985184e4c..4d2ba2f69 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -69,6 +69,10 @@ export function ok(schema: T, data: unknown, references: any[ if (result !== Value.Check(schema, references, data)) { throw Error('Compiler and Value Check disparity') } + if (result === false) { + const errors = [...Value.Errors(schema, references, data)] + if (errors.length === 0) throw Error('expected at least 1 error') + } if (!result) { console.log('---------------------------') console.log('type') @@ -86,9 +90,13 @@ export function ok(schema: T, data: unknown, references: any[ } } -export function fail(schema: T, data: unknown, additional: any[] = []) { - const C = TypeCompiler.Compile(schema, additional) +export function fail(schema: T, data: unknown, references: any[] = []) { + const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) + if (result === false) { + const errors = [...Value.Errors(schema, references, data)] + if (errors.length === 0) throw Error('expected at least 1 error') + } if (result) { console.log('---------------------------') console.log('type') From 27fd7409ba2c33a680dc0d3c13cc6716468588cf Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 22 Oct 2022 12:39:01 +0900 Subject: [PATCH 018/369] Reference TypeScript to JsonSchema Code Generation --- codegen/codegen.ts | 6 + codegen/jsonschema.ts | 428 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 434 insertions(+) create mode 100644 codegen/jsonschema.ts diff --git a/codegen/codegen.ts b/codegen/codegen.ts index ebbdab70d..63728ce18 100644 --- a/codegen/codegen.ts +++ b/codegen/codegen.ts @@ -29,6 +29,7 @@ THE SOFTWARE. import { TSchema } from '@sinclair/typebox' import { TypeBoxCodegen } from './typebox' import { TypeScriptCodeGen } from './typescript' +import { JsonSchemaCodeGen } from './jsonschema' export namespace CodeGen { /** Generates TypeScript type definitions from TypeBox types */ @@ -40,4 +41,9 @@ export namespace CodeGen { export function TypeBox(code: string): string { return TypeBoxCodegen.Generate(code) } + + /** Generates JsonSchema definitions from TypeScript code */ + export function JsonSchema(code: string): string { + return JsonSchemaCodeGen.Generate(code) + } } diff --git a/codegen/jsonschema.ts b/codegen/jsonschema.ts new file mode 100644 index 000000000..8dbfaa0c5 --- /dev/null +++ b/codegen/jsonschema.ts @@ -0,0 +1,428 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ts from 'typescript' + +export class NonExpressable extends Error { + constructor(type: string) { + super(`JsonSchemaCodeGen: Cannot express syntax type '${type}'`) + } +} + +/** Generates JsonSchema from TypeScript interface and type definitions */ +export namespace JsonSchemaCodeGen { + function isReadonlyProperty(node: ts.PropertySignature): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined + } + + function isOptionalProperty(node: ts.PropertySignature) { + return node.questionToken !== undefined + } + + function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined + } + + function* SourceFile(node: ts.SourceFile): IterableIterator { + for (const next of node.getChildren()) { + yield* Visit(next) + } + } + + function* PropertySignature(node: ts.PropertySignature): IterableIterator { + const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] + const type = Collect(node.type) + if (readonly && optional) { + return yield `${node.name.getText()}: ${type}` + } else if (readonly) { + return yield `${node.name.getText()}: ${type}` + } else if (optional) { + return yield `${node.name.getText()}: ${type}` + } else { + return yield `${node.name.getText()}: ${type}` + } + } + + function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { + const type = Collect(node.elementType) + yield `{ + type: 'array', + items: ${type} + }` + } + + function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { + const types = node.elements.map((type) => Collect(type)).join(',\n') + yield `{ + type: 'array', + items: [${types}], + minItems: ${node.elements.length}, + maxItems: ${node.elements.length} + }` + } + + function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { + const types = node.types.map((type) => Collect(type)).join(',\n') + yield `{ + anyOf: [ + ${types} + ] + }` + } + + function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { + throw new NonExpressable('IntersectionTypeNode') + } + + function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { + throw new NonExpressable('TypeOperatorNode') + } + + function* Parameter(node: ts.ParameterDeclaration): IterableIterator { + yield Collect(node.type) + } + + function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { + throw new NonExpressable('FunctionTypeNode') + } + + function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { + throw new NonExpressable('ConstructorTypeNode') + } + + function* EnumMember(node: ts.EnumMember): IterableIterator { + if (node.initializer) { + return yield `{ + const: ${node.initializer.getText()} + }` + } + return yield `{ + const: '${node.name.getText()}' + }` + } + + function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { + const exports = isExport(node) ? 'export ' : '' + const name = node.name.getText() + const members = node.members.map((member) => Collect(member)).join(',\n') + yield `${exports}const ${name} = { + anyOf: [ + ${members} + ] + }` + } + + function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { + if (node.typeParameters) { + const exports = isExport(node) ? 'export ' : '' + const members = node.members.map((member) => Collect(member)).join(',\n') + const required = node.members + .filter((member) => member.questionToken === undefined) + .map((member) => `'${member.name?.getText()}'`) + .join(',\n') + const parameters = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const definition = `${exports}const ${node.name.getText()} = (${parameters}) => { + type: 'object, + properties: { + ${members} + }, + required: [ + ${required} + ], + }` + yield `${definition}` + } else { + const exports = isExport(node) ? 'export ' : '' + const members = node.members.map((member) => Collect(member)).join(',\n') + const required = node.members + .filter((member) => member.questionToken === undefined) + .map((member) => `'${member.name?.getText()}'`) + .join(',\n') + const definition = `${exports}const ${node.name.getText()} = { + type: 'object', + properties: { + ${members} + }, + required: [ + ${required} + ] + }` + yield `${definition}` + } + } + + function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { + if (node.typeParameters) { + const exports = isExport(node) ? 'export ' : '' + const parameters = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const type = Collect(node.type) + const definition = `${exports}const ${node.name.getText()} = (${parameters}) => ${type}` + yield `${definition}` + } else { + const exports = isExport(node) ? 'export ' : '' + const type = Collect(node.type) + const definition = `${exports}const ${node.name.getText()} = ${type}` + yield `${definition}` + } + } + + function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { + yield node.name.getText() + } + + function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { + yield Collect(node.type) + } + + function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { + throw new NonExpressable('RestTypeNode') + } + + function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { + const checkType = Collect(node.checkType) + const extendsType = Collect(node.extendsType) + const trueType = Collect(node.trueType) + const falseType = Collect(node.falseType) + yield `${checkType} ${extendsType}, ${trueType}, ${falseType})` + } + + function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { + const name = node.typeName.getText() + const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' + if (name === 'Array') { + return yield `{ + type: 'array', + items: ${args} + }` + } else if (name === 'Record') { + throw new NonExpressable('TypeReferenceNode:Record') + } else if (name === 'Partial') { + throw new NonExpressable('TypeReferenceNode:Partial') + } else if (name === 'Uint8Array') { + throw new NonExpressable('TypeReferenceNode:Uint8Array') + } else if (name === 'Required') { + throw new NonExpressable('TypeReferenceNode:Required') + } else if (name === 'Omit') { + throw new NonExpressable('TypeReferenceNode:Omit') + } else if (name === 'Pick') { + throw new NonExpressable('TypeReferenceNode:Pick') + } else if (name === 'Promise') { + throw new NonExpressable('TypeReferenceNode:Promise') + } else if (name === 'ReturnType') { + throw new NonExpressable('TypeReferenceNode:ReturnType') + } else if (name === 'InstanceType') { + throw new NonExpressable('TypeReferenceNode:InstanceType') + } else if (name === 'Parameters') { + throw new NonExpressable('TypeReferenceNode:Parameters') + } else if (name === 'ConstructorParameters') { + throw new NonExpressable('TypeReferenceNode:ConstructorParameters') + } else if (name === 'Exclude') { + throw new NonExpressable('TypeReferenceNode:Exclude') + } else if (name === 'Extract') { + throw new NonExpressable('TypeReferenceNode:Extract') + } else { + return yield `${name}${args}` + } + } + + function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { + const members = node.members.map((member) => Collect(member)).join(',\n') + const required = node.members + .filter((member) => member.questionToken === undefined) + .map((member) => `'${member.name?.getText()}'`) + .join(',\n') + yield `{ + type: 'object', + properties: { + ${members} + }, + required: [ + ${required} + ] + }` + } + + function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { + const text = node.getText() + if (text === 'null') + return yield `{ + type: 'null' + }` + yield `{ + const: ${node.getText()} + }` + } + + function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { + yield node.getText() + } + + function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { + yield node.getText() + } + + function Collect(node: ts.Node | undefined): string { + return `${[...Visit(node)].join('')}` + } + + function CollectNewLine(node: ts.Node | undefined): string { + return [...Visit(node)].join('\n\n') + } + + function* Visit(node: ts.Node | undefined): IterableIterator { + if (node === undefined) return + if (ts.isSourceFile(node)) { + return yield* SourceFile(node) + } else if (ts.isInterfaceDeclaration(node)) { + return yield* InterfaceDeclaration(node) + } else if (ts.isTypeAliasDeclaration(node)) { + return yield* TypeAliasDeclaration(node) + } else if (ts.isParameter(node)) { + return yield* Parameter(node) + } else if (ts.isFunctionTypeNode(node)) { + return yield* FunctionTypeNode(node) + } else if (ts.isConstructorTypeNode(node)) { + return yield* ConstructorTypeNode(node) + } else if (ts.isEnumMember(node)) { + return yield* EnumMember(node) + } else if (ts.isEnumDeclaration(node)) { + return yield* EnumDeclaration(node) + } else if (ts.isPropertySignature(node)) { + return yield* PropertySignature(node) + } else if (ts.isTypeReferenceNode(node)) { + return yield* TypeReferenceNode(node) + } else if (ts.isTypeLiteralNode(node)) { + return yield* TypeLiteralNode(node) + } else if (ts.isLiteralTypeNode(node)) { + return yield* LiteralTypeNode(node) + } else if (ts.isArrayTypeNode(node)) { + return yield* ArrayTypeNode(node) + } else if (ts.isTupleTypeNode(node)) { + return yield* TupleTypeNode(node) + } else if (ts.isIntersectionTypeNode(node)) { + return yield* IntersectionTypeNode(node) + } else if (ts.isUnionTypeNode(node)) { + return yield* UnionTypeNode(node) + } else if (ts.isTypeOperatorNode(node)) { + return yield* TypeOperatorNode(node) + } else if (ts.isTypeParameterDeclaration(node)) { + return yield* TypeParameterDeclaration(node) + } else if (ts.isParenthesizedTypeNode(node)) { + return yield* ParenthesizedTypeNode(node) + } else if (ts.isRestTypeNode(node)) { + return yield* RestTypeNode(node) + } else if (ts.isFunctionDeclaration(node)) { + return yield* FunctionDeclaration(node) + } else if (ts.isClassDeclaration(node)) { + return yield* ClassDeclaration(node) + } else if (ts.isConditionalTypeNode(node)) { + return yield* ConditionalTypeNode(node) + } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { + throw Error('JsonSchemaCodeGen: keyof cannot be expressed as json schema') + } else if (node.kind === ts.SyntaxKind.NumberKeyword) { + return yield `{ + type: 'number' + }` + } else if (node.kind === ts.SyntaxKind.StringKeyword) { + return yield `{ + type: 'string' + }` + } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { + return yield `{ + type: 'boolean' + }` + } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { + throw Error('JsonSchemaCodeGen: undefined cannot be expressed as json schema') + } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { + return yield `{ + + }` + } else if (node.kind === ts.SyntaxKind.AnyKeyword) { + return yield `{ + + }` + } else if (node.kind === ts.SyntaxKind.NeverKeyword) { + return yield `{ + anyOf: [ + { + type: 'boolean', + const: true + }, + { + type: 'boolean', + const: false + } + ] + }` + } else if (node.kind === ts.SyntaxKind.NullKeyword) { + return yield `{ + type: 'null' + }` + } else if (node.kind === ts.SyntaxKind.VoidKeyword) { + throw Error('JsonSchemaCodeGen: void cannot be expressed as json schema') + } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { + return + } else if (node.kind === ts.SyntaxKind.SyntaxList) { + for (const child of node.getChildren()) { + yield* Visit(child) + } + return + } else { + console.log('Unhandled:', ts.SyntaxKind[node.kind]) + return yield node.getText() + } + } + + function Format(input: string): string { + function count(line: string, opens: string[]) { + const codes = opens.map((open) => open.charCodeAt(0)) + return line + .split('') + .map((char) => char.charCodeAt(0)) + .reduce((acc, current) => { + return codes.includes(current) ? acc + 1 : acc + }, 0) + } + let indent = 0 + const output: string[] = [] + for (const line of input.split('\n').map((n) => n.trim())) { + indent -= count(line, ['}', ']']) + output.push(`${''.padStart(indent * 2, ' ')}${line}`) + indent += count(line, ['{', '[']) + } + return output.join('\n') + } + + /** Generates TypeBox types from TypeScript interface and type definitions */ + export function Generate(typescriptCode: string) { + const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) + const declarations = CollectNewLine(source) + const types = Format(declarations) + return [types].join('\n') + } +} From ea3139967688de59b26a95aba30b066a4d2648c6 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 22 Oct 2022 13:03:37 +0900 Subject: [PATCH 019/369] Reference TypeScript to JsonSchema Code Generation --- codegen/jsonschema.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/codegen/jsonschema.ts b/codegen/jsonschema.ts index 8dbfaa0c5..171a55764 100644 --- a/codegen/jsonschema.ts +++ b/codegen/jsonschema.ts @@ -96,7 +96,12 @@ export namespace JsonSchemaCodeGen { } function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { - throw new NonExpressable('IntersectionTypeNode') + const types = node.types.map((type) => Collect(type)).join(',\n') + yield `{ + allOf: [ + ${types} + ] + }` } function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { From 4bcacd4f9911dd17755b44662d2a4631633ae400 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 23 Oct 2022 17:26:44 +0900 Subject: [PATCH 020/369] Non-Boolean Additional Properties (#264) --- changelog.md | 9 +++ codegen/jsonschema.ts | 12 ++-- package.json | 2 +- readme.md | 102 ++++++++++++++--------------- src/compiler/compiler.ts | 5 ++ src/errors/errors.ts | 7 +- src/guard/guard.ts | 7 +- src/typebox.ts | 13 ++-- src/value/cast.ts | 8 +++ src/value/check.ts | 8 +++ test/runtime/compiler/object.ts | 66 +++++++++++++++++++ test/runtime/guard/object.ts | 31 +++++++++ test/runtime/schema/object.ts | 68 +++++++++++++++++++ test/runtime/value/cast/object.ts | 54 +++++++++++++++ test/runtime/value/check/object.ts | 92 ++++++++++++++++++++++++++ test/static/object.ts | 22 ++++++- 16 files changed, 436 insertions(+), 70 deletions(-) diff --git a/changelog.md b/changelog.md index 7c8020b0c..220a9cfac 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,12 @@ +## [0.24.49](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.49) + +Updates: +- [264](https://github.com/sinclairzx81/typebox/pull/264) TypeBox now provides preliminary support for non-boolean `additionalProperties`. This allows existing `TObject` schemas to be augmented with additional properties of a known type. + +Additional: + +- TypeBox provides an additional reference `codegen` module for generating raw JSON Schema from TypeScript types via the TS compiler API. This generator may be used in future tooling. + ## [0.24.44](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.44) Updates: diff --git a/codegen/jsonschema.ts b/codegen/jsonschema.ts index 171a55764..23f4db8a1 100644 --- a/codegen/jsonschema.ts +++ b/codegen/jsonschema.ts @@ -209,11 +209,7 @@ export namespace JsonSchemaCodeGen { } function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - const checkType = Collect(node.checkType) - const extendsType = Collect(node.extendsType) - const trueType = Collect(node.trueType) - const falseType = Collect(node.falseType) - yield `${checkType} ${extendsType}, ${trueType}, ${falseType})` + throw new NonExpressable('ConditionalTypeNode') } function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { @@ -348,7 +344,7 @@ export namespace JsonSchemaCodeGen { } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { - throw Error('JsonSchemaCodeGen: keyof cannot be expressed as json schema') + throw new NonExpressable('KeyOfKeyword') } else if (node.kind === ts.SyntaxKind.NumberKeyword) { return yield `{ type: 'number' @@ -362,7 +358,7 @@ export namespace JsonSchemaCodeGen { type: 'boolean' }` } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { - throw Error('JsonSchemaCodeGen: undefined cannot be expressed as json schema') + throw new NonExpressable('UndefinedKeyword') } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { return yield `{ @@ -389,7 +385,7 @@ export namespace JsonSchemaCodeGen { type: 'null' }` } else if (node.kind === ts.SyntaxKind.VoidKeyword) { - throw Error('JsonSchemaCodeGen: void cannot be expressed as json schema') + throw new NonExpressable('KeyOfKeyword') } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { return } else if (node.kind === ts.SyntaxKind.SyntaxList) { diff --git a/package.json b/package.json index c16727829..3e54b2706 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.48", + "version": "0.24.49", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 6d77e6a33..a5bc4caa5 100644 --- a/readme.md +++ b/readme.md @@ -1062,29 +1062,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 384 ms' │ ' 9 ms' │ ' 42.67 x' │ -│ String │ 2000 │ ' 322 ms' │ ' 8 ms' │ ' 40.25 x' │ -│ Boolean │ 2000 │ ' 310 ms' │ ' 6 ms' │ ' 51.67 x' │ -│ Null │ 2000 │ ' 271 ms' │ ' 6 ms' │ ' 45.17 x' │ -│ RegEx │ 2000 │ ' 491 ms' │ ' 12 ms' │ ' 40.92 x' │ -│ ObjectA │ 2000 │ ' 2935 ms' │ ' 44 ms' │ ' 66.70 x' │ -│ ObjectB │ 2000 │ ' 3076 ms' │ ' 30 ms' │ ' 102.53 x' │ -│ Tuple │ 2000 │ ' 1274 ms' │ ' 22 ms' │ ' 57.91 x' │ -│ Union │ 2000 │ ' 1281 ms' │ ' 22 ms' │ ' 58.23 x' │ -│ Vector4 │ 2000 │ ' 1602 ms' │ ' 17 ms' │ ' 94.24 x' │ -│ Matrix4 │ 2000 │ ' 959 ms' │ ' 10 ms' │ ' 95.90 x' │ -│ Literal_String │ 2000 │ ' 337 ms' │ ' 6 ms' │ ' 56.17 x' │ -│ Literal_Number │ 2000 │ ' 376 ms' │ ' 5 ms' │ ' 75.20 x' │ -│ Literal_Boolean │ 2000 │ ' 370 ms' │ ' 5 ms' │ ' 74.00 x' │ -│ Array_Number │ 2000 │ ' 733 ms' │ ' 9 ms' │ ' 81.44 x' │ -│ Array_String │ 2000 │ ' 764 ms' │ ' 10 ms' │ ' 76.40 x' │ -│ Array_Boolean │ 2000 │ ' 818 ms' │ ' 6 ms' │ ' 136.33 x' │ -│ Array_ObjectA │ 2000 │ ' 3744 ms' │ ' 35 ms' │ ' 106.97 x' │ -│ Array_ObjectB │ 2000 │ ' 3893 ms' │ ' 34 ms' │ ' 114.50 x' │ -│ Array_Tuple │ 2000 │ ' 2229 ms' │ ' 16 ms' │ ' 139.31 x' │ -│ Array_Union │ 2000 │ ' 1705 ms' │ ' 18 ms' │ ' 94.72 x' │ -│ Array_Vector4 │ 2000 │ ' 2261 ms' │ ' 17 ms' │ ' 133.00 x' │ -│ Array_Matrix4 │ 2000 │ ' 1574 ms' │ ' 14 ms' │ ' 112.43 x' │ +│ Number │ 2000 │ ' 428 ms' │ ' 12 ms' │ ' 35.67 x' │ +│ String │ 2000 │ ' 337 ms' │ ' 12 ms' │ ' 28.08 x' │ +│ Boolean │ 2000 │ ' 317 ms' │ ' 11 ms' │ ' 28.82 x' │ +│ Null │ 2000 │ ' 274 ms' │ ' 10 ms' │ ' 27.40 x' │ +│ RegEx │ 2000 │ ' 500 ms' │ ' 18 ms' │ ' 27.78 x' │ +│ ObjectA │ 2000 │ ' 2717 ms' │ ' 49 ms' │ ' 55.45 x' │ +│ ObjectB │ 2000 │ ' 2854 ms' │ ' 37 ms' │ ' 77.14 x' │ +│ Tuple │ 2000 │ ' 1224 ms' │ ' 21 ms' │ ' 58.29 x' │ +│ Union │ 2000 │ ' 1266 ms' │ ' 23 ms' │ ' 55.04 x' │ +│ Vector4 │ 2000 │ ' 1513 ms' │ ' 19 ms' │ ' 79.63 x' │ +│ Matrix4 │ 2000 │ ' 841 ms' │ ' 12 ms' │ ' 70.08 x' │ +│ Literal_String │ 2000 │ ' 327 ms' │ ' 8 ms' │ ' 40.88 x' │ +│ Literal_Number │ 2000 │ ' 358 ms' │ ' 6 ms' │ ' 59.67 x' │ +│ Literal_Boolean │ 2000 │ ' 355 ms' │ ' 5 ms' │ ' 71.00 x' │ +│ Array_Number │ 2000 │ ' 685 ms' │ ' 7 ms' │ ' 97.86 x' │ +│ Array_String │ 2000 │ ' 716 ms' │ ' 11 ms' │ ' 65.09 x' │ +│ Array_Boolean │ 2000 │ ' 732 ms' │ ' 6 ms' │ ' 122.00 x' │ +│ Array_ObjectA │ 2000 │ ' 3503 ms' │ ' 34 ms' │ ' 103.03 x' │ +│ Array_ObjectB │ 2000 │ ' 3626 ms' │ ' 38 ms' │ ' 95.42 x' │ +│ Array_Tuple │ 2000 │ ' 2095 ms' │ ' 21 ms' │ ' 99.76 x' │ +│ Array_Union │ 2000 │ ' 1577 ms' │ ' 22 ms' │ ' 71.68 x' │ +│ Array_Vector4 │ 2000 │ ' 2172 ms' │ ' 17 ms' │ ' 127.76 x' │ +│ Array_Matrix4 │ 2000 │ ' 1468 ms' │ ' 19 ms' │ ' 77.26 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1098,31 +1098,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 37 ms' │ ' 5 ms' │ ' 5 ms' │ ' 1.00 x' │ -│ String │ 1000000 │ ' 31 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ -│ Boolean │ 1000000 │ ' 25 ms' │ ' 25 ms' │ ' 11 ms' │ ' 2.27 x' │ -│ Null │ 1000000 │ ' 32 ms' │ ' 26 ms' │ ' 10 ms' │ ' 2.60 x' │ -│ RegEx │ 1000000 │ ' 170 ms' │ ' 48 ms' │ ' 35 ms' │ ' 1.37 x' │ -│ ObjectA │ 1000000 │ ' 564 ms' │ ' 36 ms' │ ' 23 ms' │ ' 1.57 x' │ -│ ObjectB │ 1000000 │ ' 1000 ms' │ ' 56 ms' │ ' 40 ms' │ ' 1.40 x' │ -│ Tuple │ 1000000 │ ' 125 ms' │ ' 27 ms' │ ' 13 ms' │ ' 2.08 x' │ -│ Union │ 1000000 │ ' 316 ms' │ ' 27 ms' │ ' 14 ms' │ ' 1.93 x' │ -│ Recursive │ 1000000 │ ' 3308 ms' │ ' 415 ms' │ ' 183 ms' │ ' 2.27 x' │ -│ Vector4 │ 1000000 │ ' 135 ms' │ ' 27 ms' │ ' 12 ms' │ ' 2.25 x' │ -│ Matrix4 │ 1000000 │ ' 658 ms' │ ' 44 ms' │ ' 30 ms' │ ' 1.47 x' │ -│ Literal_String │ 1000000 │ ' 48 ms' │ ' 26 ms' │ ' 10 ms' │ ' 2.60 x' │ -│ Literal_Number │ 1000000 │ ' 49 ms' │ ' 31 ms' │ ' 9 ms' │ ' 3.44 x' │ -│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 28 ms' │ ' 10 ms' │ ' 2.80 x' │ -│ Array_Number │ 1000000 │ ' 418 ms' │ ' 38 ms' │ ' 17 ms' │ ' 2.24 x' │ -│ Array_String │ 1000000 │ ' 466 ms' │ ' 35 ms' │ ' 22 ms' │ ' 1.59 x' │ -│ Array_Boolean │ 1000000 │ ' 403 ms' │ ' 41 ms' │ ' 24 ms' │ ' 1.71 x' │ -│ Array_ObjectA │ 1000000 │ ' 13596 ms' │ ' 2861 ms' │ ' 1759 ms' │ ' 1.63 x' │ -│ Array_ObjectB │ 1000000 │ ' 15767 ms' │ ' 2798 ms' │ ' 1991 ms' │ ' 1.41 x' │ -│ Array_Tuple │ 1000000 │ ' 1666 ms' │ ' 103 ms' │ ' 73 ms' │ ' 1.41 x' │ -│ Array_Union │ 1000000 │ ' 4738 ms' │ ' 229 ms' │ ' 87 ms' │ ' 2.63 x' │ -│ Array_Recursive │ 1000000 │ ' 56371 ms' │ ' 7315 ms' │ ' 2813 ms' │ ' 2.60 x' │ -│ Array_Vector4 │ 1000000 │ ' 2180 ms' │ ' 106 ms' │ ' 53 ms' │ ' 2.00 x' │ -│ Array_Matrix4 │ 1000000 │ ' 11795 ms' │ ' 384 ms' │ ' 313 ms' │ ' 1.23 x' │ +│ Number │ 1000000 │ ' 24 ms' │ ' 9 ms' │ ' 6 ms' │ ' 1.50 x' │ +│ String │ 1000000 │ ' 23 ms' │ ' 19 ms' │ ' 12 ms' │ ' 1.58 x' │ +│ Boolean │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Null │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ RegEx │ 1000000 │ ' 164 ms' │ ' 46 ms' │ ' 38 ms' │ ' 1.21 x' │ +│ ObjectA │ 1000000 │ ' 548 ms' │ ' 36 ms' │ ' 22 ms' │ ' 1.64 x' │ +│ ObjectB │ 1000000 │ ' 1118 ms' │ ' 51 ms' │ ' 38 ms' │ ' 1.34 x' │ +│ Tuple │ 1000000 │ ' 136 ms' │ ' 25 ms' │ ' 14 ms' │ ' 1.79 x' │ +│ Union │ 1000000 │ ' 338 ms' │ ' 27 ms' │ ' 16 ms' │ ' 1.69 x' │ +│ Recursive │ 1000000 │ ' 3251 ms' │ ' 416 ms' │ ' 98 ms' │ ' 4.24 x' │ +│ Vector4 │ 1000000 │ ' 146 ms' │ ' 23 ms' │ ' 12 ms' │ ' 1.92 x' │ +│ Matrix4 │ 1000000 │ ' 584 ms' │ ' 40 ms' │ ' 25 ms' │ ' 1.60 x' │ +│ Literal_String │ 1000000 │ ' 46 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ +│ Array_Number │ 1000000 │ ' 456 ms' │ ' 31 ms' │ ' 19 ms' │ ' 1.63 x' │ +│ Array_String │ 1000000 │ ' 489 ms' │ ' 40 ms' │ ' 25 ms' │ ' 1.60 x' │ +│ Array_Boolean │ 1000000 │ ' 458 ms' │ ' 35 ms' │ ' 27 ms' │ ' 1.30 x' │ +│ Array_ObjectA │ 1000000 │ ' 13559 ms' │ ' 2568 ms' │ ' 1564 ms' │ ' 1.64 x' │ +│ Array_ObjectB │ 1000000 │ ' 15863 ms' │ ' 2744 ms' │ ' 2060 ms' │ ' 1.33 x' │ +│ Array_Tuple │ 1000000 │ ' 1694 ms' │ ' 96 ms' │ ' 63 ms' │ ' 1.52 x' │ +│ Array_Union │ 1000000 │ ' 4736 ms' │ ' 229 ms' │ ' 86 ms' │ ' 2.66 x' │ +│ Array_Recursive │ 1000000 │ ' 53804 ms' │ ' 6744 ms' │ ' 1167 ms' │ ' 5.78 x' │ +│ Array_Vector4 │ 1000000 │ ' 2244 ms' │ ' 99 ms' │ ' 46 ms' │ ' 2.15 x' │ +│ Array_Matrix4 │ 1000000 │ ' 11966 ms' │ ' 378 ms' │ ' 229 ms' │ ' 1.65 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1136,11 +1136,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 49 kb' │ ' 24 kb' │ '2.00 x' │ +│ typebox/compiler │ ' 51 kb' │ ' 25 kb' │ '2.00 x' │ │ typebox/conditional │ ' 42 kb' │ ' 17 kb' │ '2.46 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ -│ typebox/guard │ ' 21 kb' │ ' 10 kb' │ '2.07 x' │ -│ typebox/value │ ' 72 kb' │ ' 33 kb' │ '2.16 x' │ +│ typebox/guard │ ' 21 kb' │ ' 10 kb' │ '2.08 x' │ +│ typebox/value │ ' 74 kb' │ ' 34 kb' │ '2.16 x' │ │ typebox │ ' 11 kb' │ ' 6 kb' │ '1.91 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 716b0974b..7ebae8990 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -187,6 +187,11 @@ export namespace TypeCompiler { yield `(Object.keys(${value}).every(key => ${keys}.includes(key)))` } } + if (TypeGuard.TSchema(schema.additionalProperties)) { + const expression = CreateExpression(schema.additionalProperties, 'value[key]') + const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` + yield `(Object.keys(${value}).every(key => ${keys}.includes(key) || ${expression}))` + } for (const propertyKey of propertyKeys) { const memberExpression = Property.Check(propertyKey) ? `${value}.${propertyKey}` : `${value}['${propertyKey}']` const propertySchema = schema.properties[propertyKey] diff --git a/src/errors/errors.ts b/src/errors/errors.ts index b681aa313..913a60226 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -226,7 +226,12 @@ export namespace ValueErrors { yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } } } - + if (typeof schema.additionalProperties === 'object') { + for (const objectKey of globalThis.Object.keys(value)) { + if (propertyKeys.includes(objectKey)) continue + yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${objectKey}`, value[objectKey]) + } + } for (const propertyKey of propertyKeys) { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { diff --git a/src/guard/guard.ts b/src/guard/guard.ts index 01b653566..ac9d54430 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -83,17 +83,18 @@ export namespace TypeGuard { function IsOptionalBoolean(value: unknown): value is boolean | undefined { return value === undefined || (value !== undefined && IsBoolean(value)) } - function IsOptionalString(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value)) } - function IsOptionalPattern(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) } function IsOptionalFormat(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) } + function IsOptionalSchema(value: unknown): value is boolean | undefined { + return value === undefined || TSchema(value) + } /** Returns true if the given schema is TAny */ export function TAny(schema: unknown): schema is Types.TAny { return IsObject(schema) && schema[Types.Kind] === 'Any' && IsOptionalString(schema.$id) @@ -207,7 +208,7 @@ export namespace TypeGuard { schema.type === 'object' && IsOptionalString(schema.$id) && IsObject(schema.properties) && - IsOptionalBoolean(schema.additionalProperties) && + (IsOptionalBoolean(schema.additionalProperties) || IsOptionalSchema(schema.additionalProperties)) && IsOptionalNumber(schema.minProperties) && IsOptionalNumber(schema.maxProperties) ) diff --git a/src/typebox.ts b/src/typebox.ts index ec08f871a..b15fcc030 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -310,15 +310,18 @@ export type ObjectProperties = T extends TObject ? U : never export type ObjectPropertyKeys = T extends TObject ? keyof U : never -export interface ObjectOptions extends SchemaOptions { - additionalProperties?: boolean +export type TAdditionalProperties = undefined | TSchema | boolean + +export interface ObjectOptions extends SchemaOptions { + additionalProperties?: A minProperties?: number maxProperties?: number } -export interface TObject extends TSchema, ObjectOptions { +export interface TObject extends TSchema, ObjectOptions { [Kind]: 'Object' - static: PropertiesReduce + static: A extends TSchema ? PropertiesReduce & Record> : PropertiesReduce + additionalProperties?: A type: 'object' properties: T required?: string[] @@ -726,7 +729,7 @@ export class TypeBuilder { } /** Creates an object type with the given properties */ - public Object(properties: T, options: ObjectOptions = {}): TObject { + public Object(properties: T, options: ObjectOptions = {}): TObject { const property_names = Object.keys(properties) const optional = property_names.filter((name) => { const property = properties[name] as TModifier diff --git a/src/value/cast.ts b/src/value/cast.ts index 4478b5ff5..848fed23b 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -240,6 +240,14 @@ export namespace ValueCast { if (!required.has(key) && value[key] === undefined) continue result[key] = Visit(property, references, value[key]) } + // additional schema properties + if (typeof schema.additionalProperties === 'object') { + const propertyKeys = globalThis.Object.keys(schema.properties) + for (const objectKey of globalThis.Object.keys(value)) { + if (propertyKeys.includes(objectKey)) continue + result[objectKey] = Visit(schema.additionalProperties, references, value[objectKey]) + } + } return result } diff --git a/src/value/check.ts b/src/value/check.ts index b4f7bafcf..302f5609d 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -150,6 +150,14 @@ export namespace ValueCheck { } } } + if (typeof schema.additionalProperties === 'object') { + for (const objectKey of globalThis.Object.keys(value)) { + if (propertyKeys.includes(objectKey)) continue + if (!Visit(schema.additionalProperties as Types.TSchema, references, value[objectKey])) { + return false + } + } + } for (const propertyKey of propertyKeys) { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 845855cd7..c6b4aa809 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -139,4 +139,70 @@ describe('type/compiler/Object', () => { '!@#$%^&*(': 4, }) }) + + it('Should validate schema additional properties of string', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + ok(T, { + x: 1, + y: 2, + z: 'hello', + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) + it('Should validate schema additional properties of array', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Array(Type.Number()), + }, + ) + ok(T, { + x: 1, + y: 2, + z: [0, 1, 2], + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) + it('Should validate schema additional properties of object', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + z: Type.Number(), + }), + }, + ) + ok(T, { + x: 1, + y: 2, + z: { z: 1 }, + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) }) diff --git a/test/runtime/guard/object.ts b/test/runtime/guard/object.ts index a4503e2c5..c34d32639 100644 --- a/test/runtime/guard/object.ts +++ b/test/runtime/guard/object.ts @@ -96,4 +96,35 @@ describe('type/guard/TObject', () => { ) Assert.equal(R, false) }) + + it('should guard for TObject with invalid additional properties', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + // @ts-ignore + additionalProperties: 1, + }, + ), + ) + Assert.equal(R, false) + }) + + it('should not guard for TObject with valid additional properties schema', () => { + const R = TypeGuard.TObject( + Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + ) + Assert.equal(R, true) + }) }) diff --git a/test/runtime/schema/object.ts b/test/runtime/schema/object.ts index 188dc1076..658daea41 100644 --- a/test/runtime/schema/object.ts +++ b/test/runtime/schema/object.ts @@ -139,4 +139,72 @@ describe('type/schema/Object', () => { '!@#$%^&*(': 4, }) }) + + it('Should validate schema additional properties of string', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + ok(T, { + x: 1, + y: 2, + z: 'hello', + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) + + it('Should validate schema additional properties of array', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Array(Type.Number()), + }, + ) + ok(T, { + x: 1, + y: 2, + z: [0, 1, 2], + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) + + it('Should validate schema additional properties of object', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + z: Type.Number(), + }), + }, + ) + ok(T, { + x: 1, + y: 2, + z: { z: 1 }, + }) + fail(T, { + x: 1, + y: 2, + z: 3, + }) + }) }) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts index bf821147b..71bd0bf01 100644 --- a/test/runtime/value/cast/object.ts +++ b/test/runtime/value/cast/object.ts @@ -109,4 +109,58 @@ describe('value/cast/Object', () => { c: 'c', }) }) + + it('Should upcast and create invalid additional properties', () => { + const result = Value.Cast( + Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + a: Type.Number(), + b: Type.Number(), + }), + }, + ), + { + x: 1, + y: 2, + z: true, + }, + ) + Assert.deepEqual(result, { + x: 1, + y: 2, + z: { a: 0, b: 0 }, + }) + }) + + it('Should upcast and preserve additional properties', () => { + const result = Value.Cast( + Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + a: Type.Number(), + b: Type.Number(), + }), + }, + ), + { + x: 1, + y: 2, + z: { b: 1 }, + }, + ) + Assert.deepEqual(result, { + x: 1, + y: 2, + z: { a: 0, b: 1 }, + }) + }) }) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index 25ac7265f..ccd0ef10c 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -98,4 +98,96 @@ describe('value/check/Object', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + + it('Should validate schema additional properties of string', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: 'hello', + }), + true, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: 3, + }), + false, + ) + }) + + it('Should validate schema additional properties of array', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Array(Type.Number()), + }, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: [0, 1, 2], + }), + true, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: 3, + }), + false, + ) + }) + + it('Should validate schema additional properties of object', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + z: Type.Number(), + }), + }, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: { z: 1 }, + }), + true, + ) + + Assert.equal( + Value.Check(T, { + x: 1, + y: 2, + z: 3, + }), + false, + ) + }) }) diff --git a/test/static/object.ts b/test/static/object.ts index 9ed03b64a..57e58299b 100644 --- a/test/static/object.ts +++ b/test/static/object.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' { const T = Type.Object({ @@ -50,3 +50,23 @@ import { Type } from '@sinclair/typebox' } }>() } +{ + const T = Type.Object( + { + A: Type.Number(), + B: Type.Number(), + C: Type.Number(), + }, + { + additionalProperties: Type.Boolean(), + }, + ) + // note: the inferenced additionalProperty type does break usual structural + // equivelence and assignment checks, but does allow for the following usage. + function test(value: Static) { + value.A = 10 + value.B = 20 + value.C = 30 + value.D = true // ok + } +} From 262dbfffad34b93d0fbf55f5b8220953eefe4194 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 23 Oct 2022 17:59:05 +0900 Subject: [PATCH 021/369] Revert Generic AdditionalProperties Argument on TObject --- package.json | 2 +- src/typebox.ts | 12 ++++++------ test/static/object.ts | 16 +++++++--------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3e54b2706..7080127d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.49", + "version": "0.24.50", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index b15fcc030..c38070433 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -312,16 +312,16 @@ export type ObjectPropertyKeys = T extends TObject ? keyof U : never export type TAdditionalProperties = undefined | TSchema | boolean -export interface ObjectOptions extends SchemaOptions { - additionalProperties?: A +export interface ObjectOptions extends SchemaOptions { + additionalProperties?: TAdditionalProperties minProperties?: number maxProperties?: number } -export interface TObject extends TSchema, ObjectOptions { +export interface TObject extends TSchema, ObjectOptions { [Kind]: 'Object' - static: A extends TSchema ? PropertiesReduce & Record> : PropertiesReduce - additionalProperties?: A + static: PropertiesReduce + additionalProperties?: TAdditionalProperties type: 'object' properties: T required?: string[] @@ -729,7 +729,7 @@ export class TypeBuilder { } /** Creates an object type with the given properties */ - public Object(properties: T, options: ObjectOptions = {}): TObject { + public Object(properties: T, options: ObjectOptions = {}): TObject { const property_names = Object.keys(properties) const optional = property_names.filter((name) => { const property = properties[name] as TModifier diff --git a/test/static/object.ts b/test/static/object.ts index 57e58299b..d91856915 100644 --- a/test/static/object.ts +++ b/test/static/object.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' { const T = Type.Object({ @@ -61,12 +61,10 @@ import { Type, Static } from '@sinclair/typebox' additionalProperties: Type.Boolean(), }, ) - // note: the inferenced additionalProperty type does break usual structural - // equivelence and assignment checks, but does allow for the following usage. - function test(value: Static) { - value.A = 10 - value.B = 20 - value.C = 30 - value.D = true // ok - } + // note: Pending TypeScript support for negated types. + Expect(T).ToInfer<{ + A: number + B: number + C: number + }>() } From 573791b382b1b725c7e486ec3f99a5068b974a70 Mon Sep 17 00:00:00 2001 From: Shane da Silva Date: Tue, 25 Oct 2022 21:53:44 -0700 Subject: [PATCH 022/369] Allow Integer keys in Record types (#266) --- src/typebox.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index c38070433..c58665205 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -380,7 +380,7 @@ export interface TPromise extends TSchema { // Record // -------------------------------------------------------------------------- -export type TRecordKey = TString | TNumber | TUnion[]> +export type TRecordKey = TString | TInteger | TNumber | TUnion[]> export interface TRecord extends TSchema { [Kind]: 'Record' @@ -823,7 +823,7 @@ export class TypeBuilder { public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> /** Creates a record type */ - public Record(key: K, schema: T, options?: ObjectOptions): TRecord + public Record(key: K, schema: T, options?: ObjectOptions): TRecord /** Creates a record type */ public Record(key: any, value: any, options: ObjectOptions = {}) { @@ -837,7 +837,7 @@ export class TypeBuilder { ) } // otherwise return TRecord with patternProperties - const pattern = key[Kind] === 'Number' ? '^(0|[1-9][0-9]*)$' : key[Kind] === 'String' && key.pattern ? key.pattern : '^.*$' + const pattern = ['Integer', 'Number'].includes(key[Kind]) ? '^(0|[1-9][0-9]*)$' : key[Kind] === 'String' && key.pattern ? key.pattern : '^.*$' return this.Create({ ...options, [Kind]: 'Record', From d0faeabe3b3066dbb94665738411f863f7af8e64 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 26 Oct 2022 14:16:54 +0900 Subject: [PATCH 023/369] Integer Record Keys Tests --- package.json | 2 +- src/typebox.ts | 4 +- test/runtime/compiler/record.ts | 108 +++++++++++++++++++---------- test/runtime/conditional/record.ts | 32 +++++++++ test/runtime/schema/record.ts | 108 +++++++++++++++++++---------- test/runtime/value/check/record.ts | 71 +++++++++++++++++++ test/static/object.ts | 2 +- test/static/record.ts | 11 +++ 8 files changed, 264 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index 7080127d8..a4b4814db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.50", + "version": "0.24.51", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index c58665205..4f0c98e30 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -380,7 +380,7 @@ export interface TPromise extends TSchema { // Record // -------------------------------------------------------------------------- -export type TRecordKey = TString | TInteger | TNumber | TUnion[]> +export type TRecordKey = TString | TNumeric | TUnion[]> export interface TRecord extends TSchema { [Kind]: 'Record' @@ -823,7 +823,7 @@ export class TypeBuilder { public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> /** Creates a record type */ - public Record(key: K, schema: T, options?: ObjectOptions): TRecord + public Record(key: K, schema: T, options?: ObjectOptions): TRecord /** Creates a record type */ public Record(key: any, value: any, options: ObjectOptions = {}) { diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index aa3384805..608919a69 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -12,41 +12,6 @@ describe('type/compiler/Record', () => { ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) - it('Should validate when all property keys are numbers', () => { - const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) - }) - - it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) - }) - - it('Should not validate if passing a leading zeros for numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { - '00': 1, - '01': 2, - '02': 3, - '03': 4, - }) - }) - - it('Should not validate if passing a signed numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { - '-0': 1, - '-1': 2, - '-2': 3, - '-3': 4, - }) - }) - - it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) - }) - it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) @@ -98,4 +63,77 @@ describe('type/compiler/Record', () => { aop_c: 3, }) }) + + // ------------------------------------------------------------ + // Integer Keys + // ------------------------------------------------------------ + + it('Should validate when all property keys are integers', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are integers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for integers keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed integers keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + // ------------------------------------------------------------ + // Number Keys + // ------------------------------------------------------------ + + it('Should validate when all property keys are numbers', () => { + const T = Type.Record(Type.Number(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) }) diff --git a/test/runtime/conditional/record.ts b/test/runtime/conditional/record.ts index 4e4e0cb7c..48b41ec24 100644 --- a/test/runtime/conditional/record.ts +++ b/test/runtime/conditional/record.ts @@ -87,6 +87,38 @@ describe('conditional/structural/Record', () => { Assert.deepEqual(R, StructuralResult.True) }) + // ----- + + it('Should extend Record 12', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.String(), Type.Number()) + const B = Type.Record(Type.Integer(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + it('Should extend Record 13', () => { + type T = Record extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Record(Type.Integer(), Type.Number()) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Record 14', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.Integer(), Type.Number()) + const B = Type.Record(Type.String(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Record 15', () => { + type T = Record extends Record ? 1 : 2 + const A = Type.Record(Type.Integer(), Type.Number()) + const B = Type.Record(Type.Integer(), Type.Number()) + const R = Structural.Check(A, B) + Assert.deepEqual(R, StructuralResult.True) + }) + // ------------------------------------------------------------------- // Standard // ------------------------------------------------------------------- diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index d88819c94..4f93b89c6 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -12,41 +12,6 @@ describe('type/schema/Record', () => { ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) - it('Should validate when all property keys are numbers', () => { - const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) - }) - - it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) - }) - - it('Should not validate if passing a leading zeros for numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { - '00': 1, - '01': 2, - '02': 3, - '03': 4, - }) - }) - - it('Should not validate if passing a signed numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { - '-0': 1, - '-1': 2, - '-2': 3, - '-3': 4, - }) - }) - - it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) - }) - it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) @@ -98,4 +63,77 @@ describe('type/schema/Record', () => { aop_c: 3, }) }) + + // ------------------------------------------------------------ + // Integer Keys + // ------------------------------------------------------------ + + it('Should validate when all property keys are integers', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are integers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for integers keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed integers keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + // ------------------------------------------------------------ + // Number Keys + // ------------------------------------------------------------ + + it('Should validate when all property keys are numbers', () => { + const T = Type.Record(Type.Number(), Type.Number()) + ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + }) + + it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) + + it('Should not validate if passing a leading zeros for numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '00': 1, + '01': 2, + '02': 3, + '03': 4, + }) + }) + + it('Should not validate if passing a signed numeric keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { + '-0': 1, + '-1': 2, + '-2': 3, + '-3': 4, + }) + }) + + it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { + const T = Type.Record(Type.Number(), Type.Number()) + fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + }) }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index fe58df049..86d3d38e6 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -80,4 +80,75 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) + + it('Should pass record with optional property', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Optional(Type.Number()), + }), + ) + const value = { + position: { + x: 1, + y: 2, + }, + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + // ------------------------------------------------- + // Number Key + // ------------------------------------------------- + + it('Should pass record with number key', () => { + const T = Type.Record(Type.Number(), Type.String()) + const value = { + 0: 'a', + 1: 'a', + 2: 'a', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should not pass record with invalid number key', () => { + const T = Type.Record(Type.Number(), Type.String()) + const value = { + a: 'a', + 1: 'a', + 2: 'a', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + // ------------------------------------------------- + // Integer Key + // ------------------------------------------------- + + it('Should pass record with integer key', () => { + const T = Type.Record(Type.Integer(), Type.String()) + const value = { + 0: 'a', + 1: 'a', + 2: 'a', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should not pass record with invalid integer key', () => { + const T = Type.Record(Type.Integer(), Type.String()) + const value = { + a: 'a', + 1: 'a', + 2: 'a', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/static/object.ts b/test/static/object.ts index d91856915..c9d57419c 100644 --- a/test/static/object.ts +++ b/test/static/object.ts @@ -61,7 +61,7 @@ import { Type } from '@sinclair/typebox' additionalProperties: Type.Boolean(), }, ) - // note: Pending TypeScript support for negated types. + // note: Pending TypeScript support for negated types. Expect(T).ToInfer<{ A: number B: number diff --git a/test/static/record.ts b/test/static/record.ts index 928c052fa..207e30d3f 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -59,3 +59,14 @@ import { Type, Static } from '@sinclair/typebox' type T = Static Expect(T).ToInfer>() } + +{ + const T = Type.Record(Type.Number(), Type.String()) + + Expect(T).ToInfer>() +} +{ + const T = Type.Record(Type.Integer(), Type.String()) + + Expect(T).ToInfer>() +} From 16f65512f3eca8916c9b91805b79ccf712108763 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 29 Oct 2022 04:54:57 +0900 Subject: [PATCH 024/369] TypeBox 0.25.0 (#271) --- changelog.md | 9 + example/index.ts | 2 +- package.json | 2 +- readme.md | 406 +++++++++++------- src/compiler/compiler.ts | 18 +- src/conditional/structural.ts | 18 + src/errors/errors.ts | 25 ++ src/guard/guard.ts | 145 ++++++- src/typebox.ts | 151 ++++--- src/value/cast.ts | 8 +- src/value/check.ts | 27 +- src/value/clone.ts | 16 +- src/value/create.ts | 12 + src/value/delta.ts | 7 + src/value/equal.ts | 6 + src/value/is.ts | 6 +- test/runtime/compiler/date.ts | 61 +++ test/runtime/compiler/index.ts | 2 + test/runtime/compiler/integer.ts | 67 +++ test/runtime/compiler/number.ts | 30 ++ test/runtime/conditional/any.ts | 6 + test/runtime/conditional/array.ts | 6 + test/runtime/conditional/boolean.ts | 6 + test/runtime/conditional/constructor.ts | 7 + test/runtime/conditional/date.ts | 118 +++++ test/runtime/conditional/function.ts | 6 + test/runtime/conditional/index.ts | 1 + test/runtime/conditional/integer.ts | 7 + test/runtime/conditional/literal.ts | 6 + test/runtime/conditional/null.ts | 6 + test/runtime/conditional/number.ts | 8 + test/runtime/conditional/object.ts | 6 + test/runtime/conditional/promise.ts | 6 + test/runtime/conditional/record.ts | 25 ++ test/runtime/conditional/string.ts | 6 + test/runtime/conditional/tuple.ts | 7 + test/runtime/conditional/uint8array.ts | 7 + test/runtime/conditional/undefined.ts | 7 + test/runtime/conditional/union.ts | 12 + test/runtime/conditional/unknown.ts | 6 + test/runtime/conditional/void.ts | 6 + test/runtime/guard/any.ts | 6 +- test/runtime/guard/array.ts | 16 +- test/runtime/guard/boolean.ts | 6 +- test/runtime/guard/constructor.ts | 14 +- test/runtime/guard/date.ts | 45 ++ test/runtime/guard/function.ts | 14 +- test/runtime/guard/index.ts | 1 + test/runtime/guard/integer.ts | 16 +- test/runtime/guard/literal.ts | 12 +- test/runtime/guard/null.ts | 6 +- test/runtime/guard/number.ts | 16 +- test/runtime/guard/object.ts | 20 +- test/runtime/guard/promise.ts | 10 +- test/runtime/guard/record.ts | 12 +- test/runtime/guard/ref.ts | 6 +- test/runtime/guard/self.ts | 4 +- test/runtime/guard/string.ts | 12 +- test/runtime/guard/tuple.ts | 8 +- test/runtime/guard/uint8array.ts | 10 +- test/runtime/guard/undefined.ts | 6 +- test/runtime/guard/union.ts | 10 +- test/runtime/guard/unknown.ts | 6 +- test/runtime/guard/void.ts | 6 +- test/runtime/schema/date.ts | 62 +++ test/runtime/schema/index.ts | 3 + test/runtime/schema/integer.ts | 67 +++ test/runtime/schema/number.ts | 30 ++ test/runtime/schema/uint8array.ts | 56 +++ test/runtime/schema/validate.ts | 42 +- test/runtime/value/cast/any.ts | 8 + test/runtime/value/cast/array.ts | 6 + test/runtime/value/cast/boolean.ts | 6 + test/runtime/value/cast/date.ts | 56 +++ test/runtime/value/cast/enum.ts | 10 + test/runtime/value/cast/index.ts | 4 +- test/runtime/value/cast/integer.ts | 9 + test/runtime/value/cast/intersect.ts | 9 + test/runtime/value/cast/keyof.ts | 6 + test/runtime/value/cast/literal.ts | 9 + test/runtime/value/cast/namespace.ts | 0 test/runtime/value/cast/never.ts | 4 + test/runtime/value/cast/null.ts | 9 + test/runtime/value/cast/number.ts | 9 + test/runtime/value/cast/object.ts | 7 + test/runtime/value/cast/record.ts | 7 + .../value/cast/{rec.ts => recursive.ts} | 8 + test/runtime/value/cast/regex.ts | 9 + test/runtime/value/cast/string.ts | 7 + test/runtime/value/cast/tuple.ts | 6 + test/runtime/value/cast/uint8array.ts | 9 + test/runtime/value/cast/undefined.ts | 9 + test/runtime/value/cast/union.ts | 9 + test/runtime/value/cast/unknown.ts | 11 + test/runtime/value/cast/void.ts | 9 + test/runtime/value/check/any.ts | 5 + test/runtime/value/check/array.ts | 5 + test/runtime/value/check/boolean.ts | 5 + test/runtime/value/check/date.ts | 47 ++ test/runtime/value/check/enum.ts | 5 + test/runtime/value/check/index.ts | 3 +- test/runtime/value/check/integer.ts | 5 + test/runtime/value/check/null.ts | 5 + test/runtime/value/check/number.ts | 5 + .../value/check/{rec.ts => recursive.ts} | 0 test/runtime/value/check/string.ts | 5 + test/runtime/value/check/uint8array.ts | 8 +- test/runtime/value/check/undefined.ts | 5 + test/runtime/value/check/unknown.ts | 5 + test/runtime/value/check/void.ts | 5 + test/static/constructor-parameters.ts | 13 + test/static/constructor.ts | 11 + test/static/date.ts | 4 + test/static/function.ts | 11 + test/static/index.ts | 1 + test/static/parameters.ts | 13 + 116 files changed, 1834 insertions(+), 373 deletions(-) create mode 100644 test/runtime/compiler/date.ts create mode 100644 test/runtime/compiler/integer.ts create mode 100644 test/runtime/conditional/date.ts create mode 100644 test/runtime/guard/date.ts create mode 100644 test/runtime/schema/date.ts create mode 100644 test/runtime/schema/integer.ts create mode 100644 test/runtime/schema/uint8array.ts create mode 100644 test/runtime/value/cast/date.ts delete mode 100644 test/runtime/value/cast/namespace.ts rename test/runtime/value/cast/{rec.ts => recursive.ts} (94%) create mode 100644 test/runtime/value/check/date.ts rename test/runtime/value/check/{rec.ts => recursive.ts} (100%) create mode 100644 test/static/constructor-parameters.ts create mode 100644 test/static/constructor.ts create mode 100644 test/static/date.ts create mode 100644 test/static/function.ts create mode 100644 test/static/parameters.ts diff --git a/changelog.md b/changelog.md index 220a9cfac..660e2863f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,15 @@ +## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) + +Updates: + +- [271](https://github.com/sinclairzx81/typebox/pull/271) Adds a new non-standard `Type.Date()` type. This type joins the existing `Type.UInt8Array()` as a promoted extended type used to represent core JavaScript primitives. It's inclusion was prompted by end user requirements to validate Date objects prior to writing them to Date supported API's and where serialization of the Date object is handled internally by the API. + +- [271](https://github.com/sinclairzx81/typebox/pull/271) Redesign of Extended Type representations. Extended types been updated to provide external validators (such as Ajv) additional standard proporties to use when defining the custom schema. These properties are `instanceOf` (used for validating a class `object` instances), and `typeOf` (when validating `value` types). Information on configuring Ajv for these properties can be found in the Ajv section of the TypeBox readme. + ## [0.24.49](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.49) Updates: + - [264](https://github.com/sinclairzx81/typebox/pull/264) TypeBox now provides preliminary support for non-boolean `additionalProperties`. This allows existing `TObject` schemas to be augmented with additional properties of a known type. Additional: diff --git a/example/index.ts b/example/index.ts index fe4d20dc7..987d926be 100644 --- a/example/index.ts +++ b/example/index.ts @@ -4,7 +4,7 @@ import { Conditional } from '@sinclair/typebox/conditional' import { TypeGuard } from '@sinclair/typebox/guard' import { Format } from '@sinclair/typebox/format' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, Static } from '@sinclair/typebox' +import { Type, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ x: Type.Number(), diff --git a/package.json b/package.json index a4b4814db..fbc4bf63f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.24.51", + "version": "0.25.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index a5bc4caa5..60ec68e6c 100644 --- a/readme.md +++ b/readme.md @@ -56,10 +56,10 @@ License MIT - [Overview](#overview) - [Usage](#usage) - [Types](#types) - - [Standard](#types-standard) + - [Standard Types](#types-standard) + - [Extended Types](#types-extended) - [Modifiers](#types-modifiers) - [Options](#types-options) - - [Extended](#types-extended) - [Reference](#types-reference) - [Recursive](#types-recursive) - [Generic](#types-generic) @@ -79,7 +79,7 @@ License MIT - [Pointer](#values-pointer) - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - - [Compiler](#typecheck-compiler) + - [TypeCompiler](#typecheck-typecompiler) - [Formats](#typecheck-formats) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) @@ -170,9 +170,9 @@ TypeBox provides a set of functions that allow you to compose JSON Schema simila -### Standard +### Standard Types -The following table lists the standard TypeBox types. +The following table lists the Standard TypeBox types. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -375,6 +375,81 @@ The following table lists the standard TypeBox types. └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` + + +### Extended Types + +TypeBox provides a set of extended types that can be used to express schematics for core JavaScript constructs and primitives. Extended types are not valid JSON Schema and will not validate using typical validation. These types however can be used to frame JSON schema and describe callable RPC interfaces that may receive JSON validated data. + +```typescript +┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ +│ TypeBox │ TypeScript │ Extended Schema │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Constructor([ │ type T = new ( │ const T = { │ +│ Type.String(), │ arg0: string, │ type: 'object', │ +│ Type.Number() │ arg1: number │ instanceOf: 'Constructor', │ +│ ], Type.Boolean()) │ ) => boolean │ parameters: [{ │ +│ │ │ type: 'string' │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ }], │ +│ │ │ return: { │ +│ │ │ type: 'boolean' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Function([ │ type T = ( │ const T = { │ +| Type.String(), │ arg0: string, │ type : 'object', │ +│ Type.Number() │ arg1: number │ instanceOf: 'Function', │ +│ ], Type.Boolean()) │ ) => boolean │ parameters: [{ │ +│ │ │ type: 'string' │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ }], │ +│ │ │ return: { │ +│ │ │ type: 'boolean' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Promise( │ type T = Promise │ const T = { │ +│ Type.String() │ │ type: 'object', │ +│ ) │ │ instanceOf: 'Promise', │ +│ │ │ item: { │ +│ │ │ type: 'string' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │ +│ │ │ type: 'object', │ +│ │ │ instanceOf: 'Uint8Array' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Date() │ type T = Date │ const T = { │ +│ │ │ type: 'object', │ +│ │ │ instanceOf: 'Date' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Undefined() │ type T = undefined │ const T = { │ +│ │ │ type: 'null', │ +│ │ │ typeOf: 'Undefined' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Void() │ type T = void │ const T = { │ +│ │ │ type: 'null' │ +│ │ │ typeOf: 'Void' │ +│ │ │ } │ +│ │ │ │ +└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ +``` + ### Modifiers @@ -436,70 +511,6 @@ const T = Type.Number({ multipleOf: 2 }) const T = Type.Array(Type.Integer(), { minItems: 5 }) ``` - - -### Extended - -In addition to JSON schema types, TypeBox provides several extended types that allow for the composition of `function` and `constructor` types. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows. - -```typescript -┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ -│ TypeBox │ TypeScript │ Extended Schema │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Constructor([ │ type T = new ( │ const T = { │ -│ Type.String(), │ arg0: string, │ type: 'constructor' │ -│ Type.Number() │ arg1: number │ parameters: [{ │ -│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ -│ │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ }], │ -│ │ │ return: { │ -│ │ │ type: 'boolean' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Function([ │ type T = ( │ const T = { │ -| Type.String(), │ arg0: string, │ type : 'function', │ -│ Type.Number() │ arg1: number │ parameters: [{ │ -│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ -│ │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ }], │ -│ │ │ return: { │ -│ │ │ type: 'boolean' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │ -│ │ │ type: 'object', │ -│ │ │ specialized: 'Uint8Array' │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Promise( │ type T = Promise │ const T = { │ -│ Type.String() │ │ type: 'promise', │ -│ ) │ │ item: { │ -│ │ │ type: 'string' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Undefined() │ type T = undefined │ const T = { │ -│ │ │ type: 'object', │ -│ │ │ specialized: 'Undefined' │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Void() │ type T = void │ const T = { │ -│ │ │ type: 'null' │ -│ │ │ } │ -│ │ │ │ -└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ -``` - ### Reference @@ -890,70 +901,143 @@ ValuePointer.Set(A, '/z', 1) // const A = { x: 1, y: 1, ## TypeCheck -TypeBox is written to target JSON Schema Draft 6 and can be used with any Draft 6 compliant validator. TypeBox is developed and tested against Ajv and can be used in any application already making use of this validator. Additionally, TypeBox also provides an optional type compiler that can be used to attain improved compilation and validation performance for certain application types. +TypeBox targets JSON Schema Draft 6 and is built and tested against the Ajv JSON Schema validator for standards compliance. TypeBox also includes an optional built-in TypeCompiler that can provide improved compilation and validation performance specifically for TypeBox types only. + +The following sections detail using these validators. ### Ajv -The following example shows setting up Ajv to work with TypeBox. +The following are the recommended configurations to support both the [Standard](#standard) and [Extended](#extended) type sets provided by TypeBox. For schema portability and publishing to remote systems, it is recommended to use the Standard type set only. ```bash $ npm install ajv ajv-formats --save ``` +

    + + +Standard Ajv Configuration +

    Expand for Standard Type Set Configuration

    +
    + ```typescript import { Type } from '@sinclair/typebox' import addFormats from 'ajv-formats' import Ajv from 'ajv' -//-------------------------------------------------------------------------------------------- -// -// Setup Ajv validator with the following options and formats -// -//-------------------------------------------------------------------------------------------- - -const ajv = addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' -]) +export function createAjv() { + return addFormats(new Ajv({}), [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' + ]) +} -//-------------------------------------------------------------------------------------------- -// -// Create a TypeBox type -// -//-------------------------------------------------------------------------------------------- +const ajv = createAjv() -const T = Type.Object({ +const R = ajv.validate(Type.Object({ // const R = true x: Type.Number(), y: Type.Number(), z: Type.Number() -}) +}), { x: 1, y: 2, z: 3 }) +``` -//-------------------------------------------------------------------------------------------- -// -// Validate Data -// -//-------------------------------------------------------------------------------------------- +
    + +
    -const R = ajv.validate(T, { x: 1, y: 2, z: 3 }) // const R = true + +Extended Ajv Configuration +

    Expand for Extended Type Set Configuration

    +
    + +```typescript +import { TypeGuard } from '@sinclair/typebox/guard' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import addFormats from 'ajv-formats' +import Ajv from 'ajv' + +function schemaOf(schemaOf: string, value: unknown, schema: unknown) { + switch (schemaOf) { + case 'Constructor': + return TypeGuard.TConstructor(schema) && Value.Check(schema, value) // not supported + case 'Function': + return TypeGuard.TFunction(schema) && Value.Check(schema, value) // not supported + case 'Date': + return TypeGuard.TDate(schema) && Value.Check(schema, value) + case 'Promise': + return TypeGuard.TPromise(schema) && Value.Check(schema, value) // not supported + case 'Uint8Array': + return TypeGuard.TUint8Array(schema) && Value.Check(schema, value) + case 'Undefined': + return TypeGuard.TUndefined(schema) && Value.Check(schema, value) // not supported + case 'Void': + return TypeGuard.TVoid(schema) && Value.Check(schema, value) + default: + return false + } +} + +export function createAjv() { + return addFormats(new Ajv({}), [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' + ]) + .addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf }) + .addKeyword({ type: 'null', keyword: 'typeOf', validate: schemaOf }) + .addKeyword('exclusiveMinimumTimestamp') + .addKeyword('exclusiveMaximumTimestamp') + .addKeyword('minimumTimestamp') + .addKeyword('maximumTimestamp') + .addKeyword('minByteLength') + .addKeyword('maxByteLength') +} + +const ajv = createAjv() + +const R = ajv.validate(Type.Object({ // const R = true + buffer: Type.Uint8Array(), + date: Type.Date(), + void: Type.Void() +}), { + buffer: new Uint8Array(), + date: new Date(), + void: null +}) ``` - +
    + + + -### Compiler +### TypeCompiler TypeBox provides an optional high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. If defining custom types with `Type.Unsafe` please consider Ajv. @@ -1062,29 +1146,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 428 ms' │ ' 12 ms' │ ' 35.67 x' │ -│ String │ 2000 │ ' 337 ms' │ ' 12 ms' │ ' 28.08 x' │ -│ Boolean │ 2000 │ ' 317 ms' │ ' 11 ms' │ ' 28.82 x' │ -│ Null │ 2000 │ ' 274 ms' │ ' 10 ms' │ ' 27.40 x' │ -│ RegEx │ 2000 │ ' 500 ms' │ ' 18 ms' │ ' 27.78 x' │ -│ ObjectA │ 2000 │ ' 2717 ms' │ ' 49 ms' │ ' 55.45 x' │ -│ ObjectB │ 2000 │ ' 2854 ms' │ ' 37 ms' │ ' 77.14 x' │ -│ Tuple │ 2000 │ ' 1224 ms' │ ' 21 ms' │ ' 58.29 x' │ -│ Union │ 2000 │ ' 1266 ms' │ ' 23 ms' │ ' 55.04 x' │ -│ Vector4 │ 2000 │ ' 1513 ms' │ ' 19 ms' │ ' 79.63 x' │ -│ Matrix4 │ 2000 │ ' 841 ms' │ ' 12 ms' │ ' 70.08 x' │ -│ Literal_String │ 2000 │ ' 327 ms' │ ' 8 ms' │ ' 40.88 x' │ -│ Literal_Number │ 2000 │ ' 358 ms' │ ' 6 ms' │ ' 59.67 x' │ -│ Literal_Boolean │ 2000 │ ' 355 ms' │ ' 5 ms' │ ' 71.00 x' │ -│ Array_Number │ 2000 │ ' 685 ms' │ ' 7 ms' │ ' 97.86 x' │ -│ Array_String │ 2000 │ ' 716 ms' │ ' 11 ms' │ ' 65.09 x' │ -│ Array_Boolean │ 2000 │ ' 732 ms' │ ' 6 ms' │ ' 122.00 x' │ -│ Array_ObjectA │ 2000 │ ' 3503 ms' │ ' 34 ms' │ ' 103.03 x' │ -│ Array_ObjectB │ 2000 │ ' 3626 ms' │ ' 38 ms' │ ' 95.42 x' │ -│ Array_Tuple │ 2000 │ ' 2095 ms' │ ' 21 ms' │ ' 99.76 x' │ -│ Array_Union │ 2000 │ ' 1577 ms' │ ' 22 ms' │ ' 71.68 x' │ -│ Array_Vector4 │ 2000 │ ' 2172 ms' │ ' 17 ms' │ ' 127.76 x' │ -│ Array_Matrix4 │ 2000 │ ' 1468 ms' │ ' 19 ms' │ ' 77.26 x' │ +│ Number │ 2000 │ ' 421 ms' │ ' 11 ms' │ ' 38.27 x' │ +│ String │ 2000 │ ' 332 ms' │ ' 11 ms' │ ' 30.18 x' │ +│ Boolean │ 2000 │ ' 291 ms' │ ' 12 ms' │ ' 24.25 x' │ +│ Null │ 2000 │ ' 266 ms' │ ' 9 ms' │ ' 29.56 x' │ +│ RegEx │ 2000 │ ' 486 ms' │ ' 17 ms' │ ' 28.59 x' │ +│ ObjectA │ 2000 │ ' 2791 ms' │ ' 50 ms' │ ' 55.82 x' │ +│ ObjectB │ 2000 │ ' 2896 ms' │ ' 37 ms' │ ' 78.27 x' │ +│ Tuple │ 2000 │ ' 1244 ms' │ ' 21 ms' │ ' 59.24 x' │ +│ Union │ 2000 │ ' 1258 ms' │ ' 25 ms' │ ' 50.32 x' │ +│ Vector4 │ 2000 │ ' 1513 ms' │ ' 21 ms' │ ' 72.05 x' │ +│ Matrix4 │ 2000 │ ' 850 ms' │ ' 11 ms' │ ' 77.27 x' │ +│ Literal_String │ 2000 │ ' 335 ms' │ ' 7 ms' │ ' 47.86 x' │ +│ Literal_Number │ 2000 │ ' 358 ms' │ ' 7 ms' │ ' 51.14 x' │ +│ Literal_Boolean │ 2000 │ ' 356 ms' │ ' 7 ms' │ ' 50.86 x' │ +│ Array_Number │ 2000 │ ' 689 ms' │ ' 9 ms' │ ' 76.56 x' │ +│ Array_String │ 2000 │ ' 728 ms' │ ' 12 ms' │ ' 60.67 x' │ +│ Array_Boolean │ 2000 │ ' 726 ms' │ ' 7 ms' │ ' 103.71 x' │ +│ Array_ObjectA │ 2000 │ ' 3631 ms' │ ' 35 ms' │ ' 103.74 x' │ +│ Array_ObjectB │ 2000 │ ' 3636 ms' │ ' 40 ms' │ ' 90.90 x' │ +│ Array_Tuple │ 2000 │ ' 2119 ms' │ ' 31 ms' │ ' 68.35 x' │ +│ Array_Union │ 2000 │ ' 1550 ms' │ ' 23 ms' │ ' 67.39 x' │ +│ Array_Vector4 │ 2000 │ ' 2200 ms' │ ' 18 ms' │ ' 122.22 x' │ +│ Array_Matrix4 │ 2000 │ ' 1494 ms' │ ' 14 ms' │ ' 106.71 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1098,31 +1182,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 24 ms' │ ' 9 ms' │ ' 6 ms' │ ' 1.50 x' │ -│ String │ 1000000 │ ' 23 ms' │ ' 19 ms' │ ' 12 ms' │ ' 1.58 x' │ -│ Boolean │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Null │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ RegEx │ 1000000 │ ' 164 ms' │ ' 46 ms' │ ' 38 ms' │ ' 1.21 x' │ -│ ObjectA │ 1000000 │ ' 548 ms' │ ' 36 ms' │ ' 22 ms' │ ' 1.64 x' │ -│ ObjectB │ 1000000 │ ' 1118 ms' │ ' 51 ms' │ ' 38 ms' │ ' 1.34 x' │ -│ Tuple │ 1000000 │ ' 136 ms' │ ' 25 ms' │ ' 14 ms' │ ' 1.79 x' │ -│ Union │ 1000000 │ ' 338 ms' │ ' 27 ms' │ ' 16 ms' │ ' 1.69 x' │ -│ Recursive │ 1000000 │ ' 3251 ms' │ ' 416 ms' │ ' 98 ms' │ ' 4.24 x' │ -│ Vector4 │ 1000000 │ ' 146 ms' │ ' 23 ms' │ ' 12 ms' │ ' 1.92 x' │ -│ Matrix4 │ 1000000 │ ' 584 ms' │ ' 40 ms' │ ' 25 ms' │ ' 1.60 x' │ -│ Literal_String │ 1000000 │ ' 46 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ -│ Array_Number │ 1000000 │ ' 456 ms' │ ' 31 ms' │ ' 19 ms' │ ' 1.63 x' │ -│ Array_String │ 1000000 │ ' 489 ms' │ ' 40 ms' │ ' 25 ms' │ ' 1.60 x' │ -│ Array_Boolean │ 1000000 │ ' 458 ms' │ ' 35 ms' │ ' 27 ms' │ ' 1.30 x' │ -│ Array_ObjectA │ 1000000 │ ' 13559 ms' │ ' 2568 ms' │ ' 1564 ms' │ ' 1.64 x' │ -│ Array_ObjectB │ 1000000 │ ' 15863 ms' │ ' 2744 ms' │ ' 2060 ms' │ ' 1.33 x' │ -│ Array_Tuple │ 1000000 │ ' 1694 ms' │ ' 96 ms' │ ' 63 ms' │ ' 1.52 x' │ -│ Array_Union │ 1000000 │ ' 4736 ms' │ ' 229 ms' │ ' 86 ms' │ ' 2.66 x' │ -│ Array_Recursive │ 1000000 │ ' 53804 ms' │ ' 6744 ms' │ ' 1167 ms' │ ' 5.78 x' │ -│ Array_Vector4 │ 1000000 │ ' 2244 ms' │ ' 99 ms' │ ' 46 ms' │ ' 2.15 x' │ -│ Array_Matrix4 │ 1000000 │ ' 11966 ms' │ ' 378 ms' │ ' 229 ms' │ ' 1.65 x' │ +│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ +│ String │ 1000000 │ ' 25 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Boolean │ 1000000 │ ' 34 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Null │ 1000000 │ ' 37 ms' │ ' 28 ms' │ ' 10 ms' │ ' 2.80 x' │ +│ RegEx │ 1000000 │ ' 162 ms' │ ' 50 ms' │ ' 37 ms' │ ' 1.35 x' │ +│ ObjectA │ 1000000 │ ' 550 ms' │ ' 38 ms' │ ' 22 ms' │ ' 1.73 x' │ +│ ObjectB │ 1000000 │ ' 1033 ms' │ ' 58 ms' │ ' 38 ms' │ ' 1.53 x' │ +│ Tuple │ 1000000 │ ' 126 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ +│ Union │ 1000000 │ ' 326 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ +│ Recursive │ 1000000 │ ' 3089 ms' │ ' 436 ms' │ ' 101 ms' │ ' 4.32 x' │ +│ Vector4 │ 1000000 │ ' 155 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 579 ms' │ ' 41 ms' │ ' 28 ms' │ ' 1.46 x' │ +│ Literal_String │ 1000000 │ ' 50 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Array_Number │ 1000000 │ ' 424 ms' │ ' 32 ms' │ ' 19 ms' │ ' 1.68 x' │ +│ Array_String │ 1000000 │ ' 483 ms' │ ' 34 ms' │ ' 28 ms' │ ' 1.21 x' │ +│ Array_Boolean │ 1000000 │ ' 432 ms' │ ' 35 ms' │ ' 26 ms' │ ' 1.35 x' │ +│ Array_ObjectA │ 1000000 │ ' 13895 ms' │ ' 2440 ms' │ ' 1495 ms' │ ' 1.63 x' │ +│ Array_ObjectB │ 1000000 │ ' 16261 ms' │ ' 2633 ms' │ ' 2011 ms' │ ' 1.31 x' │ +│ Array_Tuple │ 1000000 │ ' 1741 ms' │ ' 98 ms' │ ' 66 ms' │ ' 1.48 x' │ +│ Array_Union │ 1000000 │ ' 4825 ms' │ ' 232 ms' │ ' 87 ms' │ ' 2.67 x' │ +│ Array_Recursive │ 1000000 │ ' 54158 ms' │ ' 6966 ms' │ ' 1173 ms' │ ' 5.94 x' │ +│ Array_Vector4 │ 1000000 │ ' 2577 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ +│ Array_Matrix4 │ 1000000 │ ' 12648 ms' │ ' 397 ms' │ ' 249 ms' │ ' 1.59 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1136,12 +1220,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 51 kb' │ ' 25 kb' │ '2.00 x' │ -│ typebox/conditional │ ' 42 kb' │ ' 17 kb' │ '2.46 x' │ +│ typebox/compiler │ ' 54 kb' │ ' 27 kb' │ '1.97 x' │ +│ typebox/conditional │ ' 44 kb' │ ' 17 kb' │ '2.45 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ -│ typebox/guard │ ' 21 kb' │ ' 10 kb' │ '2.08 x' │ -│ typebox/value │ ' 74 kb' │ ' 34 kb' │ '2.16 x' │ -│ typebox │ ' 11 kb' │ ' 6 kb' │ '1.91 x' │ +│ typebox/guard │ ' 22 kb' │ ' 11 kb' │ '2.05 x' │ +│ typebox/value │ ' 78 kb' │ ' 36 kb' │ '2.13 x' │ +│ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 7ebae8990..a7f41db03 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -54,7 +54,7 @@ export class TypeCheck { return ValueErrors.Errors(this.schema, this.references, value) } - /** Returns true if the value matches the given type. */ + /** Returns true if the value matches the compiled type. */ public Check(value: unknown): value is Types.Static { return this.checkFunc(value) } @@ -132,11 +132,19 @@ export namespace TypeCompiler { yield* Visit(schema.returns, `${value}.prototype`) } + function* Date(schema: Types.TDate, value: string): IterableIterator { + yield `(${value} instanceof Date)` + if (schema.exclusiveMinimumTimestamp !== undefined) yield `(${value}.getTime() > ${schema.exclusiveMinimumTimestamp})` + if (schema.exclusiveMaximumTimestamp !== undefined) yield `(${value}.getTime() < ${schema.exclusiveMaximumTimestamp})` + if (schema.minimumTimestamp !== undefined) yield `(${value}.getTime() >= ${schema.minimumTimestamp})` + if (schema.maximumTimestamp !== undefined) yield `(${value}.getTime() <= ${schema.maximumTimestamp})` + } + function* Function(schema: Types.TFunction, value: string): IterableIterator { yield `(typeof ${value} === 'function')` } - function* Integer(schema: Types.TNumeric, value: string): IterableIterator { + function* Integer(schema: Types.TInteger, value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` @@ -161,7 +169,7 @@ export namespace TypeCompiler { yield `(${value} === null)` } - function* Number(schema: Types.TNumeric, value: string): IterableIterator { + function* Number(schema: Types.TNumber, value: string): IterableIterator { yield `(typeof ${value} === 'number')` if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` @@ -209,7 +217,7 @@ export namespace TypeCompiler { } function* Record(schema: Types.TRecord, value: string): IterableIterator { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date))` const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${keyPattern}/)`) yield `(Object.keys(${value}).every(key => ${local}.test(key)))` @@ -304,6 +312,8 @@ export namespace TypeCompiler { return yield* Boolean(anySchema, value) case 'Constructor': return yield* Constructor(anySchema, value) + case 'Date': + return yield* Date(anySchema, value) case 'Function': return yield* Function(anySchema, value) case 'Integer': diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts index 18790d858..62c04ac33 100644 --- a/src/conditional/structural.ts +++ b/src/conditional/structural.ts @@ -197,6 +197,22 @@ export namespace Structural { } } + function Date(left: Types.TDate, right: Types.TSchema): StructuralResult { + if (AnyOrUnknownRule(right)) { + return StructuralResult.True + } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { + return StructuralResult.True + } else if (TypeGuard.TRecord(right)) { + return StructuralResult.False + } else if (TypeGuard.TDate(right)) { + return StructuralResult.True + } else if (TypeGuard.TUnion(right)) { + return UnionRightRule(left, right) + } else { + return StructuralResult.False + } + } + function Function(left: Types.TFunction, right: Types.TSchema): StructuralResult { if (AnyOrUnknownRule(right)) { return StructuralResult.True @@ -510,6 +526,8 @@ export namespace Structural { return Boolean(left, resolvedRight) } else if (TypeGuard.TConstructor(left)) { return Constructor(left, resolvedRight) + } else if (TypeGuard.TDate(left)) { + return Date(left, resolvedRight) } else if (TypeGuard.TFunction(left)) { return Function(left, resolvedRight) } else if (TypeGuard.TInteger(left)) { diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 913a60226..98fd0bf6c 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -39,6 +39,11 @@ export enum ValueErrorType { ArrayMaxItems, ArrayUniqueItems, Boolean, + Date, + DateExclusiveMinimum, + DateExclusiveMaximum, + DateMinimum, + DateMaximum, Function, Integer, IntegerMultipleOf, @@ -132,6 +137,24 @@ export namespace ValueErrors { yield* Visit(schema.returns, references, path, value.prototype) } + function* Date(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value instanceof globalThis.Date)) { + return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } + } + if (schema.exclusiveMinimumTimestamp && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + yield { type: ValueErrorType.DateExclusiveMinimum, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } + } + if (schema.exclusiveMaximumTimestamp && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + yield { type: ValueErrorType.DateExclusiveMaximum, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } + } + if (schema.minimumTimestamp && !(value.getTime() >= schema.minimumTimestamp)) { + yield { type: ValueErrorType.DateMinimum, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } + } + if (schema.maximumTimestamp && !(value.getTime() <= schema.maximumTimestamp)) { + yield { type: ValueErrorType.DateMaximum, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } + } + } + function* Function(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(typeof value === 'function')) { return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } @@ -379,6 +402,8 @@ export namespace ValueErrors { return yield* Boolean(anySchema, anyReferences, path, value) case 'Constructor': return yield* Constructor(anySchema, anyReferences, path, value) + case 'Date': + return yield* Date(anySchema, anyReferences, path, value) case 'Function': return yield* Function(anySchema, anyReferences, path, value) case 'Integer': diff --git a/src/guard/guard.ts b/src/guard/guard.ts index ac9d54430..24f23c2eb 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -83,18 +83,23 @@ export namespace TypeGuard { function IsOptionalBoolean(value: unknown): value is boolean | undefined { return value === undefined || (value !== undefined && IsBoolean(value)) } + function IsOptionalString(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value)) } + function IsOptionalPattern(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) } + function IsOptionalFormat(value: unknown): value is string | undefined { return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) } + function IsOptionalSchema(value: unknown): value is boolean | undefined { return value === undefined || TSchema(value) } + /** Returns true if the given schema is TAny */ export function TAny(schema: unknown): schema is Types.TAny { return IsObject(schema) && schema[Types.Kind] === 'Any' && IsOptionalString(schema.$id) @@ -116,12 +121,27 @@ export namespace TypeGuard { /** Returns true if the given schema is TBoolean */ export function TBoolean(schema: unknown): schema is Types.TBoolean { - return IsObject(schema) && schema[Types.Kind] === 'Boolean' && schema.type === 'boolean' && IsOptionalString(schema.$id) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Boolean' && + schema.type === 'boolean' && + IsOptionalString(schema.$id) + ) } /** Returns true if the given schema is TConstructor */ export function TConstructor(schema: unknown): schema is Types.TConstructor { - if (!(IsObject(schema) && schema[Types.Kind] === 'Constructor' && schema.type === 'constructor' && IsOptionalString(schema.$id) && IsArray(schema.parameters) && TSchema(schema.returns))) { + // prettier-ignore + if (!( + IsObject(schema) && + schema[Types.Kind] === 'Constructor' && + schema.type === 'object' && + schema.instanceOf === 'Constructor' && + IsOptionalString(schema.$id) && + IsArray(schema.parameters) && + TSchema(schema.returns)) + ) { return false } for (const parameter of schema.parameters) { @@ -130,9 +150,33 @@ export namespace TypeGuard { return true } + /** Returns true if the given schema is TDate */ + export function TDate(schema: unknown): schema is Types.TDate { + return ( + IsObject(schema) && + schema[Types.Kind] === 'Date' && + schema.type === 'object' && + schema.instanceOf === 'Date' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minimumTimestamp) && + IsOptionalNumber(schema.maximumTimestamp) && + IsOptionalNumber(schema.exclusiveMinimumTimestamp) && + IsOptionalNumber(schema.exclusiveMaximumTimestamp) + ) + } + /** Returns true if the given schema is TFunction */ export function TFunction(schema: unknown): schema is Types.TFunction { - if (!(IsObject(schema) && schema[Types.Kind] === 'Function' && schema.type === 'function' && IsOptionalString(schema.$id) && IsArray(schema.parameters) && TSchema(schema.returns))) { + // prettier-ignore + if (!( + IsObject(schema) && + schema[Types.Kind] === 'Function' && + schema.type === 'object' && + schema.instanceOf === 'Function' && + IsOptionalString(schema.$id) && + IsArray(schema.parameters) && + TSchema(schema.returns)) + ) { return false } for (const parameter of schema.parameters) { @@ -158,7 +202,17 @@ export namespace TypeGuard { /** Returns true if the given schema is TLiteral */ export function TLiteral(schema: unknown): schema is Types.TLiteral { - return IsObject(schema) && schema[Types.Kind] === 'Literal' && IsOptionalString(schema.$id) && (IsString(schema.const) || IsNumber(schema.const) || IsBoolean(schema.const)) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Literal' && + IsOptionalString(schema.$id) && + ( + IsString(schema.const) || + IsNumber(schema.const) || + IsBoolean(schema.const) + ) + ) } /** Returns true if the given schema is TNever */ @@ -224,12 +278,28 @@ export namespace TypeGuard { /** Returns true if the given schema is TPromise */ export function TPromise(schema: unknown): schema is Types.TPromise { - return IsObject(schema) && schema[Types.Kind] === 'Promise' && schema.type === 'promise' && IsOptionalString(schema.$id) && TSchema(schema.item) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Promise' && + schema.type === 'object' && + schema.instanceOf === 'Promise' && + IsOptionalString(schema.$id) && + TSchema(schema.item) + ) } /** Returns true if the given schema is TRecord */ export function TRecord(schema: unknown): schema is Types.TRecord { - if (!(IsObject(schema) && schema[Types.Kind] === 'Record' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.additionalProperties === false && IsObject(schema.patternProperties))) { + // prettier-ignore + if (!( + IsObject(schema) && + schema[Types.Kind] === 'Record' && + schema.type === 'object' && + IsOptionalString(schema.$id) && + schema.additionalProperties === false && + IsObject(schema.patternProperties)) + ) { return false } const keys = Object.keys(schema.patternProperties) @@ -247,12 +317,24 @@ export namespace TypeGuard { /** Returns true if the given schema is TSelf */ export function TSelf(schema: unknown): schema is Types.TSelf { - return IsObject(schema) && schema[Types.Kind] === 'Self' && IsOptionalString(schema.$id) && IsString(schema.$ref) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Self' && + IsOptionalString(schema.$id) && + IsString(schema.$ref) + ) } /** Returns true if the given schema is TRef */ export function TRef(schema: unknown): schema is Types.TRef { - return IsObject(schema) && schema[Types.Kind] === 'Ref' && IsOptionalString(schema.$id) && IsString(schema.$ref) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Ref' && + IsOptionalString(schema.$id) && + IsString(schema.$ref) + ) } /** Returns true if the given schema is TString */ @@ -271,7 +353,16 @@ export namespace TypeGuard { /** Returns true if the given schema is TTuple */ export function TTuple(schema: unknown): schema is Types.TTuple { - if (!(IsObject(schema) && schema[Types.Kind] === 'Tuple' && schema.type === 'array' && IsOptionalString(schema.$id) && IsNumber(schema.minItems) && IsNumber(schema.maxItems) && schema.minItems === schema.maxItems)) { + // prettier-ignore + if (!( + IsObject(schema) && + schema[Types.Kind] === 'Tuple' && + schema.type === 'array' && + IsOptionalString(schema.$id) && + IsNumber(schema.minItems) && + IsNumber(schema.maxItems) && + schema.minItems === schema.maxItems) + ) { return false } if (schema.items === undefined && schema.additionalItems === undefined && schema.minItems === 0) { @@ -288,12 +379,25 @@ export namespace TypeGuard { /** Returns true if the given schema is TUndefined */ export function TUndefined(schema: unknown): schema is Types.TUndefined { - return IsObject(schema) && schema[Types.Kind] === 'Undefined' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.specialized === 'Undefined' + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Undefined' && + schema.type === 'null' && + schema.typeOf === 'Undefined' && + IsOptionalString(schema.$id) + ) } /** Returns true if the given schema is TUnion */ export function TUnion(schema: unknown): schema is Types.TUnion { - if (!(IsObject(schema) && schema[Types.Kind] === 'Union' && IsArray(schema.anyOf) && IsOptionalString(schema.$id))) { + // prettier-ignore + if (!( + IsObject(schema) && + schema[Types.Kind] === 'Union' && + IsArray(schema.anyOf) && + IsOptionalString(schema.$id)) + ) { return false } for (const inner of schema.anyOf) { @@ -309,7 +413,7 @@ export namespace TypeGuard { schema[Types.Kind] === 'Uint8Array' && schema.type === 'object' && IsOptionalString(schema.$id) && - schema.specialized === 'Uint8Array' && + schema.instanceOf === 'Uint8Array' && IsOptionalNumber(schema.minByteLength) && IsOptionalNumber(schema.maxByteLength) ) @@ -317,12 +421,24 @@ export namespace TypeGuard { /** Returns true if the given schema is TUnknown */ export function TUnknown(schema: unknown): schema is Types.TUnknown { - return IsObject(schema) && schema[Types.Kind] === 'Unknown' && IsOptionalString(schema.$id) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Unknown' && + IsOptionalString(schema.$id) + ) } /** Returns true if the given schema is TVoid */ export function TVoid(schema: unknown): schema is Types.TVoid { - return IsObject(schema) && schema[Types.Kind] === 'Void' && schema.type === 'null' && IsOptionalString(schema.$id) + // prettier-ignore + return ( + IsObject(schema) && + schema[Types.Kind] === 'Void' && + schema.type === 'null' && + schema.typeOf === 'Void' && + IsOptionalString(schema.$id) + ) } /** Returns true if the given schema is TSchema */ @@ -332,6 +448,7 @@ export namespace TypeGuard { TArray(schema) || TBoolean(schema) || TConstructor(schema) || + TDate(schema) || TFunction(schema) || TInteger(schema) || TLiteral(schema) || diff --git a/src/typebox.ts b/src/typebox.ts index 4f0c98e30..5c8daf5cb 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -80,6 +80,7 @@ export type TAnySchema = | TArray | TBoolean | TConstructor + | TDate | TEnum | TFunction | TInteger @@ -162,11 +163,30 @@ export type StaticContructorParameters extends TSchema { [Kind]: 'Constructor' static: new (...param: StaticContructorParameters) => Static - type: 'constructor' + type: 'object' + instanceOf: 'Constructor' parameters: T returns: U } +// -------------------------------------------------------------------------- +// Date +// -------------------------------------------------------------------------- + +export interface DateOptions extends SchemaOptions { + exclusiveMaximumTimestamp?: number + exclusiveMinimumTimestamp?: number + maximumTimestamp?: number + minimumTimestamp?: number +} + +export interface TDate extends TSchema, DateOptions { + [Kind]: 'Date' + static: Date + type: 'object' + instanceOf: 'Date' +} + // -------------------------------------------------------------------------- // Enum // -------------------------------------------------------------------------- @@ -195,7 +215,8 @@ export type StaticFunctionParameters extends TSchema { [Kind]: 'Function' static: (...param: StaticFunctionParameters) => Static - type: 'function' + type: 'object' + instanceOf: 'Function' parameters: T returns: U } @@ -372,7 +393,8 @@ export type TPick[]> export interface TPromise extends TSchema { [Kind]: 'Promise' static: Promise> - type: 'promise' + type: 'object' + instanceOf: 'Promise' item: TSchema } @@ -493,9 +515,9 @@ export interface TTuple extends TSchema { export interface TUndefined extends TSchema { [Kind]: 'Undefined' - specialized: 'Undefined' static: undefined - type: 'object' + type: 'null' + typeOf: 'Undefined' } // -------------------------------------------------------------------------- @@ -520,7 +542,7 @@ export interface Uint8ArrayOptions extends SchemaOptions { export interface TUint8Array extends TSchema, Uint8ArrayOptions { [Kind]: 'Uint8Array' static: Uint8Array - specialized: 'Uint8Array' + instanceOf: 'Uint8Array' type: 'object' } @@ -554,6 +576,7 @@ export interface TVoid extends TSchema { [Kind]: 'Void' static: void type: 'null' + typeOf: 'Void' } // -------------------------------------------------------------------------- @@ -593,45 +616,50 @@ export class TypeBuilder { // Types // ---------------------------------------------------------------------- - /** Creates a any type */ + /** `Standard` Creates a any type */ public Any(options: SchemaOptions = {}): TAny { return this.Create({ ...options, [Kind]: 'Any' }) } - /** Creates a array type */ + /** `Standard` Creates a array type */ public Array(items: T, options: ArrayOptions = {}): TArray { return this.Create({ ...options, [Kind]: 'Array', type: 'array', items }) } - /** Creates a boolean type */ + /** `Standard` Creates a boolean type */ public Boolean(options: SchemaOptions = {}): TBoolean { return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) } - /** Creates a tuple type from this constructors parameters */ + /** `Extended` Creates a tuple type from this constructors parameters */ public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { return this.Tuple([...schema.parameters], { ...options }) } - /** Creates a constructor type */ + /** `Extended` Creates a constructor type */ public Constructor, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor, U> - /** Creates a constructor type */ + /** `Extended` Creates a constructor type */ public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor - /** Creates a constructor type */ + /** `Extended` Creates a constructor type */ public Constructor(parameters: any, returns: any, options: SchemaOptions = {}) { if (parameters[Kind] === 'Tuple') { const inner = parameters.items === undefined ? [] : parameters.items - return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters: inner, returns }) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: inner, returns }) } else if (globalThis.Array.isArray(parameters)) { - return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters, returns }) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters, returns }) } else { throw new Error('TypeBuilder.Constructor: Invalid parameters') } } - /** Creates a enum type */ + /** `Extended` Creates a Date type */ + public Date(options: DateOptions = {}): TDate { + return this.Create({ ...options, [Kind]: 'Date', type: 'object', instanceOf: 'Date' }) + } + + /** `Standard` Creates a enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { const values = Object.keys(item) .filter((key) => isNaN(key as any)) @@ -640,35 +668,35 @@ export class TypeBuilder { return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'Enum', anyOf }) } - /** Creates a function type */ + /** `Extended` Creates a function type */ public Function, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction, U> - /** Creates a function type */ + /** `Extended` Creates a function type */ public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction - /** Creates a function type */ + /** `Extended` Creates a function type */ public Function(parameters: any, returns: any, options: SchemaOptions = {}) { if (parameters[Kind] === 'Tuple') { const inner = parameters.items === undefined ? [] : parameters.items - return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters: inner, returns }) + return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: inner, returns }) } else if (globalThis.Array.isArray(parameters)) { - return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters, returns }) + return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters, returns }) } else { throw new Error('TypeBuilder.Function: Invalid parameters') } } - /** Creates a type from this constructors instance type */ + /** `Extended` Creates a type from this constructors instance type */ public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { return { ...options, ...this.Clone(schema.returns) } } - /** Creates a integer type */ + /** `Standard` Creates a integer type */ public Integer(options: NumericOptions = {}): TInteger { return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) } - /** Creates a intersect type. */ + /** `Standard` Creates a intersect type. */ public Intersect(objects: [...T], options: ObjectOptions = {}): TIntersect { const isOptional = (schema: TSchema) => (schema[Modifier] && schema[Modifier] === 'Optional') || schema[Modifier] === 'ReadonlyOptional' const [required, optional] = [new Set(), new Set()] @@ -695,18 +723,18 @@ export class TypeBuilder { } } - /** Creates a keyof type */ + /** `Standard` Creates a keyof type */ public KeyOf(object: T, options: SchemaOptions = {}): TKeyOf { const items = Object.keys(object.properties).map((key) => this.Create({ ...options, [Kind]: 'Literal', type: 'string', const: key })) return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'KeyOf', anyOf: items }) } - /** Creates a literal type. */ + /** `Standard` Creates a literal type. */ public Literal(value: T, options: SchemaOptions = {}): TLiteral { return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) } - /** Creates a never type */ + /** `Standard` Creates a never type */ public Never(options: SchemaOptions = {}): TNever { return this.Create({ ...options, @@ -718,17 +746,17 @@ export class TypeBuilder { }) } - /** Creates a null type */ + /** `Standard` Creates a null type */ public Null(options: SchemaOptions = {}): TNull { return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) } - /** Creates a number type */ + /** `Standard` Creates a number type */ public Number(options: NumericOptions = {}): TNumber { return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) } - /** Creates an object type with the given properties */ + /** `Standard` Creates an object type */ public Object(properties: T, options: ObjectOptions = {}): TObject { const property_names = Object.keys(properties) const optional = property_names.filter((name) => { @@ -744,13 +772,13 @@ export class TypeBuilder { } } - /** Creates a new object whose properties are omitted from the given object */ + /** `Standard` Creates a new object type whose keys are omitted from the given source type */ public Omit[]>>(schema: T, keys: K, options?: ObjectOptions): TOmit> - /** Creates a new object whose properties are omitted from the given object */ + /** `Standard` Creates a new object type whose keys are omitted from the given source type */ public Omit[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TOmit - /** Creates a new object whose properties are omitted from the given object */ + /** `Standard` Creates a new object type whose keys are omitted from the given source type */ public Omit(schema: any, keys: any, options: ObjectOptions = {}) { const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys const next = { ...this.Clone(schema), ...options, [Hint]: 'Omit' } @@ -764,12 +792,12 @@ export class TypeBuilder { return this.Create(next) } - /** Creates a tuple type from this functions parameters */ + /** `Extended` Creates a tuple type from this functions parameters */ public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { return Type.Tuple(schema.parameters, { ...options }) } - /** Creates an object type whose properties are all optional */ + /** `Standard` Creates an object type whose properties are all optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { const next = { ...this.Clone(schema), ...options, [Hint]: 'Partial' } delete next.required @@ -794,13 +822,13 @@ export class TypeBuilder { return this.Create(next) } - /** Creates a object whose properties are picked from the given object */ + /** `Standard` Creates a new object type whose keys are picked from the given source type */ public Pick[]>>(schema: T, keys: K, options?: ObjectOptions): TPick> - /** Creates a object whose properties are picked from the given object */ + /** `Standard` Creates a new object type whose keys are picked from the given source type */ public Pick[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TPick - /** Creates a object whose properties are picked from the given object */ + /** `Standard` Creates a new object type whose keys are picked from the given source type */ public Pick[]>(schema: any, keys: any, options: ObjectOptions = {}) { const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys const next = { ...this.Clone(schema), ...options, [Hint]: 'Pick' } @@ -814,18 +842,18 @@ export class TypeBuilder { return this.Create(next) } - /** Creates a promise type. This type cannot be represented in schema. */ + /** `Extended` Creates a Promise type */ public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'promise', item }) + return this.Create({ ...options, [Kind]: 'Promise', type: 'object', instanceOf: 'Promise', item }) } - /** Creates an object whose properties are derived from the given string literal union. */ + /** `Standard` Creates an object whose properties are derived from the given string literal union. */ public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> - /** Creates a record type */ + /** `Standard` Creates a record type */ public Record(key: K, schema: T, options?: ObjectOptions): TRecord - /** Creates a record type */ + /** `Standard` Creates a record type */ public Record(key: any, value: any, options: ObjectOptions = {}) { // If string literal union return TObject with properties extracted from union. if (key[Kind] === 'Union') { @@ -847,7 +875,7 @@ export class TypeBuilder { }) } - /** Creates a recursive object type */ + /** `Standard` Creates recursive type */ public Recursive(callback: (self: TSelf) => T, options: SchemaOptions = {}): TRecursive { if (options.$id === undefined) options.$id = `T${TypeOrdinal++}` const self = callback({ [Kind]: 'Self', $ref: `${options.$id}` } as any) @@ -855,18 +883,18 @@ export class TypeBuilder { return this.Create({ ...options, ...self } as any) } - /** Creates a reference schema */ + /** `Standard` Creates a reference type. The referenced type must contain a $id. */ public Ref(schema: T, options: SchemaOptions = {}): TRef { if (schema.$id === undefined) throw Error('TypeBuilder.Ref: Referenced schema must specify an $id') return this.Create({ ...options, [Kind]: 'Ref', $ref: schema.$id! }) } - /** Creates a string type from a regular expression */ + /** `Standard` Creates a string type from a regular expression */ public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern: regex.source }) } - /** Creates an object type whose properties are all required */ + /** `Standard` Creates an object type whose properties are all required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { const next = { ...this.Clone(schema), ...options, [Hint]: 'Required' } next.required = Object.keys(next.properties) @@ -891,7 +919,7 @@ export class TypeBuilder { return this.Create(next) } - /** Creates a type from this functions return type */ + /** `Extended` Creates a type from this functions return type */ public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { return { ...options, ...this.Clone(schema.returns) } } @@ -901,12 +929,12 @@ export class TypeBuilder { return JSON.parse(JSON.stringify(schema)) } - /** Creates a string type */ + /** `Standard` Creates a string type */ public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } - /** Creates a tuple type */ + /** `Standard` Creates a tuple type */ public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { const additionalItems = false const minItems = items.length @@ -915,37 +943,40 @@ export class TypeBuilder { return this.Create(schema) } - /** Creates a undefined type */ + /** `Extended` Creates a undefined type */ public Undefined(options: SchemaOptions = {}): TUndefined { - return this.Create({ ...options, [Kind]: 'Undefined', type: 'object', specialized: 'Undefined' }) + return this.Create({ ...options, [Kind]: 'Undefined', type: 'null', typeOf: 'Undefined' }) } - /** Creates a union type */ - + /** `Standard` Creates a union type */ public Union(items: [], options?: SchemaOptions): TNever + + /** `Standard` Creates a union type */ public Union(items: [...T], options?: SchemaOptions): TUnion + + /** `Standard` Creates a union type */ public Union(items: [...T], options: SchemaOptions = {}) { return items.length === 0 ? Type.Never({ ...options }) : this.Create({ ...options, [Kind]: 'Union', anyOf: items }) } - /** Creates a Uint8Array type */ + /** `Extended` Creates a Uint8Array type */ public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', specialized: 'Uint8Array' }) + return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', instanceOf: 'Uint8Array' }) } - /** Creates an unknown type */ + /** `Standard` Creates an unknown type */ public Unknown(options: SchemaOptions = {}): TUnknown { return this.Create({ ...options, [Kind]: 'Unknown' }) } - /** Creates a user defined schema that infers as type T */ + /** `Standard` Creates a user defined schema that infers as type T */ public Unsafe(options: UnsafeOptions = {}): TUnsafe { return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) } - /** Creates a void type */ + /** `Extended` Creates a void type */ public Void(options: SchemaOptions = {}): TVoid { - return this.Create({ ...options, [Kind]: 'Void', type: 'null' }) + return this.Create({ ...options, [Kind]: 'Void', type: 'null', typeOf: 'Void' }) } /** Use this function to return TSchema with static and params omitted */ diff --git a/src/value/cast.ts b/src/value/cast.ts index 848fed23b..fda08eb81 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -201,6 +201,10 @@ export namespace ValueCast { return result } + function Date(schema: Types.TDate, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + } + function Enum(schema: Types.TEnum, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } @@ -257,7 +261,7 @@ export namespace ValueCast { function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) + if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value) || value instanceof globalThis.Date) return ValueCreate.Create(schema, references) const subschemaKey = globalThis.Object.keys(schema.patternProperties)[0] const subschema = schema.patternProperties[subschemaKey] const result = {} as Record @@ -327,6 +331,8 @@ export namespace ValueCast { return Boolean(anySchema, anyReferences, value) case 'Constructor': return Constructor(anySchema, anyReferences, value) + case 'Date': + return Date(anySchema, anyReferences, value) case 'Enum': return Enum(anySchema, anyReferences, value) case 'Function': diff --git a/src/value/check.ts b/src/value/check.ts index 302f5609d..54e0418c3 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -62,12 +62,29 @@ export namespace ValueCheck { function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { return Visit(schema.returns, references, value.prototype) } - + function Date(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { + if (!(value instanceof globalThis.Date)) { + return false + } + if (schema.exclusiveMinimumTimestamp && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + return false + } + if (schema.exclusiveMaximumTimestamp && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + return false + } + if (schema.minimumTimestamp && !(value.getTime() >= schema.minimumTimestamp)) { + return false + } + if (schema.maximumTimestamp && !(value.getTime() <= schema.maximumTimestamp)) { + return false + } + return true + } function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { return typeof value === 'function' } - function Integer(schema: Types.TNumeric, references: Types.TSchema[], value: any): boolean { + function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { if (!(typeof value === 'number')) { return false } @@ -104,7 +121,7 @@ export namespace ValueCheck { return value === null } - function Number(schema: Types.TNumeric, references: Types.TSchema[], value: any): boolean { + function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { if (!(typeof value === 'number')) { return false } @@ -180,7 +197,7 @@ export namespace ValueCheck { } function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { return false } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] @@ -288,6 +305,8 @@ export namespace ValueCheck { return Boolean(anySchema, anyReferences, value) case 'Constructor': return Constructor(anySchema, anyReferences, value) + case 'Date': + return Date(anySchema, anyReferences, value) case 'Function': return Function(anySchema, anyReferences, value) case 'Integer': diff --git a/src/value/clone.ts b/src/value/clone.ts index 03b06c913..d2bb93cdb 100644 --- a/src/value/clone.ts +++ b/src/value/clone.ts @@ -29,15 +29,19 @@ THE SOFTWARE. import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' export namespace ValueClone { + function Array(value: ArrayType): any { + return value.map((element: any) => Clone(element)) + } + + function Date(value: Date): any { + return new globalThis.Date(value.toISOString()) + } + function Object(value: ObjectType): any { const keys = [...globalThis.Object.keys(value), ...globalThis.Object.getOwnPropertySymbols(value)] return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) } - function Array(value: ArrayType): any { - return value.map((element: any) => Clone(element)) - } - function TypedArray(value: TypedArrayType): any { return value.slice() } @@ -47,7 +51,9 @@ export namespace ValueClone { } export function Clone(value: T): T { - if (Is.Object(value)) { + if (Is.Date(value)) { + return Date(value) + } else if (Is.Object(value)) { return Object(value) } else if (Is.Array(value)) { return Array(value) diff --git a/src/value/create.ts b/src/value/create.ts index 5cd549096..3f9f7b52a 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -91,6 +91,16 @@ export namespace ValueCreate { } } + function Date(schema: Types.TDate, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else if (schema.minimumTimestamp !== undefined) { + return new globalThis.Date(schema.minimumTimestamp) + } else { + return new globalThis.Date(0) + } + } + function Enum(schema: Types.TEnum, references: Types.TSchema[]): any { if (schema.default !== undefined) { return schema.default @@ -291,6 +301,8 @@ export namespace ValueCreate { return Boolean(anySchema, anyReferences) case 'Constructor': return Constructor(anySchema, anyReferences) + case 'Date': + return Date(anySchema, anyReferences) case 'Enum': return Enum(anySchema, anyReferences) case 'Function': diff --git a/src/value/delta.ts b/src/value/delta.ts index 371a11f2c..73c30b069 100644 --- a/src/value/delta.ts +++ b/src/value/delta.ts @@ -96,6 +96,11 @@ export namespace ValueDelta { } } + function* Date(path: string, current: Date, next: unknown): IterableIterator> { + if (Is.Date(next) && current.getTime() === next.getTime()) return + yield Update(path, next) + } + function* Array(path: string, current: ArrayType, next: unknown): IterableIterator> { if (!Is.Array(next)) return yield Update(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { @@ -126,6 +131,8 @@ export namespace ValueDelta { function* Visit(path: string, current: unknown, next: unknown): IterableIterator> { if (Is.Object(current)) { return yield* Object(path, current, next) + } else if (Is.Date(current)) { + return yield* Date(path, current, next) } else if (Is.Array(current)) { return yield* Array(path, current, next) } else if (Is.TypedArray(current)) { diff --git a/src/value/equal.ts b/src/value/equal.ts index 773f2d3c1..2e85871ab 100644 --- a/src/value/equal.ts +++ b/src/value/equal.ts @@ -37,6 +37,10 @@ export namespace ValueEqual { return leftKeys.every((key) => Equal(left[key], right[key])) } + function Date(left: Date, right: unknown): any { + return Is.Date(right) && left.getTime() === right.getTime() + } + function Array(left: ArrayType, right: unknown): any { if (!Is.Array(right) || left.length !== right.length) return false return left.every((value, index) => Equal(value, right[index])) @@ -54,6 +58,8 @@ export namespace ValueEqual { export function Equal(left: T, right: unknown): right is T { if (Is.Object(left)) { return Object(left, right) + } else if (Is.Date(left)) { + return Date(left, right) } else if (Is.TypedArray(left)) { return TypedArray(left, right) } else if (Is.Array(left)) { diff --git a/src/value/is.ts b/src/value/is.ts index 729197d59..3d29fc898 100644 --- a/src/value/is.ts +++ b/src/value/is.ts @@ -33,7 +33,11 @@ export type ArrayType = unknown[] export namespace Is { export function Object(value: unknown): value is ObjectType { - return value !== null && typeof value === 'object' && !globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) + return value !== null && typeof value === 'object' && !globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) && !(value instanceof globalThis.Date) + } + + export function Date(value: unknown): value is Date { + return value instanceof globalThis.Date } export function Array(value: unknown): value is ArrayType { diff --git a/test/runtime/compiler/date.ts b/test/runtime/compiler/date.ts new file mode 100644 index 000000000..d6c099e6c --- /dev/null +++ b/test/runtime/compiler/date.ts @@ -0,0 +1,61 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Date', () => { + it('Should not validate number', () => { + const T = Type.Date() + fail(T, 1) + }) + it('Should not validate string', () => { + const T = Type.Date() + fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.Date() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.Date() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Date() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Date() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Date() + fail(T, undefined) + }) + it('Should validate Date', () => { + const T = Type.Date() + ok(T, new Date()) + }) + + it('Should validate Date minimumTimestamp', () => { + const T = Type.Date({ minimumTimestamp: 10 }) + fail(T, new Date(9)) + ok(T, new Date(10)) + }) + + it('Should validate Date maximumTimestamp', () => { + const T = Type.Date({ maximumTimestamp: 10 }) + ok(T, new Date(10)) + fail(T, new Date(11)) + }) + + it('Should validate Date exclusiveMinimumTimestamp', () => { + const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) + fail(T, new Date(10)) + ok(T, new Date(11)) + }) + + it('Should validate Date exclusiveMaximumTimestamp', () => { + const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) + ok(T, new Date(9)) + fail(T, new Date(10)) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index a77b5456e..01159a601 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,7 +1,9 @@ import './any' import './array' import './boolean' +import './date' import './enum' +import './integer' import './intersect' import './keyof' import './literal' diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts new file mode 100644 index 000000000..af3ac6bfa --- /dev/null +++ b/test/runtime/compiler/integer.ts @@ -0,0 +1,67 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Integer', () => { + it('Should not validate number', () => { + const T = Type.Integer() + fail(T, 3.14) + }) + + it('Should validate integer', () => { + const T = Type.Integer() + ok(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Integer() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Integer() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Integer() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Integer() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Integer() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Integer() + fail(T, undefined) + }) + + it('Should validate minimum', () => { + const T = Type.Integer({ minimum: 10 }) + fail(T, 9) + ok(T, 10) + }) + + it('Should validate maximum', () => { + const T = Type.Integer({ maximum: 10 }) + ok(T, 10) + fail(T, 11) + }) + + it('Should validate Date exclusiveMinimum', () => { + const T = Type.Integer({ exclusiveMinimum: 10 }) + fail(T, 10) + ok(T, 11) + }) + + it('Should validate Date exclusiveMaximum', () => { + const T = Type.Integer({ exclusiveMaximum: 10 }) + ok(T, 9) + fail(T, 10) + }) +}) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts index 0a9e05b08..7628bfd12 100644 --- a/test/runtime/compiler/number.ts +++ b/test/runtime/compiler/number.ts @@ -3,9 +3,15 @@ import { ok, fail } from './validate' describe('type/compiler/Number', () => { it('Should validate number', () => { + const T = Type.Number() + ok(T, 3.14) + }) + + it('Should validate integer', () => { const T = Type.Number() ok(T, 1) }) + it('Should not validate string', () => { const T = Type.Number() fail(T, 'hello') @@ -30,4 +36,28 @@ describe('type/compiler/Number', () => { const T = Type.Number() fail(T, undefined) }) + + it('Should validate minimum', () => { + const T = Type.Number({ minimum: 10 }) + fail(T, 9) + ok(T, 10) + }) + + it('Should validate maximum', () => { + const T = Type.Number({ maximum: 10 }) + ok(T, 10) + fail(T, 11) + }) + + it('Should validate Date exclusiveMinimum', () => { + const T = Type.Number({ exclusiveMinimum: 10 }) + fail(T, 10) + ok(T, 11) + }) + + it('Should validate Date exclusiveMaximum', () => { + const T = Type.Number({ exclusiveMaximum: 10 }) + ok(T, 9) + fail(T, 10) + }) }) diff --git a/test/runtime/conditional/any.ts b/test/runtime/conditional/any.ts index a8a11d46a..b450886d9 100644 --- a/test/runtime/conditional/any.ts +++ b/test/runtime/conditional/any.ts @@ -89,4 +89,10 @@ describe('conditional/structural/Any', () => { const R = Structural.Check(Type.Any(), Type.Void()) Assert.deepEqual(R, StructuralResult.Union) }) + + it('Should extend Date', () => { + type T = any extends Date ? 1 : 2 + const R = Structural.Check(Type.Any(), Type.Date()) + Assert.deepEqual(R, StructuralResult.Union) + }) }) diff --git a/test/runtime/conditional/array.ts b/test/runtime/conditional/array.ts index 2f28914d0..f9ca50ab3 100644 --- a/test/runtime/conditional/array.ts +++ b/test/runtime/conditional/array.ts @@ -307,4 +307,10 @@ describe('conditional/structural/Array', () => { const R = Structural.Check(Type.Array(Type.String()), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = Array extends Date ? 1 : 2 + const R = Structural.Check(Type.Array(Type.String()), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/boolean.ts b/test/runtime/conditional/boolean.ts index df089789c..4699a972b 100644 --- a/test/runtime/conditional/boolean.ts +++ b/test/runtime/conditional/boolean.ts @@ -104,4 +104,10 @@ describe('conditional/structural/Boolean', () => { const R = Structural.Check(Type.Boolean(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = boolean extends Date ? 1 : 2 + const R = Structural.Check(Type.Boolean(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/constructor.ts b/test/runtime/conditional/constructor.ts index de607b660..38cd73be6 100644 --- a/test/runtime/conditional/constructor.ts +++ b/test/runtime/conditional/constructor.ts @@ -8,6 +8,7 @@ describe('conditional/structural/Constructor', () => { const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) @@ -240,4 +241,10 @@ describe('conditional/structural/Constructor', () => { const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = (new () => number) extends Date ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/date.ts b/test/runtime/conditional/date.ts new file mode 100644 index 000000000..29920d23a --- /dev/null +++ b/test/runtime/conditional/date.ts @@ -0,0 +1,118 @@ +import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('conditional/structural/Date', () => { + it('Should extend Any', () => { + type T = Date extends any ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Any()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Unknown', () => { + type T = Date extends unknown ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Unknown()) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend String', () => { + type T = Date extends string ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.String()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Boolean', () => { + type T = Date extends boolean ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Boolean()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Number', () => { + type T = Date extends number ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Number()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Integer', () => { + type T = Date extends number ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Integer()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Array', () => { + type T = Date extends Array ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Array(Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + it('Should extend Tuple', () => { + type T = Date extends [number, number] ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Record', () => { + type T = Date extends Record ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 1', () => { + type T = Date extends {} ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Object 2', () => { + type T = Date extends { a: 10 } ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Object 3', () => { + type T = Date extends object ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Object({})) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 1', () => { + type T = Date extends number | string ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Union 2', () => { + type T = Date extends any | number ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.True) + }) + + it('Should extend Union 3', () => { + type T = Date extends boolean | number ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Null', () => { + type T = Date extends null ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Null()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Undefined', () => { + type T = Date extends undefined ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Undefined()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Void', () => { + type T = Date extends void ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Void()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Date', () => { + type T = Date extends Date ? 1 : 2 + const R = Structural.Check(Type.Date(), Type.Date()) + Assert.deepEqual(R, StructuralResult.True) + }) +}) diff --git a/test/runtime/conditional/function.ts b/test/runtime/conditional/function.ts index 2729aa40b..64eb9f080 100644 --- a/test/runtime/conditional/function.ts +++ b/test/runtime/conditional/function.ts @@ -242,4 +242,10 @@ describe('conditional/structural/Function', () => { const R = Structural.Check(Type.Function([], Type.Number()), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = (() => number) extends Date ? 1 : 2 + const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/index.ts b/test/runtime/conditional/index.ts index 0686b6d9d..1f7a88c56 100644 --- a/test/runtime/conditional/index.ts +++ b/test/runtime/conditional/index.ts @@ -2,6 +2,7 @@ import './any' import './array' import './boolean' import './constructor' +import './date' import './function' import './integer' import './literal' diff --git a/test/runtime/conditional/integer.ts b/test/runtime/conditional/integer.ts index 3c384c776..78b1bca8e 100644 --- a/test/runtime/conditional/integer.ts +++ b/test/runtime/conditional/integer.ts @@ -104,9 +104,16 @@ describe('conditional/structural/Integer', () => { const R = Structural.Check(Type.Integer(), Type.Undefined()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 const R = Structural.Check(Type.Integer(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/literal.ts b/test/runtime/conditional/literal.ts index 994597c2f..a699ebf42 100644 --- a/test/runtime/conditional/literal.ts +++ b/test/runtime/conditional/literal.ts @@ -321,4 +321,10 @@ describe('conditional/structural/Literal', () => { const R = Structural.Check(Type.Literal(true), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = true extends Date ? 1 : 2 + const R = Structural.Check(Type.Literal(true), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/null.ts b/test/runtime/conditional/null.ts index d8b91e504..1b1fc9661 100644 --- a/test/runtime/conditional/null.ts +++ b/test/runtime/conditional/null.ts @@ -110,4 +110,10 @@ describe('conditional/structural/Null', () => { const R = Structural.Check(Type.Null(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = null extends Date ? 1 : 2 + const R = Structural.Check(Type.Null(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/number.ts b/test/runtime/conditional/number.ts index 35e6f7c68..7cb136f35 100644 --- a/test/runtime/conditional/number.ts +++ b/test/runtime/conditional/number.ts @@ -8,11 +8,13 @@ describe('conditional/structural/Number', () => { const R = Structural.Check(Type.Number(), Type.Any()) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = Structural.Check(Type.Number(), Type.Unknown()) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = Structural.Check(Type.Number(), Type.String()) @@ -108,4 +110,10 @@ describe('conditional/structural/Number', () => { const R = Structural.Check(Type.Number(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/object.ts b/test/runtime/conditional/object.ts index 3452661ef..fb327cc40 100644 --- a/test/runtime/conditional/object.ts +++ b/test/runtime/conditional/object.ts @@ -152,4 +152,10 @@ describe('conditional/structural/Object', () => { const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = { a: number } extends Date ? 1 : 2 + const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/promise.ts b/test/runtime/conditional/promise.ts index 77de0358d..68ad4b99d 100644 --- a/test/runtime/conditional/promise.ts +++ b/test/runtime/conditional/promise.ts @@ -242,4 +242,10 @@ describe('conditional/structural/Promise', () => { const R = Structural.Check(Type.Promise(Type.Number()), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = Promise extends Date ? 1 : 2 + const R = Structural.Check(Type.Promise(Type.Number()), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/record.ts b/test/runtime/conditional/record.ts index 48b41ec24..396878420 100644 --- a/test/runtime/conditional/record.ts +++ b/test/runtime/conditional/record.ts @@ -58,6 +58,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) @@ -65,6 +66,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -72,6 +74,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -79,6 +82,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -96,6 +100,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) @@ -103,6 +108,7 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(A, B) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) @@ -140,66 +146,79 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.String()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Number', () => { type T = Record extends number ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Number()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Integer()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Object 1', () => { type T = Record extends object ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Null', () => { type T = Record extends null ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Null()) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) @@ -211,4 +230,10 @@ describe('conditional/structural/Record', () => { const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = Record extends Date ? 1 : 2 + const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/string.ts b/test/runtime/conditional/string.ts index 42c02aa1e..783a25e91 100644 --- a/test/runtime/conditional/string.ts +++ b/test/runtime/conditional/string.ts @@ -140,4 +140,10 @@ describe('conditional/structural/String', () => { const R = Structural.Check(Type.Number(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = Structural.Check(Type.Number(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/tuple.ts b/test/runtime/conditional/tuple.ts index 6c2d025e2..dcd96163d 100644 --- a/test/runtime/conditional/tuple.ts +++ b/test/runtime/conditional/tuple.ts @@ -50,6 +50,7 @@ describe('conditional/structural/Tuple', () => { const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Array 4', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) @@ -193,4 +194,10 @@ describe('conditional/structural/Tuple', () => { const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = [string, number] extends Date ? 1 : 2 + const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/uint8array.ts b/test/runtime/conditional/uint8array.ts index ddfef9031..984082d01 100644 --- a/test/runtime/conditional/uint8array.ts +++ b/test/runtime/conditional/uint8array.ts @@ -44,6 +44,7 @@ describe('conditional/structural/Uint8Array', () => { const R = Structural.Check(Type.Uint8Array(), Type.Array(Type.Any())) Assert.deepEqual(R, StructuralResult.False) }) + it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 const R = Structural.Check(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) @@ -109,4 +110,10 @@ describe('conditional/structural/Uint8Array', () => { const R = Structural.Check(Type.Uint8Array(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = Uint8Array extends Date ? 1 : 2 + const R = Structural.Check(Type.Uint8Array(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/undefined.ts b/test/runtime/conditional/undefined.ts index 3bacbf798..2a03f11ba 100644 --- a/test/runtime/conditional/undefined.ts +++ b/test/runtime/conditional/undefined.ts @@ -92,9 +92,16 @@ describe('conditional/structural/Undefined', () => { const R = Structural.Check(Type.Undefined(), Type.Undefined()) Assert.deepEqual(R, StructuralResult.True) }) + it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 const R = Structural.Check(Type.Undefined(), Type.Void()) Assert.deepEqual(R, StructuralResult.True) }) + + it('Should extend Date', () => { + type T = undefined extends Date ? 1 : 2 + const R = Structural.Check(Type.Undefined(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/union.ts b/test/runtime/conditional/union.ts index 26e06a812..bcd0da241 100644 --- a/test/runtime/conditional/union.ts +++ b/test/runtime/conditional/union.ts @@ -139,4 +139,16 @@ describe('conditional/structural/Union', () => { const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = number | string | void extends Date ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) + + it('Should extend Date 2', () => { + type T = Date | number | string | void extends Date ? 1 : 2 + const R = Structural.Check(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/unknown.ts b/test/runtime/conditional/unknown.ts index 8025a6b77..51b0e2908 100644 --- a/test/runtime/conditional/unknown.ts +++ b/test/runtime/conditional/unknown.ts @@ -116,4 +116,10 @@ describe('conditional/structural/Unknown', () => { const R = Structural.Check(Type.Unknown(), Type.Void()) Assert.deepEqual(R, StructuralResult.False) }) + + it('Should extend Date', () => { + type T = unknown extends Date ? 1 : 2 + const R = Structural.Check(Type.Unknown(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/conditional/void.ts b/test/runtime/conditional/void.ts index 289f3dfdc..6e1ece4ca 100644 --- a/test/runtime/conditional/void.ts +++ b/test/runtime/conditional/void.ts @@ -116,4 +116,10 @@ describe('conditional/structural/Void', () => { const R = Structural.Check(Type.Void(), Type.Void()) Assert.deepEqual(R, StructuralResult.True) }) + + it('Should extend Date', () => { + type T = void extends Date ? 1 : 2 + const R = Structural.Check(Type.Void(), Type.Date()) + Assert.deepEqual(R, StructuralResult.False) + }) }) diff --git a/test/runtime/guard/any.ts b/test/runtime/guard/any.ts index e2b5a7b74..039fa56ef 100644 --- a/test/runtime/guard/any.ts +++ b/test/runtime/guard/any.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TAny', () => { - it('should guard for TAny', () => { + it('Should guard for TAny', () => { const R = TypeGuard.TAny(Type.Any()) Assert.equal(R, true) }) - it('should not guard for TAny', () => { + it('Should not guard for TAny', () => { const R = TypeGuard.TAny(null) Assert.equal(R, false) }) - it('should not guard for TAny with invalid $id', () => { + it('Should not guard for TAny with invalid $id', () => { // @ts-ignore const R = TypeGuard.TAny(Type.Any({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/array.ts b/test/runtime/guard/array.ts index 088f39653..402614610 100644 --- a/test/runtime/guard/array.ts +++ b/test/runtime/guard/array.ts @@ -3,17 +3,17 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TArray', () => { - it('should guard for TArray', () => { + it('Should guard for TArray', () => { const R = TypeGuard.TArray(Type.Array(Type.Number())) Assert.equal(R, true) }) - it('should not guard for TArray', () => { + it('Should not guard for TArray', () => { const R = TypeGuard.TArray(null) Assert.equal(R, false) }) - it('should guard for nested object TArray', () => { + it('Should guard for nested object TArray', () => { const R = TypeGuard.TArray( Type.Array( Type.Object({ @@ -25,7 +25,7 @@ describe('type/guard/TArray', () => { Assert.equal(R, true) }) - it('should not guard for nested object TArray', () => { + it('Should not guard for nested object TArray', () => { const R = TypeGuard.TArray( Type.Array( Type.Object({ @@ -37,25 +37,25 @@ describe('type/guard/TArray', () => { Assert.equal(R, false) }) - it('should not guard for TArray with invalid $id', () => { + it('Should not guard for TArray with invalid $id', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TArray with invalid minItems', () => { + it('Should not guard for TArray with invalid minItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { minItems: '1' })) Assert.equal(R, false) }) - it('should not guard for TArray with invalid maxItems', () => { + it('Should not guard for TArray with invalid maxItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { maxItems: '1' })) Assert.equal(R, false) }) - it('should not guard for TArray with invalid uniqueItems', () => { + it('Should not guard for TArray with invalid uniqueItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { uniqueItems: '1' })) Assert.equal(R, false) diff --git a/test/runtime/guard/boolean.ts b/test/runtime/guard/boolean.ts index 52c94c877..71680414f 100644 --- a/test/runtime/guard/boolean.ts +++ b/test/runtime/guard/boolean.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TBoolean', () => { - it('should guard for TBoolean', () => { + it('Should guard for TBoolean', () => { const R = TypeGuard.TBoolean(Type.Boolean()) Assert.equal(R, true) }) - it('should not guard for TBoolean', () => { + it('Should not guard for TBoolean', () => { const R = TypeGuard.TBoolean(null) Assert.equal(R, false) }) - it('should not guard for TBoolean with invalid $id', () => { + it('Should not guard for TBoolean with invalid $id', () => { // @ts-ignore const R = TypeGuard.TBoolean(Type.Boolean({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/constructor.ts b/test/runtime/guard/constructor.ts index e584c0609..9bb2d4b37 100644 --- a/test/runtime/guard/constructor.ts +++ b/test/runtime/guard/constructor.ts @@ -3,32 +3,32 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TConstructor', () => { - it('should guard for TConstructor', () => { + it('Should guard for TConstructor', () => { const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number())) Assert.equal(R, true) }) - it('should not guard for TConstructor', () => { + it('Should not guard for TConstructor', () => { const R = TypeGuard.TConstructor(null) Assert.equal(R, false) }) - it('should not guard for TConstructor with invalid $id', () => { + it('Should not guard for TConstructor with invalid $id', () => { // @ts-ignore const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TConstructor with invalid Params', () => { + it('Should not guard for TConstructor with invalid Params', () => { const R = TypeGuard.TConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) Assert.equal(R, false) }) - it('should not guard for TConstructor with invalid Return', () => { + it('Should not guard for TConstructor with invalid Return', () => { const R = TypeGuard.TConstructor(Type.Constructor([], {} as any)) Assert.equal(R, false) }) - it('should guard for TConstructor with empty TTuple', () => { + it('Should guard for TConstructor with empty TTuple', () => { const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([]), Type.Number())) Assert.equal(R, true) }) - it('should guard for TConstructor with array TTuple', () => { + it('Should guard for TConstructor with array TTuple', () => { const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) Assert.equal(R, true) }) diff --git a/test/runtime/guard/date.ts b/test/runtime/guard/date.ts new file mode 100644 index 000000000..14132a7bd --- /dev/null +++ b/test/runtime/guard/date.ts @@ -0,0 +1,45 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('type/guard/TDate', () => { + it('Should guard for TDate', () => { + const R = TypeGuard.TDate(Type.Date()) + Assert.equal(R, true) + }) + + it('Should not guard for TDate', () => { + const R = TypeGuard.TDate(null) + Assert.equal(R, false) + }) + + it('Should not guard for TDate with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Number({ $id: 1 })) + Assert.equal(R, false) + }) + + it('Should not guard for TDate with invalid minimum', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Number({ minimum: '1' })) + Assert.equal(R, false) + }) + + it('Should not guard for TDate with invalid maximum', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Number({ maximum: '1' })) + Assert.equal(R, false) + }) + + it('Should not guard for TDate with invalid exclusiveMinimum', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Number({ exclusiveMinimum: '1' })) + Assert.equal(R, false) + }) + + it('Should not guard for TDate with invalid exclusiveMaximum', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Number({ exclusiveMaximum: '1' })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/function.ts b/test/runtime/guard/function.ts index 3cff158a8..102ac4f14 100644 --- a/test/runtime/guard/function.ts +++ b/test/runtime/guard/function.ts @@ -3,32 +3,32 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TFunction', () => { - it('should guard for TFunction', () => { + it('Should guard for TFunction', () => { const R = TypeGuard.TFunction(Type.Function([], Type.Number())) Assert.equal(R, true) }) - it('should not guard for TFunction', () => { + it('Should not guard for TFunction', () => { const R = TypeGuard.TFunction(null) Assert.equal(R, false) }) - it('should not guard for TFunction with invalid $id', () => { + it('Should not guard for TFunction with invalid $id', () => { // @ts-ignore const R = TypeGuard.TFunction(Type.Function([], Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TFunction with invalid Params', () => { + it('Should not guard for TFunction with invalid Params', () => { const R = TypeGuard.TFunction(Type.Function([{} as any, {} as any], Type.Number())) Assert.equal(R, false) }) - it('should not guard for TFunction with invalid Return', () => { + it('Should not guard for TFunction with invalid Return', () => { const R = TypeGuard.TFunction(Type.Function([], {} as any)) Assert.equal(R, false) }) - it('should guard for TFunction with empty TTuple', () => { + it('Should guard for TFunction with empty TTuple', () => { const R = TypeGuard.TFunction(Type.Function(Type.Tuple([]), Type.Number())) Assert.equal(R, true) }) - it('should guard for TFunction with array TTuple', () => { + it('Should guard for TFunction with array TTuple', () => { const R = TypeGuard.TFunction(Type.Function(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) Assert.equal(R, true) }) diff --git a/test/runtime/guard/index.ts b/test/runtime/guard/index.ts index 804c4865c..037d13781 100644 --- a/test/runtime/guard/index.ts +++ b/test/runtime/guard/index.ts @@ -2,6 +2,7 @@ import './any' import './array' import './boolean' import './constructor' +import './date' import './function' import './integer' import './literal' diff --git a/test/runtime/guard/integer.ts b/test/runtime/guard/integer.ts index d53536b6e..72558e07e 100644 --- a/test/runtime/guard/integer.ts +++ b/test/runtime/guard/integer.ts @@ -3,40 +3,40 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TInteger', () => { - it('should guard for TInteger', () => { + it('Should guard for TInteger', () => { const R = TypeGuard.TInteger(Type.Integer()) Assert.equal(R, true) }) - it('should not guard for TInteger', () => { + it('Should not guard for TInteger', () => { const R = TypeGuard.TInteger(null) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid $id', () => { + it('Should not guard for TInteger with invalid $id', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid multipleOf', () => { + it('Should not guard for TInteger with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ multipleOf: '1' })) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid minimum', () => { + it('Should not guard for TInteger with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ minimum: '1' })) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid maximum', () => { + it('Should not guard for TInteger with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ maximum: '1' })) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid exclusiveMinimum', () => { + it('Should not guard for TInteger with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMinimum: '1' })) Assert.equal(R, false) }) - it('should not guard for TInteger with invalid exclusiveMaximum', () => { + it('Should not guard for TInteger with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMaximum: '1' })) Assert.equal(R, false) diff --git a/test/runtime/guard/literal.ts b/test/runtime/guard/literal.ts index efe372437..df4d39e24 100644 --- a/test/runtime/guard/literal.ts +++ b/test/runtime/guard/literal.ts @@ -3,28 +3,28 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TLiteral', () => { - it('should guard for TLiteral of String', () => { + it('Should guard for TLiteral of String', () => { const R = TypeGuard.TLiteral(Type.Literal('hello')) Assert.equal(R, true) }) - it('should guard for TLiteral of Number', () => { + it('Should guard for TLiteral of Number', () => { const R = TypeGuard.TLiteral(Type.Literal(42)) Assert.equal(R, true) }) - it('should guard for TLiteral of Boolean', () => { + it('Should guard for TLiteral of Boolean', () => { const R = TypeGuard.TLiteral(Type.Literal(true)) Assert.equal(R, true) }) - it('should not guard for TLiteral of Null', () => { + it('Should not guard for TLiteral of Null', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(null)) Assert.equal(R, false) }) - it('should not guard for TLiteral', () => { + it('Should not guard for TLiteral', () => { const R = TypeGuard.TLiteral(null) Assert.equal(R, false) }) - it('should not guard for TLiteral with invalid $id', () => { + it('Should not guard for TLiteral with invalid $id', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(42, { $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/null.ts b/test/runtime/guard/null.ts index 36f8df40b..d853e112f 100644 --- a/test/runtime/guard/null.ts +++ b/test/runtime/guard/null.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TNull', () => { - it('should guard for TNull', () => { + it('Should guard for TNull', () => { const R = TypeGuard.TNull(Type.Null()) Assert.equal(R, true) }) - it('should not guard for TNull', () => { + it('Should not guard for TNull', () => { const R = TypeGuard.TNull(null) Assert.equal(R, false) }) - it('should not guard for TNull with invalid $id', () => { + it('Should not guard for TNull with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNull(Type.Null({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/number.ts b/test/runtime/guard/number.ts index 9ea80b538..eb6141606 100644 --- a/test/runtime/guard/number.ts +++ b/test/runtime/guard/number.ts @@ -3,40 +3,40 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TNumber', () => { - it('should guard for TNumber', () => { + it('Should guard for TNumber', () => { const R = TypeGuard.TNumber(Type.Number()) Assert.equal(R, true) }) - it('should not guard for TNumber', () => { + it('Should not guard for TNumber', () => { const R = TypeGuard.TNumber(null) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid $id', () => { + it('Should not guard for TNumber with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid multipleOf', () => { + it('Should not guard for TNumber with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ multipleOf: '1' })) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid minimum', () => { + it('Should not guard for TNumber with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ minimum: '1' })) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid maximum', () => { + it('Should not guard for TNumber with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ maximum: '1' })) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid exclusiveMinimum', () => { + it('Should not guard for TNumber with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMinimum: '1' })) Assert.equal(R, false) }) - it('should not guard for TNumber with invalid exclusiveMaximum', () => { + it('Should not guard for TNumber with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMaximum: '1' })) Assert.equal(R, false) diff --git a/test/runtime/guard/object.ts b/test/runtime/guard/object.ts index c34d32639..867774afc 100644 --- a/test/runtime/guard/object.ts +++ b/test/runtime/guard/object.ts @@ -3,7 +3,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TObject', () => { - it('should guard for TObject', () => { + it('Should guard for TObject', () => { const R = TypeGuard.TObject( Type.Object({ x: Type.Number(), @@ -13,12 +13,12 @@ describe('type/guard/TObject', () => { Assert.equal(R, true) }) - it('should not guard for TObject', () => { + it('Should not guard for TObject', () => { const R = TypeGuard.TObject(null) Assert.equal(R, false) }) - it('should not guard for TObject with escape characters in property key', () => { + it('Should not guard for TObject with escape characters in property key', () => { const R = TypeGuard.TObject( Type.Object({ 'hello\nworld': Type.Number(), @@ -27,7 +27,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with invalid property values', () => { + it('Should not guard for TObject with invalid property values', () => { const R = TypeGuard.TObject( Type.Object({ x: Type.Number(), @@ -37,7 +37,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with invalid additionalProperties', () => { + it('Should not guard for TObject with invalid additionalProperties', () => { const R = TypeGuard.TObject( Type.Object( { @@ -52,7 +52,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with invalid $id', () => { + it('Should not guard for TObject with invalid $id', () => { const R = TypeGuard.TObject( Type.Object( { @@ -67,7 +67,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with invalid minProperties', () => { + it('Should not guard for TObject with invalid minProperties', () => { const R = TypeGuard.TObject( Type.Object( { @@ -82,7 +82,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with invalid maxProperties', () => { + it('Should not guard for TObject with invalid maxProperties', () => { const R = TypeGuard.TObject( Type.Object( { @@ -97,7 +97,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should guard for TObject with invalid additional properties', () => { + it('Should guard for TObject with invalid additional properties', () => { const R = TypeGuard.TObject( Type.Object( { @@ -113,7 +113,7 @@ describe('type/guard/TObject', () => { Assert.equal(R, false) }) - it('should not guard for TObject with valid additional properties schema', () => { + it('Should not guard for TObject with valid additional properties schema', () => { const R = TypeGuard.TObject( Type.Object( { diff --git a/test/runtime/guard/promise.ts b/test/runtime/guard/promise.ts index 0610bc319..fdfbfb184 100644 --- a/test/runtime/guard/promise.ts +++ b/test/runtime/guard/promise.ts @@ -3,23 +3,23 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TPromise', () => { - it('should guard for TPromise', () => { + it('Should guard for TPromise', () => { const R = TypeGuard.TPromise(Type.Promise(Type.Number())) Assert.equal(R, true) }) - it('should not guard for TPromise', () => { + it('Should not guard for TPromise', () => { const R = TypeGuard.TPromise(null) Assert.equal(R, false) }) - it('should not guard for TPromise with invalid $id', () => { + it('Should not guard for TPromise with invalid $id', () => { // @ts-ignore const R = TypeGuard.TPromise(Type.Promise(Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('should guard for TPromise with nested TObject', () => { + it('Should guard for TPromise with nested TObject', () => { const R = TypeGuard.TPromise( Type.Promise( Type.Object({ @@ -31,7 +31,7 @@ describe('type/guard/TPromise', () => { Assert.equal(R, true) }) - it('should not guard for TPromise with nested TObject', () => { + it('Should not guard for TPromise with nested TObject', () => { const R = TypeGuard.TPromise( Type.Promise( Type.Object({ diff --git a/test/runtime/guard/record.ts b/test/runtime/guard/record.ts index 718f56e65..92ab62c00 100644 --- a/test/runtime/guard/record.ts +++ b/test/runtime/guard/record.ts @@ -3,12 +3,12 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TRecord', () => { - it('should guard for TRecord', () => { + it('Should guard for TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) Assert.equal(R, true) }) - it('should guard for TRecord with TObject value', () => { + it('Should guard for TRecord with TObject value', () => { const R = TypeGuard.TRecord( Type.Record( Type.String(), @@ -21,18 +21,18 @@ describe('type/guard/TRecord', () => { Assert.equal(R, true) }) - it('should not guard for TRecord', () => { + it('Should not guard for TRecord', () => { const R = TypeGuard.TRecord(null) Assert.equal(R, false) }) - it('should not guard for TRecord with invalid $id', () => { + it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TRecord with TObject value with invalid Property', () => { + it('Should not guard for TRecord with TObject value with invalid Property', () => { const R = TypeGuard.TRecord( Type.Record( Type.String(), @@ -45,7 +45,7 @@ describe('type/guard/TRecord', () => { Assert.equal(R, false) }) - it('should not guard for TRecord with invalid literal key', () => { + it('Should not guard for TRecord with invalid literal key', () => { const K = Type.Union([Type.Literal('hello\nworld')]) const R = TypeGuard.TRecord(Type.Record(K, Type.Number())) Assert.equal(R, false) diff --git a/test/runtime/guard/ref.ts b/test/runtime/guard/ref.ts index ecc260cb6..6a4a8e883 100644 --- a/test/runtime/guard/ref.ts +++ b/test/runtime/guard/ref.ts @@ -3,18 +3,18 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TRef', () => { - it('should guard for TRef', () => { + it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = TypeGuard.TRef(Type.Ref(T)) Assert.equal(R, true) }) - it('should not guard for TRef', () => { + it('Should not guard for TRef', () => { const R = TypeGuard.TRef(null) Assert.equal(R, false) }) - it('should not guard for TRef with invalid $ref', () => { + it('Should not guard for TRef with invalid $ref', () => { const T = Type.Number({ $id: 'T' }) const S = Type.Ref(T) // @ts-ignore diff --git a/test/runtime/guard/self.ts b/test/runtime/guard/self.ts index 8777789de..824d77ee8 100644 --- a/test/runtime/guard/self.ts +++ b/test/runtime/guard/self.ts @@ -3,14 +3,14 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TSelf', () => { - it('should guard for TSelf', () => { + it('Should guard for TSelf', () => { Type.Recursive((Node) => { const R = TypeGuard.TSelf(Node) Assert.equal(R, true) return Type.Object({ nodes: Type.Array(Node) }) }) }) - it('should guard for TSelf with invalid $ref', () => { + it('Should guard for TSelf with invalid $ref', () => { Type.Recursive((Node) => { // @ts-ignore Node.$ref = 1 diff --git a/test/runtime/guard/string.ts b/test/runtime/guard/string.ts index 598b9bf15..a3cc7c224 100644 --- a/test/runtime/guard/string.ts +++ b/test/runtime/guard/string.ts @@ -3,35 +3,35 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TString', () => { - it('should guard for TString', () => { + it('Should guard for TString', () => { const R = TypeGuard.TString(Type.String()) Assert.equal(R, true) }) - it('should not guard for TString', () => { + it('Should not guard for TString', () => { const R = TypeGuard.TString(null) Assert.equal(R, false) }) - it('should not guard for TString with invalid $id', () => { + it('Should not guard for TString with invalid $id', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TString with invalid minLength', () => { + it('Should not guard for TString with invalid minLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ minLength: '1' })) Assert.equal(R, false) }) - it('should not guard for TString with invalid maxLength', () => { + it('Should not guard for TString with invalid maxLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ maxLength: '1' })) Assert.equal(R, false) }) - it('should not guard for TString with invalid pattern', () => { + it('Should not guard for TString with invalid pattern', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ pattern: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/tuple.ts b/test/runtime/guard/tuple.ts index 765714caf..935426e24 100644 --- a/test/runtime/guard/tuple.ts +++ b/test/runtime/guard/tuple.ts @@ -3,20 +3,20 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TTuple', () => { - it('should guard for TTuple', () => { + it('Should guard for TTuple', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()])) Assert.equal(R, true) }) - it('should not guard for TTuple', () => { + it('Should not guard for TTuple', () => { const R = TypeGuard.TTuple(null) Assert.equal(R, false) }) - it('should not guard for TTuple with invalid $id', () => { + it('Should not guard for TTuple with invalid $id', () => { // @ts-ignore const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TTuple with invalid Items', () => { + it('Should not guard for TTuple with invalid Items', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), {} as any])) Assert.equal(R, false) }) diff --git a/test/runtime/guard/uint8array.ts b/test/runtime/guard/uint8array.ts index 963d7ad4d..0143c3f47 100644 --- a/test/runtime/guard/uint8array.ts +++ b/test/runtime/guard/uint8array.ts @@ -3,29 +3,29 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TUint8Array', () => { - it('should guard for TUint8Array', () => { + it('Should guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(Type.Uint8Array()) Assert.equal(R, true) }) - it('should not guard for TUint8Array', () => { + it('Should not guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(null) Assert.equal(R, false) }) - it('should not guard for TUint8Array with invalid $id', () => { + it('Should not guard for TUint8Array with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ $id: 1 })) Assert.equal(R, false) }) - it('should not guard for TUint8Array with invalid minByteLength', () => { + it('Should not guard for TUint8Array with invalid minByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ minByteLength: '1' })) Assert.equal(R, false) }) - it('should not guard for TUint8Array with invalid maxByteLength', () => { + it('Should not guard for TUint8Array with invalid maxByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ maxByteLength: '1' })) Assert.equal(R, false) diff --git a/test/runtime/guard/undefined.ts b/test/runtime/guard/undefined.ts index 90c854759..0b7f471c8 100644 --- a/test/runtime/guard/undefined.ts +++ b/test/runtime/guard/undefined.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TUndefined', () => { - it('should guard for TUndefined', () => { + it('Should guard for TUndefined', () => { const R = TypeGuard.TUndefined(Type.Undefined()) Assert.equal(R, true) }) - it('should not guard for TUndefined', () => { + it('Should not guard for TUndefined', () => { const R = TypeGuard.TUndefined(null) Assert.equal(R, false) }) - it('should not guard for TUndefined with invalid $id', () => { + it('Should not guard for TUndefined with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUndefined(Type.Undefined({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/union.ts b/test/runtime/guard/union.ts index 614a16654..b26465a2e 100644 --- a/test/runtime/guard/union.ts +++ b/test/runtime/guard/union.ts @@ -3,7 +3,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TUnion', () => { - it('should guard for TUnion', () => { + it('Should guard for TUnion', () => { const R = TypeGuard.TUnion( Type.Union([ Type.Object({ @@ -16,11 +16,11 @@ describe('type/guard/TUnion', () => { ) Assert.equal(R, true) }) - it('should not guard for TUnion', () => { + it('Should not guard for TUnion', () => { const R = TypeGuard.TUnion(null) Assert.equal(R, false) }) - it('should guard for TUnion with invalid $id', () => { + it('Should guard for TUnion with invalid $id', () => { const R = TypeGuard.TUnion( Type.Union( [ @@ -39,7 +39,7 @@ describe('type/guard/TUnion', () => { ) Assert.equal(R, false) }) - it('should not guard for TUnion with invalid variant', () => { + it('Should not guard for TUnion with invalid variant', () => { const R = TypeGuard.TUnion( Type.Union([ Type.Object({ @@ -50,7 +50,7 @@ describe('type/guard/TUnion', () => { ) Assert.equal(R, false) }) - it('should not guard for TUnion with invalid object variant', () => { + it('Should not guard for TUnion with invalid object variant', () => { const R = TypeGuard.TUnion( Type.Union([ Type.Object({ diff --git a/test/runtime/guard/unknown.ts b/test/runtime/guard/unknown.ts index 443698bd3..d23f2c267 100644 --- a/test/runtime/guard/unknown.ts +++ b/test/runtime/guard/unknown.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TUnknown', () => { - it('should guard for TUnknown', () => { + it('Should guard for TUnknown', () => { const R = TypeGuard.TUnknown(Type.Unknown()) Assert.equal(R, true) }) - it('should not guard for TUnknown', () => { + it('Should not guard for TUnknown', () => { const R = TypeGuard.TUnknown(null) Assert.equal(R, false) }) - it('should not guard for TUnknown with invalid $id', () => { + it('Should not guard for TUnknown with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUnknown(Type.Unknown({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/guard/void.ts b/test/runtime/guard/void.ts index b0565e69a..d513f5f2a 100644 --- a/test/runtime/guard/void.ts +++ b/test/runtime/guard/void.ts @@ -3,15 +3,15 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' describe('type/guard/TVoid', () => { - it('should guard for TVoid', () => { + it('Should guard for TVoid', () => { const R = TypeGuard.TVoid(Type.Void()) Assert.equal(R, true) }) - it('should not guard for TVoid', () => { + it('Should not guard for TVoid', () => { const R = TypeGuard.TVoid(null) Assert.equal(R, false) }) - it('should not guard for TVoid with invalid $id', () => { + it('Should not guard for TVoid with invalid $id', () => { // @ts-ignore const R = TypeGuard.TVoid(Type.Void({ $id: 1 })) Assert.equal(R, false) diff --git a/test/runtime/schema/date.ts b/test/runtime/schema/date.ts new file mode 100644 index 000000000..8e468665c --- /dev/null +++ b/test/runtime/schema/date.ts @@ -0,0 +1,62 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +// ---------------------------------------------------- +// These tests are implemented by way of .addKeyword() +// which are configured to use Value.Check() +// ---------------------------------------------------- + +describe('type/schema/Date', () => { + it('Should not validate number', () => { + const T = Type.Date() + fail(T, 1) + }) + it('Should not validate string', () => { + const T = Type.Date() + fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.Date() + fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.Date() + fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.Date() + fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.Date() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Date() + fail(T, undefined) + }) + it('Should validate Date', () => { + const T = Type.Date() + ok(T, new Date()) + }) + it('Should validate Date minimumTimestamp', () => { + const T = Type.Date({ minimumTimestamp: 10 }) + fail(T, new Date(9)) + ok(T, new Date(10)) + }) + it('Should validate Date maximumTimestamp', () => { + const T = Type.Date({ maximumTimestamp: 10 }) + ok(T, new Date(10)) + fail(T, new Date(11)) + }) + it('Should validate Date exclusiveMinimumTimestamp', () => { + const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) + fail(T, new Date(10)) + ok(T, new Date(11)) + }) + it('Should validate Date exclusiveMaximumTimestamp', () => { + const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) + ok(T, new Date(9)) + fail(T, new Date(10)) + }) +}) diff --git a/test/runtime/schema/index.ts b/test/runtime/schema/index.ts index 401e007a9..d0053aa86 100644 --- a/test/runtime/schema/index.ts +++ b/test/runtime/schema/index.ts @@ -1,7 +1,9 @@ import './any' import './array' import './boolean' +import './date' import './enum' +import './integer' import './intersect' import './keyof' import './literal' @@ -23,6 +25,7 @@ import './regex' import './required' import './string' import './tuple' +import './uint8array' import './union' import './unknown' import './unsafe' diff --git a/test/runtime/schema/integer.ts b/test/runtime/schema/integer.ts new file mode 100644 index 000000000..da32a8766 --- /dev/null +++ b/test/runtime/schema/integer.ts @@ -0,0 +1,67 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Integer', () => { + it('Should not validate number', () => { + const T = Type.Integer() + fail(T, 3.14) + }) + + it('Should validate integer', () => { + const T = Type.Integer() + ok(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Integer() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Integer() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Integer() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Integer() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Integer() + fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.Integer() + fail(T, undefined) + }) + + it('Should validate minimum', () => { + const T = Type.Integer({ minimum: 10 }) + fail(T, 9) + ok(T, 10) + }) + + it('Should validate maximum', () => { + const T = Type.Integer({ maximum: 10 }) + ok(T, 10) + fail(T, 11) + }) + + it('Should validate Date exclusiveMinimum', () => { + const T = Type.Integer({ exclusiveMinimum: 10 }) + fail(T, 10) + ok(T, 11) + }) + + it('Should validate Date exclusiveMaximum', () => { + const T = Type.Integer({ exclusiveMaximum: 10 }) + ok(T, 9) + fail(T, 10) + }) +}) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts index 62d509c0f..b1a19ea72 100644 --- a/test/runtime/schema/number.ts +++ b/test/runtime/schema/number.ts @@ -3,9 +3,15 @@ import { ok, fail } from './validate' describe('type/schema/Number', () => { it('Should validate number', () => { + const T = Type.Number() + ok(T, 3.14) + }) + + it('Should validate integer', () => { const T = Type.Number() ok(T, 1) }) + it('Should not validate string', () => { const T = Type.Number() fail(T, 'hello') @@ -30,4 +36,28 @@ describe('type/schema/Number', () => { const T = Type.Number() fail(T, undefined) }) + + it('Should validate minimum', () => { + const T = Type.Number({ minimum: 10 }) + fail(T, 9) + ok(T, 10) + }) + + it('Should validate maximum', () => { + const T = Type.Number({ maximum: 10 }) + ok(T, 10) + fail(T, 11) + }) + + it('Should validate Date exclusiveMinimum', () => { + const T = Type.Number({ exclusiveMinimum: 10 }) + fail(T, 10) + ok(T, 11) + }) + + it('Should validate Date exclusiveMaximum', () => { + const T = Type.Number({ exclusiveMaximum: 10 }) + ok(T, 9) + fail(T, 10) + }) }) diff --git a/test/runtime/schema/uint8array.ts b/test/runtime/schema/uint8array.ts new file mode 100644 index 000000000..e1f7e0f68 --- /dev/null +++ b/test/runtime/schema/uint8array.ts @@ -0,0 +1,56 @@ +import { Type } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/schema/Uint8Array', () => { + it('Should not validate number', () => { + const T = Type.Uint8Array() + fail(T, 1) + }) + + it('Should not validate string', () => { + const T = Type.Uint8Array() + fail(T, 'hello') + }) + + it('Should not validate boolean', () => { + const T = Type.Uint8Array() + fail(T, true) + }) + + it('Should not validate array', () => { + const T = Type.Uint8Array() + fail(T, [1, 2, 3]) + }) + + it('Should not validate object', () => { + const T = Type.Uint8Array() + fail(T, { a: 1, b: 2 }) + }) + + it('Should not validate null', () => { + const T = Type.Uint8Array() + fail(T, null) + }) + + it('Should not validate undefined', () => { + const T = Type.Uint8Array() + fail(T, undefined) + }) + + it('Should validate Uint8Array', () => { + const T = Type.Uint8Array() + ok(T, new Uint8Array(100)) + }) + + it('Should validate minByteLength', () => { + const T = Type.Uint8Array({ minByteLength: 4 }) + ok(T, new Uint8Array(4)) + fail(T, new Uint8Array(3)) + }) + + it('Should validate maxByteLength', () => { + const T = Type.Uint8Array({ maxByteLength: 4 }) + ok(T, new Uint8Array(4)) + fail(T, new Uint8Array(5)) + }) +}) diff --git a/test/runtime/schema/validate.ts b/test/runtime/schema/validate.ts index f12181907..bd1f822f7 100644 --- a/test/runtime/schema/validate.ts +++ b/test/runtime/schema/validate.ts @@ -1,16 +1,46 @@ +import { TypeGuard } from '@sinclair/typebox/guard' +import { Value } from '@sinclair/typebox/value' + import { TSchema } from '@sinclair/typebox' import addFormats from 'ajv-formats' import Ajv, { AnySchema } from 'ajv/dist/2019' -export function validator(additional: AnySchema[]) { +function schemaOf(schemaOf: string, value: unknown, schema: unknown) { + switch (schemaOf) { + case 'Constructor': + return TypeGuard.TConstructor(schema) && Value.Check(schema, value) + case 'Function': + return TypeGuard.TFunction(schema) && Value.Check(schema, value) + case 'Date': + return TypeGuard.TDate(schema) && Value.Check(schema, value) + case 'Promise': + return TypeGuard.TPromise(schema) && Value.Check(schema, value) + case 'Uint8Array': + return TypeGuard.TUint8Array(schema) && Value.Check(schema, value) + case 'Undefined': + return TypeGuard.TUndefined(schema) && Value.Check(schema, value) + case 'Void': + return TypeGuard.TVoid(schema) && Value.Check(schema, value) + default: + return false + } +} + +export function createAjv(references: AnySchema[]) { return addFormats(new Ajv({}), ['date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex']) - .addKeyword('kind') - .addKeyword('modifier') - .addSchema(additional) + .addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf }) + .addKeyword({ type: 'null', keyword: 'typeOf', validate: schemaOf }) + .addKeyword('exclusiveMinimumTimestamp') + .addKeyword('exclusiveMaximumTimestamp') + .addKeyword('minimumTimestamp') + .addKeyword('maximumTimestamp') + .addKeyword('minByteLength') + .addKeyword('maxByteLength') + .addSchema(references) } export function ok(type: T, data: unknown, additional: AnySchema[] = []) { - const ajv = validator(additional) + const ajv = createAjv(additional) function execute() { // required as ajv will throw if referenced schema is not found try { @@ -37,7 +67,7 @@ export function ok(type: T, data: unknown, additional: AnySch } export function fail(type: T, data: unknown, additional: AnySchema[] = []) { - const ajv = validator(additional) + const ajv = createAjv(additional) function execute() { // required as ajv will throw if referenced schema is not found try { diff --git a/test/runtime/value/cast/any.ts b/test/runtime/value/cast/any.ts index 5fcace73f..155b2fcaa 100644 --- a/test/runtime/value/cast/any.ts +++ b/test/runtime/value/cast/any.ts @@ -34,11 +34,19 @@ describe('value/cast/Any', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result.getTime(100), 100) + }) + it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/array.ts b/test/runtime/value/cast/array.ts index d23c59218..fca7b4719 100644 --- a/test/runtime/value/cast/array.ts +++ b/test/runtime/value/cast/array.ts @@ -48,6 +48,12 @@ describe('value/cast/Array', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = [6, 7, 8] const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/boolean.ts b/test/runtime/value/cast/boolean.ts index e7a3c8023..d0267b52e 100644 --- a/test/runtime/value/cast/boolean.ts +++ b/test/runtime/value/cast/boolean.ts @@ -45,6 +45,12 @@ describe('value/cast/Boolean', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = true const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts new file mode 100644 index 000000000..69c26dc40 --- /dev/null +++ b/test/runtime/value/cast/date.ts @@ -0,0 +1,56 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Date', () => { + const T = Type.Date() + const E = new Date(0) + + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preseve', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result.getTime(), 100) + }) +}) diff --git a/test/runtime/value/cast/enum.ts b/test/runtime/value/cast/enum.ts index 232505ac7..e75d5bdd8 100644 --- a/test/runtime/value/cast/enum.ts +++ b/test/runtime/value/cast/enum.ts @@ -15,21 +15,25 @@ describe('value/cast/Boolean', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 123 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -48,6 +52,12 @@ describe('value/cast/Boolean', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from enum A', () => { const value = Foo.A const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 14b81b055..74b79cd8a 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -2,17 +2,17 @@ import './any' import './array' import './boolean' import './convert' +import './date' import './enum' import './integer' import './intersect' import './keyof' import './literal' -import './namespace' import './never' import './null' import './number' import './object' -import './rec' +import './recursive' import './record' import './regex' import './string' diff --git a/test/runtime/value/cast/integer.ts b/test/runtime/value/cast/integer.ts index 1f026ac8e..194fd7b18 100644 --- a/test/runtime/value/cast/integer.ts +++ b/test/runtime/value/cast/integer.ts @@ -11,16 +11,19 @@ describe('value/cast/Integer', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, 1) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, 1) // conversion }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -44,4 +47,10 @@ describe('value/cast/Integer', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) }) diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index 78e2c0bb5..ed3ade161 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -28,16 +28,19 @@ describe('value/cast/Intersect', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -62,6 +65,12 @@ describe('value/cast/Intersect', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast and preserve object', () => { const value = { x: 7, y: 8, z: 9 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/keyof.ts b/test/runtime/value/cast/keyof.ts index c9c2b9c9c..b18d1f56f 100644 --- a/test/runtime/value/cast/keyof.ts +++ b/test/runtime/value/cast/keyof.ts @@ -54,6 +54,12 @@ describe('value/cast/KeyOf', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = 'y' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/literal.ts b/test/runtime/value/cast/literal.ts index bf0174a77..ae2e27083 100644 --- a/test/runtime/value/cast/literal.ts +++ b/test/runtime/value/cast/literal.ts @@ -11,16 +11,19 @@ describe('value/cast/Literal', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Literal', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preseve', () => { const value = 'hello' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/namespace.ts b/test/runtime/value/cast/namespace.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/runtime/value/cast/never.ts b/test/runtime/value/cast/never.ts index d8ec459fe..c493f32f2 100644 --- a/test/runtime/value/cast/never.ts +++ b/test/runtime/value/cast/never.ts @@ -32,4 +32,8 @@ describe('value/cast/Never', () => { const value = null Assert.throws(() => Value.Cast(T, value)) }) + it('Should upcast from date', () => { + const value = null + Assert.throws(() => Value.Cast(T, value)) + }) }) diff --git a/test/runtime/value/cast/null.ts b/test/runtime/value/cast/null.ts index 5857121e2..06eb0f344 100644 --- a/test/runtime/value/cast/null.ts +++ b/test/runtime/value/cast/null.ts @@ -11,16 +11,19 @@ describe('value/cast/Null', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Null', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preseve', () => { const value = null const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/number.ts b/test/runtime/value/cast/number.ts index 273f1f4ab..1fbd51807 100644 --- a/test/runtime/value/cast/number.ts +++ b/test/runtime/value/cast/number.ts @@ -11,16 +11,19 @@ describe('value/cast/Number', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, 1) }) + it('Should upcast from boolean', () => { const value = true // convert const result = Value.Cast(T, value) Assert.deepEqual(result, 1) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Number', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preseve', () => { const value = 123 const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts index 71bd0bf01..544ab46d8 100644 --- a/test/runtime/value/cast/object.ts +++ b/test/runtime/value/cast/object.ts @@ -59,6 +59,13 @@ describe('value/cast/Object', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = { x: 7, y: 8, z: 9, a: 10, b: 11, c: 12 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/record.ts b/test/runtime/value/cast/record.ts index c8e9dd350..32b5c53c4 100644 --- a/test/runtime/value/cast/record.ts +++ b/test/runtime/value/cast/record.ts @@ -53,6 +53,12 @@ describe('value/cast/Record', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = { a: { x: 1, y: 2, z: 3 }, @@ -61,6 +67,7 @@ describe('value/cast/Record', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should preserve and patch invalid records', () => { const value = { a: { x: 1, y: 2, z: 3 }, diff --git a/test/runtime/value/cast/rec.ts b/test/runtime/value/cast/recursive.ts similarity index 94% rename from test/runtime/value/cast/rec.ts rename to test/runtime/value/cast/recursive.ts index 478a5fc87..0db482577 100644 --- a/test/runtime/value/cast/rec.ts +++ b/test/runtime/value/cast/recursive.ts @@ -17,11 +17,13 @@ describe('value/cast/Recursive', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) @@ -52,6 +54,12 @@ describe('value/cast/Recursive', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = { id: 'A', diff --git a/test/runtime/value/cast/regex.ts b/test/runtime/value/cast/regex.ts index c973dba37..3adb18c98 100644 --- a/test/runtime/value/cast/regex.ts +++ b/test/runtime/value/cast/regex.ts @@ -17,16 +17,19 @@ describe('value/cast/RegEx', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/RegEx', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/string.ts b/test/runtime/value/cast/string.ts index 9b5fa1ef7..28b7f2066 100644 --- a/test/runtime/value/cast/string.ts +++ b/test/runtime/value/cast/string.ts @@ -29,6 +29,7 @@ describe('value/cast/String', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -47,6 +48,12 @@ describe('value/cast/String', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/tuple.ts b/test/runtime/value/cast/tuple.ts index 4ecb10d96..c26831b24 100644 --- a/test/runtime/value/cast/tuple.ts +++ b/test/runtime/value/cast/tuple.ts @@ -48,6 +48,12 @@ describe('value/cast/Tuple', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = [42, 'world'] const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/uint8array.ts b/test/runtime/value/cast/uint8array.ts index edcdef5de..bd861b452 100644 --- a/test/runtime/value/cast/uint8array.ts +++ b/test/runtime/value/cast/uint8array.ts @@ -17,16 +17,19 @@ describe('value/cast/Uint8Array', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Uint8Array', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = new Uint8Array([6, 7, 8, 9, 10]) const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/undefined.ts b/test/runtime/value/cast/undefined.ts index 62de89959..36a67693f 100644 --- a/test/runtime/value/cast/undefined.ts +++ b/test/runtime/value/cast/undefined.ts @@ -11,16 +11,19 @@ describe('value/cast/Undefined', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Undefined', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preseve', () => { const value = undefined const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 5998527da..f8db7ca4c 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -41,16 +41,19 @@ describe('value/cast/Union', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -69,6 +72,12 @@ describe('value/cast/Union', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve A', () => { const value = { type: 'A', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/unknown.ts b/test/runtime/value/cast/unknown.ts index 5a98faa9c..66f333d7e 100644 --- a/test/runtime/value/cast/unknown.ts +++ b/test/runtime/value/cast/unknown.ts @@ -4,26 +4,31 @@ import { Assert } from '../../assert/index' describe('value/cast/Unknown', () => { const T = Type.Unknown() + it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) + it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) @@ -42,6 +47,12 @@ describe('value/cast/Unknown', () => { Assert.deepEqual(result, value) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result: any = Value.Cast(T, value) + Assert.deepEqual(result.getTime(), 100) + }) + it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts index 33e53e02d..93d491d82 100644 --- a/test/runtime/value/cast/void.ts +++ b/test/runtime/value/cast/void.ts @@ -11,16 +11,19 @@ describe('value/cast/Void', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) + it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) @@ -45,6 +48,12 @@ describe('value/cast/Void', () => { Assert.deepEqual(result, E) }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { const value = null const result = Value.Cast(T, value) diff --git a/test/runtime/value/check/any.ts b/test/runtime/value/check/any.ts index 15f31b510..a9fab67d4 100644 --- a/test/runtime/value/check/any.ts +++ b/test/runtime/value/check/any.ts @@ -39,4 +39,9 @@ describe('value/check/Any', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) + it('Should pass Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, true) + }) }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index 5b7b327e1..909b093c6 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -30,4 +30,9 @@ describe('value/check/Array', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(Type.Array(Type.Any()), value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/boolean.ts b/test/runtime/value/check/boolean.ts index 003bc296a..887f3f020 100644 --- a/test/runtime/value/check/boolean.ts +++ b/test/runtime/value/check/boolean.ts @@ -39,4 +39,9 @@ describe('value/check/Boolean', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/date.ts b/test/runtime/value/check/date.ts new file mode 100644 index 000000000..583e1b620 --- /dev/null +++ b/test/runtime/value/check/date.ts @@ -0,0 +1,47 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Date', () => { + const T = Type.Date() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/enum.ts b/test/runtime/value/check/enum.ts index 505625ca9..5eb8c08e6 100644 --- a/test/runtime/value/check/enum.ts +++ b/test/runtime/value/check/enum.ts @@ -26,4 +26,9 @@ describe('value/check/Enum', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index e617cfa64..26759b50d 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './date' import './enum' import './integer' import './intersect' @@ -10,7 +11,7 @@ import './never' import './null' import './number' import './object' -import './rec' +import './recursive' import './record' import './regex' import './string' diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts index 27ab4d396..59a74024c 100644 --- a/test/runtime/value/check/integer.ts +++ b/test/runtime/value/check/integer.ts @@ -16,4 +16,9 @@ describe('value/check/Integer', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/null.ts b/test/runtime/value/check/null.ts index f2cbc93ff..15b9f786b 100644 --- a/test/runtime/value/check/null.ts +++ b/test/runtime/value/check/null.ts @@ -39,4 +39,9 @@ describe('value/check/Null', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts index f6b34bebf..4d9b14d6c 100644 --- a/test/runtime/value/check/number.ts +++ b/test/runtime/value/check/number.ts @@ -39,4 +39,9 @@ describe('value/check/Number', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/rec.ts b/test/runtime/value/check/recursive.ts similarity index 100% rename from test/runtime/value/check/rec.ts rename to test/runtime/value/check/recursive.ts diff --git a/test/runtime/value/check/string.ts b/test/runtime/value/check/string.ts index 85737681a..7db4daca3 100644 --- a/test/runtime/value/check/string.ts +++ b/test/runtime/value/check/string.ts @@ -39,4 +39,9 @@ describe('value/check/String', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/uint8array.ts b/test/runtime/value/check/uint8array.ts index f792d8147..9940e7c4a 100644 --- a/test/runtime/value/check/uint8array.ts +++ b/test/runtime/value/check/uint8array.ts @@ -3,28 +3,28 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/check/Uint8Array', () => { - it('Should pass uint8array', () => { + it('Should pass Uint8Array', () => { const T = Type.Uint8Array() const value = new Uint8Array(4) const result = Value.Check(T, value) Assert.equal(result, true) }) - it('Should fail uint8array', () => { + it('Should fail Uint8Array', () => { const T = Type.Uint8Array() const value = [0, 1, 2, 3] const result = Value.Check(T, value) Assert.equal(result, false) }) - it('Should fail uint8array with undefined', () => { + it('Should fail Uint8Array with undefined', () => { const T = Type.Uint8Array() const value = undefined const result = Value.Check(T, value) Assert.equal(result, false) }) - it('Should fail uint8array with null', () => { + it('Should fail Uint8Array with null', () => { const T = Type.Uint8Array() const value = null const result = Value.Check(T, value) diff --git a/test/runtime/value/check/undefined.ts b/test/runtime/value/check/undefined.ts index 162022385..83b6f3383 100644 --- a/test/runtime/value/check/undefined.ts +++ b/test/runtime/value/check/undefined.ts @@ -39,4 +39,9 @@ describe('value/check/Undefined', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/unknown.ts b/test/runtime/value/check/unknown.ts index 1bb814596..19e305188 100644 --- a/test/runtime/value/check/unknown.ts +++ b/test/runtime/value/check/unknown.ts @@ -39,4 +39,9 @@ describe('value/check/Unknown', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) + it('Should pass Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, true) + }) }) diff --git a/test/runtime/value/check/void.ts b/test/runtime/value/check/void.ts index b9d01c1f3..c6079c354 100644 --- a/test/runtime/value/check/void.ts +++ b/test/runtime/value/check/void.ts @@ -39,4 +39,9 @@ describe('value/check/Void', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) diff --git a/test/static/constructor-parameters.ts b/test/static/constructor-parameters.ts new file mode 100644 index 000000000..68081e37f --- /dev/null +++ b/test/static/constructor-parameters.ts @@ -0,0 +1,13 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +const C = Type.Constructor( + [Type.Number(), Type.String()], + Type.Object({ + method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), + }), +) + +const P = Type.ConstructorParameters(C) + +Expect(P).ToInfer<[number, string]>() diff --git a/test/static/constructor.ts b/test/static/constructor.ts new file mode 100644 index 000000000..8e05d8c40 --- /dev/null +++ b/test/static/constructor.ts @@ -0,0 +1,11 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +const C = Type.Constructor( + [Type.Number(), Type.String()], + Type.Object({ + method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), + }), +) + +Expect(C).ToInfer { method: (param_0: number, param_1: string) => boolean }>() diff --git a/test/static/date.ts b/test/static/date.ts new file mode 100644 index 000000000..5907ac9eb --- /dev/null +++ b/test/static/date.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Date()).ToInfer() diff --git a/test/static/function.ts b/test/static/function.ts new file mode 100644 index 000000000..824075f7c --- /dev/null +++ b/test/static/function.ts @@ -0,0 +1,11 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +const C = Type.Function( + [Type.Number(), Type.String()], + Type.Object({ + method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), + }), +) + +Expect(C).ToInfer<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() diff --git a/test/static/index.ts b/test/static/index.ts index 909c65591..cc84ce321 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './date' import './constructor-parameters' import './constructor' import './emum' diff --git a/test/static/parameters.ts b/test/static/parameters.ts new file mode 100644 index 000000000..02ad9c989 --- /dev/null +++ b/test/static/parameters.ts @@ -0,0 +1,13 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +const C = Type.Function( + [Type.Number(), Type.String()], + Type.Object({ + method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), + }), +) + +const P = Type.Parameters(C) + +Expect(P).ToInfer<[number, string]>() From fcf926e60c8136b779fe5fd338aa5f1508cbc802 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 29 Oct 2022 06:03:03 +0900 Subject: [PATCH 025/369] Rename error reporting enums to date constraints --- package.json | 2 +- src/errors/errors.ts | 16 ++++++++-------- src/typebox.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index fbc4bf63f..5bfde7d27 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.0", + "version": "0.25.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 98fd0bf6c..db4830256 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -40,10 +40,10 @@ export enum ValueErrorType { ArrayUniqueItems, Boolean, Date, - DateExclusiveMinimum, - DateExclusiveMaximum, - DateMinimum, - DateMaximum, + DateExclusiveMinimumTimestamp, + DateExclusiveMaximumTimestamp, + DateMinimumTimestamp, + DateMaximumTimestamp, Function, Integer, IntegerMultipleOf, @@ -142,16 +142,16 @@ export namespace ValueErrors { return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } } if (schema.exclusiveMinimumTimestamp && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMinimum, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } + yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } } if (schema.exclusiveMaximumTimestamp && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMaximum, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } + yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } } if (schema.minimumTimestamp && !(value.getTime() >= schema.minimumTimestamp)) { - yield { type: ValueErrorType.DateMinimum, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } + yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } } if (schema.maximumTimestamp && !(value.getTime() <= schema.maximumTimestamp)) { - yield { type: ValueErrorType.DateMaximum, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } + yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } } } diff --git a/src/typebox.ts b/src/typebox.ts index 5c8daf5cb..ebacf611f 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -413,7 +413,7 @@ export interface TRecord Date: Mon, 31 Oct 2022 16:30:14 +0900 Subject: [PATCH 026/369] Compiler Argument TNull to TNever --- package-lock.json | 4 ++-- src/compiler/compiler.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0754cb51b..9128aeaa5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.24.39", + "version": "0.25.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.24.39", + "version": "0.25.1", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index a7f41db03..3c797dbfc 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -161,7 +161,7 @@ export namespace TypeCompiler { } } - function* Never(schema: Types.TNull, value: string): IterableIterator { + function* Never(schema: Types.TNever, value: string): IterableIterator { yield `(false)` } From 09186986f5e06db597d87e5dd34783b9ef73608f Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 31 Oct 2022 21:29:19 +0900 Subject: [PATCH 027/369] Enclose Expression to Ensure Evaluation is Scoped --- package.json | 2 +- src/compiler/compiler.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5bfde7d27..d194415ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.1", + "version": "0.25.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 3c797dbfc..9ef44a813 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -378,7 +378,7 @@ export namespace TypeCompiler { } function CreateExpression(schema: Types.TSchema, value: string): string { - return [...Visit(schema, value)].join(' && ') + return `(${[...Visit(schema, value)].join(' && ')})` } function CreateFunctionName($id: string) { From cea45f1c67e2838e2029e842707a5ef477d8f273 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 8 Nov 2022 16:20:26 +0900 Subject: [PATCH 028/369] Union Cast Uses Default Value --- package.json | 2 +- src/value/cast.ts | 123 ++++++++++++++++--------------- src/value/create.ts | 12 --- test/runtime/value/cast/union.ts | 34 +++++++++ 4 files changed, 97 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index d194415ad..e056ba896 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.2", + "version": "0.25.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast.ts b/src/value/cast.ts index fda08eb81..19c7b9133 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -31,13 +31,47 @@ import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueClone } from './clone' -namespace UnionValueCast { - // ---------------------------------------------------------------------------------------------- - // The following will score a schema against a value. For objects, the score is the tally of - // points awarded for each property of the value. Property points are (1.0 / propertyCount) - // to prevent large property counts biasing results. Properties that match literal values are - // maximally awarded as literals are typically used as union discriminator fields. - // ---------------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------------- +// Errors +// ---------------------------------------------------------------------------------------------- + +export class ValueCastReferenceTypeError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) + } +} + +export class ValueCastArrayUniqueItemsTypeError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { + super('ValueCast: Array cast produced invalid data due to uniqueItems constraint') + } +} + +export class ValueCastNeverTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast: Never types cannot be cast') + } +} + +export class ValueCastRecursiveTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast.Recursive: Cannot cast recursive schemas') + } +} +export class ValueCastUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCast: Unknown type') + } +} + +// ---------------------------------------------------------------------------------------------- +// The following will score a schema against a value. For objects, the score is the tally of +// points awarded for each property of the value. Property points are (1.0 / propertyCount) +// to prevent large property counts biasing results. Properties that match literal values are +// maximally awarded as literals are typically used as union discriminator fields. +// ---------------------------------------------------------------------------------------------- + +namespace UnionCastCreate { function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { if (schema[Types.Kind] === 'Object' && typeof value === 'object' && value !== null) { const object = schema as Types.TObject @@ -54,6 +88,7 @@ namespace UnionValueCast { return ValueCheck.Check(schema, references, value) ? 1 : 0 } } + function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { let [select, best] = [union.anyOf[0], 0] for (const schema of union.anyOf) { @@ -67,47 +102,19 @@ namespace UnionValueCast { } export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { - return ValueCheck.Check(union, references, value) ? ValueClone.Clone(value) : ValueCast.Cast(Select(union, references, value), references, value) - } -} - -// ----------------------------------------------------------- -// Errors -// ----------------------------------------------------------- - -export class ValueCastReferenceTypeError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { - super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) - } -} - -export class ValueCastArrayUniqueItemsTypeError extends Error { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { - super('ValueCast: Array cast produced invalid data due to uniqueItems constraint') - } -} - -export class ValueCastNeverTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('ValueCast: Never types cannot be cast') - } -} - -export class ValueCastRecursiveTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('ValueCast.Recursive: Cannot cast recursive schemas') - } -} -export class ValueCastUnknownTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('ValueCast: Unknown type') + if (union.default !== undefined) { + return union.default + } else { + const schema = Select(union, references, value) + return ValueCast.Cast(schema, references, value) + } } } export namespace ValueCast { - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- // Guards - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- function IsArray(value: unknown): value is unknown[] { return typeof value === 'object' && globalThis.Array.isArray(value) @@ -145,9 +152,9 @@ export namespace ValueCast { return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === 0n) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) } - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- // Convert - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- function TryConvertString(value: unknown) { return IsValueToString(value) ? value.toString() : value @@ -165,12 +172,12 @@ export namespace ValueCast { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value } - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- // Cast - // ----------------------------------------------------------- + // ---------------------------------------------------------------------------------------------- function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { @@ -205,10 +212,6 @@ export namespace ValueCast { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Enum(schema: Types.TEnum, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } @@ -219,7 +222,7 @@ export namespace ValueCast { } function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Never(schema: Types.TNever, references: Types.TSchema[], value: any): any { @@ -227,7 +230,7 @@ export namespace ValueCast { } function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): any { @@ -300,23 +303,23 @@ export namespace ValueCast { } function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): any { - return UnionValueCast.Create(schema, references, value) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : UnionCastCreate.Create(schema, references, value) } function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { @@ -333,8 +336,6 @@ export namespace ValueCast { return Constructor(anySchema, anyReferences, value) case 'Date': return Date(anySchema, anyReferences, value) - case 'Enum': - return Enum(anySchema, anyReferences, value) case 'Function': return Function(anySchema, anyReferences, value) case 'Integer': diff --git a/src/value/create.ts b/src/value/create.ts index 3f9f7b52a..bb742c407 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -101,16 +101,6 @@ export namespace ValueCreate { } } - function Enum(schema: Types.TEnum, references: Types.TSchema[]): any { - if (schema.default !== undefined) { - return schema.default - } else if (schema.anyOf.length === 0) { - throw new Error('ValueCreate.Enum: Cannot create default enum value as this enum has no items') - } else { - return schema.anyOf[0].const - } - } - function Function(schema: Types.TFunction, references: Types.TSchema[]): any { if (schema.default !== undefined) { return schema.default @@ -303,8 +293,6 @@ export namespace ValueCreate { return Constructor(anySchema, anyReferences) case 'Date': return Date(anySchema, anyReferences) - case 'Enum': - return Enum(anySchema, anyReferences) case 'Function': return Function(anySchema, anyReferences) case 'Integer': diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index f8db7ca4c..b438b5dba 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -131,4 +131,38 @@ describe('value/cast/Union', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) }) + + it('Should cast with default value (create)', () => { + const result = Value.Cast( + Type.Object({ + id: Type.Number(), + value: Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')], { default: 'C' }), + }), + { + id: 42, + value: 'D', + }, + ) + Assert.deepEqual(result, { + id: 42, + value: 'C', + }) + }) + + it('Should cast with default value (preserve)', () => { + const result = Value.Cast( + Type.Object({ + id: Type.Number(), + value: Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')], { default: 'C' }), + }), + { + id: 42, + value: 'B', + }, + ) + Assert.deepEqual(result, { + id: 42, + value: 'B', + }) + }) }) From 6cba0d55e08d5e2cd3f9e62fbc8ab0a5be9f923c Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 9 Nov 2022 17:33:12 +0900 Subject: [PATCH 029/369] ValueDelta: Edit Types Defined as TypeBox Types --- package.json | 2 +- readme.md | 6 +-- src/value/delta.ts | 108 ++++++++++++++++++++++++++------------------- src/value/value.ts | 4 +- 4 files changed, 69 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index e056ba896..02b60fe2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.3", + "version": "0.25.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 60ec68e6c..2d1ddcdb1 100644 --- a/readme.md +++ b/readme.md @@ -832,7 +832,7 @@ const R = Value.Equal( // const R = true Use the Diff function to produce a sequence of edits to transform one value into another. ```typescript -const E = Value.Diff( // const E = [ +const E = Value.Diff( // const E = [ { x: 1, y: 2, z: 3 }, // { type: 'update', path: '/y', value: 4 }, { y: 4, z: 5, w: 6 } // { type: 'update', path: '/z', value: 5 }, ) // { type: 'insert', path: '/w', value: 6 }, @@ -851,12 +851,12 @@ const A = { x: 1, y: 2 } const B = { x: 3 } -const E = Value.Diff(A, B) // const E = [ +const E = Value.Diff(A, B) // const E = [ // { type: 'update', path: '/x', value: 3 }, // { type: 'delete', path: '/y' } // ] -const C = Value.Patch(A, E) // const C = { x: 3 } +const C = Value.Patch(A, E) // const C = { x: 3 } ``` diff --git a/src/value/delta.ts b/src/value/delta.ts index 73c30b069..00cd71902 100644 --- a/src/value/delta.ts +++ b/src/value/delta.ts @@ -26,46 +26,71 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Type, Static } from '../typebox' import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' import { ValueClone } from './clone' import { ValuePointer } from './pointer' -export type Edit = Insert | Update | Delete - -export interface Insert { - brand: T - type: 'insert' - path: string - value: any +// --------------------------------------------------------------------- +// Operations +// --------------------------------------------------------------------- + +export type Insert = Static +export const Insert = Type.Object({ + type: Type.Literal('insert'), + path: Type.String(), + value: Type.Unknown(), +}) + +export type Update = Static +export const Update = Type.Object({ + type: Type.Literal('update'), + path: Type.String(), + value: Type.Unknown(), +}) + +export type Delete = Static +export const Delete = Type.Object({ + type: Type.Literal('delete'), + path: Type.String(), +}) + +export type Edit = Static +export const Edit = Type.Union([Insert, Update, Delete]) + +// --------------------------------------------------------------------- +// Errors +// --------------------------------------------------------------------- + +export class ValueDeltaObjectWithSymbolKeyError extends Error { + constructor(public readonly key: unknown) { + super('ValueDelta: Cannot diff objects with symbol keys') + } } - -export interface Update { - brand: T - type: 'update' - path: string - value: any +export class ValueDeltaUnableToDiffUnknownValue extends Error { + constructor(public readonly value: unknown) { + super('ValueDelta: Unable to create diff edits for unknown value') + } } -export interface Delete { - brand: T - type: 'delete' - path: string -} +// --------------------------------------------------------------------- +// ValueDelta +// --------------------------------------------------------------------- export namespace ValueDelta { // --------------------------------------------------------------------- // Edits // --------------------------------------------------------------------- - function Update(path: string, value: unknown): Edit { + function Update(path: string, value: unknown): Edit { return { type: 'update', path, value } as any } - function Insert(path: string, value: unknown): Edit { + function Insert(path: string, value: unknown): Edit { return { type: 'insert', path, value } as any } - function Delete(path: string): Edit { + function Delete(path: string): Edit { return { type: 'delete', path } as any } @@ -73,35 +98,30 @@ export namespace ValueDelta { // Diff // --------------------------------------------------------------------- - function* Object(path: string, current: ObjectType, next: unknown): IterableIterator> { + function* Object(path: string, current: ObjectType, next: unknown): IterableIterator { if (!Is.Object(next)) return yield Update(path, next) const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] for (const key of currentKeys) { - if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) if (next[key] === undefined && nextKeys.includes(key)) yield Update(`${path}/${String(key)}`, undefined) } for (const key of nextKeys) { if (current[key] === undefined || next[key] === undefined) continue - if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) yield* Visit(`${path}/${String(key)}`, current[key], next[key]) } for (const key of nextKeys) { - if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) if (current[key] === undefined) yield Insert(`${path}/${String(key)}`, next[key]) } for (const key of currentKeys.reverse()) { - if (typeof key === 'symbol') throw Error('ValueDelta: Cannot produce diff symbol keys') + if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) if (next[key] === undefined && !nextKeys.includes(key)) yield Delete(`${path}/${String(key)}`) } } - function* Date(path: string, current: Date, next: unknown): IterableIterator> { - if (Is.Date(next) && current.getTime() === next.getTime()) return - yield Update(path, next) - } - - function* Array(path: string, current: ArrayType, next: unknown): IterableIterator> { + function* Array(path: string, current: ArrayType, next: unknown): IterableIterator { if (!Is.Array(next)) return yield Update(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]) @@ -116,23 +136,21 @@ export namespace ValueDelta { } } - function* TypedArray(path: string, current: TypedArrayType, next: unknown): IterableIterator> { + function* TypedArray(path: string, current: TypedArrayType, next: unknown): IterableIterator { if (!Is.TypedArray(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield Update(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]) } } - function* Value(path: string, current: ValueType, next: unknown): IterableIterator> { + function* Value(path: string, current: ValueType, next: unknown): IterableIterator { if (current === next) return yield Update(path, next) } - function* Visit(path: string, current: unknown, next: unknown): IterableIterator> { + function* Visit(path: string, current: unknown, next: unknown): IterableIterator { if (Is.Object(current)) { return yield* Object(path, current, next) - } else if (Is.Date(current)) { - return yield* Date(path, current, next) } else if (Is.Array(current)) { return yield* Array(path, current, next) } else if (Is.TypedArray(current)) { @@ -140,11 +158,11 @@ export namespace ValueDelta { } else if (Is.Value(current)) { return yield* Value(path, current, next) } else { - throw new Error('ValueDelta: Cannot produce edits for value') + throw new ValueDeltaUnableToDiffUnknownValue(current) } } - export function Diff(current: T, next: T): Edit[] { + export function Diff(current: unknown, next: unknown): Edit[] { return [...Visit('', current, next)] } @@ -152,20 +170,20 @@ export namespace ValueDelta { // Patch // --------------------------------------------------------------------- - function IsRootUpdate(edits: Edit[]): edits is [Update] { + function IsRootUpdate(edits: Edit[]): edits is [Update] { return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' } - function IsIdentity(edits: Edit[]) { + function IsIdentity(edits: Edit[]) { return edits.length === 0 } - export function Patch(current: T, edits: Edit[]): T { + export function Patch(current: unknown, edits: Edit[]): T { if (IsRootUpdate(edits)) { - return ValueClone.Clone(edits[0].value) + return ValueClone.Clone(edits[0].value) as T } if (IsIdentity(edits)) { - return ValueClone.Clone(current) + return ValueClone.Clone(current) as T } const clone = ValueClone.Clone(current) for (const edit of edits) { @@ -184,6 +202,6 @@ export namespace ValueDelta { } } } - return clone + return clone as T } } diff --git a/src/value/value.ts b/src/value/value.ts index ff0f27872..a8eea545e 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -86,12 +86,12 @@ export namespace Value { } /** Returns edits to transform the current value into the next value */ - export function Diff(current: T, next: T): Edit[] { + export function Diff(current: unknown, next: unknown): Edit[] { return ValueDelta.Diff(current, next) } /** Returns a new value with edits applied to the given value */ - export function Patch(current: T, edits: Edit[]): T { + export function Patch(current: unknown, edits: Edit[]): T { return ValueDelta.Patch(current, edits) as T } } From 1cb016b429e85516e978c2afedd7c5e5803dfe3b Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 9 Nov 2022 18:26:11 +0900 Subject: [PATCH 030/369] ValueDelta: Edit Types Defined as TypeBox Types --- package.json | 2 +- src/conditional/conditional.ts | 2 +- src/errors/errors.ts | 1 + src/format/format.ts | 2 +- src/guard/guard.ts | 2 +- src/value/pointer.ts | 2 +- src/value/value.ts | 4 ++-- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 02b60fe2b..e16bda5fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.4", + "version": "0.25.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/conditional/conditional.ts b/src/conditional/conditional.ts index dd3e71180..ecd2a756d 100644 --- a/src/conditional/conditional.ts +++ b/src/conditional/conditional.ts @@ -52,7 +52,7 @@ export interface TExtract exten static: Extract, Types.Static> } -/** Conditional Types */ +/** Conditional type mapping for TypeBox types */ export namespace Conditional { /** (Experimental) Creates a conditional expression type */ export function Extends(left: L, right: R, ok: T, fail: U): TExtends { diff --git a/src/errors/errors.ts b/src/errors/errors.ts index db4830256..4324f2bb1 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -106,6 +106,7 @@ export class ValueErrorsUnknownTypeError extends Error { } } +/** Provides functionality to generate a sequence of errors against a TypeBox type. */ export namespace ValueErrors { function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} diff --git a/src/format/format.ts b/src/format/format.ts index 3dc6de9f4..544009a7f 100644 --- a/src/format/format.ts +++ b/src/format/format.ts @@ -28,7 +28,7 @@ THE SOFTWARE. export type FormatValidationFunction = (value: string) => boolean -/** Shared string formats used by the TypeCompiler and Value modules */ +/** Provides functions to create custom string formats */ export namespace Format { const formats = new Map() diff --git a/src/guard/guard.ts b/src/guard/guard.ts index 24f23c2eb..ca61df37d 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -34,7 +34,7 @@ export class TypeGuardInvalidTypeError extends Error { } } -/** TypeGuard tests that values conform to a known TypeBox type specification */ +/** Provides functionality to test if values are TypeBox types */ export namespace TypeGuard { function IsObject(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value) diff --git a/src/value/pointer.ts b/src/value/pointer.ts index 4b7c5bf9c..fa828480c 100644 --- a/src/value/pointer.ts +++ b/src/value/pointer.ts @@ -38,7 +38,7 @@ export class ValuePointerRootDeleteError extends Error { } } -/** ValuePointer performs mutable operations on values using RFC6901 Json Pointers */ +/** Provides functionality to update values through RFC6901 string pointers */ export namespace ValuePointer { function Escape(component: string) { return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') diff --git a/src/value/value.ts b/src/value/value.ts index a8eea545e..48aab1500 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -35,9 +35,9 @@ import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueDelta, Edit } from './delta' -export type { Edit } from './delta' +export { Edit, Insert, Update, Delete } from './delta' -/** Value performs immutable operations on values */ +/** Provides functions to perform structural updates to JavaScript values */ export namespace Value { /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ export function Cast(schema: T, references: [...R], value: unknown): Types.Static From eaec121cc0e0f151e37a536ccc3ba8c2ac10b539 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 10 Nov 2022 20:16:24 +0900 Subject: [PATCH 031/369] Value Guard Numeric Constraints on Error Reporting --- package.json | 2 +- src/errors/errors.ts | 48 ++++++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index e16bda5fb..be32d047a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.5", + "version": "0.25.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 4324f2bb1..df1da6817 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -108,16 +108,20 @@ export class ValueErrorsUnknownTypeError extends Error { /** Provides functionality to generate a sequence of errors against a TypeBox type. */ export namespace ValueErrors { + function IsNumber(value: number | undefined): value is number { + return typeof value === 'number' + } + function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} function* Array(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!globalThis.Array.isArray(value)) { return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } } - if (schema.minItems !== undefined && !(value.length >= schema.minItems)) { + if (IsNumber(schema.minItems) && !(value.length >= schema.minItems)) { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } } - if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) { + if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } } if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { @@ -142,16 +146,16 @@ export namespace ValueErrors { if (!(value instanceof globalThis.Date)) { return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } } - if (schema.exclusiveMinimumTimestamp && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } } - if (schema.exclusiveMaximumTimestamp && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + if (IsNumber(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } } - if (schema.minimumTimestamp && !(value.getTime() >= schema.minimumTimestamp)) { + if (IsNumber(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } } - if (schema.maximumTimestamp && !(value.getTime() <= schema.maximumTimestamp)) { + if (IsNumber(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } } } @@ -169,19 +173,19 @@ export namespace ValueErrors { if (!globalThis.Number.isInteger(value)) { yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } } - if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } } - if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } } - if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } } - if (schema.minimum && !(value >= schema.minimum)) { + if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } } - if (schema.maximum && !(value <= schema.maximum)) { + if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } } } @@ -207,19 +211,19 @@ export namespace ValueErrors { if (!(typeof value === 'number')) { return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } - if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } } - if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } } - if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } } - if (schema.minimum && !(value >= schema.minimum)) { + if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } } - if (schema.maximum && !(value <= schema.maximum)) { + if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { yield { type: ValueErrorType.NumberMinumum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } } } @@ -228,10 +232,10 @@ export namespace ValueErrors { if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } - if (schema.minProperties !== undefined && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + if (IsNumber(schema.minProperties) && !(globalThis.Object.keys(value).length >= schema.minProperties)) { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } - if (schema.maxProperties !== undefined && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + if (IsNumber(schema.maxProperties) && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } } const propertyKeys = globalThis.Object.keys(schema.properties) @@ -307,10 +311,10 @@ export namespace ValueErrors { if (!(typeof value === 'string')) { return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } } - if (schema.minLength !== undefined && !(value.length >= schema.minLength)) { + if (IsNumber(schema.minLength) && !(value.length >= schema.minLength)) { yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } } - if (schema.maxLength !== undefined && !(value.length <= schema.maxLength)) { + if (IsNumber(schema.maxLength) && !(value.length <= schema.maxLength)) { yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } } if (schema.pattern !== undefined) { @@ -375,10 +379,10 @@ export namespace ValueErrors { return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } } - if (schema.maxByteLength && !(value.length <= schema.maxByteLength)) { + if (IsNumber(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } } - if (schema.minByteLength && !(value.length >= schema.minByteLength)) { + if (IsNumber(schema.minByteLength) && !(value.length >= schema.minByteLength)) { yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } } } From 1a6e3fbe45a440b624f161c515a1737dc34d7828 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 10 Nov 2022 20:37:55 +0900 Subject: [PATCH 032/369] Value Guard Numeric Constraints --- package.json | 2 +- src/compiler/compiler.ts | 53 ++++++++++++++++++++-------------------- src/errors/errors.ts | 2 +- src/value/check.ts | 48 +++++++++++++++++++----------------- 4 files changed, 54 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index be32d047a..a4078398d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.6", + "version": "0.25.7", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9ef44a813..922e504d8 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -108,6 +108,9 @@ export class TypeCompilerUnknownTypeError extends Error { /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { + function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } // ------------------------------------------------------------------- // Types // ------------------------------------------------------------------- @@ -118,9 +121,9 @@ export namespace TypeCompiler { function* Array(schema: Types.TArray, value: string): IterableIterator { const expression = CreateExpression(schema.items, 'value') - if (schema.minItems !== undefined) yield `(${value}.length >= ${schema.minItems})` - if (schema.maxItems !== undefined) yield `(${value}.length <= ${schema.maxItems})` - if (schema.uniqueItems !== undefined) yield `(new Set(${value}).size === ${value}.length)` + if (IsNumber(schema.minItems)) yield `(${value}.length >= ${schema.minItems})` + if (IsNumber(schema.maxItems)) yield `(${value}.length <= ${schema.maxItems})` + if (schema.uniqueItems === true) yield `(new Set(${value}).size === ${value}.length)` yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))` } @@ -134,10 +137,10 @@ export namespace TypeCompiler { function* Date(schema: Types.TDate, value: string): IterableIterator { yield `(${value} instanceof Date)` - if (schema.exclusiveMinimumTimestamp !== undefined) yield `(${value}.getTime() > ${schema.exclusiveMinimumTimestamp})` - if (schema.exclusiveMaximumTimestamp !== undefined) yield `(${value}.getTime() < ${schema.exclusiveMaximumTimestamp})` - if (schema.minimumTimestamp !== undefined) yield `(${value}.getTime() >= ${schema.minimumTimestamp})` - if (schema.maximumTimestamp !== undefined) yield `(${value}.getTime() <= ${schema.maximumTimestamp})` + if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `(${value}.getTime() > ${schema.exclusiveMinimumTimestamp})` + if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `(${value}.getTime() < ${schema.exclusiveMaximumTimestamp})` + if (IsNumber(schema.minimumTimestamp)) yield `(${value}.getTime() >= ${schema.minimumTimestamp})` + if (IsNumber(schema.maximumTimestamp)) yield `(${value}.getTime() <= ${schema.maximumTimestamp})` } function* Function(schema: Types.TFunction, value: string): IterableIterator { @@ -146,11 +149,11 @@ export namespace TypeCompiler { function* Integer(schema: Types.TInteger, value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` - if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` - if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` - if (schema.exclusiveMaximum !== undefined) yield `(${value} < ${schema.exclusiveMaximum})` - if (schema.minimum !== undefined) yield `(${value} >= ${schema.minimum})` - if (schema.maximum !== undefined) yield `(${value} <= ${schema.maximum})` + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` + if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` + if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` + if (IsNumber(schema.minimum)) yield `(${value} >= ${schema.minimum})` + if (IsNumber(schema.maximum)) yield `(${value} <= ${schema.maximum})` } function* Literal(schema: Types.TLiteral, value: string): IterableIterator { @@ -171,17 +174,17 @@ export namespace TypeCompiler { function* Number(schema: Types.TNumber, value: string): IterableIterator { yield `(typeof ${value} === 'number')` - if (schema.multipleOf !== undefined) yield `(${value} % ${schema.multipleOf} === 0)` - if (schema.exclusiveMinimum !== undefined) yield `(${value} > ${schema.exclusiveMinimum})` - if (schema.exclusiveMaximum !== undefined) yield `(${value} < ${schema.exclusiveMaximum})` - if (schema.minimum !== undefined) yield `(${value} >= ${schema.minimum})` - if (schema.maximum !== undefined) yield `(${value} <= ${schema.maximum})` + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` + if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` + if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` + if (IsNumber(schema.minimum)) yield `(${value} >= ${schema.minimum})` + if (IsNumber(schema.maximum)) yield `(${value} <= ${schema.maximum})` } function* Object(schema: Types.TObject, value: string): IterableIterator { yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` - if (schema.minProperties !== undefined) yield `(Object.keys(${value}).length >= ${schema.minProperties})` - if (schema.maxProperties !== undefined) yield `(Object.keys(${value}).length <= ${schema.maxProperties})` + if (IsNumber(schema.minProperties)) yield `(Object.keys(${value}).length >= ${schema.minProperties})` + if (IsNumber(schema.maxProperties)) yield `(Object.keys(${value}).length <= ${schema.maxProperties})` const propertyKeys = globalThis.Object.keys(schema.properties) if (schema.additionalProperties === false) { // Optimization: If the property key length matches the required keys length @@ -242,12 +245,8 @@ export namespace TypeCompiler { function* String(schema: Types.TString, value: string): IterableIterator { yield `(typeof ${value} === 'string')` - if (schema.minLength !== undefined) { - yield `(${value}.length >= ${schema.minLength})` - } - if (schema.maxLength !== undefined) { - yield `(${value}.length <= ${schema.maxLength})` - } + if (IsNumber(schema.minLength)) yield `(${value}.length >= ${schema.minLength})` + if (IsNumber(schema.maxLength)) yield `(${value}.length <= ${schema.maxLength})` if (schema.pattern !== undefined) { const local = PushLocal(`new RegExp(/${schema.pattern}/);`) yield `(${local}.test(${value}))` @@ -278,8 +277,8 @@ export namespace TypeCompiler { function* Uint8Array(schema: Types.TUint8Array, value: string): IterableIterator { yield `(${value} instanceof Uint8Array)` - if (schema.maxByteLength) yield `(${value}.length <= ${schema.maxByteLength})` - if (schema.minByteLength) yield `(${value}.length >= ${schema.minByteLength})` + if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` + if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } function* Unknown(schema: Types.TUnknown, value: string): IterableIterator { diff --git a/src/errors/errors.ts b/src/errors/errors.ts index df1da6817..c7f43e791 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -108,7 +108,7 @@ export class ValueErrorsUnknownTypeError extends Error { /** Provides functionality to generate a sequence of errors against a TypeBox type. */ export namespace ValueErrors { - function IsNumber(value: number | undefined): value is number { + function IsNumber(value: unknown): value is number { return typeof value === 'number' } diff --git a/src/value/check.ts b/src/value/check.ts index 54e0418c3..e17d90cf3 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -36,6 +36,10 @@ export class ValueCheckUnknownTypeError extends Error { } export namespace ValueCheck { + function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } + function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { return true } @@ -43,10 +47,10 @@ export namespace ValueCheck { if (!globalThis.Array.isArray(value)) { return false } - if (schema.minItems !== undefined && !(value.length >= schema.minItems)) { + if (IsNumber(schema.minItems) && !(value.length >= schema.minItems)) { return false } - if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) { + if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { return false } if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { @@ -66,16 +70,16 @@ export namespace ValueCheck { if (!(value instanceof globalThis.Date)) { return false } - if (schema.exclusiveMinimumTimestamp && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { return false } - if (schema.exclusiveMaximumTimestamp && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + if (IsNumber(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { return false } - if (schema.minimumTimestamp && !(value.getTime() >= schema.minimumTimestamp)) { + if (IsNumber(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { return false } - if (schema.maximumTimestamp && !(value.getTime() <= schema.maximumTimestamp)) { + if (IsNumber(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { return false } return true @@ -91,19 +95,19 @@ export namespace ValueCheck { if (!globalThis.Number.isInteger(value)) { return false } - if (schema.multipleOf !== undefined && !(value % schema.multipleOf === 0)) { + if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } - if (schema.exclusiveMinimum !== undefined && !(value > schema.exclusiveMinimum)) { + if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (schema.exclusiveMaximum !== undefined && !(value < schema.exclusiveMaximum)) { + if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } - if (schema.minimum !== undefined && !(value >= schema.minimum)) { + if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { return false } - if (schema.maximum !== undefined && !(value <= schema.maximum)) { + if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { return false } return true @@ -125,19 +129,19 @@ export namespace ValueCheck { if (!(typeof value === 'number')) { return false } - if (schema.multipleOf && !(value % schema.multipleOf === 0)) { + if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } - if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) { + if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) { + if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } - if (schema.minimum && !(value >= schema.minimum)) { + if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { return false } - if (schema.maximum && !(value <= schema.maximum)) { + if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { return false } return true @@ -147,10 +151,10 @@ export namespace ValueCheck { if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { return false } - if (schema.minProperties !== undefined && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + if (IsNumber(schema.minProperties) && !(globalThis.Object.keys(value).length >= schema.minProperties)) { return false } - if (schema.maxProperties !== undefined && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + if (IsNumber(schema.maxProperties) && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { return false } const propertyKeys = globalThis.Object.keys(schema.properties) @@ -227,10 +231,10 @@ export namespace ValueCheck { if (!(typeof value === 'string')) { return false } - if (schema.minLength !== undefined) { + if (IsNumber(schema.minLength)) { if (!(value.length >= schema.minLength)) return false } - if (schema.maxLength !== undefined) { + if (IsNumber(schema.maxLength)) { if (!(value.length <= schema.maxLength)) return false } if (schema.pattern !== undefined) { @@ -276,10 +280,10 @@ export namespace ValueCheck { if (!(value instanceof globalThis.Uint8Array)) { return false } - if (schema.maxByteLength && !(value.length <= schema.maxByteLength)) { + if (IsNumber(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { return false } - if (schema.minByteLength && !(value.length >= schema.minByteLength)) { + if (IsNumber(schema.minByteLength) && !(value.length >= schema.minByteLength)) { return false } return true From 0a34eca379edf2750979c30fda4caaf4470dacdb Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 11 Nov 2022 13:11:57 +0900 Subject: [PATCH 033/369] Specify Submodule Exports --- package.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a4078398d..8d606c612 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.7", + "version": "0.25.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -12,6 +12,15 @@ "license": "MIT", "main": "./typebox.js", "types": "./typebox.d.ts", + "exports": { + "./compiler": "./compiler/index.js", + "./conditional": "./conditional/index.js", + "./errors": "./errors/index.js", + "./format": "./format/index.js", + "./guard": "./guard/index.js", + "./value": "./value/index.js", + ".": "./typebox.js" + }, "repository": { "type": "git", "url": "https://github.com/sinclairzx81/typebox" From 3fb1d37c3393a116d39601c61599913d4a18a3d2 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 16 Nov 2022 13:40:07 +0900 Subject: [PATCH 034/369] Deno Npm Imports --- readme.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 2d1ddcdb1..94ef87319 100644 --- a/readme.md +++ b/readme.md @@ -19,28 +19,45 @@ ## Install -Node - +### npm ```bash $ npm install @sinclair/typebox --save ``` -Deno and ESM +### deno +```typescript +import { Static, Type } from 'npm:@sinclair/typebox' +``` + +### esm ```typescript import { Static, Type } from 'https://esm.sh/@sinclair/typebox' ``` -## Example +## Usage ```typescript import { Static, Type } from '@sinclair/typebox' -const T = Type.String() // const T = { type: 'string' } +const T = Type.Object({ // const T = { + x: Type.Number(), // type: 'object', + y: Type.Number(), // required: ['x', 'y', 'z'], + z: Type.Number() // properties: { +}) // x: { type: 'number' }, + // y: { type: 'number' }, + // z: { type: 'number' } + // } + // } -type T = Static // type T = string +type T = Static // type T = { + // x: number, + // y: number, + // z: number + // } ``` + ## Overview @@ -54,7 +71,7 @@ License MIT ## Contents - [Install](#install) - [Overview](#overview) -- [Usage](#usage) +- [Example](#Example) - [Types](#types) - [Standard Types](#types-standard) - [Extended Types](#types-extended) @@ -89,7 +106,7 @@ License MIT -## Usage +## Example The following demonstrates TypeBox's general usage. From f33423fcf35ce5cfbd6909c45fe8972d88b029da Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 22 Nov 2022 21:53:36 +0900 Subject: [PATCH 035/369] Custom Types (#282) --- .../compression/module/typebox-custom.ts | 4 + changelog.md | 6 + example/index.ts | 1 + package.json | 3 +- readme.md | 128 +++++++++++------- src/compiler/compiler.ts | 29 ++-- src/conditional/structural.ts | 45 +++--- src/custom/custom.ts | 54 ++++++++ src/custom/index.ts | 29 ++++ src/errors/errors.ts | 13 +- src/guard/guard.ts | 17 ++- src/tsconfig.json | 2 +- src/value/cast.ts | 8 +- src/value/check.ts | 12 +- src/value/create.ts | 12 +- test/runtime/compiler/custom.ts | 28 ++++ test/runtime/compiler/index.ts | 1 + test/runtime/errors/index.ts | 1 + test/runtime/index.ts | 1 + test/runtime/value/cast/custom.ts | 64 +++++++++ test/runtime/value/cast/index.ts | 1 + test/runtime/value/check/custom.ts | 28 ++++ test/runtime/value/check/index.ts | 1 + test/runtime/value/create/custom.ts | 18 +++ test/runtime/value/create/index.ts | 1 + tsconfig.json | 3 + 26 files changed, 417 insertions(+), 93 deletions(-) create mode 100644 benchmark/compression/module/typebox-custom.ts create mode 100644 src/custom/custom.ts create mode 100644 src/custom/index.ts create mode 100644 test/runtime/compiler/custom.ts create mode 100644 test/runtime/errors/index.ts create mode 100644 test/runtime/value/cast/custom.ts create mode 100644 test/runtime/value/check/custom.ts create mode 100644 test/runtime/value/create/custom.ts diff --git a/benchmark/compression/module/typebox-custom.ts b/benchmark/compression/module/typebox-custom.ts new file mode 100644 index 000000000..8620c02f1 --- /dev/null +++ b/benchmark/compression/module/typebox-custom.ts @@ -0,0 +1,4 @@ +import { Custom } from 'src/custom/index' +import { Type } from '@sinclair/typebox' + +Custom.Set('custom', (value) => true) diff --git a/changelog.md b/changelog.md index 660e2863f..d4535926a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9) + +Updates: + +- TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })`. + ## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) Updates: diff --git a/example/index.ts b/example/index.ts index 987d926be..97c0303f9 100644 --- a/example/index.ts +++ b/example/index.ts @@ -3,6 +3,7 @@ import { TypeCompiler } from '@sinclair/typebox/compiler' import { Conditional } from '@sinclair/typebox/conditional' import { TypeGuard } from '@sinclair/typebox/guard' import { Format } from '@sinclair/typebox/format' +import { Custom } from '@sinclair/typebox/custom' import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Static, TSchema } from '@sinclair/typebox' diff --git a/package.json b/package.json index 8d606c612..ab27031c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.8", + "version": "0.25.9", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -15,6 +15,7 @@ "exports": { "./compiler": "./compiler/index.js", "./conditional": "./conditional/index.js", + "./custom": "./custom/index.js", "./errors": "./errors/index.js", "./format": "./format/index.js", "./guard": "./guard/index.js", diff --git a/readme.md b/readme.md index 94ef87319..f1a6a94c5 100644 --- a/readme.md +++ b/readme.md @@ -97,6 +97,7 @@ License MIT - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) + - [Custom](#typecheck-custom) - [Formats](#typecheck-formats) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) @@ -1117,6 +1118,30 @@ console.log(C.Code()) // return function check(va // } ``` + + +### Custom + +Use custom module to create a custom types. When creating a custom type you must specify a `Kind` symbol property on the types schema. The `Kind` symbol property should match the name used to register the type. Custom types are used by the Value and TypeCompiler modules only. + +The custom module is an optional import. + +```typescript +import { Custom } from '@sinclair/typebox/custom' +``` + +The following registers a BigInt custom type. + +```typescript +import { Type, Kind } from '@sinclair/typebox' + +Custom.Set('BigInt', value => typeof value === 'bigint') + +const T = Type.Unsafe({ [Kind]: 'BigInt' }) // const T = { [Kind]: 'BigInt' } + +const R = Value.Check(T, 65536n) // const R = true +``` + ### Formats @@ -1163,29 +1188,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 421 ms' │ ' 11 ms' │ ' 38.27 x' │ -│ String │ 2000 │ ' 332 ms' │ ' 11 ms' │ ' 30.18 x' │ -│ Boolean │ 2000 │ ' 291 ms' │ ' 12 ms' │ ' 24.25 x' │ -│ Null │ 2000 │ ' 266 ms' │ ' 9 ms' │ ' 29.56 x' │ -│ RegEx │ 2000 │ ' 486 ms' │ ' 17 ms' │ ' 28.59 x' │ -│ ObjectA │ 2000 │ ' 2791 ms' │ ' 50 ms' │ ' 55.82 x' │ -│ ObjectB │ 2000 │ ' 2896 ms' │ ' 37 ms' │ ' 78.27 x' │ -│ Tuple │ 2000 │ ' 1244 ms' │ ' 21 ms' │ ' 59.24 x' │ -│ Union │ 2000 │ ' 1258 ms' │ ' 25 ms' │ ' 50.32 x' │ -│ Vector4 │ 2000 │ ' 1513 ms' │ ' 21 ms' │ ' 72.05 x' │ -│ Matrix4 │ 2000 │ ' 850 ms' │ ' 11 ms' │ ' 77.27 x' │ -│ Literal_String │ 2000 │ ' 335 ms' │ ' 7 ms' │ ' 47.86 x' │ -│ Literal_Number │ 2000 │ ' 358 ms' │ ' 7 ms' │ ' 51.14 x' │ -│ Literal_Boolean │ 2000 │ ' 356 ms' │ ' 7 ms' │ ' 50.86 x' │ -│ Array_Number │ 2000 │ ' 689 ms' │ ' 9 ms' │ ' 76.56 x' │ -│ Array_String │ 2000 │ ' 728 ms' │ ' 12 ms' │ ' 60.67 x' │ -│ Array_Boolean │ 2000 │ ' 726 ms' │ ' 7 ms' │ ' 103.71 x' │ -│ Array_ObjectA │ 2000 │ ' 3631 ms' │ ' 35 ms' │ ' 103.74 x' │ -│ Array_ObjectB │ 2000 │ ' 3636 ms' │ ' 40 ms' │ ' 90.90 x' │ -│ Array_Tuple │ 2000 │ ' 2119 ms' │ ' 31 ms' │ ' 68.35 x' │ -│ Array_Union │ 2000 │ ' 1550 ms' │ ' 23 ms' │ ' 67.39 x' │ -│ Array_Vector4 │ 2000 │ ' 2200 ms' │ ' 18 ms' │ ' 122.22 x' │ -│ Array_Matrix4 │ 2000 │ ' 1494 ms' │ ' 14 ms' │ ' 106.71 x' │ +│ Number │ 2000 │ ' 427 ms' │ ' 13 ms' │ ' 32.85 x' │ +│ String │ 2000 │ ' 367 ms' │ ' 12 ms' │ ' 30.58 x' │ +│ Boolean │ 2000 │ ' 297 ms' │ ' 13 ms' │ ' 22.85 x' │ +│ Null │ 2000 │ ' 255 ms' │ ' 9 ms' │ ' 28.33 x' │ +│ RegEx │ 2000 │ ' 487 ms' │ ' 17 ms' │ ' 28.65 x' │ +│ ObjectA │ 2000 │ ' 2718 ms' │ ' 53 ms' │ ' 51.28 x' │ +│ ObjectB │ 2000 │ ' 2874 ms' │ ' 36 ms' │ ' 79.83 x' │ +│ Tuple │ 2000 │ ' 1221 ms' │ ' 22 ms' │ ' 55.50 x' │ +│ Union │ 2000 │ ' 1223 ms' │ ' 24 ms' │ ' 50.96 x' │ +│ Vector4 │ 2000 │ ' 1517 ms' │ ' 21 ms' │ ' 72.24 x' │ +│ Matrix4 │ 2000 │ ' 830 ms' │ ' 10 ms' │ ' 83.00 x' │ +│ Literal_String │ 2000 │ ' 334 ms' │ ' 8 ms' │ ' 41.75 x' │ +│ Literal_Number │ 2000 │ ' 357 ms' │ ' 6 ms' │ ' 59.50 x' │ +│ Literal_Boolean │ 2000 │ ' 353 ms' │ ' 7 ms' │ ' 50.43 x' │ +│ Array_Number │ 2000 │ ' 679 ms' │ ' 11 ms' │ ' 61.73 x' │ +│ Array_String │ 2000 │ ' 713 ms' │ ' 10 ms' │ ' 71.30 x' │ +│ Array_Boolean │ 2000 │ ' 710 ms' │ ' 7 ms' │ ' 101.43 x' │ +│ Array_ObjectA │ 2000 │ ' 3571 ms' │ ' 35 ms' │ ' 102.03 x' │ +│ Array_ObjectB │ 2000 │ ' 3567 ms' │ ' 41 ms' │ ' 87.00 x' │ +│ Array_Tuple │ 2000 │ ' 2083 ms' │ ' 19 ms' │ ' 109.63 x' │ +│ Array_Union │ 2000 │ ' 1559 ms' │ ' 23 ms' │ ' 67.78 x' │ +│ Array_Vector4 │ 2000 │ ' 2156 ms' │ ' 19 ms' │ ' 113.47 x' │ +│ Array_Matrix4 │ 2000 │ ' 1483 ms' │ ' 13 ms' │ ' 114.08 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1199,31 +1224,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ -│ String │ 1000000 │ ' 25 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Boolean │ 1000000 │ ' 34 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Null │ 1000000 │ ' 37 ms' │ ' 28 ms' │ ' 10 ms' │ ' 2.80 x' │ -│ RegEx │ 1000000 │ ' 162 ms' │ ' 50 ms' │ ' 37 ms' │ ' 1.35 x' │ -│ ObjectA │ 1000000 │ ' 550 ms' │ ' 38 ms' │ ' 22 ms' │ ' 1.73 x' │ -│ ObjectB │ 1000000 │ ' 1033 ms' │ ' 58 ms' │ ' 38 ms' │ ' 1.53 x' │ -│ Tuple │ 1000000 │ ' 126 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ -│ Union │ 1000000 │ ' 326 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ -│ Recursive │ 1000000 │ ' 3089 ms' │ ' 436 ms' │ ' 101 ms' │ ' 4.32 x' │ -│ Vector4 │ 1000000 │ ' 155 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 579 ms' │ ' 41 ms' │ ' 28 ms' │ ' 1.46 x' │ -│ Literal_String │ 1000000 │ ' 50 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Number │ 1000000 │ ' 25 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ +│ String │ 1000000 │ ' 22 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Boolean │ 1000000 │ ' 21 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Null │ 1000000 │ ' 24 ms' │ ' 21 ms' │ ' 9 ms' │ ' 2.33 x' │ +│ RegEx │ 1000000 │ ' 158 ms' │ ' 46 ms' │ ' 37 ms' │ ' 1.24 x' │ +│ ObjectA │ 1000000 │ ' 566 ms' │ ' 36 ms' │ ' 24 ms' │ ' 1.50 x' │ +│ ObjectB │ 1000000 │ ' 1026 ms' │ ' 52 ms' │ ' 40 ms' │ ' 1.30 x' │ +│ Tuple │ 1000000 │ ' 121 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Union │ 1000000 │ ' 299 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ +│ Recursive │ 1000000 │ ' 3168 ms' │ ' 414 ms' │ ' 97 ms' │ ' 4.27 x' │ +│ Vector4 │ 1000000 │ ' 147 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 573 ms' │ ' 40 ms' │ ' 29 ms' │ ' 1.38 x' │ +│ Literal_String │ 1000000 │ ' 48 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ │ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Array_Number │ 1000000 │ ' 424 ms' │ ' 32 ms' │ ' 19 ms' │ ' 1.68 x' │ -│ Array_String │ 1000000 │ ' 483 ms' │ ' 34 ms' │ ' 28 ms' │ ' 1.21 x' │ -│ Array_Boolean │ 1000000 │ ' 432 ms' │ ' 35 ms' │ ' 26 ms' │ ' 1.35 x' │ -│ Array_ObjectA │ 1000000 │ ' 13895 ms' │ ' 2440 ms' │ ' 1495 ms' │ ' 1.63 x' │ -│ Array_ObjectB │ 1000000 │ ' 16261 ms' │ ' 2633 ms' │ ' 2011 ms' │ ' 1.31 x' │ -│ Array_Tuple │ 1000000 │ ' 1741 ms' │ ' 98 ms' │ ' 66 ms' │ ' 1.48 x' │ -│ Array_Union │ 1000000 │ ' 4825 ms' │ ' 232 ms' │ ' 87 ms' │ ' 2.67 x' │ -│ Array_Recursive │ 1000000 │ ' 54158 ms' │ ' 6966 ms' │ ' 1173 ms' │ ' 5.94 x' │ -│ Array_Vector4 │ 1000000 │ ' 2577 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ -│ Array_Matrix4 │ 1000000 │ ' 12648 ms' │ ' 397 ms' │ ' 249 ms' │ ' 1.59 x' │ +│ Array_Number │ 1000000 │ ' 454 ms' │ ' 32 ms' │ ' 18 ms' │ ' 1.78 x' │ +│ Array_String │ 1000000 │ ' 481 ms' │ ' 32 ms' │ ' 22 ms' │ ' 1.45 x' │ +│ Array_Boolean │ 1000000 │ ' 448 ms' │ ' 34 ms' │ ' 27 ms' │ ' 1.26 x' │ +│ Array_ObjectA │ 1000000 │ ' 13709 ms' │ ' 2360 ms' │ ' 1548 ms' │ ' 1.52 x' │ +│ Array_ObjectB │ 1000000 │ ' 16602 ms' │ ' 2585 ms' │ ' 1904 ms' │ ' 1.36 x' │ +│ Array_Tuple │ 1000000 │ ' 1831 ms' │ ' 96 ms' │ ' 63 ms' │ ' 1.52 x' │ +│ Array_Union │ 1000000 │ ' 4958 ms' │ ' 234 ms' │ ' 86 ms' │ ' 2.72 x' │ +│ Array_Recursive │ 1000000 │ ' 55876 ms' │ ' 7160 ms' │ ' 1138 ms' │ ' 6.29 x' │ +│ Array_Vector4 │ 1000000 │ ' 2381 ms' │ ' 99 ms' │ ' 47 ms' │ ' 2.11 x' │ +│ Array_Matrix4 │ 1000000 │ ' 11670 ms' │ ' 383 ms' │ ' 228 ms' │ ' 1.68 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1237,11 +1262,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 54 kb' │ ' 27 kb' │ '1.97 x' │ -│ typebox/conditional │ ' 44 kb' │ ' 17 kb' │ '2.45 x' │ +│ typebox/compiler │ ' 55 kb' │ ' 27 kb' │ '2.00 x' │ +│ typebox/conditional │ ' 45 kb' │ ' 18 kb' │ '2.44 x' │ +│ typebox/custom │ ' 0 kb' │ ' 0 kb' │ '2.61 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ -│ typebox/guard │ ' 22 kb' │ ' 11 kb' │ '2.05 x' │ -│ typebox/value │ ' 78 kb' │ ' 36 kb' │ '2.13 x' │ +│ typebox/guard │ ' 23 kb' │ ' 11 kb' │ '2.07 x' │ +│ typebox/value │ ' 80 kb' │ ' 37 kb' │ '2.15 x' │ │ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 922e504d8..04d0d2bce 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -29,6 +29,7 @@ THE SOFTWARE. import { ValueErrors, ValueError } from '../errors/index' import { TypeGuard } from '../guard/index' import { Format } from '../format/index' +import { Custom } from '../custom/index' import * as Types from '../typebox' // ------------------------------------------------------------------- @@ -289,6 +290,10 @@ export namespace TypeCompiler { yield `(${value} === null)` } + function* Kind(schema: Types.TSchema, value: string): IterableIterator { + yield `(custom('${schema[Types.Kind]}', ${value}))` + } + function* Visit(schema: T, value: string): IterableIterator { // Reference: Referenced schemas can originate from either additional schemas // or inline in the schema itself. Ideally the recursive path should align to @@ -350,7 +355,8 @@ export namespace TypeCompiler { case 'Void': return yield* Void(anySchema, value) default: - throw new TypeCompilerUnknownTypeError(schema) + if (!Custom.Has(anySchema[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) + return yield* Kind(anySchema, value) } } @@ -419,12 +425,19 @@ export namespace TypeCompiler { export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { TypeGuard.Assert(schema, references) const code = Build(schema, references) - const func1 = globalThis.Function('format', code) - const func2 = func1((format: string, value: string) => { - if (!Format.Has(format)) return false - const func = Format.Get(format)! - return func(value) - }) - return new TypeCheck(schema, references, func2, code) + const compiledFunction = globalThis.Function('custom', 'format', code) + const checkFunction = compiledFunction( + (kind: string, value: unknown) => { + if (!Custom.Has(kind)) return false + const func = Custom.Get(kind)! + return func(value) + }, + (format: string, value: string) => { + if (!Format.Has(format)) return false + const func = Format.Get(format)! + return func(value) + }, + ) + return new TypeCheck(schema, references, checkFunction, code) } } diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts index 62c04ac33..2f0465091 100644 --- a/src/conditional/structural.ts +++ b/src/conditional/structural.ts @@ -51,11 +51,12 @@ export namespace Structural { // Rules // ------------------------------------------------------------------------ - function AnyOrUnknownRule(right: Types.TSchema) { + function AnyUnknownOrCustomRule(right: Types.TSchema) { // https://github.com/microsoft/TypeScript/issues/40049 - if (right[Types.Kind] === 'Union' && right.anyOf.some((schema: Types.TSchema) => schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown')) return true - if (right[Types.Kind] === 'Unknown') return true - if (right[Types.Kind] === 'Any') return true + if (TypeGuard.TUnion(right) && right.anyOf.some((schema: Types.TSchema) => schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown')) return true + if (TypeGuard.TUnknown(right)) return true + if (TypeGuard.TAny(right)) return true + if (TypeGuard.TCustom(right)) throw Error(`Structural: Cannot structurally compare custom type '${right[Types.Kind]}'`) return false } @@ -138,11 +139,11 @@ export namespace Structural { // ------------------------------------------------------------------------ function Any(left: Types.TAny, right: Types.TSchema): StructuralResult { - return AnyOrUnknownRule(right) ? StructuralResult.True : StructuralResult.Union + return AnyUnknownOrCustomRule(right) ? StructuralResult.True : StructuralResult.Union } function Array(left: Types.TArray, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True @@ -163,7 +164,7 @@ export namespace Structural { } function Boolean(left: Types.TBoolean, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -177,7 +178,7 @@ export namespace Structural { } function Constructor(left: Types.TConstructor, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && globalThis.Object.keys(right.properties).length === 0) { return StructuralResult.True @@ -198,7 +199,7 @@ export namespace Structural { } function Date(left: Types.TDate, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -214,7 +215,7 @@ export namespace Structural { } function Function(left: Types.TFunction, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True @@ -236,7 +237,7 @@ export namespace Structural { } function Integer(left: Types.TInteger, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -250,7 +251,7 @@ export namespace Structural { } function Literal(left: Types.TLiteral, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -278,7 +279,7 @@ export namespace Structural { } function Number(left: Types.TNumber, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -294,7 +295,7 @@ export namespace Structural { } function Null(left: Types.TNull, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TNull(right)) { return StructuralResult.True @@ -319,7 +320,7 @@ export namespace Structural { } function Object(left: Types.TObject, right: Types.TAnySchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { return Properties(PropertyMap(left), PropertyMap(right)) @@ -335,7 +336,7 @@ export namespace Structural { } function Promise(left: Types.TPromise, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { if (ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0) { @@ -352,7 +353,7 @@ export namespace Structural { } function Record(left: Types.TRecord, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { if (RecordPattern(left) === '^.*$' && right[Types.Hint] === 'Record') { @@ -394,7 +395,7 @@ export namespace Structural { } function String(left: Types.TString, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -410,7 +411,7 @@ export namespace Structural { } function Tuple(left: Types.TTuple, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right)) { const result = ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0 @@ -442,7 +443,7 @@ export namespace Structural { } function Uint8Array(left: Types.TUint8Array, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { return StructuralResult.True @@ -458,7 +459,7 @@ export namespace Structural { } function Undefined(left: Types.TUndefined, right: Types.TSchema): StructuralResult { - if (AnyOrUnknownRule(right)) { + if (AnyUnknownOrCustomRule(right)) { return StructuralResult.True } else if (TypeGuard.TUndefined(right)) { return StructuralResult.True @@ -562,6 +563,8 @@ export namespace Structural { return Unknown(left, resolvedRight) } else if (TypeGuard.TVoid(left)) { return Void(left, resolvedRight) + } else if (TypeGuard.TCustom(left)) { + throw Error(`Structural: Cannot structurally compare custom type '${left[Types.Kind]}'`) } else { throw Error(`Structural: Unknown left operand '${left[Types.Kind]}'`) } diff --git a/src/custom/custom.ts b/src/custom/custom.ts new file mode 100644 index 000000000..0f9128581 --- /dev/null +++ b/src/custom/custom.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/custom + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type CustomValidationFunction = (value: unknown) => boolean + +/** Provides functions to create custom types */ +export namespace Custom { + const customs = new Map() + + /** Clears all custom types */ + export function Clear() { + return customs.clear() + } + + /** Returns true if this kind exists */ + export function Has(kind: string) { + return customs.has(kind) + } + + /** Sets a validation function for a custom kind */ + export function Set(kind: string, func: CustomValidationFunction) { + customs.set(kind, func) + } + + /** Gets a custom validation function */ + export function Get(kind: string) { + return customs.get(kind) + } +} diff --git a/src/custom/index.ts b/src/custom/index.ts new file mode 100644 index 000000000..5c5b0bf66 --- /dev/null +++ b/src/custom/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/custom + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './custom' diff --git a/src/errors/errors.ts b/src/errors/errors.ts index c7f43e791..f2cd2d00e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { Format } from '../format/index' +import { Custom } from '../custom/index' // ------------------------------------------------------------------- // ValueErrorType @@ -82,6 +83,7 @@ export enum ValueErrorType { Uint8ArrayMinByteLength, Uint8ArrayMaxByteLength, Void, + Custom, } // ------------------------------------------------------------------- @@ -378,7 +380,6 @@ export namespace ValueErrors { if (!(value instanceof globalThis.Uint8Array)) { return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } } - if (IsNumber(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } } @@ -395,6 +396,13 @@ export namespace ValueErrors { } } + function* CustomType(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { + const func = Custom.Get(schema[Types.Kind])! + if (!func(value)) { + return yield { type: ValueErrorType.Custom, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } + } + } + function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { const anyReferences = schema.$id === undefined ? references : [schema, ...references] const anySchema = schema as any @@ -446,7 +454,8 @@ export namespace ValueErrors { case 'Void': return yield* Void(anySchema, anyReferences, path, value) default: - throw new ValueErrorsUnknownTypeError(schema) + if (!Custom.Has(anySchema[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) + return yield* CustomType(anySchema, anyReferences, path, value) } } diff --git a/src/guard/guard.ts b/src/guard/guard.ts index ca61df37d..429e908cb 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -26,11 +26,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Custom } from '../custom/index' import * as Types from '../typebox' -export class TypeGuardInvalidTypeError extends Error { +export class TypeGuardUnknownTypeError extends Error { constructor(public readonly schema: unknown) { - super('TypeGuard: Invalid type') + super('TypeGuard: Unknown type') } } @@ -441,6 +442,11 @@ export namespace TypeGuard { ) } + /** Returns true if the given schema is a registered custom type */ + export function TCustom(schema: unknown): schema is Types.TSchema { + return IsObject(schema) && IsString(schema[Types.Kind]) && Custom.Has(schema[Types.Kind]) + } + /** Returns true if the given schema is TSchema */ export function TSchema(schema: unknown): schema is Types.TSchema { return ( @@ -466,15 +472,16 @@ export namespace TypeGuard { TUnion(schema) || TUint8Array(schema) || TUnknown(schema) || - TVoid(schema) + TVoid(schema) || + TCustom(schema) ) } /** Asserts if this schema and associated references are valid. */ export function Assert(schema: T, references: Types.TSchema[] = []) { - if (!TSchema(schema)) throw new TypeGuardInvalidTypeError(schema) + if (!TSchema(schema)) throw new TypeGuardUnknownTypeError(schema) for (const schema of references) { - if (!TSchema(schema)) throw new TypeGuardInvalidTypeError(schema) + if (!TSchema(schema)) throw new TypeGuardUnknownTypeError(schema) } } } diff --git a/src/tsconfig.json b/src/tsconfig.json index 3bca8a311..c8ce66e6d 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "conditional/index.ts", "format/index.ts", "guard/index.ts", "value/index.ts", "typebox.ts"] + "files": ["compiler/index.ts", "conditional/index.ts", "custom/index.ts", "format/index.ts", "guard/index.ts", "value/index.ts", "typebox.ts"] } diff --git a/src/value/cast.ts b/src/value/cast.ts index 19c7b9133..094cbfd5c 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -30,6 +30,7 @@ import * as Types from '../typebox' import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueClone } from './clone' +import { Custom } from '../custom/index' // ---------------------------------------------------------------------------------------------- // Errors @@ -322,6 +323,10 @@ export namespace ValueCast { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } + function Kind(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + } + export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { const anyReferences = schema.$id === undefined ? references : [schema, ...references] const anySchema = schema as any @@ -375,7 +380,8 @@ export namespace ValueCast { case 'Void': return Void(anySchema, anyReferences, value) default: - throw new ValueCastUnknownTypeError(anySchema) + if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCastUnknownTypeError(anySchema) + return Kind(anySchema, anyReferences, value) } } diff --git a/src/value/check.ts b/src/value/check.ts index e17d90cf3..7c7c8bef9 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -27,7 +27,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { Format } from '../format' +import { Format } from '../format/index' +import { Custom } from '../custom/index' export class ValueCheckUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { @@ -297,6 +298,12 @@ export namespace ValueCheck { return value === null } + function Kind(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { + if (!Custom.Has(schema[Types.Kind])) return false + const func = Custom.Get(schema[Types.Kind])! + return func(value) + } + function Visit(schema: T, references: Types.TSchema[], value: any): boolean { const anyReferences = schema.$id === undefined ? references : [schema, ...references] const anySchema = schema as any @@ -348,7 +355,8 @@ export namespace ValueCheck { case 'Void': return Void(anySchema, anyReferences, value) default: - throw new ValueCheckUnknownTypeError(anySchema) + if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCheckUnknownTypeError(anySchema) + return Kind(anySchema, anyReferences, value) } } diff --git a/src/value/create.ts b/src/value/create.ts index bb742c407..ad67b771d 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' +import { Custom } from '../custom/index' export class ValueCreateUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { @@ -277,6 +278,14 @@ export namespace ValueCreate { return null } + function Kind(schema: Types.TSchema, references: Types.TSchema[]): any { + if (schema.default !== undefined) { + return schema.default + } else { + throw new Error('ValueCreate.Kind: Custom types must specify a default value') + } + } + /** Creates a value from the given schema. If the schema specifies a default value, then that value is returned. */ export function Visit(schema: T, references: Types.TSchema[]): Types.Static { const anyReferences = schema.$id === undefined ? references : [schema, ...references] @@ -332,7 +341,8 @@ export namespace ValueCreate { case 'Void': return Void(anySchema, anyReferences) default: - throw new ValueCreateUnknownTypeError(anySchema) + if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCreateUnknownTypeError(anySchema) + return Kind(anySchema, anyReferences) } } diff --git a/test/runtime/compiler/custom.ts b/test/runtime/compiler/custom.ts new file mode 100644 index 000000000..14ef55e66 --- /dev/null +++ b/test/runtime/compiler/custom.ts @@ -0,0 +1,28 @@ +import { Custom } from '@sinclair/typebox/custom' +import { Type, Kind } from '@sinclair/typebox' +import { ok, fail } from './validate' + +describe('type/compiler/Custom', () => { + Custom.Set('BigInt', (value) => typeof value === 'bigint') + + it('Should validate bigint', () => { + const T = Type.Unsafe({ [Kind]: 'BigInt' }) + ok(T, 1n) + }) + it('Should not validate bigint', () => { + const T = Type.Unsafe({ [Kind]: 'BigInt' }) + fail(T, 1) + }) + it('Should validate bigint nested', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'BigInt' }), + }) + ok(T, { x: 1n }) + }) + it('Should not validate bigint nested', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'BigInt' }), + }) + fail(T, { x: 1 }) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index 01159a601..d06324993 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './custom' import './date' import './enum' import './integer' diff --git a/test/runtime/errors/index.ts b/test/runtime/errors/index.ts new file mode 100644 index 000000000..3443ef957 --- /dev/null +++ b/test/runtime/errors/index.ts @@ -0,0 +1 @@ +// todo: implement once errors are standardized diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 77bb736d2..ea1496280 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -1,5 +1,6 @@ import './compiler/index' import './conditional/index' +import './errors/index' import './format/index' import './guard/index' import './schema/index' diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts new file mode 100644 index 000000000..ceb931198 --- /dev/null +++ b/test/runtime/value/cast/custom.ts @@ -0,0 +1,64 @@ +import { Value } from '@sinclair/typebox/value' +import { Custom } from '@sinclair/typebox/custom' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Custom', () => { + Custom.Set('CustomCast', (value) => value === 'hello' || value === 'world') + const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' }) + const E = 'hello' + + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from boolean', () => { + const value = false + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + + it('Should preserve', () => { + const value = { a: 'hello', b: 'world' } + const result = Value.Cast( + Type.Object({ + a: T, + b: T, + }), + value, + ) + Assert.deepEqual(result, { a: 'hello', b: 'world' }) + }) +}) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 74b79cd8a..58b1e8252 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -2,6 +2,7 @@ import './any' import './array' import './boolean' import './convert' +import './custom' import './date' import './enum' import './integer' diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts new file mode 100644 index 000000000..026b64e60 --- /dev/null +++ b/test/runtime/value/check/custom.ts @@ -0,0 +1,28 @@ +import { Value } from '@sinclair/typebox/value' +import { Custom } from '@sinclair/typebox/custom' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/check/Custom', () => { + Custom.Set('BigInt', (value) => typeof value === 'bigint') + it('Should validate bigint', () => { + const T = Type.Unsafe({ [Kind]: 'BigInt' }) + Assert.deepEqual(Value.Check(T, 1n), true) + }) + it('Should not validate bigint', () => { + const T = Type.Unsafe({ [Kind]: 'BigInt' }) + Assert.deepEqual(Value.Check(T, 1), false) + }) + it('Should validate bigint nested', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'BigInt' }), + }) + Assert.deepEqual(Value.Check(T, { x: 1n }), true) + }) + it('Should not validate bigint nested', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'BigInt' }), + }) + Assert.deepEqual(Value.Check(T, { x: 1 }), false) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 26759b50d..eb99c6eba 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './custom' import './date' import './enum' import './integer' diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts new file mode 100644 index 000000000..2703379fc --- /dev/null +++ b/test/runtime/value/create/custom.ts @@ -0,0 +1,18 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind } from '@sinclair/typebox' +import { Custom } from '@sinclair/typebox/custom' +import { Assert } from '../../assert/index' + +describe('value/create/Custom', () => { + it('Should create custom value with default', () => { + Custom.Set('CustomCreate1', (value) => true) + const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) + + it('Should throw when no default value is specified', () => { + Custom.Set('CustomCreate2', (value) => true) + const T = Type.Unsafe({ [Kind]: 'CustomCreate2' }) + Assert.throws(() => Value.Create(T)) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index db38f47ad..2e06b6f0f 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './custom' import './constructor' import './enum' import './function' diff --git a/tsconfig.json b/tsconfig.json index df793c134..41c97c61c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,9 @@ "@sinclair/typebox/conditional": [ "src/conditional/index.ts" ], + "@sinclair/typebox/custom": [ + "src/custom/index.ts" + ], "@sinclair/typebox/format": [ "src/format/index.ts" ], From 0c1741a179af328c0627c420fe32ed8a70ed7c3d Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 22 Nov 2022 22:09:21 +0900 Subject: [PATCH 036/369] changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index d4535926a..bd439349b 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,7 @@ Updates: -- TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })`. +- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })`. ## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) From 18f892b2ae6e34e9836a88f12578e8b041c021de Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 22 Nov 2022 22:10:35 +0900 Subject: [PATCH 037/369] Changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index bd439349b..d8ffa70a0 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,7 @@ Updates: -- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })`. +- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })` which allow the TypeCompiler and Value API's to make use of user defined validation logic. ## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) From 18017549c8e26748117daab38c74cad962b9e3a2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 25 Nov 2022 14:39:52 +0900 Subject: [PATCH 038/369] Custom Constraints (#283) --- example/index.ts | 4 +- package-lock.json | 46 +++++----- package.json | 8 +- readme.md | 130 ++++++++++++++-------------- src/compiler/compiler.ts | 54 +++++++----- src/conditional/structural.ts | 4 +- src/custom/custom.ts | 16 ++-- src/errors/errors.ts | 6 +- src/format/format.ts | 10 +-- src/guard/guard.ts | 6 +- src/value/cast.ts | 4 +- src/value/check.ts | 8 +- src/value/create.ts | 6 +- test/runtime/compiler/custom.ts | 2 +- test/runtime/value/cast/custom.ts | 2 +- test/runtime/value/check/custom.ts | 6 +- test/runtime/value/create/custom.ts | 4 +- 17 files changed, 165 insertions(+), 151 deletions(-) diff --git a/example/index.ts b/example/index.ts index 97c0303f9..dfde052cf 100644 --- a/example/index.ts +++ b/example/index.ts @@ -5,7 +5,7 @@ import { TypeGuard } from '@sinclair/typebox/guard' import { Format } from '@sinclair/typebox/format' import { Custom } from '@sinclair/typebox/custom' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, Static, TSchema } from '@sinclair/typebox' +import { Type, Kind, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ x: Type.Number(), @@ -15,4 +15,4 @@ const T = Type.Object({ type T = Static -console.log(T) \ No newline at end of file +console.log(T) diff --git a/package-lock.json b/package-lock.json index 9128aeaa5..81dc5039e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,24 @@ { "name": "@sinclair/typebox", - "version": "0.25.1", + "version": "0.25.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.25.1", + "version": "0.25.10", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", - "@types/node": "^18.7.13", - "ajv": "^8.11.0", + "@types/node": "^18.11.9", + "ajv": "^8.11.2", "ajv-formats": "^2.1.1", "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^4.8.2" + "typescript": "^4.9.3" } }, "node_modules/@esbuild/linux-loong64": { @@ -63,9 +63,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.7.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", - "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "node_modules/@ungap/promise-all-settled": { @@ -75,9 +75,9 @@ "dev": true }, "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1410,9 +1410,9 @@ } }, "node_modules/typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1570,9 +1570,9 @@ "dev": true }, "@types/node": { - "version": "18.7.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", - "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", + "version": "18.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", "dev": true }, "@ungap/promise-all-settled": { @@ -1582,9 +1582,9 @@ "dev": true }, "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -2442,9 +2442,9 @@ "dev": true }, "typescript": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", - "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index ab27031c1..6954d63f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.9", + "version": "0.25.10", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -39,12 +39,12 @@ "@sinclair/hammer": "^0.17.1", "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", - "@types/node": "^18.7.13", - "ajv": "^8.11.0", + "@types/node": "^18.11.9", + "ajv": "^8.11.2", "ajv-formats": "^2.1.1", "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^4.8.2" + "typescript": "^4.9.3" } } diff --git a/readme.md b/readme.md index f1a6a94c5..dbbb0b9af 100644 --- a/readme.md +++ b/readme.md @@ -97,8 +97,8 @@ License MIT - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) - - [Custom](#typecheck-custom) - - [Formats](#typecheck-formats) + - [Custom Types](#typecheck-custom-types) + - [Custom Formats](#typecheck-custom-formats) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) - [Validate](#benchmark-validate) @@ -1118,11 +1118,11 @@ console.log(C.Code()) // return function check(va // } ``` - + -### Custom +### Custom Types -Use custom module to create a custom types. When creating a custom type you must specify a `Kind` symbol property on the types schema. The `Kind` symbol property should match the name used to register the type. Custom types are used by the Value and TypeCompiler modules only. +Use the custom module to create user defined types. User defined types require a `[Kind]` symbol property which is used to match the schema against a registered type. Custom types are specific to TypeBox and can only be used with the TypeCompiler and Value modules. The custom module is an optional import. @@ -1130,23 +1130,27 @@ The custom module is an optional import. import { Custom } from '@sinclair/typebox/custom' ``` -The following registers a BigInt custom type. +The following creates a `BigInt` type. ```typescript import { Type, Kind } from '@sinclair/typebox' -Custom.Set('BigInt', value => typeof value === 'bigint') - +Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') +// │ +// └───────────────────┐ The [Kind] is used to match registered type +// │ const T = Type.Unsafe({ [Kind]: 'BigInt' }) // const T = { [Kind]: 'BigInt' } -const R = Value.Check(T, 65536n) // const R = true +const A = TypeCompiler.Compile(T).Check(65536) // const A = false + +const B = Value.Check(T, 65536n) // const B = true ``` - + -### Formats +### Custom Formats -Use the format module to create user defined string formats. The format module is used by the Value and TypeCompiler modules only. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). +Use the format module to create user defined string formats. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). The format module is specific to TypeBox can only be used with the TypeCompiler and Value modules. The format module is an optional import. @@ -1154,7 +1158,7 @@ The format module is an optional import. import { Format } from '@sinclair/typebox/format' ``` -The following creates a `palindrome` string format. +The following creates a palindrome string format. ```typescript Format.Set('palindrome', value => value === value.split('').reverse().join('')) @@ -1174,7 +1178,7 @@ const B = Value.Check(T, 'kayak') // const B = true ## Benchmark -This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.11.0. +This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.11.2. For additional comparative benchmarks, please refer to [typescript-runtime-type-benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/). @@ -1188,29 +1192,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 427 ms' │ ' 13 ms' │ ' 32.85 x' │ -│ String │ 2000 │ ' 367 ms' │ ' 12 ms' │ ' 30.58 x' │ -│ Boolean │ 2000 │ ' 297 ms' │ ' 13 ms' │ ' 22.85 x' │ -│ Null │ 2000 │ ' 255 ms' │ ' 9 ms' │ ' 28.33 x' │ -│ RegEx │ 2000 │ ' 487 ms' │ ' 17 ms' │ ' 28.65 x' │ -│ ObjectA │ 2000 │ ' 2718 ms' │ ' 53 ms' │ ' 51.28 x' │ -│ ObjectB │ 2000 │ ' 2874 ms' │ ' 36 ms' │ ' 79.83 x' │ -│ Tuple │ 2000 │ ' 1221 ms' │ ' 22 ms' │ ' 55.50 x' │ -│ Union │ 2000 │ ' 1223 ms' │ ' 24 ms' │ ' 50.96 x' │ -│ Vector4 │ 2000 │ ' 1517 ms' │ ' 21 ms' │ ' 72.24 x' │ +│ Number │ 2000 │ ' 414 ms' │ ' 14 ms' │ ' 29.57 x' │ +│ String │ 2000 │ ' 326 ms' │ ' 12 ms' │ ' 27.17 x' │ +│ Boolean │ 2000 │ ' 301 ms' │ ' 11 ms' │ ' 27.36 x' │ +│ Null │ 2000 │ ' 253 ms' │ ' 8 ms' │ ' 31.63 x' │ +│ RegEx │ 2000 │ ' 480 ms' │ ' 18 ms' │ ' 26.67 x' │ +│ ObjectA │ 2000 │ ' 2704 ms' │ ' 53 ms' │ ' 51.02 x' │ +│ ObjectB │ 2000 │ ' 2846 ms' │ ' 35 ms' │ ' 81.31 x' │ +│ Tuple │ 2000 │ ' 1244 ms' │ ' 23 ms' │ ' 54.09 x' │ +│ Union │ 2000 │ ' 1222 ms' │ ' 26 ms' │ ' 47.00 x' │ +│ Vector4 │ 2000 │ ' 1522 ms' │ ' 22 ms' │ ' 69.18 x' │ │ Matrix4 │ 2000 │ ' 830 ms' │ ' 10 ms' │ ' 83.00 x' │ -│ Literal_String │ 2000 │ ' 334 ms' │ ' 8 ms' │ ' 41.75 x' │ -│ Literal_Number │ 2000 │ ' 357 ms' │ ' 6 ms' │ ' 59.50 x' │ -│ Literal_Boolean │ 2000 │ ' 353 ms' │ ' 7 ms' │ ' 50.43 x' │ -│ Array_Number │ 2000 │ ' 679 ms' │ ' 11 ms' │ ' 61.73 x' │ -│ Array_String │ 2000 │ ' 713 ms' │ ' 10 ms' │ ' 71.30 x' │ -│ Array_Boolean │ 2000 │ ' 710 ms' │ ' 7 ms' │ ' 101.43 x' │ -│ Array_ObjectA │ 2000 │ ' 3571 ms' │ ' 35 ms' │ ' 102.03 x' │ -│ Array_ObjectB │ 2000 │ ' 3567 ms' │ ' 41 ms' │ ' 87.00 x' │ -│ Array_Tuple │ 2000 │ ' 2083 ms' │ ' 19 ms' │ ' 109.63 x' │ -│ Array_Union │ 2000 │ ' 1559 ms' │ ' 23 ms' │ ' 67.78 x' │ -│ Array_Vector4 │ 2000 │ ' 2156 ms' │ ' 19 ms' │ ' 113.47 x' │ -│ Array_Matrix4 │ 2000 │ ' 1483 ms' │ ' 13 ms' │ ' 114.08 x' │ +│ Literal_String │ 2000 │ ' 348 ms' │ ' 9 ms' │ ' 38.67 x' │ +│ Literal_Number │ 2000 │ ' 356 ms' │ ' 9 ms' │ ' 39.56 x' │ +│ Literal_Boolean │ 2000 │ ' 353 ms' │ ' 6 ms' │ ' 58.83 x' │ +│ Array_Number │ 2000 │ ' 686 ms' │ ' 11 ms' │ ' 62.36 x' │ +│ Array_String │ 2000 │ ' 722 ms' │ ' 11 ms' │ ' 65.64 x' │ +│ Array_Boolean │ 2000 │ ' 724 ms' │ ' 9 ms' │ ' 80.44 x' │ +│ Array_ObjectA │ 2000 │ ' 3706 ms' │ ' 36 ms' │ ' 102.94 x' │ +│ Array_ObjectB │ 2000 │ ' 3678 ms' │ ' 38 ms' │ ' 96.79 x' │ +│ Array_Tuple │ 2000 │ ' 2217 ms' │ ' 23 ms' │ ' 96.39 x' │ +│ Array_Union │ 2000 │ ' 1654 ms' │ ' 24 ms' │ ' 68.92 x' │ +│ Array_Vector4 │ 2000 │ ' 2283 ms' │ ' 20 ms' │ ' 114.15 x' │ +│ Array_Matrix4 │ 2000 │ ' 1567 ms' │ ' 19 ms' │ ' 82.47 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1224,31 +1228,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 25 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ -│ String │ 1000000 │ ' 22 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Boolean │ 1000000 │ ' 21 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Null │ 1000000 │ ' 24 ms' │ ' 21 ms' │ ' 9 ms' │ ' 2.33 x' │ -│ RegEx │ 1000000 │ ' 158 ms' │ ' 46 ms' │ ' 37 ms' │ ' 1.24 x' │ -│ ObjectA │ 1000000 │ ' 566 ms' │ ' 36 ms' │ ' 24 ms' │ ' 1.50 x' │ -│ ObjectB │ 1000000 │ ' 1026 ms' │ ' 52 ms' │ ' 40 ms' │ ' 1.30 x' │ -│ Tuple │ 1000000 │ ' 121 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Union │ 1000000 │ ' 299 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ -│ Recursive │ 1000000 │ ' 3168 ms' │ ' 414 ms' │ ' 97 ms' │ ' 4.27 x' │ -│ Vector4 │ 1000000 │ ' 147 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 573 ms' │ ' 40 ms' │ ' 29 ms' │ ' 1.38 x' │ -│ Literal_String │ 1000000 │ ' 48 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Literal_Number │ 1000000 │ ' 46 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Array_Number │ 1000000 │ ' 454 ms' │ ' 32 ms' │ ' 18 ms' │ ' 1.78 x' │ -│ Array_String │ 1000000 │ ' 481 ms' │ ' 32 ms' │ ' 22 ms' │ ' 1.45 x' │ -│ Array_Boolean │ 1000000 │ ' 448 ms' │ ' 34 ms' │ ' 27 ms' │ ' 1.26 x' │ -│ Array_ObjectA │ 1000000 │ ' 13709 ms' │ ' 2360 ms' │ ' 1548 ms' │ ' 1.52 x' │ -│ Array_ObjectB │ 1000000 │ ' 16602 ms' │ ' 2585 ms' │ ' 1904 ms' │ ' 1.36 x' │ -│ Array_Tuple │ 1000000 │ ' 1831 ms' │ ' 96 ms' │ ' 63 ms' │ ' 1.52 x' │ -│ Array_Union │ 1000000 │ ' 4958 ms' │ ' 234 ms' │ ' 86 ms' │ ' 2.72 x' │ -│ Array_Recursive │ 1000000 │ ' 55876 ms' │ ' 7160 ms' │ ' 1138 ms' │ ' 6.29 x' │ -│ Array_Vector4 │ 1000000 │ ' 2381 ms' │ ' 99 ms' │ ' 47 ms' │ ' 2.11 x' │ -│ Array_Matrix4 │ 1000000 │ ' 11670 ms' │ ' 383 ms' │ ' 228 ms' │ ' 1.68 x' │ +│ Number │ 1000000 │ ' 33 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ +│ String │ 1000000 │ ' 26 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ +│ Boolean │ 1000000 │ ' 24 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Null │ 1000000 │ ' 28 ms' │ ' 24 ms' │ ' 11 ms' │ ' 2.18 x' │ +│ RegEx │ 1000000 │ ' 171 ms' │ ' 56 ms' │ ' 41 ms' │ ' 1.37 x' │ +│ ObjectA │ 1000000 │ ' 614 ms' │ ' 42 ms' │ ' 24 ms' │ ' 1.75 x' │ +│ ObjectB │ 1000000 │ ' 1109 ms' │ ' 59 ms' │ ' 45 ms' │ ' 1.31 x' │ +│ Tuple │ 1000000 │ ' 122 ms' │ ' 25 ms' │ ' 13 ms' │ ' 1.92 x' │ +│ Union │ 1000000 │ ' 316 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ +│ Recursive │ 1000000 │ ' 3219 ms' │ ' 453 ms' │ ' 128 ms' │ ' 3.54 x' │ +│ Vector4 │ 1000000 │ ' 145 ms' │ ' 26 ms' │ ' 13 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 537 ms' │ ' 42 ms' │ ' 28 ms' │ ' 1.50 x' │ +│ Literal_String │ 1000000 │ ' 51 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Literal_Number │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Literal_Boolean │ 1000000 │ ' 49 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Array_Number │ 1000000 │ ' 438 ms' │ ' 35 ms' │ ' 19 ms' │ ' 1.84 x' │ +│ Array_String │ 1000000 │ ' 468 ms' │ ' 33 ms' │ ' 23 ms' │ ' 1.43 x' │ +│ Array_Boolean │ 1000000 │ ' 448 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ +│ Array_ObjectA │ 1000000 │ ' 13904 ms' │ ' 2536 ms' │ ' 1530 ms' │ ' 1.66 x' │ +│ Array_ObjectB │ 1000000 │ ' 16188 ms' │ ' 2724 ms' │ ' 1834 ms' │ ' 1.49 x' │ +│ Array_Tuple │ 1000000 │ ' 1725 ms' │ ' 98 ms' │ ' 64 ms' │ ' 1.53 x' │ +│ Array_Union │ 1000000 │ ' 4762 ms' │ ' 237 ms' │ ' 87 ms' │ ' 2.72 x' │ +│ Array_Recursive │ 1000000 │ ' 54754 ms' │ ' 7707 ms' │ ' 1152 ms' │ ' 6.69 x' │ +│ Array_Vector4 │ 1000000 │ ' 2485 ms' │ ' 100 ms' │ ' 48 ms' │ ' 2.08 x' │ +│ Array_Matrix4 │ 1000000 │ ' 13116 ms' │ ' 387 ms' │ ' 232 ms' │ ' 1.67 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1262,12 +1266,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 55 kb' │ ' 27 kb' │ '2.00 x' │ +│ typebox/compiler │ ' 56 kb' │ ' 28 kb' │ '2.01 x' │ │ typebox/conditional │ ' 45 kb' │ ' 18 kb' │ '2.44 x' │ │ typebox/custom │ ' 0 kb' │ ' 0 kb' │ '2.61 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ │ typebox/guard │ ' 23 kb' │ ' 11 kb' │ '2.07 x' │ -│ typebox/value │ ' 80 kb' │ ' 37 kb' │ '2.15 x' │ +│ typebox/value │ ' 80 kb' │ ' 37 kb' │ '2.16 x' │ │ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 04d0d2bce..0164c7c3a 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -233,9 +233,9 @@ export namespace TypeCompiler { // Reference: If we have seen this reference before we can just yield and return // the function call. If this isn't the case we defer to visit to generate and // set the function for subsequent passes. Consider for refactor. - if (names.has(schema.$ref)) return yield `(${CreateFunctionName(schema.$ref)}(${value}))` - if (!referenceMap.has(schema.$ref)) throw Error(`TypeCompiler.Ref: Cannot de-reference schema with $id '${schema.$ref}'`) - const reference = referenceMap.get(schema.$ref)! + if (state_local_function_names.has(schema.$ref)) return yield `(${CreateFunctionName(schema.$ref)}(${value}))` + if (!state_reference_map.has(schema.$ref)) throw Error(`TypeCompiler.Ref: Cannot de-reference schema with $id '${schema.$ref}'`) + const reference = state_reference_map.get(schema.$ref)! yield* Visit(reference, value) } @@ -290,16 +290,18 @@ export namespace TypeCompiler { yield `(${value} === null)` } - function* Kind(schema: Types.TSchema, value: string): IterableIterator { - yield `(custom('${schema[Types.Kind]}', ${value}))` + function* UserDefined(schema: Types.TSchema, value: string): IterableIterator { + const schema_key = `schema_key_${state_remote_custom_types.size}` + state_remote_custom_types.set(schema_key, schema) + yield `(custom('${schema[Types.Kind]}', '${schema_key}', ${value}))` } function* Visit(schema: T, value: string): IterableIterator { // Reference: Referenced schemas can originate from either additional schemas // or inline in the schema itself. Ideally the recursive path should align to // reference path. Consider for refactor. - if (schema.$id && !names.has(schema.$id)) { - names.add(schema.$id) + if (schema.$id && !state_local_function_names.has(schema.$id)) { + state_local_function_names.add(schema.$id) const name = CreateFunctionName(schema.$id) const body = CreateFunction(name, schema, 'value') PushFunction(body) @@ -356,29 +358,31 @@ export namespace TypeCompiler { return yield* Void(anySchema, value) default: if (!Custom.Has(anySchema[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) - return yield* Kind(anySchema, value) + return yield* UserDefined(anySchema, value) } } // ------------------------------------------------------------------- - // Compile State + // Compiler State // ------------------------------------------------------------------- - const referenceMap = new Map() - const locals = new Set() // local variables and functions - const names = new Set() // cache of local functions + const state_reference_map = new Map() // tracks schemas with identifiers + const state_local_variables = new Set() // local variables and functions + const state_local_function_names = new Set() // local function names used call ref validators + const state_remote_custom_types = new Map() // remote custom types used during compilation function ResetCompiler() { - referenceMap.clear() - locals.clear() - names.clear() + state_reference_map.clear() + state_local_variables.clear() + state_local_function_names.clear() + state_remote_custom_types.clear() } function AddReferences(schemas: Types.TSchema[] = []) { for (const schema of schemas) { if (!schema.$id) throw new Error(`TypeCompiler: Referenced schemas must specify an $id.`) - if (referenceMap.has(schema.$id)) throw new Error(`TypeCompiler: Duplicate schema $id found for '${schema.$id}'`) - referenceMap.set(schema.$id, schema) + if (state_reference_map.has(schema.$id)) throw new Error(`TypeCompiler: Duplicate schema $id found for '${schema.$id}'`) + state_reference_map.set(schema.$id, schema) } } @@ -396,17 +400,17 @@ export namespace TypeCompiler { } function PushFunction(functionBody: string) { - locals.add(functionBody) + state_local_variables.add(functionBody) } function PushLocal(expression: string) { - const local = `local_${locals.size}` - locals.add(`const ${local} = ${expression}`) + const local = `local_${state_local_variables.size}` + state_local_variables.add(`const ${local} = ${expression}`) return local } function GetLocals() { - return [...locals.values()] + return [...state_local_variables.values()] } // ------------------------------------------------------------------- @@ -425,12 +429,14 @@ export namespace TypeCompiler { export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { TypeGuard.Assert(schema, references) const code = Build(schema, references) + const custom_schemas = new Map(state_remote_custom_types) const compiledFunction = globalThis.Function('custom', 'format', code) const checkFunction = compiledFunction( - (kind: string, value: unknown) => { - if (!Custom.Has(kind)) return false + (kind: string, schema_key: string, value: unknown) => { + if (!Custom.Has(kind) || !custom_schemas.has(schema_key)) return false + const schema = custom_schemas.get(schema_key)! const func = Custom.Get(kind)! - return func(value) + return func(schema, value) }, (format: string, value: string) => { if (!Format.Has(format)) return false diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts index 2f0465091..323c761ab 100644 --- a/src/conditional/structural.ts +++ b/src/conditional/structural.ts @@ -56,7 +56,7 @@ export namespace Structural { if (TypeGuard.TUnion(right) && right.anyOf.some((schema: Types.TSchema) => schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown')) return true if (TypeGuard.TUnknown(right)) return true if (TypeGuard.TAny(right)) return true - if (TypeGuard.TCustom(right)) throw Error(`Structural: Cannot structurally compare custom type '${right[Types.Kind]}'`) + if (TypeGuard.TUserDefined(right)) throw Error(`Structural: Cannot structurally compare custom type '${right[Types.Kind]}'`) return false } @@ -563,7 +563,7 @@ export namespace Structural { return Unknown(left, resolvedRight) } else if (TypeGuard.TVoid(left)) { return Void(left, resolvedRight) - } else if (TypeGuard.TCustom(left)) { + } else if (TypeGuard.TUserDefined(left)) { throw Error(`Structural: Cannot structurally compare custom type '${left[Types.Kind]}'`) } else { throw Error(`Structural: Unknown left operand '${left[Types.Kind]}'`) diff --git a/src/custom/custom.ts b/src/custom/custom.ts index 0f9128581..3dfc8ae8f 100644 --- a/src/custom/custom.ts +++ b/src/custom/custom.ts @@ -26,28 +26,28 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export type CustomValidationFunction = (value: unknown) => boolean +export type CustomValidationFunction = (schema: TSchema, value: unknown) => boolean -/** Provides functions to create custom types */ +/** Provides functions to create user defined types */ export namespace Custom { - const customs = new Map() + const customs = new Map>() - /** Clears all custom types */ + /** Clears all user defined types */ export function Clear() { return customs.clear() } - /** Returns true if this kind exists */ + /** Returns true if this user defined type exists */ export function Has(kind: string) { return customs.has(kind) } - /** Sets a validation function for a custom kind */ - export function Set(kind: string, func: CustomValidationFunction) { + /** Sets a validation function for a user defined type */ + export function Set(kind: string, func: CustomValidationFunction) { customs.set(kind, func) } - /** Gets a custom validation function */ + /** Gets a custom validation function for a user defined type */ export function Get(kind: string) { return customs.get(kind) } diff --git a/src/errors/errors.ts b/src/errors/errors.ts index f2cd2d00e..f6f7cb159 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -396,9 +396,9 @@ export namespace ValueErrors { } } - function* CustomType(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { + function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { const func = Custom.Get(schema[Types.Kind])! - if (!func(value)) { + if (!func(schema, value)) { return yield { type: ValueErrorType.Custom, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } } } @@ -455,7 +455,7 @@ export namespace ValueErrors { return yield* Void(anySchema, anyReferences, path, value) default: if (!Custom.Has(anySchema[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) - return yield* CustomType(anySchema, anyReferences, path, value) + return yield* UserDefined(anySchema, anyReferences, path, value) } } diff --git a/src/format/format.ts b/src/format/format.ts index 544009a7f..c185ab00b 100644 --- a/src/format/format.ts +++ b/src/format/format.ts @@ -28,26 +28,26 @@ THE SOFTWARE. export type FormatValidationFunction = (value: string) => boolean -/** Provides functions to create custom string formats */ +/** Provides functions to create user defined string formats */ export namespace Format { const formats = new Map() - /** Clears all formats */ + /** Clears all user defined string formats */ export function Clear() { return formats.clear() } - /** Returns true if the string format exists */ + /** Returns true if the user defined string format exists */ export function Has(format: string) { return formats.has(format) } - /** Sets a string format validation function */ + /** Sets a validation function for a user defined string format */ export function Set(format: string, func: FormatValidationFunction) { formats.set(format, func) } - /** Gets a string format validation function */ + /** Gets a validation function for a user defined string format */ export function Get(format: string) { return formats.get(format) } diff --git a/src/guard/guard.ts b/src/guard/guard.ts index 429e908cb..c6904f0b6 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -442,8 +442,8 @@ export namespace TypeGuard { ) } - /** Returns true if the given schema is a registered custom type */ - export function TCustom(schema: unknown): schema is Types.TSchema { + /** Returns true if the given schema is a registered user defined type */ + export function TUserDefined(schema: unknown): schema is Types.TSchema { return IsObject(schema) && IsString(schema[Types.Kind]) && Custom.Has(schema[Types.Kind]) } @@ -473,7 +473,7 @@ export namespace TypeGuard { TUint8Array(schema) || TUnknown(schema) || TVoid(schema) || - TCustom(schema) + TUserDefined(schema) ) } diff --git a/src/value/cast.ts b/src/value/cast.ts index 094cbfd5c..17d72e375 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -323,7 +323,7 @@ export namespace ValueCast { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Kind(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } @@ -381,7 +381,7 @@ export namespace ValueCast { return Void(anySchema, anyReferences, value) default: if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCastUnknownTypeError(anySchema) - return Kind(anySchema, anyReferences, value) + return UserDefined(anySchema, anyReferences, value) } } diff --git a/src/value/check.ts b/src/value/check.ts index 7c7c8bef9..16fe2ad8e 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -32,7 +32,7 @@ import { Custom } from '../custom/index' export class ValueCheckUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { - super('ValueCheck: Unknown type') + super(`ValueCheck: ${schema[Types.Kind] ? `Unknown type '${schema[Types.Kind]}'` : 'Unknown type'}`) } } @@ -298,10 +298,10 @@ export namespace ValueCheck { return value === null } - function Kind(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { + function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { if (!Custom.Has(schema[Types.Kind])) return false const func = Custom.Get(schema[Types.Kind])! - return func(value) + return func(schema, value) } function Visit(schema: T, references: Types.TSchema[], value: any): boolean { @@ -356,7 +356,7 @@ export namespace ValueCheck { return Void(anySchema, anyReferences, value) default: if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCheckUnknownTypeError(anySchema) - return Kind(anySchema, anyReferences, value) + return UserDefined(anySchema, anyReferences, value) } } diff --git a/src/value/create.ts b/src/value/create.ts index ad67b771d..ab4d33ad0 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -278,11 +278,11 @@ export namespace ValueCreate { return null } - function Kind(schema: Types.TSchema, references: Types.TSchema[]): any { + function UserDefined(schema: Types.TSchema, references: Types.TSchema[]): any { if (schema.default !== undefined) { return schema.default } else { - throw new Error('ValueCreate.Kind: Custom types must specify a default value') + throw new Error('ValueCreate.UserDefined: User defined types must specify a default value') } } @@ -342,7 +342,7 @@ export namespace ValueCreate { return Void(anySchema, anyReferences) default: if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCreateUnknownTypeError(anySchema) - return Kind(anySchema, anyReferences) + return UserDefined(anySchema, anyReferences) } } diff --git a/test/runtime/compiler/custom.ts b/test/runtime/compiler/custom.ts index 14ef55e66..1a42cdd8c 100644 --- a/test/runtime/compiler/custom.ts +++ b/test/runtime/compiler/custom.ts @@ -3,7 +3,7 @@ import { Type, Kind } from '@sinclair/typebox' import { ok, fail } from './validate' describe('type/compiler/Custom', () => { - Custom.Set('BigInt', (value) => typeof value === 'bigint') + Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts index ceb931198..cbdd0085e 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/custom.ts @@ -4,7 +4,7 @@ import { Type, Kind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Custom', () => { - Custom.Set('CustomCast', (value) => value === 'hello' || value === 'world') + Custom.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' }) const E = 'hello' diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts index 026b64e60..8518802fd 100644 --- a/test/runtime/value/check/custom.ts +++ b/test/runtime/value/check/custom.ts @@ -4,21 +4,25 @@ import { Type, Kind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/check/Custom', () => { - Custom.Set('BigInt', (value) => typeof value === 'bigint') + Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') + it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) Assert.deepEqual(Value.Check(T, 1n), true) }) + it('Should not validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) Assert.deepEqual(Value.Check(T, 1), false) }) + it('Should validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), }) Assert.deepEqual(Value.Check(T, { x: 1n }), true) }) + it('Should not validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts index 2703379fc..90ca03152 100644 --- a/test/runtime/value/create/custom.ts +++ b/test/runtime/value/create/custom.ts @@ -5,13 +5,13 @@ import { Assert } from '../../assert/index' describe('value/create/Custom', () => { it('Should create custom value with default', () => { - Custom.Set('CustomCreate1', (value) => true) + Custom.Set('CustomCreate1', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) Assert.deepEqual(Value.Create(T), 'hello') }) it('Should throw when no default value is specified', () => { - Custom.Set('CustomCreate2', (value) => true) + Custom.Set('CustomCreate2', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate2' }) Assert.throws(() => Value.Create(T)) }) From a7f2d536f5397ecd502b837b1eb16ca262162d77 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 25 Nov 2022 14:43:42 +0900 Subject: [PATCH 039/369] Documentation --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index dbbb0b9af..0d4b885df 100644 --- a/readme.md +++ b/readme.md @@ -1137,8 +1137,8 @@ import { Type, Kind } from '@sinclair/typebox' Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') // │ -// └───────────────────┐ The [Kind] is used to match registered type -// │ +// └────────────────────────────┐ The [Kind] is used to match registered type +// │ const T = Type.Unsafe({ [Kind]: 'BigInt' }) // const T = { [Kind]: 'BigInt' } const A = TypeCompiler.Compile(T).Check(65536) // const A = false From 6407167c24d18555872715fb4fc12f53b3085bdb Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 25 Nov 2022 14:48:31 +0900 Subject: [PATCH 040/369] Changelog --- changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index d8ffa70a0..e07572ea2 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## [0.25.10](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.10) + +Updates: + +- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance which may include additional constraints (such as options) that may be used in the validation process. `Custom.Set('', (schema, value) => { ... })`. + ## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9) Updates: From a77d13616718d1e8fd9f30ccd8ece9be5b1aac35 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 25 Nov 2022 14:48:34 +0900 Subject: [PATCH 041/369] Changelog --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index e07572ea2..bd34becd7 100644 --- a/changelog.md +++ b/changelog.md @@ -2,7 +2,7 @@ Updates: -- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance which may include additional constraints (such as options) that may be used in the validation process. `Custom.Set('', (schema, value) => { ... })`. +- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance. The schema instance may include additional constraints (such as options) that may be used during the validation process. `Custom.Set('', (schema, value) => { ... })`. ## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9) From c465464ea03d49d25a3cbbe02708e20548304f3d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 7 Dec 2022 02:54:58 +0900 Subject: [PATCH 042/369] Value Hash (#286) --- benchmark/compression/module/typebox-hash.ts | 3 + changelog.md | 6 + example/index.ts | 2 +- package.json | 3 +- readme.md | 114 ++++++----- src/compiler/compiler.ts | 10 +- src/errors/errors.ts | 4 +- src/hash/hash.ts | 193 +++++++++++++++++++ src/hash/index.ts | 29 +++ src/tsconfig.json | 2 +- src/value/check.ts | 5 +- src/value/index.ts | 1 + src/value/value.ts | 18 +- test/runtime/compiler/array.ts | 11 +- test/runtime/hash/hash.ts | 94 +++++++++ test/runtime/hash/index.ts | 1 + test/runtime/index.ts | 1 + test/runtime/schema/array.ts | 11 +- test/runtime/value/check/array.ts | 33 ++++ tsconfig.json | 3 + 20 files changed, 475 insertions(+), 69 deletions(-) create mode 100644 benchmark/compression/module/typebox-hash.ts create mode 100644 src/hash/hash.ts create mode 100644 src/hash/index.ts create mode 100644 test/runtime/hash/hash.ts create mode 100644 test/runtime/hash/index.ts diff --git a/benchmark/compression/module/typebox-hash.ts b/benchmark/compression/module/typebox-hash.ts new file mode 100644 index 000000000..94c5787e1 --- /dev/null +++ b/benchmark/compression/module/typebox-hash.ts @@ -0,0 +1,3 @@ +import { ValueHash } from '@sinclair/typebox/hash' + +const H = ValueHash.Create(1) diff --git a/changelog.md b/changelog.md index bd34becd7..8ab7ca510 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## [0.25.11](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.11) + +Updates: + +- [286](https://github.com/sinclairzx81/typebox/pull/286) implements a FNV1A-64 non cryptographic hashing function in TypeBox. This function should not be used in place of cryptographic hashing functions, rather it's purpose is to provide relatively fast, hashing mechanism to assist with checks for arrays with uniqueItems constraints, specifically for cases where the array may contains reference values (such as objects, arrays, Dates and Uint8Array). This function is provided via `Value.Hash()` for convenience as the hash may be useful to generate a numeric identifier for values (with some considerations to React array rendering in absence of key or identifier) + ## [0.25.10](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.10) Updates: diff --git a/example/index.ts b/example/index.ts index dfde052cf..dee703c07 100644 --- a/example/index.ts +++ b/example/index.ts @@ -15,4 +15,4 @@ const T = Type.Object({ type T = Static -console.log(T) +console.log(T) \ No newline at end of file diff --git a/package.json b/package.json index 6954d63f2..31deefffa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.10", + "version": "0.25.11", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -19,6 +19,7 @@ "./errors": "./errors/index.js", "./format": "./format/index.js", "./guard": "./guard/index.js", + "./hash": "./hash/index.js", "./value": "./value/index.js", ".": "./typebox.js" }, diff --git a/readme.md b/readme.md index 0d4b885df..660ae6100 100644 --- a/readme.md +++ b/readme.md @@ -90,6 +90,7 @@ License MIT - [Check](#values-check) - [Cast](#values-cast) - [Equal](#values-equal) + - [Hash](#values-hash) - [Diff](#values-diff) - [Patch](#values-patch) - [Errors](#values-errors) @@ -843,6 +844,18 @@ const R = Value.Equal( // const R = true ) ``` + + +### Hash + +Use the Hash function to create a [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) non cryptographic hash of a value. + +```typescript +const A = Value.Hash({ x: 1, y: 2, z: 3 }) // const A = 2910466848807138541n + +const B = Value.Hash({ x: 1, y: 4, z: 3 }) // const B = 1418369778807423581n +``` + ### Diff @@ -1192,29 +1205,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 414 ms' │ ' 14 ms' │ ' 29.57 x' │ -│ String │ 2000 │ ' 326 ms' │ ' 12 ms' │ ' 27.17 x' │ -│ Boolean │ 2000 │ ' 301 ms' │ ' 11 ms' │ ' 27.36 x' │ -│ Null │ 2000 │ ' 253 ms' │ ' 8 ms' │ ' 31.63 x' │ -│ RegEx │ 2000 │ ' 480 ms' │ ' 18 ms' │ ' 26.67 x' │ -│ ObjectA │ 2000 │ ' 2704 ms' │ ' 53 ms' │ ' 51.02 x' │ -│ ObjectB │ 2000 │ ' 2846 ms' │ ' 35 ms' │ ' 81.31 x' │ -│ Tuple │ 2000 │ ' 1244 ms' │ ' 23 ms' │ ' 54.09 x' │ -│ Union │ 2000 │ ' 1222 ms' │ ' 26 ms' │ ' 47.00 x' │ -│ Vector4 │ 2000 │ ' 1522 ms' │ ' 22 ms' │ ' 69.18 x' │ -│ Matrix4 │ 2000 │ ' 830 ms' │ ' 10 ms' │ ' 83.00 x' │ -│ Literal_String │ 2000 │ ' 348 ms' │ ' 9 ms' │ ' 38.67 x' │ -│ Literal_Number │ 2000 │ ' 356 ms' │ ' 9 ms' │ ' 39.56 x' │ -│ Literal_Boolean │ 2000 │ ' 353 ms' │ ' 6 ms' │ ' 58.83 x' │ -│ Array_Number │ 2000 │ ' 686 ms' │ ' 11 ms' │ ' 62.36 x' │ -│ Array_String │ 2000 │ ' 722 ms' │ ' 11 ms' │ ' 65.64 x' │ -│ Array_Boolean │ 2000 │ ' 724 ms' │ ' 9 ms' │ ' 80.44 x' │ -│ Array_ObjectA │ 2000 │ ' 3706 ms' │ ' 36 ms' │ ' 102.94 x' │ -│ Array_ObjectB │ 2000 │ ' 3678 ms' │ ' 38 ms' │ ' 96.79 x' │ -│ Array_Tuple │ 2000 │ ' 2217 ms' │ ' 23 ms' │ ' 96.39 x' │ -│ Array_Union │ 2000 │ ' 1654 ms' │ ' 24 ms' │ ' 68.92 x' │ -│ Array_Vector4 │ 2000 │ ' 2283 ms' │ ' 20 ms' │ ' 114.15 x' │ -│ Array_Matrix4 │ 2000 │ ' 1567 ms' │ ' 19 ms' │ ' 82.47 x' │ +│ Number │ 2000 │ ' 415 ms' │ ' 14 ms' │ ' 29.64 x' │ +│ String │ 2000 │ ' 335 ms' │ ' 12 ms' │ ' 27.92 x' │ +│ Boolean │ 2000 │ ' 295 ms' │ ' 13 ms' │ ' 22.69 x' │ +│ Null │ 2000 │ ' 255 ms' │ ' 8 ms' │ ' 31.88 x' │ +│ RegEx │ 2000 │ ' 482 ms' │ ' 17 ms' │ ' 28.35 x' │ +│ ObjectA │ 2000 │ ' 2700 ms' │ ' 55 ms' │ ' 49.09 x' │ +│ ObjectB │ 2000 │ ' 2871 ms' │ ' 36 ms' │ ' 79.75 x' │ +│ Tuple │ 2000 │ ' 1229 ms' │ ' 22 ms' │ ' 55.86 x' │ +│ Union │ 2000 │ ' 1216 ms' │ ' 26 ms' │ ' 46.77 x' │ +│ Vector4 │ 2000 │ ' 1575 ms' │ ' 25 ms' │ ' 63.00 x' │ +│ Matrix4 │ 2000 │ ' 908 ms' │ ' 13 ms' │ ' 69.85 x' │ +│ Literal_String │ 2000 │ ' 353 ms' │ ' 9 ms' │ ' 39.22 x' │ +│ Literal_Number │ 2000 │ ' 371 ms' │ ' 7 ms' │ ' 53.00 x' │ +│ Literal_Boolean │ 2000 │ ' 361 ms' │ ' 8 ms' │ ' 45.13 x' │ +│ Array_Number │ 2000 │ ' 694 ms' │ ' 7 ms' │ ' 99.14 x' │ +│ Array_String │ 2000 │ ' 778 ms' │ ' 8 ms' │ ' 97.25 x' │ +│ Array_Boolean │ 2000 │ ' 735 ms' │ ' 7 ms' │ ' 105.00 x' │ +│ Array_ObjectA │ 2000 │ ' 3732 ms' │ ' 44 ms' │ ' 84.82 x' │ +│ Array_ObjectB │ 2000 │ ' 3624 ms' │ ' 40 ms' │ ' 90.60 x' │ +│ Array_Tuple │ 2000 │ ' 2101 ms' │ ' 15 ms' │ ' 140.07 x' │ +│ Array_Union │ 2000 │ ' 1480 ms' │ ' 25 ms' │ ' 59.20 x' │ +│ Array_Vector4 │ 2000 │ ' 2175 ms' │ ' 18 ms' │ ' 120.83 x' │ +│ Array_Matrix4 │ 2000 │ ' 1516 ms' │ ' 13 ms' │ ' 116.62 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1228,31 +1241,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 33 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ -│ String │ 1000000 │ ' 26 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ -│ Boolean │ 1000000 │ ' 24 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Null │ 1000000 │ ' 28 ms' │ ' 24 ms' │ ' 11 ms' │ ' 2.18 x' │ -│ RegEx │ 1000000 │ ' 171 ms' │ ' 56 ms' │ ' 41 ms' │ ' 1.37 x' │ -│ ObjectA │ 1000000 │ ' 614 ms' │ ' 42 ms' │ ' 24 ms' │ ' 1.75 x' │ -│ ObjectB │ 1000000 │ ' 1109 ms' │ ' 59 ms' │ ' 45 ms' │ ' 1.31 x' │ -│ Tuple │ 1000000 │ ' 122 ms' │ ' 25 ms' │ ' 13 ms' │ ' 1.92 x' │ -│ Union │ 1000000 │ ' 316 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ -│ Recursive │ 1000000 │ ' 3219 ms' │ ' 453 ms' │ ' 128 ms' │ ' 3.54 x' │ -│ Vector4 │ 1000000 │ ' 145 ms' │ ' 26 ms' │ ' 13 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 537 ms' │ ' 42 ms' │ ' 28 ms' │ ' 1.50 x' │ -│ Literal_String │ 1000000 │ ' 51 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Literal_Number │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Literal_Boolean │ 1000000 │ ' 49 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Array_Number │ 1000000 │ ' 438 ms' │ ' 35 ms' │ ' 19 ms' │ ' 1.84 x' │ -│ Array_String │ 1000000 │ ' 468 ms' │ ' 33 ms' │ ' 23 ms' │ ' 1.43 x' │ -│ Array_Boolean │ 1000000 │ ' 448 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ -│ Array_ObjectA │ 1000000 │ ' 13904 ms' │ ' 2536 ms' │ ' 1530 ms' │ ' 1.66 x' │ -│ Array_ObjectB │ 1000000 │ ' 16188 ms' │ ' 2724 ms' │ ' 1834 ms' │ ' 1.49 x' │ -│ Array_Tuple │ 1000000 │ ' 1725 ms' │ ' 98 ms' │ ' 64 ms' │ ' 1.53 x' │ -│ Array_Union │ 1000000 │ ' 4762 ms' │ ' 237 ms' │ ' 87 ms' │ ' 2.72 x' │ -│ Array_Recursive │ 1000000 │ ' 54754 ms' │ ' 7707 ms' │ ' 1152 ms' │ ' 6.69 x' │ -│ Array_Vector4 │ 1000000 │ ' 2485 ms' │ ' 100 ms' │ ' 48 ms' │ ' 2.08 x' │ -│ Array_Matrix4 │ 1000000 │ ' 13116 ms' │ ' 387 ms' │ ' 232 ms' │ ' 1.67 x' │ +│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ +│ String │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Boolean │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Null │ 1000000 │ ' 25 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ RegEx │ 1000000 │ ' 167 ms' │ ' 48 ms' │ ' 38 ms' │ ' 1.26 x' │ +│ ObjectA │ 1000000 │ ' 591 ms' │ ' 36 ms' │ ' 22 ms' │ ' 1.64 x' │ +│ ObjectB │ 1000000 │ ' 1168 ms' │ ' 61 ms' │ ' 37 ms' │ ' 1.65 x' │ +│ Tuple │ 1000000 │ ' 127 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ +│ Union │ 1000000 │ ' 340 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ +│ Recursive │ 1000000 │ ' 3174 ms' │ ' 432 ms' │ ' 100 ms' │ ' 4.32 x' │ +│ Vector4 │ 1000000 │ ' 142 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 577 ms' │ ' 40 ms' │ ' 28 ms' │ ' 1.43 x' │ +│ Literal_String │ 1000000 │ ' 47 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ +│ Literal_Boolean │ 1000000 │ ' 45 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ +│ Array_Number │ 1000000 │ ' 432 ms' │ ' 33 ms' │ ' 19 ms' │ ' 1.74 x' │ +│ Array_String │ 1000000 │ ' 462 ms' │ ' 32 ms' │ ' 22 ms' │ ' 1.45 x' │ +│ Array_Boolean │ 1000000 │ ' 455 ms' │ ' 32 ms' │ ' 26 ms' │ ' 1.23 x' │ +│ Array_ObjectA │ 1000000 │ ' 14595 ms' │ ' 2306 ms' │ ' 1496 ms' │ ' 1.54 x' │ +│ Array_ObjectB │ 1000000 │ ' 17310 ms' │ ' 2625 ms' │ ' 2105 ms' │ ' 1.25 x' │ +│ Array_Tuple │ 1000000 │ ' 1794 ms' │ ' 97 ms' │ ' 66 ms' │ ' 1.47 x' │ +│ Array_Union │ 1000000 │ ' 5085 ms' │ ' 246 ms' │ ' 91 ms' │ ' 2.70 x' │ +│ Array_Recursive │ 1000000 │ ' 59747 ms' │ ' 7866 ms' │ ' 1136 ms' │ ' 6.92 x' │ +│ Array_Vector4 │ 1000000 │ ' 2375 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ +│ Array_Matrix4 │ 1000000 │ ' 13115 ms' │ ' 385 ms' │ ' 246 ms' │ ' 1.57 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1266,12 +1279,13 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 56 kb' │ ' 28 kb' │ '2.01 x' │ +│ typebox/compiler │ ' 61 kb' │ ' 30 kb' │ '2.03 x' │ │ typebox/conditional │ ' 45 kb' │ ' 18 kb' │ '2.44 x' │ │ typebox/custom │ ' 0 kb' │ ' 0 kb' │ '2.61 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ │ typebox/guard │ ' 23 kb' │ ' 11 kb' │ '2.07 x' │ -│ typebox/value │ ' 80 kb' │ ' 37 kb' │ '2.16 x' │ +│ typebox/hash │ ' 4 kb' │ ' 1 kb' │ '2.27 x' │ +│ typebox/value │ ' 85 kb' │ ' 39 kb' │ '2.16 x' │ │ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 0164c7c3a..011e12d45 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -30,6 +30,7 @@ import { ValueErrors, ValueError } from '../errors/index' import { TypeGuard } from '../guard/index' import { Format } from '../format/index' import { Custom } from '../custom/index' +import { ValueHash } from '../hash/index' import * as Types from '../typebox' // ------------------------------------------------------------------- @@ -122,10 +123,10 @@ export namespace TypeCompiler { function* Array(schema: Types.TArray, value: string): IterableIterator { const expression = CreateExpression(schema.items, 'value') + yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))` if (IsNumber(schema.minItems)) yield `(${value}.length >= ${schema.minItems})` if (IsNumber(schema.maxItems)) yield `(${value}.length <= ${schema.maxItems})` - if (schema.uniqueItems === true) yield `(new Set(${value}).size === ${value}.length)` - yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))` + if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` } function* Boolean(schema: Types.TBoolean, value: string): IterableIterator { @@ -430,7 +431,7 @@ export namespace TypeCompiler { TypeGuard.Assert(schema, references) const code = Build(schema, references) const custom_schemas = new Map(state_remote_custom_types) - const compiledFunction = globalThis.Function('custom', 'format', code) + const compiledFunction = globalThis.Function('custom', 'format', 'hash', code) const checkFunction = compiledFunction( (kind: string, schema_key: string, value: unknown) => { if (!Custom.Has(kind) || !custom_schemas.has(schema_key)) return false @@ -443,6 +444,9 @@ export namespace TypeCompiler { const func = Format.Get(format)! return func(value) }, + (value: unknown) => { + return ValueHash.Create(value) + }, ) return new TypeCheck(schema, references, checkFunction, code) } diff --git a/src/errors/errors.ts b/src/errors/errors.ts index f6f7cb159..d3407418e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -29,6 +29,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { Format } from '../format/index' import { Custom } from '../custom/index' +import { ValueHash } from '../hash/index' // ------------------------------------------------------------------- // ValueErrorType @@ -126,7 +127,8 @@ export namespace ValueErrors { if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } } - if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { + // prettier-ignore + if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Create(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } } for (let i = 0; i < value.length; i++) { diff --git a/src/hash/hash.ts b/src/hash/hash.ts new file mode 100644 index 000000000..f1955cd54 --- /dev/null +++ b/src/hash/hash.ts @@ -0,0 +1,193 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/hash + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export class ValueHashError extends Error { + constructor(public readonly value: unknown) { + super(`Hash: Unable to hash value`) + } +} + +export namespace ValueHash { + enum ByteMarker { + Undefined, + Null, + Boolean, + Number, + String, + Object, + Array, + Date, + Uint8Array, + } + + // ---------------------------------------------------- + // State + // ---------------------------------------------------- + + let Hash = 14695981039346656037n + const [Prime, Size] = [1099511628211n, 2n ** 64n] + const Bytes = globalThis.Array.from({ length: 256 }).map((_, i) => globalThis.BigInt(i)) + const F64 = new globalThis.Float64Array(1) + const F64In = new globalThis.DataView(F64.buffer) + const F64Out = new globalThis.Uint8Array(F64.buffer) + + // ---------------------------------------------------- + // Guards + // ---------------------------------------------------- + + function IsDate(value: unknown): value is Date { + return value instanceof globalThis.Date + } + + function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof globalThis.Uint8Array + } + + function IsArray(value: unknown): value is Array { + return globalThis.Array.isArray(value) + } + + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + + function IsNull(value: unknown): value is null { + return value === null + } + + function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } + + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !IsArray(value) && !IsDate(value) && !IsUint8Array(value) + } + + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + + function IsUndefined(value: unknown): value is undefined { + return value === undefined + } + + // ---------------------------------------------------- + // Encoding + // ---------------------------------------------------- + + function Array(value: Array) { + Fnv1A64(ByteMarker.Array) + for (const item of value) { + Visit(item) + } + } + + function Boolean(value: boolean) { + Fnv1A64(ByteMarker.Boolean) + Fnv1A64(value ? 1 : 0) + } + + function Date(value: Date) { + Fnv1A64(ByteMarker.Date) + Visit(value.getTime()) + } + + function Null(value: null) { + Fnv1A64(ByteMarker.Null) + } + + function Number(value: number) { + Fnv1A64(ByteMarker.Number) + F64In.setFloat64(0, value) + for (const byte of F64Out) { + Fnv1A64(byte) + } + } + + function Object(value: Record) { + Fnv1A64(ByteMarker.Object) + for (const key of globalThis.Object.keys(value).sort()) { + Visit(key) + Visit(value[key]) + } + } + + function String(value: string) { + Fnv1A64(ByteMarker.String) + for (let i = 0; i < value.length; i++) { + Fnv1A64(value.charCodeAt(i)) + } + } + + function Uint8Array(value: Uint8Array) { + Fnv1A64(ByteMarker.Uint8Array) + for (let i = 0; i < value.length; i++) { + Fnv1A64(value[i]) + } + } + + function Undefined(value: undefined) { + return Fnv1A64(ByteMarker.Undefined) + } + + function Visit(value: any) { + if (IsArray(value)) { + Array(value) + } else if (IsBoolean(value)) { + Boolean(value) + } else if (IsDate(value)) { + Date(value) + } else if (IsNull(value)) { + Null(value) + } else if (IsNumber(value)) { + Number(value) + } else if (IsObject(value)) { + Object(value) + } else if (IsString(value)) { + String(value) + } else if (IsUint8Array(value)) { + Uint8Array(value) + } else if (IsUndefined(value)) { + Undefined(value) + } else { + throw new ValueHashError(value) + } + } + + function Fnv1A64(byte: number) { + Hash = Hash ^ Bytes[byte] + Hash = (Hash * Prime) % Size + } + + /** Creates a FNV1A-64 non cryptographic hash of the given value */ + export function Create(value: unknown) { + Hash = 14695981039346656037n + Visit(value) + return Hash + } +} diff --git a/src/hash/index.ts b/src/hash/index.ts new file mode 100644 index 000000000..d6597c698 --- /dev/null +++ b/src/hash/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/hash + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './hash' diff --git a/src/tsconfig.json b/src/tsconfig.json index c8ce66e6d..52cb52269 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "conditional/index.ts", "custom/index.ts", "format/index.ts", "guard/index.ts", "value/index.ts", "typebox.ts"] + "files": ["compiler/index.ts", "conditional/index.ts", "custom/index.ts", "format/index.ts", "guard/index.ts", "hash/index.ts", "value/index.ts", "typebox.ts"] } diff --git a/src/value/check.ts b/src/value/check.ts index 16fe2ad8e..8ceca89e5 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -29,6 +29,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { Format } from '../format/index' import { Custom } from '../custom/index' +import { ValueHash } from '../hash/index' export class ValueCheckUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { @@ -44,6 +45,7 @@ export namespace ValueCheck { function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { return true } + function Array(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { if (!globalThis.Array.isArray(value)) { return false @@ -54,7 +56,8 @@ export namespace ValueCheck { if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { return false } - if (schema.uniqueItems === true && !(new Set(value).size === value.length)) { + // prettier-ignore + if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Create(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { return false } return value.every((val) => Visit(schema.items, references, val)) diff --git a/src/value/index.ts b/src/value/index.ts index f96cee562..b962dac8c 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -27,5 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export { ValueError, ValueErrorType } from '../errors/index' +export { Edit, Insert, Update, Delete } from './delta' export * from './pointer' export * from './value' diff --git a/src/value/value.ts b/src/value/value.ts index 48aab1500..1e36736d0 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { ValueErrors, ValueError } from '../errors/index' +import { ValueHash } from '../hash/index' import { ValueEqual } from './equal' import { ValueCast } from './cast' import { ValueClone } from './clone' @@ -35,8 +36,6 @@ import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueDelta, Edit } from './delta' -export { Edit, Insert, Update, Delete } from './delta' - /** Provides functions to perform structural updates to JavaScript values */ export namespace Value { /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ @@ -66,6 +65,11 @@ export namespace Value { return ValueCheck.Check(schema, references, value) } + /** Returns a structural clone of the given value */ + export function Clone(value: T): T { + return ValueClone.Clone(value) + } + /** Returns an iterator for each error in this value. */ export function Errors(schema: T, references: [...R], value: unknown): IterableIterator /** Returns an iterator for each error in this value. */ @@ -80,16 +84,16 @@ export namespace Value { return ValueEqual.Equal(left, right) } - /** Returns a structural clone of the given value */ - export function Clone(value: T): T { - return ValueClone.Clone(value) - } - /** Returns edits to transform the current value into the next value */ export function Diff(current: unknown, next: unknown): Edit[] { return ValueDelta.Diff(current, next) } + /** Returns a FNV1A-64 non cryptographic hash of the given value */ + export function Hash(value: unknown): bigint { + return ValueHash.Create(value) + } + /** Returns a new value with edits applied to the given value */ export function Patch(current: unknown, edits: Edit[]): T { return ValueDelta.Patch(current, edits) as T diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index f74b7b4d6..61fc8171c 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -83,12 +83,19 @@ describe('type/compiler/Array', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) fail(T, [0, 1, 2, 3]) }) - it('Should validate array with uniqueItems when items are references', () => { + it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) ok(T, [ { x: 0, y: 1 }, { x: 1, y: 0 }, - ]) // references distinct + ]) + }) + it('Should not validate array with uniqueItems when items are not distinct objects', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + fail(T, [ + { x: 1, y: 0 }, + { x: 1, y: 0 }, + ]) }) it('Should not validate array with non uniqueItems', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) diff --git a/test/runtime/hash/hash.ts b/test/runtime/hash/hash.ts new file mode 100644 index 000000000..dd508fce4 --- /dev/null +++ b/test/runtime/hash/hash.ts @@ -0,0 +1,94 @@ +import { ValueHash } from '@sinclair/typebox/hash' +import { Assert } from '../assert/index' + +describe('Hash', () => { + it('Should hash number', () => { + Assert.equal('bigint', typeof ValueHash.Create(1)) + const A = ValueHash.Create(1) + const B = ValueHash.Create(2) + Assert.notEqual(A, B) + }) + + it('Should hash string', () => { + Assert.equal('bigint', typeof ValueHash.Create('hello')) + const A = ValueHash.Create('hello') + const B = ValueHash.Create('world') + Assert.notEqual(A, B) + }) + + it('Should hash boolean', () => { + Assert.equal('bigint', typeof ValueHash.Create(true)) + Assert.equal('bigint', typeof ValueHash.Create(false)) + const A = ValueHash.Create(true) + const B = ValueHash.Create(false) + Assert.notEqual(A, B) + }) + + it('Should not hash bigint', () => { + Assert.throws(() => ValueHash.Create(1n)) + }) + + it('Should hash null', () => { + Assert.equal('bigint', typeof ValueHash.Create(null)) + const A = ValueHash.Create(null) + const B = ValueHash.Create(undefined) + Assert.notEqual(A, B) + }) + + it('Should hash array', () => { + Assert.equal('bigint', typeof ValueHash.Create([0, 1, 2, 3])) + const A = ValueHash.Create([0, 1, 2, 3]) + const B = ValueHash.Create([0, 2, 2, 3]) + Assert.notEqual(A, B) + }) + + it('Should hash object 1', () => { + // prettier-ignore + Assert.equal('bigint', typeof ValueHash.Create({ x: 1, y: 2 })) + const A = ValueHash.Create({ x: 1, y: 2 }) + const B = ValueHash.Create({ x: 2, y: 2 }) + Assert.notEqual(A, B) + }) + it('Should hash object 2', () => { + const A = ValueHash.Create({ x: 1, y: [1, 2] }) + const B = ValueHash.Create({ x: 1, y: [1, 3] }) + Assert.notEqual(A, B) + }) + it('Should hash object 3', () => { + const A = ValueHash.Create({ x: 1, y: undefined }) + const B = ValueHash.Create({ x: 1 }) + Assert.notEqual(A, B) + }) + it('Should hash object 4', () => { + const A = ValueHash.Create({ x: 1, y: new Uint8Array([0, 1, 2]) }) + const B = ValueHash.Create({ x: 1, y: [0, 1, 2] }) + Assert.notEqual(A, B) + }) + + it('Should hash object 5', () => { + const A = ValueHash.Create({ x: 1, y: undefined }) + const B = ValueHash.Create({ x: 2, y: undefined }) + Assert.notEqual(A, B) + }) + + it('Should hash Date', () => { + Assert.equal('bigint', typeof ValueHash.Create(new Date())) + const A = ValueHash.Create(new Date(1)) + const B = ValueHash.Create(new Date(2)) + Assert.notEqual(A, B) + }) + + it('Should hash Uint8Array', () => { + Assert.equal('bigint', typeof ValueHash.Create(new Uint8Array([0, 1, 2, 3]))) + const A = ValueHash.Create(new Uint8Array([0, 1, 2, 3])) + const B = ValueHash.Create(new Uint8Array([0, 2, 2, 3])) + Assert.notEqual(A, B) + }) + + it('Should hash undefined', () => { + Assert.equal('bigint', typeof ValueHash.Create(undefined)) + const A = ValueHash.Create(undefined) + const B = ValueHash.Create(null) + Assert.notEqual(A, B) + }) +}) diff --git a/test/runtime/hash/index.ts b/test/runtime/hash/index.ts new file mode 100644 index 000000000..50fbad4e5 --- /dev/null +++ b/test/runtime/hash/index.ts @@ -0,0 +1 @@ +import './hash' diff --git a/test/runtime/index.ts b/test/runtime/index.ts index ea1496280..f5ed2d8cd 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -3,5 +3,6 @@ import './conditional/index' import './errors/index' import './format/index' import './guard/index' +import './hash/index' import './schema/index' import './value/index' diff --git a/test/runtime/schema/array.ts b/test/runtime/schema/array.ts index bc3d1b1cf..57df3662a 100644 --- a/test/runtime/schema/array.ts +++ b/test/runtime/schema/array.ts @@ -82,12 +82,19 @@ describe('type/schema/Array', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) fail(T, [0, 1, 2, 3]) }) - it('Should validate array with uniqueItems when items are references', () => { + it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) ok(T, [ { x: 0, y: 1 }, { x: 1, y: 0 }, - ]) // references distinct + ]) + }) + it('Should not validate array with uniqueItems when items are not distinct objects', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + fail(T, [ + { x: 1, y: 0 }, + { x: 1, y: 0 }, + ]) }) it('Should not validate array with non uniqueItems', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index 909b093c6..64f3f3806 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -30,9 +30,42 @@ describe('value/check/Array', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { const value = new Date() const result = Value.Check(Type.Array(Type.Any()), value) Assert.equal(result, false) }) + + it('Should validate array with unique primitive items', () => { + const T = Type.Array(Type.Number(), { uniqueItems: true }) + const result = Value.Check(T, [0, 1, 2]) + Assert.equal(result, true) + }) + + it('Should not validate array with non-unique primitive items', () => { + const T = Type.Array(Type.Number(), { uniqueItems: true }) + const result = Value.Check(T, [0, 0, 2]) + Assert.equal(result, false) + }) + + it('Should validate array with unique object items', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + const result = Value.Check(T, [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 }, + ]) + Assert.equal(result, true) + }) + + it('Should not validate array with non-unique object items', () => { + const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) + const result = Value.Check(T, [ + { x: 1, y: 1 }, + { x: 1, y: 1 }, + { x: 3, y: 3 }, + ]) + Assert.equal(result, false) + }) }) diff --git a/tsconfig.json b/tsconfig.json index 41c97c61c..c1619dc92 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,9 @@ "@sinclair/typebox/guard": [ "src/guard/index.ts" ], + "@sinclair/typebox/hash": [ + "src/hash/index.ts" + ], "@sinclair/typebox/value": [ "src/value/index.ts" ], From 7dcdea9e7d59c103a39fe4e732767c2177912a54 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 9 Dec 2022 19:41:10 +0900 Subject: [PATCH 043/369] Examples IntersectAllOf and UnionOneOf --- example/custom/index.ts | 2 ++ example/custom/intersectAllOf.ts | 21 +++++++++++++++++++++ example/custom/unionOneOf.ts | 17 +++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 example/custom/index.ts create mode 100644 example/custom/intersectAllOf.ts create mode 100644 example/custom/unionOneOf.ts diff --git a/example/custom/index.ts b/example/custom/index.ts new file mode 100644 index 000000000..7f9f50c3c --- /dev/null +++ b/example/custom/index.ts @@ -0,0 +1,2 @@ +export * from './intersectAllOf' +export * from './unionOneOf' \ No newline at end of file diff --git a/example/custom/intersectAllOf.ts b/example/custom/intersectAllOf.ts new file mode 100644 index 000000000..4b8e24efe --- /dev/null +++ b/example/custom/intersectAllOf.ts @@ -0,0 +1,21 @@ +import { Kind, TSchema, SchemaOptions, IntersectReduce, IntersectEvaluate } from '@sinclair/typebox' +import { Custom } from '@sinclair/typebox/custom' +import { Value } from '@sinclair/typebox/value' + +export interface IntersectAllOfOptions extends SchemaOptions { + unevaluatedProperties?: boolean +} + +export interface IntersectAllOf extends TSchema, IntersectAllOfOptions { + [Kind]: 'IntersectAllOf', + static: IntersectReduce> + allOf: T +} + +/** Creates a Intersect type with a allOf schema representation */ +export function IntersectAllOf(allOf: [...T], options: IntersectAllOfOptions = {}) { + if(!Custom.Has('IntersectAllOf')) Custom.Set('IntersectAllOf', (schema: { allOf: TSchema[] }, value) => { + return schema.allOf.every(schema => Value.Check(schema, value)) + }) + return { ...options, [Kind]: 'IntersectAllOf', allOf } as IntersectAllOf +} \ No newline at end of file diff --git a/example/custom/unionOneOf.ts b/example/custom/unionOneOf.ts new file mode 100644 index 000000000..0db14c59d --- /dev/null +++ b/example/custom/unionOneOf.ts @@ -0,0 +1,17 @@ +import { Kind, TSchema, SchemaOptions, Static } from '@sinclair/typebox' +import { Custom } from '@sinclair/typebox/custom' +import { Value } from '@sinclair/typebox/value' + +export interface UnionOneOf extends TSchema { + [Kind]: 'UnionOneOf', + static: { [K in keyof T]: Static }[number] + oneOf: T +} + +/** Creates a Union type with a oneOf schema representation */ +export function UnionOneOf(oneOf: [...T], options: SchemaOptions = {}) { + if (!Custom.Has('UnionOneOf')) Custom.Set('UnionOneOf', (schema: { oneOf: TSchema[] }, value) => { + return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) + }) + return { [Kind]: 'UnionOneOf', oneOf } as UnionOneOf +} From 581ec5de076f5ce921dee08e456118441eb3ac66 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 9 Dec 2022 21:04:48 +0900 Subject: [PATCH 044/369] Use BigInt Constructor (#287) --- example/index.ts | 2 +- package.json | 2 +- src/hash/hash.ts | 6 +++--- src/value/cast.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/index.ts b/example/index.ts index dee703c07..dfde052cf 100644 --- a/example/index.ts +++ b/example/index.ts @@ -15,4 +15,4 @@ const T = Type.Object({ type T = Static -console.log(T) \ No newline at end of file +console.log(T) diff --git a/package.json b/package.json index 31deefffa..834e2b71a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.11", + "version": "0.25.12", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/hash/hash.ts b/src/hash/hash.ts index f1955cd54..3d0ac21b6 100644 --- a/src/hash/hash.ts +++ b/src/hash/hash.ts @@ -49,8 +49,8 @@ export namespace ValueHash { // State // ---------------------------------------------------- - let Hash = 14695981039346656037n - const [Prime, Size] = [1099511628211n, 2n ** 64n] + let Hash = globalThis.BigInt('14695981039346656037') + const [Prime, Size] = [globalThis.BigInt('1099511628211'), globalThis.BigInt('2') ** globalThis.BigInt('64')] const Bytes = globalThis.Array.from({ length: 256 }).map((_, i) => globalThis.BigInt(i)) const F64 = new globalThis.Float64Array(1) const F64In = new globalThis.DataView(F64.buffer) @@ -186,7 +186,7 @@ export namespace ValueHash { /** Creates a FNV1A-64 non cryptographic hash of the given value */ export function Create(value: unknown) { - Hash = 14695981039346656037n + Hash = globalThis.BigInt('14695981039346656037') Visit(value) return Hash } diff --git a/src/value/cast.ts b/src/value/cast.ts index 17d72e375..5abe5f60c 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -146,11 +146,11 @@ export namespace ValueCast { } function IsValueTrue(value: unknown): value is true { - return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === 1n) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) + return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === globalThis.BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) } function IsValueFalse(value: unknown): value is true { - return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === 0n) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) + return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === globalThis.BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) } // ---------------------------------------------------------------------------------------------- From 31d927baf39b510351128dd8951fcd32dc2395ef Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 10 Dec 2022 17:05:09 +0900 Subject: [PATCH 045/369] Documentation --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 660ae6100..acb9d9283 100644 --- a/readme.md +++ b/readme.md @@ -73,8 +73,8 @@ License MIT - [Overview](#overview) - [Example](#Example) - [Types](#types) - - [Standard Types](#types-standard) - - [Extended Types](#types-extended) + - [Standard](#types-standard) + - [Extended](#types-extended) - [Modifiers](#types-modifiers) - [Options](#types-options) - [Reference](#types-reference) @@ -189,7 +189,7 @@ TypeBox provides a set of functions that allow you to compose JSON Schema simila -### Standard Types +### Standard The following table lists the Standard TypeBox types. @@ -396,7 +396,7 @@ The following table lists the Standard TypeBox types. -### Extended Types +### Extended TypeBox provides a set of extended types that can be used to express schematics for core JavaScript constructs and primitives. Extended types are not valid JSON Schema and will not validate using typical validation. These types however can be used to frame JSON schema and describe callable RPC interfaces that may receive JSON validated data. From 9989fc784ed31bf880ab6d12cae8dee108dc6ad5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 11 Dec 2022 17:12:13 +0900 Subject: [PATCH 046/369] Assert Numeric for NaN (#288) --- example/index.ts | 2 +- package.json | 2 +- readme.md | 96 ++++++++++++++--------------- src/compiler/compiler.ts | 5 +- src/errors/errors.ts | 6 +- src/guard/guard.ts | 2 +- src/value/cast.ts | 2 +- src/value/check.ts | 7 ++- test/runtime/compiler/integer.ts | 4 ++ test/runtime/compiler/number.ts | 4 ++ test/runtime/schema/integer.ts | 4 ++ test/runtime/schema/number.ts | 4 ++ test/runtime/value/check/integer.ts | 6 ++ test/runtime/value/check/number.ts | 5 ++ 14 files changed, 89 insertions(+), 60 deletions(-) diff --git a/example/index.ts b/example/index.ts index dfde052cf..dee703c07 100644 --- a/example/index.ts +++ b/example/index.ts @@ -15,4 +15,4 @@ const T = Type.Object({ type T = Static -console.log(T) +console.log(T) \ No newline at end of file diff --git a/package.json b/package.json index 834e2b71a..bc74862d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.12", + "version": "0.25.13", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index acb9d9283..94bc7374b 100644 --- a/readme.md +++ b/readme.md @@ -1205,29 +1205,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 415 ms' │ ' 14 ms' │ ' 29.64 x' │ -│ String │ 2000 │ ' 335 ms' │ ' 12 ms' │ ' 27.92 x' │ -│ Boolean │ 2000 │ ' 295 ms' │ ' 13 ms' │ ' 22.69 x' │ -│ Null │ 2000 │ ' 255 ms' │ ' 8 ms' │ ' 31.88 x' │ -│ RegEx │ 2000 │ ' 482 ms' │ ' 17 ms' │ ' 28.35 x' │ -│ ObjectA │ 2000 │ ' 2700 ms' │ ' 55 ms' │ ' 49.09 x' │ -│ ObjectB │ 2000 │ ' 2871 ms' │ ' 36 ms' │ ' 79.75 x' │ -│ Tuple │ 2000 │ ' 1229 ms' │ ' 22 ms' │ ' 55.86 x' │ -│ Union │ 2000 │ ' 1216 ms' │ ' 26 ms' │ ' 46.77 x' │ -│ Vector4 │ 2000 │ ' 1575 ms' │ ' 25 ms' │ ' 63.00 x' │ -│ Matrix4 │ 2000 │ ' 908 ms' │ ' 13 ms' │ ' 69.85 x' │ -│ Literal_String │ 2000 │ ' 353 ms' │ ' 9 ms' │ ' 39.22 x' │ -│ Literal_Number │ 2000 │ ' 371 ms' │ ' 7 ms' │ ' 53.00 x' │ -│ Literal_Boolean │ 2000 │ ' 361 ms' │ ' 8 ms' │ ' 45.13 x' │ -│ Array_Number │ 2000 │ ' 694 ms' │ ' 7 ms' │ ' 99.14 x' │ -│ Array_String │ 2000 │ ' 778 ms' │ ' 8 ms' │ ' 97.25 x' │ -│ Array_Boolean │ 2000 │ ' 735 ms' │ ' 7 ms' │ ' 105.00 x' │ -│ Array_ObjectA │ 2000 │ ' 3732 ms' │ ' 44 ms' │ ' 84.82 x' │ -│ Array_ObjectB │ 2000 │ ' 3624 ms' │ ' 40 ms' │ ' 90.60 x' │ -│ Array_Tuple │ 2000 │ ' 2101 ms' │ ' 15 ms' │ ' 140.07 x' │ -│ Array_Union │ 2000 │ ' 1480 ms' │ ' 25 ms' │ ' 59.20 x' │ -│ Array_Vector4 │ 2000 │ ' 2175 ms' │ ' 18 ms' │ ' 120.83 x' │ -│ Array_Matrix4 │ 2000 │ ' 1516 ms' │ ' 13 ms' │ ' 116.62 x' │ +│ Number │ 2000 │ ' 437 ms' │ ' 15 ms' │ ' 29.13 x' │ +│ String │ 2000 │ ' 332 ms' │ ' 13 ms' │ ' 25.54 x' │ +│ Boolean │ 2000 │ ' 293 ms' │ ' 12 ms' │ ' 24.42 x' │ +│ Null │ 2000 │ ' 259 ms' │ ' 9 ms' │ ' 28.78 x' │ +│ RegEx │ 2000 │ ' 505 ms' │ ' 21 ms' │ ' 24.05 x' │ +│ ObjectA │ 2000 │ ' 2726 ms' │ ' 53 ms' │ ' 51.43 x' │ +│ ObjectB │ 2000 │ ' 2835 ms' │ ' 37 ms' │ ' 76.62 x' │ +│ Tuple │ 2000 │ ' 1239 ms' │ ' 24 ms' │ ' 51.63 x' │ +│ Union │ 2000 │ ' 1244 ms' │ ' 34 ms' │ ' 36.59 x' │ +│ Vector4 │ 2000 │ ' 1521 ms' │ ' 21 ms' │ ' 72.43 x' │ +│ Matrix4 │ 2000 │ ' 836 ms' │ ' 10 ms' │ ' 83.60 x' │ +│ Literal_String │ 2000 │ ' 333 ms' │ ' 9 ms' │ ' 37.00 x' │ +│ Literal_Number │ 2000 │ ' 357 ms' │ ' 9 ms' │ ' 39.67 x' │ +│ Literal_Boolean │ 2000 │ ' 352 ms' │ ' 6 ms' │ ' 58.67 x' │ +│ Array_Number │ 2000 │ ' 685 ms' │ ' 12 ms' │ ' 57.08 x' │ +│ Array_String │ 2000 │ ' 720 ms' │ ' 12 ms' │ ' 60.00 x' │ +│ Array_Boolean │ 2000 │ ' 709 ms' │ ' 8 ms' │ ' 88.63 x' │ +│ Array_ObjectA │ 2000 │ ' 3590 ms' │ ' 40 ms' │ ' 89.75 x' │ +│ Array_ObjectB │ 2000 │ ' 3610 ms' │ ' 51 ms' │ ' 70.78 x' │ +│ Array_Tuple │ 2000 │ ' 2124 ms' │ ' 19 ms' │ ' 111.79 x' │ +│ Array_Union │ 2000 │ ' 1533 ms' │ ' 21 ms' │ ' 73.00 x' │ +│ Array_Vector4 │ 2000 │ ' 2278 ms' │ ' 19 ms' │ ' 119.89 x' │ +│ Array_Matrix4 │ 2000 │ ' 1504 ms' │ ' 20 ms' │ ' 75.20 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1241,31 +1241,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ -│ String │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Boolean │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Null │ 1000000 │ ' 25 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ RegEx │ 1000000 │ ' 167 ms' │ ' 48 ms' │ ' 38 ms' │ ' 1.26 x' │ -│ ObjectA │ 1000000 │ ' 591 ms' │ ' 36 ms' │ ' 22 ms' │ ' 1.64 x' │ -│ ObjectB │ 1000000 │ ' 1168 ms' │ ' 61 ms' │ ' 37 ms' │ ' 1.65 x' │ -│ Tuple │ 1000000 │ ' 127 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ -│ Union │ 1000000 │ ' 340 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ -│ Recursive │ 1000000 │ ' 3174 ms' │ ' 432 ms' │ ' 100 ms' │ ' 4.32 x' │ -│ Vector4 │ 1000000 │ ' 142 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 577 ms' │ ' 40 ms' │ ' 28 ms' │ ' 1.43 x' │ -│ Literal_String │ 1000000 │ ' 47 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ -│ Literal_Boolean │ 1000000 │ ' 45 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ -│ Array_Number │ 1000000 │ ' 432 ms' │ ' 33 ms' │ ' 19 ms' │ ' 1.74 x' │ -│ Array_String │ 1000000 │ ' 462 ms' │ ' 32 ms' │ ' 22 ms' │ ' 1.45 x' │ -│ Array_Boolean │ 1000000 │ ' 455 ms' │ ' 32 ms' │ ' 26 ms' │ ' 1.23 x' │ -│ Array_ObjectA │ 1000000 │ ' 14595 ms' │ ' 2306 ms' │ ' 1496 ms' │ ' 1.54 x' │ -│ Array_ObjectB │ 1000000 │ ' 17310 ms' │ ' 2625 ms' │ ' 2105 ms' │ ' 1.25 x' │ -│ Array_Tuple │ 1000000 │ ' 1794 ms' │ ' 97 ms' │ ' 66 ms' │ ' 1.47 x' │ -│ Array_Union │ 1000000 │ ' 5085 ms' │ ' 246 ms' │ ' 91 ms' │ ' 2.70 x' │ -│ Array_Recursive │ 1000000 │ ' 59747 ms' │ ' 7866 ms' │ ' 1136 ms' │ ' 6.92 x' │ -│ Array_Vector4 │ 1000000 │ ' 2375 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ -│ Array_Matrix4 │ 1000000 │ ' 13115 ms' │ ' 385 ms' │ ' 246 ms' │ ' 1.57 x' │ +│ Number │ 1000000 │ ' 29 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ +│ String │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ +│ Boolean │ 1000000 │ ' 22 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Null │ 1000000 │ ' 27 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ +│ RegEx │ 1000000 │ ' 160 ms' │ ' 52 ms' │ ' 40 ms' │ ' 1.30 x' │ +│ ObjectA │ 1000000 │ ' 577 ms' │ ' 37 ms' │ ' 26 ms' │ ' 1.42 x' │ +│ ObjectB │ 1000000 │ ' 990 ms' │ ' 50 ms' │ ' 37 ms' │ ' 1.35 x' │ +│ Tuple │ 1000000 │ ' 124 ms' │ ' 22 ms' │ ' 17 ms' │ ' 1.29 x' │ +│ Union │ 1000000 │ ' 318 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ +│ Recursive │ 1000000 │ ' 3102 ms' │ ' 408 ms' │ ' 102 ms' │ ' 4.00 x' │ +│ Vector4 │ 1000000 │ ' 150 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Matrix4 │ 1000000 │ ' 579 ms' │ ' 43 ms' │ ' 27 ms' │ ' 1.59 x' │ +│ Literal_String │ 1000000 │ ' 50 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ +│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Array_Number │ 1000000 │ ' 429 ms' │ ' 32 ms' │ ' 20 ms' │ ' 1.60 x' │ +│ Array_String │ 1000000 │ ' 474 ms' │ ' 33 ms' │ ' 25 ms' │ ' 1.32 x' │ +│ Array_Boolean │ 1000000 │ ' 447 ms' │ ' 35 ms' │ ' 31 ms' │ ' 1.13 x' │ +│ Array_ObjectA │ 1000000 │ ' 14098 ms' │ ' 2352 ms' │ ' 2015 ms' │ ' 1.17 x' │ +│ Array_ObjectB │ 1000000 │ ' 16216 ms' │ ' 2628 ms' │ ' 2511 ms' │ ' 1.05 x' │ +│ Array_Tuple │ 1000000 │ ' 1749 ms' │ ' 95 ms' │ ' 75 ms' │ ' 1.27 x' │ +│ Array_Union │ 1000000 │ ' 4712 ms' │ ' 233 ms' │ ' 84 ms' │ ' 2.77 x' │ +│ Array_Recursive │ 1000000 │ ' 56118 ms' │ ' 7192 ms' │ ' 1180 ms' │ ' 6.09 x' │ +│ Array_Vector4 │ 1000000 │ ' 2284 ms' │ ' 99 ms' │ ' 50 ms' │ ' 1.98 x' │ +│ Array_Matrix4 │ 1000000 │ ' 12640 ms' │ ' 412 ms' │ ' 272 ms' │ ' 1.51 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 011e12d45..df9dd4821 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -111,8 +111,9 @@ export class TypeCompilerUnknownTypeError extends Error { /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { function IsNumber(value: unknown): value is number { - return typeof value === 'number' + return typeof value === 'number' && !globalThis.isNaN(value) } + // ------------------------------------------------------------------- // Types // ------------------------------------------------------------------- @@ -175,7 +176,7 @@ export namespace TypeCompiler { } function* Number(schema: Types.TNumber, value: string): IterableIterator { - yield `(typeof ${value} === 'number')` + yield `(typeof ${value} === 'number' && !isNaN(${value}))` if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` diff --git a/src/errors/errors.ts b/src/errors/errors.ts index d3407418e..ed39446a8 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -112,7 +112,7 @@ export class ValueErrorsUnknownTypeError extends Error { /** Provides functionality to generate a sequence of errors against a TypeBox type. */ export namespace ValueErrors { function IsNumber(value: unknown): value is number { - return typeof value === 'number' + return typeof value === 'number' && !isNaN(value) } function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} @@ -171,7 +171,7 @@ export namespace ValueErrors { } function* Integer(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'number')) { + if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } if (!globalThis.Number.isInteger(value)) { @@ -212,7 +212,7 @@ export namespace ValueErrors { } function* Number(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'number')) { + if (!(typeof value === 'number' && !isNaN(value))) { return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { diff --git a/src/guard/guard.ts b/src/guard/guard.ts index c6904f0b6..437dc4acd 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -70,7 +70,7 @@ export namespace TypeGuard { } function IsNumber(value: unknown): value is number { - return typeof value === 'number' + return typeof value === 'number' && !isNaN(value) } function IsBoolean(value: unknown): value is boolean { diff --git a/src/value/cast.ts b/src/value/cast.ts index 5abe5f60c..3f085611b 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -134,7 +134,7 @@ export namespace ValueCast { } function IsNumber(value: unknown): value is number { - return typeof value === 'number' + return typeof value === 'number' && !isNaN(value) } function IsStringNumeric(value: unknown): value is string { diff --git a/src/value/check.ts b/src/value/check.ts index 8ceca89e5..f4f8d0f24 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -39,7 +39,7 @@ export class ValueCheckUnknownTypeError extends Error { export namespace ValueCheck { function IsNumber(value: unknown): value is number { - return typeof value === 'number' + return typeof value === 'number' && !isNaN(value) } function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { @@ -88,12 +88,13 @@ export namespace ValueCheck { } return true } + function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { return typeof value === 'function' } function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'number')) { + if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { return false } if (!globalThis.Number.isInteger(value)) { @@ -130,7 +131,7 @@ export namespace ValueCheck { } function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'number')) { + if (!(typeof value === 'number' && !isNaN(value))) { return false } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts index af3ac6bfa..ba1110be9 100644 --- a/test/runtime/compiler/integer.ts +++ b/test/runtime/compiler/integer.ts @@ -64,4 +64,8 @@ describe('type/compiler/Integer', () => { ok(T, 9) fail(T, 10) }) + + it('Should not validate NaN', () => { + fail(Type.Integer(), NaN) + }) }) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts index 7628bfd12..b92e944b8 100644 --- a/test/runtime/compiler/number.ts +++ b/test/runtime/compiler/number.ts @@ -60,4 +60,8 @@ describe('type/compiler/Number', () => { ok(T, 9) fail(T, 10) }) + + it('Should not validate NaN', () => { + fail(Type.Number(), NaN) + }) }) diff --git a/test/runtime/schema/integer.ts b/test/runtime/schema/integer.ts index da32a8766..d176053ab 100644 --- a/test/runtime/schema/integer.ts +++ b/test/runtime/schema/integer.ts @@ -64,4 +64,8 @@ describe('type/schema/Integer', () => { ok(T, 9) fail(T, 10) }) + + it('Should not validate NaN', () => { + fail(Type.Integer(), NaN) + }) }) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts index b1a19ea72..3b3d6fc36 100644 --- a/test/runtime/schema/number.ts +++ b/test/runtime/schema/number.ts @@ -60,4 +60,8 @@ describe('type/schema/Number', () => { ok(T, 9) fail(T, 10) }) + + it('Should not validate NaN', () => { + fail(Type.Number(), NaN) + }) }) diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts index 59a74024c..e02a6f55c 100644 --- a/test/runtime/value/check/integer.ts +++ b/test/runtime/value/check/integer.ts @@ -16,9 +16,15 @@ describe('value/check/Integer', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) Assert.equal(result, false) }) + + it('Should fail NaN', () => { + const result = Value.Check(Type.Integer(), NaN) + Assert.equal(result, false) + }) }) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts index 4d9b14d6c..86dd673f1 100644 --- a/test/runtime/value/check/number.ts +++ b/test/runtime/value/check/number.ts @@ -44,4 +44,9 @@ describe('value/check/Number', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + + it('Should fail NaN', () => { + const result = Value.Check(Type.Number(), NaN) + Assert.equal(result, false) + }) }) From b66949f32ba2b25f8c314994ba874c7317dfbf53 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 11 Dec 2022 20:28:29 +0900 Subject: [PATCH 047/369] Documentation --- readme.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/readme.md b/readme.md index 94bc7374b..257316fc8 100644 --- a/readme.md +++ b/readme.md @@ -1148,22 +1148,23 @@ The following creates a `BigInt` type. ```typescript import { Type, Kind } from '@sinclair/typebox' -Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') +Custom.Set('bigint', (schema, value) => typeof value === 'bigint') +// ▲ // │ -// └────────────────────────────┐ The [Kind] is used to match registered type +// └────────────────────────────┐ // │ -const T = Type.Unsafe({ [Kind]: 'BigInt' }) // const T = { [Kind]: 'BigInt' } +const T = Type.Unsafe({ [Kind]: 'bigint' }) // const T = { [Kind]: 'BigInt' } -const A = TypeCompiler.Compile(T).Check(65536) // const A = false +const A = TypeCompiler.Compile(T).Check(1n) // const A = true -const B = Value.Check(T, 65536n) // const B = true +const B = Value.Check(T, 1) // const B = false ``` ### Custom Formats -Use the format module to create user defined string formats. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). The format module is specific to TypeBox can only be used with the TypeCompiler and Value modules. +Use the format module to create user defined string formats. The format module is specific to TypeBox can only be used with the TypeCompiler and Value modules. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). The format module is an optional import. @@ -1171,20 +1172,19 @@ The format module is an optional import. import { Format } from '@sinclair/typebox/format' ``` -The following creates a palindrome string format. +The following creates a custom string format that checks for lowercase. ```typescript -Format.Set('palindrome', value => value === value.split('').reverse().join('')) -``` - -Once set, this format can then be used by the TypeCompiler and Value modules. - -```typescript -const T = Type.String({ format: 'palindrome' }) +Format.Set('lowercase', value => value === value.toLowerCase()) +// ▲ +// │ +// └────────────────────┐ +// │ +const T = Type.String({ format: 'lowercase' }) -const A = TypeCompiler.Compile(T).Check('engine') // const A = false +const A = TypeCompiler.Compile(T).Check('action') // const A = true -const B = Value.Check(T, 'kayak') // const B = true +const B = Value.Check(T, 'Action') // const B = false ``` From 6c410b2006dc207872237ad3142934e26a4ae0ee Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 11 Dec 2022 20:29:13 +0900 Subject: [PATCH 048/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 257316fc8..ac6ea8174 100644 --- a/readme.md +++ b/readme.md @@ -1143,7 +1143,7 @@ The custom module is an optional import. import { Custom } from '@sinclair/typebox/custom' ``` -The following creates a `BigInt` type. +The following creates a `bigint` type. ```typescript import { Type, Kind } from '@sinclair/typebox' From 29f73eae36e5f685e886ae7f1508c3f75683db8e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 15 Dec 2022 19:48:59 +0900 Subject: [PATCH 049/369] Extensions and Collections (#290) --- example/collections/array.ts | 381 ++++++++++++++++++++++++++ example/collections/index.ts | 31 +++ example/collections/map.ts | 182 ++++++++++++ example/collections/set.ts | 123 +++++++++ example/custom/index.ts | 2 - example/custom/intersectAllOf.ts | 21 -- example/custom/unionOneOf.ts | 17 -- example/extensions/index.ts | 31 +++ example/extensions/intersect-allof.ts | 60 ++++ example/extensions/union-enum.ts | 46 ++++ example/extensions/union-oneof.ts | 47 ++++ example/index.ts | 2 +- hammer.mjs | 2 +- 13 files changed, 903 insertions(+), 42 deletions(-) create mode 100644 example/collections/array.ts create mode 100644 example/collections/index.ts create mode 100644 example/collections/map.ts create mode 100644 example/collections/set.ts delete mode 100644 example/custom/index.ts delete mode 100644 example/custom/intersectAllOf.ts delete mode 100644 example/custom/unionOneOf.ts create mode 100644 example/extensions/index.ts create mode 100644 example/extensions/intersect-allof.ts create mode 100644 example/extensions/union-enum.ts create mode 100644 example/extensions/union-oneof.ts diff --git a/example/collections/array.ts b/example/collections/array.ts new file mode 100644 index 000000000..66a255a5c --- /dev/null +++ b/example/collections/array.ts @@ -0,0 +1,381 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/collections + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' +import { TSchema, Static } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +// ---------------------------------------------------------------- +// TypeArrayError +// ---------------------------------------------------------------- + +export class TypeArrayError extends Error { + constructor(message: string) { + super(`${message}`) + } +} +export class TypeArrayLengthError extends Error { + constructor() { + super('arrayLength not a number') + } +} + +// ---------------------------------------------------------------- +// TypeArray +// ---------------------------------------------------------------- + +export class TypeArray implements Iterable> { + readonly #typeCheck: TypeCheck + readonly #values: Static[] + + constructor(schema: T, arrayLength: number = 0) { + if (typeof arrayLength !== 'number') throw new TypeArrayLengthError() + this.#typeCheck = TypeCompiler.Compile(schema) + this.#values = new Array(arrayLength) + for (let i = 0; i < arrayLength; i++) { + this.#values[i] = Value.Create(schema) + } + } + + // --------------------------------------------------- + // Indexer + // --------------------------------------------------- + + /** Sets the value at the given index */ + public set(index: number, item: Static): void { + this.#assertIndexInBounds(index) + this.#assertItem(index, item) + this.#values[index] = item + } + + // --------------------------------------------------- + // Array + // --------------------------------------------------- + + /** Iterator for values in this array */ + public [Symbol.iterator](): IterableIterator> { + return this.#values[Symbol.iterator]() as IterableIterator + } + + /** Gets the value at the given index */ + public at(index: number): Static { + this.#assertIndexInBounds(index) + return this.#values[index] as T + } + + /** + * Gets the length of the array. This is a number one higher than the highest index in the array. + */ + public get length(): number { + return this.#values.length + } + /** + * Returns a string representation of an array. + */ + public toString(): string { + return this.#values.toString() + } + /** + * Returns a string representation of an array. The elements are converted to string using their toLocaleString methods. + */ + public toLocaleString(): string { + return this.#values.toLocaleString() + } + + /** + * Removes the last element from an array and returns it. + * If the array is empty, undefined is returned and the array is not modified. + */ + public pop(): Static | undefined { + return this.#values.pop() + } + /** + * Appends new elements to the end of an array, and returns the new length of the array. + * @param items New elements to add to the array. + */ + public push(...items: Static[]): number { + this.#assertItems(items) + return this.#values.push(...items) + } + /** + * Combines two or more arrays. + * This method returns a new array without modifying any existing arrays. + * @param items Additional arrays and/or items to add to the end of the array. + */ + public concat(...items: ConcatArray>[]): Static[] + + /** + * Combines two or more arrays. + * This method returns a new array without modifying any existing arrays. + * @param items Additional arrays and/or items to add to the end of the array. + */ + public concat(...items: (T | ConcatArray>)[]): Static[] { + this.#assertItems(items) + return this.#values.concat(...items) as Static[] + } + + /** + * Adds all the elements of an array into a string, separated by the specified separator string. + * @param separator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma. + */ + public join(separator?: string): string { + return this.#values.join(separator) + } + /** + * Reverses the elements in an array in place. + * This method mutates the array and returns a reference to the same array. + */ + public reverse(): Static[] { + return this.#values.reverse() as Static[] + } + /** + * Removes the first element from an array and returns it. + * If the array is empty, undefined is returned and the array is not modified. + */ + public shift(): Static | undefined { + return this.#values.shift() as Static | undefined + } + + /** + * Returns a copy of a section of an array. + * For both start and end, a negative index can be used to indicate an offset from the end of the array. + * For example, -2 refers to the second to last element of the array. + * @param start The beginning index of the specified portion of the array. + * If start is undefined, then the slice begins at index 0. + * @param end The end index of the specified portion of the array. This is exclusive of the element at the index 'end'. + * If end is undefined, then the slice extends to the end of the array. + */ + public slice(start?: number, end?: number): Static[] { + return this.#values.slice(start, end) as Static[] + } + + /** + * Sorts an array in place. + * This method mutates the array and returns a reference to the same array. + * @param compareFn Function used to determine the order of the elements. It is expected to return + * a negative value if the first argument is less than the second argument, zero if they're equal, and a positive + * value otherwise. If omitted, the elements are sorted in ascending, ASCII character order. + * ```ts + * [11,2,22,1].sort((a, b) => a - b) + * ``` + */ + public sort(compareFn?: (a: Static, b: Static) => number): this { + this.#values.sort(compareFn as any) + return this + } + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + * @param deleteCount The number of elements to remove. + * @returns An array containing the elements that were deleted. + */ + public splice(start: number, deleteCount?: number): Static[] + + /** + * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. + * @param start The zero-based location in the array from which to start removing elements. + * @param deleteCount The number of elements to remove. + * @param items Elements to insert into the array in place of the deleted elements. + * @returns An array containing the elements that were deleted. + */ + public splice(start: number, deleteCount: number, ...items: Static[]): Static[] { + this.#assertItems(items) + return this.#values.splice(start, deleteCount, items) as Static[] + } + + /** + * Inserts new elements at the start of an array, and returns the new length of the array. + * @param items Elements to insert at the start of the array. + */ + public unshift(...items: Static[]): number { + this.#assertItems(items) + return this.#values.unshift(items) + } + + /** + * Returns the index of the first occurrence of a value in an array, or -1 if it is not present. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the search starts at index 0. + */ + public indexOf(searchElement: Static, fromIndex?: number): number { + return this.#values.indexOf(searchElement, fromIndex) + } + + /** + * Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present. + * @param searchElement The value to locate in the array. + * @param fromIndex The array index at which to begin searching backward. If fromIndex is omitted, the search starts at the last index in the array. + */ + public lastIndexOf(searchElement: Static, fromIndex?: number): number { + return this.#values.lastIndexOf(searchElement, fromIndex) + } + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param predicate A function that accepts up to three arguments. The every method calls + * the predicate function for each element in the array until the predicate returns a value + * which is coercible to the Boolean value false, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the predicate function. + * If thisArg is omitted, undefined is used as the this value. + */ + public every(predicate: (value: Static, index: number, array: Static[]) => value is Static, thisArg?: any): this is Static[] + + /** + * Determines whether all the members of an array satisfy the specified test. + * @param predicate A function that accepts up to three arguments. The every method calls + * the predicate function for each element in the array until the predicate returns a value + * which is coercible to the Boolean value false, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the predicate function. + * If thisArg is omitted, undefined is used as the this value. + */ + public every(predicate: (value: Static, index: number, array: Static[]) => unknown, thisArg?: any): boolean { + return this.#values.every(predicate as any, thisArg) + } + + /** + * Determines whether the specified callback function returns true for any element of an array. + * @param predicate A function that accepts up to three arguments. The some method calls + * the predicate function for each element in the array until the predicate returns a value + * which is coercible to the Boolean value true, or until the end of the array. + * @param thisArg An object to which the this keyword can refer in the predicate function. + * If thisArg is omitted, undefined is used as the this value. + */ + public some(predicate: (value: Static, index: number, array: T[]) => unknown, thisArg?: any): boolean { + return this.#values.some(predicate as any, thisArg) + } + + /** + * Performs the specified action for each element in an array. + * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + public forEach(callbackfn: (value: Static, index: number, array: Static[]) => void, thisArg?: any): void { + return this.#values.forEach(callbackfn as any, thisArg) + } + /** + * Calls a defined callback function on each element of an array, and returns an array that contains the results. + * @param callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value. + */ + public map(callbackfn: (value: Static, index: number, array: Static[]) => U, thisArg?: any): U[] { + return this.#values.map(callbackfn as any, thisArg) + } + + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value. + */ + public filter(predicate: (value: Static, index: number, array: Static[]) => value is Static, thisArg?: any): Static[] + /** + * Returns the elements of an array that meet the condition specified in a callback function. + * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value. + */ + public filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[] + public filter(predicate: any, thisArg: any): any { + return this.#values.filter(predicate as any, thisArg) + } + + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + public reduce(callbackfn: (previousValue: Static, currentValue: Static, currentIndex: number, array: T[]) => Static): Static + public reduce(callbackfn: (previousValue: Static, currentValue: Static, currentIndex: number, array: T[]) => Static, initialValue: Static): Static + /** + * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + public reduce(callbackfn: (previousValue: U, currentValue: Static, currentIndex: number, array: Static[]) => U, initialValue: U): U + public reduce(callbackfn: any, initialValue?: any): any { + return this.#values.reduce(callbackfn, initialValue) + } + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + public reduceRight(callbackfn: (previousValue: Static, currentValue: Static, currentIndex: number, array: Static[]) => Static): Static + public reduceRight(callbackfn: (previousValue: Static, currentValue: Static, currentIndex: number, array: Static[]) => Static, initialValue: T): Static + /** + * Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. + * @param callbackfn A function that accepts up to four arguments. The reduceRight method calls the callbackfn function one time for each element in the array. + * @param initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value. + */ + public reduceRight(callbackfn: (previousValue: U, currentValue: Static, currentIndex: number, array: Static[]) => U, initialValue: U): U + public reduceRight(callbackfn: any, initialValue?: any): any { + return this.#values.reduceRight(callbackfn, initialValue) + } + + // --------------------------------------------------- + // Assertions + // --------------------------------------------------- + + #formatError(errors: ValueError[]) { + return errors.map((error) => `${error.message} ${error.path}`).join('. ') + } + /** Asserts the given values */ + #assertIndexInBounds(index: number) { + if (index >= 0 && index < this.#values.length) return + throw new TypeArrayError(`Index ${index} is outside the bounds of this Array.`) + } + + /** Asserts the given values */ + #assertItem(index: number, item: unknown): asserts item is Static { + if (this.#typeCheck.Check(item)) return + const message = this.#formatError([...this.#typeCheck.Errors(item)]) + throw new TypeArrayError(`Item at Index ${index} is invalid. ${message}`) + } + + /** Asserts the given values */ + #assertItems(items: unknown[]): asserts items is Static[] { + for (let i = 0; i < items.length; i++) { + this.#assertItem(i, items[i]) + } + } + + /** Creates a typed array from an existing array */ + public static from(schema: T, iterable: IterableIterator> | Array>): TypeArray { + if (globalThis.Array.isArray(iterable)) { + const array = new TypeArray(schema, iterable.length) + for (let i = 0; i < iterable.length; i++) { + array.set(i, iterable[i]) + } + return array + } + const array = new TypeArray(schema) + for (const value of iterable) { + array.push(value) + } + return array + } +} diff --git a/example/collections/index.ts b/example/collections/index.ts new file mode 100644 index 000000000..004ddbfb5 --- /dev/null +++ b/example/collections/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/collections + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './array' +export * from './map' +export * from './set' diff --git a/example/collections/map.ts b/example/collections/map.ts new file mode 100644 index 000000000..040bd5fa7 --- /dev/null +++ b/example/collections/map.ts @@ -0,0 +1,182 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/collections + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' +import { TSchema, Static } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +// ---------------------------------------------------------------- +// TypeMapKeyError +// ---------------------------------------------------------------- + +export class TypeMapKeyError extends Error { + constructor(message: string) { + super(`${message} for key`) + } +} + +// ---------------------------------------------------------------- +// TypeMapKeyError +// ---------------------------------------------------------------- + +export class TypeMapValueError extends Error { + constructor(key: unknown, message: string) { + super(`${message} for key ${JSON.stringify(key)}`) + } +} + +// ---------------------------------------------------------------- +// TypeMap +// ---------------------------------------------------------------- + +// prettier-ignore +type TypeMapEntries = + | Iterable<[Static, Static]> + | Array<[Static, Static]> + +/** Runtime type checked Map collection */ +export class TypeMap { + readonly #keycheck: TypeCheck + readonly #valuecheck: TypeCheck + readonly #keys: Map> + readonly #values: Map> + + /** Constructs a new HashMap of the given key and value types. */ + constructor(key: K, value: V, entries: TypeMapEntries = []) { + this.#keycheck = TypeCompiler.Compile(key) + this.#valuecheck = TypeCompiler.Compile(value) + this.#keys = new Map>() + this.#values = new Map>() + for (const [key, value] of entries) { + this.set(key, value) + } + } + + /** Iterator for this TypeMap */ + public *[Symbol.iterator](): IterableIterator<[Static, Static]> { + for (const [key, value] of this.#values) { + yield [this.#keys.get(key)!, value] + } + } + + /** Iterator for the keys in this TypeMap */ + public *keys(): IterableIterator> { + yield* this.#keys.values() + } + + /** Iterator for the values in this TypeMap */ + public *values(): IterableIterator> { + yield* this.#values.values() + } + + /** Clears all entries in this map */ + public clear(): void { + this.#values.clear() + this.#keys.clear() + } + + /** Executes a provided function once per each key/value pair in the Map, in insertion order. */ + public forEach(callbackfn: (value: Static, key: Static, map: TypeMap) => void, thisArg?: any): void { + this.#values.forEach((value, key) => callbackfn(value, this.#keys.get(key)!, this)) + } + + /** @returns the number of elements in the TypeMap. */ + public get size(): number { + return this.#values.size + } + + /** + * @returns boolean indicating whether an element with the specified key exists or not. + */ + public has(key: Static): boolean { + this.#assertKey(key) + return this.#values.has(this.#encodeKey(key)) + } + + /** + * Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map. + * @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned. + */ + public get(key: Static): Static | undefined { + this.#assertKey(key) + return this.#values.get(this.#encodeKey(key))! + } + + /** + * Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated. + */ + public set(key: Static, value: Static) { + this.#assertKey(key) + this.#assertValue(key, value) + const encodedKey = this.#encodeKey(key) + this.#keys.set(encodedKey, key) + this.#values.set(encodedKey, value) + } + + /** + * @returns true if an element in the Map existed and has been removed, or false if the element does not exist. + */ + public delete(key: Static): boolean { + this.#assertKey(key) + const encodedKey = this.#encodeKey(key) + this.#keys.delete(encodedKey) + return this.#values.delete(encodedKey) + } + + // --------------------------------------------------- + // Encoder + // --------------------------------------------------- + + /** Encodes the key as a 64bit numeric */ + #encodeKey(key: Static) { + return Value.Hash(key) + } + + // --------------------------------------------------- + // Assertions + // --------------------------------------------------- + + #formatError(errors: ValueError[]) { + return errors + .map((error) => `${error.message} ${error.path}`) + .join('. ') + .trim() + } + + /** Asserts the key matches the key schema */ + #assertKey(key: unknown): asserts key is Static { + if (this.#keycheck.Check(key)) return + throw new TypeMapKeyError(this.#formatError([...this.#keycheck.Errors(key)])) + } + + /** Asserts the key matches the value schema */ + #assertValue(key: Static, value: unknown): asserts value is Static { + if (this.#valuecheck.Check(value)) return + throw new TypeMapValueError(key, this.#formatError([...this.#valuecheck.Errors(value)])) + } +} diff --git a/example/collections/set.ts b/example/collections/set.ts new file mode 100644 index 000000000..aa83c5d26 --- /dev/null +++ b/example/collections/set.ts @@ -0,0 +1,123 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/collections + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' +import { TSchema, Static } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +// ---------------------------------------------------------------- +// TypeSetError +// ---------------------------------------------------------------- + +export class TypeSetError extends Error { + constructor(message: string) { + super(`${message}`) + } +} + +// ---------------------------------------------------------------- +// TypeSet +// ---------------------------------------------------------------- + +/** Runtime type checked Set collection */ +export class TypeSet { + readonly #valuecheck: TypeCheck + readonly values: Map> + constructor(schema: T, iterable: Array | Iterable = []) { + this.#valuecheck = TypeCompiler.Compile(schema) + this.values = new Map>() + for (const value of iterable) { + this.add(value) + } + } + + /** Adds a value to this set */ + public add(value: Static): this { + this.#assertValue(value) + this.values.set(this.#encodeKey(value), value) + return this + } + + /** Clears the values in this set */ + public clear(): void { + this.values.clear() + } + + /** + * Removes a specified value from the Set. + * @returns Returns true if an element in the Set existed and has been removed, or false if the element does not exist. + */ + public delete(value: Static): boolean { + return this.values.delete(this.#encodeKey(value)) + } + + /** Executes a provided function once per each value in the Set object, in insertion order. */ + public forEach(callbackfn: (value: Static, value2: Static, set: TypeSet) => void, thisArg?: any): void { + this.values.forEach((value, value2) => callbackfn(value, value2, this), thisArg) + } + + /** + * @returns a boolean indicating whether an element with the specified value exists in the Set or not. + */ + public has(value: Static): boolean { + return this.values.has(this.#encodeKey(value)) + } + + /** + * @returns the number of (unique) elements in Set. + */ + public get size(): number { + return this.values.size + } + + // --------------------------------------------------- + // Encoder + // --------------------------------------------------- + + #encodeKey(value: Static) { + return Value.Hash(value) + } + + // --------------------------------------------------- + // Assertions + // --------------------------------------------------- + + /** Formats errors */ + #formatError(errors: ValueError[]) { + return errors + .map((error) => `${error.message} ${error.path}`) + .join('. ') + .trim() + } + + /** Asserts the key matches the value schema */ + #assertValue(value: unknown): asserts value is Static { + if (this.#valuecheck.Check(value)) return + throw new TypeSetError(this.#formatError([...this.#valuecheck.Errors(value)])) + } +} diff --git a/example/custom/index.ts b/example/custom/index.ts deleted file mode 100644 index 7f9f50c3c..000000000 --- a/example/custom/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './intersectAllOf' -export * from './unionOneOf' \ No newline at end of file diff --git a/example/custom/intersectAllOf.ts b/example/custom/intersectAllOf.ts deleted file mode 100644 index 4b8e24efe..000000000 --- a/example/custom/intersectAllOf.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Kind, TSchema, SchemaOptions, IntersectReduce, IntersectEvaluate } from '@sinclair/typebox' -import { Custom } from '@sinclair/typebox/custom' -import { Value } from '@sinclair/typebox/value' - -export interface IntersectAllOfOptions extends SchemaOptions { - unevaluatedProperties?: boolean -} - -export interface IntersectAllOf extends TSchema, IntersectAllOfOptions { - [Kind]: 'IntersectAllOf', - static: IntersectReduce> - allOf: T -} - -/** Creates a Intersect type with a allOf schema representation */ -export function IntersectAllOf(allOf: [...T], options: IntersectAllOfOptions = {}) { - if(!Custom.Has('IntersectAllOf')) Custom.Set('IntersectAllOf', (schema: { allOf: TSchema[] }, value) => { - return schema.allOf.every(schema => Value.Check(schema, value)) - }) - return { ...options, [Kind]: 'IntersectAllOf', allOf } as IntersectAllOf -} \ No newline at end of file diff --git a/example/custom/unionOneOf.ts b/example/custom/unionOneOf.ts deleted file mode 100644 index 0db14c59d..000000000 --- a/example/custom/unionOneOf.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Kind, TSchema, SchemaOptions, Static } from '@sinclair/typebox' -import { Custom } from '@sinclair/typebox/custom' -import { Value } from '@sinclair/typebox/value' - -export interface UnionOneOf extends TSchema { - [Kind]: 'UnionOneOf', - static: { [K in keyof T]: Static }[number] - oneOf: T -} - -/** Creates a Union type with a oneOf schema representation */ -export function UnionOneOf(oneOf: [...T], options: SchemaOptions = {}) { - if (!Custom.Has('UnionOneOf')) Custom.Set('UnionOneOf', (schema: { oneOf: TSchema[] }, value) => { - return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) - }) - return { [Kind]: 'UnionOneOf', oneOf } as UnionOneOf -} diff --git a/example/extensions/index.ts b/example/extensions/index.ts new file mode 100644 index 000000000..a8ff621e5 --- /dev/null +++ b/example/extensions/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './intersect-allof' +export * from './union-oneof' +export * from './union-enum' diff --git a/example/extensions/intersect-allof.ts b/example/extensions/intersect-allof.ts new file mode 100644 index 000000000..7cf413e73 --- /dev/null +++ b/example/extensions/intersect-allof.ts @@ -0,0 +1,60 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, TSchema, SchemaOptions, IntersectReduce, IntersectEvaluate } from '@sinclair/typebox' +import { TypeGuard } from '@sinclair/typebox/guard' +import { Custom } from '@sinclair/typebox/custom' +import { Value } from '@sinclair/typebox/value' + +function IntersectAllOfCheck(schema: IntersectAllOf, value: unknown) { + if (!schema.allOf.every((schema) => Value.Check(schema, value))) return false + if (schema.unevaluatedProperties === undefined || schema.unevaluatedProperties === true) return true + const valueKeys = typeof value === 'object' && value !== null ? globalThis.Object.keys(value) : [] + const knownKeys = schema.allOf.reduce((set, schema) => { + if (!TypeGuard.TObject(schema)) return set + Object.keys(schema.properties).forEach((key) => set.add(key)) + return set + }, new Set()) + return valueKeys.every((key) => knownKeys.has(key)) +} + +export interface IntersectAllOfOptions extends SchemaOptions { + unevaluatedProperties?: boolean +} + +export interface IntersectAllOf extends TSchema, IntersectAllOfOptions { + [Kind]: 'IntersectAllOf' + static: IntersectReduce> + allOf: T +} + +/** Creates a Intersect type with a `allOf` schema representation */ +export function IntersectAllOf(allOf: [...T], options: IntersectAllOfOptions = {}) { + if (!Custom.Has('IntersectAllOf')) Custom.Set('IntersectAllOf', IntersectAllOfCheck) + return { ...options, [Kind]: 'IntersectAllOf', allOf } as IntersectAllOf +} diff --git a/example/extensions/union-enum.ts b/example/extensions/union-enum.ts new file mode 100644 index 000000000..ac895338e --- /dev/null +++ b/example/extensions/union-enum.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, TSchema, SchemaOptions } from '@sinclair/typebox' +import { Custom } from '@sinclair/typebox/custom' + +function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { + return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) +} + +export interface TUnionEnum extends TSchema { + [Kind]: 'UnionEnum' + static: T[number] + enum: T +} + +/** Creates a Union type with a `enum` schema representation */ +export function UnionEnum(values: [...T], options: SchemaOptions = {}) { + if (!Custom.Has('UnionEnum')) Custom.Set('UnionEnum', UnionEnumCheck) + return { ...options, [Kind]: 'UnionEnum', enum: values } as TUnionEnum +} diff --git a/example/extensions/union-oneof.ts b/example/extensions/union-oneof.ts new file mode 100644 index 000000000..c40a9f9b5 --- /dev/null +++ b/example/extensions/union-oneof.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, TSchema, SchemaOptions, Static } from '@sinclair/typebox' +import { Custom } from '@sinclair/typebox/custom' +import { Value } from '@sinclair/typebox/value' + +function UnionOneOfCheck(schema: UnionOneOf, value: unknown) { + return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) +} + +export interface UnionOneOf extends TSchema { + [Kind]: 'UnionOneOf' + static: { [K in keyof T]: Static }[number] + oneOf: T +} + +/** Creates a Union type with a `oneOf` schema representation */ +export function UnionOneOf(oneOf: [...T], options: SchemaOptions = {}) { + if (!Custom.Has('UnionOneOf')) Custom.Set('UnionOneOf', UnionOneOfCheck) + return { ...options, [Kind]: 'UnionOneOf', oneOf } as UnionOneOf +} diff --git a/example/index.ts b/example/index.ts index dee703c07..dfde052cf 100644 --- a/example/index.ts +++ b/example/index.ts @@ -15,4 +15,4 @@ const T = Type.Object({ type T = Static -console.log(T) \ No newline at end of file +console.log(T) diff --git a/hammer.mjs b/hammer.mjs index 34b412fca..19e88cee3 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -15,7 +15,7 @@ export async function clean() { // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test benchmark codegen') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example benchmark codegen') } // ------------------------------------------------------------------------------- From 1cb07ad032d1afcd44efbc55139f0e88e87ecc4f Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 19 Dec 2022 15:30:14 +0900 Subject: [PATCH 050/369] Example String Formats --- example/formats/additional.ts | 94 +++++++++++++++++++++++++++++++++++ example/formats/index.ts | 1 + 2 files changed, 95 insertions(+) create mode 100644 example/formats/additional.ts create mode 100644 example/formats/index.ts diff --git a/example/formats/additional.ts b/example/formats/additional.ts new file mode 100644 index 000000000..60396d3cf --- /dev/null +++ b/example/formats/additional.ts @@ -0,0 +1,94 @@ +import { Format } from '@sinclair/typebox/format' + +/** Configures TypeBox for additional string formats */ +export namespace AdditionalFormats { + // ------------------------------------------------------------------------------------------- + // These formats are ported from the ajv-formats library. All credit goes to the original + // authors with the original code located at the following location. + // + // https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts + // ------------------------------------------------------------------------------------------- + + const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i + const DATE_TIME_SEPARATOR = /t|\s/i + const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i + const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ + const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ + const IPV6 = + /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i + const URL = + /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu + const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i + + function IsLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) + } + + function IsDate(str: string): boolean { + const matches: string[] | null = DATE.exec(str) + if (!matches) return false + const year: number = +matches[1] + const month: number = +matches[2] + const day: number = +matches[3] + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) + } + + function IsTime(str: string, strictTimeZone?: boolean): boolean { + const matches: string[] | null = TIME.exec(str) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tz: string | undefined = matches[4] + const tzSign: number = matches[5] === '-' ? -1 : 1 + const tzH: number = +(matches[6] || 0) + const tzM: number = +(matches[7] || 0) + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false + if (hr <= 23 && min <= 59 && sec < 60) return true + // leap second + const utcMin = min - tzM * tzSign + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 + } + + function IsDateTime(value: string, strictTimeZone?: boolean): boolean { + const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) + return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) + } + + function IsEmail(value: string) { + return EMAIL.test(value) + } + + function IsUuid(value: string) { + return UUID.test(value) + } + + function IsUrl(value: string) { + return URL.test(value) + } + + function IsIPv6(value: string) { + return IPV6.test(value) + } + + function IsIPv4(value: string) { + return IPV4.test(value) + } + + /** Configures additional string formats */ + export function Configure(): void { + Format.Set('date-time', (value) => IsDateTime(value, true)) + Format.Set('date', (value) => IsDate(value)) + Format.Set('time', (value) => IsTime(value)) + Format.Set('email', (value) => IsEmail(value)) + Format.Set('uuid', (value) => IsUuid(value)) + Format.Set('url', (value) => IsUrl(value)) + Format.Set('ipv6', (value) => IsIPv6(value)) + Format.Set('ipv4', (value) => IsIPv4(value)) + // + // optional: add additional formats + // + } +} diff --git a/example/formats/index.ts b/example/formats/index.ts new file mode 100644 index 000000000..2923362e3 --- /dev/null +++ b/example/formats/index.ts @@ -0,0 +1 @@ +export * from './additional' From 0ce824bfb391d36e5c18008938af2da2b101adfa Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 19 Dec 2022 22:50:32 +0900 Subject: [PATCH 051/369] Additional Defaults (#294) --- example/index.ts | 2 +- package.json | 2 +- src/value/create.ts | 24 ++++++++++++++++++++---- test/runtime/value/create/literal.ts | 6 +++++- test/runtime/value/create/null.ts | 4 ++++ test/runtime/value/create/undefined.ts | 4 ++++ test/runtime/value/create/void.ts | 5 +++++ 7 files changed, 40 insertions(+), 7 deletions(-) diff --git a/example/index.ts b/example/index.ts index dfde052cf..ec83c1476 100644 --- a/example/index.ts +++ b/example/index.ts @@ -10,7 +10,7 @@ import { Type, Kind, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ x: Type.Number(), y: Type.Number(), - z: Type.Number() + z: Type.Number(), }) type T = Static diff --git a/package.json b/package.json index bc74862d6..184b312a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.13", + "version": "0.25.14", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/create.ts b/src/value/create.ts index ab4d33ad0..19469ced5 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -121,7 +121,11 @@ export namespace ValueCreate { } function Literal(schema: Types.TLiteral, references: Types.TSchema[]): any { - return schema.const + if (schema.default !== undefined) { + return schema.default + } else { + return schema.const + } } function Never(schema: Types.TNever, references: Types.TSchema[]): any { @@ -129,7 +133,11 @@ export namespace ValueCreate { } function Null(schema: Types.TNull, references: Types.TSchema[]): any { - return null + if (schema.default !== undefined) { + return schema.default + } else { + return null + } } function Number(schema: Types.TNumber, references: Types.TSchema[]): any { @@ -244,7 +252,11 @@ export namespace ValueCreate { } function Undefined(schema: Types.TUndefined, references: Types.TSchema[]): any { - return undefined + if (schema.default !== undefined) { + return schema.default + } else { + return undefined + } } function Union(schema: Types.TUnion, references: Types.TSchema[]): any { @@ -275,7 +287,11 @@ export namespace ValueCreate { } function Void(schema: Types.TVoid, references: Types.TSchema[]): any { - return null + if (schema.default !== undefined) { + return schema.default + } else { + return null + } } function UserDefined(schema: Types.TSchema, references: Types.TSchema[]): any { diff --git a/test/runtime/value/create/literal.ts b/test/runtime/value/create/literal.ts index 591799b39..d2913ab7f 100644 --- a/test/runtime/value/create/literal.ts +++ b/test/runtime/value/create/literal.ts @@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/create/KeyOf', () => { +describe('value/create/Literal', () => { it('Should create literal string', () => { const T = Type.Literal('hello') Assert.deepEqual(Value.Create(T), 'hello') @@ -15,4 +15,8 @@ describe('value/create/KeyOf', () => { const T = Type.Literal(true) Assert.deepEqual(Value.Create(T), true) }) + it('Should create literal from default value', () => { + const T = Type.Literal(true, { default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) }) diff --git a/test/runtime/value/create/null.ts b/test/runtime/value/create/null.ts index 4b11e6d1a..45c42fc86 100644 --- a/test/runtime/value/create/null.ts +++ b/test/runtime/value/create/null.ts @@ -7,4 +7,8 @@ describe('value/create/Null', () => { const T = Type.Null() Assert.deepEqual(Value.Create(T), null) }) + it('Should create null from default value', () => { + const T = Type.Null({ default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) }) diff --git a/test/runtime/value/create/undefined.ts b/test/runtime/value/create/undefined.ts index 45e369ad4..b684204c6 100644 --- a/test/runtime/value/create/undefined.ts +++ b/test/runtime/value/create/undefined.ts @@ -7,4 +7,8 @@ describe('value/create/Undefined', () => { const T = Type.Undefined() Assert.deepEqual(Value.Create(T), undefined) }) + it('Should create value from default value', () => { + const T = Type.Undefined({ default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) }) diff --git a/test/runtime/value/create/void.ts b/test/runtime/value/create/void.ts index eff8379a7..b74886656 100644 --- a/test/runtime/value/create/void.ts +++ b/test/runtime/value/create/void.ts @@ -7,4 +7,9 @@ describe('value/create/Void', () => { const T = Type.Void() Assert.deepEqual(Value.Create(T), null) }) + + it('Should create value from default value', () => { + const T = Type.Void({ default: 'hello' }) + Assert.deepEqual(Value.Create(T), 'hello') + }) }) From 34d6fb2ed1b823fb2117af7733d7f9f755e2c3b6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 20 Dec 2022 11:45:17 +0900 Subject: [PATCH 052/369] Structural Value Checks (#295) --- example/index.ts | 19 +++---- package.json | 3 +- src/compiler/compiler.ts | 27 ++++++---- src/errors/errors.ts | 50 ++++++++++------- src/system/index.ts | 29 ++++++++++ src/system/system.ts | 59 ++++++++++++++++++++ src/value/cast.ts | 12 ++--- src/value/check.ts | 36 +++++++++---- test/runtime/compiler/any.ts | 16 +++--- test/runtime/compiler/array.ts | 30 +++++------ test/runtime/compiler/boolean.ts | 18 +++---- test/runtime/compiler/custom.ts | 10 ++-- test/runtime/compiler/date.ts | 34 ++++++------ test/runtime/compiler/enum.ts | 28 +++++----- test/runtime/compiler/integer.ts | 36 ++++++------- test/runtime/compiler/intersect.ts | 44 +++++++-------- test/runtime/compiler/keyof.ts | 22 ++++---- test/runtime/compiler/literal.ts | 22 ++++---- test/runtime/compiler/never.ts | 16 +++--- test/runtime/compiler/null.ts | 16 +++--- test/runtime/compiler/number.ts | 36 ++++++------- test/runtime/compiler/object.ts | 50 ++++++++--------- test/runtime/compiler/omit.ts | 8 +-- test/runtime/compiler/optional.ts | 6 +-- test/runtime/compiler/partial.ts | 10 ++-- test/runtime/compiler/pick.ts | 8 +-- test/runtime/compiler/readonly-optional.ts | 6 +-- test/runtime/compiler/readonly.ts | 4 +- test/runtime/compiler/record.ts | 36 ++++++------- test/runtime/compiler/recursive.ts | 8 +-- test/runtime/compiler/ref.ts | 14 ++--- test/runtime/compiler/regex.ts | 32 +++++------ test/runtime/compiler/required.ts | 10 ++-- test/runtime/compiler/string.ts | 32 +++++------ test/runtime/compiler/tuple.ts | 20 +++---- test/runtime/compiler/uint8array.ts | 26 ++++----- test/runtime/compiler/union.ts | 32 +++++------ test/runtime/compiler/unknown.ts | 16 +++--- test/runtime/compiler/validate.ts | 12 ++++- test/runtime/compiler/void.ts | 16 +++--- test/runtime/index.ts | 1 + test/runtime/schema/any.ts | 16 +++--- test/runtime/schema/array.ts | 30 +++++------ test/runtime/schema/boolean.ts | 18 +++---- test/runtime/schema/date.ts | 34 ++++++------ test/runtime/schema/enum.ts | 28 +++++----- test/runtime/schema/integer.ts | 36 ++++++------- test/runtime/schema/intersect.ts | 52 +++++++++--------- test/runtime/schema/keyof.ts | 22 ++++---- test/runtime/schema/literal.ts | 22 ++++---- test/runtime/schema/never.ts | 16 +++--- test/runtime/schema/null.ts | 16 +++--- test/runtime/schema/number.ts | 36 ++++++------- test/runtime/schema/object.ts | 50 ++++++++--------- test/runtime/schema/omit.ts | 8 +-- test/runtime/schema/optional.ts | 6 +-- test/runtime/schema/partial.ts | 10 ++-- test/runtime/schema/pick.ts | 8 +-- test/runtime/schema/readonly-optional.ts | 6 +-- test/runtime/schema/readonly.ts | 4 +- test/runtime/schema/record.ts | 36 ++++++------- test/runtime/schema/recursive.ts | 8 +-- test/runtime/schema/ref.ts | 14 ++--- test/runtime/schema/regex.ts | 32 +++++------ test/runtime/schema/required.ts | 10 ++-- test/runtime/schema/string.ts | 32 +++++------ test/runtime/schema/tuple.ts | 20 +++---- test/runtime/schema/uint8array.ts | 26 ++++----- test/runtime/schema/union.ts | 32 +++++------ test/runtime/schema/unknown.ts | 16 +++--- test/runtime/schema/unsafe.ts | 6 +-- test/runtime/schema/validate.ts | 4 +- test/runtime/schema/void.ts | 16 +++--- test/runtime/settings/index.ts | 1 + test/runtime/settings/structural.ts | 62 ++++++++++++++++++++++ tsconfig.json | 3 ++ 76 files changed, 923 insertions(+), 723 deletions(-) create mode 100644 src/system/index.ts create mode 100644 src/system/system.ts create mode 100644 test/runtime/settings/index.ts create mode 100644 test/runtime/settings/structural.ts diff --git a/example/index.ts b/example/index.ts index ec83c1476..c367f5f78 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,18 +1,19 @@ import { CodeGen } from '@sinclair/typebox/codegen' +import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' -import { Conditional } from '@sinclair/typebox/conditional' import { TypeGuard } from '@sinclair/typebox/guard' +import { Conditional } from '@sinclair/typebox/conditional' import { Format } from '@sinclair/typebox/format' import { Custom } from '@sinclair/typebox/custom' import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Kind, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), -}) - -type T = Static - -console.log(T) + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + + type T = Static + + console.log(T) \ No newline at end of file diff --git a/package.json b/package.json index 184b312a4..0b6dce662 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.14", + "version": "0.25.15", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -20,6 +20,7 @@ "./format": "./format/index.js", "./guard": "./guard/index.js", "./hash": "./hash/index.js", + "./system": "./system/index.js", "./value": "./value/index.js", ".": "./typebox.js" }, diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index df9dd4821..73c4814ca 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { ValueErrors, ValueError } from '../errors/index' +import { TypeSystem } from '../system/index' import { TypeGuard } from '../guard/index' import { Format } from '../format/index' import { Custom } from '../custom/index' @@ -185,26 +186,30 @@ export namespace TypeCompiler { } function* Object(schema: Types.TObject, value: string): IterableIterator { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` - if (IsNumber(schema.minProperties)) yield `(Object.keys(${value}).length >= ${schema.minProperties})` - if (IsNumber(schema.maxProperties)) yield `(Object.keys(${value}).length <= ${schema.maxProperties})` - const propertyKeys = globalThis.Object.keys(schema.properties) + if (TypeSystem.Kind === 'json-schema') { + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` + } else if (TypeSystem.Kind === 'structural') { + yield `(typeof ${value} === 'object' && ${value} !== null)` + } + if (IsNumber(schema.minProperties)) yield `(Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties})` + if (IsNumber(schema.maxProperties)) yield `(Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties})` + const propertyKeys = globalThis.Object.getOwnPropertyNames(schema.properties) if (schema.additionalProperties === false) { // Optimization: If the property key length matches the required keys length // then we only need check that the values property key length matches that // of the property key length. This is because exhaustive testing for values // will occur in subsequent property tests. if (schema.required && schema.required.length === propertyKeys.length) { - yield `(Object.keys(${value}).length === ${propertyKeys.length})` + yield `(Object.getOwnPropertyNames(${value}).length === ${propertyKeys.length})` } else { const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` - yield `(Object.keys(${value}).every(key => ${keys}.includes(key)))` + yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key)))` } } if (TypeGuard.TSchema(schema.additionalProperties)) { const expression = CreateExpression(schema.additionalProperties, 'value[key]') const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` - yield `(Object.keys(${value}).every(key => ${keys}.includes(key) || ${expression}))` + yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } for (const propertyKey of propertyKeys) { const memberExpression = Property.Check(propertyKey) ? `${value}.${propertyKey}` : `${value}['${propertyKey}']` @@ -223,10 +228,14 @@ export namespace TypeCompiler { } function* Record(schema: Types.TRecord, value: string): IterableIterator { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date))` + if (TypeSystem.Kind === 'json-schema') { + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date))` + } else if (TypeSystem.Kind === 'structural') { + yield `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date))` + } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${keyPattern}/)`) - yield `(Object.keys(${value}).every(key => ${local}.test(key)))` + yield `(Object.getOwnPropertyNames(${value}).every(key => ${local}.test(key)))` const expression = CreateExpression(valueSchema, 'value') yield `(Object.values(${value}).every(value => ${expression}))` } diff --git a/src/errors/errors.ts b/src/errors/errors.ts index ed39446a8..36f774ab2 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' +import { TypeSystem } from '../system/index' import { Format } from '../format/index' import { Custom } from '../custom/index' import { ValueHash } from '../hash/index' @@ -128,7 +129,7 @@ export namespace ValueErrors { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } } // prettier-ignore - if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Create(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { + if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = ValueHash.Create(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } } for (let i = 0; i < value.length; i++) { @@ -233,38 +234,43 @@ export namespace ValueErrors { } function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + if (TypeSystem.Kind === 'json-schema') { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } + } else if (TypeSystem.Kind === 'structural') { + if (!(typeof value === 'object' && value !== null)) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } } - if (IsNumber(schema.minProperties) && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + if (IsNumber(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } - if (IsNumber(schema.maxProperties) && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + if (IsNumber(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } } - const propertyKeys = globalThis.Object.keys(schema.properties) + const propertyNames = globalThis.Object.getOwnPropertyNames(schema.properties) if (schema.additionalProperties === false) { - for (const objectKey of globalThis.Object.keys(value)) { - if (!propertyKeys.includes(objectKey)) { - yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${objectKey}`, value: value[objectKey], message: `Unexpected property` } + for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { + if (!propertyNames.includes(propertyName)) { + yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${propertyName}`, value: value[propertyName], message: `Unexpected property` } } } } - if (schema.required && schema.required.length > 0) { - const objectKeys = globalThis.Object.keys(value) + const propertyNames = globalThis.Object.getOwnPropertyNames(value) for (const requiredKey of schema.required) { - if (objectKeys.includes(requiredKey)) continue + if (propertyNames.includes(requiredKey)) continue yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } } } if (typeof schema.additionalProperties === 'object') { - for (const objectKey of globalThis.Object.keys(value)) { - if (propertyKeys.includes(objectKey)) continue - yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${objectKey}`, value[objectKey]) + for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { + if (propertyNames.includes(propertyName)) continue + yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${propertyName}`, value[propertyName]) } } - for (const propertyKey of propertyKeys) { + for (const propertyKey of propertyNames) { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) @@ -283,12 +289,18 @@ export namespace ValueErrors { } function* Record(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + if (TypeSystem.Kind === 'json-schema') { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } + } else if (TypeSystem.Kind === 'structural') { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) - if (!globalThis.Object.keys(value).every((key) => regex.test(key))) { + if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { const numeric = keyPattern === '^(0|[1-9][0-9]*)$' const type = numeric ? ValueErrorType.RecordKeyNumeric : ValueErrorType.RecordKeyString const message = numeric ? 'Expected all object property keys to be numeric' : 'Expected all object property keys to be strings' diff --git a/src/system/index.ts b/src/system/index.ts new file mode 100644 index 000000000..9f65a6522 --- /dev/null +++ b/src/system/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/system + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './system' diff --git a/src/system/system.ts b/src/system/system.ts new file mode 100644 index 000000000..b2985a72b --- /dev/null +++ b/src/system/system.ts @@ -0,0 +1,59 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/system + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export class InvalidTypeSystemError extends Error { + constructor(typeSystem: string) { + super(`TypeSystemSettings: Unknown TypeSystem '${typeSystem}'`) + } +} + +export type TypeSystemKind = 'json-schema' | 'structural' + +class TypeSystemSettings { + private kind: TypeSystemKind + constructor() { + this.kind = 'json-schema' + } + /** + * `Experimental` Sets the type system kind used by TypeBox. By default TypeBox uses `json-schema` assertion + * rules to verify JavaScript values. If setting the type system to `structural`, TypeBox will use TypeScript + * structural checking rules enabling Arrays to be validated as Objects. + */ + public get Kind(): TypeSystemKind { + return this.kind + } + public set Kind(value: TypeSystemKind) { + if (!(value === 'json-schema' || value === 'structural')) { + throw new InvalidTypeSystemError(value) + } + this.kind = value + } +} + +/** TypeBox TypeSystem Settings */ +export const TypeSystem = new TypeSystemSettings() diff --git a/src/value/cast.ts b/src/value/cast.ts index 3f085611b..5fccc466d 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -250,10 +250,10 @@ export namespace ValueCast { } // additional schema properties if (typeof schema.additionalProperties === 'object') { - const propertyKeys = globalThis.Object.keys(schema.properties) - for (const objectKey of globalThis.Object.keys(value)) { - if (propertyKeys.includes(objectKey)) continue - result[objectKey] = Visit(schema.additionalProperties, references, value[objectKey]) + const propertyNames = globalThis.Object.getOwnPropertyNames(schema.properties) + for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { + if (propertyNames.includes(propertyName)) continue + result[propertyName] = Visit(schema.additionalProperties, references, value[propertyName]) } } return result @@ -266,8 +266,8 @@ export namespace ValueCast { function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value) || value instanceof globalThis.Date) return ValueCreate.Create(schema, references) - const subschemaKey = globalThis.Object.keys(schema.patternProperties)[0] - const subschema = schema.patternProperties[subschemaKey] + const subschemaPropertyName = globalThis.Object.getOwnPropertyNames(schema.patternProperties)[0] + const subschema = schema.patternProperties[subschemaPropertyName] const result = {} as Record for (const [propKey, propValue] of globalThis.Object.entries(value)) { result[propKey] = Visit(subschema, references, propValue) diff --git a/src/value/check.ts b/src/value/check.ts index f4f8d0f24..e1415462d 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' +import { TypeSystem } from '../system/index' import { Format } from '../format/index' import { Custom } from '../custom/index' import { ValueHash } from '../hash/index' @@ -70,6 +71,7 @@ export namespace ValueCheck { function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { return Visit(schema.returns, references, value.prototype) } + function Date(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { if (!(value instanceof globalThis.Date)) { return false @@ -153,31 +155,37 @@ export namespace ValueCheck { } function Object(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { - return false + if (TypeSystem.Kind === 'json-schema') { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + return false + } + } else if (TypeSystem.Kind === 'structural') { + if (!(typeof value === 'object' && value !== null)) { + return false + } } - if (IsNumber(schema.minProperties) && !(globalThis.Object.keys(value).length >= schema.minProperties)) { + if (IsNumber(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { return false } - if (IsNumber(schema.maxProperties) && !(globalThis.Object.keys(value).length <= schema.maxProperties)) { + if (IsNumber(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { return false } - const propertyKeys = globalThis.Object.keys(schema.properties) + const propertyKeys = globalThis.Object.getOwnPropertyNames(schema.properties) if (schema.additionalProperties === false) { // optimization: If the property key length matches the required keys length // then we only need check that the values property key length matches that // of the property key length. This is because exhaustive testing for values // will occur in subsequent property tests. - if (schema.required && schema.required.length === propertyKeys.length && !(globalThis.Object.keys(value).length === propertyKeys.length)) { + if (schema.required && schema.required.length === propertyKeys.length && !(globalThis.Object.getOwnPropertyNames(value).length === propertyKeys.length)) { return false } else { - if (!globalThis.Object.keys(value).every((key) => propertyKeys.includes(key))) { + if (!globalThis.Object.getOwnPropertyNames(value).every((key) => propertyKeys.includes(key))) { return false } } } if (typeof schema.additionalProperties === 'object') { - for (const objectKey of globalThis.Object.keys(value)) { + for (const objectKey of globalThis.Object.getOwnPropertyNames(value)) { if (propertyKeys.includes(objectKey)) continue if (!Visit(schema.additionalProperties as Types.TSchema, references, value[objectKey])) { return false @@ -206,12 +214,18 @@ export namespace ValueCheck { } function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { - return false + if (TypeSystem.Kind === 'json-schema') { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { + return false + } + } else if (TypeSystem.Kind === 'structural') { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { + return false + } } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) - if (!globalThis.Object.keys(value).every((key) => regex.test(key))) { + if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { return false } for (const propValue of globalThis.Object.values(value)) { diff --git a/test/runtime/compiler/any.ts b/test/runtime/compiler/any.ts index fdccc5f61..7519db00f 100644 --- a/test/runtime/compiler/any.ts +++ b/test/runtime/compiler/any.ts @@ -1,33 +1,33 @@ import { Type } from '@sinclair/typebox' -import { ok } from './validate' +import { Ok } from './validate' describe('type/compiler/Any', () => { it('Should validate number', () => { const T = Type.Any() - ok(T, 1) + Ok(T, 1) }) it('Should validate string', () => { const T = Type.Any() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate boolean', () => { const T = Type.Any() - ok(T, true) + Ok(T, true) }) it('Should validate array', () => { const T = Type.Any() - ok(T, [1, 2, 3]) + Ok(T, [1, 2, 3]) }) it('Should validate object', () => { const T = Type.Any() - ok(T, { a: 1, b: 2 }) + Ok(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Any() - ok(T, null) + Ok(T, null) }) it('Should validate undefined', () => { const T = Type.Any() - ok(T, undefined) + Ok(T, undefined) }) }) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index 61fc8171c..adff196f9 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -1,30 +1,30 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Array', () => { it('Should validate an array of any', () => { const T = Type.Array(Type.Any()) - ok(T, [0, true, 'hello', {}]) + Ok(T, [0, true, 'hello', {}]) }) it('Should not validate varying array when item is number', () => { const T = Type.Array(Type.Number()) - fail(T, [1, 2, 3, 'hello']) + Fail(T, [1, 2, 3, 'hello']) }) it('Should validate for an array of unions', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, [1, 'hello', 3, 'world']) + Ok(T, [1, 'hello', 3, 'world']) }) it('Should not validate for an array of unions where item is not in union.', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - fail(T, [1, 'hello', 3, 'world', true]) + Fail(T, [1, 'hello', 3, 'world', true]) }) it('Should validate for an empty array', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, []) + Ok(T, []) }) it('Should validate for an array of intersection types', () => { @@ -32,7 +32,7 @@ describe('type/compiler/Array', () => { const B = Type.Object({ b: Type.String() }) const C = Type.Intersect([A, B]) const T = Type.Array(C) - ok(T, [ + Ok(T, [ { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, @@ -44,7 +44,7 @@ describe('type/compiler/Array', () => { const B = Type.Object({ b: Type.String() }) const C = Type.Intersect([A, B], { additionalProperties: false }) const T = Type.Array(C) - fail(T, [ + Fail(T, [ { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello', c: 'additional' }, @@ -56,7 +56,7 @@ describe('type/compiler/Array', () => { const B = Type.Number() const C = Type.Tuple([A, B]) const T = Type.Array(C) - ok(T, [ + Ok(T, [ ['hello', 1], ['hello', 1], ['hello', 1], @@ -68,7 +68,7 @@ describe('type/compiler/Array', () => { const B = Type.Number() const C = Type.Tuple([A, B]) const T = Type.Array(C) - fail(T, [ + Fail(T, [ [1, 'hello'], [1, 'hello'], [1, 'hello'], @@ -77,28 +77,28 @@ describe('type/compiler/Array', () => { it('Should not validate array with failed minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) - fail(T, [0, 1]) + Fail(T, [0, 1]) }) it('Should not validate array with failed maxItems', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) - fail(T, [0, 1, 2, 3]) + Fail(T, [0, 1, 2, 3]) }) it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) - ok(T, [ + Ok(T, [ { x: 0, y: 1 }, { x: 1, y: 0 }, ]) }) it('Should not validate array with uniqueItems when items are not distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) - fail(T, [ + Fail(T, [ { x: 1, y: 0 }, { x: 1, y: 0 }, ]) }) it('Should not validate array with non uniqueItems', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) - fail(T, [0, 0]) + Fail(T, [0, 0]) }) }) diff --git a/test/runtime/compiler/boolean.ts b/test/runtime/compiler/boolean.ts index 07293041f..c0d7997fb 100644 --- a/test/runtime/compiler/boolean.ts +++ b/test/runtime/compiler/boolean.ts @@ -1,40 +1,40 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Boolean', () => { it('Should validate a boolean', () => { const T = Type.Boolean() - ok(T, true) - ok(T, false) + Ok(T, true) + Ok(T, false) }) it('Should not validate a number', () => { const T = Type.Boolean() - fail(T, 1) + Fail(T, 1) }) it('Should not validate a string', () => { const T = Type.Boolean() - fail(T, 'true') + Fail(T, 'true') }) it('Should not validate an array', () => { const T = Type.Boolean() - fail(T, [true]) + Fail(T, [true]) }) it('Should not validate an object', () => { const T = Type.Boolean() - fail(T, {}) + Fail(T, {}) }) it('Should not validate an null', () => { const T = Type.Boolean() - fail(T, null) + Fail(T, null) }) it('Should not validate an undefined', () => { const T = Type.Boolean() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/compiler/custom.ts b/test/runtime/compiler/custom.ts index 1a42cdd8c..4cba073d9 100644 --- a/test/runtime/compiler/custom.ts +++ b/test/runtime/compiler/custom.ts @@ -1,28 +1,28 @@ import { Custom } from '@sinclair/typebox/custom' import { Type, Kind } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Custom', () => { Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) - ok(T, 1n) + Ok(T, 1n) }) it('Should not validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) - fail(T, 1) + Fail(T, 1) }) it('Should validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), }) - ok(T, { x: 1n }) + Ok(T, { x: 1n }) }) it('Should not validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), }) - fail(T, { x: 1 }) + Fail(T, { x: 1 }) }) }) diff --git a/test/runtime/compiler/date.ts b/test/runtime/compiler/date.ts index d6c099e6c..ae8855bd8 100644 --- a/test/runtime/compiler/date.ts +++ b/test/runtime/compiler/date.ts @@ -1,61 +1,61 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Date', () => { it('Should not validate number', () => { const T = Type.Date() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Date() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Date() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Date() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Date() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Date() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Date() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate Date', () => { const T = Type.Date() - ok(T, new Date()) + Ok(T, new Date()) }) it('Should validate Date minimumTimestamp', () => { const T = Type.Date({ minimumTimestamp: 10 }) - fail(T, new Date(9)) - ok(T, new Date(10)) + Fail(T, new Date(9)) + Ok(T, new Date(10)) }) it('Should validate Date maximumTimestamp', () => { const T = Type.Date({ maximumTimestamp: 10 }) - ok(T, new Date(10)) - fail(T, new Date(11)) + Ok(T, new Date(10)) + Fail(T, new Date(11)) }) it('Should validate Date exclusiveMinimumTimestamp', () => { const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) - fail(T, new Date(10)) - ok(T, new Date(11)) + Fail(T, new Date(10)) + Ok(T, new Date(11)) }) it('Should validate Date exclusiveMaximumTimestamp', () => { const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) - ok(T, new Date(9)) - fail(T, new Date(10)) + Ok(T, new Date(9)) + Fail(T, new Date(10)) }) }) diff --git a/test/runtime/compiler/enum.ts b/test/runtime/compiler/enum.ts index 736cee47b..08460de6f 100644 --- a/test/runtime/compiler/enum.ts +++ b/test/runtime/compiler/enum.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Enum', () => { it('Should validate when emum uses default numeric values', () => { @@ -8,8 +8,8 @@ describe('type/compiler/Enum', () => { Bar, // = 1 } const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 1) + Ok(T, 0) + Ok(T, 1) }) it('Should not validate when given enum values are not numeric', () => { enum Kind { @@ -17,8 +17,8 @@ describe('type/compiler/Enum', () => { Bar, // = 1 } const T = Type.Enum(Kind) - fail(T, 'Foo') - fail(T, 'Bar') + Fail(T, 'Foo') + Fail(T, 'Bar') }) it('Should validate when emum has defined string values', () => { @@ -27,8 +27,8 @@ describe('type/compiler/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - ok(T, 'foo') - ok(T, 'bar') + Ok(T, 'foo') + Ok(T, 'bar') }) it('Should not validate when emum has defined string values and user passes numeric', () => { @@ -37,8 +37,8 @@ describe('type/compiler/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - fail(T, 0) - fail(T, 1) + Fail(T, 0) + Fail(T, 1) }) it('Should validate when enum has one or more string values', () => { @@ -47,10 +47,10 @@ describe('type/compiler/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 'bar') - fail(T, 'baz') - fail(T, 'Foo') - fail(T, 1) + Ok(T, 0) + Ok(T, 'bar') + Fail(T, 'baz') + Fail(T, 'Foo') + Fail(T, 1) }) }) diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts index ba1110be9..1d3b85dbb 100644 --- a/test/runtime/compiler/integer.ts +++ b/test/runtime/compiler/integer.ts @@ -1,71 +1,71 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Integer', () => { it('Should not validate number', () => { const T = Type.Integer() - fail(T, 3.14) + Fail(T, 3.14) }) it('Should validate integer', () => { const T = Type.Integer() - ok(T, 1) + Ok(T, 1) }) it('Should not validate string', () => { const T = Type.Integer() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Integer() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Integer() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Integer() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Integer() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Integer() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate minimum', () => { const T = Type.Integer({ minimum: 10 }) - fail(T, 9) - ok(T, 10) + Fail(T, 9) + Ok(T, 10) }) it('Should validate maximum', () => { const T = Type.Integer({ maximum: 10 }) - ok(T, 10) - fail(T, 11) + Ok(T, 10) + Fail(T, 11) }) it('Should validate Date exclusiveMinimum', () => { const T = Type.Integer({ exclusiveMinimum: 10 }) - fail(T, 10) - ok(T, 11) + Fail(T, 10) + Ok(T, 11) }) it('Should validate Date exclusiveMaximum', () => { const T = Type.Integer({ exclusiveMaximum: 10 }) - ok(T, 9) - fail(T, 10) + Ok(T, 9) + Fail(T, 10) }) it('Should not validate NaN', () => { - fail(Type.Integer(), NaN) + Fail(Type.Integer(), NaN) }) }) diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index f59d9adae..5cb524514 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -1,19 +1,19 @@ import { Type, Static } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Intersect', () => { it('Should intersect two objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Intersect([A, B], { additionalProperties: false }) - ok(T, { a: 'hello', b: 42 }) + Ok(T, { a: 'hello', b: 42 }) }) it('Should intersect with partial', () => { const A = Type.Partial(Type.Object({ a: Type.Number() })) const B = Type.Partial(Type.Object({ b: Type.Number() })) const P = Type.Intersect([A, B], { additionalProperties: false }) - ok(P, { a: 1, b: 2 }) + Ok(P, { a: 1, b: 2 }) // ok(P, { a: 1 }) // ok(P, { b: 1 }) // ok(P, {}) @@ -24,18 +24,18 @@ describe('type/compiler/Intersect', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Number() }) const P = Type.Intersect([A, B]) - ok(P, { a: 1 }) - fail(P, { a: 'hello' }) - fail(P, {}) + Ok(P, { a: 1 }) + Fail(P, { a: 'hello' }) + Fail(P, {}) }) it('Should intersect with overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const T = Type.Intersect([A, B]) - ok(T, { a: 1 }) - ok(T, { a: 'hello' }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Fail(T, {}) }) it('Should intersect with deeply nest overlapping varying type', () => { @@ -44,12 +44,12 @@ describe('type/compiler/Intersect', () => { const C = Type.Object({ a: Type.Boolean() }) const D = Type.Object({ a: Type.Null() }) const T = Type.Intersect([A, B, C, D]) - ok(T, { a: 1 }) - ok(T, { a: 'hello' }) - ok(T, { a: false }) - ok(T, { a: null }) - fail(T, { a: [] }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Ok(T, { a: false }) + Ok(T, { a: null }) + Fail(T, { a: [] }) + Fail(T, {}) }) it('Should pick from intersected type', () => { @@ -58,8 +58,8 @@ describe('type/compiler/Intersect', () => { const C = Type.Object({ z: Type.Number() }) const T = Type.Intersect([A, B, C]) const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) - ok(P, { x: 1, y: 1 }) - fail(P, { x: 1, y: 1, z: 1 }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) }) it('Should omit from intersected type', () => { @@ -68,16 +68,16 @@ describe('type/compiler/Intersect', () => { const C = Type.Object({ z: Type.Number() }) const T = Type.Intersect([A, B, C]) const P = Type.Omit(T, ['z'], { additionalProperties: false }) - ok(P, { x: 1, y: 1 }) - fail(P, { x: 1, y: 1, z: 1 }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) }) it('Should intersect nested object properties', () => { const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) const T = Type.Intersect([A, B]) - ok(T, { x: { x: 1 } }) - ok(T, { x: { x: 'hello' } }) - fail(T, { x: { x: false } }) + Ok(T, { x: { x: 1 } }) + Ok(T, { x: { x: 'hello' } }) + Fail(T, { x: { x: false } }) }) }) diff --git a/test/runtime/compiler/keyof.ts b/test/runtime/compiler/keyof.ts index 3920b1485..e96ad8762 100644 --- a/test/runtime/compiler/keyof.ts +++ b/test/runtime/compiler/keyof.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/KeyOf', () => { @@ -11,10 +11,10 @@ describe('type/compiler/KeyOf', () => { z: Type.Number(), }), ) - ok(T, 'x') - ok(T, 'y') - ok(T, 'z') - fail(T, 'w') + Ok(T, 'x') + Ok(T, 'y') + Ok(T, 'z') + Fail(T, 'w') }) it('Should validate when using pick', () => { @@ -28,9 +28,9 @@ describe('type/compiler/KeyOf', () => { ['x', 'y'], ), ) - ok(T, 'x') - ok(T, 'y') - fail(T, 'z') + Ok(T, 'x') + Ok(T, 'y') + Fail(T, 'z') }) it('Should validate when using omit', () => { @@ -45,8 +45,8 @@ describe('type/compiler/KeyOf', () => { ), ) - fail(T, 'x') - fail(T, 'y') - ok(T, 'z') + Fail(T, 'x') + Fail(T, 'y') + Ok(T, 'z') }) }) diff --git a/test/runtime/compiler/literal.ts b/test/runtime/compiler/literal.ts index 1aa46921f..a7251b789 100644 --- a/test/runtime/compiler/literal.ts +++ b/test/runtime/compiler/literal.ts @@ -1,43 +1,43 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Literal', () => { it('Should validate literal number', () => { const T = Type.Literal(42) - ok(T, 42) + Ok(T, 42) }) it('Should validate literal string', () => { const T = Type.Literal('hello') - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate literal boolean', () => { const T = Type.Literal(true) - ok(T, true) + Ok(T, true) }) it('Should not validate invalid literal number', () => { const T = Type.Literal(42) - fail(T, 43) + Fail(T, 43) }) it('Should not validate invalid literal string', () => { const T = Type.Literal('hello') - fail(T, 'world') + Fail(T, 'world') }) it('Should not validate invalid literal boolean', () => { const T = Type.Literal(false) - fail(T, true) + Fail(T, true) }) it('Should validate literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - ok(T, 42) - ok(T, 'hello') + Ok(T, 42) + Ok(T, 'hello') }) it('Should not validate invalid literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - fail(T, 43) - fail(T, 'world') + Fail(T, 43) + Fail(T, 'world') }) }) diff --git a/test/runtime/compiler/never.ts b/test/runtime/compiler/never.ts index 8be660cdf..a19036774 100644 --- a/test/runtime/compiler/never.ts +++ b/test/runtime/compiler/never.ts @@ -1,36 +1,36 @@ import { Type } from '@sinclair/typebox' -import { fail } from './validate' +import { Fail } from './validate' describe('type/compiler/Never', () => { it('Should not validate number', () => { const T = Type.Never() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Never() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Never() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Never() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Never() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Never() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Never() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/compiler/null.ts b/test/runtime/compiler/null.ts index a3e6009c4..1253ea64a 100644 --- a/test/runtime/compiler/null.ts +++ b/test/runtime/compiler/null.ts @@ -1,39 +1,39 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Null', () => { it('Should not validate number', () => { const T = Type.Null() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Null() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Null() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Null() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Null() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Null() - ok(T, null) + Ok(T, null) }) it('Should not validate undefined', () => { const T = Type.Null() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts index b92e944b8..90a229f64 100644 --- a/test/runtime/compiler/number.ts +++ b/test/runtime/compiler/number.ts @@ -1,67 +1,67 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Number', () => { it('Should validate number', () => { const T = Type.Number() - ok(T, 3.14) + Ok(T, 3.14) }) it('Should validate integer', () => { const T = Type.Number() - ok(T, 1) + Ok(T, 1) }) it('Should not validate string', () => { const T = Type.Number() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Number() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Number() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Number() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Number() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Number() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate minimum', () => { const T = Type.Number({ minimum: 10 }) - fail(T, 9) - ok(T, 10) + Fail(T, 9) + Ok(T, 10) }) it('Should validate maximum', () => { const T = Type.Number({ maximum: 10 }) - ok(T, 10) - fail(T, 11) + Ok(T, 10) + Fail(T, 11) }) it('Should validate Date exclusiveMinimum', () => { const T = Type.Number({ exclusiveMinimum: 10 }) - fail(T, 10) - ok(T, 11) + Fail(T, 10) + Ok(T, 11) }) it('Should validate Date exclusiveMaximum', () => { const T = Type.Number({ exclusiveMaximum: 10 }) - ok(T, 9) - fail(T, 10) + Ok(T, 9) + Fail(T, 10) }) it('Should not validate NaN', () => { - fail(Type.Number(), NaN) + Fail(Type.Number(), NaN) }) }) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index c6b4aa809..924b75c86 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -1,30 +1,30 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Object', () => { it('Should not validate a number', () => { const T = Type.Object({}) - fail(T, 42) + Fail(T, 42) }) it('Should not validate a string', () => { const T = Type.Object({}) - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate a boolean', () => { const T = Type.Object({}) - fail(T, true) + Fail(T, true) }) it('Should not validate a null', () => { const T = Type.Object({}) - fail(T, null) + Fail(T, null) }) it('Should not validate an array', () => { const T = Type.Object({}) - fail(T, [1, 2]) + Fail(T, [1, 2]) }) it('Should validate with correct property values', () => { @@ -35,7 +35,7 @@ describe('type/compiler/Object', () => { d: Type.Array(Type.Number()), e: Type.Object({ x: Type.Number(), y: Type.Number() }), }) - ok(T, { + Ok(T, { a: 10, b: 'hello', c: true, @@ -52,7 +52,7 @@ describe('type/compiler/Object', () => { d: Type.Array(Type.Number()), e: Type.Object({ x: Type.Number(), y: Type.Number() }), }) - fail(T, { + Fail(T, { a: 'not a number', // error b: 'hello', c: true, @@ -66,7 +66,7 @@ describe('type/compiler/Object', () => { a: Type.Number(), b: Type.String(), }) - ok(T, { + Ok(T, { a: 1, b: 'hello', c: true, @@ -81,9 +81,9 @@ describe('type/compiler/Object', () => { }, { additionalProperties: false, minProperties: 1 }, ) - ok(T, { a: 1 }) - ok(T, { b: 'hello' }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { b: 'hello' }) + Fail(T, {}) }) it('Should not allow 3 properties if maxProperties is set to 2', () => { @@ -95,9 +95,9 @@ describe('type/compiler/Object', () => { }, { additionalProperties: false, maxProperties: 2 }, ) - ok(T, { a: 1 }) - ok(T, { a: 1, b: 'hello' }) - fail(T, { + Ok(T, { a: 1 }) + Ok(T, { a: 1, b: 'hello' }) + Fail(T, { a: 1, b: 'hello', c: true, @@ -112,7 +112,7 @@ describe('type/compiler/Object', () => { }, { additionalProperties: false }, ) - fail(T, { + Fail(T, { a: 1, b: 'hello', c: true, @@ -121,8 +121,8 @@ describe('type/compiler/Object', () => { it('Should not allow properties for an empty object when additionalProperties is false', () => { const T = Type.Object({}, { additionalProperties: false }) - ok(T, {}) - fail(T, { a: 10 }) + Ok(T, {}) + Fail(T, { a: 10 }) }) it('Should validate with non-syntax property keys', () => { @@ -132,7 +132,7 @@ describe('type/compiler/Object', () => { '$-leading': Type.Literal(3), '!@#$%^&*(': Type.Literal(4), }) - ok(T, { + Ok(T, { 'with-hyphen': 1, '0-leading': 2, '$-leading': 3, @@ -150,12 +150,12 @@ describe('type/compiler/Object', () => { additionalProperties: Type.String(), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: 'hello', }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, @@ -171,12 +171,12 @@ describe('type/compiler/Object', () => { additionalProperties: Type.Array(Type.Number()), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: [0, 1, 2], }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, @@ -194,12 +194,12 @@ describe('type/compiler/Object', () => { }), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: { z: 1 }, }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts index 29b65bab7..42634995f 100644 --- a/test/runtime/compiler/omit.ts +++ b/test/runtime/compiler/omit.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/Omit', () => { @@ -13,7 +13,7 @@ describe('type/compiler/Omit', () => { { additionalProperties: false }, ) const T = Type.Omit(A, ['z']) - ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1, y: 1 }) }) it('Should remove required properties on the target schema', () => { @@ -54,7 +54,7 @@ describe('type/compiler/Omit', () => { y: Type.Number(), }) const T = Type.Omit(A, Type.KeyOf(B), { additionalProperties: false }) - ok(T, { z: 0 }) - fail(T, { x: 0, y: 0, z: 0 }) + Ok(T, { z: 0 }) + Fail(T, { x: 0, y: 0, z: 0 }) }) }) diff --git a/test/runtime/compiler/optional.ts b/test/runtime/compiler/optional.ts index abbcb86c3..a78384e67 100644 --- a/test/runtime/compiler/optional.ts +++ b/test/runtime/compiler/optional.ts @@ -1,6 +1,6 @@ import { strictEqual } from 'assert' import { Type } from '@sinclair/typebox' -import { ok } from './validate' +import { Ok } from './validate' describe('type/compiler/Optional', () => { it('Should validate object with optional', () => { @@ -11,8 +11,8 @@ describe('type/compiler/Optional', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) + Ok(T, { b: 'world' }) }) it('Should remove required value from schema', () => { const T = Type.Object( diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index c060a0e20..6f11fb90b 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -1,5 +1,5 @@ import { Type, Modifier } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/Partial', () => { @@ -13,10 +13,10 @@ describe('type/compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - ok(T, { x: 1, y: 1, z: 1 }) - ok(T, { x: 1, y: 1 }) - ok(T, { x: 1 }) - ok(T, {}) + Ok(T, { x: 1, y: 1, z: 1 }) + Ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1 }) + Ok(T, {}) }) it('Should update modifier types correctly when converting to partial', () => { diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts index c66cbb912..e6c9eb805 100644 --- a/test/runtime/compiler/pick.ts +++ b/test/runtime/compiler/pick.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/Pick', () => { @@ -13,7 +13,7 @@ describe('type/compiler/Pick', () => { { additionalProperties: false }, ) const T = Type.Pick(Vector3, ['x', 'y']) - ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1, y: 1 }) }) it('Should remove required properties on the target schema', () => { @@ -54,7 +54,7 @@ describe('type/compiler/Pick', () => { y: Type.Number(), }) const T = Type.Pick(A, Type.KeyOf(B), { additionalProperties: false }) - ok(T, { x: 0, y: 0 }) - fail(T, { x: 0, y: 0, z: 0 }) + Ok(T, { x: 0, y: 0 }) + Fail(T, { x: 0, y: 0, z: 0 }) }) }) diff --git a/test/runtime/compiler/readonly-optional.ts b/test/runtime/compiler/readonly-optional.ts index 33b78d762..90ec45097 100644 --- a/test/runtime/compiler/readonly-optional.ts +++ b/test/runtime/compiler/readonly-optional.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/ReadonlyOptional', () => { @@ -11,8 +11,8 @@ describe('type/compiler/ReadonlyOptional', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) + Ok(T, { b: 'world' }) }) it('Should remove required value from schema', () => { const T = Type.Object( diff --git a/test/runtime/compiler/readonly.ts b/test/runtime/compiler/readonly.ts index 1ad7ff703..3edbab2a3 100644 --- a/test/runtime/compiler/readonly.ts +++ b/test/runtime/compiler/readonly.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, strictEqual } from 'assert' import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Readonly', () => { it('Should validate object with readonly', () => { @@ -11,7 +11,7 @@ describe('type/compiler/Readonly', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) }) it('Should retain required array on object', () => { diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 608919a69..d73a14848 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -1,27 +1,27 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Record', () => { it('Should validate when all property values are numbers', () => { const T = Type.Record(Type.String(), Type.Number()) - ok(T, { a: 1, b: 2, c: 3 }) + Ok(T, { a: 1, b: 2, c: 3 }) }) it('Should validate when all property keys are strings', () => { const T = Type.Record(Type.String(), Type.Number()) - ok(T, { a: 1, b: 2, c: 3, '0': 4 }) + Ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) - ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) + Ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) it('Should not validate when specifying string union literals when additionalProperties is false', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) - fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) + Fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) it('Should validate for keyof records', () => { @@ -31,7 +31,7 @@ describe('type/compiler/Record', () => { c: Type.String(), }) const R = Type.Record(Type.KeyOf(T), Type.Number()) - ok(R, { a: 1, b: 2, c: 3 }) + Ok(R, { a: 1, b: 2, c: 3 }) }) it('Should not validate for unknown key via keyof', () => { @@ -41,13 +41,13 @@ describe('type/compiler/Record', () => { c: Type.String(), }) const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) - fail(R, { a: 1, b: 2, c: 3, d: 4 }) + Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) it('Should should validate when specifying regular expressions', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) - ok(T, { + Ok(T, { op_a: 1, op_b: 2, op_c: 3, @@ -57,7 +57,7 @@ describe('type/compiler/Record', () => { it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) - fail(T, { + Fail(T, { op_a: 1, op_b: 2, aop_c: 3, @@ -70,17 +70,17 @@ describe('type/compiler/Record', () => { it('Should validate when all property keys are integers', () => { const T = Type.Record(Type.Integer(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are integers, but one property is a string with varying type', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { + Fail(T, { '00': 1, '01': 2, '02': 3, @@ -90,7 +90,7 @@ describe('type/compiler/Record', () => { it('Should not validate if passing a signed integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { + Fail(T, { '-0': 1, '-1': 2, '-2': 3, @@ -104,17 +104,17 @@ describe('type/compiler/Record', () => { it('Should validate when all property keys are numbers', () => { const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { + Fail(T, { '00': 1, '01': 2, '02': 3, @@ -124,7 +124,7 @@ describe('type/compiler/Record', () => { it('Should not validate if passing a signed numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { + Fail(T, { '-0': 1, '-1': 2, '-2': 3, @@ -134,6 +134,6 @@ describe('type/compiler/Record', () => { it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) }) diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts index 0bd48baf0..3cc78be1a 100644 --- a/test/runtime/compiler/recursive.ts +++ b/test/runtime/compiler/recursive.ts @@ -1,6 +1,6 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Recursive', () => { it('Should generate default ordinal $id if not specified', () => { @@ -32,7 +32,7 @@ describe('type/compiler/Recursive', () => { nodes: Type.Array(Self), }), ) - ok(Node, { + Ok(Node, { id: 'A', nodes: [ { id: 'B', nodes: [] }, @@ -50,7 +50,7 @@ describe('type/compiler/Recursive', () => { }), ), ]) - ok(Node, [ + Ok(Node, [ { id: 'A', nodes: [ @@ -70,7 +70,7 @@ describe('type/compiler/Recursive', () => { }), ), ]) - fail(Node, [ + Fail(Node, [ { id: 'A', nodes: [ diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index b816f895c..632ac0fe7 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { Assert } from '../assert/index' describe('type/compiler/Ref', () => { @@ -13,7 +13,7 @@ describe('type/compiler/Ref', () => { { $id: Assert.nextId() }, ) const R = Type.Ref(T) - ok( + Ok( R, { x: 1, @@ -34,7 +34,7 @@ describe('type/compiler/Ref', () => { { $id: Assert.nextId() }, ) const R = Type.Ref(T) - fail( + Fail( R, { x: 1, @@ -62,9 +62,9 @@ describe('type/compiler/Ref', () => { { $id: 'T' }, ) - ok(T, { x: 1, y: 2, z: 3 }, [R]) - ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) - fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) - fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) + Ok(T, { x: 1, y: 2, z: 3 }, [R]) + Ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) + Fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) + Fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) }) }) diff --git a/test/runtime/compiler/regex.ts b/test/runtime/compiler/regex.ts index 0213a8216..5f3418047 100644 --- a/test/runtime/compiler/regex.ts +++ b/test/runtime/compiler/regex.ts @@ -1,35 +1,35 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/RegEx', () => { it('Should validate numeric value', () => { const T = Type.RegEx(/[012345]/) - ok(T, '0') - ok(T, '1') - ok(T, '2') - ok(T, '3') - ok(T, '4') - ok(T, '5') + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') }) it('Should validate true or false string value', () => { const T = Type.RegEx(/true|false/) - ok(T, 'true') - ok(T, 'true') - ok(T, 'true') - ok(T, 'false') - ok(T, 'false') - ok(T, 'false') - fail(T, '6') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') }) it('Should not validate failed regex test', () => { const T = Type.RegEx(/true|false/) - fail(T, 'unknown') + Fail(T, 'unknown') }) it('Should pass numeric 5 digit test', () => { const T = Type.RegEx(/[\d]{5}/) - ok(T, '12345') + Ok(T, '12345') }) }) diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index 56d27bc5a..fc6f32f83 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -1,5 +1,5 @@ import { Type, Modifier } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/compiler/Required', () => { @@ -13,10 +13,10 @@ describe('type/compiler/compiler/Required', () => { { additionalProperties: false }, ) const T = Type.Required(A) - ok(T, { x: 1, y: 1, z: 1 }) - fail(T, { x: 1, y: 1 }) - fail(T, { x: 1 }) - fail(T, {}) + Ok(T, { x: 1, y: 1, z: 1 }) + Fail(T, { x: 1, y: 1 }) + Fail(T, { x: 1 }) + Fail(T, {}) }) it('Should update modifier types correctly when converting to required', () => { diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts index f90398e7b..2f0005afb 100644 --- a/test/runtime/compiler/string.ts +++ b/test/runtime/compiler/string.ts @@ -1,65 +1,65 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/String', () => { it('Should not validate number', () => { const T = Type.String() - fail(T, 1) + Fail(T, 1) }) it('Should validate string', () => { const T = Type.String() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.String() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.String() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.String() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.String() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.String() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate string format as email', () => { const T = Type.String({ format: 'email' }) - ok(T, 'name@domain.com') + Ok(T, 'name@domain.com') }) it('Should validate string format as uuid', () => { const T = Type.String({ format: 'uuid' }) - ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') + Ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') }) it('Should validate string format as iso8601 date', () => { const T = Type.String({ format: 'date-time' }) - ok(T, '2021-06-11T20:30:00-04:00') + Ok(T, '2021-06-11T20:30:00-04:00') }) it('Should validate minLength', () => { const T = Type.String({ minLength: 4 }) - ok(T, '....') - fail(T, '...') + Ok(T, '....') + Fail(T, '...') }) it('Should validate maxLength', () => { const T = Type.String({ maxLength: 4 }) - ok(T, '....') - fail(T, '.....') + Ok(T, '....') + Fail(T, '.....') }) it('Should pass numeric 5 digit test', () => { const T = Type.String({ pattern: '[\\d]{5}' }) - ok(T, '12345') + Ok(T, '12345') }) }) diff --git a/test/runtime/compiler/tuple.ts b/test/runtime/compiler/tuple.ts index 574410d8b..63a436182 100644 --- a/test/runtime/compiler/tuple.ts +++ b/test/runtime/compiler/tuple.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { Assert } from '../assert' describe('type/compiler/Tuple', () => { @@ -7,56 +7,56 @@ describe('type/compiler/Tuple', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) - ok(T, ['hello', 42]) + Ok(T, ['hello', 42]) }) it('Should not validate tuple of [string, number] when reversed', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) - fail(T, [42, 'hello']) + Fail(T, [42, 'hello']) }) it('Should validate with empty tuple', () => { const T = Type.Tuple([]) - ok(T, []) + Ok(T, []) }) it('Should not validate with empty tuple with more items', () => { const T = Type.Tuple([]) - fail(T, [1]) + Fail(T, [1]) }) it('Should not validate with empty tuple with less items', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) - fail(T, [1]) + Fail(T, [1]) }) it('Should validate tuple of objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - ok(T, [{ a: 'hello' }, { b: 42 }]) + Ok(T, [{ a: 'hello' }, { b: 42 }]) }) it('Should not validate tuple of objects when reversed', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ b: 42 }, { a: 'hello' }]) + Fail(T, [{ b: 42 }, { a: 'hello' }]) }) it('Should not validate tuple when array is less than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ a: 'hello' }]) + Fail(T, [{ a: 'hello' }]) }) it('Should not validate tuple when array is greater than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) + Fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) }) }) diff --git a/test/runtime/compiler/uint8array.ts b/test/runtime/compiler/uint8array.ts index 9a552e53d..ae94d7872 100644 --- a/test/runtime/compiler/uint8array.ts +++ b/test/runtime/compiler/uint8array.ts @@ -1,56 +1,56 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Uint8Array', () => { it('Should not validate number', () => { const T = Type.Uint8Array() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Uint8Array() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Uint8Array() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Uint8Array() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Uint8Array() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Uint8Array() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Uint8Array() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate Uint8Array', () => { const T = Type.Uint8Array() - ok(T, new Uint8Array(100)) + Ok(T, new Uint8Array(100)) }) it('Should validate minByteLength', () => { const T = Type.Uint8Array({ minByteLength: 4 }) - ok(T, new Uint8Array(4)) - fail(T, new Uint8Array(3)) + Ok(T, new Uint8Array(4)) + Fail(T, new Uint8Array(3)) }) it('Should validate maxByteLength', () => { const T = Type.Uint8Array({ maxByteLength: 4 }) - ok(T, new Uint8Array(4)) - fail(T, new Uint8Array(5)) + Ok(T, new Uint8Array(4)) + Fail(T, new Uint8Array(5)) }) }) diff --git a/test/runtime/compiler/union.ts b/test/runtime/compiler/union.ts index fcf751910..8ad6e4f64 100644 --- a/test/runtime/compiler/union.ts +++ b/test/runtime/compiler/union.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Union', () => { it('Should validate union of string, number and boolean', () => { @@ -7,56 +7,56 @@ describe('type/compiler/Union', () => { const B = Type.Number() const C = Type.Boolean() const T = Type.Union([A, B, C]) - ok(T, 'hello') - ok(T, true) - ok(T, 42) + Ok(T, 'hello') + Ok(T, true) + Ok(T, 42) }) it('Should validate union of objects', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello' }) + Ok(T, { b: 'world' }) }) it('Should fail to validate for descriminated union types', () => { const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) const T = Type.Union([A, B]) - fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } - fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } + Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } + Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } }) it('Should validate union of objects where properties overlap', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) // A - ok(T, { a: 'hello', b: 'world' }) // B + Ok(T, { a: 'hello' }) // A + Ok(T, { a: 'hello', b: 'world' }) // B }) it('Should validate union of overlapping property of varying type', () => { const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello', b: 42 }) // A - ok(T, { a: 'hello', b: 'world' }) // B + Ok(T, { a: 'hello', b: 42 }) // A + Ok(T, { a: 'hello', b: 'world' }) // B }) it('Should validate union of literal strings', () => { const A = Type.Literal('hello') const B = Type.Literal('world') const T = Type.Union([A, B]) - ok(T, 'hello') // A - ok(T, 'world') // B + Ok(T, 'hello') // A + Ok(T, 'world') // B }) it('Should not validate union of literal strings for unknown string', () => { const A = Type.Literal('hello') const B = Type.Literal('world') const T = Type.Union([A, B]) - fail(T, 'foo') // A - fail(T, 'bar') // B + Fail(T, 'foo') // A + Fail(T, 'bar') // B }) }) diff --git a/test/runtime/compiler/unknown.ts b/test/runtime/compiler/unknown.ts index 7f3cb116d..30376d8e3 100644 --- a/test/runtime/compiler/unknown.ts +++ b/test/runtime/compiler/unknown.ts @@ -1,33 +1,33 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Unknown', () => { it('Should validate number', () => { const T = Type.Any() - ok(T, 1) + Ok(T, 1) }) it('Should validate string', () => { const T = Type.Any() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate boolean', () => { const T = Type.Any() - ok(T, true) + Ok(T, true) }) it('Should validate array', () => { const T = Type.Any() - ok(T, [1, 2, 3]) + Ok(T, [1, 2, 3]) }) it('Should validate object', () => { const T = Type.Any() - ok(T, { a: 1, b: 2 }) + Ok(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Any() - ok(T, null) + Ok(T, null) }) it('Should validate undefined', () => { const T = Type.Any() - ok(T, undefined) + Ok(T, undefined) }) }) diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index 4d2ba2f69..c242768a3 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -63,7 +63,7 @@ Format.Set('email', (value) => EMAIL.test(value)) Format.Set('uuid', (value) => UUID.test(value)) Format.Set('date-time', (value) => isDateTime(value, true)) -export function ok(schema: T, data: unknown, references: any[] = []) { +export function Ok(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) if (result !== Value.Check(schema, references, data)) { @@ -73,6 +73,10 @@ export function ok(schema: T, data: unknown, references: any[ const errors = [...Value.Errors(schema, references, data)] if (errors.length === 0) throw Error('expected at least 1 error') } + if (result === true) { + const errors = [...Value.Errors(schema, references, data)] + if (errors.length > 0) throw Error('expected no errors') + } if (!result) { console.log('---------------------------') console.log('type') @@ -90,13 +94,17 @@ export function ok(schema: T, data: unknown, references: any[ } } -export function fail(schema: T, data: unknown, references: any[] = []) { +export function Fail(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) if (result === false) { const errors = [...Value.Errors(schema, references, data)] if (errors.length === 0) throw Error('expected at least 1 error') } + if (result === true) { + const errors = [...Value.Errors(schema, references, data)] + if (errors.length > 0) throw Error('expected no errors') + } if (result) { console.log('---------------------------') console.log('type') diff --git a/test/runtime/compiler/void.ts b/test/runtime/compiler/void.ts index 3beefc21d..a5e80992e 100644 --- a/test/runtime/compiler/void.ts +++ b/test/runtime/compiler/void.ts @@ -1,39 +1,39 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/compiler/Void', () => { it('Should not validate number', () => { const T = Type.Void() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Void() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Void() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Void() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Void() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Null() - ok(T, null) + Ok(T, null) }) it('Should not validate undefined', () => { const T = Type.Void() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/index.ts b/test/runtime/index.ts index f5ed2d8cd..095140dab 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -5,4 +5,5 @@ import './format/index' import './guard/index' import './hash/index' import './schema/index' +import './settings/index' import './value/index' diff --git a/test/runtime/schema/any.ts b/test/runtime/schema/any.ts index 6ffcb54c5..3e857c831 100644 --- a/test/runtime/schema/any.ts +++ b/test/runtime/schema/any.ts @@ -1,33 +1,33 @@ import { Type } from '@sinclair/typebox' -import { ok } from './validate' +import { Ok } from './validate' describe('type/schema/Any', () => { it('Should validate number', () => { const T = Type.Any() - ok(T, 1) + Ok(T, 1) }) it('Should validate string', () => { const T = Type.Any() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate boolean', () => { const T = Type.Any() - ok(T, true) + Ok(T, true) }) it('Should validate array', () => { const T = Type.Any() - ok(T, [1, 2, 3]) + Ok(T, [1, 2, 3]) }) it('Should validate object', () => { const T = Type.Any() - ok(T, { a: 1, b: 2 }) + Ok(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Any() - ok(T, null) + Ok(T, null) }) it('Should validate undefined', () => { const T = Type.Any() - ok(T, undefined) + Ok(T, undefined) }) }) diff --git a/test/runtime/schema/array.ts b/test/runtime/schema/array.ts index 57df3662a..3b9b972b6 100644 --- a/test/runtime/schema/array.ts +++ b/test/runtime/schema/array.ts @@ -1,30 +1,30 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Array', () => { it('Should validate an array of any', () => { const T = Type.Array(Type.Any()) - ok(T, [0, true, 'hello', {}]) + Ok(T, [0, true, 'hello', {}]) }) it('Should not validate varying array when item is number', () => { const T = Type.Array(Type.Number()) - fail(T, [1, 2, 3, 'hello']) + Fail(T, [1, 2, 3, 'hello']) }) it('Should validate for an array of unions', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, [1, 'hello', 3, 'world']) + Ok(T, [1, 'hello', 3, 'world']) }) it('Should not validate for an array of unions where item is not in union.', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - fail(T, [1, 'hello', 3, 'world', true]) + Fail(T, [1, 'hello', 3, 'world', true]) }) it('Should validate for an empty array', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) - ok(T, []) + Ok(T, []) }) it('Should validate for an array of intersection types', () => { @@ -32,7 +32,7 @@ describe('type/schema/Array', () => { const B = Type.Object({ b: Type.String() }) const C = Type.Intersect([A, B]) const T = Type.Array(C) - ok(T, [ + Ok(T, [ { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, @@ -44,7 +44,7 @@ describe('type/schema/Array', () => { const B = Type.Object({ b: Type.String() }) const C = Type.Intersect([A, B], { additionalProperties: false }) const T = Type.Array(C) - fail(T, [ + Fail(T, [ { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello' }, { a: 'hello', b: 'hello', c: 'additional' }, @@ -56,7 +56,7 @@ describe('type/schema/Array', () => { const B = Type.Number() const C = Type.Tuple([A, B]) const T = Type.Array(C) - ok(T, [ + Ok(T, [ ['hello', 1], ['hello', 1], ['hello', 1], @@ -68,7 +68,7 @@ describe('type/schema/Array', () => { const B = Type.Number() const C = Type.Tuple([A, B]) const T = Type.Array(C) - fail(T, [ + Fail(T, [ [1, 'hello'], [1, 'hello'], [1, 'hello'], @@ -76,28 +76,28 @@ describe('type/schema/Array', () => { }) it('Should not validate array with failed minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) - fail(T, [0, 1]) + Fail(T, [0, 1]) }) it('Should not validate array with failed maxItems', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) - fail(T, [0, 1, 2, 3]) + Fail(T, [0, 1, 2, 3]) }) it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) - ok(T, [ + Ok(T, [ { x: 0, y: 1 }, { x: 1, y: 0 }, ]) }) it('Should not validate array with uniqueItems when items are not distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) - fail(T, [ + Fail(T, [ { x: 1, y: 0 }, { x: 1, y: 0 }, ]) }) it('Should not validate array with non uniqueItems', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) - fail(T, [0, 0]) + Fail(T, [0, 0]) }) }) diff --git a/test/runtime/schema/boolean.ts b/test/runtime/schema/boolean.ts index 4402a1b60..a784cf6b9 100644 --- a/test/runtime/schema/boolean.ts +++ b/test/runtime/schema/boolean.ts @@ -1,40 +1,40 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Boolean', () => { it('Should validate a boolean', () => { const T = Type.Boolean() - ok(T, true) - ok(T, false) + Ok(T, true) + Ok(T, false) }) it('Should not validate a number', () => { const T = Type.Boolean() - fail(T, 1) + Fail(T, 1) }) it('Should not validate a string', () => { const T = Type.Boolean() - fail(T, 'true') + Fail(T, 'true') }) it('Should not validate an array', () => { const T = Type.Boolean() - fail(T, [true]) + Fail(T, [true]) }) it('Should not validate an object', () => { const T = Type.Boolean() - fail(T, {}) + Fail(T, {}) }) it('Should not validate an null', () => { const T = Type.Boolean() - fail(T, null) + Fail(T, null) }) it('Should not validate an undefined', () => { const T = Type.Boolean() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/schema/date.ts b/test/runtime/schema/date.ts index 8e468665c..2e0672e95 100644 --- a/test/runtime/schema/date.ts +++ b/test/runtime/schema/date.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' // ---------------------------------------------------- // These tests are implemented by way of .addKeyword() @@ -9,54 +9,54 @@ import { ok, fail } from './validate' describe('type/schema/Date', () => { it('Should not validate number', () => { const T = Type.Date() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Date() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Date() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Date() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Date() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Date() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Date() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate Date', () => { const T = Type.Date() - ok(T, new Date()) + Ok(T, new Date()) }) it('Should validate Date minimumTimestamp', () => { const T = Type.Date({ minimumTimestamp: 10 }) - fail(T, new Date(9)) - ok(T, new Date(10)) + Fail(T, new Date(9)) + Ok(T, new Date(10)) }) it('Should validate Date maximumTimestamp', () => { const T = Type.Date({ maximumTimestamp: 10 }) - ok(T, new Date(10)) - fail(T, new Date(11)) + Ok(T, new Date(10)) + Fail(T, new Date(11)) }) it('Should validate Date exclusiveMinimumTimestamp', () => { const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) - fail(T, new Date(10)) - ok(T, new Date(11)) + Fail(T, new Date(10)) + Ok(T, new Date(11)) }) it('Should validate Date exclusiveMaximumTimestamp', () => { const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) - ok(T, new Date(9)) - fail(T, new Date(10)) + Ok(T, new Date(9)) + Fail(T, new Date(10)) }) }) diff --git a/test/runtime/schema/enum.ts b/test/runtime/schema/enum.ts index dbdbacf4c..4e52e0ead 100644 --- a/test/runtime/schema/enum.ts +++ b/test/runtime/schema/enum.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Enum', () => { it('Should validate when emum uses default numeric values', () => { @@ -8,8 +8,8 @@ describe('type/schema/Enum', () => { Bar, // = 1 } const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 1) + Ok(T, 0) + Ok(T, 1) }) it('Should not validate when given enum values are not numeric', () => { enum Kind { @@ -17,8 +17,8 @@ describe('type/schema/Enum', () => { Bar, // = 1 } const T = Type.Enum(Kind) - fail(T, 'Foo') - fail(T, 'Bar') + Fail(T, 'Foo') + Fail(T, 'Bar') }) it('Should validate when emum has defined string values', () => { @@ -27,8 +27,8 @@ describe('type/schema/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - ok(T, 'foo') - ok(T, 'bar') + Ok(T, 'foo') + Ok(T, 'bar') }) it('Should not validate when emum has defined string values and user passes numeric', () => { @@ -37,8 +37,8 @@ describe('type/schema/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - fail(T, 0) - fail(T, 1) + Fail(T, 0) + Fail(T, 1) }) it('Should validate when enum has one or more string values', () => { @@ -47,10 +47,10 @@ describe('type/schema/Enum', () => { Bar = 'bar', } const T = Type.Enum(Kind) - ok(T, 0) - ok(T, 'bar') - fail(T, 'baz') - fail(T, 'Foo') - fail(T, 1) + Ok(T, 0) + Ok(T, 'bar') + Fail(T, 'baz') + Fail(T, 'Foo') + Fail(T, 1) }) }) diff --git a/test/runtime/schema/integer.ts b/test/runtime/schema/integer.ts index d176053ab..281774ac3 100644 --- a/test/runtime/schema/integer.ts +++ b/test/runtime/schema/integer.ts @@ -1,71 +1,71 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Integer', () => { it('Should not validate number', () => { const T = Type.Integer() - fail(T, 3.14) + Fail(T, 3.14) }) it('Should validate integer', () => { const T = Type.Integer() - ok(T, 1) + Ok(T, 1) }) it('Should not validate string', () => { const T = Type.Integer() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Integer() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Integer() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Integer() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Integer() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Integer() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate minimum', () => { const T = Type.Integer({ minimum: 10 }) - fail(T, 9) - ok(T, 10) + Fail(T, 9) + Ok(T, 10) }) it('Should validate maximum', () => { const T = Type.Integer({ maximum: 10 }) - ok(T, 10) - fail(T, 11) + Ok(T, 10) + Fail(T, 11) }) it('Should validate Date exclusiveMinimum', () => { const T = Type.Integer({ exclusiveMinimum: 10 }) - fail(T, 10) - ok(T, 11) + Fail(T, 10) + Ok(T, 11) }) it('Should validate Date exclusiveMaximum', () => { const T = Type.Integer({ exclusiveMaximum: 10 }) - ok(T, 9) - fail(T, 10) + Ok(T, 9) + Fail(T, 10) }) it('Should not validate NaN', () => { - fail(Type.Integer(), NaN) + Fail(Type.Integer(), NaN) }) }) diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts index 24e3406ba..e1671dbc3 100644 --- a/test/runtime/schema/intersect.ts +++ b/test/runtime/schema/intersect.ts @@ -1,42 +1,42 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Intersect', () => { it('Should intersect two objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Intersect([A, B], { additionalProperties: false }) - ok(T, { a: 'hello', b: 42 }) + Ok(T, { a: 'hello', b: 42 }) }) it('Should intersect with partial', () => { const A = Type.Partial(Type.Object({ a: Type.Number() })) const B = Type.Partial(Type.Object({ b: Type.Number() })) const P = Type.Intersect([A, B], { additionalProperties: false }) - ok(P, { a: 1, b: 2 }) - ok(P, { a: 1 }) - ok(P, { b: 1 }) - ok(P, {}) - fail(P, { c: 1 }) + Ok(P, { a: 1, b: 2 }) + Ok(P, { a: 1 }) + Ok(P, { b: 1 }) + Ok(P, {}) + Fail(P, { c: 1 }) }) it('Should intersect with overlapping same type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Number() }) const P = Type.Intersect([A, B]) - ok(P, { a: 1 }) - fail(P, { a: 'hello' }) - fail(P, {}) + Ok(P, { a: 1 }) + Fail(P, { a: 'hello' }) + Fail(P, {}) }) it('Should intersect with overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const T = Type.Intersect([A, B]) - ok(T, { a: 1 }) - ok(T, { a: 'hello' }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Fail(T, {}) }) it('Should intersect with deeply nest overlapping varying type', () => { @@ -45,12 +45,12 @@ describe('type/schema/Intersect', () => { const C = Type.Object({ a: Type.Boolean() }) const D = Type.Object({ a: Type.Null() }) const T = Type.Intersect([A, B, C, D]) - ok(T, { a: 1 }) - ok(T, { a: 'hello' }) - ok(T, { a: false }) - ok(T, { a: null }) - fail(T, { a: [] }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Ok(T, { a: false }) + Ok(T, { a: null }) + Fail(T, { a: [] }) + Fail(T, {}) }) it('Should pick from intersected type', () => { @@ -59,8 +59,8 @@ describe('type/schema/Intersect', () => { const C = Type.Object({ z: Type.Number() }) const T = Type.Intersect([A, B, C]) const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) - ok(P, { x: 1, y: 1 }) - fail(P, { x: 1, y: 1, z: 1 }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) }) it('Should omit from intersected type', () => { @@ -69,17 +69,17 @@ describe('type/schema/Intersect', () => { const C = Type.Object({ z: Type.Number() }) const T = Type.Intersect([A, B, C]) const P = Type.Omit(T, ['z'], { additionalProperties: false }) - ok(P, { x: 1, y: 1 }) - fail(P, { x: 1, y: 1, z: 1 }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) }) it('Should intersect nested object properties', () => { const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) const T = Type.Intersect([A, B]) - ok(T, { x: { x: 1 } }) - ok(T, { x: { x: 'hello' } }) - fail(T, { x: { x: false } }) + Ok(T, { x: { x: 1 } }) + Ok(T, { x: { x: 'hello' } }) + Fail(T, { x: { x: false } }) }) // todo: move to composition / type guard spec diff --git a/test/runtime/schema/keyof.ts b/test/runtime/schema/keyof.ts index d929b3770..f637ee5f0 100644 --- a/test/runtime/schema/keyof.ts +++ b/test/runtime/schema/keyof.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/KeyOf', () => { @@ -11,10 +11,10 @@ describe('type/schema/KeyOf', () => { z: Type.Number(), }), ) - ok(T, 'x') - ok(T, 'y') - ok(T, 'z') - fail(T, 'w') + Ok(T, 'x') + Ok(T, 'y') + Ok(T, 'z') + Fail(T, 'w') }) it('Should validate when using pick', () => { @@ -28,9 +28,9 @@ describe('type/schema/KeyOf', () => { ['x', 'y'], ), ) - ok(T, 'x') - ok(T, 'y') - fail(T, 'z') + Ok(T, 'x') + Ok(T, 'y') + Fail(T, 'z') }) it('Should validate when using omit', () => { @@ -45,8 +45,8 @@ describe('type/schema/KeyOf', () => { ), ) - fail(T, 'x') - fail(T, 'y') - ok(T, 'z') + Fail(T, 'x') + Fail(T, 'y') + Ok(T, 'z') }) }) diff --git a/test/runtime/schema/literal.ts b/test/runtime/schema/literal.ts index 92b065986..fe5e0f578 100644 --- a/test/runtime/schema/literal.ts +++ b/test/runtime/schema/literal.ts @@ -1,43 +1,43 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Literal', () => { it('Should validate literal number', () => { const T = Type.Literal(42) - ok(T, 42) + Ok(T, 42) }) it('Should validate literal string', () => { const T = Type.Literal('hello') - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate literal boolean', () => { const T = Type.Literal(true) - ok(T, true) + Ok(T, true) }) it('Should not validate invalid literal number', () => { const T = Type.Literal(42) - fail(T, 43) + Fail(T, 43) }) it('Should not validate invalid literal string', () => { const T = Type.Literal('hello') - fail(T, 'world') + Fail(T, 'world') }) it('Should not validate invalid literal boolean', () => { const T = Type.Literal(false) - fail(T, true) + Fail(T, true) }) it('Should validate literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - ok(T, 42) - ok(T, 'hello') + Ok(T, 42) + Ok(T, 'hello') }) it('Should not validate invalid literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) - fail(T, 43) - fail(T, 'world') + Fail(T, 43) + Fail(T, 'world') }) }) diff --git a/test/runtime/schema/never.ts b/test/runtime/schema/never.ts index a32e4b66b..8a3f6e386 100644 --- a/test/runtime/schema/never.ts +++ b/test/runtime/schema/never.ts @@ -1,33 +1,33 @@ import { Type } from '@sinclair/typebox' -import { fail } from './validate' +import { Fail } from './validate' describe('type/schema/Never', () => { it('Should not validate number', () => { const T = Type.Never() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Never() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Never() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Never() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Never() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Never() - fail(T, null) + Fail(T, null) }) it('Should validate undefined', () => { const T = Type.Never() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/schema/null.ts b/test/runtime/schema/null.ts index 8d5dca921..bd9c709bb 100644 --- a/test/runtime/schema/null.ts +++ b/test/runtime/schema/null.ts @@ -1,39 +1,39 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Null', () => { it('Should not validate number', () => { const T = Type.Null() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Null() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Null() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Null() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Null() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Null() - ok(T, null) + Ok(T, null) }) it('Should not validate undefined', () => { const T = Type.Null() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts index 3b3d6fc36..99140d591 100644 --- a/test/runtime/schema/number.ts +++ b/test/runtime/schema/number.ts @@ -1,67 +1,67 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Number', () => { it('Should validate number', () => { const T = Type.Number() - ok(T, 3.14) + Ok(T, 3.14) }) it('Should validate integer', () => { const T = Type.Number() - ok(T, 1) + Ok(T, 1) }) it('Should not validate string', () => { const T = Type.Number() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Number() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Number() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Number() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Number() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Number() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate minimum', () => { const T = Type.Number({ minimum: 10 }) - fail(T, 9) - ok(T, 10) + Fail(T, 9) + Ok(T, 10) }) it('Should validate maximum', () => { const T = Type.Number({ maximum: 10 }) - ok(T, 10) - fail(T, 11) + Ok(T, 10) + Fail(T, 11) }) it('Should validate Date exclusiveMinimum', () => { const T = Type.Number({ exclusiveMinimum: 10 }) - fail(T, 10) - ok(T, 11) + Fail(T, 10) + Ok(T, 11) }) it('Should validate Date exclusiveMaximum', () => { const T = Type.Number({ exclusiveMaximum: 10 }) - ok(T, 9) - fail(T, 10) + Ok(T, 9) + Fail(T, 10) }) it('Should not validate NaN', () => { - fail(Type.Number(), NaN) + Fail(Type.Number(), NaN) }) }) diff --git a/test/runtime/schema/object.ts b/test/runtime/schema/object.ts index 658daea41..98b9a2761 100644 --- a/test/runtime/schema/object.ts +++ b/test/runtime/schema/object.ts @@ -1,30 +1,30 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Object', () => { it('Should not validate a number', () => { const T = Type.Object({}) - fail(T, 42) + Fail(T, 42) }) it('Should not validate a string', () => { const T = Type.Object({}) - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate a boolean', () => { const T = Type.Object({}) - fail(T, true) + Fail(T, true) }) it('Should not validate a null', () => { const T = Type.Object({}) - fail(T, null) + Fail(T, null) }) it('Should not validate an array', () => { const T = Type.Object({}) - fail(T, [1, 2]) + Fail(T, [1, 2]) }) it('Should validate with correct property values', () => { @@ -35,7 +35,7 @@ describe('type/schema/Object', () => { d: Type.Array(Type.Number()), e: Type.Object({ x: Type.Number(), y: Type.Number() }), }) - ok(T, { + Ok(T, { a: 10, b: 'hello', c: true, @@ -52,7 +52,7 @@ describe('type/schema/Object', () => { d: Type.Array(Type.Number()), e: Type.Object({ x: Type.Number(), y: Type.Number() }), }) - fail(T, { + Fail(T, { a: 'not a number', // error b: 'hello', c: true, @@ -66,7 +66,7 @@ describe('type/schema/Object', () => { a: Type.Number(), b: Type.String(), }) - ok(T, { + Ok(T, { a: 1, b: 'hello', c: true, @@ -81,9 +81,9 @@ describe('type/schema/Object', () => { }, { additionalProperties: false, minProperties: 1 }, ) - ok(T, { a: 1 }) - ok(T, { b: 'hello' }) - fail(T, {}) + Ok(T, { a: 1 }) + Ok(T, { b: 'hello' }) + Fail(T, {}) }) it('Should not allow 3 properties if maxProperties is set to 2', () => { @@ -95,9 +95,9 @@ describe('type/schema/Object', () => { }, { additionalProperties: false, maxProperties: 2 }, ) - ok(T, { a: 1 }) - ok(T, { a: 1, b: 'hello' }) - fail(T, { + Ok(T, { a: 1 }) + Ok(T, { a: 1, b: 'hello' }) + Fail(T, { a: 1, b: 'hello', c: true, @@ -112,7 +112,7 @@ describe('type/schema/Object', () => { }, { additionalProperties: false }, ) - fail(T, { + Fail(T, { a: 1, b: 'hello', c: true, @@ -121,8 +121,8 @@ describe('type/schema/Object', () => { it('Should not allow properties for an empty object when additionalProperties is false', () => { const T = Type.Object({}, { additionalProperties: false }) - ok(T, {}) - fail(T, { a: 10 }) + Ok(T, {}) + Fail(T, { a: 10 }) }) it('Should validate with non-syntax property keys', () => { @@ -132,7 +132,7 @@ describe('type/schema/Object', () => { '$-leading': Type.Literal(3), '!@#$%^&*(': Type.Literal(4), }) - ok(T, { + Ok(T, { 'with-hyphen': 1, '0-leading': 2, '$-leading': 3, @@ -150,12 +150,12 @@ describe('type/schema/Object', () => { additionalProperties: Type.String(), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: 'hello', }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, @@ -172,12 +172,12 @@ describe('type/schema/Object', () => { additionalProperties: Type.Array(Type.Number()), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: [0, 1, 2], }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, @@ -196,12 +196,12 @@ describe('type/schema/Object', () => { }), }, ) - ok(T, { + Ok(T, { x: 1, y: 2, z: { z: 1 }, }) - fail(T, { + Fail(T, { x: 1, y: 2, z: 3, diff --git a/test/runtime/schema/omit.ts b/test/runtime/schema/omit.ts index b605646f5..3ba6a3aa3 100644 --- a/test/runtime/schema/omit.ts +++ b/test/runtime/schema/omit.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/Omit', () => { @@ -13,7 +13,7 @@ describe('type/schema/Omit', () => { { additionalProperties: false }, ) const T = Type.Omit(A, ['z']) - ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1, y: 1 }) }) it('Should remove required properties on the target schema', () => { @@ -67,7 +67,7 @@ describe('type/schema/Omit', () => { y: Type.Number(), }) const T = Type.Omit(A, Type.KeyOf(B), { additionalProperties: false }) - ok(T, { z: 0 }) - fail(T, { x: 0, y: 0, z: 0 }) + Ok(T, { z: 0 }) + Fail(T, { x: 0, y: 0, z: 0 }) }) }) diff --git a/test/runtime/schema/optional.ts b/test/runtime/schema/optional.ts index ba95bd7fb..298550f73 100644 --- a/test/runtime/schema/optional.ts +++ b/test/runtime/schema/optional.ts @@ -1,6 +1,6 @@ import { strictEqual } from 'assert' import { Type } from '@sinclair/typebox' -import { ok } from './validate' +import { Ok } from './validate' describe('type/schema/Optional', () => { it('Should validate object with optional', () => { @@ -11,8 +11,8 @@ describe('type/schema/Optional', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) + Ok(T, { b: 'world' }) }) it('Should remove required value from schema', () => { const T = Type.Object( diff --git a/test/runtime/schema/partial.ts b/test/runtime/schema/partial.ts index 6fafdd867..9b9490288 100644 --- a/test/runtime/schema/partial.ts +++ b/test/runtime/schema/partial.ts @@ -1,5 +1,5 @@ import { Type, Modifier } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/Partial', () => { @@ -13,10 +13,10 @@ describe('type/schema/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - ok(T, { x: 1, y: 1, z: 1 }) - ok(T, { x: 1, y: 1 }) - ok(T, { x: 1 }) - ok(T, {}) + Ok(T, { x: 1, y: 1, z: 1 }) + Ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1 }) + Ok(T, {}) }) it('Should update modifier types correctly when converting to partial', () => { diff --git a/test/runtime/schema/pick.ts b/test/runtime/schema/pick.ts index f96332bfc..99f49f700 100644 --- a/test/runtime/schema/pick.ts +++ b/test/runtime/schema/pick.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/Pick', () => { @@ -13,7 +13,7 @@ describe('type/schema/Pick', () => { { additionalProperties: false }, ) const T = Type.Pick(Vector3, ['x', 'y']) - ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1, y: 1 }) }) it('Should remove required properties on the target schema', () => { @@ -67,7 +67,7 @@ describe('type/schema/Pick', () => { y: Type.Number(), }) const T = Type.Pick(A, Type.KeyOf(B), { additionalProperties: false }) - ok(T, { x: 0, y: 0 }) - fail(T, { x: 0, y: 0, z: 0 }) + Ok(T, { x: 0, y: 0 }) + Fail(T, { x: 0, y: 0, z: 0 }) }) }) diff --git a/test/runtime/schema/readonly-optional.ts b/test/runtime/schema/readonly-optional.ts index 8353010be..59f2f4baf 100644 --- a/test/runtime/schema/readonly-optional.ts +++ b/test/runtime/schema/readonly-optional.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/ReadonlyOptional', () => { @@ -11,8 +11,8 @@ describe('type/schema/ReadonlyOptional', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) + Ok(T, { b: 'world' }) }) it('Should remove required value from schema', () => { const T = Type.Object( diff --git a/test/runtime/schema/readonly.ts b/test/runtime/schema/readonly.ts index c0b55dc85..50f75eb99 100644 --- a/test/runtime/schema/readonly.ts +++ b/test/runtime/schema/readonly.ts @@ -1,6 +1,6 @@ import { deepStrictEqual, strictEqual } from 'assert' import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Readonly', () => { it('Should validate object with readonly', () => { @@ -11,7 +11,7 @@ describe('type/schema/Readonly', () => { }, { additionalProperties: false }, ) - ok(T, { a: 'hello', b: 'world' }) + Ok(T, { a: 'hello', b: 'world' }) }) it('Should retain required array on object', () => { diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index 4f93b89c6..e9f2de88b 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -1,27 +1,27 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Record', () => { it('Should validate when all property values are numbers', () => { const T = Type.Record(Type.String(), Type.Number()) - ok(T, { a: 1, b: 2, c: 3 }) + Ok(T, { a: 1, b: 2, c: 3 }) }) it('Should validate when all property keys are strings', () => { const T = Type.Record(Type.String(), Type.Number()) - ok(T, { a: 1, b: 2, c: 3, '0': 4 }) + Ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) - ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) + Ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) it('Should not validate when specifying string union literals when additionalProperties is false', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) - fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) + Fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) it('Should validate for keyof records', () => { @@ -31,7 +31,7 @@ describe('type/schema/Record', () => { c: Type.String(), }) const R = Type.Record(Type.KeyOf(T), Type.Number()) - ok(R, { a: 1, b: 2, c: 3 }) + Ok(R, { a: 1, b: 2, c: 3 }) }) it('Should not validate for unknown key via keyof', () => { @@ -41,13 +41,13 @@ describe('type/schema/Record', () => { c: Type.String(), }) const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) - fail(R, { a: 1, b: 2, c: 3, d: 4 }) + Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) it('Should should validate when specifying regular expressions', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) - ok(T, { + Ok(T, { op_a: 1, op_b: 2, op_c: 3, @@ -57,7 +57,7 @@ describe('type/schema/Record', () => { it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) - fail(T, { + Fail(T, { op_a: 1, op_b: 2, aop_c: 3, @@ -70,17 +70,17 @@ describe('type/schema/Record', () => { it('Should validate when all property keys are integers', () => { const T = Type.Record(Type.Integer(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are integers, but one property is a string with varying type', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { + Fail(T, { '00': 1, '01': 2, '02': 3, @@ -90,7 +90,7 @@ describe('type/schema/Record', () => { it('Should not validate if passing a signed integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) - fail(T, { + Fail(T, { '-0': 1, '-1': 2, '-2': 3, @@ -104,17 +104,17 @@ describe('type/schema/Record', () => { it('Should validate when all property keys are numbers', () => { const T = Type.Record(Type.Number(), Type.Number()) - ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) + Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { + Fail(T, { '00': 1, '01': 2, '02': 3, @@ -124,7 +124,7 @@ describe('type/schema/Record', () => { it('Should not validate if passing a signed numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { + Fail(T, { '-0': 1, '-1': 2, '-2': 3, @@ -134,6 +134,6 @@ describe('type/schema/Record', () => { it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) - fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) + Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) }) diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts index a0b132232..f5e9a352f 100644 --- a/test/runtime/schema/recursive.ts +++ b/test/runtime/schema/recursive.ts @@ -1,6 +1,6 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Recursive', () => { it('Should generate default ordinal $id if not specified', () => { @@ -32,7 +32,7 @@ describe('type/schema/Recursive', () => { nodes: Type.Array(Self), }), ) - ok(Node, { + Ok(Node, { id: 'A', nodes: [ { id: 'B', nodes: [] }, @@ -50,7 +50,7 @@ describe('type/schema/Recursive', () => { }), ), ]) - ok(Node, [ + Ok(Node, [ { id: 'A', nodes: [ @@ -70,7 +70,7 @@ describe('type/schema/Recursive', () => { }), ), ]) - fail(Node, [ + Fail(Node, [ { id: 'A', nodes: [ diff --git a/test/runtime/schema/ref.ts b/test/runtime/schema/ref.ts index ade5e054a..cfab94010 100644 --- a/test/runtime/schema/ref.ts +++ b/test/runtime/schema/ref.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { Assert } from '../assert/index' describe('type/schema/Ref', () => { @@ -13,7 +13,7 @@ describe('type/schema/Ref', () => { { $id: 'T' }, ) const R = Type.Ref(T) - ok( + Ok( R, { x: 1, @@ -34,7 +34,7 @@ describe('type/schema/Ref', () => { { $id: 'T' }, ) const R = Type.Ref(T) - fail( + Fail( R, { x: 1, @@ -61,9 +61,9 @@ describe('type/schema/Ref', () => { { $id: 'T' }, ) - ok(T, { x: 1, y: 2, z: 3 }, [R]) - ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) - fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) - fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) + Ok(T, { x: 1, y: 2, z: 3 }, [R]) + Ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) + Fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) + Fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) }) }) diff --git a/test/runtime/schema/regex.ts b/test/runtime/schema/regex.ts index 0c5c56faa..3c3b49a39 100644 --- a/test/runtime/schema/regex.ts +++ b/test/runtime/schema/regex.ts @@ -1,35 +1,35 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/RegEx', () => { it('Should validate numeric value', () => { const T = Type.RegEx(/[012345]/) - ok(T, '0') - ok(T, '1') - ok(T, '2') - ok(T, '3') - ok(T, '4') - ok(T, '5') + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') }) it('Should validate true or false string value', () => { const T = Type.RegEx(/true|false/) - ok(T, 'true') - ok(T, 'true') - ok(T, 'true') - ok(T, 'false') - ok(T, 'false') - ok(T, 'false') - fail(T, '6') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') }) it('Should not validate failed regex test', () => { const T = Type.RegEx(/true|false/) - fail(T, 'unknown') + Fail(T, 'unknown') }) it('Should pass numeric 5 digit test', () => { const T = Type.RegEx(/[\d]{5}/) - ok(T, '12345') + Ok(T, '12345') }) }) diff --git a/test/runtime/schema/required.ts b/test/runtime/schema/required.ts index f3090fcdd..fc8b6149e 100644 --- a/test/runtime/schema/required.ts +++ b/test/runtime/schema/required.ts @@ -1,5 +1,5 @@ import { Type, Modifier } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/schema/Required', () => { @@ -13,10 +13,10 @@ describe('type/schema/Required', () => { { additionalProperties: false }, ) const T = Type.Required(A) - ok(T, { x: 1, y: 1, z: 1 }) - fail(T, { x: 1, y: 1 }) - fail(T, { x: 1 }) - fail(T, {}) + Ok(T, { x: 1, y: 1, z: 1 }) + Fail(T, { x: 1, y: 1 }) + Fail(T, { x: 1 }) + Fail(T, {}) }) it('Should update modifier types correctly when converting to required', () => { diff --git a/test/runtime/schema/string.ts b/test/runtime/schema/string.ts index 9061de762..3a536bddd 100644 --- a/test/runtime/schema/string.ts +++ b/test/runtime/schema/string.ts @@ -1,65 +1,65 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/String', () => { it('Should not validate number', () => { const T = Type.String() - fail(T, 1) + Fail(T, 1) }) it('Should validate string', () => { const T = Type.String() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.String() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.String() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.String() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.String() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.String() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate string format as email', () => { const T = Type.String({ format: 'email' }) - ok(T, 'name@domain.com') + Ok(T, 'name@domain.com') }) it('Should validate string format as uuid', () => { const T = Type.String({ format: 'uuid' }) - ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') + Ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') }) it('Should validate string format as iso8601 date', () => { const T = Type.String({ format: 'date-time' }) - ok(T, '2021-06-11T20:30:00-04:00') + Ok(T, '2021-06-11T20:30:00-04:00') }) it('Should validate minLength', () => { const T = Type.String({ minLength: 4 }) - ok(T, '....') - fail(T, '...') + Ok(T, '....') + Fail(T, '...') }) it('Should validate maxLength', () => { const T = Type.String({ maxLength: 4 }) - ok(T, '....') - fail(T, '.....') + Ok(T, '....') + Fail(T, '.....') }) it('Should pass numeric 5 digit test', () => { const T = Type.String({ pattern: '[\\d]{5}' }) - ok(T, '12345') + Ok(T, '12345') }) }) diff --git a/test/runtime/schema/tuple.ts b/test/runtime/schema/tuple.ts index 60ba0be00..773586e04 100644 --- a/test/runtime/schema/tuple.ts +++ b/test/runtime/schema/tuple.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' import { Assert } from '../assert' describe('type/schema/Tuple', () => { @@ -7,56 +7,56 @@ describe('type/schema/Tuple', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) - ok(T, ['hello', 42]) + Ok(T, ['hello', 42]) }) it('Should not validate tuple of [string, number] when reversed', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) - fail(T, [42, 'hello']) + Fail(T, [42, 'hello']) }) it('Should validate with empty tuple', () => { const T = Type.Tuple([]) - ok(T, []) + Ok(T, []) }) it('Should not validate with empty tuple with more items', () => { const T = Type.Tuple([]) - fail(T, [1]) + Fail(T, [1]) }) it('Should not validate with empty tuple with less items', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) - fail(T, [1]) + Fail(T, [1]) }) it('Should validate tuple of objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - ok(T, [{ a: 'hello' }, { b: 42 }]) + Ok(T, [{ a: 'hello' }, { b: 42 }]) }) it('Should not validate tuple of objects when reversed', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ b: 42 }, { a: 'hello' }]) + Fail(T, [{ b: 42 }, { a: 'hello' }]) }) it('Should not validate tuple when array is less than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ a: 'hello' }]) + Fail(T, [{ a: 'hello' }]) }) it('Should not validate tuple when array is greater than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) - fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) + Fail(T, [{ a: 'hello' }, { b: 42 }, { b: 42 }]) }) }) diff --git a/test/runtime/schema/uint8array.ts b/test/runtime/schema/uint8array.ts index e1f7e0f68..04cb8f732 100644 --- a/test/runtime/schema/uint8array.ts +++ b/test/runtime/schema/uint8array.ts @@ -1,56 +1,56 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Uint8Array', () => { it('Should not validate number', () => { const T = Type.Uint8Array() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Uint8Array() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Uint8Array() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Uint8Array() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Uint8Array() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should not validate null', () => { const T = Type.Uint8Array() - fail(T, null) + Fail(T, null) }) it('Should not validate undefined', () => { const T = Type.Uint8Array() - fail(T, undefined) + Fail(T, undefined) }) it('Should validate Uint8Array', () => { const T = Type.Uint8Array() - ok(T, new Uint8Array(100)) + Ok(T, new Uint8Array(100)) }) it('Should validate minByteLength', () => { const T = Type.Uint8Array({ minByteLength: 4 }) - ok(T, new Uint8Array(4)) - fail(T, new Uint8Array(3)) + Ok(T, new Uint8Array(4)) + Fail(T, new Uint8Array(3)) }) it('Should validate maxByteLength', () => { const T = Type.Uint8Array({ maxByteLength: 4 }) - ok(T, new Uint8Array(4)) - fail(T, new Uint8Array(5)) + Ok(T, new Uint8Array(4)) + Fail(T, new Uint8Array(5)) }) }) diff --git a/test/runtime/schema/union.ts b/test/runtime/schema/union.ts index 45d371abe..10d3e5081 100644 --- a/test/runtime/schema/union.ts +++ b/test/runtime/schema/union.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Union', () => { it('Should validate union of string, number and boolean', () => { @@ -7,56 +7,56 @@ describe('type/schema/Union', () => { const B = Type.Number() const C = Type.Boolean() const T = Type.Union([A, B, C]) - ok(T, 'hello') - ok(T, true) - ok(T, 42) + Ok(T, 'hello') + Ok(T, true) + Ok(T, 42) }) it('Should validate union of objects', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) - ok(T, { b: 'world' }) + Ok(T, { a: 'hello' }) + Ok(T, { b: 'world' }) }) it('Should fail to validate for descriminated union types', () => { const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) const T = Type.Union([A, B]) - fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } - fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } + Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } + Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } }) it('Should validate union of objects where properties overlap', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello' }) // A - ok(T, { a: 'hello', b: 'world' }) // B + Ok(T, { a: 'hello' }) // A + Ok(T, { a: 'hello', b: 'world' }) // B }) it('Should validate union of overlapping property of varying type', () => { const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) const T = Type.Union([A, B]) - ok(T, { a: 'hello', b: 42 }) // A - ok(T, { a: 'hello', b: 'world' }) // B + Ok(T, { a: 'hello', b: 42 }) // A + Ok(T, { a: 'hello', b: 'world' }) // B }) it('Should validate union of literal strings', () => { const A = Type.Literal('hello') const B = Type.Literal('world') const T = Type.Union([A, B]) - ok(T, 'hello') // A - ok(T, 'world') // B + Ok(T, 'hello') // A + Ok(T, 'world') // B }) it('Should not validate union of literal strings for unknown string', () => { const A = Type.Literal('hello') const B = Type.Literal('world') const T = Type.Union([A, B]) - fail(T, 'foo') // A - fail(T, 'bar') // B + Fail(T, 'foo') // A + Fail(T, 'bar') // B }) }) diff --git a/test/runtime/schema/unknown.ts b/test/runtime/schema/unknown.ts index c9b94f339..2bc681767 100644 --- a/test/runtime/schema/unknown.ts +++ b/test/runtime/schema/unknown.ts @@ -1,33 +1,33 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Unknown', () => { it('Should validate number', () => { const T = Type.Any() - ok(T, 1) + Ok(T, 1) }) it('Should validate string', () => { const T = Type.Any() - ok(T, 'hello') + Ok(T, 'hello') }) it('Should validate boolean', () => { const T = Type.Any() - ok(T, true) + Ok(T, true) }) it('Should validate array', () => { const T = Type.Any() - ok(T, [1, 2, 3]) + Ok(T, [1, 2, 3]) }) it('Should validate object', () => { const T = Type.Any() - ok(T, { a: 1, b: 2 }) + Ok(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Any() - ok(T, null) + Ok(T, null) }) it('Should validate undefined', () => { const T = Type.Any() - ok(T, undefined) + Ok(T, undefined) }) }) diff --git a/test/runtime/schema/unsafe.ts b/test/runtime/schema/unsafe.ts index d21fe3411..509d2d4fb 100644 --- a/test/runtime/schema/unsafe.ts +++ b/test/runtime/schema/unsafe.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Unsafe', () => { it('Should validate an unsafe type', () => { @@ -12,7 +12,7 @@ describe('type/schema/Unsafe', () => { }, additionalProperties: false, }) - ok(T, { x: 1, y: 2, z: 3 }) - fail(T, { x: 1, y: 2, z: 3, w: 4 }) + Ok(T, { x: 1, y: 2, z: 3 }) + Fail(T, { x: 1, y: 2, z: 3, w: 4 }) }) }) diff --git a/test/runtime/schema/validate.ts b/test/runtime/schema/validate.ts index bd1f822f7..7390db7bb 100644 --- a/test/runtime/schema/validate.ts +++ b/test/runtime/schema/validate.ts @@ -39,7 +39,7 @@ export function createAjv(references: AnySchema[]) { .addSchema(references) } -export function ok(type: T, data: unknown, additional: AnySchema[] = []) { +export function Ok(type: T, data: unknown, additional: AnySchema[] = []) { const ajv = createAjv(additional) function execute() { // required as ajv will throw if referenced schema is not found @@ -66,7 +66,7 @@ export function ok(type: T, data: unknown, additional: AnySch } } -export function fail(type: T, data: unknown, additional: AnySchema[] = []) { +export function Fail(type: T, data: unknown, additional: AnySchema[] = []) { const ajv = createAjv(additional) function execute() { // required as ajv will throw if referenced schema is not found diff --git a/test/runtime/schema/void.ts b/test/runtime/schema/void.ts index 8d5c13e21..99d984c3e 100644 --- a/test/runtime/schema/void.ts +++ b/test/runtime/schema/void.ts @@ -1,39 +1,39 @@ import { Type } from '@sinclair/typebox' -import { ok, fail } from './validate' +import { Ok, Fail } from './validate' describe('type/schema/Void', () => { it('Should not validate number', () => { const T = Type.Void() - fail(T, 1) + Fail(T, 1) }) it('Should not validate string', () => { const T = Type.Void() - fail(T, 'hello') + Fail(T, 'hello') }) it('Should not validate boolean', () => { const T = Type.Void() - fail(T, true) + Fail(T, true) }) it('Should not validate array', () => { const T = Type.Void() - fail(T, [1, 2, 3]) + Fail(T, [1, 2, 3]) }) it('Should not validate object', () => { const T = Type.Void() - fail(T, { a: 1, b: 2 }) + Fail(T, { a: 1, b: 2 }) }) it('Should validate null', () => { const T = Type.Null() - ok(T, null) + Ok(T, null) }) it('Should not validate undefined', () => { const T = Type.Void() - fail(T, undefined) + Fail(T, undefined) }) }) diff --git a/test/runtime/settings/index.ts b/test/runtime/settings/index.ts new file mode 100644 index 000000000..e63c403f9 --- /dev/null +++ b/test/runtime/settings/index.ts @@ -0,0 +1 @@ +import './structural' diff --git a/test/runtime/settings/structural.ts b/test/runtime/settings/structural.ts new file mode 100644 index 000000000..e857ff265 --- /dev/null +++ b/test/runtime/settings/structural.ts @@ -0,0 +1,62 @@ +import { Ok, Fail } from '../compiler/validate' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('Settings/Structural', () => { + before(() => { + TypeSystem.Kind = 'structural' + }) + after(() => { + TypeSystem.Kind = 'json-schema' + }) + + // --------------------------------------------------------------- + // Object + // --------------------------------------------------------------- + it('Should validate arrays with empty objects', () => { + const T = Type.Object({}) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with length property', () => { + const T = Type.Object({ length: Type.Number() }) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with additionalProperties false when array has no elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Ok(T, []) + }) + it('Should not validate arrays with objects with additionalProperties false when array has elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Fail(T, [0, 1, 2]) + }) + it('Should not validate arrays with objects when length property is string', () => { + const T = Type.Object({ length: Type.String() }) + Fail(T, [0, 1, 2]) + }) + // --------------------------------------------------------------- + // Record + // --------------------------------------------------------------- + it('Should validate arrays as Records with String Keys', () => { + const T = Type.Record(Type.String(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should not validate arrays as Records with Number Keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + Fail(T, [0, 1, 2]) + }) + it('Should not validate arrays as Records with Object Values', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Ok(T, [ + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + ]) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index c1619dc92..d660b0d6d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,9 @@ "@sinclair/typebox/hash": [ "src/hash/index.ts" ], + "@sinclair/typebox/system": [ + "src/system/index.ts" + ], "@sinclair/typebox/value": [ "src/value/index.ts" ], From 999d5bc35bbf50bac64a6f953d4fb3e59975005d Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 20 Dec 2022 11:46:05 +0900 Subject: [PATCH 053/369] Format --- example/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/example/index.ts b/example/index.ts index c367f5f78..6307054b6 100644 --- a/example/index.ts +++ b/example/index.ts @@ -9,11 +9,11 @@ import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Kind, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }) - - type T = Static - - console.log(T) \ No newline at end of file + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), +}) + +type T = Static + +console.log(T) From ce18d623165bf608e50a5cbd72bdb01fbfa8dcf3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 22 Dec 2022 03:33:31 +0900 Subject: [PATCH 054/369] Structural Configuration (#297) --- package.json | 2 +- src/compiler/compiler.ts | 18 ++++++---- src/errors/errors.ts | 31 ++++++++-------- src/system/system.ts | 35 ++++--------------- src/value/check.ts | 29 ++++++++------- test/runtime/index.ts | 2 +- test/runtime/settings/index.ts | 1 - .../AllowArrayObjects.ts} | 7 ++-- test/runtime/system/AllowNaN.ts | 30 ++++++++++++++++ test/runtime/system/index.ts | 2 ++ 10 files changed, 88 insertions(+), 69 deletions(-) delete mode 100644 test/runtime/settings/index.ts rename test/runtime/{settings/structural.ts => system/AllowArrayObjects.ts} (93%) create mode 100644 test/runtime/system/AllowNaN.ts create mode 100644 test/runtime/system/index.ts diff --git a/package.json b/package.json index 0b6dce662..b94472262 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.15", + "version": "0.25.16", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 73c4814ca..1e7e964bc 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -177,7 +177,11 @@ export namespace TypeCompiler { } function* Number(schema: Types.TNumber, value: string): IterableIterator { - yield `(typeof ${value} === 'number' && !isNaN(${value}))` + if (TypeSystem.AllowNaN) { + yield `(typeof ${value} === 'number')` + } else { + yield `(typeof ${value} === 'number' && !isNaN(${value}))` + } if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` @@ -186,10 +190,10 @@ export namespace TypeCompiler { } function* Object(schema: Types.TObject, value: string): IterableIterator { - if (TypeSystem.Kind === 'json-schema') { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` - } else if (TypeSystem.Kind === 'structural') { + if (TypeSystem.AllowArrayObjects) { yield `(typeof ${value} === 'object' && ${value} !== null)` + } else { + yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` } if (IsNumber(schema.minProperties)) yield `(Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties})` if (IsNumber(schema.maxProperties)) yield `(Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties})` @@ -228,10 +232,10 @@ export namespace TypeCompiler { } function* Record(schema: Types.TRecord, value: string): IterableIterator { - if (TypeSystem.Kind === 'json-schema') { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date))` - } else if (TypeSystem.Kind === 'structural') { + if (TypeSystem.AllowArrayObjects) { yield `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date))` + } else { + yield `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !Array.isArray(${value}))` } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${keyPattern}/)`) diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 36f774ab2..ad346e3a5 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -173,10 +173,7 @@ export namespace ValueErrors { function* Integer(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } - } - if (!globalThis.Number.isInteger(value)) { - yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } + return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } @@ -213,8 +210,14 @@ export namespace ValueErrors { } function* Number(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'number' && !isNaN(value))) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } + if (TypeSystem.AllowNaN) { + if (!(typeof value === 'number')) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } + } + } else { + if (!(typeof value === 'number' && !isNaN(value))) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } + } } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } @@ -234,12 +237,12 @@ export namespace ValueErrors { } function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (TypeSystem.Kind === 'json-schema') { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + if (TypeSystem.AllowArrayObjects) { + if (!(typeof value === 'object' && value !== null)) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } - } else if (TypeSystem.Kind === 'structural') { - if (!(typeof value === 'object' && value !== null)) { + } else { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } } @@ -289,12 +292,12 @@ export namespace ValueErrors { } function* Record(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (TypeSystem.Kind === 'json-schema') { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { + if (TypeSystem.AllowArrayObjects) { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } - } else if (TypeSystem.Kind === 'structural') { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { + } else { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date) && !globalThis.Array.isArray(value))) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } } diff --git a/src/system/system.ts b/src/system/system.ts index b2985a72b..0ddd2a1f0 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,34 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export class InvalidTypeSystemError extends Error { - constructor(typeSystem: string) { - super(`TypeSystemSettings: Unknown TypeSystem '${typeSystem}'`) - } -} - -export type TypeSystemKind = 'json-schema' | 'structural' - -class TypeSystemSettings { - private kind: TypeSystemKind - constructor() { - this.kind = 'json-schema' - } +export namespace TypeSystem { /** - * `Experimental` Sets the type system kind used by TypeBox. By default TypeBox uses `json-schema` assertion - * rules to verify JavaScript values. If setting the type system to `structural`, TypeBox will use TypeScript - * structural checking rules enabling Arrays to be validated as Objects. + * Sets whether arrays should be treated as kinds of objects. The default is `false` */ - public get Kind(): TypeSystemKind { - return this.kind - } - public set Kind(value: TypeSystemKind) { - if (!(value === 'json-schema' || value === 'structural')) { - throw new InvalidTypeSystemError(value) - } - this.kind = value - } + export let AllowArrayObjects: boolean = false + /** + * Sets whether numeric checks should consider NaN a valid number type. The default is `false` + */ + export let AllowNaN: boolean = false } - -/** TypeBox TypeSystem Settings */ -export const TypeSystem = new TypeSystemSettings() diff --git a/src/value/check.ts b/src/value/check.ts index e1415462d..4bd8389ce 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -99,9 +99,6 @@ export namespace ValueCheck { if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { return false } - if (!globalThis.Number.isInteger(value)) { - return false - } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } @@ -133,8 +130,14 @@ export namespace ValueCheck { } function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'number' && !isNaN(value))) { - return false + if (TypeSystem.AllowNaN) { + if (!(typeof value === 'number')) { + return false + } + } else { + if (!(typeof value === 'number' && !isNaN(value))) { + return false + } } if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false @@ -155,12 +158,12 @@ export namespace ValueCheck { } function Object(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { - if (TypeSystem.Kind === 'json-schema') { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { + if (TypeSystem.AllowArrayObjects) { + if (!(typeof value === 'object' && value !== null)) { return false } - } else if (TypeSystem.Kind === 'structural') { - if (!(typeof value === 'object' && value !== null)) { + } else { + if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { return false } } @@ -214,12 +217,12 @@ export namespace ValueCheck { } function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (TypeSystem.Kind === 'json-schema') { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) && !(value instanceof globalThis.Date))) { + if (TypeSystem.AllowArrayObjects) { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { return false } - } else if (TypeSystem.Kind === 'structural') { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { + } else { + if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date) && !globalThis.Array.isArray(value))) { return false } } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 095140dab..6bd775e76 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -5,5 +5,5 @@ import './format/index' import './guard/index' import './hash/index' import './schema/index' -import './settings/index' +import './system/index' import './value/index' diff --git a/test/runtime/settings/index.ts b/test/runtime/settings/index.ts deleted file mode 100644 index e63c403f9..000000000 --- a/test/runtime/settings/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './structural' diff --git a/test/runtime/settings/structural.ts b/test/runtime/system/AllowArrayObjects.ts similarity index 93% rename from test/runtime/settings/structural.ts rename to test/runtime/system/AllowArrayObjects.ts index e857ff265..9ee0c46e3 100644 --- a/test/runtime/settings/structural.ts +++ b/test/runtime/system/AllowArrayObjects.ts @@ -2,14 +2,13 @@ import { Ok, Fail } from '../compiler/validate' import { TypeSystem } from '@sinclair/typebox/system' import { Type } from '@sinclair/typebox' -describe('Settings/Structural', () => { +describe('TypeSystem/AllowArrayObjects', () => { before(() => { - TypeSystem.Kind = 'structural' + TypeSystem.AllowArrayObjects = true }) after(() => { - TypeSystem.Kind = 'json-schema' + TypeSystem.AllowArrayObjects = false }) - // --------------------------------------------------------------- // Object // --------------------------------------------------------------- diff --git a/test/runtime/system/AllowNaN.ts b/test/runtime/system/AllowNaN.ts new file mode 100644 index 000000000..bcf0d9ee5 --- /dev/null +++ b/test/runtime/system/AllowNaN.ts @@ -0,0 +1,30 @@ +import { Ok, Fail } from '../compiler/validate' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('TypeSystem/AllowNaN', () => { + before(() => { + TypeSystem.AllowNaN = true + }) + after(() => { + TypeSystem.AllowNaN = false + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should validate number with NaN', () => { + const T = Type.Number() + Ok(T, NaN) + }) + + // --------------------------------------------------------------- + // Integer + // + // Note: The Number.isInteger() test will fail for NaN. Because + // of this we cannot reasonably override NaN handling for integers. + // --------------------------------------------------------------- + it('Should not validate integer with NaN', () => { + const T = Type.Integer() + Fail(T, NaN) + }) +}) diff --git a/test/runtime/system/index.ts b/test/runtime/system/index.ts new file mode 100644 index 000000000..fde988aa0 --- /dev/null +++ b/test/runtime/system/index.ts @@ -0,0 +1,2 @@ +import './AllowArrayObjects' +import './AllowNaN' From eed7cab39447ad5752aeecc03944db35511ab34f Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 23 Dec 2022 05:41:41 +0900 Subject: [PATCH 055/369] Additional Codegen --- codegen/codegen.ts | 16 ++- codegen/formatter.ts | 46 ++++++++ codegen/jsonschema.ts | 25 +--- codegen/typebox.ts | 23 +--- codegen/typescript.ts | 29 +---- codegen/zod.ts | 257 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 323 insertions(+), 73 deletions(-) create mode 100644 codegen/formatter.ts create mode 100644 codegen/zod.ts diff --git a/codegen/codegen.ts b/codegen/codegen.ts index 63728ce18..8e0237439 100644 --- a/codegen/codegen.ts +++ b/codegen/codegen.ts @@ -28,15 +28,19 @@ THE SOFTWARE. import { TSchema } from '@sinclair/typebox' import { TypeBoxCodegen } from './typebox' -import { TypeScriptCodeGen } from './typescript' -import { JsonSchemaCodeGen } from './jsonschema' +import { TypeScriptCodegen } from './typescript' +import { JsonSchemaCodegen } from './jsonschema' +import { ZodCodegen, ZodCodegenOptions } from './zod' -export namespace CodeGen { +export namespace Codegen { /** Generates TypeScript type definitions from TypeBox types */ export function TypeScript(schema: TSchema, references: TSchema[] = []): string { - return TypeScriptCodeGen.Generate(schema, references) + return TypeScriptCodegen.Generate(schema, references) + } + /** Generates Zod type definitions from TypeBox types */ + export function Zod(schema: TSchema, references: TSchema[] = [], options: ZodCodegenOptions = { imports: true, exports: false }): string { + return ZodCodegen.Generate(schema, references, options) } - /** Generates TypeBox type definitions from TypeScript code */ export function TypeBox(code: string): string { return TypeBoxCodegen.Generate(code) @@ -44,6 +48,6 @@ export namespace CodeGen { /** Generates JsonSchema definitions from TypeScript code */ export function JsonSchema(code: string): string { - return JsonSchemaCodeGen.Generate(code) + return JsonSchemaCodegen.Generate(code) } } diff --git a/codegen/formatter.ts b/codegen/formatter.ts new file mode 100644 index 000000000..6e3b4a7b8 --- /dev/null +++ b/codegen/formatter.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export namespace Formatter { + // Code formatter + export function Format(input: string): string { + function count(line: string, opens: string[]) { + const codes = opens.map((open) => open.charCodeAt(0)) + // prettier-ignore + return line.split('').map((char) => char.charCodeAt(0)).reduce((acc, current) => codes.includes(current) ? acc + 1 : acc, 0) + } + let indent = 0 + const output: string[] = [] + for (const line of input.split('\n').map((n) => n.trim())) { + indent -= count(line, ['}']) + output.push(`${''.padStart(indent * 2, ' ')}${line}`) + indent += count(line, ['{']) + } + return output.join('\n') + } +} diff --git a/codegen/jsonschema.ts b/codegen/jsonschema.ts index 23f4db8a1..31c85b854 100644 --- a/codegen/jsonschema.ts +++ b/codegen/jsonschema.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Formatter } from './formatter' import * as ts from 'typescript' export class NonExpressable extends Error { @@ -35,7 +36,7 @@ export class NonExpressable extends Error { } /** Generates JsonSchema from TypeScript interface and type definitions */ -export namespace JsonSchemaCodeGen { +export namespace JsonSchemaCodegen { function isReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined } @@ -399,31 +400,11 @@ export namespace JsonSchemaCodeGen { } } - function Format(input: string): string { - function count(line: string, opens: string[]) { - const codes = opens.map((open) => open.charCodeAt(0)) - return line - .split('') - .map((char) => char.charCodeAt(0)) - .reduce((acc, current) => { - return codes.includes(current) ? acc + 1 : acc - }, 0) - } - let indent = 0 - const output: string[] = [] - for (const line of input.split('\n').map((n) => n.trim())) { - indent -= count(line, ['}', ']']) - output.push(`${''.padStart(indent * 2, ' ')}${line}`) - indent += count(line, ['{', '[']) - } - return output.join('\n') - } - /** Generates TypeBox types from TypeScript interface and type definitions */ export function Generate(typescriptCode: string) { const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) const declarations = CollectNewLine(source) - const types = Format(declarations) + const types = Formatter.Format(declarations) return [types].join('\n') } } diff --git a/codegen/typebox.ts b/codegen/typebox.ts index d18637bd2..4d18be49c 100644 --- a/codegen/typebox.ts +++ b/codegen/typebox.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Formatter } from './formatter' import * as ts from 'typescript' /** Generates TypeBox types from TypeScript interface and type definitions */ @@ -319,26 +320,6 @@ export namespace TypeBoxCodegen { } } - function Format(input: string): string { - function count(line: string, opens: string[]) { - const codes = opens.map((open) => open.charCodeAt(0)) - return line - .split('') - .map((char) => char.charCodeAt(0)) - .reduce((acc, current) => { - return codes.includes(current) ? acc + 1 : acc - }, 0) - } - let indent = 0 - const output: string[] = [] - for (const line of input.split('\n').map((n) => n.trim())) { - indent -= count(line, ['}', ']']) - output.push(`${''.padStart(indent * 2, ' ')}${line}`) - indent += count(line, ['{', '[']) - } - return output.join('\n') - } - let useImports = false let useConditional = false let useGenerics = false @@ -358,7 +339,7 @@ export namespace TypeBoxCodegen { if (!useGenerics) importStatments.push(`import { Type, Static } from '@sinclair/typebox'`) } const imports = importStatments.join('\n') - const types = Format(typeDeclarations) + const types = Formatter.Format(typeDeclarations) return [imports, '', types].join('\n') } } diff --git a/codegen/typescript.ts b/codegen/typescript.ts index 6087ece23..a0885fdd9 100644 --- a/codegen/typescript.ts +++ b/codegen/typescript.ts @@ -26,10 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Formatter } from './formatter' import { TypeGuard } from '@sinclair/typebox/guard' import * as Types from '@sinclair/typebox' -export namespace TypeScriptCodeGen { +export namespace TypeScriptCodegen { function Any(schema: Types.TAny) { return 'any' } @@ -193,33 +194,13 @@ export namespace TypeScriptCodeGen { } } - function Format(input: string): string { - function count(line: string, opens: string[]) { - const codes = opens.map((open) => open.charCodeAt(0)) - return line - .split('') - .map((char) => char.charCodeAt(0)) - .reduce((acc, current) => { - return codes.includes(current) ? acc + 1 : acc - }, 0) - } - let indent = 0 - const output: string[] = [] - for (const line of input.split('\n').map((n) => n.trim())) { - indent -= count(line, ['}']) - output.push(`${''.padStart(indent * 2, ' ')}${line}`) - indent += count(line, ['{']) - } - return output.join('\n') - } - /** Generates TypeScript code from TypeBox types */ export function Generate(schema: Types.TSchema, references: Types.TSchema[] = []) { const result: string[] = [] for (const reference of references) { - result.push(`type ${reference.$id} = ${Format([...Visit(reference)].join(''))}`) + result.push(`type ${reference.$id} = ${[...Visit(reference)].join('')}`) } - result.push(`type ${schema.$id || 'T'} = ${Format([...Visit(schema)].join(''))}`) - return result.join('\n\n') + result.push(`type ${schema.$id || 'T'} = ${[...Visit(schema)].join('')}`) + return Formatter.Format(result.join('\n\n')) } } diff --git a/codegen/zod.ts b/codegen/zod.ts new file mode 100644 index 000000000..4f900d64d --- /dev/null +++ b/codegen/zod.ts @@ -0,0 +1,257 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/codegen + +The MIT License (MIT) + +Copyright (c) 2022 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Formatter } from './formatter' +import { TypeGuard } from '@sinclair/typebox/guard' +import * as Types from '@sinclair/typebox' + +export class ZodNonReferentialType extends Error { + constructor(message: string) { + super(`ZodNonReferentialType: ${message}`) + } +} +export class ZodUnsupportedType extends Error { + constructor(message: string) { + super(`ZodUnsupportedType: ${message}`) + } +} +export interface ZodCodegenOptions { + imports: boolean + exports: boolean +} +export namespace ZodCodegen { + function Any(schema: Types.TAny) { + return `z.any()` + } + function Array(schema: Types.TArray) { + const items = Visit(schema.items) + return `z.array(${items})` + } + function Boolean(schema: Types.TBoolean) { + return `z.boolean()` + } + function Constructor(schema: Types.TConstructor): string { + throw new ZodUnsupportedType(`TConstructor`) + } + function Function(schema: Types.TFunction) { + const params = schema.parameters.map((param) => Visit(param)).join(`, `) + const returns = Visit(schema.returns) + return `z.function().args(${params}).returns(${returns})` + } + function Integer(schema: Types.TInteger) { + const buffer: string[] = [] + buffer.push(`z.number().int()`) + if (schema.minimum !== undefined) buffer.push(`.min(${schema.minimum})`) + if (schema.maximum !== undefined) buffer.push(`.max(${schema.maximum})`) + if (schema.exclusiveMaximum !== undefined) buffer.push(`.max(${schema.exclusiveMaximum - 1})`) + if (schema.exclusiveMinimum !== undefined) buffer.push(`.max(${schema.exclusiveMinimum + 1})`) + if (schema.multipleOf !== undefined) buffer.push(`.multipleOf(${schema.multipleOf})`) + return buffer.join(``) + } + function Literal(schema: Types.TLiteral) { + if (typeof schema.const === `string`) { + return `z.literal('${schema.const}')` + } else { + return `z.literal(${schema.const})` + } + } + function Never(schema: Types.TNever) { + return `z.never()` + } + function Null(schema: Types.TNull) { + return `z.null()` + } + function String(schema: Types.TString) { + const buffer: string[] = [] + buffer.push(`z.string()`) + if (schema.maxLength !== undefined) buffer.push(`.max(${schema.maxLength})`) + if (schema.minLength !== undefined) buffer.push(`.min(${schema.minLength})`) + return buffer.join(``) + } + function Number(schema: Types.TNumber) { + const buffer: string[] = [] + buffer.push(`z.number()`) + if (schema.minimum !== undefined) buffer.push(`.min(${schema.minimum})`) + if (schema.maximum !== undefined) buffer.push(`.max(${schema.maximum})`) + if (schema.exclusiveMaximum !== undefined) buffer.push(`.max(${schema.exclusiveMaximum - 1})`) + if (schema.exclusiveMinimum !== undefined) buffer.push(`.max(${schema.exclusiveMinimum + 1})`) + if (schema.multipleOf !== undefined) buffer.push(`.multipleOf(${schema.multipleOf})`) + return buffer.join(``) + } + function Object(schema: Types.TObject) { + const properties: string = globalThis.Object.entries(schema.properties) + .map(([key, value]) => { + const quoted = key.includes('-') || '1234567890'.includes(key.charAt(0)) + const property = quoted ? `'${key}'` : key + return [`Optional`, `ReadonlyOptional`].includes(value[Types.Modifier] as string) ? `${property}: ${Visit(value)}.optional()` : `${property}: ${Visit(value)}` + }) + .join(`,\n`) + const buffer: string[] = [] + buffer.push(`z.object({\n${properties}\n})`) + if (schema.additionalProperties === false) buffer.push(`.strict()`) + return buffer.join(``) + } + function Promise(schema: Types.TPromise) { + const item = Visit(schema.item) + return `${item}.promise()` + } + function Record(schema: Types.TRecord) { + for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { + const type = Visit(value) + if (key === `^(0|[1-9][0-9]*)$`) { + throw new ZodUnsupportedType(`TRecord`) + } else { + return `z.record(${type})` + } + } + throw Error(`TypeScriptCodeGen: Unreachable`) + } + function Ref(schema: Types.TRef) { + if (!reference_map.has(schema.$ref!)) throw new ZodNonReferentialType(schema.$ref!) + return schema.$ref + } + function Self(schema: Types.TSelf) { + if (!reference_map.has(schema.$ref!)) throw new ZodNonReferentialType(schema.$ref!) + recursive_set.add(schema.$ref) + return schema.$ref + } + function Tuple(schema: Types.TTuple) { + if (schema.items === undefined) return `[]` + const items = schema.items.map((schema) => Visit(schema)).join(`, `) + return `z.tuple([${items}])` + } + function UInt8Array(schema: Types.TUint8Array): string { + throw new ZodUnsupportedType(`TUint8Array`) + } + function Undefined(schema: Types.TUndefined) { + return `z.undefined()` + } + function Union(schema: Types.TUnion) { + return `z.union([${schema.anyOf.map((schema) => Visit(schema)).join(`, `)}])` + } + function Unknown(schema: Types.TUnknown) { + return `z.unknown()` + } + function Void(schema: Types.TVoid) { + return `z.void()` + } + function Visit(schema: Types.TSchema): string { + if (schema.$id !== undefined) reference_map.set(schema.$id, schema) + if (TypeGuard.TAny(schema)) { + return Any(schema) + } else if (TypeGuard.TArray(schema)) { + return Array(schema) + } else if (TypeGuard.TBoolean(schema)) { + return Boolean(schema) + } else if (TypeGuard.TConstructor(schema)) { + return Constructor(schema) + } else if (TypeGuard.TFunction(schema)) { + return Function(schema) + } else if (TypeGuard.TInteger(schema)) { + return Integer(schema) + } else if (TypeGuard.TLiteral(schema)) { + return Literal(schema) + } else if (TypeGuard.TNever(schema)) { + return Never(schema) + } else if (TypeGuard.TNull(schema)) { + return Null(schema) + } else if (TypeGuard.TNumber(schema)) { + return Number(schema) + } else if (TypeGuard.TObject(schema)) { + return Object(schema) + } else if (TypeGuard.TPromise(schema)) { + return Promise(schema) + } else if (TypeGuard.TRecord(schema)) { + return Record(schema) + } else if (TypeGuard.TRef(schema)) { + return Ref(schema) + } else if (TypeGuard.TSelf(schema)) { + return Self(schema) + } else if (TypeGuard.TString(schema)) { + return String(schema) + } else if (TypeGuard.TTuple(schema)) { + return Tuple(schema) + } else if (TypeGuard.TUint8Array(schema)) { + return UInt8Array(schema) + } else if (TypeGuard.TUndefined(schema)) { + return Undefined(schema) + } else if (TypeGuard.TUnion(schema)) { + return Union(schema) + } else if (TypeGuard.TUnknown(schema)) { + return Unknown(schema) + } else if (TypeGuard.TVoid(schema)) { + return Void(schema) + } else { + throw Error(`TypeScriptCodeGen: Unknown type`) + } + } + const reference_map = new Map() + const recursive_set = new Set() + /** Renders a Zod definition of a TypeBox type */ + export function Generate(schema: Types.TSchema, references: Types.TSchema[], options: ZodCodegenOptions = { imports: true, exports: false }) { + const emitted = new Set() + const exports = options.exports ? 'export' : '' + const imports_code: string[] = [] + const reference_code: string[] = [] + const type_code: string[] = [] + // initialize root schematic and reference map + if (schema.$id === undefined) schema.$id = `T_Generated` + for (const reference of references) { + if (reference.$id === undefined) throw new ZodNonReferentialType(JSON.stringify(reference)) + reference_map.set(reference.$id, reference) + } + // render-code: Imports required for the generated code + if (options.imports) { + imports_code.push(`import z from 'zod'`) + } + // render-type: If we detect the root schematic has been referenced, we interpret this as a recursive + // root. It`s noted that zod performs 4x slower when wrapped in a lazy(() => ...), so this is considered + // an optimization. + const typedef = [...Visit(schema)].join(``) + if (recursive_set.has(schema.$id!)) { + type_code.push(`${exports} const ${schema.$id || `T`} = z.lazy(() => ${Formatter.Format(typedef)})`) + } else { + type_code.push(`${exports} const ${schema.$id || `T`} = ${Formatter.Format(typedef)}`) + } + emitted.add(schema.$id!) + // render-reference: References may either be recursive or not. We track a recursive_set when visiting + // schemas of type TSelf. If we`ve never observed the reference through recursion, when it should be safe + // to omit the lazy(() => ...) wrap. + for (const reference of reference_map.values()) { + if (emitted.has(reference.$id!)) continue + const typedef = [...Visit(schema)].join(``) + if (recursive_set.has(reference.$id!)) { + reference_code.push(`${exports} const ${reference.$id} = z.lazy(() => ${typedef})`) + } else { + reference_code.push(`${exports} const ${reference.$id} = ${typedef}`) + } + emitted.add(reference.$id!) + } + return Formatter.Format([...imports_code, ...reference_code, ...type_code].join(`\n\n`)) + } +} From c843318058b10729a769220bebc75cbc5306f2e5 Mon Sep 17 00:00:00 2001 From: Yeongjet Tang Date: Sat, 7 Jan 2023 20:59:24 +0800 Subject: [PATCH 056/369] Escape Regex String Patterns in Compiler (#302) Co-authored-by: yeongjet --- src/compiler/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 1e7e964bc..92fb69739 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -264,7 +264,7 @@ export namespace TypeCompiler { if (IsNumber(schema.minLength)) yield `(${value}.length >= ${schema.minLength})` if (IsNumber(schema.maxLength)) yield `(${value}.length <= ${schema.maxLength})` if (schema.pattern !== undefined) { - const local = PushLocal(`new RegExp(/${schema.pattern}/);`) + const local = PushLocal(`${new RegExp(schema.pattern)};`) yield `(${local}.test(${value}))` } if (schema.format !== undefined) { From d8849641694101d66795d0263ced392965fab59d Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 7 Jan 2023 22:03:42 +0900 Subject: [PATCH 057/369] Version --- package.json | 2 +- test/runtime/compiler/string.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b94472262..056480855 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.16", + "version": "0.25.17", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts index 2f0005afb..baff1e18e 100644 --- a/test/runtime/compiler/string.ts +++ b/test/runtime/compiler/string.ts @@ -62,4 +62,9 @@ describe('type/compiler/String', () => { const T = Type.String({ pattern: '[\\d]{5}' }) Ok(T, '12345') }) + + it('Should should escape characters in the pattern', () => { + const T = Type.String({ pattern: '/a/' }) + Ok(T, '/a/') + }) }) From df1dda9fd15f770bb6e82f1fb08732fcb62589ad Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 9 Jan 2023 20:53:24 +0900 Subject: [PATCH 058/369] Support Date Conversion For Cast (#307) --- changelog.md | 6 ++ example/index.ts | 2 +- package.json | 2 +- src/compiler/compiler.ts | 2 +- src/errors/errors.ts | 3 + src/value/cast.ts | 93 ++++++++++++++-------------- src/value/check.ts | 3 + src/value/value.ts | 4 +- test/runtime/compiler/date.ts | 8 +-- test/runtime/schema/date.ts | 4 ++ test/runtime/value/cast/convert.ts | 98 ++++++++++++++++-------------- test/runtime/value/cast/date.ts | 4 +- test/runtime/value/check/date.ts | 5 ++ 13 files changed, 131 insertions(+), 103 deletions(-) diff --git a/changelog.md b/changelog.md index 8ab7ca510..57c8a06bf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## [0.25.18](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.18) + +Updates: + +- [307](https://github.com/sinclairzx81/typebox/pull/307) implements date conversion when casting values with `Value.Cast(Type.Date(), ...)`. Castable values include numbers (interpretted as timestamps) and iso8601 string values. Uncastable values will result in dates with values of `1970-01-01T00:00:00.000Z`. This version also includes more robust checks for Dates initialized with invalid values. + ## [0.25.11](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.11) Updates: diff --git a/example/index.ts b/example/index.ts index 6307054b6..588558a9d 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,4 +1,4 @@ -import { CodeGen } from '@sinclair/typebox/codegen' +import { Codegen } from '@sinclair/typebox/codegen' import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { TypeGuard } from '@sinclair/typebox/guard' diff --git a/package.json b/package.json index 056480855..295a2dedf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.17", + "version": "0.25.18", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 92fb69739..016279112 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -140,7 +140,7 @@ export namespace TypeCompiler { } function* Date(schema: Types.TDate, value: string): IterableIterator { - yield `(${value} instanceof Date)` + yield `(${value} instanceof Date) && !isNaN(${value}.getTime())` if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `(${value}.getTime() > ${schema.exclusiveMinimumTimestamp})` if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `(${value}.getTime() < ${schema.exclusiveMaximumTimestamp})` if (IsNumber(schema.minimumTimestamp)) yield `(${value}.getTime() >= ${schema.minimumTimestamp})` diff --git a/src/errors/errors.ts b/src/errors/errors.ts index ad346e3a5..464d477d0 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -151,6 +151,9 @@ export namespace ValueErrors { if (!(value instanceof globalThis.Date)) { return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } } + if (isNaN(value.getTime())) { + return yield { type: ValueErrorType.Date, schema, path, value, message: `Invalid Date` } + } if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } } diff --git a/src/value/cast.ts b/src/value/cast.ts index 5fccc466d..2535addff 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -35,25 +35,21 @@ import { Custom } from '../custom/index' // ---------------------------------------------------------------------------------------------- // Errors // ---------------------------------------------------------------------------------------------- - export class ValueCastReferenceTypeError extends Error { constructor(public readonly schema: Types.TRef | Types.TSelf) { super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) } } - export class ValueCastArrayUniqueItemsTypeError extends Error { constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { super('ValueCast: Array cast produced invalid data due to uniqueItems constraint') } } - export class ValueCastNeverTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueCast: Never types cannot be cast') } } - export class ValueCastRecursiveTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueCast.Recursive: Cannot cast recursive schemas') @@ -71,7 +67,6 @@ export class ValueCastUnknownTypeError extends Error { // to prevent large property counts biasing results. Properties that match literal values are // maximally awarded as literals are typically used as union discriminator fields. // ---------------------------------------------------------------------------------------------- - namespace UnionCastCreate { function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { if (schema[Types.Kind] === 'Object' && typeof value === 'object' && value !== null) { @@ -89,7 +84,6 @@ namespace UnionCastCreate { return ValueCheck.Check(schema, references, value) ? 1 : 0 } } - function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { let [select, best] = [union.anyOf[0], 0] for (const schema of union.anyOf) { @@ -101,7 +95,6 @@ namespace UnionCastCreate { } return select } - export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { if (union.default !== undefined) { return union.default @@ -116,71 +109,99 @@ export namespace ValueCast { // ---------------------------------------------------------------------------------------------- // Guards // ---------------------------------------------------------------------------------------------- - function IsArray(value: unknown): value is unknown[] { return typeof value === 'object' && globalThis.Array.isArray(value) } - + function IsDate(value: unknown): value is Date { + return typeof value === 'object' && value instanceof globalThis.Date + } function IsString(value: unknown): value is string { return typeof value === 'string' } - function IsBoolean(value: unknown): value is boolean { return typeof value === 'boolean' } - function IsBigInt(value: unknown): value is bigint { return typeof value === 'bigint' } - function IsNumber(value: unknown): value is number { return typeof value === 'number' && !isNaN(value) } - function IsStringNumeric(value: unknown): value is string { return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) } - function IsValueToString(value: unknown): value is { toString: () => string } { return IsBigInt(value) || IsBoolean(value) || IsNumber(value) } - function IsValueTrue(value: unknown): value is true { return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === globalThis.BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) } - function IsValueFalse(value: unknown): value is true { return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === globalThis.BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) } + function IsTimeStringWithTimeZone(value: unknown): value is string { + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + } + function IsTimeStringWithoutTimeZone(value: unknown): value is string { + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + } + function IsDateTimeStringWithTimeZone(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + } + function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + } + function IsDateString(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) + } // ---------------------------------------------------------------------------------------------- // Convert // ---------------------------------------------------------------------------------------------- - function TryConvertString(value: unknown) { return IsValueToString(value) ? value.toString() : value } - function TryConvertNumber(value: unknown) { return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : value } - function TryConvertInteger(value: unknown) { return IsStringNumeric(value) ? parseInt(value) : IsValueTrue(value) ? 1 : value } - function TryConvertBoolean(value: unknown) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value } + function TryConvertDate(value: unknown) { + // note: this function may return an invalid dates for the regex tests + // above. Invalid dates will however be checked during the casting + // function and will return a epoch date if invalid. Consider better + // string parsing for the iso dates in future revisions. + return IsDate(value) + ? value + : IsNumber(value) + ? new globalThis.Date(value) + : IsValueTrue(value) + ? new globalThis.Date(1) + : IsStringNumeric(value) + ? new globalThis.Date(parseInt(value)) + : IsTimeStringWithoutTimeZone(value) + ? new globalThis.Date(`1970-01-01T${value}.000Z`) + : IsTimeStringWithTimeZone(value) + ? new globalThis.Date(`1970-01-01T${value}`) + : IsDateTimeStringWithoutTimeZone(value) + ? new globalThis.Date(`${value}.000Z`) + : IsDateTimeStringWithTimeZone(value) + ? new globalThis.Date(value) + : IsDateString(value) + ? new globalThis.Date(`${value}T00:00:00.000Z`) + : value + } // ---------------------------------------------------------------------------------------------- // Cast // ---------------------------------------------------------------------------------------------- - function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) const created = IsArray(value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) @@ -192,12 +213,10 @@ export namespace ValueCast { if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) return unique } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { const conversion = TryConvertBoolean(value) return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) const required = new Set(schema.returns.required || []) @@ -208,37 +227,30 @@ export namespace ValueCast { } return result } - function Date(schema: Types.TDate, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + const conversion = TryConvertDate(value) + return ValueCheck.Check(schema, references, conversion) ? ValueClone.Clone(conversion) : ValueCreate.Create(schema, references) } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } - function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): any { const conversion = TryConvertInteger(value) return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) } - function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Never(schema: Types.TNever, references: Types.TSchema[], value: any): any { throw new ValueCastNeverTypeError(schema) } - function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): any { const conversion = TryConvertNumber(value) return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) } - function Object(schema: Types.TObject, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) @@ -258,11 +270,9 @@ export namespace ValueCast { } return result } - function Promise(schema: Types.TSchema, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } - function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value) || value instanceof globalThis.Date) return ValueCreate.Create(schema, references) @@ -274,59 +284,47 @@ export namespace ValueCast { } return result } - function Recursive(schema: Types.TRecursive, references: Types.TSchema[], value: any): any { throw new ValueCastRecursiveTypeError(schema) } - function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): any { const reference = references.find((reference) => reference.$id === schema.$ref) if (reference === undefined) throw new ValueCastReferenceTypeError(schema) return Visit(reference, references, value) } - function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): any { const reference = references.find((reference) => reference.$id === schema.$ref) if (reference === undefined) throw new ValueCastReferenceTypeError(schema) return Visit(reference, references, value) } - function String(schema: Types.TString, references: Types.TSchema[], value: any): any { const conversion = TryConvertString(value) return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) } - function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) if (!globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) if (schema.items === undefined) return [] return schema.items.map((schema, index) => Visit(schema, references, value[index])) } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : UnionCastCreate.Create(schema, references, value) } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } - export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { const anyReferences = schema.$id === undefined ? references : [schema, ...references] const anySchema = schema as any @@ -384,7 +382,6 @@ export namespace ValueCast { return UserDefined(anySchema, anyReferences, value) } } - export function Cast(schema: T, references: [...R], value: any): Types.Static { return schema.$id === undefined ? Visit(schema, references, value) : Visit(schema, [schema, ...references], value) } diff --git a/src/value/check.ts b/src/value/check.ts index 4bd8389ce..4d4a50558 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -76,6 +76,9 @@ export namespace ValueCheck { if (!(value instanceof globalThis.Date)) { return false } + if (isNaN(value.getTime())) { + return false + } if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { return false } diff --git a/src/value/value.ts b/src/value/value.ts index 1e36736d0..aba57e044 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -38,9 +38,9 @@ import { ValueDelta, Edit } from './delta' /** Provides functions to perform structural updates to JavaScript values */ export namespace Value { - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number, boolean and date values if a reasonable conversion is possible. */ export function Cast(schema: T, references: [...R], value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number and boolean values if a reasonable conversion is possible. */ + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number, boolean and date values if a reasonable conversion is possible. */ export function Cast(schema: T, value: unknown): Types.Static export function Cast(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] diff --git a/test/runtime/compiler/date.ts b/test/runtime/compiler/date.ts index ae8855bd8..943bfa0e2 100644 --- a/test/runtime/compiler/date.ts +++ b/test/runtime/compiler/date.ts @@ -34,25 +34,25 @@ describe('type/compiler/Date', () => { const T = Type.Date() Ok(T, new Date()) }) - + it('Should not validate Date if is invalid', () => { + const T = Type.Date() + Fail(T, new Date('not-a-valid-date')) + }) it('Should validate Date minimumTimestamp', () => { const T = Type.Date({ minimumTimestamp: 10 }) Fail(T, new Date(9)) Ok(T, new Date(10)) }) - it('Should validate Date maximumTimestamp', () => { const T = Type.Date({ maximumTimestamp: 10 }) Ok(T, new Date(10)) Fail(T, new Date(11)) }) - it('Should validate Date exclusiveMinimumTimestamp', () => { const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) Fail(T, new Date(10)) Ok(T, new Date(11)) }) - it('Should validate Date exclusiveMaximumTimestamp', () => { const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) Ok(T, new Date(9)) diff --git a/test/runtime/schema/date.ts b/test/runtime/schema/date.ts index 2e0672e95..7843e4af1 100644 --- a/test/runtime/schema/date.ts +++ b/test/runtime/schema/date.ts @@ -39,6 +39,10 @@ describe('type/schema/Date', () => { const T = Type.Date() Ok(T, new Date()) }) + it('Should not validate Date if is invalid', () => { + const T = Type.Date() + Fail(T, new Date('not-a-valid-date')) + }) it('Should validate Date minimumTimestamp', () => { const T = Type.Date({ minimumTimestamp: 10 }) Fail(T, new Date(9)) diff --git a/test/runtime/value/cast/convert.ts b/test/runtime/value/cast/convert.ts index ba63f16a8..825dddc73 100644 --- a/test/runtime/value/cast/convert.ts +++ b/test/runtime/value/cast/convert.ts @@ -2,280 +2,290 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' +//--------------------------------------------------------------- +// String Convert +//--------------------------------------------------------------- describe('value/convert/String', () => { it('Should convert string', () => { const value = 'hello' const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, 'hello') }) - it('Should convert number #1', () => { const value = 42 const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, '42') }) - it('Should convert number #2', () => { const value = 42n const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, '42') }) - it('Should convert true', () => { const value = true const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, 'true') }) - it('Should convert false', () => { const value = false const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, 'false') }) - it('Should convert object', () => { const value = {} const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, '') }) - it('Should convert array', () => { const value = [] as any[] const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, '') }) }) - +//--------------------------------------------------------------- +// Number Convert +//--------------------------------------------------------------- describe('value/convert/Number', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 0) }) - it('Should convert string #2', () => { const value = '3.14' const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 3.14) }) - it('Should convert string #3', () => { const value = '-0' const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 0) }) - it('Should convert string #4', () => { const value = '-100' const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, -100) }) - it('Should convert number', () => { const value = 42 const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 42) }) - it('Should convert true', () => { const value = true const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 1) }) - it('Should convert false', () => { const value = false const result = Value.Cast(Type.Number(), value) Assert.deepEqual(result, 0) }) - it('Should convert object', () => { const value = {} const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, 0) }) - it('Should convert array', () => { const value = [] as any[] const result = Value.Cast(Type.String(), value) Assert.deepEqual(result, 0) }) }) - +//--------------------------------------------------------------- +// Integer Convert +//--------------------------------------------------------------- describe('value/convert/Integer', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 0) }) - it('Should convert string #2', () => { const value = '3.14' const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 3) }) - it('Should convert string #3', () => { const value = '-0' const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 0) }) - it('Should convert string #4', () => { const value = '-100' const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, -100) }) - it('Should convert number', () => { const value = 42 const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 42) }) - it('Should convert true', () => { const value = true const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 1) }) - it('Should convert false', () => { const value = false const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 0) }) - it('Should convert object', () => { const value = {} const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 0) }) - it('Should convert array', () => { const value = [] as any[] const result = Value.Cast(Type.Integer(), value) Assert.deepEqual(result, 0) }) }) - +//--------------------------------------------------------------- +// Boolean Convert +//--------------------------------------------------------------- describe('value/convert/Boolean', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert string #2', () => { const value = 'true' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert string #3', () => { const value = 'TRUE' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert string #4', () => { const value = 'false' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert string #5', () => { const value = '0' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert string #6', () => { const value = '1' const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert string #7', () => { const value = '0' const result = Value.Cast(Type.Boolean({ default: true }), value) Assert.deepEqual(result, false) }) - it('Should convert string #8', () => { const value = '1' const result = Value.Cast(Type.Boolean({ default: false }), value) Assert.deepEqual(result, true) }) - it('Should convert string #8', () => { const value = '2' const result = Value.Cast(Type.Boolean({ default: true }), value) Assert.deepEqual(result, true) }) - it('Should convert number #1', () => { const value = 0 const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert number #2', () => { const value = 1n const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert number #3', () => { const value = 1 const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert number #4', () => { const value = 2 const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert number #5', () => { const value = 0 const result = Value.Cast(Type.Boolean({ default: true }), value) Assert.deepEqual(result, false) }) - it('Should convert number #6', () => { const value = 1 const result = Value.Cast(Type.Boolean({ default: false }), value) Assert.deepEqual(result, true) }) - it('Should convert number #7', () => { const value = 2 const result = Value.Cast(Type.Boolean({ default: true }), value) Assert.deepEqual(result, true) }) - it('Should convert true', () => { const value = true const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, true) }) - it('Should convert false', () => { const value = false const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert object', () => { const value = {} const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) - it('Should convert array', () => { const value = [] as any[] const result = Value.Cast(Type.Boolean(), value) Assert.deepEqual(result, false) }) }) + +//--------------------------------------------------------------- +// Date Convert +//--------------------------------------------------------------- +describe('value/convert/Date', () => { + it('Should convert from number', () => { + const result = Value.Cast(Type.Date(), 123) + Assert.deepEqual(result.getTime(), 123) + }) + it('Should convert from numeric string', () => { + const result = Value.Cast(Type.Date(), '123') + Assert.deepEqual(result.getTime(), 123) + }) + it('Should convert from boolean true (interpretted as numeric 1)', () => { + const result = Value.Cast(Type.Date(), true) + Assert.deepEqual(result.getTime(), 1) + }) + it('Should convert from datetime string', () => { + const result = Value.Cast(Type.Date(), '1980-02-03T01:02:03.000Z') + Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + }) + it('Should convert from datetime string without timezone', () => { + const result = Value.Cast(Type.Date(), '1980-02-03T01:02:03') + Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + }) + it('Should convert from time with timezone', () => { + const result = Value.Cast(Type.Date(), '01:02:03.000Z') + Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + }) + it('Should convert from time without timezone', () => { + const result = Value.Cast(Type.Date(), '01:02:03') + Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + }) + it('Should convert from date string', () => { + const result = Value.Cast(Type.Date(), '1980-02-03') + Assert.deepEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') + }) + it('Should convert invalid strings to unix epoch 0', () => { + const result = Value.Cast(Type.Date(), 'invalid-date') + Assert.deepEqual(result.toISOString(), '1970-01-01T00:00:00.000Z') + }) +}) diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts index 69c26dc40..a991af828 100644 --- a/test/runtime/value/cast/date.ts +++ b/test/runtime/value/cast/date.ts @@ -15,13 +15,13 @@ describe('value/cast/Date', () => { it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.deepEqual(result.getTime(), 1) // convert }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.deepEqual(result.getTime(), 1) // convert }) it('Should upcast from object', () => { diff --git a/test/runtime/value/check/date.ts b/test/runtime/value/check/date.ts index 583e1b620..39d40e00d 100644 --- a/test/runtime/value/check/date.ts +++ b/test/runtime/value/check/date.ts @@ -44,4 +44,9 @@ describe('value/check/Date', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) + it('Should not validate Date if is invalid', () => { + const value = new Date('not-a-valid-date') + const result = Value.Check(T, value) + Assert.equal(result, false) + }) }) From 4d7ef420d19af8d133ad651285845e73e17198ab Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 9 Jan 2023 23:26:24 +0900 Subject: [PATCH 059/369] Revised User Defined Type and Format API (#308) --- package.json | 2 +- readme.md | 216 +++++++++++++---------- src/system/system.ts | 39 +++- test/runtime/system/AllowArrayObjects.ts | 2 +- test/runtime/system/AllowNaN.ts | 2 +- test/runtime/system/CreateFormat.ts | 17 ++ test/runtime/system/CreateType.ts | 23 +++ test/runtime/system/index.ts | 2 + 8 files changed, 200 insertions(+), 103 deletions(-) create mode 100644 test/runtime/system/CreateFormat.ts create mode 100644 test/runtime/system/CreateType.ts diff --git a/package.json b/package.json index 295a2dedf..fd8c28e3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.18", + "version": "0.25.19", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index ac6ea8174..3a576e9f7 100644 --- a/readme.md +++ b/readme.md @@ -96,10 +96,11 @@ License MIT - [Errors](#values-errors) - [Pointer](#values-pointer) - [TypeCheck](#typecheck) - - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) - - [Custom Types](#typecheck-custom-types) - - [Custom Formats](#typecheck-custom-formats) + - [Ajv](#typecheck-ajv) +- [TypeSystem](#typecheck) + - [Types](#typesystem-types) + - [Formats](#typesystem-formats) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) - [Validate](#benchmark-validate) @@ -936,6 +937,71 @@ TypeBox targets JSON Schema Draft 6 and is built and tested against the Ajv JSON The following sections detail using these validators. + + +### TypeCompiler + +TypeBox includes an high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. + +The compiler module is provided as an optional import. + +```typescript +import { TypeCompiler } from '@sinclair/typebox/compiler' +``` + +Use the `Compile(...)` function to compile a type. + +```typescript +const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> + +const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true +``` + +Validation errors can be read with the `Errors(...)` function. + +```typescript +const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> + +const value = { } + +const errors = [...C.Errors(value)] // const errors = [{ + // schema: { type: 'number' }, + // path: '/x', + // value: undefined, + // message: 'Expected number' + // }, { + // schema: { type: 'number' }, + // path: '/y', + // value: undefined, + // message: 'Expected number' + // }, { + // schema: { type: 'number' }, + // path: '/z', + // value: undefined, + // message: 'Expected number' + // }] +``` + +Compiled routines can be inspected with the `.Code()` function. + +```typescript +const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck + +console.log(C.Code()) // return function check(value) { + // return ( + // (typeof value === 'string') + // ) + // } +``` + ### Ajv @@ -1066,125 +1132,87 @@ const R = ajv.validate(Type.Object({ // const R = true - + -### TypeCompiler +## TypeSystem -TypeBox provides an optional high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. If defining custom types with `Type.Unsafe` please consider Ajv. +TypeBox provides an extensible TypeSystem module that enables developers to register additional types above and beyond the standard or extended type set. This module also allows developers to define custom string formats as well as override certain type checking behaviours. -The compiler module is provided as an optional import. +The TypeSystem module is provided as an optional import. ```typescript -import { TypeCompiler } from '@sinclair/typebox/compiler' +import { TypeSystem } from '@sinclair/typebox/system' ``` -Use the `Compile(...)` function to compile a type. - -```typescript -const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> + -const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true -``` +### Types -Validation errors can be read with the `Errors(...)` function. +Use the `CreateType(...)` function to specify and return a custom type. This function will return a type factory function that can be used to construct the type. The following creates and registers a BigNumber type which will statically infer as `bigint`. ```typescript -const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck> +//-------------------------------------------------------------------------------------------- +// +// Use TypeSystem.CreateType(...) to define and return a type factory function +// +//-------------------------------------------------------------------------------------------- -const value = { } +type BigNumberOptions = { minimum?: bigint; maximum?: bigint } -const errors = [...C.Errors(value)] // const errors = [{ - // schema: { type: 'number' }, - // path: '/x', - // value: undefined, - // message: 'Expected number' - // }, { - // schema: { type: 'number' }, - // path: '/y', - // value: undefined, - // message: 'Expected number' - // }, { - // schema: { type: 'number' }, - // path: '/z', - // value: undefined, - // message: 'Expected number' - // }] -``` +const BigNumber = TypeSystem.CreateType( + 'BigNumber', + (options, value) => { + if (typeof value !== 'bigint') return false + if (options.maximum !== undefined && value > options.maximum) return false + if (options.minimum !== undefined && value < options.minimum) return false + return true + } +) -Compiled routines can be inspected with the `.Code()` function. +//-------------------------------------------------------------------------------------------- +// +// Use the custom type like any other type +// +//-------------------------------------------------------------------------------------------- -```typescript -const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck +const T = BigNumber({ minimum: 10n, maximum: 20n }) // const T = { + // minimum: 10n, + // maximum: 20n, + // [Symbol(TypeBox.Kind)]: 'BigNumber' + // } -console.log(C.Code()) // return function check(value) { - // return ( - // (typeof value === 'string') - // ) - // } +const C = TypeCompiler.Compile(T) +const X = C.Check(15n) // const X = true +const Y = C.Check(5n) // const Y = false +const Z = C.Check(25n) // const Z = false ``` - + -### Custom Types +### Formats -Use the custom module to create user defined types. User defined types require a `[Kind]` symbol property which is used to match the schema against a registered type. Custom types are specific to TypeBox and can only be used with the TypeCompiler and Value modules. - -The custom module is an optional import. +Use the `CreateFormat(...)` function to specify user defined string formats. The following creates a custom string format that checks for lowercase. ```typescript -import { Custom } from '@sinclair/typebox/custom' -``` - -The following creates a `bigint` type. - -```typescript -import { Type, Kind } from '@sinclair/typebox' - -Custom.Set('bigint', (schema, value) => typeof value === 'bigint') -// ▲ -// │ -// └────────────────────────────┐ -// │ -const T = Type.Unsafe({ [Kind]: 'bigint' }) // const T = { [Kind]: 'BigInt' } - -const A = TypeCompiler.Compile(T).Check(1n) // const A = true - -const B = Value.Check(T, 1) // const B = false -``` - - - -### Custom Formats - -Use the format module to create user defined string formats. The format module is specific to TypeBox can only be used with the TypeCompiler and Value modules. If using Ajv, please refer to the official Ajv format documentation located [here](https://ajv.js.org/guide/formats.html). - -The format module is an optional import. +//-------------------------------------------------------------------------------------------- +// +// Use TypeSystem.CreateFormat(...) to define a custom string format +// +//-------------------------------------------------------------------------------------------- -```typescript -import { Format } from '@sinclair/typebox/format' -``` +TypeSystem.CreateFormat('lowercase', value => value === value.toLowerCase()) -The following creates a custom string format that checks for lowercase. +//-------------------------------------------------------------------------------------------- +// +// Use the format by creating string types with the 'format' option +// +//-------------------------------------------------------------------------------------------- -```typescript -Format.Set('lowercase', value => value === value.toLowerCase()) -// ▲ -// │ -// └────────────────────┐ -// │ const T = Type.String({ format: 'lowercase' }) -const A = TypeCompiler.Compile(T).Check('action') // const A = true +const A = Value.Check(T, 'action') // const A = true -const B = Value.Check(T, 'Action') // const B = false +const B = Value.Check(T, 'ACTION') // const B = false ``` diff --git a/src/system/system.ts b/src/system/system.ts index 0ddd2a1f0..61486609b 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,13 +26,40 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { Kind, Type } from '@sinclair/typebox' +import { Custom } from '../custom/index' +import { Format } from '../format/index' + +export class TypeSystemDuplicateTypeKind extends Error { + constructor(kind: string) { + super(`Duplicate kind '${kind}' detected`) + } +} +export class TypeSystemDuplicateFormat extends Error { + constructor(kind: string) { + super(`Duplicate format '${kind}' detected`) + } +} + +/** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { - /** - * Sets whether arrays should be treated as kinds of objects. The default is `false` - */ + /** Sets whether arrays should be treated as kinds of objects. The default is `false` */ export let AllowArrayObjects: boolean = false - /** - * Sets whether numeric checks should consider NaN a valid number type. The default is `false` - */ + + /** Sets whether numeric checks should consider NaN a valid number type. The default is `false` */ export let AllowNaN: boolean = false + + /** Creates a custom type */ + export function CreateType(kind: string, callback: (options: Options, value: unknown) => boolean) { + if (Custom.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) + Custom.Set(kind, callback) + return (options: Partial = {}) => Type.Unsafe({ ...options, [Kind]: kind }) + } + + /** Creates a custom string format */ + export function CreateFormat(format: string, callback: (value: string) => boolean) { + if (Format.Has(format)) throw new TypeSystemDuplicateFormat(format) + Format.Set(format, callback) + return callback + } } diff --git a/test/runtime/system/AllowArrayObjects.ts b/test/runtime/system/AllowArrayObjects.ts index 9ee0c46e3..064139813 100644 --- a/test/runtime/system/AllowArrayObjects.ts +++ b/test/runtime/system/AllowArrayObjects.ts @@ -2,7 +2,7 @@ import { Ok, Fail } from '../compiler/validate' import { TypeSystem } from '@sinclair/typebox/system' import { Type } from '@sinclair/typebox' -describe('TypeSystem/AllowArrayObjects', () => { +describe('system/TypeSystem/AllowArrayObjects', () => { before(() => { TypeSystem.AllowArrayObjects = true }) diff --git a/test/runtime/system/AllowNaN.ts b/test/runtime/system/AllowNaN.ts index bcf0d9ee5..4795e7936 100644 --- a/test/runtime/system/AllowNaN.ts +++ b/test/runtime/system/AllowNaN.ts @@ -2,7 +2,7 @@ import { Ok, Fail } from '../compiler/validate' import { TypeSystem } from '@sinclair/typebox/system' import { Type } from '@sinclair/typebox' -describe('TypeSystem/AllowNaN', () => { +describe('system/TypeSystem/AllowNaN', () => { before(() => { TypeSystem.AllowNaN = true }) diff --git a/test/runtime/system/CreateFormat.ts b/test/runtime/system/CreateFormat.ts new file mode 100644 index 000000000..751eb34e8 --- /dev/null +++ b/test/runtime/system/CreateFormat.ts @@ -0,0 +1,17 @@ +import { Ok, Fail } from '../compiler/validate' +import { Assert } from '../assert/index' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystem/CreateFormat', () => { + it('Should create and validate a format', () => { + TypeSystem.CreateFormat('CreateFormat0', (value) => value === value.toLowerCase()) + const T = Type.String({ format: 'CreateFormat0' }) + Ok(T, 'action') + Fail(T, 'ACTION') + }) + it('Should throw if registering the same format twice', () => { + TypeSystem.CreateFormat('CreateFormat1', (value) => true) + Assert.throws(() => TypeSystem.CreateFormat('CreateFormat1', (value) => true)) + }) +}) diff --git a/test/runtime/system/CreateType.ts b/test/runtime/system/CreateType.ts new file mode 100644 index 000000000..60626b7d2 --- /dev/null +++ b/test/runtime/system/CreateType.ts @@ -0,0 +1,23 @@ +import { Ok, Fail } from '../compiler/validate' +import { Assert } from '../assert/index' +import { TypeSystem } from '@sinclair/typebox/system' + +describe('system/TypeSystem/CreateType', () => { + it('Should create and validate a type', () => { + type BigNumberOptions = { minimum?: bigint; maximum?: bigint } + const BigNumber = TypeSystem.CreateType('CreateType0', (options, value) => { + if (typeof value !== 'bigint') return false + if (options.maximum !== undefined && value > options.maximum) return false + if (options.minimum !== undefined && value < options.minimum) return false + return true + }) + const T = BigNumber({ minimum: 10n, maximum: 20n }) + Ok(T, 15n) + Fail(T, 5n) + Fail(T, 25n) + }) + it('Should throw if registering the same type twice', () => { + TypeSystem.CreateType('CreateType1', () => true) + Assert.throws(() => TypeSystem.CreateType('CreateType1', () => true)) + }) +}) diff --git a/test/runtime/system/index.ts b/test/runtime/system/index.ts index fde988aa0..72bdb9c85 100644 --- a/test/runtime/system/index.ts +++ b/test/runtime/system/index.ts @@ -1,2 +1,4 @@ import './AllowArrayObjects' import './AllowNaN' +import './CreateFormat' +import './CreateType' From a57f858d79976b0b02fbfde4bd94659817e54a7e Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 9 Jan 2023 23:33:37 +0900 Subject: [PATCH 060/369] Use Relative Import --- package.json | 2 +- src/system/system.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fd8c28e3e..22362682c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.19", + "version": "0.25.20", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/system/system.ts b/src/system/system.ts index 61486609b..97c86e2ec 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Kind, Type } from '@sinclair/typebox' +import { Kind, Type } from '../typebox' import { Custom } from '../custom/index' import { Format } from '../format/index' From c4061c162eb99be3d07b08c9cb393804c3977060 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 19 Jan 2023 15:23:30 +0900 Subject: [PATCH 061/369] Annual License Update (#312) --- codegen/codegen.ts | 2 +- codegen/formatter.ts | 2 +- codegen/index.ts | 2 +- codegen/jsonschema.ts | 2 +- codegen/typebox.ts | 2 +- codegen/typescript.ts | 2 +- codegen/zod.ts | 2 +- example/collections/array.ts | 2 +- example/collections/index.ts | 2 +- example/collections/map.ts | 2 +- example/collections/set.ts | 2 +- example/extensions/index.ts | 2 +- example/extensions/intersect-allof.ts | 2 +- example/extensions/union-enum.ts | 2 +- example/extensions/union-oneof.ts | 2 +- license | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 2 +- src/compiler/index.ts | 2 +- src/conditional/conditional.ts | 2 +- src/conditional/index.ts | 2 +- src/conditional/structural.ts | 2 +- src/custom/custom.ts | 2 +- src/custom/index.ts | 2 +- src/errors/errors.ts | 2 +- src/errors/index.ts | 2 +- src/format/format.ts | 2 +- src/format/index.ts | 2 +- src/guard/guard.ts | 2 +- src/guard/index.ts | 2 +- src/hash/hash.ts | 2 +- src/hash/index.ts | 2 +- src/system/index.ts | 2 +- src/system/system.ts | 2 +- src/typebox.ts | 2 +- src/value/cast.ts | 2 +- src/value/check.ts | 2 +- src/value/clone.ts | 2 +- src/value/create.ts | 2 +- src/value/delta.ts | 2 +- src/value/equal.ts | 2 +- src/value/index.ts | 2 +- src/value/is.ts | 2 +- src/value/pointer.ts | 2 +- src/value/value.ts | 2 +- 46 files changed, 47 insertions(+), 47 deletions(-) diff --git a/codegen/codegen.ts b/codegen/codegen.ts index 8e0237439..81d3797d3 100644 --- a/codegen/codegen.ts +++ b/codegen/codegen.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/formatter.ts b/codegen/formatter.ts index 6e3b4a7b8..68d8cd440 100644 --- a/codegen/formatter.ts +++ b/codegen/formatter.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/index.ts b/codegen/index.ts index d72a0bbe4..01fc5f56e 100644 --- a/codegen/index.ts +++ b/codegen/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/jsonschema.ts b/codegen/jsonschema.ts index 31c85b854..c429a1125 100644 --- a/codegen/jsonschema.ts +++ b/codegen/jsonschema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/typebox.ts b/codegen/typebox.ts index 4d18be49c..598921ba0 100644 --- a/codegen/typebox.ts +++ b/codegen/typebox.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/typescript.ts b/codegen/typescript.ts index a0885fdd9..bb1d7a6c8 100644 --- a/codegen/typescript.ts +++ b/codegen/typescript.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/codegen/zod.ts b/codegen/zod.ts index 4f900d64d..bfa9eaecf 100644 --- a/codegen/zod.ts +++ b/codegen/zod.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/array.ts b/example/collections/array.ts index 66a255a5c..8bc93a37b 100644 --- a/example/collections/array.ts +++ b/example/collections/array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/index.ts b/example/collections/index.ts index 004ddbfb5..49183354a 100644 --- a/example/collections/index.ts +++ b/example/collections/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/map.ts b/example/collections/map.ts index 040bd5fa7..debda1af6 100644 --- a/example/collections/map.ts +++ b/example/collections/map.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/set.ts b/example/collections/set.ts index aa83c5d26..6ca6e055a 100644 --- a/example/collections/set.ts +++ b/example/collections/set.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/extensions/index.ts b/example/extensions/index.ts index a8ff621e5..31d34239e 100644 --- a/example/extensions/index.ts +++ b/example/extensions/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/extensions/intersect-allof.ts b/example/extensions/intersect-allof.ts index 7cf413e73..4a3a6016d 100644 --- a/example/extensions/intersect-allof.ts +++ b/example/extensions/intersect-allof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/extensions/union-enum.ts b/example/extensions/union-enum.ts index ac895338e..6f61163f2 100644 --- a/example/extensions/union-enum.ts +++ b/example/extensions/union-enum.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/extensions/union-oneof.ts b/example/extensions/union-oneof.ts index c40a9f9b5..ef749b778 100644 --- a/example/extensions/union-oneof.ts +++ b/example/extensions/union-oneof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/license b/license index cd929d327..08641fd64 100644 --- a/license +++ b/license @@ -2,7 +2,7 @@ TypeBox: JSON Schema Type Builder with Static Type Resolution for TypeScript The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package-lock.json b/package-lock.json index 81dc5039e..63868b5b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.25.10", + "version": "0.25.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.25.10", + "version": "0.25.21", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 22362682c..20872e73a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.20", + "version": "0.25.21", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 016279112..f3150170c 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 2785e30e6..e747dd76a 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/conditional/conditional.ts b/src/conditional/conditional.ts index ecd2a756d..a71a8d4d2 100644 --- a/src/conditional/conditional.ts +++ b/src/conditional/conditional.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/conditional/index.ts b/src/conditional/index.ts index 93afa1daa..c6f97147d 100644 --- a/src/conditional/index.ts +++ b/src/conditional/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts index 323c761ab..ec444955a 100644 --- a/src/conditional/structural.ts +++ b/src/conditional/structural.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/custom/custom.ts b/src/custom/custom.ts index 3dfc8ae8f..f1d2cfcfb 100644 --- a/src/custom/custom.ts +++ b/src/custom/custom.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/custom/index.ts b/src/custom/index.ts index 5c5b0bf66..b96208819 100644 --- a/src/custom/index.ts +++ b/src/custom/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 464d477d0..e90ddfbf3 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/index.ts b/src/errors/index.ts index eae491eeb..71cdce9af 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/format/format.ts b/src/format/format.ts index c185ab00b..9bf384637 100644 --- a/src/format/format.ts +++ b/src/format/format.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/format/index.ts b/src/format/index.ts index f3153aa36..58e298c28 100644 --- a/src/format/index.ts +++ b/src/format/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/guard/guard.ts b/src/guard/guard.ts index 437dc4acd..87d4bc1a8 100644 --- a/src/guard/guard.ts +++ b/src/guard/guard.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/guard/index.ts b/src/guard/index.ts index d39efe4ec..980961dae 100644 --- a/src/guard/index.ts +++ b/src/guard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/hash/hash.ts b/src/hash/hash.ts index 3d0ac21b6..dbe5123a2 100644 --- a/src/hash/hash.ts +++ b/src/hash/hash.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/hash/index.ts b/src/hash/index.ts index d6597c698..c1f89ba06 100644 --- a/src/hash/index.ts +++ b/src/hash/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/index.ts b/src/system/index.ts index 9f65a6522..457186e0b 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/system.ts b/src/system/system.ts index 97c86e2ec..1a637f8aa 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/typebox.ts b/src/typebox.ts index ebacf611f..0949f9b31 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/cast.ts b/src/value/cast.ts index 2535addff..5c205b538 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/check.ts b/src/value/check.ts index 4d4a50558..538db1ccb 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clone.ts b/src/value/clone.ts index d2bb93cdb..82bf94b04 100644 --- a/src/value/clone.ts +++ b/src/value/clone.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/create.ts b/src/value/create.ts index 19469ced5..7766960b6 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/delta.ts b/src/value/delta.ts index 00cd71902..427a1d675 100644 --- a/src/value/delta.ts +++ b/src/value/delta.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/equal.ts b/src/value/equal.ts index 2e85871ab..841cba47a 100644 --- a/src/value/equal.ts +++ b/src/value/equal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/index.ts b/src/value/index.ts index b962dac8c..5e48c738f 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/is.ts b/src/value/is.ts index 3d29fc898..c808f9db6 100644 --- a/src/value/is.ts +++ b/src/value/is.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/pointer.ts b/src/value/pointer.ts index fa828480c..1e7718e89 100644 --- a/src/value/pointer.ts +++ b/src/value/pointer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/value.ts b/src/value/value.ts index aba57e044..373005b7a 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2022 Haydn Paterson (sinclair) +Copyright (c) 2017-2023 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 14b4d4a50bccddeca560f5dd73e32e84c5f6e3d9 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 15 Feb 2023 15:56:00 +0900 Subject: [PATCH 062/369] Support UTF-16 Identifiers (#323) --- changelog.md | 6 ++ package.json | 2 +- readme.md | 102 +++++++++++++++---------------- src/compiler/compiler.ts | 54 +++++++++++----- test/runtime/compiler/index.ts | 1 + test/runtime/compiler/unicode.ts | 84 +++++++++++++++++++++++++ 6 files changed, 183 insertions(+), 66 deletions(-) create mode 100644 test/runtime/compiler/unicode.ts diff --git a/changelog.md b/changelog.md index 57c8a06bf..5034c30f7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,9 @@ +## [0.25.22](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.22) + +Updates: + +- [323](https://github.com/sinclairzx81/typebox/pull/323) adds compiler support for UTF-16 (unicode) characters for schema identifiers. + ## [0.25.18](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.18) Updates: diff --git a/package.json b/package.json index 20872e73a..9f54111ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.21", + "version": "0.25.22", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 3a576e9f7..4276f179c 100644 --- a/readme.md +++ b/readme.md @@ -1233,29 +1233,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 437 ms' │ ' 15 ms' │ ' 29.13 x' │ -│ String │ 2000 │ ' 332 ms' │ ' 13 ms' │ ' 25.54 x' │ -│ Boolean │ 2000 │ ' 293 ms' │ ' 12 ms' │ ' 24.42 x' │ -│ Null │ 2000 │ ' 259 ms' │ ' 9 ms' │ ' 28.78 x' │ -│ RegEx │ 2000 │ ' 505 ms' │ ' 21 ms' │ ' 24.05 x' │ -│ ObjectA │ 2000 │ ' 2726 ms' │ ' 53 ms' │ ' 51.43 x' │ -│ ObjectB │ 2000 │ ' 2835 ms' │ ' 37 ms' │ ' 76.62 x' │ -│ Tuple │ 2000 │ ' 1239 ms' │ ' 24 ms' │ ' 51.63 x' │ -│ Union │ 2000 │ ' 1244 ms' │ ' 34 ms' │ ' 36.59 x' │ -│ Vector4 │ 2000 │ ' 1521 ms' │ ' 21 ms' │ ' 72.43 x' │ -│ Matrix4 │ 2000 │ ' 836 ms' │ ' 10 ms' │ ' 83.60 x' │ -│ Literal_String │ 2000 │ ' 333 ms' │ ' 9 ms' │ ' 37.00 x' │ -│ Literal_Number │ 2000 │ ' 357 ms' │ ' 9 ms' │ ' 39.67 x' │ -│ Literal_Boolean │ 2000 │ ' 352 ms' │ ' 6 ms' │ ' 58.67 x' │ -│ Array_Number │ 2000 │ ' 685 ms' │ ' 12 ms' │ ' 57.08 x' │ -│ Array_String │ 2000 │ ' 720 ms' │ ' 12 ms' │ ' 60.00 x' │ -│ Array_Boolean │ 2000 │ ' 709 ms' │ ' 8 ms' │ ' 88.63 x' │ -│ Array_ObjectA │ 2000 │ ' 3590 ms' │ ' 40 ms' │ ' 89.75 x' │ -│ Array_ObjectB │ 2000 │ ' 3610 ms' │ ' 51 ms' │ ' 70.78 x' │ -│ Array_Tuple │ 2000 │ ' 2124 ms' │ ' 19 ms' │ ' 111.79 x' │ -│ Array_Union │ 2000 │ ' 1533 ms' │ ' 21 ms' │ ' 73.00 x' │ -│ Array_Vector4 │ 2000 │ ' 2278 ms' │ ' 19 ms' │ ' 119.89 x' │ -│ Array_Matrix4 │ 2000 │ ' 1504 ms' │ ' 20 ms' │ ' 75.20 x' │ +│ Number │ 2000 │ ' 418 ms' │ ' 14 ms' │ ' 29.86 x' │ +│ String │ 2000 │ ' 331 ms' │ ' 12 ms' │ ' 27.58 x' │ +│ Boolean │ 2000 │ ' 290 ms' │ ' 13 ms' │ ' 22.31 x' │ +│ Null │ 2000 │ ' 253 ms' │ ' 8 ms' │ ' 31.63 x' │ +│ RegEx │ 2000 │ ' 481 ms' │ ' 18 ms' │ ' 26.72 x' │ +│ ObjectA │ 2000 │ ' 2675 ms' │ ' 54 ms' │ ' 49.54 x' │ +│ ObjectB │ 2000 │ ' 2849 ms' │ ' 39 ms' │ ' 73.05 x' │ +│ Tuple │ 2000 │ ' 1224 ms' │ ' 22 ms' │ ' 55.64 x' │ +│ Union │ 2000 │ ' 1225 ms' │ ' 26 ms' │ ' 47.12 x' │ +│ Vector4 │ 2000 │ ' 1777 ms' │ ' 24 ms' │ ' 74.04 x' │ +│ Matrix4 │ 2000 │ ' 825 ms' │ ' 12 ms' │ ' 68.75 x' │ +│ Literal_String │ 2000 │ ' 345 ms' │ ' 9 ms' │ ' 38.33 x' │ +│ Literal_Number │ 2000 │ ' 363 ms' │ ' 7 ms' │ ' 51.86 x' │ +│ Literal_Boolean │ 2000 │ ' 358 ms' │ ' 6 ms' │ ' 59.67 x' │ +│ Array_Number │ 2000 │ ' 687 ms' │ ' 8 ms' │ ' 85.88 x' │ +│ Array_String │ 2000 │ ' 726 ms' │ ' 8 ms' │ ' 90.75 x' │ +│ Array_Boolean │ 2000 │ ' 703 ms' │ ' 8 ms' │ ' 87.88 x' │ +│ Array_ObjectA │ 2000 │ ' 3686 ms' │ ' 40 ms' │ ' 92.15 x' │ +│ Array_ObjectB │ 2000 │ ' 3821 ms' │ ' 40 ms' │ ' 95.53 x' │ +│ Array_Tuple │ 2000 │ ' 2070 ms' │ ' 17 ms' │ ' 121.76 x' │ +│ Array_Union │ 2000 │ ' 1503 ms' │ ' 21 ms' │ ' 71.57 x' │ +│ Array_Vector4 │ 2000 │ ' 2185 ms' │ ' 21 ms' │ ' 104.05 x' │ +│ Array_Matrix4 │ 2000 │ ' 1502 ms' │ ' 16 ms' │ ' 93.88 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1269,31 +1269,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 29 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ -│ String │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ -│ Boolean │ 1000000 │ ' 22 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Null │ 1000000 │ ' 27 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ -│ RegEx │ 1000000 │ ' 160 ms' │ ' 52 ms' │ ' 40 ms' │ ' 1.30 x' │ -│ ObjectA │ 1000000 │ ' 577 ms' │ ' 37 ms' │ ' 26 ms' │ ' 1.42 x' │ -│ ObjectB │ 1000000 │ ' 990 ms' │ ' 50 ms' │ ' 37 ms' │ ' 1.35 x' │ -│ Tuple │ 1000000 │ ' 124 ms' │ ' 22 ms' │ ' 17 ms' │ ' 1.29 x' │ -│ Union │ 1000000 │ ' 318 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ -│ Recursive │ 1000000 │ ' 3102 ms' │ ' 408 ms' │ ' 102 ms' │ ' 4.00 x' │ -│ Vector4 │ 1000000 │ ' 150 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Matrix4 │ 1000000 │ ' 579 ms' │ ' 43 ms' │ ' 27 ms' │ ' 1.59 x' │ -│ Literal_String │ 1000000 │ ' 50 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ -│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Array_Number │ 1000000 │ ' 429 ms' │ ' 32 ms' │ ' 20 ms' │ ' 1.60 x' │ -│ Array_String │ 1000000 │ ' 474 ms' │ ' 33 ms' │ ' 25 ms' │ ' 1.32 x' │ -│ Array_Boolean │ 1000000 │ ' 447 ms' │ ' 35 ms' │ ' 31 ms' │ ' 1.13 x' │ -│ Array_ObjectA │ 1000000 │ ' 14098 ms' │ ' 2352 ms' │ ' 2015 ms' │ ' 1.17 x' │ -│ Array_ObjectB │ 1000000 │ ' 16216 ms' │ ' 2628 ms' │ ' 2511 ms' │ ' 1.05 x' │ -│ Array_Tuple │ 1000000 │ ' 1749 ms' │ ' 95 ms' │ ' 75 ms' │ ' 1.27 x' │ -│ Array_Union │ 1000000 │ ' 4712 ms' │ ' 233 ms' │ ' 84 ms' │ ' 2.77 x' │ -│ Array_Recursive │ 1000000 │ ' 56118 ms' │ ' 7192 ms' │ ' 1180 ms' │ ' 6.09 x' │ -│ Array_Vector4 │ 1000000 │ ' 2284 ms' │ ' 99 ms' │ ' 50 ms' │ ' 1.98 x' │ -│ Array_Matrix4 │ 1000000 │ ' 12640 ms' │ ' 412 ms' │ ' 272 ms' │ ' 1.51 x' │ +│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ +│ String │ 1000000 │ ' 25 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ +│ Boolean │ 1000000 │ ' 24 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Null │ 1000000 │ ' 25 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ +│ RegEx │ 1000000 │ ' 164 ms' │ ' 53 ms' │ ' 37 ms' │ ' 1.43 x' │ +│ ObjectA │ 1000000 │ ' 593 ms' │ ' 47 ms' │ ' 25 ms' │ ' 1.88 x' │ +│ ObjectB │ 1000000 │ ' 1053 ms' │ ' 54 ms' │ ' 40 ms' │ ' 1.35 x' │ +│ Tuple │ 1000000 │ ' 129 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ +│ Union │ 1000000 │ ' 334 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ +│ Recursive │ 1000000 │ ' 3127 ms' │ ' 424 ms' │ ' 98 ms' │ ' 4.33 x' │ +│ Vector4 │ 1000000 │ ' 152 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 593 ms' │ ' 41 ms' │ ' 27 ms' │ ' 1.52 x' │ +│ Literal_String │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ +│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Array_Number │ 1000000 │ ' 495 ms' │ ' 32 ms' │ ' 21 ms' │ ' 1.52 x' │ +│ Array_String │ 1000000 │ ' 481 ms' │ ' 31 ms' │ ' 21 ms' │ ' 1.48 x' │ +│ Array_Boolean │ 1000000 │ ' 446 ms' │ ' 32 ms' │ ' 27 ms' │ ' 1.19 x' │ +│ Array_ObjectA │ 1000000 │ ' 14314 ms' │ ' 2341 ms' │ ' 1969 ms' │ ' 1.19 x' │ +│ Array_ObjectB │ 1000000 │ ' 16883 ms' │ ' 2661 ms' │ ' 2606 ms' │ ' 1.02 x' │ +│ Array_Tuple │ 1000000 │ ' 1834 ms' │ ' 98 ms' │ ' 77 ms' │ ' 1.27 x' │ +│ Array_Union │ 1000000 │ ' 4960 ms' │ ' 240 ms' │ ' 87 ms' │ ' 2.76 x' │ +│ Array_Recursive │ 1000000 │ ' 56273 ms' │ ' 7118 ms' │ ' 1122 ms' │ ' 6.34 x' │ +│ Array_Vector4 │ 1000000 │ ' 2498 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ +│ Array_Matrix4 │ 1000000 │ ' 12487 ms' │ ' 383 ms' │ ' 246 ms' │ ' 1.56 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1307,13 +1307,13 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 61 kb' │ ' 30 kb' │ '2.03 x' │ +│ typebox/compiler │ ' 64 kb' │ ' 31 kb' │ '2.02 x' │ │ typebox/conditional │ ' 45 kb' │ ' 18 kb' │ '2.44 x' │ │ typebox/custom │ ' 0 kb' │ ' 0 kb' │ '2.61 x' │ │ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ │ typebox/guard │ ' 23 kb' │ ' 11 kb' │ '2.07 x' │ -│ typebox/hash │ ' 4 kb' │ ' 1 kb' │ '2.27 x' │ -│ typebox/value │ ' 85 kb' │ ' 39 kb' │ '2.16 x' │ +│ typebox/hash │ ' 4 kb' │ ' 1 kb' │ '2.30 x' │ +│ typebox/value │ ' 89 kb' │ ' 41 kb' │ '2.15 x' │ │ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index f3150170c..032e7a0bb 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -62,41 +62,65 @@ export class TypeCheck { return this.checkFunc(value) } } - // ------------------------------------------------------------------- -// Property +// Character // ------------------------------------------------------------------- - -export namespace Property { - function DollarSign(code: number) { +namespace Character { + export function DollarSign(code: number) { return code === 36 } - function Underscore(code: number) { + export function Underscore(code: number) { return code === 95 } - function Numeric(code: number) { + export function Numeric(code: number) { return code >= 48 && code <= 57 } - function Alpha(code: number) { + export function Alpha(code: number) { return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) } +} - export function Check(propertyName: string) { +// ------------------------------------------------------------------- +// Identifier +// ------------------------------------------------------------------- +namespace Identifier { + export function Encode($id: string) { + const buffer: string[] = [] + for (let i = 0; i < $id.length; i++) { + const code = $id.charCodeAt(i) + if (Character.Numeric(code) || Character.Alpha(code)) { + buffer.push($id.charAt(i)) + } else { + buffer.push(`_${code}_`) + } + } + return buffer.join('').replace(/__/g, '_') + } +} + +// ------------------------------------------------------------------- +// MemberExpression +// ------------------------------------------------------------------- +export namespace MemberExpression { + function Check(propertyName: string) { if (propertyName.length === 0) return false { const code = propertyName.charCodeAt(0) - if (!(DollarSign(code) || Underscore(code) || Alpha(code))) { + if (!(Character.DollarSign(code) || Character.Underscore(code) || Character.Alpha(code))) { return false } } for (let i = 1; i < propertyName.length; i++) { const code = propertyName.charCodeAt(i) - if (!(DollarSign(code) || Underscore(code) || Alpha(code) || Numeric(code))) { + if (!(Character.DollarSign(code) || Character.Underscore(code) || Character.Alpha(code) || Character.Numeric(code))) { return false } } return true } + export function Encode(object: string, key: string) { + return !Check(key) ? `${object}['${key}']` : `${object}.${key}` + } } // ------------------------------------------------------------------- @@ -111,6 +135,9 @@ export class TypeCompilerUnknownTypeError extends Error { /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { + // ------------------------------------------------------------------- + // Guards + // ------------------------------------------------------------------- function IsNumber(value: unknown): value is number { return typeof value === 'number' && !globalThis.isNaN(value) } @@ -118,7 +145,6 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- // Types // ------------------------------------------------------------------- - function* Any(schema: Types.TAny, value: string): IterableIterator { yield '(true)' } @@ -216,7 +242,7 @@ export namespace TypeCompiler { yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } for (const propertyKey of propertyKeys) { - const memberExpression = Property.Check(propertyKey) ? `${value}.${propertyKey}` : `${value}['${propertyKey}']` + const memberExpression = MemberExpression.Encode(value, propertyKey) const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { yield* Visit(propertySchema, memberExpression) @@ -406,7 +432,7 @@ export namespace TypeCompiler { } function CreateFunctionName($id: string) { - return `check_${$id.replace(/-/g, '_')}` + return `check_${Identifier.Encode($id)}` } function CreateFunction(name: string, schema: Types.TSchema, value: string): string { diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index d06324993..a842d535a 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -3,6 +3,7 @@ import './array' import './boolean' import './custom' import './date' +import './unicode' import './enum' import './integer' import './intersect' diff --git a/test/runtime/compiler/unicode.ts b/test/runtime/compiler/unicode.ts new file mode 100644 index 000000000..2e65bd6cf --- /dev/null +++ b/test/runtime/compiler/unicode.ts @@ -0,0 +1,84 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('type/compiler/Unicode', () => { + // --------------------------------------------------------- + // Identifiers + // --------------------------------------------------------- + it('Should support unicode identifiers', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + $id: '식별자', + }, + ) + Ok(T, { + x: 1, + y: 2, + }) + }) + it('Should support unicode identifier references', () => { + const R = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + $id: '식별자', + }, + ) + const T = Type.Object({ + vector: Type.Ref(R), + }) + Ok( + T, + { + vector: { + x: 1, + y: 2, + }, + }, + [R], + ) + }) + it('Should support unicode identifier recursion', () => { + const Node = Type.Recursive( + (Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + { + $id: '식별자', + }, + ) + Ok(Node, { + id: 'A', + nodes: [ + { + id: 'B', + nodes: [ + { + id: 'C', + nodes: [], + }, + ], + }, + ], + }) + }) + // --------------------------------------------------------- + // Properties + // --------------------------------------------------------- + it('Should support unicode properties', () => { + const T = Type.Object({ + 이름: Type.String(), + }) + Ok(T, { + 이름: 'dave', + }) + }) +}) From 13682f66c348531bba9e98ddb312ac952676b659 Mon Sep 17 00:00:00 2001 From: Steve Zhu <4130171+stevezhu@users.noreply.github.com> Date: Thu, 16 Feb 2023 05:15:35 -0500 Subject: [PATCH 063/369] Support JSDoc IntelliSense on Object Properties (#324) --- src/typebox.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index 0949f9b31..a425d3696 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -316,10 +316,10 @@ export type RequiredPropertyKeys = keyof Omit = - { readonly [K in ReadonlyOptionalPropertyKeys]?: Static } & - { readonly [K in ReadonlyPropertyKeys]: Static } & - { [K in OptionalPropertyKeys]?: Static } & - { [K in RequiredPropertyKeys]: Static } extends infer R ? { [K in keyof R]: R[K] } : never + Readonly }, ReadonlyOptionalPropertyKeys>>> & + Readonly }, ReadonlyPropertyKeys>> & + Partial }, OptionalPropertyKeys>> & + Required }, RequiredPropertyKeys>> extends infer R ? { [K in keyof R]: R[K] } : never export type TRecordProperties, T extends TSchema> = Static extends string ? { [X in Static]: T } : never From 5515fb681f279322f8f628b411634f2566c5fac5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 16 Feb 2023 19:42:14 +0900 Subject: [PATCH 064/369] Property Inference Optimization (#325) --- changelog.md | 12 ++++++++++++ example/index.ts | 1 + package.json | 2 +- src/typebox.ts | 16 +++++++++++----- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 5034c30f7..6a6e9ac45 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,15 @@ +## [0.25.23](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) + +Updates: + +- [324](https://github.com/sinclairzx81/typebox/pull/324) TypeScript Language Service now presents JSDoc comments when inferring static object properties. (IntelliSense) +- [325](https://github.com/sinclairzx81/typebox/pull/325) Additional property inference optimizations. + +Additional: + +- Huge thank you to Github user [stevezhu](https://github.com/stevezhu) for these excellent contributions. + + ## [0.25.22](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.22) Updates: diff --git a/example/index.ts b/example/index.ts index 588558a9d..75d4d616a 100644 --- a/example/index.ts +++ b/example/index.ts @@ -9,6 +9,7 @@ import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Kind, Static, TSchema } from '@sinclair/typebox' const T = Type.Object({ + /** It's a X */ x: Type.Number(), y: Type.Number(), z: Type.Number(), diff --git a/package.json b/package.json index 9f54111ca..b9a59fe67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.22", + "version": "0.25.23", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index a425d3696..e5fb7ff5d 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -315,11 +315,17 @@ export type OptionalPropertyKeys = { [K in keyof T]: T[K] export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> // prettier-ignore -export type PropertiesReduce = - Readonly }, ReadonlyOptionalPropertyKeys>>> & - Readonly }, ReadonlyPropertyKeys>> & - Partial }, OptionalPropertyKeys>> & - Required }, RequiredPropertyKeys>> extends infer R ? { [K in keyof R]: R[K] } : never +export type PropertiesReducer> = ( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +) extends infer O ? { [K in keyof O]: O[K] } : never + +// prettier-ignore +export type PropertiesReduce = PropertiesReducer +}> export type TRecordProperties, T extends TSchema> = Static extends string ? { [X in Static]: T } : never From ac0a3e296315c25952aded837d4241f316e62eb8 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 16 Feb 2023 19:52:28 +0900 Subject: [PATCH 065/369] Update Example --- example/index.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/example/index.ts b/example/index.ts index 75d4d616a..ed8d1cdc2 100644 --- a/example/index.ts +++ b/example/index.ts @@ -8,8 +8,11 @@ import { Custom } from '@sinclair/typebox/custom' import { Value, ValuePointer } from '@sinclair/typebox/value' import { Type, Kind, Static, TSchema } from '@sinclair/typebox' +// ----------------------------------------------------------- +// Create: Type +// ----------------------------------------------------------- + const T = Type.Object({ - /** It's a X */ x: Type.Number(), y: Type.Number(), z: Type.Number(), @@ -18,3 +21,25 @@ const T = Type.Object({ type T = Static console.log(T) + +// ----------------------------------------------------------- +// Create: Value +// ----------------------------------------------------------- + +const V = Value.Create(T) + +console.log(V) + +// ----------------------------------------------------------- +// Compile: Type +// ----------------------------------------------------------- + +const C = TypeCompiler.Compile(T) + +console.log(C.Code()) + +// ----------------------------------------------------------- +// Check: Value +// ----------------------------------------------------------- + +console.log(C.Check(V)) From fb5fabfa95a9f40e06ff47be1b98f4ee0a333769 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 17 Feb 2023 00:10:11 +0900 Subject: [PATCH 066/369] ReadonlyObject Extension --- example/extensions/index.ts | 1 + example/extensions/readonly-object.ts | 56 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 example/extensions/readonly-object.ts diff --git a/example/extensions/index.ts b/example/extensions/index.ts index 31d34239e..c8ec2834b 100644 --- a/example/extensions/index.ts +++ b/example/extensions/index.ts @@ -27,5 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './intersect-allof' +export * from './readonly-object' export * from './union-oneof' export * from './union-enum' diff --git a/example/extensions/readonly-object.ts b/example/extensions/readonly-object.ts new file mode 100644 index 000000000..cf92fd2cc --- /dev/null +++ b/example/extensions/readonly-object.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Type, TSchema, TObject, TProperties, Modifier, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' + +// prettier-ignore +export type TReadonlyObject = TObject<{ + [K in keyof T['properties']]: + T['properties'][K] extends TReadonlyOptional ? TReadonlyOptional : + T['properties'][K] extends TReadonly ? TReadonly : + T['properties'][K] extends TOptional ? TReadonlyOptional : + TReadonly +}> + +/** Remaps all properties of an object to be readonly */ +export function ReadonlyObject(schema: T): TReadonlyObject { + return Type.Object( + Object.keys(schema.properties).reduce((acc, key) => { + const property = schema.properties[key] as TSchema + const modifier = property[Modifier] + // prettier-ignore + const mapped = ( + (modifier === 'ReadonlyOptional') ? { ...property, [Modifier]: 'ReadonlyOptional' } : + (modifier === 'Readonly') ? { ...property, [Modifier]: 'Readonly' } : + (modifier === 'Optional') ? { ...property, [Modifier]: 'ReadonlyOptional' } : + ({...property, [Modifier]: 'Readonly' }) + ) + return { ...acc, [key]: mapped } + }, {} as TProperties), + ) as TReadonlyObject +} From 1159041ec068d7e7d4cf5543c528f28ec00f689e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 18 Feb 2023 01:54:57 +0900 Subject: [PATCH 067/369] TRPC Documentation (#326) --- example/trpc/readme.md | 97 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 example/trpc/readme.md diff --git a/example/trpc/readme.md b/example/trpc/readme.md new file mode 100644 index 000000000..c63f4f0b9 --- /dev/null +++ b/example/trpc/readme.md @@ -0,0 +1,97 @@ +# TypeBox TRPC Integration + +To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assert function. Once wrapped, TypeBox can offer typical auto type inference for procedures, enhanced runtime checking performance and enable publishable procedure schematics based on the JSON Schema specification. + +## Contents + +- [Example](#Example) +- [TypeCompiler](#TypeComplier) +- [Value](#Value) +- [Ajv](#Ajv) + +## Example + +The following example shows a minimal TypeBox TRPC setup with the `IoType` wrapper function described in the sections below. + +```typescript +import { Type } from '@sinclair/typebox' +import { initTRPC } from '@trpc/server' +import { IoType } from './io-type' + +const addInput = IoType(Type.Object({ + a: Type.Number(), + b: Type.Number() +})) + +const addOutput = IoType(Type.Number()) + +const t = initTRPC.create() + +export const appRouter = t.router({ + add: t.procedure + .input(addInput) + .output(addOutput) + .query(({ input }) => input.a + input.b) // type-safe +}) +``` + + + +## With [TypeCompiler](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAKgMoDGAFvFTJQAX1CQmOVKADk4KtES9YyaE2DEyAIxwAPWXQbNWHTqXgBhaSTjwmYiVJnzFy1es0Xde4Lxt2TIb0jCzsXABKAAqWAKJMUg7iktJy4ARMJLzAVPYAbvaGNPB6oaz+iFSsAJI45mSgALygADycoCUESAAmVFx8gsIAfAAUVAJCyFicADSgTPCQ9ki88FTTA5MA2gC6TaC7AJRNQ+w0oKAVVZeCvADW+-VWAQhM5NYMdmMTwnMLSwtlGtDud5vACABXJiIUAjPLIWAQzCgCGIO6IHAAd0Qx0apwm93etzucIRSOOAH5QPDEfBQFgRiNcac2KCLldTKASMgCPw5ugqFRkABzOniZpbchSgl3cjxRJUUm0w47LYABh2bNAvKkmNAiHges40TiCWYIw4AqFoqwAAMACRsK0i+DJZigR3c3miW1zfzdZGyABCAEEACIAfQisQAigBVWLcTiyMQgi6iQ5MmiiGg0EAQGAIFDoLBSCFdN7UIxlcJKaAEY0xRwpFzpTLZXJMApBasmcJPZvONJuFRqDRaeA+YLGMIcWoD5JD2TkYC4QgWIoc0DIbrdaqIEjl-bziwjJ7kADy2gAVvBeAQLaCplwLOQAHIQ1DaexMmag7TTK+H5fj+IIZiCNBbju3QXuWh6sM0J5kGeQGft+TBMhBW4IdgiD1o2ljkLwCw8vAWbFKUfZQSQJARDg5b2PsBDkGWFaPhc0FYMxJBSKs3RQvAWrkEo8EjNB+7wWmFygOQ9EEKJ0GwfJ5ZSRc5AAI5IkwRCMhwIlHhmJy4fB5AiAA1MZ5bkNoxz5hOeBCks2aHEAA) + +The following uses the TypeCompiler to perform JIT optimized type checking for TRPC. + +```typescript +import { TSchema } from '@sinclair/typebox' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { TRPCError } from '@trpc/server' + +export const IoType = (schema: T, references: TSchema[] = []) => { + const check = TypeCompiler.Compile(schema, references) + return (value: unknown) => check.Check(value) ? value : (() => { + const { path, message } = [...check.Errors(value)][0] + throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) + })() +} +``` + + + +## With [Value](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAasrAK7ygAvqEhMcqUAHJwVaIgDGsZNCbBiZAEY4AHsABuPfpLoNmrDgBUAyvIAW8VMiEixE6bIVKVa0vC3bjekYWdlALACUABQBhAFEmMSZnUXEpcAImEnlgKngmPTzjGnhtYNZ5HEQqVgBJHAtfUABeUAAeC1ASgiQAEyowm3tHAD4ACio7B2QsCwAaUCZ4SDykeXgqGcGpgG0AXWbQPYBKZuHQGlBQUYM+TFBeRABrRBwAd0QTprPuW-Jo+3kj3Gk0c8xu-BOAH5QOCBFhRqNPmc2BdLqAKlVzKASMgCLZ5ugqFRkABzATCFrbcjUn78cjxRJUYFDZDzRbLRYKdZgwzwI67bYABl2qMueLEr1AiHgkoiMQZzFGHEJxLJWAABgASNgq0nwYSQZigbU4vGCdXzCo9O6SABCAEEACIAfXCsQAigBVWJWCySIRHVGCI6Img0EAQGAIFDoLBiXjdJiUWhBMyhOTQAhy6LJVxpDJZHJ5ApMQKmEKWRoGvPuOSKZSqdR+HRlsqhOoNMi51KScjAXCEXxFDHVUDIHo9GqIEgJg4d3yjTsUADyGgAVvB5AQlajpmFfOQAHK8VAaPKI2aojQzA-H0-nwPBwM0Eescc9ZcJmesFrzsiL28TzPJhEWfV9QB-bBEEzbNyHkRZcXgUNilKNNwOQEgSHCHAEzyA4CHIeNEx3S53ywAiSDENYel4RZRVAcg5G-UZ3ynb9AzRBicIIZj30-HiEw4tFyAAR34JgiARDgmNnYNTig79yCcABqBSE3IDQTgjJs8GJZYaGDIA) + +The following performs dynamic type checking without code evaluation. + +```typescript +import { Value } from '@sinclair/typebox/value' +import { TSchema } from '@sinclair/typebox' +import { TRPCError } from '@trpc/server' + +export const IoType = (schema: T, references: TSchema[] = []) => + (value: unknown) => Value.Check(schema, value) ? value : (() => { + const { path, message } = [...Value.Errors(schema, references, value)][0] + throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) + })() +``` + + + +## With [Ajv](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAKgMoDGAFvFTIANKG4FkBaL1ABfUJCY5UoAOTgq0RL1jJoTYMTIAjHAA81dBs1YdOAJQAKAYQCiTZU3mLlqjQRMJLzAVPBMAG7hVvSMLKAAggBWEb4q6sgpwAAm0FQEwABMAAwlVjTw5nGsvDiI+aAAkjicpPCgALygADycoJUESNlUXHyCwgB8ABRUAkLIWJxiTPCQ4Ui88FSLY-MA2gC6naCHAJSdE+w0oKC19ayZqV2I8ADuiSlTp9egK2srOi25EgzDcyAEUz+60BF1Aj3IyGy2V2wkhq2hm1O3xudwac14AGtjvDagw4PAZnNhNjfvACABXJiIUBTCLIWD0zCgemIAmIHCvRCnLASKQyXqXDqXfEE1nsznnAD8oDZHPaWCmX1hbB+OLqDQ46CoVGQAHN4GJtPlkICnFJ+D4ujLyOEvFQAIR7YoHXWgAj8ZTvF7vRyuDxeKaGrYm81YUAAAwAJGwjTH4AoQd5k1bJLb7XJ42JatkuWoAEIJAAiAH0HG4AIoAVTc3E4ankNLkpy+NDkNBoIAgMAQKHQWGU9MGTEotFitnY2EQ0AIoZcPiU6QCQRCYUi0Ws1QXrTI6786k02l0+kMxngZksB-nHGax-aGbPanIwFwhDa5VxDxIo0iAkJOxwvm0UyvuQADyJhJPAvAEJGPwLFwbTkAAcvSqAmOEXwiD8JiLBh2G4fh3xdt8NAAXCSIwZOoGsF0EFkFBpE4XhTBfNRtHMYuy6ruQvArFIFLUZUh60cgJAkA4OCTuExwEOQE5TihNyItkWAqSQyibNkjLwL65DaExUxacBTE0jc5AKQQ5laQxDmTjZoDkAAjpyTBEJqHBmWBXawgFKnIKAADUi5MeQJjnIOt54Caay9qcQA) + + +The following uses Ajv to perform more generalized JSON Schema checks across the complete JSON Schema specification. + +```typescript +import { TSchema, Static } from '@sinclair/typebox' +import { TRPCError } from '@trpc/server' +import Ajv from 'ajv/dist/2020' + +export const IoType = (schema: T, references: TSchema[] = []) => { + const ajv = new Ajv() + references.forEach(reference => ajv.addSchema(reference)) + const check = ajv.compile(schema) + return (value: unknown): Static => check(value) ? value : (() => { + const { message, instancePath } = check.errors![0] + throw new TRPCError({ message: `${message} for ${instancePath}`, code: 'BAD_REQUEST' }) + })() +} +``` \ No newline at end of file From 5a520a19427cd6a5e734076e1330a7a405bca1a7 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 19 Feb 2023 04:22:34 +0900 Subject: [PATCH 068/369] Documentation --- example/trpc/readme.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/example/trpc/readme.md b/example/trpc/readme.md index c63f4f0b9..fe753c01c 100644 --- a/example/trpc/readme.md +++ b/example/trpc/readme.md @@ -1,14 +1,16 @@ -# TypeBox TRPC Integration +# Using TypeBox with TRPC To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assert function. Once wrapped, TypeBox can offer typical auto type inference for procedures, enhanced runtime checking performance and enable publishable procedure schematics based on the JSON Schema specification. ## Contents - [Example](#Example) -- [TypeCompiler](#TypeComplier) +- [TypeCompiler](#TypeCompiler) - [Value](#Value) - [Ajv](#Ajv) + + ## Example The following example shows a minimal TypeBox TRPC setup with the `IoType` wrapper function described in the sections below. @@ -94,4 +96,4 @@ export const IoType = (schema: T, references: TSchema[] = []) throw new TRPCError({ message: `${message} for ${instancePath}`, code: 'BAD_REQUEST' }) })() } -``` \ No newline at end of file +``` From 078cfacd44336aa01528c3241658516d7f2ec46e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 23 Feb 2023 20:34:10 +0900 Subject: [PATCH 069/369] Support Required Undefined Property Check (#331) --- benchmark/compression/index.ts | 4 +- .../compression/module/typebox-system.ts | 3 + changelog.md | 11 + example/trpc/readme.md | 66 ++-- example/typedef/index.ts | 29 ++ example/typedef/readme.md | 100 ++++++ example/typedef/typedef.ts | 88 +++++ package.json | 2 +- readme.md | 306 +++++++----------- src/compiler/compiler.ts | 5 +- src/errors/errors.ts | 4 + src/guard/extends.ts | 46 +++ src/guard/index.ts | 1 + src/value/cast.ts | 5 - src/value/check.ts | 12 + src/value/create.ts | 10 - test/runtime/compiler/object.ts | 22 ++ test/runtime/value/check/object.ts | 22 ++ 18 files changed, 493 insertions(+), 243 deletions(-) create mode 100644 benchmark/compression/module/typebox-system.ts create mode 100644 example/typedef/index.ts create mode 100644 example/typedef/readme.md create mode 100644 example/typedef/typedef.ts create mode 100644 src/guard/extends.ts diff --git a/benchmark/compression/index.ts b/benchmark/compression/index.ts index 9454d15ce..fc49651ab 100644 --- a/benchmark/compression/index.ts +++ b/benchmark/compression/index.ts @@ -9,8 +9,8 @@ export async function measure(test: string) { const minified = statSync(`target/benchmark/compression/${test}.js`) return { test: test.padEnd(20), - compiled: `${Math.floor(compiled.size / 1000)} kb`.padStart(8), - minified: `${Math.floor(minified.size / 1000)} kb`.padStart(8), + compiled: `${(compiled.size / 1000).toFixed(1)} kb`.padStart(8), + minified: `${(minified.size / 1000).toFixed(1)} kb`.padStart(8), ratio: compiled.size / minified.size, } } diff --git a/benchmark/compression/module/typebox-system.ts b/benchmark/compression/module/typebox-system.ts new file mode 100644 index 000000000..929092b50 --- /dev/null +++ b/benchmark/compression/module/typebox-system.ts @@ -0,0 +1,3 @@ +import { TypeSystem } from '@sinclair/typebox/system' + +TypeSystem.AllowNaN = true diff --git a/changelog.md b/changelog.md index 6a6e9ac45..fa1f19caf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,14 @@ +## [0.25.24](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) + +Updates: + +- [331](https://github.com/sinclairzx81/typebox/pull/331) Implements an additional check specific to property types of `required & undefined`. This to ensure the property key exists when the property value extends `undefined`. +- [331](https://github.com/sinclairzx81/typebox/pull/331) Documentation updates for Ajv and TypeCompiler + +Additional: + +- [331](https://github.com/sinclairzx81/typebox/pull/331) Remove unusued recursive code paths for create and cast. + ## [0.25.23](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) Updates: diff --git a/example/trpc/readme.md b/example/trpc/readme.md index fe753c01c..f3b26ccbc 100644 --- a/example/trpc/readme.md +++ b/example/trpc/readme.md @@ -1,31 +1,34 @@ # Using TypeBox with TRPC -To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assert function. Once wrapped, TypeBox can offer typical auto type inference for procedures, enhanced runtime checking performance and enable publishable procedure schematics based on the JSON Schema specification. +To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assertion function. Once wrapped, TypeBox can provide TRPC auto type inference for procedures, enhanced runtime checking performance and well as providing options for enabling publishable procedure schematics based on the JSON Schema specification. ## Contents - [Example](#Example) -- [TypeCompiler](#TypeCompiler) -- [Value](#Value) -- [Ajv](#Ajv) +- [RpcType](#RpcType) + - [TypeCompiler](#TypeCompiler) + - [Value](#Value) + - [Ajv](#Ajv) ## Example -The following example shows a minimal TypeBox TRPC setup with the `IoType` wrapper function described in the sections below. +The following shows a minimal TypeBox + TRPC setup with the `RpcType` wrapper function described in the sections below. ```typescript import { Type } from '@sinclair/typebox' import { initTRPC } from '@trpc/server' -import { IoType } from './io-type' +import { RpcType } from './rpctype' -const addInput = IoType(Type.Object({ - a: Type.Number(), - b: Type.Number() -})) +const addInput = RpcType( + Type.Object({ + a: Type.Number(), + b: Type.Number(), + }), +) -const addOutput = IoType(Type.Number()) +const addOutput = RpcType(Type.Number()) const t = initTRPC.create() @@ -33,13 +36,19 @@ export const appRouter = t.router({ add: t.procedure .input(addInput) .output(addOutput) - .query(({ input }) => input.a + input.b) // type-safe + .query(({ input }) => input.a + input.b), // type-safe }) ``` + + +## RpcType + +Unlike Zod, Yup, Superstruct, TypeBox types are pure JSON Schema and do not implement built a in `parse` or `validate` function. Because of this, you will need to wrap the Type in a TRPC compatible function which implements `parse` or `validate` on behalf of TRPC. As JSON Schema is a formal specification, there are a number of ways you can choose to implement this function, the following shows recommended implementations. + -## With [TypeCompiler](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAKgMoDGAFvFTJQAX1CQmOVKADk4KtES9YyaE2DEyAIxwAPWXQbNWHTqXgBhaSTjwmYiVJnzFy1es0Xde4Lxt2TIb0jCzsXABKAAqWAKJMUg7iktJy4ARMJLzAVPYAbvaGNPB6oaz+iFSsAJI45mSgALygADycoCUESAAmVFx8gsIAfAAUVAJCyFicADSgTPCQ9ki88FTTA5MA2gC6TaC7AJRNQ+w0oKAVVZeCvADW+-VWAQhM5NYMdmMTwnMLSwtlGtDud5vACABXJiIUAjPLIWAQzCgCGIO6IHAAd0Qx0apwm93etzucIRSOOAH5QPDEfBQFgRiNcac2KCLldTKASMgCPw5ugqFRkABzOniZpbchSgl3cjxRJUUm0w47LYABh2bNAvKkmNAiHges40TiCWYIw4AqFoqwAAMACRsK0i+DJZigR3c3miW1zfzdZGyABCAEEACIAfQisQAigBVWLcTiyMQgi6iQ5MmiiGg0EAQGAIFDoLBSCFdN7UIxlcJKaAEY0xRwpFzpTLZXJMApBasmcJPZvONJuFRqDRaeA+YLGMIcWoD5JD2TkYC4QgWIoc0DIbrdaqIEjl-bziwjJ7kADy2gAVvBeAQLaCplwLOQAHIQ1DaexMmag7TTK+H5fj+IIZiCNBbju3QXuWh6sM0J5kGeQGft+TBMhBW4IdgiD1o2ljkLwCw8vAWbFKUfZQSQJARDg5b2PsBDkGWFaPhc0FYMxJBSKs3RQvAWrkEo8EjNB+7wWmFygOQ9EEKJ0GwfJ5ZSRc5AAI5IkwRCMhwIlHhmJy4fB5AiAA1MZ5bkNoxz5hOeBCks2aHEAA) +### With [TypeCompiler](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAKgMr4AW8VMlABfUJGyNUoAOTgq0RPljJo2YCTIAjRgA8ZdBs1YdOpeAGEpuOPGyjxk6XIVKVajeZ27g+a7ewDeiYWdi4AJQAFCwBRbEl7MQkpWXBCPHxgKjsANzsDGnhdENZIAFclQmhGRFBwgjMyAB5OUCLCJAATKi5eASEAPgAKKn5BZCxOABoceEg7JHx4Kkm+8YBtAF1QAF5QLYBKdhpQUD9EKlYx-ABrXa5zKwYA8iebBBGxoRnsOYWlZYHE6zQhlbC1IY5ZCwMqYUAVG6IRgAd0QRx2A2Op1O0EgQ2uN1eAlukOhsIOR1+oPBoChMPgwNO50uYVwyEIfBm6CoVGQAHN4A49utyKKCeQ4gkqKT6QdNusAAybRmgDmSZGgRDwDWcKKxeLMIYcbm8gVYAAGABI2Cb+fAksxQNa2RyROaZn5OnCZAAhACCABEAPrhGIARQAqjFuJwZKIgacRDQkzQQBAYAgUOgsJIyh1sJRaMFjGFFNBCLrog5ks40hksrl8oYSmFGoKkk5Uq5lKp1Jp4N4gkZQhx6vg29XOzJyMAMv2CszWMhOp0AJKIXB5+5jttDNvkADyWgAVvAiEbgRMHmRyAA5MqoLR2IYHKbArSTcx3h9P7Av5MUjQNCLqAy6dAeeabqwew7uYe5fvej7PoBwE1Cy0GgGWFZ6uQ+C-Oy8D-oUxQliByC4Lg4SMHmdj3IQ5C5vmF6nGBWD0bgkhLJ0YIMtioDkIoUFDGB65QQm2LkNRhBCWBEHSXm4mnOQACOsLYMQQxGphG5biI6KYoJebkMIADU2lQeQWhHGm-YALS8vMAFAA) The following uses the TypeCompiler to perform JIT optimized type checking for TRPC. @@ -48,18 +57,19 @@ import { TSchema } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' import { TRPCError } from '@trpc/server' -export const IoType = (schema: T, references: TSchema[] = []) => { +export function RpcType(schema: T, references: TSchema[] = []) { const check = TypeCompiler.Compile(schema, references) - return (value: unknown) => check.Check(value) ? value : (() => { + return (value: unknown) => { + if (check.Check(value)) return value const { path, message } = [...check.Errors(value)][0] throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) - })() + } } ``` -## With [Value](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAasrAK7ygAvqEhMcqUAHJwVaIgDGsZNCbBiZAEY4AHsABuPfpLoNmrDgBUAyvIAW8VMiEixE6bIVKVa0vC3bjekYWdlALACUABQBhAFEmMSZnUXEpcAImEnlgKngmPTzjGnhtYNZ5HEQqVgBJHAtfUABeUAAeC1ASgiQAEyowm3tHAD4ACio7B2QsCwAaUCZ4SDykeXgqGcGpgG0AXWbQPYBKZuHQGlBQUYM+TFBeRABrRBwAd0QTprPuW-Jo+3kj3Gk0c8xu-BOAH5QOCBFhRqNPmc2BdLqAKlVzKASMgCLZ5ugqFRkABzATCFrbcjUn78cjxRJUYFDZDzRbLRYKdZgwzwI67bYABl2qMueLEr1AiHgkoiMQZzFGHEJxLJWAABgASNgq0nwYSQZigbU4vGCdXzCo9O6SABCAEEACIAfXCsQAigBVWJWCySIRHVGCI6Img0EAQGAIFDoLBiXjdJiUWhBMyhOTQAhy6LJVxpDJZHJ5ApMQKmEKWRoGvPuOSKZSqdR+HRlsqhOoNMi51KScjAXCEXxFDHVUDIHo9GqIEgJg4d3yjTsUADyGgAVvB5AQlajpmFfOQAHK8VAaPKI2aojQzA-H0-nwPBwM0Eescc9ZcJmesFrzsiL28TzPJhEWfV9QB-bBEEzbNyHkRZcXgUNilKNNwOQEgSHCHAEzyA4CHIeNEx3S53ywAiSDENYel4RZRVAcg5G-UZ3ynb9AzRBicIIZj30-HiEw4tFyAAR34JgiARDgmNnYNTig79yCcABqBSE3IDQTgjJs8GJZYaGDIA) +### With [Value](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAasrAK7ygAvqEjZGqUAHJwVaInyxk0bMBJkARowAewAG49+kug2asOAFQDK+ABbxUyISLETps+YuWrS8TVqP0TCzsoOYASgAKAMIAothi2E6i4lLghHj4wFTw2Lo5RjTwWkGskLzyhNCMiKBhBOY+ADzmoEWESAAmVKHWdg4AfAAUVLb2yFjmADQ48JA5SPjwVBO9YwDaALqgALygmwCU7DSgM4S82DWD+nyYoOUA1oiMAO6Ih9v9Rycn0JCD3DdyFE7Ph7sNRg5ptd+PtDth4GcLqBofBjt98NUqGZQLhkIQbNN0FQqMgAOYCYS7NbkGkA-jkOIJKjgvrIabwubw+RLKEGeD7DZrAAMGzRJ3xYmeoEQ8Cl4WijOYgw4RJJ5KwAAMACRsVVk+DCSDMUA63H4wQa6YYjq3SQAIQAggARAD6YRiAEUAKoxSzmSRCfZowQ0EM0EAQGAIFDoLBiXjtbCUWiBUwhOTQQjyqJJFypdIELI5PLYAImYIWHy5lJuOQKJQqNS+bRlkohOr4BpkauucjADJNgoYxBY0DIDodACSiFwCZ2tXqPkGXYoAHl1AAreBEZVo8ahHzkAByvFQ6hyg32kzR6gmh5PZ4vQcEsJoNGHo-HHVXCdnrF2HYrsu96nue2CXkG76Yqw-6gBmWaRFE5D4PCeLwJeb5FG2H6sMguC4GEjAJjk86EOQ8aJruJxflgZG4GIiwdOcqLfKA5ByH+gxftOf5Bqx5BEYQnFfj+QkJnx3zkAAjvw2DEIMypwTOc4vjsnwcQm5COAA1Epf7kOohwRk2AC0JJzKG+xAA) The following performs dynamic type checking without code evaluation. @@ -68,17 +78,18 @@ import { Value } from '@sinclair/typebox/value' import { TSchema } from '@sinclair/typebox' import { TRPCError } from '@trpc/server' -export const IoType = (schema: T, references: TSchema[] = []) => - (value: unknown) => Value.Check(schema, value) ? value : (() => { +export function RpcType(schema: T, references: TSchema[] = []) { + return (value: unknown) => { + if (Value.Check(schema, value)) return value const { path, message } = [...Value.Errors(schema, references, value)][0] throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) - })() + } +} ``` -## With [Ajv](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKaB7AtAC4CeADvAHQEDOAUNKiTgE4GgDeoAKgMoDGAFvFTIANKG4FkBaL1ABfUJCY5UoAOTgq0RL1jJoTYMTIAjHAA81dBs1YdOAJQAKAYQCiTZU3mLlqjQRMJLzAVPBMAG7hVvSMLKAAggBWEb4q6sgpwAAm0FQEwABMAAwlVjTw5nGsvDiI+aAAkjicpPCgALygADycoJUESNlUXHyCwgB8ABRUAkLIWJxiTPCQ4Ui88FSLY-MA2gC6naCHAJSdE+w0oKC19ayZqV2I8ADuiSlTp9egK2srOi25EgzDcyAEUz+60BF1Aj3IyGy2V2wkhq2hm1O3xudwac14AGtjvDagw4PAZnNhNjfvACABXJiIUBTCLIWD0zCgemIAmIHCvRCnLASKQyXqXDqXfEE1nsznnAD8oDZHPaWCmX1hbB+OLqDQ46CoVGQAHN4GJtPlkICnFJ+D4ujLyOEvFQAIR7YoHXWgAj8ZTvF7vRyuDxeKaGrYm81YUAAAwAJGwjTH4AoQd5k1bJLb7XJ42JatkuWoAEIJAAiAH0HG4AIoAVTc3E4ankNLkpy+NDkNBoIAgMAQKHQWGU9MGTEotFitnY2EQ0AIoZcPiU6QCQRCYUi0Ws1QXrTI6786k02l0+kMxngZksB-nHGax-aGbPanIwFwhDa5VxDxIo0iAkJOxwvm0UyvuQADyJhJPAvAEJGPwLFwbTkAAcvSqAmOEXwiD8JiLBh2G4fh3xdt8NAAXCSIwZOoGsF0EFkFBpE4XhTBfNRtHMYuy6ruQvArFIFLUZUh60cgJAkA4OCTuExwEOQE5TihNyItkWAqSQyibNkjLwL65DaExUxacBTE0jc5AKQQ5laQxDmTjZoDkAAjpyTBEJqHBmWBXawgFKnIKAADUi5MeQJjnIOt54Caay9qcQA) - +### With [Ajv](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAKgMr4AW8VMgA0oboWSFo+UAF9QkbI1SgA5OCrRE+WMmjZgJMgCNGAD1V0GzVh04AlAAoBhAKLYl2OQqUr1hPHxgKnhsADdQy3omFlAAQQArMJ9lNWQk4AATaCpCYAAmAAYiyxp4MxjWSABXbSlGRFB7Ak5SeAAeTlBywiRMqi5eASEAPgAKKn5BZCxOUWx4SFCkfHgqWaHpgG0AXVAAXlBdgEp2GlBQfAbc0HTkw8R4AHd4pLHj85xF5e018khmK5kPwxmMFksFr9TvsRrckuRkJlMpshGDvpDVscPhcrogblN8ABrA5wsLkK4MODwCZTITYr6EarYRpjMLIWDVTCgWqExCMJ6IY5YcSSaSdWEws4XC7QSBjAmE1nszlYhlMxpsjnwT44662UDoKhUZAAc3goi0uWQv0ckj43kOCvIoU8VAAhFtCjsdaBCHwlC9Hi8HC53J4xhxDcazVgAAYAEjYUdN8HkAK8ictEhtdtksdEV0yXNUACE4gARAD69lcAEUAKqubicVRyemyGgdmggCAwBAodBYJTVXrYSi0aI2digLTQQgh5zeRSpfyBYKhCLYKLWWJ2NpL3xqDRaHR6AxGeCmCxWSrT5r4VpkA8r8jAQIX0q4m6IzIASUQuAjiS96PtSoHkAA8sYCTwEQEafDMXBtOQABy1SoMYoTvMInzGLMyFoRhWEfLIWI0DQX6sD+EEjoBrCHCBbRjOBhGYdg7wfBReq+iSs7zk4zjkgskjUpx5S3pRty4Lg9iMCOoQkoQ5DDqO8EXD+WBKbgSirJkTLatKoDkFodFjD+-50fSFzkHJhCmdRtEjlZRkAI6ctgxCghwJlAaRBywj5SnIKAADUM4ASO5DGKcPYXgAtMaSydscQA) The following uses Ajv to perform more generalized JSON Schema checks across the complete JSON Schema specification. @@ -87,13 +98,14 @@ import { TSchema, Static } from '@sinclair/typebox' import { TRPCError } from '@trpc/server' import Ajv from 'ajv/dist/2020' -export const IoType = (schema: T, references: TSchema[] = []) => { +export function RpcType(schema: T, references: TSchema[] = []) { const ajv = new Ajv() - references.forEach(reference => ajv.addSchema(reference)) + references.forEach((reference) => ajv.addSchema(reference)) const check = ajv.compile(schema) - return (value: unknown): Static => check(value) ? value : (() => { + return (value: unknown): Static => { + if (check(value)) return value const { message, instancePath } = check.errors![0] - throw new TRPCError({ message: `${message} for ${instancePath}`, code: 'BAD_REQUEST' }) - })() + throw new TRPCError({ message: `${message} for ${instancePath}`, code: 'BAD_REQUEST' }) + } } ``` diff --git a/example/typedef/index.ts b/example/typedef/index.ts new file mode 100644 index 000000000..7b932b243 --- /dev/null +++ b/example/typedef/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/extensions + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './typedef' diff --git a/example/typedef/readme.md b/example/typedef/readme.md new file mode 100644 index 000000000..0f0106a67 --- /dev/null +++ b/example/typedef/readme.md @@ -0,0 +1,100 @@ +# JSON Type Definition + +JSON Type Definition (JTD) specification is an alternative schema specification to JSON Schema that provides better support for nominal type systems. TypeBox doesn't provide JTD support by default, but can be expressed through unsafe types and validated with Ajv. + +This example provides a reference implementation for JSON Type Definition. + +## TypeDef + +Refer to the `typedef.ts` file in this directory for a reference implementation of the JSON Type Definition type builder. + +```typescript +import { Static } from '@sinclair/typebox' +import { TypeDef } from './typedef' +``` + +## Properties + +```typescript +// ------------------------------------------------------------------------ +// PropertiesType +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/#properties-schemas +// +// ------------------------------------------------------------------------ + +export type PropertiesType = Static +export const PropertiesType = TypeDef.Properties({ + x: TypeDef.Float32(), + y: TypeDef.Float32(), + z: TypeDef.Float32(), +}) +``` + +## Values + +```typescript +// ------------------------------------------------------------------------ +// ValuesType +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/#values-schemas +// +// ------------------------------------------------------------------------ + +export type ValuesType = Static +export const ValuesType = TypeDef.Values(TypeDef.Float64()) +``` + +## Enum + +```typescript +// ------------------------------------------------------------------------ +// EnumType +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/#enum-schemas +// +// ------------------------------------------------------------------------ + +export type EnumType = Static +export const EnumType = TypeDef.Enum(['FOO', 'BAR', 'BAZ']) +``` + +## Elements + +```typescript +// ------------------------------------------------------------------------ +// ElementsType +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/#elements-schemas +// +// ------------------------------------------------------------------------ + +export type ElementsType = Static +export const ElementsType = TypeDef.Elements(PropertiesType) +``` + +## Union + +```typescript +// ------------------------------------------------------------------------ +// UnionType +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/#discriminator-schemas +// +// ------------------------------------------------------------------------ + +export type UnionType = Static +export const UnionType = TypeDef.Union('eventType', { + USER_CREATED: TypeDef.Properties({ + id: TypeDef.String(), + }), + USER_PAYMENT_PLAN_CHANGED: TypeDef.Properties({ + id: TypeDef.String(), + plan: TypeDef.Enum(['FREE', 'PAID']), + }), + USER_DELETED: TypeDef.Properties({ + id: TypeDef.String(), + softDelete: TypeDef.Boolean(), + }), +}) +``` diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts new file mode 100644 index 000000000..06a60c1bc --- /dev/null +++ b/example/typedef/typedef.ts @@ -0,0 +1,88 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/typedef + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Type, Static, TUnsafe } from '@sinclair/typebox' + +// ------------------------------------------------------------------------ +// TypeDef Namespace +// +// https://jsontypedef.com/docs/jtd-in-5-minutes/ +// ------------------------------------------------------------------------ + +export type StaticTypeDefUnion>> = { [K in keyof M]: { [P in D]: K } & Static }[keyof M] + +export namespace TypeDef { + export function Boolean() { + return Type.Unsafe({ type: 'boolean' }) + } + export function String() { + return Type.Unsafe({ type: 'string' }) + } + export function TimeStamp() { + return Type.Unsafe({ type: 'timestamp' }) + } + export function Float32() { + return Type.Unsafe({ type: 'float32' }) + } + export function Float64() { + return Type.Unsafe({ type: 'float64' }) + } + export function Int8() { + return Type.Unsafe({ type: 'int8' }) + } + export function Uint8() { + return Type.Unsafe({ type: 'uint8' }) + } + export function Int16() { + return Type.Unsafe({ type: 'int16' }) + } + export function Uint16() { + return Type.Unsafe({ type: 'uint16' }) + } + export function Int32() { + return Type.Unsafe({ type: 'int32' }) + } + export function Uint32() { + return Type.Unsafe({ type: 'uint32' }) + } + export function Enum(values: [...T]) { + return Type.Unsafe({ enum: values }) + } + export function Elements>(element: T) { + return Type.Unsafe>>({ elements: element }) + } + export function Properties>>(properties: T) { + return Type.Unsafe<{ [K in keyof T]: Static }>({ properties }) + } + export function Values>(values: V) { + return Type.Unsafe>>({ values }) + } + export function Union>>(discriminator: D, mapping: M) { + return Type.Unsafe>({ discriminator, mapping }) + } +} diff --git a/package.json b/package.json index b9a59fe67..50ce0021c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.23", + "version": "0.25.24", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 4276f179c..34acd8bbd 100644 --- a/readme.md +++ b/readme.md @@ -19,17 +19,17 @@ ## Install -### npm +#### Npm ```bash $ npm install @sinclair/typebox --save ``` -### deno +#### Deno ```typescript import { Static, Type } from 'npm:@sinclair/typebox' ``` -### esm +#### Esm ```typescript import { Static, Type } from 'https://esm.sh/@sinclair/typebox' @@ -64,7 +64,7 @@ type T = Static // type T = { 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 a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. -TypeBox is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used either as a simple tool to build up complex schemas or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used either as a simple tool to build up complex schemas or integrated into REST and RPC services to help validate data received over the wire. License MIT @@ -96,8 +96,8 @@ License MIT - [Errors](#values-errors) - [Pointer](#values-pointer) - [TypeCheck](#typecheck) - - [TypeCompiler](#typecheck-typecompiler) - [Ajv](#typecheck-ajv) + - [TypeCompiler](#typecheck-typecompiler) - [TypeSystem](#typecheck) - [Types](#typesystem-types) - [Formats](#typesystem-formats) @@ -933,17 +933,58 @@ ValuePointer.Set(A, '/z', 1) // const A = { x: 1, y: 1, ## TypeCheck -TypeBox targets JSON Schema Draft 6 and is built and tested against the Ajv JSON Schema validator for standards compliance. TypeBox also includes an optional built-in TypeCompiler that can provide improved compilation and validation performance specifically for TypeBox types only. +TypeBox constructs JSON Schema draft 6 compliant schematics and can be used with any validator that supports this specification. In JavaScript, an ideal validator to use is Ajv which supports draft 6 as well as more recent revisions to the specification. In addition to Ajv, TypeBox provides an optional built in type compiler which can offer faster runtime type compilation, as well as providing high performance data validation for TypeBox types only. The following sections detail using these validators. + + +## Ajv + +The following shows the recommended setup for Ajv. + +```bash +$ npm install ajv ajv-formats --save +``` + +```typescript +import { Type } from '@sinclair/typebox' +import addFormats from 'ajv-formats' +import Ajv from 'ajv' + +const ajv = addFormats(new Ajv({}), [ + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', + 'uuid', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', + 'regex' +]) + +const C = ajv.compile(Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +})) + +const R = C({ x: 1, y: 2, z: 3 }) // const R = true +``` + ### TypeCompiler -TypeBox includes an high performance just-in-time (JIT) compiler and type checker that can be used in applications that require extremely fast validation. Note that this compiler is optimized for TypeBox types only where the schematics are known in advance. +The TypeCompiler is a Just-In-Time (JIT) runtime compiler that can be used to convert TypeBox types into fast validation routines. This compiler is specifically tuned for fast compilation and validation for TypeBox types only. -The compiler module is provided as an optional import. +The TypeCompiler is provided as an optional import. ```typescript import { TypeCompiler } from '@sinclair/typebox/compiler' @@ -961,7 +1002,7 @@ const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck - -### Ajv - -The following are the recommended configurations to support both the [Standard](#standard) and [Extended](#extended) type sets provided by TypeBox. For schema portability and publishing to remote systems, it is recommended to use the Standard type set only. - -```bash -$ npm install ajv ajv-formats --save -``` - -
    - - -Standard Ajv Configuration -

    Expand for Standard Type Set Configuration

    -
    - -```typescript -import { Type } from '@sinclair/typebox' -import addFormats from 'ajv-formats' -import Ajv from 'ajv' - -export function createAjv() { - return addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' - ]) -} - -const ajv = createAjv() - -const R = ajv.validate(Type.Object({ // const R = true - x: Type.Number(), - y: Type.Number(), - z: Type.Number() -}), { x: 1, y: 2, z: 3 }) -``` - -
    - -
    - - -Extended Ajv Configuration -

    Expand for Extended Type Set Configuration

    -
    - -```typescript -import { TypeGuard } from '@sinclair/typebox/guard' -import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' -import addFormats from 'ajv-formats' -import Ajv from 'ajv' - -function schemaOf(schemaOf: string, value: unknown, schema: unknown) { - switch (schemaOf) { - case 'Constructor': - return TypeGuard.TConstructor(schema) && Value.Check(schema, value) // not supported - case 'Function': - return TypeGuard.TFunction(schema) && Value.Check(schema, value) // not supported - case 'Date': - return TypeGuard.TDate(schema) && Value.Check(schema, value) - case 'Promise': - return TypeGuard.TPromise(schema) && Value.Check(schema, value) // not supported - case 'Uint8Array': - return TypeGuard.TUint8Array(schema) && Value.Check(schema, value) - case 'Undefined': - return TypeGuard.TUndefined(schema) && Value.Check(schema, value) // not supported - case 'Void': - return TypeGuard.TVoid(schema) && Value.Check(schema, value) - default: - return false - } -} - -export function createAjv() { - return addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', - 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', - 'regex' - ]) - .addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf }) - .addKeyword({ type: 'null', keyword: 'typeOf', validate: schemaOf }) - .addKeyword('exclusiveMinimumTimestamp') - .addKeyword('exclusiveMaximumTimestamp') - .addKeyword('minimumTimestamp') - .addKeyword('maximumTimestamp') - .addKeyword('minByteLength') - .addKeyword('maxByteLength') -} - -const ajv = createAjv() - -const R = ajv.validate(Type.Object({ // const R = true - buffer: Type.Uint8Array(), - date: Type.Date(), - void: Type.Void() -}), { - buffer: new Uint8Array(), - date: new Date(), - void: null -}) -``` - -
    - - ## TypeSystem -TypeBox provides an extensible TypeSystem module that enables developers to register additional types above and beyond the standard or extended type set. This module also allows developers to define custom string formats as well as override certain type checking behaviours. +TypeBox provides an extensible TypeSystem module that enables developers to define additional types above and beyond the built in type set. This module also allows developers to define custom string formats as well as override certain type checking behaviours. The TypeSystem module is provided as an optional import. @@ -1148,7 +1059,7 @@ import { TypeSystem } from '@sinclair/typebox/system' ### Types -Use the `CreateType(...)` function to specify and return a custom type. This function will return a type factory function that can be used to construct the type. The following creates and registers a BigNumber type which will statically infer as `bigint`. +Use the `CreateType(...)` function to specify custom type. This function will return a type factory function that can be used to construct the type. The following creates and registers a BigNumber type which will statically infer as `bigint`. ```typescript //-------------------------------------------------------------------------------------------- @@ -1233,29 +1144,29 @@ This benchmark measures compilation performance for varying types. You can revie ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 418 ms' │ ' 14 ms' │ ' 29.86 x' │ -│ String │ 2000 │ ' 331 ms' │ ' 12 ms' │ ' 27.58 x' │ -│ Boolean │ 2000 │ ' 290 ms' │ ' 13 ms' │ ' 22.31 x' │ -│ Null │ 2000 │ ' 253 ms' │ ' 8 ms' │ ' 31.63 x' │ -│ RegEx │ 2000 │ ' 481 ms' │ ' 18 ms' │ ' 26.72 x' │ -│ ObjectA │ 2000 │ ' 2675 ms' │ ' 54 ms' │ ' 49.54 x' │ -│ ObjectB │ 2000 │ ' 2849 ms' │ ' 39 ms' │ ' 73.05 x' │ -│ Tuple │ 2000 │ ' 1224 ms' │ ' 22 ms' │ ' 55.64 x' │ -│ Union │ 2000 │ ' 1225 ms' │ ' 26 ms' │ ' 47.12 x' │ -│ Vector4 │ 2000 │ ' 1777 ms' │ ' 24 ms' │ ' 74.04 x' │ -│ Matrix4 │ 2000 │ ' 825 ms' │ ' 12 ms' │ ' 68.75 x' │ -│ Literal_String │ 2000 │ ' 345 ms' │ ' 9 ms' │ ' 38.33 x' │ -│ Literal_Number │ 2000 │ ' 363 ms' │ ' 7 ms' │ ' 51.86 x' │ -│ Literal_Boolean │ 2000 │ ' 358 ms' │ ' 6 ms' │ ' 59.67 x' │ -│ Array_Number │ 2000 │ ' 687 ms' │ ' 8 ms' │ ' 85.88 x' │ -│ Array_String │ 2000 │ ' 726 ms' │ ' 8 ms' │ ' 90.75 x' │ -│ Array_Boolean │ 2000 │ ' 703 ms' │ ' 8 ms' │ ' 87.88 x' │ -│ Array_ObjectA │ 2000 │ ' 3686 ms' │ ' 40 ms' │ ' 92.15 x' │ -│ Array_ObjectB │ 2000 │ ' 3821 ms' │ ' 40 ms' │ ' 95.53 x' │ -│ Array_Tuple │ 2000 │ ' 2070 ms' │ ' 17 ms' │ ' 121.76 x' │ -│ Array_Union │ 2000 │ ' 1503 ms' │ ' 21 ms' │ ' 71.57 x' │ -│ Array_Vector4 │ 2000 │ ' 2185 ms' │ ' 21 ms' │ ' 104.05 x' │ -│ Array_Matrix4 │ 2000 │ ' 1502 ms' │ ' 16 ms' │ ' 93.88 x' │ +│ Number │ 2000 │ ' 451 ms' │ ' 16 ms' │ ' 28.19 x' │ +│ String │ 2000 │ ' 338 ms' │ ' 14 ms' │ ' 24.14 x' │ +│ Boolean │ 2000 │ ' 297 ms' │ ' 13 ms' │ ' 22.85 x' │ +│ Null │ 2000 │ ' 265 ms' │ ' 8 ms' │ ' 33.13 x' │ +│ RegEx │ 2000 │ ' 492 ms' │ ' 18 ms' │ ' 27.33 x' │ +│ ObjectA │ 2000 │ ' 2744 ms' │ ' 55 ms' │ ' 49.89 x' │ +│ ObjectB │ 2000 │ ' 3005 ms' │ ' 44 ms' │ ' 68.30 x' │ +│ Tuple │ 2000 │ ' 1283 ms' │ ' 26 ms' │ ' 49.35 x' │ +│ Union │ 2000 │ ' 1263 ms' │ ' 27 ms' │ ' 46.78 x' │ +│ Vector4 │ 2000 │ ' 1622 ms' │ ' 23 ms' │ ' 70.52 x' │ +│ Matrix4 │ 2000 │ ' 888 ms' │ ' 12 ms' │ ' 74.00 x' │ +│ Literal_String │ 2000 │ ' 344 ms' │ ' 14 ms' │ ' 24.57 x' │ +│ Literal_Number │ 2000 │ ' 389 ms' │ ' 8 ms' │ ' 48.63 x' │ +│ Literal_Boolean │ 2000 │ ' 374 ms' │ ' 9 ms' │ ' 41.56 x' │ +│ Array_Number │ 2000 │ ' 710 ms' │ ' 12 ms' │ ' 59.17 x' │ +│ Array_String │ 2000 │ ' 739 ms' │ ' 9 ms' │ ' 82.11 x' │ +│ Array_Boolean │ 2000 │ ' 732 ms' │ ' 7 ms' │ ' 104.57 x' │ +│ Array_ObjectA │ 2000 │ ' 3733 ms' │ ' 42 ms' │ ' 88.88 x' │ +│ Array_ObjectB │ 2000 │ ' 3602 ms' │ ' 42 ms' │ ' 85.76 x' │ +│ Array_Tuple │ 2000 │ ' 2204 ms' │ ' 20 ms' │ ' 110.20 x' │ +│ Array_Union │ 2000 │ ' 1533 ms' │ ' 24 ms' │ ' 63.88 x' │ +│ Array_Vector4 │ 2000 │ ' 2263 ms' │ ' 21 ms' │ ' 107.76 x' │ +│ Array_Matrix4 │ 2000 │ ' 1576 ms' │ ' 14 ms' │ ' 112.57 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1269,31 +1180,31 @@ This benchmark measures validation performance for varying types. You can review ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 28 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ -│ String │ 1000000 │ ' 25 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ -│ Boolean │ 1000000 │ ' 24 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Null │ 1000000 │ ' 25 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ -│ RegEx │ 1000000 │ ' 164 ms' │ ' 53 ms' │ ' 37 ms' │ ' 1.43 x' │ -│ ObjectA │ 1000000 │ ' 593 ms' │ ' 47 ms' │ ' 25 ms' │ ' 1.88 x' │ -│ ObjectB │ 1000000 │ ' 1053 ms' │ ' 54 ms' │ ' 40 ms' │ ' 1.35 x' │ -│ Tuple │ 1000000 │ ' 129 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ -│ Union │ 1000000 │ ' 334 ms' │ ' 25 ms' │ ' 16 ms' │ ' 1.56 x' │ -│ Recursive │ 1000000 │ ' 3127 ms' │ ' 424 ms' │ ' 98 ms' │ ' 4.33 x' │ -│ Vector4 │ 1000000 │ ' 152 ms' │ ' 24 ms' │ ' 12 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 593 ms' │ ' 41 ms' │ ' 27 ms' │ ' 1.52 x' │ -│ Literal_String │ 1000000 │ ' 48 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 22 ms' │ ' 10 ms' │ ' 2.20 x' │ -│ Literal_Boolean │ 1000000 │ ' 48 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Array_Number │ 1000000 │ ' 495 ms' │ ' 32 ms' │ ' 21 ms' │ ' 1.52 x' │ -│ Array_String │ 1000000 │ ' 481 ms' │ ' 31 ms' │ ' 21 ms' │ ' 1.48 x' │ -│ Array_Boolean │ 1000000 │ ' 446 ms' │ ' 32 ms' │ ' 27 ms' │ ' 1.19 x' │ -│ Array_ObjectA │ 1000000 │ ' 14314 ms' │ ' 2341 ms' │ ' 1969 ms' │ ' 1.19 x' │ -│ Array_ObjectB │ 1000000 │ ' 16883 ms' │ ' 2661 ms' │ ' 2606 ms' │ ' 1.02 x' │ -│ Array_Tuple │ 1000000 │ ' 1834 ms' │ ' 98 ms' │ ' 77 ms' │ ' 1.27 x' │ -│ Array_Union │ 1000000 │ ' 4960 ms' │ ' 240 ms' │ ' 87 ms' │ ' 2.76 x' │ -│ Array_Recursive │ 1000000 │ ' 56273 ms' │ ' 7118 ms' │ ' 1122 ms' │ ' 6.34 x' │ -│ Array_Vector4 │ 1000000 │ ' 2498 ms' │ ' 99 ms' │ ' 48 ms' │ ' 2.06 x' │ -│ Array_Matrix4 │ 1000000 │ ' 12487 ms' │ ' 383 ms' │ ' 246 ms' │ ' 1.56 x' │ +│ Number │ 1000000 │ ' 30 ms' │ ' 7 ms' │ ' 6 ms' │ ' 1.17 x' │ +│ String │ 1000000 │ ' 23 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Boolean │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ +│ Null │ 1000000 │ ' 27 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ RegEx │ 1000000 │ ' 163 ms' │ ' 47 ms' │ ' 38 ms' │ ' 1.24 x' │ +│ ObjectA │ 1000000 │ ' 654 ms' │ ' 41 ms' │ ' 24 ms' │ ' 1.71 x' │ +│ ObjectB │ 1000000 │ ' 1173 ms' │ ' 59 ms' │ ' 41 ms' │ ' 1.44 x' │ +│ Tuple │ 1000000 │ ' 124 ms' │ ' 24 ms' │ ' 17 ms' │ ' 1.41 x' │ +│ Union │ 1000000 │ ' 332 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ +│ Recursive │ 1000000 │ ' 3129 ms' │ ' 412 ms' │ ' 102 ms' │ ' 4.04 x' │ +│ Vector4 │ 1000000 │ ' 147 ms' │ ' 26 ms' │ ' 13 ms' │ ' 2.00 x' │ +│ Matrix4 │ 1000000 │ ' 576 ms' │ ' 41 ms' │ ' 28 ms' │ ' 1.46 x' │ +│ Literal_String │ 1000000 │ ' 51 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ +│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ +│ Array_Number │ 1000000 │ ' 490 ms' │ ' 33 ms' │ ' 18 ms' │ ' 1.83 x' │ +│ Array_String │ 1000000 │ ' 502 ms' │ ' 31 ms' │ ' 25 ms' │ ' 1.24 x' │ +│ Array_Boolean │ 1000000 │ ' 465 ms' │ ' 33 ms' │ ' 27 ms' │ ' 1.22 x' │ +│ Array_ObjectA │ 1000000 │ ' 15463 ms' │ ' 2470 ms' │ ' 2052 ms' │ ' 1.20 x' │ +│ Array_ObjectB │ 1000000 │ ' 18047 ms' │ ' 2497 ms' │ ' 2348 ms' │ ' 1.06 x' │ +│ Array_Tuple │ 1000000 │ ' 1958 ms' │ ' 99 ms' │ ' 77 ms' │ ' 1.29 x' │ +│ Array_Union │ 1000000 │ ' 5348 ms' │ ' 254 ms' │ ' 89 ms' │ ' 2.85 x' │ +│ Array_Recursive │ 1000000 │ ' 54643 ms' │ ' 8870 ms' │ ' 1158 ms' │ ' 7.66 x' │ +│ Array_Vector4 │ 1000000 │ ' 2724 ms' │ ' 105 ms' │ ' 48 ms' │ ' 2.19 x' │ +│ Array_Matrix4 │ 1000000 │ ' 13821 ms' │ ' 437 ms' │ ' 266 ms' │ ' 1.64 x' │ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1307,14 +1218,15 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 64 kb' │ ' 31 kb' │ '2.02 x' │ -│ typebox/conditional │ ' 45 kb' │ ' 18 kb' │ '2.44 x' │ -│ typebox/custom │ ' 0 kb' │ ' 0 kb' │ '2.61 x' │ -│ typebox/format │ ' 0 kb' │ ' 0 kb' │ '2.66 x' │ -│ typebox/guard │ ' 23 kb' │ ' 11 kb' │ '2.07 x' │ -│ typebox/hash │ ' 4 kb' │ ' 1 kb' │ '2.30 x' │ -│ typebox/value │ ' 89 kb' │ ' 41 kb' │ '2.15 x' │ -│ typebox │ ' 12 kb' │ ' 6 kb' │ '1.89 x' │ +│ typebox/compiler │ ' 65.4 kb' │ ' 32.2 kb' │ '2.03 x' │ +│ typebox/conditional │ ' 45.5 kb' │ ' 18.6 kb' │ '2.45 x' │ +│ typebox/custom │ ' 0.6 kb' │ ' 0.2 kb' │ '2.61 x' │ +│ typebox/format │ ' 0.6 kb' │ ' 0.2 kb' │ '2.66 x' │ +│ typebox/guard │ ' 23.8 kb' │ ' 11.4 kb' │ '2.08 x' │ +│ typebox/hash │ ' 4.2 kb' │ ' 1.8 kb' │ '2.30 x' │ +│ typebox/system │ ' 14.0 kb' │ ' 7.1 kb' │ '1.96 x' │ +│ typebox/value │ ' 90.0 kb' │ ' 41.8 kb' │ '2.15 x' │ +│ typebox │ ' 12.0 kb' │ ' 6.4 kb' │ '1.89 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 032e7a0bb..de1b76664 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { ValueErrors, ValueError } from '../errors/index' import { TypeSystem } from '../system/index' -import { TypeGuard } from '../guard/index' +import { TypeExtends, TypeGuard } from '../guard/index' import { Format } from '../format/index' import { Custom } from '../custom/index' import { ValueHash } from '../hash/index' @@ -246,6 +246,9 @@ export namespace TypeCompiler { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { yield* Visit(propertySchema, memberExpression) + if (TypeExtends.Undefined(propertySchema)) { + yield `('${propertyKey}' in ${value})` + } } else { const expression = CreateExpression(propertySchema, memberExpression) yield `(${memberExpression} === undefined ? true : (${expression}))` diff --git a/src/errors/errors.ts b/src/errors/errors.ts index e90ddfbf3..0cbf89419 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { TypeSystem } from '../system/index' +import { TypeExtends } from '../guard/extends' import { Format } from '../format/index' import { Custom } from '../custom/index' import { ValueHash } from '../hash/index' @@ -280,6 +281,9 @@ export namespace ValueErrors { const propertySchema = schema.properties[propertyKey] if (schema.required && schema.required.includes(propertyKey)) { yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) + if (TypeExtends.Undefined(schema) && !(propertyKey in value)) { + yield { type: ValueErrorType.ObjectRequiredProperties, schema: propertySchema, path: `${path}/${propertyKey}`, value: undefined, message: `Expected required property` } + } } else { if (value[propertyKey] !== undefined) { yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) diff --git a/src/guard/extends.ts b/src/guard/extends.ts new file mode 100644 index 000000000..441a87dca --- /dev/null +++ b/src/guard/extends.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/guard + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, dTribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' + +export namespace TypeExtends { + /** + * This function returns true if the given schema is undefined, either directly or + * through union composition. This check is required on object property types of + * undefined, where an additional `'x' in value` check is required to determine + * the keys existence. + */ + export function Undefined(schema: Types.TSchema): boolean { + if (schema[Types.Kind] === 'Undefined') return true + if (schema[Types.Kind] === 'Union') { + const union = schema as Types.TUnion + return union.anyOf.some((schema) => Undefined(schema)) + } + return false + } +} diff --git a/src/guard/index.ts b/src/guard/index.ts index 980961dae..036419519 100644 --- a/src/guard/index.ts +++ b/src/guard/index.ts @@ -27,3 +27,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './guard' +export * from './extends' diff --git a/src/value/cast.ts b/src/value/cast.ts index 5c205b538..1a12d1333 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -284,9 +284,6 @@ export namespace ValueCast { } return result } - function Recursive(schema: Types.TRecursive, references: Types.TSchema[], value: any): any { - throw new ValueCastRecursiveTypeError(schema) - } function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): any { const reference = references.find((reference) => reference.$id === schema.$ref) if (reference === undefined) throw new ValueCastReferenceTypeError(schema) @@ -357,8 +354,6 @@ export namespace ValueCast { return Promise(anySchema, anyReferences, value) case 'Record': return Record(anySchema, anyReferences, value) - case 'Rec': - return Recursive(anySchema, anyReferences, value) case 'Ref': return Ref(anySchema, anyReferences, value) case 'Self': diff --git a/src/value/check.ts b/src/value/check.ts index 538db1ccb..6149cb7f6 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { TypeSystem } from '../system/index' +import { TypeExtends } from '../guard/extends' import { Format } from '../format/index' import { Custom } from '../custom/index' import { ValueHash } from '../hash/index' @@ -39,10 +40,18 @@ export class ValueCheckUnknownTypeError extends Error { } export namespace ValueCheck { + // -------------------------------------------------------- + // Guards + // -------------------------------------------------------- + function IsNumber(value: unknown): value is number { return typeof value === 'number' && !isNaN(value) } + // -------------------------------------------------------- + // Guards + // -------------------------------------------------------- + function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { return true } @@ -204,6 +213,9 @@ export namespace ValueCheck { if (!Visit(propertySchema, references, value[propertyKey])) { return false } + if (TypeExtends.Undefined(propertySchema)) { + return propertyKey in value + } } else { if (value[propertyKey] !== undefined) { if (!Visit(propertySchema, references, value[propertyKey])) { diff --git a/src/value/create.ts b/src/value/create.ts index 7766960b6..980d180d8 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -186,14 +186,6 @@ export namespace ValueCreate { } } - function Recursive(schema: Types.TRecursive, references: Types.TSchema[]): any { - if (schema.default !== undefined) { - return schema.default - } else { - throw new Error('ValueCreate.Recursive: Recursive types require a default value') - } - } - function Ref(schema: Types.TRef, references: Types.TSchema[]): any { if (schema.default !== undefined) { return schema.default @@ -336,8 +328,6 @@ export namespace ValueCreate { return Promise(anySchema, anyReferences) case 'Record': return Record(anySchema, anyReferences) - case 'Rec': - return Recursive(anySchema, anyReferences) case 'Ref': return Ref(anySchema, anyReferences) case 'Self': diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 924b75c86..5a4bbe1e7 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -205,4 +205,26 @@ describe('type/compiler/Object', () => { z: 3, }) }) + it('Should check for property key if property type is undefined', () => { + const T = Type.Object({ x: Type.Undefined() }) + Ok(T, { x: undefined }) + Fail(T, {}) + }) + it('Should check for property key if property type extends undefined', () => { + const T = Type.Object({ x: Type.Union([Type.Number(), Type.Undefined()]) }) + Ok(T, { x: 1 }) + Ok(T, { x: undefined }) + Fail(T, {}) + }) + it('Should not check for property key if property type is undefined and optional', () => { + const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) + Ok(T, { x: undefined }) + Ok(T, {}) + }) + it('Should not check for property key if property type extends undefined and optional', () => { + const T = Type.Object({ x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])) }) + Ok(T, { x: 1 }) + Ok(T, { x: undefined }) + Ok(T, {}) + }) }) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index ccd0ef10c..ed6a76d93 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -190,4 +190,26 @@ describe('value/check/Object', () => { false, ) }) + it('Should check for property key if property type is undefined', () => { + const T = Type.Object({ x: Type.Undefined() }) + Assert.equal(Value.Check(T, { x: undefined }), true) + Assert.equal(Value.Check(T, {}), false) + }) + it('Should check for property key if property type extends undefined', () => { + const T = Type.Object({ x: Type.Union([Type.Number(), Type.Undefined()]) }) + Assert.equal(Value.Check(T, { x: 1 }), true) + Assert.equal(Value.Check(T, { x: undefined }), true) + Assert.equal(Value.Check(T, {}), false) + }) + it('Should not check for property key if property type is undefined and optional', () => { + const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) + Assert.equal(Value.Check(T, { x: undefined }), true) + Assert.equal(Value.Check(T, {}), true) + }) + it('Should not check for property key if property type extends undefined and optional', () => { + const T = Type.Object({ x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])) }) + Assert.equal(Value.Check(T, { x: 1 }), true) + Assert.equal(Value.Check(T, { x: undefined }), true) + Assert.equal(Value.Check(T, {}), true) + }) }) From 4ec4daad440f94034f2c9b3600fc28630eebe62e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 22 Mar 2023 01:01:38 +0900 Subject: [PATCH 070/369] Revision 0.26.0 (#346) --- .github/workflows/ci.yml | 2 +- .gitignore | 1 + .vscode/settings.json | 4 +- .../compression/module/typebox-conditional.ts | 4 - .../compression/module/typebox-custom.ts | 4 - .../compression/module/typebox-errors.ts | 3 + .../compression/module/typebox-format.ts | 4 - benchmark/compression/module/typebox-guard.ts | 4 - benchmark/compression/module/typebox-hash.ts | 3 - benchmark/measurement/module/cases.ts | 105 +- benchmark/measurement/module/check.ts | 3 +- benchmark/measurement/module/compile.ts | 7 +- benchmark/measurement/module/index.ts | 4 +- changelog.md | 410 --- changelog/0.17.1.md | 15 + changelog/0.17.4.md | 38 + changelog/0.17.6.md | 56 + changelog/0.18.0.md | 22 + changelog/0.18.1.md | 5 + changelog/0.19.0.md | 20 + changelog/0.20.0.md | 17 + changelog/0.20.1.md | 5 + changelog/0.21.0.md | 7 + changelog/0.21.2.md | 26 + changelog/0.22.0.md | 8 + changelog/0.23.0.md | 8 + changelog/0.23.1.md | 5 + changelog/0.23.3.md | 5 + changelog/0.24.0.md | 28 + changelog/0.24.15.md | 10 + changelog/0.24.44.md | 18 + changelog/0.24.49.md | 9 + changelog/0.24.6.md | 20 + changelog/0.24.8.md | 6 + changelog/0.25.0.md | 7 + changelog/0.25.10.md | 5 + changelog/0.25.11.md | 5 + changelog/0.25.18.md | 5 + changelog/0.25.22.md | 5 + changelog/0.25.23.md | 10 + changelog/0.25.24.md | 10 + changelog/0.25.9.md | 5 + changelog/0.26.0.md | 394 +++ codegen/codegen.ts | 53 - codegen/index.ts | 5 +- ...typescript.ts => typebox-to-typescript.ts} | 81 +- codegen/{zod.ts => typebox-to-zod.ts} | 90 +- ...schema.ts => typescript-to-json-schema.ts} | 71 +- .../{typebox.ts => typescript-to-typebox.ts} | 71 +- example/experimental/experimental.ts | 109 + {src/hash => example/experimental}/index.ts | 4 +- example/experimental/readme.md | 30 + example/extensions/index.ts | 32 - example/extensions/intersect-allof.ts | 60 - example/extensions/readonly-object.ts | 56 - example/extensions/union-enum.ts | 46 - example/extensions/union-oneof.ts | 47 - example/formats/additional.ts | 94 - example/formats/index.ts | 2 +- example/formats/readme.md | 43 + example/formats/standard.ts | 74 + example/index.ts | 7 +- example/trpc/readme.md | 12 +- example/typedef/index.ts | 2 +- example/typedef/readme.md | 159 +- example/typedef/typedef.ts | 399 ++- hammer.mjs | 16 +- package-lock.json | 20 +- package.json | 9 +- readme.md | 654 +++-- src/compiler/compiler.ts | 458 ++-- src/conditional/conditional.ts | 114 - src/conditional/index.ts | 30 - src/conditional/structural.ts | 579 ---- src/custom/custom.ts | 54 - src/custom/index.ts | 29 - src/errors/errors.ts | 389 +-- src/format/format.ts | 54 - src/format/index.ts | 29 - src/guard/extends.ts | 46 - src/guard/guard.ts | 487 ---- src/guard/index.ts | 30 - src/system/system.ts | 49 +- src/tsconfig.json | 2 +- src/typebox.ts | 2335 +++++++++++++---- src/value/cast.ts | 207 +- src/value/check.ts | 350 +-- src/value/clone.ts | 5 - src/value/convert.ts | 342 +++ src/value/create.ts | 220 +- src/value/equal.ts | 5 - src/{hash => value}/hash.ts | 76 +- src/value/index.ts | 3 +- src/value/value.ts | 29 +- test/runtime/compiler/any.ts | 8 + test/runtime/compiler/array.ts | 4 +- test/runtime/compiler/bigint.ts | 84 + test/runtime/compiler/boolean.ts | 8 + test/runtime/compiler/composite.ts | 73 + test/runtime/compiler/custom.ts | 6 +- test/runtime/compiler/date.ts | 8 + test/runtime/compiler/index.ts | 4 + test/runtime/compiler/integer.ts | 26 +- test/runtime/compiler/intersect.ts | 130 +- test/runtime/compiler/literal.ts | 4 - test/runtime/compiler/never.ts | 11 +- test/runtime/compiler/not.ts | 38 + test/runtime/compiler/null.ts | 14 +- test/runtime/compiler/number.ts | 30 +- test/runtime/compiler/object.ts | 36 +- test/runtime/compiler/omit.ts | 26 + test/runtime/compiler/pick.ts | 26 + test/runtime/compiler/record.ts | 8 + test/runtime/compiler/ref.ts | 26 +- test/runtime/compiler/string.ts | 9 +- test/runtime/compiler/symbol.ts | 42 + test/runtime/compiler/validate.ts | 9 +- test/runtime/compiler/void.ts | 18 +- test/runtime/conditional/any.ts | 98 - test/runtime/conditional/array.ts | 316 --- test/runtime/conditional/boolean.ts | 113 - test/runtime/conditional/constructor.ts | 250 -- test/runtime/conditional/date.ts | 118 - test/runtime/conditional/function.ts | 251 -- test/runtime/conditional/integer.ts | 119 - test/runtime/conditional/literal.ts | 330 --- test/runtime/conditional/null.ts | 119 - test/runtime/conditional/number.ts | 119 - test/runtime/conditional/object.ts | 161 -- test/runtime/conditional/promise.ts | 251 -- test/runtime/conditional/string.ts | 149 -- test/runtime/conditional/tuple.ts | 203 -- test/runtime/conditional/uint8array.ts | 119 - test/runtime/conditional/undefined.ts | 107 - test/runtime/conditional/union.ts | 154 -- test/runtime/conditional/unknown.ts | 125 - test/runtime/conditional/void.ts | 125 - test/runtime/errors/index.ts | 2 +- test/runtime/errors/iterator.ts | 32 + test/runtime/format/format.ts | 25 - test/runtime/index.ts | 5 +- test/runtime/schema/composite.ts | 94 + test/runtime/schema/index.ts | 2 + test/runtime/schema/intersect.ts | 141 +- test/runtime/schema/not.ts | 38 + test/runtime/schema/validate.ts | 2 +- test/runtime/system/AllowArrayObjects.ts | 61 - test/runtime/system/AllowNaN.ts | 30 - test/runtime/system/CreateFormat.ts | 17 - test/runtime/system/CreateType.ts | 23 - test/runtime/system/index.ts | 5 +- test/runtime/system/system.ts | 169 ++ test/runtime/type/clone/clone.ts | 147 ++ test/runtime/type/clone/index.ts | 1 + test/runtime/type/extends/any.ts | 98 + test/runtime/type/extends/array.ts | 316 +++ test/runtime/type/extends/bigint.ts | 101 + test/runtime/type/extends/boolean.ts | 107 + test/runtime/type/extends/constructor.ts | 250 ++ test/runtime/type/extends/date.ts | 112 + test/runtime/type/extends/function.ts | 251 ++ .../{conditional => type/extends}/index.ts | 2 + test/runtime/type/extends/integer.ts | 113 + test/runtime/type/extends/literal.ts | 312 +++ test/runtime/type/extends/null.ts | 119 + test/runtime/type/extends/number.ts | 113 + test/runtime/type/extends/object.ts | 177 ++ test/runtime/type/extends/promise.ts | 251 ++ .../{conditional => type/extends}/record.ts | 140 +- test/runtime/type/extends/string.ts | 131 + test/runtime/type/extends/symbol.ts | 106 + test/runtime/type/extends/tuple.ts | 161 ++ test/runtime/type/extends/uint8array.ts | 113 + test/runtime/type/extends/undefined.ts | 107 + test/runtime/type/extends/union.ts | 136 + test/runtime/type/extends/unknown.ts | 119 + test/runtime/type/extends/void.ts | 125 + test/runtime/{ => type}/guard/any.ts | 4 +- test/runtime/{ => type}/guard/array.ts | 4 +- test/runtime/type/guard/bigint.ts | 19 + test/runtime/{ => type}/guard/boolean.ts | 4 +- test/runtime/{ => type}/guard/constructor.ts | 4 +- test/runtime/{ => type}/guard/date.ts | 4 +- test/runtime/type/guard/exclude.ts | 24 + test/runtime/type/guard/extract.ts | 24 + test/runtime/{ => type}/guard/function.ts | 4 +- test/runtime/{ => type}/guard/index.ts | 6 + test/runtime/{ => type}/guard/integer.ts | 4 +- test/runtime/type/guard/intersect.ts | 32 + test/runtime/{ => type}/guard/literal.ts | 4 +- test/runtime/type/guard/not.ts | 23 + test/runtime/{ => type}/guard/null.ts | 4 +- test/runtime/{ => type}/guard/number.ts | 4 +- test/runtime/{ => type}/guard/object.ts | 4 +- test/runtime/{ => type}/guard/promise.ts | 4 +- test/runtime/{ => type}/guard/record.ts | 17 +- test/runtime/{ => type}/guard/ref.ts | 4 +- test/runtime/{ => type}/guard/self.ts | 4 +- test/runtime/{ => type}/guard/string.ts | 4 +- test/runtime/type/guard/symbol.ts | 19 + test/runtime/{ => type}/guard/tuple.ts | 4 +- test/runtime/{ => type}/guard/uint8array.ts | 4 +- test/runtime/{ => type}/guard/undefined.ts | 4 +- test/runtime/{ => type}/guard/union.ts | 24 +- test/runtime/{ => type}/guard/unknown.ts | 4 +- test/runtime/{ => type}/guard/void.ts | 4 +- test/runtime/type/index.ts | 5 + test/runtime/type/normal/exclude.ts | 21 + test/runtime/type/normal/extract.ts | 31 + test/runtime/type/normal/index.ts | 5 + test/runtime/type/normal/intersect.ts | 21 + test/runtime/type/normal/record.ts | 12 + test/runtime/type/normal/union.ts | 21 + test/runtime/type/registry/format.ts | 25 + .../{format => type/registry}/index.ts | 0 test/runtime/value/cast/any.ts | 3 - test/runtime/value/cast/array.ts | 25 - test/runtime/value/cast/bigint.ts | 53 + test/runtime/value/cast/boolean.ts | 6 - test/runtime/value/cast/composite.ts | 89 + test/runtime/value/cast/convert.ts | 291 -- test/runtime/value/cast/custom.ts | 13 +- test/runtime/value/cast/date.ts | 18 - test/runtime/value/cast/enum.ts | 10 - test/runtime/value/cast/index.ts | 5 +- test/runtime/value/cast/integer.ts | 13 - test/runtime/value/cast/intersect.ts | 147 +- test/runtime/value/cast/keyof.ts | 9 - test/runtime/value/cast/literal.ts | 9 - test/runtime/value/cast/not.ts | 59 + test/runtime/value/cast/null.ts | 9 - test/runtime/value/cast/number.ts | 14 - test/runtime/value/cast/object.ts | 11 - test/runtime/value/cast/record.ts | 7 - test/runtime/value/cast/recursive.ts | 32 +- test/runtime/value/cast/regex.ts | 9 - test/runtime/value/cast/string.ts | 19 - test/runtime/value/cast/symbol.ts | 52 + test/runtime/value/cast/tuple.ts | 13 - test/runtime/value/cast/uint8array.ts | 9 - test/runtime/value/cast/undefined.ts | 9 - test/runtime/value/cast/union.ts | 19 - test/runtime/value/cast/unknown.ts | 9 - test/runtime/value/cast/void.ts | 9 - test/runtime/value/check/bigint.ts | 44 + test/runtime/value/check/composite.ts | 82 + test/runtime/value/check/custom.ts | 5 +- test/runtime/value/check/index.ts | 5 + test/runtime/value/check/integer.ts | 21 +- test/runtime/value/check/intersect.ts | 138 +- test/runtime/value/check/not.ts | 39 + test/runtime/value/check/number.ts | 15 + test/runtime/value/check/object.ts | 12 + test/runtime/value/check/record.ts | 11 +- test/runtime/value/check/ref.ts | 83 + test/runtime/value/check/symbol.ts | 52 + test/runtime/value/check/void.ts | 6 +- test/runtime/value/convert/any.ts | 42 + test/runtime/value/convert/array.ts | 32 + test/runtime/value/convert/bigint.ts | 46 + test/runtime/value/convert/boolean.ts | 154 ++ test/runtime/value/convert/composite.ts | 16 + test/runtime/value/convert/custom.ts | 24 + test/runtime/value/convert/date.ts | 42 + test/runtime/value/convert/enum.ts | 7 + test/runtime/value/convert/index.ts | 26 + test/runtime/value/convert/integer.ts | 79 + test/runtime/value/convert/keyof.ts | 7 + test/runtime/value/convert/literal.ts | 85 + test/runtime/value/convert/never.ts | 21 + test/runtime/value/convert/null.ts | 19 + test/runtime/value/convert/number.ts | 76 + test/runtime/value/convert/object.ts | 16 + test/runtime/value/convert/record.ts | 7 + test/runtime/value/convert/recursive.ts | 7 + test/runtime/value/convert/regex.ts | 7 + test/runtime/value/convert/string.ts | 69 + test/runtime/value/convert/symbol.ts | 15 + test/runtime/value/convert/tuple.ts | 21 + test/runtime/value/convert/uint8array.ts | 9 + test/runtime/value/convert/undefined.ts | 15 + test/runtime/value/convert/union.ts | 7 + test/runtime/value/convert/unknown.ts | 42 + test/runtime/value/convert/void.ts | 7 + test/runtime/value/create/bigint.ts | 14 + test/runtime/value/create/boolean.ts | 2 +- test/runtime/value/create/composite.ts | 66 + test/runtime/value/create/custom.ts | 7 +- test/runtime/value/create/index.ts | 7 +- test/runtime/value/create/intersect.ts | 86 +- test/runtime/value/create/not.ts | 21 + .../value/create/{rec.ts => recursive.ts} | 0 test/runtime/value/create/ref.ts | 30 + test/runtime/value/create/symbol.ts | 15 + test/runtime/{ => value}/hash/hash.ts | 49 +- test/runtime/{ => value}/hash/index.ts | 0 test/runtime/value/index.ts | 2 + test/static/assert.ts | 6 +- test/static/bigint.ts | 4 + test/static/composite.ts | 19 + test/static/exclude.ts | 15 + test/static/extract.ts | 15 + test/static/index.ts | 6 + test/static/intersect.ts | 27 +- test/static/keyof.ts | 24 + test/static/not.ts | 7 + test/static/omit.ts | 48 + test/static/partial.ts | 53 + test/static/pick.ts | 70 + test/static/required.ts | 54 + test/static/symbol.ts | 4 + tsconfig.json | 16 +- typebox.png | Bin 706199 -> 946728 bytes 313 files changed, 12163 insertions(+), 8924 deletions(-) delete mode 100644 benchmark/compression/module/typebox-conditional.ts delete mode 100644 benchmark/compression/module/typebox-custom.ts create mode 100644 benchmark/compression/module/typebox-errors.ts delete mode 100644 benchmark/compression/module/typebox-format.ts delete mode 100644 benchmark/compression/module/typebox-guard.ts delete mode 100644 benchmark/compression/module/typebox-hash.ts delete mode 100644 changelog.md create mode 100644 changelog/0.17.1.md create mode 100644 changelog/0.17.4.md create mode 100644 changelog/0.17.6.md create mode 100644 changelog/0.18.0.md create mode 100644 changelog/0.18.1.md create mode 100644 changelog/0.19.0.md create mode 100644 changelog/0.20.0.md create mode 100644 changelog/0.20.1.md create mode 100644 changelog/0.21.0.md create mode 100644 changelog/0.21.2.md create mode 100644 changelog/0.22.0.md create mode 100644 changelog/0.23.0.md create mode 100644 changelog/0.23.1.md create mode 100644 changelog/0.23.3.md create mode 100644 changelog/0.24.0.md create mode 100644 changelog/0.24.15.md create mode 100644 changelog/0.24.44.md create mode 100644 changelog/0.24.49.md create mode 100644 changelog/0.24.6.md create mode 100644 changelog/0.24.8.md create mode 100644 changelog/0.25.0.md create mode 100644 changelog/0.25.10.md create mode 100644 changelog/0.25.11.md create mode 100644 changelog/0.25.18.md create mode 100644 changelog/0.25.22.md create mode 100644 changelog/0.25.23.md create mode 100644 changelog/0.25.24.md create mode 100644 changelog/0.25.9.md create mode 100644 changelog/0.26.0.md delete mode 100644 codegen/codegen.ts rename codegen/{typescript.ts => typebox-to-typescript.ts} (76%) rename codegen/{zod.ts => typebox-to-zod.ts} (74%) rename codegen/{jsonschema.ts => typescript-to-json-schema.ts} (85%) rename codegen/{typebox.ts => typescript-to-typebox.ts} (91%) create mode 100644 example/experimental/experimental.ts rename {src/hash => example/experimental}/index.ts (95%) create mode 100644 example/experimental/readme.md delete mode 100644 example/extensions/index.ts delete mode 100644 example/extensions/intersect-allof.ts delete mode 100644 example/extensions/readonly-object.ts delete mode 100644 example/extensions/union-enum.ts delete mode 100644 example/extensions/union-oneof.ts delete mode 100644 example/formats/additional.ts create mode 100644 example/formats/readme.md create mode 100644 example/formats/standard.ts delete mode 100644 src/conditional/conditional.ts delete mode 100644 src/conditional/index.ts delete mode 100644 src/conditional/structural.ts delete mode 100644 src/custom/custom.ts delete mode 100644 src/custom/index.ts delete mode 100644 src/format/format.ts delete mode 100644 src/format/index.ts delete mode 100644 src/guard/extends.ts delete mode 100644 src/guard/guard.ts delete mode 100644 src/guard/index.ts create mode 100644 src/value/convert.ts rename src/{hash => value}/hash.ts (83%) create mode 100644 test/runtime/compiler/bigint.ts create mode 100644 test/runtime/compiler/composite.ts create mode 100644 test/runtime/compiler/not.ts create mode 100644 test/runtime/compiler/symbol.ts delete mode 100644 test/runtime/conditional/any.ts delete mode 100644 test/runtime/conditional/array.ts delete mode 100644 test/runtime/conditional/boolean.ts delete mode 100644 test/runtime/conditional/constructor.ts delete mode 100644 test/runtime/conditional/date.ts delete mode 100644 test/runtime/conditional/function.ts delete mode 100644 test/runtime/conditional/integer.ts delete mode 100644 test/runtime/conditional/literal.ts delete mode 100644 test/runtime/conditional/null.ts delete mode 100644 test/runtime/conditional/number.ts delete mode 100644 test/runtime/conditional/object.ts delete mode 100644 test/runtime/conditional/promise.ts delete mode 100644 test/runtime/conditional/string.ts delete mode 100644 test/runtime/conditional/tuple.ts delete mode 100644 test/runtime/conditional/uint8array.ts delete mode 100644 test/runtime/conditional/undefined.ts delete mode 100644 test/runtime/conditional/union.ts delete mode 100644 test/runtime/conditional/unknown.ts delete mode 100644 test/runtime/conditional/void.ts create mode 100644 test/runtime/errors/iterator.ts delete mode 100644 test/runtime/format/format.ts create mode 100644 test/runtime/schema/composite.ts create mode 100644 test/runtime/schema/not.ts delete mode 100644 test/runtime/system/AllowArrayObjects.ts delete mode 100644 test/runtime/system/AllowNaN.ts delete mode 100644 test/runtime/system/CreateFormat.ts delete mode 100644 test/runtime/system/CreateType.ts create mode 100644 test/runtime/system/system.ts create mode 100644 test/runtime/type/clone/clone.ts create mode 100644 test/runtime/type/clone/index.ts create mode 100644 test/runtime/type/extends/any.ts create mode 100644 test/runtime/type/extends/array.ts create mode 100644 test/runtime/type/extends/bigint.ts create mode 100644 test/runtime/type/extends/boolean.ts create mode 100644 test/runtime/type/extends/constructor.ts create mode 100644 test/runtime/type/extends/date.ts create mode 100644 test/runtime/type/extends/function.ts rename test/runtime/{conditional => type/extends}/index.ts (91%) create mode 100644 test/runtime/type/extends/integer.ts create mode 100644 test/runtime/type/extends/literal.ts create mode 100644 test/runtime/type/extends/null.ts create mode 100644 test/runtime/type/extends/number.ts create mode 100644 test/runtime/type/extends/object.ts create mode 100644 test/runtime/type/extends/promise.ts rename test/runtime/{conditional => type/extends}/record.ts (58%) create mode 100644 test/runtime/type/extends/string.ts create mode 100644 test/runtime/type/extends/symbol.ts create mode 100644 test/runtime/type/extends/tuple.ts create mode 100644 test/runtime/type/extends/uint8array.ts create mode 100644 test/runtime/type/extends/undefined.ts create mode 100644 test/runtime/type/extends/union.ts create mode 100644 test/runtime/type/extends/unknown.ts create mode 100644 test/runtime/type/extends/void.ts rename test/runtime/{ => type}/guard/any.ts (83%) rename test/runtime/{ => type}/guard/array.ts (94%) create mode 100644 test/runtime/type/guard/bigint.ts rename test/runtime/{ => type}/guard/boolean.ts (84%) rename test/runtime/{ => type}/guard/constructor.ts (93%) rename test/runtime/{ => type}/guard/date.ts (92%) create mode 100644 test/runtime/type/guard/exclude.ts create mode 100644 test/runtime/type/guard/extract.ts rename test/runtime/{ => type}/guard/function.ts (93%) rename test/runtime/{ => type}/guard/index.ts (78%) rename test/runtime/{ => type}/guard/integer.ts (93%) create mode 100644 test/runtime/type/guard/intersect.ts rename test/runtime/{ => type}/guard/literal.ts (91%) create mode 100644 test/runtime/type/guard/not.ts rename test/runtime/{ => type}/guard/null.ts (83%) rename test/runtime/{ => type}/guard/number.ts (93%) rename test/runtime/{ => type}/guard/object.ts (96%) rename test/runtime/{ => type}/guard/promise.ts (91%) rename test/runtime/{ => type}/guard/record.ts (66%) rename test/runtime/{ => type}/guard/ref.ts (85%) rename test/runtime/{ => type}/guard/self.ts (85%) rename test/runtime/{ => type}/guard/string.ts (91%) create mode 100644 test/runtime/type/guard/symbol.ts rename test/runtime/{ => type}/guard/tuple.ts (88%) rename test/runtime/{ => type}/guard/uint8array.ts (90%) rename test/runtime/{ => type}/guard/undefined.ts (84%) rename test/runtime/{ => type}/guard/union.ts (63%) rename test/runtime/{ => type}/guard/unknown.ts (84%) rename test/runtime/{ => type}/guard/void.ts (83%) create mode 100644 test/runtime/type/index.ts create mode 100644 test/runtime/type/normal/exclude.ts create mode 100644 test/runtime/type/normal/extract.ts create mode 100644 test/runtime/type/normal/index.ts create mode 100644 test/runtime/type/normal/intersect.ts create mode 100644 test/runtime/type/normal/record.ts create mode 100644 test/runtime/type/normal/union.ts create mode 100644 test/runtime/type/registry/format.ts rename test/runtime/{format => type/registry}/index.ts (100%) create mode 100644 test/runtime/value/cast/bigint.ts create mode 100644 test/runtime/value/cast/composite.ts delete mode 100644 test/runtime/value/cast/convert.ts create mode 100644 test/runtime/value/cast/not.ts create mode 100644 test/runtime/value/cast/symbol.ts create mode 100644 test/runtime/value/check/bigint.ts create mode 100644 test/runtime/value/check/composite.ts create mode 100644 test/runtime/value/check/not.ts create mode 100644 test/runtime/value/check/ref.ts create mode 100644 test/runtime/value/check/symbol.ts create mode 100644 test/runtime/value/convert/any.ts create mode 100644 test/runtime/value/convert/array.ts create mode 100644 test/runtime/value/convert/bigint.ts create mode 100644 test/runtime/value/convert/boolean.ts create mode 100644 test/runtime/value/convert/composite.ts create mode 100644 test/runtime/value/convert/custom.ts create mode 100644 test/runtime/value/convert/date.ts create mode 100644 test/runtime/value/convert/enum.ts create mode 100644 test/runtime/value/convert/index.ts create mode 100644 test/runtime/value/convert/integer.ts create mode 100644 test/runtime/value/convert/keyof.ts create mode 100644 test/runtime/value/convert/literal.ts create mode 100644 test/runtime/value/convert/never.ts create mode 100644 test/runtime/value/convert/null.ts create mode 100644 test/runtime/value/convert/number.ts create mode 100644 test/runtime/value/convert/object.ts create mode 100644 test/runtime/value/convert/record.ts create mode 100644 test/runtime/value/convert/recursive.ts create mode 100644 test/runtime/value/convert/regex.ts create mode 100644 test/runtime/value/convert/string.ts create mode 100644 test/runtime/value/convert/symbol.ts create mode 100644 test/runtime/value/convert/tuple.ts create mode 100644 test/runtime/value/convert/uint8array.ts create mode 100644 test/runtime/value/convert/undefined.ts create mode 100644 test/runtime/value/convert/union.ts create mode 100644 test/runtime/value/convert/unknown.ts create mode 100644 test/runtime/value/convert/void.ts create mode 100644 test/runtime/value/create/bigint.ts create mode 100644 test/runtime/value/create/composite.ts create mode 100644 test/runtime/value/create/not.ts rename test/runtime/value/create/{rec.ts => recursive.ts} (100%) create mode 100644 test/runtime/value/create/ref.ts create mode 100644 test/runtime/value/create/symbol.ts rename test/runtime/{ => value}/hash/hash.ts (69%) rename test/runtime/{ => value}/hash/index.ts (100%) create mode 100644 test/static/bigint.ts create mode 100644 test/static/composite.ts create mode 100644 test/static/exclude.ts create mode 100644 test/static/extract.ts create mode 100644 test/static/not.ts create mode 100644 test/static/symbol.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d64a14027..a07597518 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,4 +20,4 @@ jobs: - name: Build Library run: npm run build - name: Test Library - run: npm run test + run: npm run test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2c085d1d2..adfbc990e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules target +dist diff --git a/.vscode/settings.json b/.vscode/settings.json index a9232115d..df318270b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,6 @@ "node_modules": true, "package-lock.json": true }, - "typescript.tsdk": "node_modules\\typescript\\lib", - "editor.suggest.showStatusBar": false + "editor.suggest.showStatusBar": false, + "typescript.tsdk": "node_modules\\typescript\\lib" } \ No newline at end of file diff --git a/benchmark/compression/module/typebox-conditional.ts b/benchmark/compression/module/typebox-conditional.ts deleted file mode 100644 index 3a12edfa2..000000000 --- a/benchmark/compression/module/typebox-conditional.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Conditional } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' - -const T = Conditional.Extends(Type.String(), Type.String(), Type.String(), Type.String()) diff --git a/benchmark/compression/module/typebox-custom.ts b/benchmark/compression/module/typebox-custom.ts deleted file mode 100644 index 8620c02f1..000000000 --- a/benchmark/compression/module/typebox-custom.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Custom } from 'src/custom/index' -import { Type } from '@sinclair/typebox' - -Custom.Set('custom', (value) => true) diff --git a/benchmark/compression/module/typebox-errors.ts b/benchmark/compression/module/typebox-errors.ts new file mode 100644 index 000000000..883bf9614 --- /dev/null +++ b/benchmark/compression/module/typebox-errors.ts @@ -0,0 +1,3 @@ +import { ValueErrorType } from '@sinclair/typebox/errors' + +console.log(ValueErrorType) diff --git a/benchmark/compression/module/typebox-format.ts b/benchmark/compression/module/typebox-format.ts deleted file mode 100644 index 360a2ce85..000000000 --- a/benchmark/compression/module/typebox-format.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Format } from 'src/format' -import { Type } from '@sinclair/typebox' - -Format.Set('custom', (value) => true) diff --git a/benchmark/compression/module/typebox-guard.ts b/benchmark/compression/module/typebox-guard.ts deleted file mode 100644 index 9c453c2d5..000000000 --- a/benchmark/compression/module/typebox-guard.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { TypeGuard } from '@sinclair/typebox/guard' -import { Type } from '@sinclair/typebox' - -const T = TypeGuard.TSchema(Type.String()) diff --git a/benchmark/compression/module/typebox-hash.ts b/benchmark/compression/module/typebox-hash.ts deleted file mode 100644 index 94c5787e1..000000000 --- a/benchmark/compression/module/typebox-hash.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { ValueHash } from '@sinclair/typebox/hash' - -const H = ValueHash.Create(1) diff --git a/benchmark/measurement/module/cases.ts b/benchmark/measurement/module/cases.ts index fb88ceb36..e59f281f2 100644 --- a/benchmark/measurement/module/cases.ts +++ b/benchmark/measurement/module/cases.ts @@ -1,42 +1,40 @@ import { Type } from '@sinclair/typebox' export namespace Cases { - export const Number = Type.Number() + export const Literal_String = Type.Literal('hello') - export const String = Type.String() + export const Literal_Number = Type.Literal(1) + + export const Literal_Boolean = Type.Literal(true) - export const Boolean = Type.Boolean() + export const Primitive_Number = Type.Number() - export const Null = Type.Null() + export const Primitive_String = Type.String() - export const RegEx = Type.RegEx(/foo/, { default: 'foo' }) + export const Primitive_String_Pattern = Type.RegEx(/foo/, { default: 'foo' }) - export const ObjectA = Type.Object({ - p0: Type.String(), - p1: Type.Number(), - p2: Type.Number(), - p3: Type.Array(Type.Number(), { minItems: 4 }), - p4: Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }), - p5: Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String(), + export const Primitive_Boolean = Type.Boolean() + + export const Primitive_Null = Type.Null() + + export const Object_Unconstrained = Type.Object({ + number: Type.Number(), + negNumber: Type.Number(), + maxNumber: Type.Number(), + string: Type.String(), + longString: Type.String(), + boolean: Type.Boolean(), + deeplyNested: Type.Object({ + foo: Type.String(), + num: Type.Number(), + bool: Type.Boolean(), }), }) - export const ObjectB = Type.Object(ObjectA.properties, { + export const Object_Constrained = Type.Object(Object_Unconstrained.properties, { additionalProperties: false, }) - - export const Tuple = Type.Tuple([Type.String(), Type.Number(), Type.Boolean()]) - - export const Union = Type.Union([Type.Object({ x: Type.Number(), y: Type.Number() }), Type.Object({ a: Type.String(), b: Type.String() })], { default: { a: 'a', b: 'b' } }) - - export const Recursive = Type.Recursive( + export const Object_Recursive = Type.Recursive( (Recursive) => Type.Object({ id: Type.String(), @@ -75,9 +73,32 @@ export namespace Cases { }, ) - export const Vector4 = Type.Tuple([Type.Number(), Type.Number(), Type.Number(), Type.Number()]) - - export const Matrix4 = Type.Array(Type.Array(Type.Number()), { + // prettier-ignore + export const Tuple_Primitive = Type.Tuple([ + Type.String(), + Type.Number(), + Type.Boolean() + ]) + // prettier-ignore + export const Tuple_Object = Type.Tuple([ + Type.Object({ x: Type.Number(), y: Type.Number() }), + Type.Object({ a: Type.String(), b: Type.String() }) + ]) + // prettier-ignore + export const Composite_Intersect = Type.Intersect([ + Type.Object({ x: Type.Number(), y: Type.Number() }), + Type.Object({ a: Type.String(), b: Type.String() }) + ], { default: { x: 1, y: 2, a: 'a', b: 'b' } }) + + // prettier-ignore + export const Composite_Union = Type.Union([ + Type.Object({ x: Type.Number(), y: Type.Number() }), + Type.Object({ a: Type.String(), b: Type.String() }) + ], { default: { a: 'a', b: 'b' } }) + + export const Math_Vector4 = Type.Tuple([Type.Number(), Type.Number(), Type.Number(), Type.Number()]) + + export const Math_Matrix4 = Type.Array(Type.Array(Type.Number()), { default: [ [1, 0, 0, 0], [0, 1, 0, 0], @@ -86,29 +107,27 @@ export namespace Cases { ], }) - export const Literal_String = Type.Literal('foo') + export const Array_Primitive_Number = Type.Array(Type.Number(), { minItems: 4 }) - export const Literal_Number = Type.Literal(1) - - export const Literal_Boolean = Type.Literal(true) + export const Array_Primitive_String = Type.Array(Type.String(), { minItems: 4 }) - export const Array_Number = Type.Array(Type.Number(), { minItems: 16 }) + export const Array_Primitive_Boolean = Type.Array(Type.Boolean(), { minItems: 4 }) - export const Array_String = Type.Array(Type.String(), { minItems: 16 }) + export const Array_Object_Unconstrained = Type.Array(Object_Unconstrained, { minItems: 4 }) - export const Array_Boolean = Type.Array(Type.Boolean(), { minItems: 16 }) + export const Array_Object_Constrained = Type.Array(Object_Constrained, { minItems: 4 }) - export const Array_ObjectA = Type.Array(ObjectA, { minItems: 16 }) + export const Array_Object_Recursive = Type.Array(Object_Recursive, { minItems: 4 }) - export const Array_ObjectB = Type.Array(ObjectB, { minItems: 16 }) + export const Array_Tuple_Primitive = Type.Array(Tuple_Primitive, { minItems: 4 }) - export const Array_Tuple = Type.Array(Tuple, { minItems: 16 }) + export const Array_Tuple_Object = Type.Array(Tuple_Object, { minItems: 4 }) - export const Array_Union = Type.Array(Union, { minItems: 16 }) + export const Array_Composite_Intersect = Type.Array(Composite_Intersect, { minItems: 4 }) - export const Array_Recursive = Type.Array(Recursive, { minItems: 16 }) + export const Array_Composite_Union = Type.Array(Composite_Union, { minItems: 4 }) - export const Array_Vector4 = Type.Array(Vector4, { minItems: 16 }) + export const Array_Math_Vector4 = Type.Array(Math_Vector4, { minItems: 4 }) - export const Array_Matrix4 = Type.Array(Matrix4, { minItems: 16 }) + export const Array_Math_Matrix4 = Type.Array(Math_Matrix4, { minItems: 4 }) } diff --git a/benchmark/measurement/module/check.ts b/benchmark/measurement/module/check.ts index 3b8f975a4..a6e2e4324 100644 --- a/benchmark/measurement/module/check.ts +++ b/benchmark/measurement/module/check.ts @@ -1,8 +1,7 @@ import { Cases } from './cases' import { Benchmark } from './benchmark' import { TypeCompiler } from '@sinclair/typebox/compiler' -import { TypeGuard } from '@sinclair/typebox/guard' -import { TSchema } from '@sinclair/typebox' +import { TSchema, TypeGuard } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' import Ajv from 'ajv' diff --git a/benchmark/measurement/module/compile.ts b/benchmark/measurement/module/compile.ts index 1dd039601..98b61c006 100644 --- a/benchmark/measurement/module/compile.ts +++ b/benchmark/measurement/module/compile.ts @@ -1,15 +1,14 @@ import { Cases } from './cases' import { Benchmark } from './benchmark' import { TypeCompiler } from '@sinclair/typebox/compiler' -import { TypeGuard } from '@sinclair/typebox/guard' -import { TSchema } from '@sinclair/typebox' +import { TSchema, TypeGuard } from '@sinclair/typebox' import Ajv from 'ajv' const ajv = new Ajv() // ensure single instance export namespace CompileBenchmark { function Measure(type: string, schema: T) { - const iterations = 2000 + const iterations = 1000 console.log('CompileBenchmark.Measure(', type, ')') // ------------------------------------------------------------------------------- // Note: Ajv caches schemas by reference. To ensure we measure actual @@ -28,7 +27,7 @@ export namespace CompileBenchmark { // track duplicate $id (resulting in compile error). It is not possible to ammend // recursive $id's without potentially biasing results, so we omit on this case. // ------------------------------------------------------------------------------- - if (type === 'Recursive' || type === 'Array_Recursive') continue + if (type === 'Object_Recursive' || type === 'Array_Object_Recursive') continue yield Measure(type, schema) } diff --git a/benchmark/measurement/module/index.ts b/benchmark/measurement/module/index.ts index 5b6d43480..65b47f716 100644 --- a/benchmark/measurement/module/index.ts +++ b/benchmark/measurement/module/index.ts @@ -9,7 +9,7 @@ export function present(results: Result[]) { if (result.value) { return { ...acc, - [result.type.padStart(16, ' ')]: { + [result.type.padEnd(26, ' ')]: { Iterations: result.compiler.iterations, ValueCheck: `${result.value.completed} ms`.padStart(10), Ajv: `${result.ajv.completed} ms`.padStart(10), @@ -20,7 +20,7 @@ export function present(results: Result[]) { } else { return { ...acc, - [result.type.padStart(16, ' ')]: { + [result.type.padEnd(26, ' ')]: { Iterations: result.compiler.iterations, Ajv: `${result.ajv.completed} ms`.padStart(10), TypeCompiler: `${result.compiler.completed} ms`.padStart(10), diff --git a/changelog.md b/changelog.md deleted file mode 100644 index fa1f19caf..000000000 --- a/changelog.md +++ /dev/null @@ -1,410 +0,0 @@ -## [0.25.24](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) - -Updates: - -- [331](https://github.com/sinclairzx81/typebox/pull/331) Implements an additional check specific to property types of `required & undefined`. This to ensure the property key exists when the property value extends `undefined`. -- [331](https://github.com/sinclairzx81/typebox/pull/331) Documentation updates for Ajv and TypeCompiler - -Additional: - -- [331](https://github.com/sinclairzx81/typebox/pull/331) Remove unusued recursive code paths for create and cast. - -## [0.25.23](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) - -Updates: - -- [324](https://github.com/sinclairzx81/typebox/pull/324) TypeScript Language Service now presents JSDoc comments when inferring static object properties. (IntelliSense) -- [325](https://github.com/sinclairzx81/typebox/pull/325) Additional property inference optimizations. - -Additional: - -- Huge thank you to Github user [stevezhu](https://github.com/stevezhu) for these excellent contributions. - - -## [0.25.22](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.22) - -Updates: - -- [323](https://github.com/sinclairzx81/typebox/pull/323) adds compiler support for UTF-16 (unicode) characters for schema identifiers. - -## [0.25.18](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.18) - -Updates: - -- [307](https://github.com/sinclairzx81/typebox/pull/307) implements date conversion when casting values with `Value.Cast(Type.Date(), ...)`. Castable values include numbers (interpretted as timestamps) and iso8601 string values. Uncastable values will result in dates with values of `1970-01-01T00:00:00.000Z`. This version also includes more robust checks for Dates initialized with invalid values. - -## [0.25.11](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.11) - -Updates: - -- [286](https://github.com/sinclairzx81/typebox/pull/286) implements a FNV1A-64 non cryptographic hashing function in TypeBox. This function should not be used in place of cryptographic hashing functions, rather it's purpose is to provide relatively fast, hashing mechanism to assist with checks for arrays with uniqueItems constraints, specifically for cases where the array may contains reference values (such as objects, arrays, Dates and Uint8Array). This function is provided via `Value.Hash()` for convenience as the hash may be useful to generate a numeric identifier for values (with some considerations to React array rendering in absence of key or identifier) - -## [0.25.10](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.10) - -Updates: - -- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance. The schema instance may include additional constraints (such as options) that may be used during the validation process. `Custom.Set('', (schema, value) => { ... })`. - -## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9) - -Updates: - -- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })` which allow the TypeCompiler and Value API's to make use of user defined validation logic. - -## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) - -Updates: - -- [271](https://github.com/sinclairzx81/typebox/pull/271) Adds a new non-standard `Type.Date()` type. This type joins the existing `Type.UInt8Array()` as a promoted extended type used to represent core JavaScript primitives. It's inclusion was prompted by end user requirements to validate Date objects prior to writing them to Date supported API's and where serialization of the Date object is handled internally by the API. - -- [271](https://github.com/sinclairzx81/typebox/pull/271) Redesign of Extended Type representations. Extended types been updated to provide external validators (such as Ajv) additional standard proporties to use when defining the custom schema. These properties are `instanceOf` (used for validating a class `object` instances), and `typeOf` (when validating `value` types). Information on configuring Ajv for these properties can be found in the Ajv section of the TypeBox readme. - -## [0.24.49](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.49) - -Updates: - -- [264](https://github.com/sinclairzx81/typebox/pull/264) TypeBox now provides preliminary support for non-boolean `additionalProperties`. This allows existing `TObject` schemas to be augmented with additional properties of a known type. - -Additional: - -- TypeBox provides an additional reference `codegen` module for generating raw JSON Schema from TypeScript types via the TS compiler API. This generator may be used in future tooling. - -## [0.24.44](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.44) - -Updates: -- [189](https://github.com/sinclairzx81/typebox/pull/189) Both `Value.Error(T, value)` and `TypeCheck.Error(value)` now return an iterator for validation errors. -- [191](https://github.com/sinclairzx81/typebox/pull/191) TypeBox now provides a `TypeGuard` API that can be used to check the structural validity of TypeBox type. The TypeGuard can be used in reflection / code generation scenarios to resolve the appropriate inner `TSchema` type while traversing a outer type. -- [197](https://github.com/sinclairzx81/typebox/pull/197) TypeBox now implements conditional runtime type mapping. This functionality is offered as seperate import for the `0.24.0` release but may be provided as standard type in later releases. This API enables `type T = Foo extends Bar ? true : false` conditional checks to be implemented at runtime. This API also provides the `Exclude` and `Extract` utility types which are implemented through conditional types in TypeScript. -- [199](https://github.com/sinclairzx81/typebox/pull/199) TypeBox now provides better support for varidiac function and constructor signatures. Currently Variadics are mapped through `Tuple` types. -- [200](https://github.com/sinclairzx81/typebox/pull/200) The types `TPick` and `TOmit` now support types of `TUnion[]>` to be used to select properties. Additionally, `KeyOf` now returns `TUnion[]>`, allowing `KeyOf` schemas to be passed to `TPick` and `TOmit`. -- [214](https://github.com/sinclairzx81/typebox/pull/214) TypeBox now provides better support for i18n. To achieve this, TypeBox includes fixed mappable error codes on the `ValueError` type. These codes can be used by external implementors to create localized error messages. TypeBox may include localized error codes as an optional import in future releases. -- [288](https://github.com/sinclairzx81/typebox/pull/228) TypeBox now allows users to implement custom string validator formats. These formats are internally shared between the `Value` and `TypeCompiler` API's. TypeBox does not currently provide any built in formats, however the standard expected set (email, uuid, uri, etc) may be provided via optional import (inline with ajv-formats usage) -- [229](https://github.com/sinclairzx81/typebox/pull/229) The `Value.Cast()` function now implements automatic coersion of string, number and boolean types. -- [231](https://github.com/sinclairzx81/typebox/pull/231) TypeBox provides a new `Value.Diff()` and `Value.Patch()` utility API for JavaScript values. This API is intended to provide a basis for the efficient transmission of state updates across a network. This API can diff any JavaScript value (typed or untyped) but is recommended to be used in conjunction with a formal static type. -- [236](https://github.com/sinclairzx81/typebox/pull/236) TypeBox now implements the `TNever` type. This type is analogous to TypeScript's `never` type and is used in instances a composition results in a non-reconcilable type. Currently this type is implemented for empty `TUnion<[]>` types only. Future releases may utilize this type for planned updates to `TIntersect` (for example `string & number` resolves to `never`) -- [241](https://github.com/sinclairzx81/typebox/pull/241) [247](https://github.com/sinclairzx81/typebox/pull/247) TypeBox now exposes a ValuePointer API that can be used to mutate a value via an RFC6901 JSON Pointer. Previously this functionality was internally used by `Value.Diff()` and `Value.Patch()` functions but is now offered as an optional import for implementations that need to update values manually through pointer references. - -Additional: - -- This project now includes two reference code generation utilities that can be used in custom build tooling. The first is `TypeScriptCodeGen` which will remap TypeScript `interface` and `type` definitions to TypeBox types. The second is `TypeBoxCodeGen` which will map existing TypeBox types into TypeScript type definitions. These implementations are not expected to be part of the TypeBox package, but users are free to clone and enhance them in their existing tool chains. Reference implementations can be found https://github.com/sinclairzx81/typebox/tree/master/codegen - - - -## [0.24.15](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.15) - -Added: -- `Conditional.Extends(...)` This enables TypeBox to conditionally map types inline with TypeScripts structural equivelence checks. Tested against TypeScript 4.7.4. -- `Conditional.Extract(...)` Which analogs TypeScripts `Extract<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) -- `Conditional.Exclude(...)` Which analogs TypeScripts `Exclude<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) -- `Type.Parameters(...)` Returns the parameters of a `TFunction` as a `TTuple` -- `Type.ReturnType(...)` Returns the return type schema of a `TFunction` -- `Type.ConstructorParameters(...)` Returns the parameters of a `TConstructor` as a `TTuple` -- `Type.InstanceType(...)` Returns the instance type schema of a `TConstructor` - -## [0.24.8](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.8) - -Added: -- `Value.Cast(T, value)` structurally casts a value into another form while retaining information within the original value. -- `Value.Check(T, value)` provides slow dynamic type checking for values. For performance, one should consider the `TypeCompiler` or `Ajv` validator. -- `Value.Errors(T, value)` returns an iterable iterator errors found in a given value. - -## [0.24.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.6) - -Added: - -- TypeBox now offers a `TypeGuard` module for structurally checking TypeBox schematics. This module can be used in runtime type reflection scenarios where it's helpful to test a schema is of a particular form. This module can be imported under the `@sinclair/typebox/guard` import path. - -Example: - -```typescript -import { TypeGuard } from '@sinclair/typebox/guard' - -const T: any = {} // T is any - -const { type } = T // unsafe: type is any - -if(TypeGuard.TString(T)) { - - const { type } = T // safe: type is 'string' -} - -``` - -## [0.24.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.0) - -Changes: - -- The `kind` and `modifier` keywords are now expressed as symbol keys. This change allows AJV to leverage TypeBox schemas directly without explicit configuration of `kind` and `modifier` in strict mode. -- `Type.Intersect([...])` now returns a composite `TObject` instead of a `allOf` schema representation. This change allows intersected types to be leveraged in calls to `Omit`, `Pick`, `Partial`, `Required`. -- `Type.Void(...)` now generates a `{ type: null }` schema representation. This is principally used for RPC implementations where a RPC target function needs to respond with a serializable value for `void` return. -- `Type.Rec(...)` renamed to `Type.Recursive(...)` and now supports non-mutual recursive type inference. - -Added: - -- `Type.Unsafe(...)`. This type enables custom schema representations whose static type is informed by generic type T. -- `Type.Uint8Array(...)`. This is a non-standard schema that can be configured on AJV to enable binary buffer range validation. -- Added optional extended `design` property on all schema options. This property can be used to specify design time metadata when rendering forms. - -Compiler: - -- TypeBox now provides an optional experimental type compiler that can be used to validate types without AJV. This compiler is not a standard JSON schema compiler and will only compile TypeBox's known schema representations. For full JSON schema validation, AJV should still be the preference. This compiler is a work in progress. - -Value: - -- TypeBox now provides a value generator that can generate default values from TypeBox types. - -Breaking Changes: - -- `Type.Intersect(...)` is constrained to accept types of `TObject` only. -- `Type.Namespace(...)` has been removed. -- The types `TUnion`, `TEnum`, `KeyOf` and `TLiteral[]` are all now expressed via `allOf`. For Open API users, Please consider `Type.Unsafe()` to express `enum` string union representations. Documentation on using `Type.Unsafe()` can be found [here](https://github.com/sinclairzx81/typebox#Unsafe-Types) - -## [0.23.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.3) - -Updates: - -- Fix: Rename BoxKind to NamespaceKind - -## [0.23.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.1) - -Updates: - -- The `Type.KeyOf(...)` type can now accept references of `Type.Ref(TObject)` - -## [0.23.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.0) - -Updates: - -- The types `Type.Namespace(...)` and `Type.Ref(...)` are promoted to `Standard`. -- TypeBox now includes an additional type named `TRef<...>` that is returned on calls to `Type.Ref(...)`. The `TRef<...>` includes a new `RefKind` symbol for introspection of the reference type. -- TypeBox now maintains an internal dictionary of all schemas passed that contain an `$id` property. This dictionary is checked whenever a user attempts to reference a type and will throw if attempting to reference a target schema with no `$id`. -- The types `Type.Partial(...)`, `Type.Required(...)`, `Type.Omit()` and `Type.Pick(...)` now support reference types. Note that when using these functions with references, TypeBox will replicate the source schema and apply the nessasary modifiers to the replication. - -## [0.22.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.22.0) - -Updates: - -- The type `TSchema` is now expressed as an HKT compatible interface. All types now extend the `TSchema` interface and are themselves also expressed as interfaces. This work was undertaken to explore recursive type aliases in future releases. -- The phantom property `_infer` has been renamed to `$static`. Callers should not interact with this property as it will always be `undefined` and used exclusively for optimizing type inference in TypeScript 4.5 and above. -- TypeBox re-adds the feature to deeply introspect schema properties. This feature was temporarily removed on the `0.21.0` update to resolve deep instantiation errors on TypeScript 4.5. -- The `Type.Box(...)` and `Type.Rec(...)` functions internally rename the property `definitions` to `$defs` inline with JSON schema draft 2019-09 conventions. Reference [here](https://opis.io/json-schema/2.x/definitions.html). - -## [0.21.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.2) - -Updates: - -- TypeBox now correctly infers for nested union and intersect types. - -Before - -```typescript -const A = Type.Object({ a: Type.String() }) -const B = Type.Object({ b: Type.String() }) -const C = Type.Object({ c: Type.String() }) -const T = Type.Intersect([A, Type.Union([B, C])]) - -// type T = { a: string } & { b: string } & { c: string } -``` -After - -```typescript -const A = Type.Object({ a: Type.String() }) -const B = Type.Object({ b: Type.String() }) -const C = Type.Object({ c: Type.String() }) -const T = Type.Intersect([A, Type.Union([B, C])]) - -// type T = { a: string } & ({ b: string } | { c: string }) -``` - -## [0.21.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.0) - -Updates: - -- TypeBox static inference has been updated inline with additional inference constraints added in TypeScript 4.5. All types now include a phantom `_infer` property which contains the inference TS type for a given schema. The type of this property is inferred at the construction of the schema, and referenced directly via `Static`. -- `Type.Box(...)` has been renamed to `Type.Namespace(...)` to draw an analogy with XML's `xmlns` XSD types. - -## [0.20.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.1) - -Updates: - -- TypeBox mandates TypeScript compiler version `4.3.5` and above. - -## [0.20.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.0) - -Updates: - -- Function `Type.Rec(...)` signature change. -- Minor documentation updates. - -Notes: - -The `Type.Rec(...)` function signature has been changed to allow passing the `$id` as a custom option. This is to align `Type.Rec(...)` with other functions that accept `$id` as an option. `Type.Rec(...)` can work with or without an explicit `$id`, but it is recommend to specify one if the recursive type is nested in an outer schema. - -```typescript -const Node = Type.Rec(Self => Type.Object({ - id: Type.String(), - nodes: Type.Array(Self) -}), { $id: 'Node' }) -``` - -## [0.19.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.19.0) - -Updates: - -- Function `Type.Box(...)` removes `$id` parameter as first argument. -- Function `Type.Ref(...)` is now overloaded to support referencing `Type.Box(...)` and `TSchema`. - -Notes: - -This update changes the signature of `Type.Box(...)` and removes the explicit `$id` passing on the first parameter. The `$id` must be passed as an option if the caller wants to reference that type. - -```typescript -const T = Type.String({ $id: 'T' }) - -const B = Type.Box({ T }, { $id: 'B' }) - -const R1 = Type.Ref(T) // const R1 = { $ref: 'T' } - -const R2 = Type.Ref(B, 'T') // const R2 = { $ref: 'B#/definitions/T' } -``` - -## [0.18.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.1) - -- Function `Type.Enum(...)` now expressed with `anyOf`. This to remove the `allowUnionTypes` configuration required to use `enum` with in AJV strict. -- Function `Type.Rec(...)` now takes a required `$id` as the first parameter. -- Function `Type.Strict(...)` no longer includes a `$schema`. Callers can now optionally pass `CustomOptions` on `Type.Strict(...)` - -## [0.18.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.0) - -Changes: - -- Function `Type.Intersect(...)` is now implemented with `allOf` and constrained with `unevaluatedProperties` (draft `2019-09`) -- Function `Type.Dict(...)` has been deprecated and replaced with `Type.Record(...)`. -- Function `Type.Strict(...)` now includes the `$schema` property referencing the `2019-09` draft. - -### Type.Intersect(...) - -TypeBox now targets JSON schema draft `2019-09` for expressing `Type.Intersect(...)`. This is now expressed via `allOf` with additionalProperties constrained with `unevaluatedProperties`. Note that `unevaluatedProperties` is a feature of the `2019-09` specification. - -### Type.Record(K, V) - -TypeBox has deprecated `Type.Dict(...)` in favor of the more generic `Type.Record(...)`. Where as `Type.Dict(...)` was previously expressed with `additionalProperties: { ... }`, `Type.Record(...)` is expressed with `patternProperties` and supports both `string` and `number` indexer keys. Additionally, `Type.Record(...)` supports string union arguments. This is analogous to TypeScript's utility record type `Record<'a' | 'b' | 'c', T>`. - -## [0.17.7](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.7) - -Changes: - -- Added optional `$id` argument on `Type.Rec()`. -- Documentation updates. - -## [0.17.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.6) - -Changes: - -- Added `Type.Rec(...)` function. - -Notes: - -This update introduces the `Type.Rec()` function for enabling Recursive Types. Please note that due to current inference limitations in TypeScript, TypeBox is unable to infer the type and resolves inner types to `any`. - -This functionality enables for complex self referential schemas to be composed. The following creates a binary expression syntax node with the expression self referential for left and right oprands. - -```typescript -const Operator = Type.Union([ - Type.Literal('+'), - Type.Literal('-'), - Type.Literal('/'), - Type.Literal('*') -]) - -type Expression = Static - -// Defines a self referencing type. -const Expression = Type.Rec(Self => Type.Object({ - left: Type.Union([Self, Type.Number()]), - right: Type.Union([Self, Type.Number()]), - operator: Operator -})) - -function evaluate(expression: Expression): number { - const left = typeof expression.left !== 'number' - ? evaluate(expression.left as Expression) // assert as Expression - : expression.left - const right = typeof expression.right !== 'number' - ? evaluate(expression.right as Expression) // assert as Expression - : expression.right - switch(expression.operator) { - case '+': return left + right - case '-': return left - right - case '*': return left * right - case '/': return left / right - } -} - -const result = evaluate({ - left: { - left: 10, - operator: '*', - right: 4, - }, - operator: '+', - right: 2, -}) // -> 42 -``` - -This functionality is flagged as `EXPERIMENTAL` and awaits community feedback. - -## [0.17.4](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.4) - -Changes: - -- Added `Type.Box()` and `Type.Ref()` functions. - -Notes: - -This update provides the `Type.Box()` function to enable common related schemas to grouped under a common namespace; typically expressed as a `URI`. This functionality is primarily geared towards allowing one to define a common set of domain objects that may be shared across application domains running over a network. The `Type.Box()` is intended to be an analog to `XML` `xmlns` namespacing. - -The `Type.Ref()` function is limited to referencing from boxes only. The following is an example. - -```typescript -// Domain objects for the fruit service. -const Fruit = Type.Box('https://fruit.domain.com', { - Apple: Type.Object({ ... }), - Orange: Type.Object({ ... }), -}) - -// An order referencing types of the fruit service. -const Order = Type.Object({ - id: Type.String(), - quantity: Type.Number(), - item: Type.Union([ - Type.Ref(Fruit, 'Apple'), - Type.Ref(Fruit, 'Orange') - ]) -}) -``` -> Note: As of this release, the `Type.Omit()`, `Type.Pick()`, `Type.Partial()`, `Type.Readonly()` and `Type.Intersect()` functions do not work with Reference Types. This may change in later revisions. - -For validation using `Ajv`, its possible to apply the `Box` directly as a schema. - -```typescript -ajv.addSchema(Fruit) // makes all boxed types known to Ajv -``` - -This functionality is flagged as `EXPERIMENTAL` and awaits community feedback. - -## [0.17.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.1) - -- Remove default `additionalProperties: false` constraint from all object schemas. - -This update removes the `additionalProperties: false` constraint on all object schemas. This constraint was introduced on `0.16.x` but has resulted in significant downstream problems composing schemas whose types `intersect`. This is due to a JSON schema design principle where constraints should only be added (never removed), and that intersection types may require removal of the `additionalProperties` constraint in some cases, this had resulted in some ambiguity with respect to how TypeBox should handle such intersections. - -This update can also be seen as a precursor towards TypeBox potentially leveraging `unevaluatedProperties` for type intersection in future releases. Implementors should take note that in order to constrain the schema to known properties, one should apply the `additionalProperties: false` as the second argument to `Type.Object({...})`. - -```typescript -const T = Type.Object({ - a: Type.String(), - b: Type.Number() -}, { - additionalProperties: false -}) diff --git a/changelog/0.17.1.md b/changelog/0.17.1.md new file mode 100644 index 000000000..b458c0b15 --- /dev/null +++ b/changelog/0.17.1.md @@ -0,0 +1,15 @@ +## [0.17.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.1) + +- Remove default `additionalProperties: false` constraint from all object schemas. + +This update removes the `additionalProperties: false` constraint on all object schemas. This constraint was introduced on `0.16.x` but has resulted in significant downstream problems composing schemas whose types `intersect`. This is due to a JSON schema design principle where constraints should only be added (never removed), and that intersection types may require removal of the `additionalProperties` constraint in some cases, this had resulted in some ambiguity with respect to how TypeBox should handle such intersections. + +This update can also be seen as a precursor towards TypeBox potentially leveraging `unevaluatedProperties` for type intersection in future releases. Implementers should take note that in order to constrain the schema to known properties, one should apply the `additionalProperties: false` as the second argument to `Type.Object({...})`. + +```typescript +const T = Type.Object({ + a: Type.String(), + b: Type.Number() +}, { + additionalProperties: false +}) \ No newline at end of file diff --git a/changelog/0.17.4.md b/changelog/0.17.4.md new file mode 100644 index 000000000..07a8cf5e4 --- /dev/null +++ b/changelog/0.17.4.md @@ -0,0 +1,38 @@ +## [0.17.4](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.4) + +Changes: + +- Added `Type.Box()` and `Type.Ref()` functions. + +Notes: + +This update provides the `Type.Box()` function to enable common related schemas to grouped under a common namespace; typically expressed as a `URI`. This functionality is primarily geared towards allowing one to define a common set of domain objects that may be shared across application domains running over a network. The `Type.Box()` is intended to be an analog to `XML` `xmlns` namespaces. + +The `Type.Ref()` function is limited to referencing from boxes only. The following is an example. + +```typescript +// Domain objects for the fruit service. +const Fruit = Type.Box('https://fruit.domain.com', { + Apple: Type.Object({ ... }), + Orange: Type.Object({ ... }), +}) + +// An order referencing types of the fruit service. +const Order = Type.Object({ + id: Type.String(), + quantity: Type.Number(), + item: Type.Union([ + Type.Ref(Fruit, 'Apple'), + Type.Ref(Fruit, 'Orange') + ]) +}) +``` +> Note: As of this release, the `Type.Omit()`, `Type.Pick()`, `Type.Partial()`, `Type.Readonly()` and `Type.Intersect()` functions do not work with Reference Types. This may change in later revisions. + +For validation using `Ajv`, its possible to apply the `Box` directly as a schema. + +```typescript +ajv.addSchema(Fruit) // makes all boxed types known to Ajv +``` + +This functionality is flagged as `EXPERIMENTAL` and awaits community feedback. \ No newline at end of file diff --git a/changelog/0.17.6.md b/changelog/0.17.6.md new file mode 100644 index 000000000..291ea5f3e --- /dev/null +++ b/changelog/0.17.6.md @@ -0,0 +1,56 @@ +## [0.17.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.6) + +Changes: + +- Added `Type.Rec(...)` function. + +Notes: + +This update introduces the `Type.Rec()` function for enabling Recursive Types. Please note that due to current inference limitations in TypeScript, TypeBox is unable to infer the type and resolves inner types to `any`. + +This functionality enables for complex self referential schemas to be composed. The following creates a binary expression syntax node with the expression self referential for left and right operands. + +```typescript +const Operator = Type.Union([ + Type.Literal('+'), + Type.Literal('-'), + Type.Literal('/'), + Type.Literal('*') +]) + +type Expression = Static + +// Defines a self referencing type. +const Expression = Type.Rec(Self => Type.Object({ + left: Type.Union([Self, Type.Number()]), + right: Type.Union([Self, Type.Number()]), + operator: Operator +})) + +function evaluate(expression: Expression): number { + const left = typeof expression.left !== 'number' + ? evaluate(expression.left as Expression) // assert as Expression + : expression.left + const right = typeof expression.right !== 'number' + ? evaluate(expression.right as Expression) // assert as Expression + : expression.right + switch(expression.operator) { + case '+': return left + right + case '-': return left - right + case '*': return left * right + case '/': return left / right + } +} + +const result = evaluate({ + left: { + left: 10, + operator: '*', + right: 4, + }, + operator: '+', + right: 2, +}) // -> 42 +``` + +This functionality is flagged as `EXPERIMENTAL` and awaits community feedback. \ No newline at end of file diff --git a/changelog/0.18.0.md b/changelog/0.18.0.md new file mode 100644 index 000000000..88a7d4f2e --- /dev/null +++ b/changelog/0.18.0.md @@ -0,0 +1,22 @@ +## [0.18.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.0) + +Changes: + +- Function `Type.Intersect(...)` is now implemented with `allOf` and constrained with `unevaluatedProperties` (draft `2019-09`) +- Function `Type.Dict(...)` has been deprecated and replaced with `Type.Record(...)`. +- Function `Type.Strict(...)` now includes the `$schema` property referencing the `2019-09` draft. + +### Type.Intersect(...) + +TypeBox now targets JSON schema draft `2019-09` for expressing `Type.Intersect(...)`. This is now expressed via `allOf` with additionalProperties constrained with `unevaluatedProperties`. Note that `unevaluatedProperties` is a feature of the `2019-09` specification. + +### Type.Record(K, V) + +TypeBox has deprecated `Type.Dict(...)` in favor of the more generic `Type.Record(...)`. Where as `Type.Dict(...)` was previously expressed with `additionalProperties: { ... }`, `Type.Record(...)` is expressed with `patternProperties` and supports both `string` and `number` indexer keys. Additionally, `Type.Record(...)` supports string union arguments. This is analogous to TypeScript's utility record type `Record<'a' | 'b' | 'c', T>`. + +## [0.17.7](https://www.npmjs.com/package/@sinclair/typebox/v/0.17.7) + +Changes: + +- Added optional `$id` argument on `Type.Rec()`. +- Documentation updates. \ No newline at end of file diff --git a/changelog/0.18.1.md b/changelog/0.18.1.md new file mode 100644 index 000000000..1a0bbe7f9 --- /dev/null +++ b/changelog/0.18.1.md @@ -0,0 +1,5 @@ +## [0.18.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.18.1) + +- Function `Type.Enum(...)` now expressed with `anyOf`. This to remove the `allowUnionTypes` configuration required to use `enum` with in AJV strict. +- Function `Type.Rec(...)` now takes a required `$id` as the first parameter. +- Function `Type.Strict(...)` no longer includes a `$schema`. Callers can now optionally pass `CustomOptions` on `Type.Strict(...)` \ No newline at end of file diff --git a/changelog/0.19.0.md b/changelog/0.19.0.md new file mode 100644 index 000000000..377ccdbff --- /dev/null +++ b/changelog/0.19.0.md @@ -0,0 +1,20 @@ +## [0.19.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.19.0) + +Updates: + +- Function `Type.Box(...)` removes `$id` parameter as first argument. +- Function `Type.Ref(...)` is now overloaded to support referencing `Type.Box(...)` and `TSchema`. + +Notes: + +This update changes the signature of `Type.Box(...)` and removes the explicit `$id` passing on the first parameter. The `$id` must be passed as an option if the caller wants to reference that type. + +```typescript +const T = Type.String({ $id: 'T' }) + +const B = Type.Box({ T }, { $id: 'B' }) + +const R1 = Type.Ref(T) // const R1 = { $ref: 'T' } + +const R2 = Type.Ref(B, 'T') // const R2 = { $ref: 'B#/definitions/T' } +``` \ No newline at end of file diff --git a/changelog/0.20.0.md b/changelog/0.20.0.md new file mode 100644 index 000000000..80a01da97 --- /dev/null +++ b/changelog/0.20.0.md @@ -0,0 +1,17 @@ +## [0.20.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.0) + +Updates: + +- Function `Type.Rec(...)` signature change. +- Minor documentation updates. + +Notes: + +The `Type.Rec(...)` function signature has been changed to allow passing the `$id` as a custom option. This is to align `Type.Rec(...)` with other functions that accept `$id` as an option. `Type.Rec(...)` can work with or without an explicit `$id`, but it is recommend to specify one if the recursive type is nested in an outer schema. + +```typescript +const Node = Type.Rec(Self => Type.Object({ + id: Type.String(), + nodes: Type.Array(Self) +}), { $id: 'Node' }) +``` \ No newline at end of file diff --git a/changelog/0.20.1.md b/changelog/0.20.1.md new file mode 100644 index 000000000..2dfec120f --- /dev/null +++ b/changelog/0.20.1.md @@ -0,0 +1,5 @@ +## [0.20.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.20.1) + +Updates: + +- TypeBox mandates TypeScript compiler version `4.3.5` and above. \ No newline at end of file diff --git a/changelog/0.21.0.md b/changelog/0.21.0.md new file mode 100644 index 000000000..31df5f78f --- /dev/null +++ b/changelog/0.21.0.md @@ -0,0 +1,7 @@ +## [0.21.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.0) + +Updates: + +- TypeBox static inference has been updated inline with additional inference constraints added in TypeScript 4.5. All types now include a phantom `_infer` property which contains the inference TS type for a given schema. The type of this property is inferred at the construction of the schema, and referenced directly via `Static`. +- `Type.Box(...)` has been renamed to `Type.Namespace(...)` to draw an analogy with XML's `xmlns` XSD types. + diff --git a/changelog/0.21.2.md b/changelog/0.21.2.md new file mode 100644 index 000000000..12d31df53 --- /dev/null +++ b/changelog/0.21.2.md @@ -0,0 +1,26 @@ +## [0.21.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.21.2) + +Updates: + +- TypeBox now correctly infers for nested union and intersect types. + +Before + +```typescript +const A = Type.Object({ a: Type.String() }) +const B = Type.Object({ b: Type.String() }) +const C = Type.Object({ c: Type.String() }) +const T = Type.Intersect([A, Type.Union([B, C])]) + +// type T = { a: string } & { b: string } & { c: string } +``` +After + +```typescript +const A = Type.Object({ a: Type.String() }) +const B = Type.Object({ b: Type.String() }) +const C = Type.Object({ c: Type.String() }) +const T = Type.Intersect([A, Type.Union([B, C])]) + +// type T = { a: string } & ({ b: string } | { c: string }) +``` \ No newline at end of file diff --git a/changelog/0.22.0.md b/changelog/0.22.0.md new file mode 100644 index 000000000..f65eeeb7a --- /dev/null +++ b/changelog/0.22.0.md @@ -0,0 +1,8 @@ +## [0.22.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.22.0) + +Updates: + +- The type `TSchema` is now expressed as an HKT compatible interface. All types now extend the `TSchema` interface and are themselves also expressed as interfaces. This work was undertaken to explore recursive type aliases in future releases. +- The phantom property `_infer` has been renamed to `$static`. Callers should not interact with this property as it will always be `undefined` and used exclusively for optimizing type inference in TypeScript 4.5 and above. +- TypeBox re-adds the feature to deeply introspect schema properties. This feature was temporarily removed on the `0.21.0` update to resolve deep instantiation errors on TypeScript 4.5. +- The `Type.Box(...)` and `Type.Rec(...)` functions internally rename the property `definitions` to `$defs` inline with JSON schema draft 2019-09 conventions. Reference [here](https://opis.io/json-schema/2.x/definitions.html). \ No newline at end of file diff --git a/changelog/0.23.0.md b/changelog/0.23.0.md new file mode 100644 index 000000000..5dce7d99f --- /dev/null +++ b/changelog/0.23.0.md @@ -0,0 +1,8 @@ +## [0.23.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.0) + +Updates: + +- The types `Type.Namespace(...)` and `Type.Ref(...)` are promoted to `Standard`. +- TypeBox now includes an additional type named `TRef<...>` that is returned on calls to `Type.Ref(...)`. The `TRef<...>` includes a new `RefKind` symbol for introspection of the reference type. +- TypeBox now maintains an internal dictionary of all schemas passed that contain an `$id` property. This dictionary is checked whenever a user attempts to reference a type and will throw if attempting to reference a target schema with no `$id`. +- The types `Type.Partial(...)`, `Type.Required(...)`, `Type.Omit()` and `Type.Pick(...)` now support reference types. Note that when using these functions with references, TypeBox will replicate the source schema and apply the necessary modifiers to the replication. \ No newline at end of file diff --git a/changelog/0.23.1.md b/changelog/0.23.1.md new file mode 100644 index 000000000..565f779ba --- /dev/null +++ b/changelog/0.23.1.md @@ -0,0 +1,5 @@ +## [0.23.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.1) + +Updates: + +- The `Type.KeyOf(...)` type can now accept references of `Type.Ref(TObject)` \ No newline at end of file diff --git a/changelog/0.23.3.md b/changelog/0.23.3.md new file mode 100644 index 000000000..bb89450bc --- /dev/null +++ b/changelog/0.23.3.md @@ -0,0 +1,5 @@ +## [0.23.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.23.3) + +Updates: + +- Fix: Rename BoxKind to NamespaceKind \ No newline at end of file diff --git a/changelog/0.24.0.md b/changelog/0.24.0.md new file mode 100644 index 000000000..b433b5827 --- /dev/null +++ b/changelog/0.24.0.md @@ -0,0 +1,28 @@ +## [0.24.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.0) + +Changes: + +- The `kind` and `modifier` keywords are now expressed as symbol keys. This change allows AJV to leverage TypeBox schemas directly without explicit configuration of `kind` and `modifier` in strict mode. +- `Type.Intersect([...])` now returns a composite `TObject` instead of a `allOf` schema representation. This change allows intersected types to be leveraged in calls to `Omit`, `Pick`, `Partial`, `Required`. +- `Type.Void(...)` now generates a `{ type: null }` schema representation. This is principally used for RPC implementations where a RPC target function needs to respond with a serializable value for `void` return. +- `Type.Rec(...)` renamed to `Type.Recursive(...)` and now supports non-mutual recursive type inference. + +Added: + +- `Type.Unsafe(...)`. This type enables custom schema representations whose static type is informed by generic type T. +- `Type.Uint8Array(...)`. This is a non-standard schema that can be configured on AJV to enable binary buffer range validation. +- Added optional extended `design` property on all schema options. This property can be used to specify design time metadata when rendering forms. + +Compiler: + +- TypeBox now provides an optional experimental type compiler that can be used to validate types without AJV. This compiler is not a standard JSON schema compiler and will only compile TypeBox's known schema representations. For full JSON schema validation, AJV should still be the preference. This compiler is a work in progress. + +Value: + +- TypeBox now provides a value generator that can generate default values from TypeBox types. + +Breaking Changes: + +- `Type.Intersect(...)` is constrained to accept types of `TObject` only. +- `Type.Namespace(...)` has been removed. +- The types `TUnion`, `TEnum`, `KeyOf` and `TLiteral[]` are all now expressed via `allOf`. For Open API users, Please consider `Type.Unsafe()` to express `enum` string union representations. Documentation on using `Type.Unsafe()` can be found [here](https://github.com/sinclairzx81/typebox#Unsafe-Types) \ No newline at end of file diff --git a/changelog/0.24.15.md b/changelog/0.24.15.md new file mode 100644 index 000000000..69745b875 --- /dev/null +++ b/changelog/0.24.15.md @@ -0,0 +1,10 @@ +## [0.24.15](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.15) + +Added: +- `Conditional.Extends(...)` This enables TypeBox to conditionally map types inline with TypeScripts structural equivalence checks. Tested against TypeScript 4.7.4. +- `Conditional.Extract(...)` Which analogs TypeScripts `Extract<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) +- `Conditional.Exclude(...)` Which analogs TypeScripts `Exclude<...>` utility type. Additional information [here](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) +- `Type.Parameters(...)` Returns the parameters of a `TFunction` as a `TTuple` +- `Type.ReturnType(...)` Returns the return type schema of a `TFunction` +- `Type.ConstructorParameters(...)` Returns the parameters of a `TConstructor` as a `TTuple` +- `Type.InstanceType(...)` Returns the instance type schema of a `TConstructor` \ No newline at end of file diff --git a/changelog/0.24.44.md b/changelog/0.24.44.md new file mode 100644 index 000000000..e5aac8bde --- /dev/null +++ b/changelog/0.24.44.md @@ -0,0 +1,18 @@ +## [0.24.44](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.44) + +Updates: +- [189](https://github.com/sinclairzx81/typebox/pull/189) Both `Value.Error(T, value)` and `TypeCheck.Error(value)` now return an iterator for validation errors. +- [191](https://github.com/sinclairzx81/typebox/pull/191) TypeBox now provides a `TypeGuard` API that can be used to check the structural validity of TypeBox type. The TypeGuard can be used in reflection / code generation scenarios to resolve the appropriate inner `TSchema` type while traversing a outer type. +- [197](https://github.com/sinclairzx81/typebox/pull/197) TypeBox now implements conditional runtime type mapping. This functionality is offered as separate import for the `0.24.0` release but may be provided as standard type in later releases. This API enables `type T = Foo extends Bar ? true : false` conditional checks to be implemented at runtime. This API also provides the `Exclude` and `Extract` utility types which are implemented through conditional types in TypeScript. +- [199](https://github.com/sinclairzx81/typebox/pull/199) TypeBox now provides better support for variadic function and constructor signatures. Currently variadic types are mapped as `Tuple` types. +- [200](https://github.com/sinclairzx81/typebox/pull/200) The types `TPick` and `TOmit` now support types of `TUnion[]>` to be used to select properties. Additionally, `KeyOf` now returns `TUnion[]>`, allowing `KeyOf` schemas to be passed to `TPick` and `TOmit`. +- [214](https://github.com/sinclairzx81/typebox/pull/214) TypeBox now provides better support for i18n. To achieve this, TypeBox includes fixed mappable error codes on the `ValueError` type. These codes can be used by external implementers to create localized error messages. TypeBox may include localized error codes as an optional import in future releases. +- [288](https://github.com/sinclairzx81/typebox/pull/228) TypeBox now allows users to implement custom string validator formats. These formats are internally shared between the `Value` and `TypeCompiler` API's. TypeBox does not currently provide any built in formats, however the standard expected set (email, uuid, uri, etc) may be provided via optional import (inline with ajv-formats usage) +- [229](https://github.com/sinclairzx81/typebox/pull/229) The `Value.Cast()` function now implements automatic coercion of string, number and Boolean types. +- [231](https://github.com/sinclairzx81/typebox/pull/231) TypeBox provides a new `Value.Diff()` and `Value.Patch()` utility API for JavaScript values. This API is intended to provide a basis for the efficient transmission of state updates across a network. This API can diff any JavaScript value (typed or untyped) but is recommended to be used in conjunction with a formal static type. +- [236](https://github.com/sinclairzx81/typebox/pull/236) TypeBox now implements the `TNever` type. This type is analogous to TypeScript's `never` type and is used in instances a composition results in a non-reconcilable type. Currently this type is implemented for empty `TUnion<[]>` types only. Future releases may utilize this type for planned updates to `TIntersect` (for example `string & number` resolves to `never`) +- [241](https://github.com/sinclairzx81/typebox/pull/241) [247](https://github.com/sinclairzx81/typebox/pull/247) TypeBox now exposes a ValuePointer API that can be used to mutate a value via an RFC6901 JSON Pointer. Previously this functionality was internally used by `Value.Diff()` and `Value.Patch()` functions but is now offered as an optional import for implementations that need to update values manually through pointer references. + +Additional: + +- This project now includes two reference code generation utilities that can be used in custom build tooling. The first is `TypeScriptCodeGen` which will remap TypeScript `interface` and `type` definitions to TypeBox types. The second is `TypeBoxCodeGen` which will map existing TypeBox types into TypeScript type definitions. These implementations are not expected to be part of the TypeBox package, but users are free to clone and enhance them in their existing tool chains. Reference implementations can be found https://github.com/sinclairzx81/typebox/tree/master/codegen \ No newline at end of file diff --git a/changelog/0.24.49.md b/changelog/0.24.49.md new file mode 100644 index 000000000..e0fd0a65e --- /dev/null +++ b/changelog/0.24.49.md @@ -0,0 +1,9 @@ +## [0.24.49](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.49) + +Updates: + +- [264](https://github.com/sinclairzx81/typebox/pull/264) TypeBox now provides preliminary support for non Boolean `additionalProperties`. This allows existing `TObject` schemas to be augmented with additional properties of a known type. + +Additional: + +- TypeBox provides an additional reference `codegen` module for generating raw JSON Schema from TypeScript types via the TS compiler API. This generator may be used in future tooling. \ No newline at end of file diff --git a/changelog/0.24.6.md b/changelog/0.24.6.md new file mode 100644 index 000000000..68477e071 --- /dev/null +++ b/changelog/0.24.6.md @@ -0,0 +1,20 @@ +## [0.24.6](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.6) + +Added: + +- TypeBox now offers a `TypeGuard` module for structurally checking TypeBox schematics. This module can be used in runtime type reflection scenarios where it's helpful to test a schema is of a particular form. This module can be imported under the `@sinclair/typebox/guard` import path. + +Example: + +```typescript +import { TypeGuard } from '@sinclair/typebox/guard' + +const T: any = {} // T is any + +const { type } = T // unsafe: type is any + +if(TypeGuard.TString(T)) { + + const { type } = T // safe: type is 'string' +} +``` \ No newline at end of file diff --git a/changelog/0.24.8.md b/changelog/0.24.8.md new file mode 100644 index 000000000..05f1576d0 --- /dev/null +++ b/changelog/0.24.8.md @@ -0,0 +1,6 @@ +## [0.24.8](https://www.npmjs.com/package/@sinclair/typebox/v/0.24.8) + +Added: +- `Value.Cast(T, value)` structurally casts a value into another form while retaining information within the original value. +- `Value.Check(T, value)` provides slow dynamic type checking for values. For performance, one should consider the `TypeCompiler` or `Ajv` validator. +- `Value.Errors(T, value)` returns an iterator of errors found in a given value. \ No newline at end of file diff --git a/changelog/0.25.0.md b/changelog/0.25.0.md new file mode 100644 index 000000000..5085679b5 --- /dev/null +++ b/changelog/0.25.0.md @@ -0,0 +1,7 @@ +## [0.25.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.0) + +Updates: + +- [271](https://github.com/sinclairzx81/typebox/pull/271) Adds a new non-standard `Type.Date()` type. This type joins the existing `Type.UInt8Array()` as a promoted extended type used to represent core JavaScript primitives. It's inclusion was prompted by end user requirements to validate Date objects prior to writing them to Date supported API's and where serialization of the Date object is handled internally by the API. + +- [271](https://github.com/sinclairzx81/typebox/pull/271) Redesign of Extended Type representations. Extended types been updated to provide external validators (such as Ajv) additional standard proporties to use when defining the custom schema. These properties are `instanceOf` (used for validating a class `object` instances), and `typeOf` (when validating `value` types). Information on configuring AJV for these properties can be found in the AJV section of the TypeBox readme. \ No newline at end of file diff --git a/changelog/0.25.10.md b/changelog/0.25.10.md new file mode 100644 index 000000000..fc1d3d567 --- /dev/null +++ b/changelog/0.25.10.md @@ -0,0 +1,5 @@ +## [0.25.10](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.10) + +Updates: + +- [283](https://github.com/sinclairzx81/typebox/pull/283) Updates the custom type validator callback signature to accept a schema instance. The schema instance may include additional constraints (such as options) that may be used during the validation process. `Custom.Set('', (schema, value) => { ... })`. \ No newline at end of file diff --git a/changelog/0.25.11.md b/changelog/0.25.11.md new file mode 100644 index 000000000..ad5144be0 --- /dev/null +++ b/changelog/0.25.11.md @@ -0,0 +1,5 @@ +## [0.25.11](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.11) + +Updates: + +- [286](https://github.com/sinclairzx81/typebox/pull/286) implements a FNV1A-64 non cryptographic hashing function in TypeBox. This function should not be used in place of cryptographic hashing functions, rather it's purpose is to provide relatively fast, hashing mechanism to assist with checks for arrays with uniqueItems constraints, specifically for cases where the array may contains reference values (such as objects, arrays, Dates and Uint8Array). This function is provided via `Value.Hash()` for convenience as the hash may be useful to generate a numeric identifier for values (with some considerations to React array rendering in absence of key or identifier) \ No newline at end of file diff --git a/changelog/0.25.18.md b/changelog/0.25.18.md new file mode 100644 index 000000000..d762e176f --- /dev/null +++ b/changelog/0.25.18.md @@ -0,0 +1,5 @@ +## [0.25.18](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.18) + +Updates: + +- [307](https://github.com/sinclairzx81/typebox/pull/307) implements date conversion when casting values with `Value.Cast(Type.Date(), ...)`. Castable values include numbers (interpretted as timestamps) and iso8601 string values. UNCASTABLE values will result in dates with values of `1970-01-01T00:00:00.000Z`. This version also includes more robust checks for Dates initialized with invalid values. \ No newline at end of file diff --git a/changelog/0.25.22.md b/changelog/0.25.22.md new file mode 100644 index 000000000..3e9240379 --- /dev/null +++ b/changelog/0.25.22.md @@ -0,0 +1,5 @@ +## [0.25.22](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.22) + +Updates: + +- [323](https://github.com/sinclairzx81/typebox/pull/323) adds compiler support for UTF-16 (unicode) characters for schema identifiers. \ No newline at end of file diff --git a/changelog/0.25.23.md b/changelog/0.25.23.md new file mode 100644 index 000000000..76e93040a --- /dev/null +++ b/changelog/0.25.23.md @@ -0,0 +1,10 @@ +## [0.25.23](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) + +Updates: + +- [324](https://github.com/sinclairzx81/typebox/pull/324) TypeScript Language Service now presents JSDoc comments when inferring static object properties. (IntelliSense) +- [325](https://github.com/sinclairzx81/typebox/pull/325) Additional property inference optimizations. + +Additional: + +- Huge thank you to GITHUB user [stevezhu](https://github.com/stevezhu) for these excellent contributions. \ No newline at end of file diff --git a/changelog/0.25.24.md b/changelog/0.25.24.md new file mode 100644 index 000000000..f45dc03c0 --- /dev/null +++ b/changelog/0.25.24.md @@ -0,0 +1,10 @@ +## [0.25.24](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.23) + +Updates: + +- [331](https://github.com/sinclairzx81/typebox/pull/331) Implements an additional check specific to property types of `required & undefined`. This to ensure the property key exists when the property value extends `undefined`. +- [331](https://github.com/sinclairzx81/typebox/pull/331) Documentation updates for AJV and TypeCompiler + +Additional: + +- [331](https://github.com/sinclairzx81/typebox/pull/331) Remove unused recursive code paths for create and cast. \ No newline at end of file diff --git a/changelog/0.25.9.md b/changelog/0.25.9.md new file mode 100644 index 000000000..c0155a84b --- /dev/null +++ b/changelog/0.25.9.md @@ -0,0 +1,5 @@ +## [0.25.9](https://www.npmjs.com/package/@sinclair/typebox/v/0.25.9) + +Updates: + +- [282](https://github.com/sinclairzx81/typebox/pull/282) TypeBox now supports custom types. These types require the user to specify a custom `[Kind]` string on the type. Custom types can be registered via `Custom.Set('', (value) => { ... })` which allow the TypeCompiler and Value API's to make use of user defined validation logic. \ No newline at end of file diff --git a/changelog/0.26.0.md b/changelog/0.26.0.md new file mode 100644 index 000000000..34b69e876 --- /dev/null +++ b/changelog/0.26.0.md @@ -0,0 +1,394 @@ +## [0.26.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.26.0) + +## Overview + +TypeBox now provides "runtime conditional types" (formally the `Conditional` module) as standard on `Type.*`. Additional updates in this revision include automatic union and intersection unwrap, universal support for utility types, several ergonomic enhancements and additional options for framework integrators. This revision also carries out a number an internal refactorings to reduce the amount of submodule imports. + +Revision 0.26.0 is a milestone release for the TypeBox project and requires a minor semver update. + +## Contents + +- Enhancements + - [Standard Type Builder](#Standard-Type-Builder) + - [Automatic Unwrap for Union and Intersect](#Automatic-Unwrap-for-Union-and-Intersect) + - [Intersect and Union now Compose](#Intersect-and-Union-now-Compose) + - [Runtime Conditional Types](#Runtime-Conditional-Types) + - [Value Convert](#Value-Convert) + - [Error Iterator](#Error-Iterator) + - [Codegen without JIT](#Codegen-without-JIT) + - [Standard Type (Composite)](#Standard-Type-Composite) + - [Standard Type (Not)](#Standard-Type-Not) + - [Extended Type (BigInt)](#Extended-Type-BigInt) + - [Extended Type (Symbol)](#Extended-Type-Symbol) +- Breaking + - [Minimum TypeScript Version](#Minimum-TypeScript-Version) + - [Intersect Schema Representation](#Intersect-Schema-Representation) + - [Never Schema Representation](#Never-Schema-Representation) + - [Value Cast and Convert](#Value-Cast-and-Convert) + - [Moved TypeGuard Module](#Moved-TypeGuard-Module) + - [Format Renamed to FormatRegistry](#Format-Renamed-to-FormatRegistry) + - [Custom Renamed to TypeRegistry](#Custom-Renamed-to-TypeRegistry) + + + +## Standard Type Builder + +Revision 0.26.0 exports a new type builder called `StandardType`. This builder only allows for the construction JSON Schema compliant types by omitting all Extended types. + +```typescript +import { StandardType as Type, Static } from '@sinclair/typebox' + +const T = Type.Date() // error: no such function +``` + + + +## Automatic Unwrap for Union and Intersect + +Revision 0.26.0 will automatically unwrap unions and intersections for the following cases. + +```typescript +const T1 = Type.Union([Type.String(), Type.Number()]) // TUnion<[TString, TNumber]> + +const T2 = Type.Union([Type.String()]) // TString + +const T3 = Type.Union([]) // TNever +``` + + + +## Intersect and Union now Compose + +Revision 0.26.0 re-enables support for union and intersection type composition. These types are also made compatible with `Pick`, `Omit`, `Partial`, `Required` and `KeyOf` utility types. + +```typescript +const A = Type.Object({ type: Type.Literal('A') }) +const B = Type.Object({ type: Type.Literal('B') }) +const C = Type.Object({ type: Type.Literal('C') }) + +const Union = Type.Union([A, B, C]) + +const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}) + +const T = Type.Intersect([Union, Extended]) // type T = ({ + // type: "A"; + // } | { + // type: "B"; + // } | { + // type: "C"; + // }) & { + // x: number; + // y: number; + // z: number; + // } + +const K = Type.KeyOf(T) // type K = "type" | "x" | "y" | "z" + +const P = Type.Pick(T, ['type', 'x']) // type P = ({ + // type: "A"; + // } | { + // type: "B"; + // } | { + // type: "C"; + // }) & { + // x: number; + // } + +const O = Type.Partial(P) // type O = ({ + // type?: "A" | undefined; + // } | { + // type?: "B" | undefined; + // } | { + // type?: "C" | undefined; + // }) & { + // x?: number | undefined; + // } +``` + + + +## Runtime Conditional Types + +Revision 0.26.0 adds the runtime conditional types `Extends`, `Extract` and `Exclude` as standard. + +#### TypeScript + +```typescript +type T0 = string extends number ? true : false +// ^ false +type T1 = Extract +// ^ number +type T2 = Exclude +// ^ string +``` + +#### TypeBox +```typescript +const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) +// ^ TLiteral +const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number()) +// ^ TNumber +const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number()) +// ^ TString +``` + + + +## Value Convert + +Revision 0.26.0 adds a new `Convert` function to the `Value.*` module. This function will perform a type coercion for any value mismatched to its type if a reasonable conversion is possible. + +```typescript +const T = Type.Number() + +const A = Value.Convert(T, '42') // const A: unknown = 42 - ... Convert(...) will return `unknown` + +const B = Value.Check(T, A) // const B = true - ... so should be checked +``` + + + +## Error Iterator + +Revision 0.26.0 now returns a `ValueErrorIterator` for `.Errors(...)`. This iterator provides a utility function to obtain the first error only. To obtain all errors, continue to use `for-of` enumeration or array spread syntax. + +```typescript +const T = Type.Number() + +const First = Value.Errors(T, 'foo').First() // const First = { path: '', message: 'Expected number', ... } + +const All = [...Value.Errors(T, 'foo')] // const All = [{ path: '', message: 'Expected number', ... }] +``` + + + + +## Codegen without JIT + +Revision 0.26.0 adds a `.Code()` function to the `TypeCompiler` to enable code generation without JIT evaluation. + +```typescript +import { TypeCompiler } from '@sinclair/typebox/compiler' + +const T = Type.Object({ + x: Type.Number(), + y: Type.Number() +}) + +const C = TypeCompiler.Code(T) // return function check(value) { + // return ( + // (typeof value === 'object' && value !== null) && + // !Array.isArray(value) && + // typeof value.x === 'number' && + // Number.isFinite(value.x) && + // typeof value.y === 'number' && + // Number.isFinite(value.y) + // ) + // } +``` + + + +## Standard Type (Not) + +Revision 0.26.0 introduces the `Not` standard type. This type allows for the inversion of assertion logic which can be useful to narrow for broader types. + +#### Example 1 + +```typescript +const T = Type.Not(Type.String({ pattern: 'A|B|C' }), Type.String()) + +Value.Check(T, 'A') // false +Value.Check(T, 'B') // false +Value.Check(T, 'C') // false +Value.Check(T, 'D') // true +``` + +#### Example 2 + +```typescript +const Even = Type.Number({ multipleOf: 2 }) +const Odd = Type.Not(Even, Type.Number()) + +Value.Check(Even, 0) // true +Value.Check(Even, 1) // false +Value.Check(Even, 2) // true + +Value.Check(Odd, 0) // false +Value.Check(Odd, 1) // true +Value.Check(Odd, 2) // false +``` + + + +## Standard Type (Composite) + +Revision 0.26.0 includes a new `Composite` standard type. This type will combine an array of `TObject[]` into a `TObject` by taking a union of any overlapping properties. + +```typescript +const A = Type.Object({ type: Type.Literal('A') }) + +const B = Type.Object({ type: Type.Literal('B') }) + +const C = Type.Object({ type: Type.Literal('C'), value: Type.Number() }) + +const T = Type.Composite([A, B, C]) // type T = { + // type: 'A' | 'B' | 'C' + // value: number + // } +``` + + + +## Extended Type (Symbol) + +Revision 0.26.0 provides provisional support for `Symbol` type validation. + +```typescript +const T = Type.Symbol() + +Value.Check(A, Symbol('Foo')) // true +``` + + + +## Extended Type (BigInt) + +Revision 0.26.0 provides provisional support for `BigInt` type validation. + +```typescript +const T = Type.BigInt({ minimum: 10n }) + +Value.Check(B, 1_000_000n) // true +``` + + + +## Breaking Changes + +The following are breaking changed in Revision 0.26.0 + + + +## Minimum TypeScript Version + +Revision 0.26.0 requires a minimum recommended TypeScript version of `4.2.3`. Version `4.1.5` is no longer supported. + + + +## Intersect Schema Representation + +Revision 0.26.0 changes the schema representation for `Intersect`. Revision 0.25.0 would construct a composite `object` type, in 0.26.0, `Intersect` is expressed as `anyOf`. If upgrading, consider using `Type.Composite(...)` to return backwards compatible representations. + +#### Intersect 0.25.0 + +```typescript +const T = Type.Intersect([ // const U = { + Type.Object({ // type: 'object', + x: Type.Number(), // required: ['x', 'y'], + }), // properties: { + Type.Object({ // x: { + y: Type.Number(), // type: 'number' + }) // }, +]) // y: { + // type: 'number' + // } + // } + // } +``` +#### Intersect 0.26.0 + +```typescript +const T = Type.Intersect([ // const U = { + Type.Object({ // type: 'object', + x: Type.Number(), // allOf: [{ + }), // type: 'object', + Type.Object({ // required: [ 'x' ], + y: Type.Number(), // properties: { + }) // x: { type: 'number' } +]) // } + // }, { + // type: 'object', + // required: ['y'], + // properties: { + // y: { type: 'number' } + // } + // }] + // } +``` + + + +## Never Schema Representation + +Revision 0.26.0 simplifies the representation for `TNever`. Previous versions of TypeBox used an illogical intersection of Boolean constants via `allOf`. In 0.26.0, `never` is expressed as a `not` schema of type `any`. + +#### Intersect 0.25.0 + +```typescript +const T = Type.Never() // const T = { + // allOf: [ + // { type: 'boolean', const: true } + // { type: 'boolean', const: false } + // ] + // } +``` +#### Intersect 0.26.0 + +```typescript +const T = Type.Never() // const T = { not: {} } +``` + + + +## Value Cast and Convert + +Revision 0.26.0 removes the `Cast` functions ability to coerce values. Use the new `Convert` function prior to `Cast`. + +```typescript +const T = Type.Number() + +const V = Value.Cast(T, '42') // const V = 42 - 0.25.0 coerces to 42 + +const V = Value.Cast(T, Value.Convert(T, '42')) // const V = 42 - 0.26.0 convert then cast +``` + + + +## Moved TypeGuard Module + +The `TypeGuard` is now imported via the `@sinclair/typebox` module. This move is due to the TypeBox compositor internally using the guard when constructing types. + +```typescript +import { TypeGuard } from '@sinclair/typebox/guard' // 0.25.0 + +import { TypeGuard } from '@sinclair/typebox' // 0.26.0 +``` + + + +## Format Renamed to FormatRegistry + +The `Format` module has been renamed to `FormatRegistry` and moved to the `typebox.ts` module. + +```typescript +import { Format } from '@sinclair/typebox/format' // 0.25.0 + +import { FormatRegistry } from '@sinclair/typebox' // 0.26.0 +``` + + + +## Custom Renamed to TypeRegistry + +The `Format` module has been renamed to `FormatRegistry` and moved to the `typebox.ts` module. + +```typescript +import { Custom } from '@sinclair/typebox/format' // 0.25.0 + +import { TypeRegistry } from '@sinclair/typebox' // 0.26.0 +``` \ No newline at end of file diff --git a/codegen/codegen.ts b/codegen/codegen.ts deleted file mode 100644 index 81d3797d3..000000000 --- a/codegen/codegen.ts +++ /dev/null @@ -1,53 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { TSchema } from '@sinclair/typebox' -import { TypeBoxCodegen } from './typebox' -import { TypeScriptCodegen } from './typescript' -import { JsonSchemaCodegen } from './jsonschema' -import { ZodCodegen, ZodCodegenOptions } from './zod' - -export namespace Codegen { - /** Generates TypeScript type definitions from TypeBox types */ - export function TypeScript(schema: TSchema, references: TSchema[] = []): string { - return TypeScriptCodegen.Generate(schema, references) - } - /** Generates Zod type definitions from TypeBox types */ - export function Zod(schema: TSchema, references: TSchema[] = [], options: ZodCodegenOptions = { imports: true, exports: false }): string { - return ZodCodegen.Generate(schema, references, options) - } - /** Generates TypeBox type definitions from TypeScript code */ - export function TypeBox(code: string): string { - return TypeBoxCodegen.Generate(code) - } - - /** Generates JsonSchema definitions from TypeScript code */ - export function JsonSchema(code: string): string { - return JsonSchemaCodegen.Generate(code) - } -} diff --git a/codegen/index.ts b/codegen/index.ts index 01fc5f56e..0513ff9ca 100644 --- a/codegen/index.ts +++ b/codegen/index.ts @@ -26,4 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './codegen' +export { TypeBoxToTypeScript } from './typebox-to-typescript' +export { TypeBoxToZod } from './typebox-to-zod' +export { TypeScriptToJsonSchema } from './typescript-to-json-schema' +export { TypeScriptToTypeBox } from './typescript-to-typebox' diff --git a/codegen/typescript.ts b/codegen/typebox-to-typescript.ts similarity index 76% rename from codegen/typescript.ts rename to codegen/typebox-to-typescript.ts index bb1d7a6c8..6629c6022 100644 --- a/codegen/typescript.ts +++ b/codegen/typebox-to-typescript.ts @@ -27,39 +27,35 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { Formatter } from './formatter' -import { TypeGuard } from '@sinclair/typebox/guard' import * as Types from '@sinclair/typebox' -export namespace TypeScriptCodegen { +export namespace TypeBoxToTypeScript { function Any(schema: Types.TAny) { return 'any' } - function Array(schema: Types.TArray) { const items = Visit(schema.items) return `Array<${items}>` } - function Boolean(schema: Types.TBoolean) { return 'boolean' } - function Constructor(schema: Types.TConstructor) { const params = schema.parameters.map((param) => Visit(param)).join(', ') const returns = Visit(schema.returns) return `new (${params}) => ${returns}` } - function Function(schema: Types.TFunction) { const params = schema.parameters.map((param) => Visit(param)).join(', ') const returns = Visit(schema.returns) return `(${params}) => ${returns}` } - function Integer(schema: Types.TInteger) { return 'number' } - + function Intersect(schema: Types.TIntersect) { + return `(${schema.allOf.map((schema) => Visit(schema)).join(' & ')})` + } function Literal(schema: Types.TLiteral) { if (typeof schema.const === 'string') { return `'${schema.const}'` @@ -67,23 +63,18 @@ export namespace TypeScriptCodegen { return `${schema.const}` } } - function Never(schema: Types.TNever) { return 'never' } - function Null(schema: Types.TNull) { return 'null' } - function String(schema: Types.TString) { return 'string' } - function Number(schema: Types.TNumber) { return 'number' } - function Object(schema: Types.TObject) { const properties: string = globalThis.Object.entries(schema.properties) .map(([key, value]) => { @@ -92,12 +83,10 @@ export namespace TypeScriptCodegen { .join(',\n') return `{\n${properties}\n}` } - function Promise(schema: Types.TPromise) { const item = Visit(schema.item) return `Promise<${item}>` } - function Record(schema: Types.TRecord) { for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { const type = Visit(value) @@ -107,93 +96,85 @@ export namespace TypeScriptCodegen { return `Record` } } - throw Error('TypeScriptCodeGen: Unreachable') + throw Error('TypeBoxToTypeScript: Unreachable') } - function Ref(schema: Types.TRef) { return schema.$ref } - function Self(schema: Types.TSelf) { return schema.$ref } - function Tuple(schema: Types.TTuple) { if (schema.items === undefined) return `[]` const items = schema.items.map((schema) => Visit(schema)).join(', ') return `[${items}]` } - function UInt8Array(schema: Types.TUint8Array) { return `Uint8Array` } - function Undefined(schema: Types.TUndefined) { return `undefined` } - function Union(schema: Types.TUnion) { - return schema.anyOf.map((schema) => Visit(schema)).join(' | ') + return `${schema.anyOf.map((schema) => Visit(schema)).join(' | ')}` } - function Unknown(schema: Types.TUnknown) { return `unknown` } - function Void(schema: Types.TVoid) { return `void` } - function Visit(schema: Types.TSchema): string { - if (TypeGuard.TAny(schema)) { + if (Types.TypeGuard.TAny(schema)) { return Any(schema) - } else if (TypeGuard.TArray(schema)) { + } else if (Types.TypeGuard.TArray(schema)) { return Array(schema) - } else if (TypeGuard.TBoolean(schema)) { + } else if (Types.TypeGuard.TBoolean(schema)) { return Boolean(schema) - } else if (TypeGuard.TConstructor(schema)) { + } else if (Types.TypeGuard.TConstructor(schema)) { return Constructor(schema) - } else if (TypeGuard.TFunction(schema)) { + } else if (Types.TypeGuard.TFunction(schema)) { return Function(schema) - } else if (TypeGuard.TInteger(schema)) { + } else if (Types.TypeGuard.TInteger(schema)) { return Integer(schema) - } else if (TypeGuard.TLiteral(schema)) { + } else if (Types.TypeGuard.TIntersect(schema)) { + return Intersect(schema) + } else if (Types.TypeGuard.TLiteral(schema)) { return Literal(schema) - } else if (TypeGuard.TNever(schema)) { + } else if (Types.TypeGuard.TNever(schema)) { return Never(schema) - } else if (TypeGuard.TNull(schema)) { + } else if (Types.TypeGuard.TNull(schema)) { return Null(schema) - } else if (TypeGuard.TNumber(schema)) { + } else if (Types.TypeGuard.TNumber(schema)) { return Number(schema) - } else if (TypeGuard.TObject(schema)) { + } else if (Types.TypeGuard.TObject(schema)) { return Object(schema) - } else if (TypeGuard.TPromise(schema)) { + } else if (Types.TypeGuard.TPromise(schema)) { return Promise(schema) - } else if (TypeGuard.TRecord(schema)) { + } else if (Types.TypeGuard.TRecord(schema)) { return Record(schema) - } else if (TypeGuard.TRef(schema)) { + } else if (Types.TypeGuard.TRef(schema)) { return Ref(schema) - } else if (TypeGuard.TSelf(schema)) { + } else if (Types.TypeGuard.TSelf(schema)) { return Self(schema) - } else if (TypeGuard.TString(schema)) { + } else if (Types.TypeGuard.TString(schema)) { return String(schema) - } else if (TypeGuard.TTuple(schema)) { + } else if (Types.TypeGuard.TTuple(schema)) { return Tuple(schema) - } else if (TypeGuard.TUint8Array(schema)) { + } else if (Types.TypeGuard.TUint8Array(schema)) { return UInt8Array(schema) - } else if (TypeGuard.TUndefined(schema)) { + } else if (Types.TypeGuard.TUndefined(schema)) { return Undefined(schema) - } else if (TypeGuard.TUnion(schema)) { + } else if (Types.TypeGuard.TUnion(schema)) { return Union(schema) - } else if (TypeGuard.TUnknown(schema)) { + } else if (Types.TypeGuard.TUnknown(schema)) { return Unknown(schema) - } else if (TypeGuard.TVoid(schema)) { + } else if (Types.TypeGuard.TVoid(schema)) { return Void(schema) } else { - throw Error('TypeScriptCodeGen: Unknown type') + throw Error('TypeBoxToTypeScript: Unknown type') } } - /** Generates TypeScript code from TypeBox types */ export function Generate(schema: Types.TSchema, references: Types.TSchema[] = []) { const result: string[] = [] diff --git a/codegen/zod.ts b/codegen/typebox-to-zod.ts similarity index 74% rename from codegen/zod.ts rename to codegen/typebox-to-zod.ts index bfa9eaecf..84d84706d 100644 --- a/codegen/zod.ts +++ b/codegen/typebox-to-zod.ts @@ -27,24 +27,29 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { Formatter } from './formatter' -import { TypeGuard } from '@sinclair/typebox/guard' import * as Types from '@sinclair/typebox' -export class ZodNonReferentialType extends Error { +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- +export class TypeBoxToZodNonReferentialType extends Error { constructor(message: string) { - super(`ZodNonReferentialType: ${message}`) + super(`TypeBoxToZod: ${message}`) } } -export class ZodUnsupportedType extends Error { +export class TypeBoxToZodUnsupportedType extends Error { constructor(message: string) { - super(`ZodUnsupportedType: ${message}`) + super(`TypeBoxToZod: ${message}`) } } +// -------------------------------------------------------------------------- +// Transform +// -------------------------------------------------------------------------- export interface ZodCodegenOptions { imports: boolean exports: boolean } -export namespace ZodCodegen { +export namespace TypeBoxToZod { function Any(schema: Types.TAny) { return `z.any()` } @@ -56,7 +61,7 @@ export namespace ZodCodegen { return `z.boolean()` } function Constructor(schema: Types.TConstructor): string { - throw new ZodUnsupportedType(`TConstructor`) + throw new TypeBoxToZodUnsupportedType(`TConstructor`) } function Function(schema: Types.TFunction) { const params = schema.parameters.map((param) => Visit(param)).join(`, `) @@ -73,6 +78,17 @@ export namespace ZodCodegen { if (schema.multipleOf !== undefined) buffer.push(`.multipleOf(${schema.multipleOf})`) return buffer.join(``) } + function Intersect(schema: Types.TIntersect) { + // note: Zod only supports binary intersection. While correct, this is partially at odds with TypeScript's + // ability to distribute across (A & B & C). This code reduces intersection to binary ops. + function reduce(rest: Types.TSchema[]): string { + if (rest.length === 0) throw Error('Expected at least one intersect type') + if (rest.length === 1) return Visit(rest[0]) + const [left, right] = [rest[0], rest.slice(1)] + return `z.intersection(${Visit(left)}, ${reduce(right)})` + } + return reduce(schema.allOf) + } function Literal(schema: Types.TLiteral) { if (typeof schema.const === `string`) { return `z.literal('${schema.const}')` @@ -124,19 +140,19 @@ export namespace ZodCodegen { for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { const type = Visit(value) if (key === `^(0|[1-9][0-9]*)$`) { - throw new ZodUnsupportedType(`TRecord`) + throw new TypeBoxToZodUnsupportedType(`TRecord`) } else { return `z.record(${type})` } } - throw Error(`TypeScriptCodeGen: Unreachable`) + throw Error(`TypeBoxToZod: Unreachable`) } function Ref(schema: Types.TRef) { - if (!reference_map.has(schema.$ref!)) throw new ZodNonReferentialType(schema.$ref!) + if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) return schema.$ref } function Self(schema: Types.TSelf) { - if (!reference_map.has(schema.$ref!)) throw new ZodNonReferentialType(schema.$ref!) + if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) recursive_set.add(schema.$ref) return schema.$ref } @@ -146,7 +162,7 @@ export namespace ZodCodegen { return `z.tuple([${items}])` } function UInt8Array(schema: Types.TUint8Array): string { - throw new ZodUnsupportedType(`TUint8Array`) + throw new TypeBoxToZodUnsupportedType(`TUint8Array`) } function Undefined(schema: Types.TUndefined) { return `z.undefined()` @@ -162,52 +178,54 @@ export namespace ZodCodegen { } function Visit(schema: Types.TSchema): string { if (schema.$id !== undefined) reference_map.set(schema.$id, schema) - if (TypeGuard.TAny(schema)) { + if (Types.TypeGuard.TAny(schema)) { return Any(schema) - } else if (TypeGuard.TArray(schema)) { + } else if (Types.TypeGuard.TArray(schema)) { return Array(schema) - } else if (TypeGuard.TBoolean(schema)) { + } else if (Types.TypeGuard.TBoolean(schema)) { return Boolean(schema) - } else if (TypeGuard.TConstructor(schema)) { + } else if (Types.TypeGuard.TConstructor(schema)) { return Constructor(schema) - } else if (TypeGuard.TFunction(schema)) { + } else if (Types.TypeGuard.TFunction(schema)) { return Function(schema) - } else if (TypeGuard.TInteger(schema)) { + } else if (Types.TypeGuard.TInteger(schema)) { return Integer(schema) - } else if (TypeGuard.TLiteral(schema)) { + } else if (Types.TypeGuard.TIntersect(schema)) { + return Intersect(schema) + } else if (Types.TypeGuard.TLiteral(schema)) { return Literal(schema) - } else if (TypeGuard.TNever(schema)) { + } else if (Types.TypeGuard.TNever(schema)) { return Never(schema) - } else if (TypeGuard.TNull(schema)) { + } else if (Types.TypeGuard.TNull(schema)) { return Null(schema) - } else if (TypeGuard.TNumber(schema)) { + } else if (Types.TypeGuard.TNumber(schema)) { return Number(schema) - } else if (TypeGuard.TObject(schema)) { + } else if (Types.TypeGuard.TObject(schema)) { return Object(schema) - } else if (TypeGuard.TPromise(schema)) { + } else if (Types.TypeGuard.TPromise(schema)) { return Promise(schema) - } else if (TypeGuard.TRecord(schema)) { + } else if (Types.TypeGuard.TRecord(schema)) { return Record(schema) - } else if (TypeGuard.TRef(schema)) { + } else if (Types.TypeGuard.TRef(schema)) { return Ref(schema) - } else if (TypeGuard.TSelf(schema)) { + } else if (Types.TypeGuard.TSelf(schema)) { return Self(schema) - } else if (TypeGuard.TString(schema)) { + } else if (Types.TypeGuard.TString(schema)) { return String(schema) - } else if (TypeGuard.TTuple(schema)) { + } else if (Types.TypeGuard.TTuple(schema)) { return Tuple(schema) - } else if (TypeGuard.TUint8Array(schema)) { + } else if (Types.TypeGuard.TUint8Array(schema)) { return UInt8Array(schema) - } else if (TypeGuard.TUndefined(schema)) { + } else if (Types.TypeGuard.TUndefined(schema)) { return Undefined(schema) - } else if (TypeGuard.TUnion(schema)) { + } else if (Types.TypeGuard.TUnion(schema)) { return Union(schema) - } else if (TypeGuard.TUnknown(schema)) { + } else if (Types.TypeGuard.TUnknown(schema)) { return Unknown(schema) - } else if (TypeGuard.TVoid(schema)) { + } else if (Types.TypeGuard.TVoid(schema)) { return Void(schema) } else { - throw Error(`TypeScriptCodeGen: Unknown type`) + throw Error(`TypeBoxToZod: Unknown type`) } } const reference_map = new Map() @@ -222,7 +240,7 @@ export namespace ZodCodegen { // initialize root schematic and reference map if (schema.$id === undefined) schema.$id = `T_Generated` for (const reference of references) { - if (reference.$id === undefined) throw new ZodNonReferentialType(JSON.stringify(reference)) + if (reference.$id === undefined) throw new TypeBoxToZodNonReferentialType(JSON.stringify(reference)) reference_map.set(reference.$id, reference) } // render-code: Imports required for the generated code diff --git a/codegen/jsonschema.ts b/codegen/typescript-to-json-schema.ts similarity index 85% rename from codegen/jsonschema.ts rename to codegen/typescript-to-json-schema.ts index c429a1125..2f198dbcb 100644 --- a/codegen/jsonschema.ts +++ b/codegen/typescript-to-json-schema.ts @@ -29,32 +29,32 @@ THE SOFTWARE. import { Formatter } from './formatter' import * as ts from 'typescript' -export class NonExpressable extends Error { +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- +export class TypeScriptToJsonSchemaNonExpressable extends Error { constructor(type: string) { - super(`JsonSchemaCodeGen: Cannot express syntax type '${type}'`) + super(`TypeScriptToJsonSchema: Cannot express syntax type '${type}'`) } } - -/** Generates JsonSchema from TypeScript interface and type definitions */ -export namespace JsonSchemaCodegen { +// -------------------------------------------------------------------------- +// Transform +// -------------------------------------------------------------------------- +export namespace TypeScriptToJsonSchema { function isReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined } - function isOptionalProperty(node: ts.PropertySignature) { return node.questionToken !== undefined } - function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined } - function* SourceFile(node: ts.SourceFile): IterableIterator { for (const next of node.getChildren()) { yield* Visit(next) } } - function* PropertySignature(node: ts.PropertySignature): IterableIterator { const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] const type = Collect(node.type) @@ -68,7 +68,6 @@ export namespace JsonSchemaCodegen { return yield `${node.name.getText()}: ${type}` } } - function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { const type = Collect(node.elementType) yield `{ @@ -76,7 +75,6 @@ export namespace JsonSchemaCodegen { items: ${type} }` } - function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { const types = node.elements.map((type) => Collect(type)).join(',\n') yield `{ @@ -86,7 +84,6 @@ export namespace JsonSchemaCodegen { maxItems: ${node.elements.length} }` } - function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') yield `{ @@ -95,7 +92,6 @@ export namespace JsonSchemaCodegen { ] }` } - function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') yield `{ @@ -104,23 +100,18 @@ export namespace JsonSchemaCodegen { ] }` } - function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { - throw new NonExpressable('TypeOperatorNode') + throw new TypeScriptToJsonSchemaNonExpressable('TypeOperatorNode') } - function* Parameter(node: ts.ParameterDeclaration): IterableIterator { yield Collect(node.type) } - function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { - throw new NonExpressable('FunctionTypeNode') + throw new TypeScriptToJsonSchemaNonExpressable('FunctionTypeNode') } - function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { - throw new NonExpressable('ConstructorTypeNode') + throw new TypeScriptToJsonSchemaNonExpressable('ConstructorTypeNode') } - function* EnumMember(node: ts.EnumMember): IterableIterator { if (node.initializer) { return yield `{ @@ -131,7 +122,6 @@ export namespace JsonSchemaCodegen { const: '${node.name.getText()}' }` } - function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { const exports = isExport(node) ? 'export ' : '' const name = node.name.getText() @@ -142,7 +132,6 @@ export namespace JsonSchemaCodegen { ] }` } - function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { if (node.typeParameters) { const exports = isExport(node) ? 'export ' : '' @@ -206,11 +195,11 @@ export namespace JsonSchemaCodegen { } function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { - throw new NonExpressable('RestTypeNode') + throw new TypeScriptToJsonSchemaNonExpressable('RestTypeNode') } function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - throw new NonExpressable('ConditionalTypeNode') + throw new TypeScriptToJsonSchemaNonExpressable('ConditionalTypeNode') } function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { @@ -222,31 +211,31 @@ export namespace JsonSchemaCodegen { items: ${args} }` } else if (name === 'Record') { - throw new NonExpressable('TypeReferenceNode:Record') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Record') } else if (name === 'Partial') { - throw new NonExpressable('TypeReferenceNode:Partial') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Partial') } else if (name === 'Uint8Array') { - throw new NonExpressable('TypeReferenceNode:Uint8Array') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Uint8Array') } else if (name === 'Required') { - throw new NonExpressable('TypeReferenceNode:Required') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Required') } else if (name === 'Omit') { - throw new NonExpressable('TypeReferenceNode:Omit') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Omit') } else if (name === 'Pick') { - throw new NonExpressable('TypeReferenceNode:Pick') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Pick') } else if (name === 'Promise') { - throw new NonExpressable('TypeReferenceNode:Promise') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Promise') } else if (name === 'ReturnType') { - throw new NonExpressable('TypeReferenceNode:ReturnType') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:ReturnType') } else if (name === 'InstanceType') { - throw new NonExpressable('TypeReferenceNode:InstanceType') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:InstanceType') } else if (name === 'Parameters') { - throw new NonExpressable('TypeReferenceNode:Parameters') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Parameters') } else if (name === 'ConstructorParameters') { - throw new NonExpressable('TypeReferenceNode:ConstructorParameters') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:ConstructorParameters') } else if (name === 'Exclude') { - throw new NonExpressable('TypeReferenceNode:Exclude') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Exclude') } else if (name === 'Extract') { - throw new NonExpressable('TypeReferenceNode:Extract') + throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Extract') } else { return yield `${name}${args}` } @@ -345,7 +334,7 @@ export namespace JsonSchemaCodegen { } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { - throw new NonExpressable('KeyOfKeyword') + throw new TypeScriptToJsonSchemaNonExpressable('KeyOfKeyword') } else if (node.kind === ts.SyntaxKind.NumberKeyword) { return yield `{ type: 'number' @@ -359,7 +348,7 @@ export namespace JsonSchemaCodegen { type: 'boolean' }` } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { - throw new NonExpressable('UndefinedKeyword') + throw new TypeScriptToJsonSchemaNonExpressable('UndefinedKeyword') } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { return yield `{ @@ -386,7 +375,7 @@ export namespace JsonSchemaCodegen { type: 'null' }` } else if (node.kind === ts.SyntaxKind.VoidKeyword) { - throw new NonExpressable('KeyOfKeyword') + throw new TypeScriptToJsonSchemaNonExpressable('KeyOfKeyword') } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { return } else if (node.kind === ts.SyntaxKind.SyntaxList) { diff --git a/codegen/typebox.ts b/codegen/typescript-to-typebox.ts similarity index 91% rename from codegen/typebox.ts rename to codegen/typescript-to-typebox.ts index 598921ba0..a42f11750 100644 --- a/codegen/typebox.ts +++ b/codegen/typescript-to-typebox.ts @@ -29,26 +29,29 @@ THE SOFTWARE. import { Formatter } from './formatter' import * as ts from 'typescript' -/** Generates TypeBox types from TypeScript interface and type definitions */ -export namespace TypeBoxCodegen { +// -------------------------------------------------------------------------- +// Transform +// -------------------------------------------------------------------------- + +/** Generates TypeBox types from TypeScript code */ +export namespace TypeScriptToTypeBox { function isReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined } - function isOptionalProperty(node: ts.PropertySignature) { return node.questionToken !== undefined } - - function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration): boolean { + function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined } - + function isNamespace(node: ts.ModuleDeclaration) { + return node.flags === ts.NodeFlags.Namespace + } function* SourceFile(node: ts.SourceFile): IterableIterator { for (const next of node.getChildren()) { yield* Visit(next) } } - function* PropertySignature(node: ts.PropertySignature): IterableIterator { const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] const type = Collect(node.type) @@ -62,50 +65,41 @@ export namespace TypeBoxCodegen { return yield `${node.name.getText()}: ${type}` } } - function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { const type = Collect(node.elementType) yield `Type.Array(${type})` } - function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { const types = node.elements.map((type) => Collect(type)).join(',\n') yield `Type.Tuple([\n${types}\n])` } - function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') yield `Type.Union([\n${types}\n])` } - function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') yield `Type.Intersect([\n${types}\n])` } - function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { if (node.operator === ts.SyntaxKind.KeyOfKeyword) { const type = Collect(node.type) yield `Type.KeyOf(${type})` } } - function* Parameter(node: ts.ParameterDeclaration): IterableIterator { yield Collect(node.type) } - function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { const parameters = node.parameters.map((param) => Collect(param)).join(', ') const returns = Collect(node.type) yield `Type.Function([${parameters}], ${returns})` } - function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { const parameters = node.parameters.map((param) => Collect(param)).join(', ') const returns = Collect(node.type) yield `Type.Constructor([${parameters}], ${returns})` } - function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { const exports = isExport(node) ? 'export ' : '' const name = node.name.getText() @@ -114,7 +108,6 @@ export namespace TypeBoxCodegen { const type = `${exports}const ${name} = Type.Enum(${name}Enum)` yield [enumType, '', type].join('\n') } - function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true if (node.typeParameters) { @@ -135,7 +128,6 @@ export namespace TypeBoxCodegen { yield `${staticDeclaration}\n${typeDeclaration}` } } - function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { useImports = true if (node.typeParameters) { @@ -156,28 +148,22 @@ export namespace TypeBoxCodegen { yield `${staticDeclaration}\n${typeDeclaration}` } } - function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { yield node.name.getText() } - function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { yield Collect(node.type) } - function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { yield `Type.Rest()` } - function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - useConditional = true const checkType = Collect(node.checkType) const extendsType = Collect(node.extendsType) const trueType = Collect(node.trueType) const falseType = Collect(node.falseType) - yield `Conditional.Extends(${checkType}, ${extendsType}, ${trueType}, ${falseType})` + yield `Type.Extends(${checkType}, ${extendsType}, ${trueType}, ${falseType})` } - function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { const name = node.typeName.getText() const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' @@ -206,41 +192,46 @@ export namespace TypeBoxCodegen { } else if (name === 'ConstructorParameters') { return yield `Type.ConstructorParameters${args}` } else if (name === 'Exclude') { - return yield `Conditional.Exclude${args}` + return yield `Type.Exclude${args}` } else if (name === 'Extract') { - return yield `Conditional.Extract${args}` + return yield `Type.Extract${args}` } else { return yield `${name}${args}` } } - function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { const members = node.members.map((member) => Collect(member)).join(',\n') yield `Type.Object({\n${members}\n})` } - function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { const text = node.getText() if (text === 'null') return yield `Type.Null()` yield `Type.Literal(${node.getText()})` } - + function* ModuleDeclaration(node: ts.ModuleDeclaration): IterableIterator { + const export_specifier = isExport(node) ? 'export ' : '' + const module_specifier = isNamespace(node) ? 'namespace' : 'module' + yield `${export_specifier}${module_specifier} ${node.name.getText()} {` + yield* Visit(node.body) + yield `}` + } + function* ModuleBlock(node: ts.ModuleBlock): IterableIterator { + for (const statement of node.statements) { + yield* Visit(statement) + } + } function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { yield node.getText() } - function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { yield node.getText() } - function Collect(node: ts.Node | undefined): string { return `${[...Visit(node)].join('')}` } - function CollectNewLine(node: ts.Node | undefined): string { return [...Visit(node)].join('\n\n') } - function* Visit(node: ts.Node | undefined): IterableIterator { if (node === undefined) return if (ts.isSourceFile(node)) { @@ -265,6 +256,10 @@ export namespace TypeBoxCodegen { return yield* TypeLiteralNode(node) } else if (ts.isLiteralTypeNode(node)) { return yield* LiteralTypeNode(node) + } else if (ts.isModuleDeclaration(node)) { + return yield* ModuleDeclaration(node) + } else if (ts.isModuleBlock(node)) { + return yield* ModuleBlock(node) } else if (ts.isArrayTypeNode(node)) { return yield* ArrayTypeNode(node) } else if (ts.isTupleTypeNode(node)) { @@ -287,6 +282,8 @@ export namespace TypeBoxCodegen { return yield* ClassDeclaration(node) } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) + } else if (node.kind === ts.SyntaxKind.ExportKeyword) { + return yield `export` } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { return yield `Type.KeyOf()` } else if (node.kind === ts.SyntaxKind.NumberKeyword) { @@ -319,22 +316,16 @@ export namespace TypeBoxCodegen { return yield node.getText() } } - let useImports = false - let useConditional = false let useGenerics = false - /** Generates TypeBox types from TypeScript interface and type definitions */ export function Generate(typescriptCode: string) { useImports = false - useConditional = false useGenerics = false - const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) const typeDeclarations = CollectNewLine(source) const importStatments: string[] = [] if (useImports) { - if (useConditional) importStatments.push(`import { Conditional } from '@sinclair/typebox/conditional'`) if (useGenerics) importStatments.push(`import { Type, Static, TSchema } from '@sinclair/typebox'`) if (!useGenerics) importStatments.push(`import { Type, Static } from '@sinclair/typebox'`) } diff --git a/example/experimental/experimental.ts b/example/experimental/experimental.ts new file mode 100644 index 000000000..facf22474 --- /dev/null +++ b/example/experimental/experimental.ts @@ -0,0 +1,109 @@ + +/*-------------------------------------------------------------------------- + +@sinclair/typebox/experimental + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +// ------------------------------------------------------------------------------------- +// TReadonlyObject +// ------------------------------------------------------------------------------------- +export type TReadonlyArray = Types.Assert<{ [K in keyof T]: TReadonlyObject> }, Types.TSchema[]> +// prettier-ignore +export type TReadonlyProperties = Types.Evaluate ? Types.TReadonlyOptional : + T[K] extends Types.TReadonly ? Types.TReadonly : + T[K] extends Types.TOptional ? Types.TReadonlyOptional : + Types.TReadonly +}, Types.TProperties>> +// prettier-ignore +export type TReadonlyObject = + T extends Types.TIntersect ? Types.TIntersect> : + T extends Types.TUnion ? Types.TUnion> : + T extends Types.TObject ? Types.TObject> : + Types.TReadonly + +// ------------------------------------------------------------------------------------- +// TUnionEnum +// ------------------------------------------------------------------------------------- +export interface TUnionEnum extends Types.TSchema { + [Types.Kind]: 'UnionEnum' + static: T[number] + enum: T +} +// ------------------------------------------------------------------------------------- +// UnionOneOf +// ------------------------------------------------------------------------------------- +export interface UnionOneOf extends Types.TSchema { + [Types.Kind]: 'UnionOneOf' + static: { [K in keyof T]: Types.Static }[number] + oneOf: T +} +// ------------------------------------------------------------------------------------- +// ExperimentalTypeBuilder +// ------------------------------------------------------------------------------------- +export class ExperimentalTypeBuilder extends Types.ExtendedTypeBuilder { + /** `[Experimental]` Remaps a Intersect, Union or Object as readonly */ + public ReadonlyObject(schema: T): TReadonlyObject { + function Apply(property: Types.TSchema): any { + // prettier-ignore + switch (property[Types.Modifier]) { + case 'ReadonlyOptional': property[Types.Modifier] = 'ReadonlyOptional'; break + case 'Readonly': property[Types.Modifier] = 'Readonly'; break + case 'Optional': property[Types.Modifier] = 'ReadonlyOptional'; break + default: property[Types.Modifier] = 'Readonly'; break + } + return property + } + // prettier-ignore + return (Types.TypeGuard.TIntersect(schema) || Types.TypeGuard.TUnion(schema) || Types.TypeGuard.TObject(schema)) + ? Types.ObjectMap.Map>(schema, (schema) => { + globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) + return schema + }, {}) : Apply(schema) + } + /** `[Experimental]` Creates a Union type with a `enum` schema representation */ + public UnionEnum(values: [...T], options: Types.SchemaOptions = {}) { + function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { + return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) + } + if (!Types.TypeRegistry.Has('UnionEnum')) Types.TypeRegistry.Set('UnionEnum', UnionEnumCheck) + return { ...options, [Types.Kind]: 'UnionEnum', enum: values } as TUnionEnum + } + /** `[Experimental]` Creates a Union type with a `oneOf` schema representation */ + public UnionOneOf(oneOf: [...T], options: Types.SchemaOptions = {}) { + function UnionOneOfCheck(schema: UnionOneOf, value: unknown) { + return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) + } + if (!Types.TypeRegistry.Has('UnionOneOf')) Types.TypeRegistry.Set('UnionOneOf', UnionOneOfCheck) + return { ...options, [Types.Kind]: 'UnionOneOf', oneOf } as UnionOneOf + } +} + +export const Type = new ExperimentalTypeBuilder() \ No newline at end of file diff --git a/src/hash/index.ts b/example/experimental/index.ts similarity index 95% rename from src/hash/index.ts rename to example/experimental/index.ts index c1f89ba06..9dcc0367b 100644 --- a/src/hash/index.ts +++ b/example/experimental/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/hash +@sinclair/typebox/extensions The MIT License (MIT) @@ -26,4 +26,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './hash' +export * from './experimental' diff --git a/example/experimental/readme.md b/example/experimental/readme.md new file mode 100644 index 000000000..16f8b3873 --- /dev/null +++ b/example/experimental/readme.md @@ -0,0 +1,30 @@ +# ExperimentalTypeBuilder + +An experimental TypeBox type builder with additional custom types. + +## Overview + +The TypeBox TypeBuilder classes are designed to be extended with user defined types. Instances where you may wish to do this are if your application is dependent on custom schematics and/or non-JSON serializable values (an example of which might be a Mongo's `ObjectId` or other such non-serializable value) + +## Application Type Builder + +The following shows creating a simple `ApplicationTypeBuilder` with additional types `Nullable` and `StringEnum`. These types are fairly common in OpenAPI implementations. + +```typescript +import { StandardTypeBuilder, Static, TSchema } from '@sinclair/typebox' + +export class ApplicationTypeBuilder extends StandardTypeBuilder { // only JSON Schema types + public Nullable(schema: T) { + return this.Unsafe | null>({ ...schema, nullable: true }) + } + public StringEnum(values: [...T]) { + return this.Unsafe({ type: 'string', enum: values }) + } +} + +export const Type = new ApplicationTypeBuilder() // re-export! +``` + +## Experimental Type Builder + +The `experimental.ts` file provided with this example shows advanced usage by creating complex types for potential inclusion in the TypeBox library in later revisions. It is offered for reference, experimentation and is open to contributor submission. \ No newline at end of file diff --git a/example/extensions/index.ts b/example/extensions/index.ts deleted file mode 100644 index c8ec2834b..000000000 --- a/example/extensions/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/extensions - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './intersect-allof' -export * from './readonly-object' -export * from './union-oneof' -export * from './union-enum' diff --git a/example/extensions/intersect-allof.ts b/example/extensions/intersect-allof.ts deleted file mode 100644 index 4a3a6016d..000000000 --- a/example/extensions/intersect-allof.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/extensions - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Kind, TSchema, SchemaOptions, IntersectReduce, IntersectEvaluate } from '@sinclair/typebox' -import { TypeGuard } from '@sinclair/typebox/guard' -import { Custom } from '@sinclair/typebox/custom' -import { Value } from '@sinclair/typebox/value' - -function IntersectAllOfCheck(schema: IntersectAllOf, value: unknown) { - if (!schema.allOf.every((schema) => Value.Check(schema, value))) return false - if (schema.unevaluatedProperties === undefined || schema.unevaluatedProperties === true) return true - const valueKeys = typeof value === 'object' && value !== null ? globalThis.Object.keys(value) : [] - const knownKeys = schema.allOf.reduce((set, schema) => { - if (!TypeGuard.TObject(schema)) return set - Object.keys(schema.properties).forEach((key) => set.add(key)) - return set - }, new Set()) - return valueKeys.every((key) => knownKeys.has(key)) -} - -export interface IntersectAllOfOptions extends SchemaOptions { - unevaluatedProperties?: boolean -} - -export interface IntersectAllOf extends TSchema, IntersectAllOfOptions { - [Kind]: 'IntersectAllOf' - static: IntersectReduce> - allOf: T -} - -/** Creates a Intersect type with a `allOf` schema representation */ -export function IntersectAllOf(allOf: [...T], options: IntersectAllOfOptions = {}) { - if (!Custom.Has('IntersectAllOf')) Custom.Set('IntersectAllOf', IntersectAllOfCheck) - return { ...options, [Kind]: 'IntersectAllOf', allOf } as IntersectAllOf -} diff --git a/example/extensions/readonly-object.ts b/example/extensions/readonly-object.ts deleted file mode 100644 index cf92fd2cc..000000000 --- a/example/extensions/readonly-object.ts +++ /dev/null @@ -1,56 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/extensions - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Type, TSchema, TObject, TProperties, Modifier, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' - -// prettier-ignore -export type TReadonlyObject = TObject<{ - [K in keyof T['properties']]: - T['properties'][K] extends TReadonlyOptional ? TReadonlyOptional : - T['properties'][K] extends TReadonly ? TReadonly : - T['properties'][K] extends TOptional ? TReadonlyOptional : - TReadonly -}> - -/** Remaps all properties of an object to be readonly */ -export function ReadonlyObject(schema: T): TReadonlyObject { - return Type.Object( - Object.keys(schema.properties).reduce((acc, key) => { - const property = schema.properties[key] as TSchema - const modifier = property[Modifier] - // prettier-ignore - const mapped = ( - (modifier === 'ReadonlyOptional') ? { ...property, [Modifier]: 'ReadonlyOptional' } : - (modifier === 'Readonly') ? { ...property, [Modifier]: 'Readonly' } : - (modifier === 'Optional') ? { ...property, [Modifier]: 'ReadonlyOptional' } : - ({...property, [Modifier]: 'Readonly' }) - ) - return { ...acc, [key]: mapped } - }, {} as TProperties), - ) as TReadonlyObject -} diff --git a/example/extensions/union-enum.ts b/example/extensions/union-enum.ts deleted file mode 100644 index 6f61163f2..000000000 --- a/example/extensions/union-enum.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/extensions - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Kind, TSchema, SchemaOptions } from '@sinclair/typebox' -import { Custom } from '@sinclair/typebox/custom' - -function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { - return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) -} - -export interface TUnionEnum extends TSchema { - [Kind]: 'UnionEnum' - static: T[number] - enum: T -} - -/** Creates a Union type with a `enum` schema representation */ -export function UnionEnum(values: [...T], options: SchemaOptions = {}) { - if (!Custom.Has('UnionEnum')) Custom.Set('UnionEnum', UnionEnumCheck) - return { ...options, [Kind]: 'UnionEnum', enum: values } as TUnionEnum -} diff --git a/example/extensions/union-oneof.ts b/example/extensions/union-oneof.ts deleted file mode 100644 index ef749b778..000000000 --- a/example/extensions/union-oneof.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/extensions - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Kind, TSchema, SchemaOptions, Static } from '@sinclair/typebox' -import { Custom } from '@sinclair/typebox/custom' -import { Value } from '@sinclair/typebox/value' - -function UnionOneOfCheck(schema: UnionOneOf, value: unknown) { - return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) -} - -export interface UnionOneOf extends TSchema { - [Kind]: 'UnionOneOf' - static: { [K in keyof T]: Static }[number] - oneOf: T -} - -/** Creates a Union type with a `oneOf` schema representation */ -export function UnionOneOf(oneOf: [...T], options: SchemaOptions = {}) { - if (!Custom.Has('UnionOneOf')) Custom.Set('UnionOneOf', UnionOneOfCheck) - return { ...options, [Kind]: 'UnionOneOf', oneOf } as UnionOneOf -} diff --git a/example/formats/additional.ts b/example/formats/additional.ts deleted file mode 100644 index 60396d3cf..000000000 --- a/example/formats/additional.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Format } from '@sinclair/typebox/format' - -/** Configures TypeBox for additional string formats */ -export namespace AdditionalFormats { - // ------------------------------------------------------------------------------------------- - // These formats are ported from the ajv-formats library. All credit goes to the original - // authors with the original code located at the following location. - // - // https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts - // ------------------------------------------------------------------------------------------- - - const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i - const DATE_TIME_SEPARATOR = /t|\s/i - const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i - const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ - const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ - const IPV6 = - /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i - const URL = - /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu - const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i - - function IsLeapYear(year: number): boolean { - return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) - } - - function IsDate(str: string): boolean { - const matches: string[] | null = DATE.exec(str) - if (!matches) return false - const year: number = +matches[1] - const month: number = +matches[2] - const day: number = +matches[3] - return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) - } - - function IsTime(str: string, strictTimeZone?: boolean): boolean { - const matches: string[] | null = TIME.exec(str) - if (!matches) return false - const hr: number = +matches[1] - const min: number = +matches[2] - const sec: number = +matches[3] - const tz: string | undefined = matches[4] - const tzSign: number = matches[5] === '-' ? -1 : 1 - const tzH: number = +(matches[6] || 0) - const tzM: number = +(matches[7] || 0) - if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false - if (hr <= 23 && min <= 59 && sec < 60) return true - // leap second - const utcMin = min - tzM * tzSign - const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) - return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 - } - - function IsDateTime(value: string, strictTimeZone?: boolean): boolean { - const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) - return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) - } - - function IsEmail(value: string) { - return EMAIL.test(value) - } - - function IsUuid(value: string) { - return UUID.test(value) - } - - function IsUrl(value: string) { - return URL.test(value) - } - - function IsIPv6(value: string) { - return IPV6.test(value) - } - - function IsIPv4(value: string) { - return IPV4.test(value) - } - - /** Configures additional string formats */ - export function Configure(): void { - Format.Set('date-time', (value) => IsDateTime(value, true)) - Format.Set('date', (value) => IsDate(value)) - Format.Set('time', (value) => IsTime(value)) - Format.Set('email', (value) => IsEmail(value)) - Format.Set('uuid', (value) => IsUuid(value)) - Format.Set('url', (value) => IsUrl(value)) - Format.Set('ipv6', (value) => IsIPv6(value)) - Format.Set('ipv4', (value) => IsIPv4(value)) - // - // optional: add additional formats - // - } -} diff --git a/example/formats/index.ts b/example/formats/index.ts index 2923362e3..4251323c5 100644 --- a/example/formats/index.ts +++ b/example/formats/index.ts @@ -1 +1 @@ -export * from './additional' +export * from './standard' diff --git a/example/formats/readme.md b/example/formats/readme.md new file mode 100644 index 000000000..03eb80d35 --- /dev/null +++ b/example/formats/readme.md @@ -0,0 +1,43 @@ +# String Formats + +TypeBox does not implement any string formats by default. However it is possible to register user defined formats using the `FormatRegistry`. Once registered, the format becomes available to both `Value` and `TypeCompiler` modules. + +## FormatRegistry + +The following shows basic usage of the format registry + +```typescript +import { Type, FormatRegistry } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +// Register the 'foo-only' format. The format checks for 'foo' only. +FormatRegistry.Set('foo-only', value => value === 'foo') + +const T = Type.String({ format: 'foo-only' }) + +// Validate +Value.Check(T, 'foo') // true +Value.Check(T, 'bar') // false +``` + +## Standard Formats + +The `standard.ts` file provided with this example implements several standard string formats. + +```typescript +import './standard' +``` + +The following formats are implemented by `standard.ts` + +| Format | Description | +| --- | --- | +| `email` | Internet email address, see [RFC 5321, section 4.1.2.](http://tools.ietf.org/html/rfc5321#section-4.1.2) | +| `date-time` | ISO8601 DateTime. example, `2018-11-13T20:20:39+00:00` | +| `date` | ISO8601 Date component, for example, `2018-11-13` | +| `time` | ISO8601 Time component, for example, `20:20:39+00:00` | +| `uuid` | A Universally Unique Identifier as defined by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122). | +| `url` | A uniform resource locator as defined in [RFC 1738](https://www.rfc-editor.org/rfc/rfc1738) +| `ipv4` | IPv4 address, according to dotted-quad ABNF syntax as defined in [RFC 2673, section 3.2](http://tools.ietf.org/html/rfc2673#section-3.2). | +| `ipv6` | IPv6 address, as defined in [RFC 2373, section 2.2](http://tools.ietf.org/html/rfc2373#section-2.2). | + diff --git a/example/formats/standard.ts b/example/formats/standard.ts new file mode 100644 index 000000000..7a0d9b06e --- /dev/null +++ b/example/formats/standard.ts @@ -0,0 +1,74 @@ +import { FormatRegistry } from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------------- +// Format Registration +// ------------------------------------------------------------------------------------------- +FormatRegistry.Set('date-time', (value) => IsDateTime(value, true)) +FormatRegistry.Set('date', (value) => IsDate(value)) +FormatRegistry.Set('time', (value) => IsTime(value)) +FormatRegistry.Set('email', (value) => IsEmail(value)) +FormatRegistry.Set('uuid', (value) => IsUuid(value)) +FormatRegistry.Set('url', (value) => IsUrl(value)) +FormatRegistry.Set('ipv6', (value) => IsIPv6(value)) +FormatRegistry.Set('ipv4', (value) => IsIPv4(value)) + +// ------------------------------------------------------------------------------------------- +// https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts +// ------------------------------------------------------------------------------------------- + +const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i +const DATE_TIME_SEPARATOR = /t|\s/i +const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] +const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ +const IPV6 = /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i +const URL = /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu +const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i +function IsLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) +} +function IsDate(str: string): boolean { + const matches: string[] | null = DATE.exec(str) + if (!matches) return false + const year: number = +matches[1] + const month: number = +matches[2] + const day: number = +matches[3] + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) +} +function IsTime(str: string, strictTimeZone?: boolean): boolean { + const matches: string[] | null = TIME.exec(str) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tz: string | undefined = matches[4] + const tzSign: number = matches[5] === '-' ? -1 : 1 + const tzH: number = +(matches[6] || 0) + const tzM: number = +(matches[7] || 0) + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false + if (hr <= 23 && min <= 59 && sec < 60) return true + const utcMin = min - tzM * tzSign + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 +} +function IsDateTime(value: string, strictTimeZone?: boolean): boolean { + const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) + return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) +} +function IsEmail(value: string) { + return EMAIL.test(value) +} +function IsUuid(value: string) { + return UUID.test(value) +} +function IsUrl(value: string) { + return URL.test(value) +} +function IsIPv6(value: string) { + return IPV6.test(value) +} +function IsIPv4(value: string) { + return IPV4.test(value) +} + diff --git a/example/index.ts b/example/index.ts index ed8d1cdc2..ed52156bd 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,12 +1,7 @@ -import { Codegen } from '@sinclair/typebox/codegen' import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' -import { TypeGuard } from '@sinclair/typebox/guard' -import { Conditional } from '@sinclair/typebox/conditional' -import { Format } from '@sinclair/typebox/format' -import { Custom } from '@sinclair/typebox/custom' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, Kind, Static, TSchema } from '@sinclair/typebox' +import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // ----------------------------------------------------------- // Create: Type diff --git a/example/trpc/readme.md b/example/trpc/readme.md index f3b26ccbc..e321ae253 100644 --- a/example/trpc/readme.md +++ b/example/trpc/readme.md @@ -1,6 +1,6 @@ # Using TypeBox with TRPC -To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assertion function. Once wrapped, TypeBox can provide TRPC auto type inference for procedures, enhanced runtime checking performance and well as providing options for enabling publishable procedure schematics based on the JSON Schema specification. +To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assertion function. Once wrapped, TypeBox can provide TRPC auto type inference for procedures, enhanced runtime checking performance as well as provide options to enabling publishable procedure schematics based on the JSON Schema specification. ## Contents @@ -57,11 +57,11 @@ import { TSchema } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' import { TRPCError } from '@trpc/server' -export function RpcType(schema: T, references: TSchema[] = []) { - const check = TypeCompiler.Compile(schema, references) +export function RpcType(schema: T) { + const check = TypeCompiler.Compile(schema) return (value: unknown) => { if (check.Check(value)) return value - const { path, message } = [...check.Errors(value)][0] + const { path, message } = check.Errors(value).First()! throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) } } @@ -78,10 +78,10 @@ import { Value } from '@sinclair/typebox/value' import { TSchema } from '@sinclair/typebox' import { TRPCError } from '@trpc/server' -export function RpcType(schema: T, references: TSchema[] = []) { +export function RpcType(schema: T) { return (value: unknown) => { if (Value.Check(schema, value)) return value - const { path, message } = [...Value.Errors(schema, references, value)][0] + const { path, message } = check.Errors(value).First()! throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) } } diff --git a/example/typedef/index.ts b/example/typedef/index.ts index 7b932b243..b5db4f7aa 100644 --- a/example/typedef/index.ts +++ b/example/typedef/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/extensions +@sinclair/typebox/typedef The MIT License (MIT) diff --git a/example/typedef/readme.md b/example/typedef/readme.md index 0f0106a67..07837ad72 100644 --- a/example/typedef/readme.md +++ b/example/typedef/readme.md @@ -1,100 +1,81 @@ -# JSON Type Definition +# TypeDef -JSON Type Definition (JTD) specification is an alternative schema specification to JSON Schema that provides better support for nominal type systems. TypeBox doesn't provide JTD support by default, but can be expressed through unsafe types and validated with Ajv. +TypeBox may offer support for [RPC8927](https://www.rfc-editor.org/rfc/rfc8927) JSON Type Definition in future revisions of the library. This specification is much simpler than JSON Schema but can be useful when describing schematics that need to be shared with nominal type languages. -This example provides a reference implementation for JSON Type Definition. +## Usage -## TypeDef - -Refer to the `typedef.ts` file in this directory for a reference implementation of the JSON Type Definition type builder. +The file `typedef.ts` provided with this example contains the provisional implementation for RPC8927. ```typescript -import { Static } from '@sinclair/typebox' -import { TypeDef } from './typedef' +import { Value } from '@sinclair/typebox/value' +import { Type, Static } from './typedef' + +const T = Type.Struct('T', { // const T = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: "float32" }, + z: Type.Float32() // y: { type: 'float32' }, +}) // z: { type: 'float32' } + // } + // } + +type T = Static // type T = { + // x: number, + // z: number, + // y: number + // } + +const R = Value.Check(T, { x: 1, y: 2, z: 3 }) // const R = true ``` -## Properties - -```typescript -// ------------------------------------------------------------------------ -// PropertiesType -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/#properties-schemas -// -// ------------------------------------------------------------------------ - -export type PropertiesType = Static -export const PropertiesType = TypeDef.Properties({ - x: TypeDef.Float32(), - y: TypeDef.Float32(), - z: TypeDef.Float32(), -}) -``` +### Unions -## Values +The JSON Type Definition has a different representation for unions and is primarily orientated towards discriminated unions. To use unions, you will need to name each struct on the first argument. TypeBox will take care of producing the union representation and static type. ```typescript -// ------------------------------------------------------------------------ -// ValuesType -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/#values-schemas -// -// ------------------------------------------------------------------------ - -export type ValuesType = Static -export const ValuesType = TypeDef.Values(TypeDef.Float64()) -``` - -## Enum - -```typescript -// ------------------------------------------------------------------------ -// EnumType -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/#enum-schemas -// -// ------------------------------------------------------------------------ - -export type EnumType = Static -export const EnumType = TypeDef.Enum(['FOO', 'BAR', 'BAZ']) -``` - -## Elements - -```typescript -// ------------------------------------------------------------------------ -// ElementsType -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/#elements-schemas -// -// ------------------------------------------------------------------------ - -export type ElementsType = Static -export const ElementsType = TypeDef.Elements(PropertiesType) -``` - -## Union - -```typescript -// ------------------------------------------------------------------------ -// UnionType -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/#discriminator-schemas -// -// ------------------------------------------------------------------------ - -export type UnionType = Static -export const UnionType = TypeDef.Union('eventType', { - USER_CREATED: TypeDef.Properties({ - id: TypeDef.String(), - }), - USER_PAYMENT_PLAN_CHANGED: TypeDef.Properties({ - id: TypeDef.String(), - plan: TypeDef.Enum(['FREE', 'PAID']), - }), - USER_DELETED: TypeDef.Properties({ - id: TypeDef.String(), - softDelete: TypeDef.Boolean(), - }), -}) +import { Type, Static } from './typedef' + +const Vector2 = Type.Struct('Vector2', { // const Vector2 = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, +}) // y: { type: 'float32' } + // } + // } + +const Vector3 = Type.Struct('Vector3', { // const Vector3 = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, + z: Type.Float32() // y: { type: 'float32' }, +}) // z: { type: 'float32' } + // } + // } + +const Union = Type.Union('type', [ // const Union = { + Vector2, // discriminator: 'type', + Vector3 // mapping: { +]) // Vector2: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' }, + // } + // }, + // Vector3: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' }, + // z: { type: 'float32' } + // } + // } + // } + // } + +type Union = Static // type Union = { + // type: 'Vector2' + // x: number + // y: number + // } | { + // type: 'Vector3' + // x: number + // y: number + // z: number + // } ``` diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 06a60c1bc..544d1b893 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -26,63 +26,378 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Type, Static, TUnsafe } from '@sinclair/typebox' +export { type Static, TSchema, PropertiesReduce, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' +import * as Types from '@sinclair/typebox' -// ------------------------------------------------------------------------ -// TypeDef Namespace -// -// https://jsontypedef.com/docs/jtd-in-5-minutes/ -// ------------------------------------------------------------------------ - -export type StaticTypeDefUnion>> = { [K in keyof M]: { [P in D]: K } & Static }[keyof M] - -export namespace TypeDef { - export function Boolean() { - return Type.Unsafe({ type: 'boolean' }) +// -------------------------------------------------------------------------- +// Symbols +// -------------------------------------------------------------------------- +export const Name = Symbol.for('TypeBox:Name') +// -------------------------------------------------------------------------- +// TArray +// -------------------------------------------------------------------------- +export interface TArray extends Types.TSchema { + [Types.Kind]: 'TypeDef:Array' + static: Types.Static[] + elements: T +} +// -------------------------------------------------------------------------- +// TBoolean +// -------------------------------------------------------------------------- +export interface TBoolean extends Types.TSchema { + [Types.Kind]: 'TypeDef:Boolean' + static: 'boolean' + type: 'boolean' +} +// -------------------------------------------------------------------------- +// TEnum +// -------------------------------------------------------------------------- +export interface TEnum extends Types.TSchema { + [Types.Kind]: 'TypeDef:Enum' + static: T[number] + enum: [...T] +} +// -------------------------------------------------------------------------- +// TFloat32 +// -------------------------------------------------------------------------- +export interface TFloat32 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Float32' + type: 'float32' + static: number +} +// -------------------------------------------------------------------------- +// TFloat64 +// -------------------------------------------------------------------------- +export interface TFloat64 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Float64' + type: 'float64' + static: number +} +// -------------------------------------------------------------------------- +// TInt8 +// -------------------------------------------------------------------------- +export interface TInt8 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Int8' + type: 'int8' + static: number +} +// -------------------------------------------------------------------------- +// TInt16 +// -------------------------------------------------------------------------- +export interface TInt16 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Int16' + type: 'int16' + static: number +} +// -------------------------------------------------------------------------- +// TInt32 +// -------------------------------------------------------------------------- +export interface TInt32 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Int32' + type: 'int32' + static: number +} +// -------------------------------------------------------------------------- +// TUint8 +// -------------------------------------------------------------------------- +export interface TUint8 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Uint8' + type: 'uint8' + static: number +} +// -------------------------------------------------------------------------- +// TUint16 +// -------------------------------------------------------------------------- +export interface TUint16 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Uint16' + type: 'uint16' + static: number +} +// -------------------------------------------------------------------------- +// TUint32 +// -------------------------------------------------------------------------- +export interface TUint32 extends Types.TSchema { + [Types.Kind]: 'TypeDef:Uint32' + type: 'uint32' + static: number +} +// -------------------------------------------------------------------------- +// TProperties +// -------------------------------------------------------------------------- +export type TFields = Record +// -------------------------------------------------------------------------- +// TRecord +// -------------------------------------------------------------------------- +export interface TRecord extends Types.TSchema { + [Types.Kind]: 'TypeDef:Record' + static: Record> + values: T +} +// -------------------------------------------------------------------------- +// TString +// -------------------------------------------------------------------------- +export interface TString extends Types.TSchema { + [Types.Kind]: 'TypeDef:String' + static: string +} +// -------------------------------------------------------------------------- +// TStruct +// -------------------------------------------------------------------------- +type OptionalKeys = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? T[K] : never } +type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? never : T[K] } +export interface StructOptions { + additionalProperties?: boolean +} +export interface TStruct extends Types.TSchema, StructOptions { + [Name]: D + [Types.Kind]: 'TypeDef:Struct' + static: Types.PropertiesReduce + optionalProperties: {[K in Types.Assert, keyof T>]: T[K] } + properties: {[K in Types.Assert, keyof T>]: T[K] } +} +// -------------------------------------------------------------------------- +// TTimestamp +// -------------------------------------------------------------------------- +export interface TTimestamp extends Types.TSchema { + [Types.Kind]: 'TypeDef:Timestamp' + static: number +} +// -------------------------------------------------------------------------- +// TUnion +// -------------------------------------------------------------------------- +export interface TUnion extends Types.TSchema { + [Types.Kind]: 'TypeDef:Union' + static: Types.Evaluate<{ [K in keyof T]: { [key in D]: T[K][typeof Name] } & Types.Static }[number]> + discriminator: D, + mapping: T +} +// -------------------------------------------------------------------------- +// TypeRegistry +// -------------------------------------------------------------------------- +Types.TypeRegistry.Set('TypeDef:Array', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:String', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => TypeDefCheck.Check(schema, value)) +// -------------------------------------------------------------------------- +// TypeDefCheck +// -------------------------------------------------------------------------- +export class TypeDefCheckUnionTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('TypeDefCheck: Unknown type') + } +} +export namespace TypeDefCheck { + // ------------------------------------------------------------------------ + // Guards + // ------------------------------------------------------------------------ + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) + } + function IsArray(value: unknown): value is unknown[] { + return globalThis.Array.isArray(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsInt(value: unknown, min: number, max: number): value is number { + return typeof value === 'number' && globalThis.Number.isInteger(value) && value >= min && value < max + } + // ------------------------------------------------------------------------ + // Types + // ------------------------------------------------------------------------ + function Array(schema: TArray, value: unknown): boolean { + return IsArray(value) && value.every(value => Visit(schema.elements, value)) } - export function String() { - return Type.Unsafe({ type: 'string' }) + function Boolean(schema: TBoolean, value: unknown): boolean { + return typeof value === 'boolean' } - export function TimeStamp() { - return Type.Unsafe({ type: 'timestamp' }) + function Enum(schema: TEnum, value: unknown): boolean { + return typeof value === 'string' && schema.enum.includes(value) } - export function Float32() { - return Type.Unsafe({ type: 'float32' }) + function Float32(schema: TFloat32, value: unknown): boolean { + return typeof value === 'number' } - export function Float64() { - return Type.Unsafe({ type: 'float64' }) + function Float64(schema: TFloat64, value: unknown): boolean { + return typeof value === 'number' } - export function Int8() { - return Type.Unsafe({ type: 'int8' }) + function Int8(schema: TInt8, value: unknown): boolean { + return IsInt(value, -128, 127) } - export function Uint8() { - return Type.Unsafe({ type: 'uint8' }) + function Int16(schema: TInt16, value: unknown): boolean { + return IsInt(value, -32_768, 32_767) } - export function Int16() { - return Type.Unsafe({ type: 'int16' }) + function Int32(schema: TInt32, value: unknown): boolean { + return IsInt(value, -2_147_483_648, 2_147_483_647) } - export function Uint16() { - return Type.Unsafe({ type: 'uint16' }) + function Uint8(schema: TUint8, value: unknown): boolean { + return IsInt(value, 0, 255) } - export function Int32() { - return Type.Unsafe({ type: 'int32' }) + function Uint16(schema: TUint16, value: unknown): boolean { + return IsInt(value, 0, 65535) } - export function Uint32() { - return Type.Unsafe({ type: 'uint32' }) + function Uint32(schema: TUint32, value: unknown): boolean { + return IsInt(value, 0, 4_294_967_295) } - export function Enum(values: [...T]) { - return Type.Unsafe({ enum: values }) + function Record(schema: TRecord, value: unknown): boolean { + return IsObject(value) && globalThis.Object.getOwnPropertyNames(value).every(key => Visit(schema.values, value[key])) } - export function Elements>(element: T) { - return Type.Unsafe>>({ elements: element }) + function String(schema: TString, value: unknown): boolean { + return typeof value === 'string' } - export function Properties>>(properties: T) { - return Type.Unsafe<{ [K in keyof T]: Static }>({ properties }) + function Struct(schema: TStruct, value: unknown): boolean { + const optionalKeys = schema.optionalProperties === undefined ? [] : globalThis.Object.getOwnPropertyNames(schema.optionalProperties) + const requiredKeys = schema.properties === undefined ? [] : globalThis.Object.getOwnPropertyNames(schema.properties) + if(!(IsObject(value) && + optionalKeys.every(key => key in value ? Visit((schema.optionalProperties as any)[key], value[key]) : true) && + requiredKeys.every(key => key in value && Visit(((schema as any).properties[key] as any), value[key])))) return false + if(schema.additionalProperties === true) return true + const unknownKeys = globalThis.Object.getOwnPropertyNames(value) + return unknownKeys.every(key => optionalKeys.includes(key) || requiredKeys.includes(key)) } - export function Values>(values: V) { - return Type.Unsafe>>({ values }) + function Timestamp(schema: TString, value: unknown): boolean { + return IsInt(value, 0, Number.MAX_SAFE_INTEGER) } - export function Union>>(discriminator: D, mapping: M) { - return Type.Unsafe>({ discriminator, mapping }) + function Union(schema: TUnion, value: unknown): boolean { + return IsObject(value) && + schema.discriminator in value && + IsString(value[schema.discriminator]) && + value[schema.discriminator] as any in schema.mapping && + Visit(schema.mapping[value[schema.discriminator] as any], value) + } + function Visit(schema: Types.TSchema, value: unknown): boolean { + const anySchema = schema as any + switch(anySchema[Types.Kind]) { + case 'TypeDef:Array': return Array(anySchema, value) + case 'TypeDef:Boolean': return Boolean(anySchema, value) + case 'TypeDef:Enum': return Enum(anySchema, value) + case 'TypeDef:Float32': return Float32(anySchema, value) + case 'TypeDef:Float64': return Float64(anySchema, value) + case 'TypeDef:Int8': return Int8(anySchema, value) + case 'TypeDef:Int16': return Int16(anySchema, value) + case 'TypeDef:Int32': return Int32(anySchema, value) + case 'TypeDef:Uint8': return Uint8(anySchema, value) + case 'TypeDef:Uint16': return Uint16(anySchema, value) + case 'TypeDef:Uint32': return Uint32(anySchema, value) + case 'TypeDef:Record': return Record(anySchema, value) + case 'TypeDef:String': return String(anySchema, value) + case 'TypeDef:Struct': return Struct(anySchema, value) + case 'TypeDef:Timestamp': return Timestamp(anySchema, value) + case 'TypeDef:Union': return Union(anySchema, value) + default: throw new TypeDefCheckUnionTypeError(anySchema) + } + } + export function Check(schema: Types.TSchema, value: unknown): boolean { + return Visit(schema, value) + } +} +// -------------------------------------------------------------------------- +// TypeDefTypeBuilder +// -------------------------------------------------------------------------- +export class TypeDefTypeBuilder extends Types.TypeBuilder { + // ------------------------------------------------------------------------ + // Modifiers + // ------------------------------------------------------------------------ + /** `[Modifier]` Creates a Optional property */ + public Optional(schema: T): Types.TOptional { + return { [Types.Modifier]: 'Optional', ...Types.TypeClone.Clone(schema, {}) } + } + /** `[Modifier]` Creates a ReadonlyOptional property */ + public ReadonlyOptional(schema: T): Types.TReadonlyOptional { + return { [Types.Modifier]: 'ReadonlyOptional', ...Types.TypeClone.Clone(schema, {}) } + } + /** `[Modifier]` Creates a Readonly object or property */ + public Readonly(schema: T): Types.TReadonly { + return { [Types.Modifier]: 'Readonly', ...schema } + } + // ------------------------------------------------------------------------ + // Modifiers + // ------------------------------------------------------------------------ + /** `[Standard]` Creates a TypeDef Array type */ + public Array(elements: T): TArray { + return this.Create({ [Types.Kind]: 'TypeDef:Array', elements }) + } + /** `[Standard]` Creates a TypeDef Boolean type */ + public Boolean(): TBoolean { + return this.Create({ [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }) + } + /** `[Standard]` Creates a TypeDef Enum type */ + public Enum(values: [...T]): TEnum { + return this.Create({ [Types.Kind]: 'TypeDef:Enum', enum: values }) + } + /** `[Standard]` Creates a TypeDef Float32 type */ + public Float32(): TFloat32 { + return this.Create({ [Types.Kind]: 'TypeDef:Float32', type: 'float32' }) + } + /** `[Standard]` Creates a TypeDef Float64 type */ + public Float64(): TFloat64 { + return this.Create({ [Types.Kind]: 'TypeDef:Float64', type: 'float64' }) + } + /** `[Standard]` Creates a TypeDef Int8 type */ + public Int8(): TInt8 { + return this.Create({ [Types.Kind]: 'TypeDef:Int8', type: 'int8' }) + } + /** `[Standard]` Creates a TypeDef Int16 type */ + public Int16(): TInt16 { + return this.Create({ [Types.Kind]: 'TypeDef:Int16', type: 'int16' }) + } + /** `[Standard]` Creates a TypeDef Int32 type */ + public Int32(): TInt32 { + return this.Create({ [Types.Kind]: 'TypeDef:Int32', type: 'int32' }) + } + /** `[Standard]` Creates a TypeDef Uint8 type */ + public Uint8(): TUint8 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }) + } + /** `[Standard]` Creates a TypeDef Uint16 type */ + public Uint16(): TUint16 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }) + } + /** `[Standard]` Creates a TypeDef Uint32 type */ + public Uint32(): TUint32 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }) + } + /** `[Standard]` Creates a TypeDef Record type */ + public Record(values: T): TRecord { + return this.Create({ [Types.Kind]: 'TypeDef:Record',values }) + } + /** `[Standard]` Creates a TypeDef String type */ + public String(): TString { + return this.Create({ [Types.Kind]: 'TypeDef:String',type: 'string' }) + } + /** `[Standard]` Creates a TypeDef Struct type */ + public Struct(name: N, fields: T, options?: StructOptions): TStruct { + const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) + const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? {... acc } : { ...acc, [key]: fields[key] }), {} as TFields) + const optionalPropertiesObject = globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0 ? { optionalProperties: optionalProperties } : {} + const propertiesObject = globalThis.Object.getOwnPropertyNames(properties).length === 0 ? {} : { properties: properties } + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Struct', [Name]: name, ...propertiesObject, ...optionalPropertiesObject }) + } + /** `[Standard]` Creates a TypeDef Timestamp type */ + public Timestamp(): TTimestamp { + return this.Create({ [Types.Kind]: 'TypeDef:Timestamp', type: 'timestamp' }) + } + + /** `[Standard]` Creates a TypeDef Discriminated Union type */ + public Union[]>(discriminator: D, objects: [...T]): TUnion { + if(objects.length === 0) throw new Error('TypeDefTypeBuilder: Union types must have at least one object') + const exists = objects.every(object => typeof object[Name] === 'string') + if(!exists) throw new Error('TypeDefTypeBuilder: All union objects MUST have a descriminator') + const unique = objects.reduce((set, current) => set.add(current[Name]), new Set()) + if(unique.size !== objects.length) throw new Error('TypeDefTypeBuilder: All union objects MUST unique descriminator strings') + const mapping = objects.reduce((acc, current) => ({ ...acc, [current[Name]]: current }), {}) + return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }) } } + +/** JSON Type Definition Type Builder */ +export const Type = new TypeDefTypeBuilder() + diff --git a/hammer.mjs b/hammer.mjs index 19e88cee3..0957385eb 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -1,11 +1,9 @@ import { compression, measurement } from './benchmark' import { readFileSync } from 'fs' - // ------------------------------------------------------------------------------- // Clean // ------------------------------------------------------------------------------- - export async function clean() { await folder('target').delete() } @@ -13,15 +11,13 @@ export async function clean() { // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- - export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example benchmark codegen') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark codegen') } // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- - export async function start(example = 'index') { await shell(`hammer run example/${example}.ts --dist target/example/${example}`) } @@ -29,34 +25,27 @@ export async function start(example = 'index') { // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- - export async function benchmark() { await compression() await measurement() } - // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- - export async function test_static() { await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) } - export async function test_runtime(filter) { await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) await shell(`mocha target/test/runtime/index.js -g "${filter}"`) } - export async function test(filter = '') { await test_static() await test_runtime(filter) } - // ------------------------------------------------------------------------------- // Build // ------------------------------------------------------------------------------- - export async function build(target = 'target/build') { await test() await folder(target).delete() @@ -66,12 +55,9 @@ export async function build(target = 'target/build') { await folder(target).add('license') await shell(`cd ${target} && npm pack`) } - - // ------------------------------------------------------------- // Publish // ------------------------------------------------------------- - export async function publish(otp, target = 'target/build') { const { version } = JSON.parse(readFileSync('package.json', 'utf8')) await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) diff --git a/package-lock.json b/package-lock.json index 63868b5b2..e013c6885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.25.21", + "version": "0.26.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.25.21", + "version": "0.26.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", @@ -18,7 +18,7 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^4.9.3" + "typescript": "^5.0.2" } }, "node_modules/@esbuild/linux-loong64": { @@ -1410,16 +1410,16 @@ } }, "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=12.20" } }, "node_modules/uri-js": { @@ -2442,9 +2442,9 @@ "dev": true }, "typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", + "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 50ce0021c..8043b31b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.25.24", + "version": "0.26.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -14,12 +14,7 @@ "types": "./typebox.d.ts", "exports": { "./compiler": "./compiler/index.js", - "./conditional": "./conditional/index.js", - "./custom": "./custom/index.js", "./errors": "./errors/index.js", - "./format": "./format/index.js", - "./guard": "./guard/index.js", - "./hash": "./hash/index.js", "./system": "./system/index.js", "./value": "./value/index.js", ".": "./typebox.js" @@ -47,6 +42,6 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^4.9.3" + "typescript": "^5.0.2" } } diff --git a/readme.md b/readme.md index 34acd8bbd..2d199c156 100644 --- a/readme.md +++ b/readme.md @@ -35,7 +35,7 @@ import { Static, Type } from 'npm:@sinclair/typebox' import { Static, Type } from 'https://esm.sh/@sinclair/typebox' ``` -## Usage +## Example ```typescript import { Static, Type } from '@sinclair/typebox' @@ -62,32 +62,33 @@ type T = Static // type T = { ## Overview -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 a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. +TypeBox is a runtime type builder 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 assertion rules of the TypeScript compiler. TypeBox enables one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. -This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used either as a simple tool to build up complex schemas or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. License MIT ## Contents - [Install](#install) - [Overview](#overview) -- [Example](#Example) +- [Usage](#usage) - [Types](#types) - [Standard](#types-standard) - [Extended](#types-extended) - [Modifiers](#types-modifiers) - [Options](#types-options) - - [Reference](#types-reference) + - [Generics](#types-generics) + - [References](#types-references) - [Recursive](#types-recursive) - - [Generic](#types-generic) - [Conditional](#types-conditional) - - [Unsafe](#types-unsafe) - [Guards](#types-guards) + - [Unsafe](#types-unsafe) - [Strict](#types-strict) - [Values](#values) - [Create](#values-create) - [Clone](#values-clone) - [Check](#values-check) + - [Convert](#values-convert) - [Cast](#values-cast) - [Equal](#values-equal) - [Hash](#values-hash) @@ -101,20 +102,20 @@ License MIT - [TypeSystem](#typecheck) - [Types](#typesystem-types) - [Formats](#typesystem-formats) + - [Policies](#typesystem-policies) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) - [Validate](#benchmark-validate) - [Compression](#benchmark-compression) - [Contribute](#contribute) - + -## Example +## Usage -The following demonstrates TypeBox's general usage. +The following shows general usage. ```typescript - import { Static, Type } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- @@ -173,9 +174,11 @@ type T = Static // type T = { // //-------------------------------------------------------------------------------------------- -function receive(value: T) { // ... as a Type +import { Value } from '@sinclair/typebox/value' + +function receive(value: T) { // ... as a Static Type - if(JSON.validate(T, value)) { // ... as a Schema + if(Value.Check(T, value)) { // ... as a JSON Schema // ok... } @@ -186,13 +189,13 @@ function receive(value: T) { // ... as a Type ## Types -TypeBox provides a set of functions that allow you to compose JSON Schema similar to how you would compose static types with TypeScript. Each function creates a JSON schema fragment which can compose into more complex types. The schemas produced by TypeBox can be passed directly to any JSON Schema compliant validator, or used to reflect runtime metadata for a type. +TypeBox types are JSON schema fragments that can be composed into more complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. -### Standard +### Standard Types -The following table lists the Standard TypeBox types. +The following table lists the Standard TypeBox types. These types are fully compatible with the JSON Schema Draft 6 specification. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -230,12 +233,6 @@ The following table lists the Standard TypeBox types. │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │ -│ │ │ type: 'string', │ -│ │ │ pattern: 'foo' │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Literal(42) │ type T = 42 │ const T = { │ │ │ │ const: 42, │ │ │ │ type: 'number' │ @@ -252,15 +249,14 @@ The following table lists the Standard TypeBox types. ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ │ x: Type.Number(), │ x: number, │ type: 'object', │ -│ y: Type.Number() │ y: number │ properties: { │ -│ }) │ } │ x: { │ -│ │ │ type: 'number' │ -│ │ │ }, │ -│ │ │ y: { │ -│ │ │ type: 'number' │ -│ │ │ } │ -│ │ │ }, │ -│ │ │ required: ['x', 'y'] │ +│ y: Type.Number() │ y: number │ required: ['x', 'y'], │ +│ }) │ } │ properties: { │ +│ │ │ x: { │ +│ │ │ type: 'number' │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -310,13 +306,36 @@ The following table lists the Standard TypeBox types. │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Intersect([ │ type T = { │ const T = { │ -│ Type.Object({ │ x: number │ type: 'object', │ -│ x: Type.Number() │ } & { │ properties: { │ -│ }), │ y: number │ x: { │ -│ Type.Object({ │ } │ type: 'number' │ -│ y: Type.Number() │ │ }, │ -│ }) │ │ y: { │ -│ ]) │ │ type: 'number' │ +│ Type.Object({ │ x: number │ allOf: [{ │ +│ x: Type.Number() │ } & { │ type: 'object', │ +│ }), │ y: number │ required: ['x'], │ +│ Type.Object({ │ } │ properties: { │ +│ y: Type.Number() │ │ x: { │ +│ ]) │ │ type: 'number' │ +│ ]) │ │ } │ +│ │ │ } │ +│ │ │ }, { │ +│ │ │ type: 'object', | +│ │ │ required: ['y'], │ +│ │ │ properties: { │ +│ │ │ y: { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ }] │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Composite([ │ type T = { │ const T = { │ +│ Type.Object({ │ x: number | string │ type: 'object', │ +│ x: Type.Number() │ y: number │ properties: { │ +│ }), │ } │ x: { │ +│ Type.Object({ │ │ anyOf: [ │ +│ x: Type.String() │ │ { type: 'number' }, │ +│ y: Type.Number() │ │ { type: 'string' } │ +│ }) │ │ ] │ +│ ]) │ │ }, │ +│ │ │ y: { │ +│ │ │ type: 'number' │ │ │ │ } │ │ │ │ }, │ │ │ │ required: ['x', 'y'] │ @@ -324,16 +343,51 @@ The following table lists the Standard TypeBox types. │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Never() │ type T = never │ const T = { │ -│ │ │ allOf: [{ │ -│ │ │ type: 'boolean', │ -│ │ │ const: false │ +│ │ │ not: {} │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Not( | type T = string │ const T = { │ +| Type.Union([ │ │ allOf: [{ │ +│ Type.Literal('x'), │ │ not: { │ +│ Type.Literal('y'), │ │ anyOf: [ │ +│ Type.Literal('z') │ │ { const: 'x' }, │ +│ ]), │ │ { const: 'y' }, │ +│ Type.String() │ │ { const: 'z' } │ +│ ) │ │ ] │ +│ │ │ } │ │ │ │ }, { │ -│ │ │ type: 'boolean', │ -│ │ │ const: true │ +│ │ │ type: 'string' │ │ │ │ }] │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Extends( │ type T = │ const T = { │ +│ Type.String(), │ string extends number │ const: false, │ +│ Type.Number(), │ true : false │ type: 'boolean' │ +│ Type.Literal(true), │ │ } │ +│ Type.Literal(false) │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Extract( │ type T = Extract< │ const T = { │ +│ Type.Union([ │ string | number, │ type: 'string' │ +│ Type.String(), │ string │ } │ +│ Type.Number(), │ > │ │ +│ ]), │ │ │ +│ Type.String() │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Exclude( │ type T = Exclude< │ const T = { │ +│ Type.Union([ │ string | number, │ type: 'number' │ +│ Type.String(), │ string │ } │ +│ Type.Number(), │ > │ │ +│ ]), │ │ │ +│ Type.String() │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Record( │ type T = Record< │ const T = { │ │ Type.String(), │ string, │ type: 'object', │ │ Type.Number() │ number, │ patternProperties: { │ @@ -359,15 +413,15 @@ The following table lists the Standard TypeBox types. ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Required( │ type T = Required<{ │ const T = { │ │ Type.Object({ │ x?: number, │ type: 'object', │ -│ x: Type.Optional( │ y?: number │ properties: { │ -│ Type.Number() | }> │ x: { │ -│ ), │ │ type: 'number' │ -│ y: Type.Optional( │ │ }, │ -│ Type.Number() │ │ y: { │ -│ ) │ │ type: 'number' │ -│ }) │ │ } │ -│ ) │ │ }, │ -│ │ │ required: ['x', 'y'] │ +│ x: Type.Optional( │ y?: number │ required: ['x', 'y'], │ +│ Type.Number() | }> │ properties: { │ +│ ), │ │ x: { │ +│ y: Type.Optional( │ │ type: 'number' │ +│ Type.Number() │ │ }, │ +│ ) │ │ y: { │ +│ }) │ │ type: 'number' │ +│ ) │ │ } │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -392,14 +446,25 @@ The following table lists the Standard TypeBox types. │ │ │ required: ['y'] │ │ │ │ } │ │ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const A = Type.Object({ │ type A = { │ const T = { │ +│ x: Type.Number(), │ x: number, │ $ref: 'A' │ +│ y: Type.Number() │ y: number │ } │ +│ }, { $id: 'T' }) | } │ │ +│ │ │ │ +│ const T = Type.Ref(A) │ type T = A │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` -### Extended +### Extended Types -TypeBox provides a set of extended types that can be used to express schematics for core JavaScript constructs and primitives. Extended types are not valid JSON Schema and will not validate using typical validation. These types however can be used to frame JSON schema and describe callable RPC interfaces that may receive JSON validated data. +TypeBox provides several extended types that can be used to produce schematics for common JavaScript constructs. These types can not be used with standard JSON schema validators; but are useful to help frame schematics for RPC interfaces that may receive JSON validated data. Extended types are prefixed with the `[Extended]` doc comment for convenience. The following table lists the supported types. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -462,6 +527,24 @@ TypeBox provides a set of extended types that can be used to express schematics │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │ +│ │ │ type: 'string', │ +│ │ │ pattern: 'foo' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Symbol() │ type T = symbol │ const T = { │ +│ │ │ type: 'null', │ +│ │ │ typeOf: 'Symbol' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.BigInt() │ type T = bigint │ const T = { │ +│ │ │ type: 'null', │ +│ │ │ typeOf: 'BigInt' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Void() │ type T = void │ const T = { │ │ │ │ type: 'null' │ │ │ │ typeOf: 'Void' │ @@ -474,7 +557,7 @@ TypeBox provides a set of extended types that can be used to express schematics ### Modifiers -TypeBox provides modifiers that can be applied to an objects properties. This allows for `optional` and `readonly` to be applied to that property. The following table illustates how they map between TypeScript and JSON Schema. +TypeBox provides modifiers that allow schema properties to be statically inferred as `readonly` or `optional`. The following table shows the supported modifiers and how they map between TypeScript and JSON Schema. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -484,9 +567,9 @@ TypeBox provides modifiers that can be applied to an objects properties. This al │ const T = Type.Object({ │ type T = { │ const T = { │ │ name: Type.Optional( │ name?: string │ type: 'object', │ │ Type.String() │ } │ properties: { │ -│ ) │ │ name: { │ -│ }) │ │ type: 'string' │ -│ │ │ } │ +│ ) │ │ name: { │ +│ }) │ │ type: 'string' │ +│ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ │ @@ -518,24 +601,96 @@ TypeBox provides modifiers that can be applied to an objects properties. This al ### Options -You can pass additional JSON schema options on the last argument of any given type. The following are some examples. +You can pass JSON Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. ```typescript -// string must be an email -const T = Type.String({ format: 'email' }) +// String must be an email +const T = Type.String({ // const T = { + format: 'email' // type: 'string', +}) // format: 'email' + // } -// number must be a multiple of 2 -const T = Type.Number({ multipleOf: 2 }) +// Mumber must be a multiple of 2 +const T = Type.Number({ // const T = { + multipleOf: 2 // type: 'number', +}) // multipleOf: 2 + // } + +// Array must have at least 5 integer values +const T = Type.Array(Type.Integer(), { // const T = { + minItems: 5 // type: 'array', +}) // minItems: 5, + // items: { + // type: 'integer' + // } + // } -// array must have at least 5 integer values -const T = Type.Array(Type.Integer(), { minItems: 5 }) ``` - + -### Reference +### Generic Types -Use `Type.Ref(...)` to create referenced types. The target type must specify an `$id`. +Generic types can be created with generic functions constrained to type `TSchema`. The following creates a generic `Vector` type. + +```typescript +import { Type, Static, TSchema } from '@sinclair/typebox' + +const Vector = (t: T) => Type.Object({ x: t, y: t, z: t }) + +const NumberVector = Vector(Type.Number()) // const NumberVector = { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'number' }, + // y: { type: 'number' }, + // z: { type: 'number' } + // } + // } + +type NumberVector = Static // type NumberVector = { + // x: number, + // y: number, + // z: number + // } + +const BooleanVector = Vector(Type.Boolean()) // const BooleanVector = { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'boolean' }, + // y: { type: 'boolean' }, + // z: { type: 'boolean' } + // } + // } + +type BooleanVector = Static // type BooleanVector = { + // x: boolean, + // y: boolean, + // z: boolean + // } +``` + +The following creates a generic `Nullable` type. + +```typescript +const Nullable = (schema: T) => Type.Union([schema, Type.Null()]) + +const T = Nullable(Type.String()) // const T = { + // anyOf: [ + // { type: 'string' }, + // { type: 'null' } + // ] + // } + +type T = Static // type T = string | null +``` + + + +### Reference Types + +Reference types are supported with `Type.Ref(...)`. The target type must specify a valid `$id`. ```typescript const T = Type.String({ $id: 'T' }) // const T = { @@ -550,9 +705,9 @@ const R = Type.Ref(T) // const R = { -### Recursive +### Recursive Types -Use `Type.Recursive(...)` to create recursive types. +Recursive types are supported with `Type.Recursive(...)`. ```typescript const Node = Type.Recursive(Node => Type.Object({ // const Node = { @@ -581,102 +736,41 @@ type Node = Static // type Node = { // } function test(node: Node) { - const id = node.nodes[0].nodes[0] // id is string - .nodes[0].nodes[0] - .id + const id = node.nodes[0].nodes[0].id // id is string } ``` - - -### Generic - -Use functions to create generic types. The following creates a generic `Nullable` type. - -```typescript -import { Type, Static, TSchema } from '@sinclair/typebox' - -const Nullable = (type: T) => Type.Union([type, Type.Null()]) - -const T = Nullable(Type.String()) // const T = { - // anyOf: [{ - // type: 'string' - // }, { - // type: 'null' - // }] - // } - -type T = Static // type T = string | null - -const U = Nullable(Type.Number()) // const U = { - // anyOf: [{ - // type: 'number' - // }, { - // type: 'null' - // }] - // } - -type U = Static // type U = number | null -``` - -### Conditional +### Conditional Types -Use the conditional module to create [Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html). This module implements TypeScript's structural equivalence checks to enable TypeBox types to be conditionally inferred at runtime. This module also provides the [Extract](https://www.typescriptlang.org/docs/handbook/utility-types.html#extracttype-union) and [Exclude](https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers) utility types which are expressed as conditional types in TypeScript. +Conditional types are supported with `Extends`, `Exclude` and `Extract`. -The conditional module is provided as an optional import. +**TypeScript** ```typescript -import { Conditional } from '@sinclair/typebox/conditional' +type T0 = string extends number ? true : false +// ^ false +type T1 = Extract +// ^ number +type T2 = Exclude +// ^ string ``` -The following table shows the TypeBox mappings between TypeScript and JSON schema. - +**TypeBox** ```typescript -┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ -│ TypeBox │ TypeScript │ JSON Schema │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Conditional.Extends( │ type T = │ const T = { │ -│ Type.String(), │ string extends number │ const: false, │ -│ Type.Number(), │ true : false │ type: 'boolean' │ -│ Type.Literal(true), │ │ } │ -│ Type.Literal(false) │ │ │ -│ ) │ │ │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Conditional.Extract( │ type T = Extract< │ const T = { │ -│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │ -│ Type.Literal('a'), │ 'a' | 'f' │ const: 'a' │ -│ Type.Literal('b'), │ > │ type: 'string' │ -│ Type.Literal('c') │ │ }] │ -│ ]), │ │ } │ -│ Type.Union([ │ │ │ -│ Type.Literal('a'), │ │ │ -│ Type.Literal('f') │ │ │ -│ ]) │ │ │ -│ ) │ │ │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Conditional.Exclude( │ type T = Exclude< │ const T = { │ -│ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │ -│ Type.Literal('a'), │ 'a' │ const: 'b', │ -│ Type.Literal('b'), │ > │ type: 'string' │ -│ Type.Literal('c') │ │ }, { │ -│ ]), │ │ const: 'c', │ -│ Type.Union([ │ │ type: 'string' │ -│ Type.Literal('a') │ │ }] │ -│ ]) │ │ } │ -│ ) │ │ │ -│ │ │ │ -└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ +const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) +// ^ TLiteral +const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number()) +// ^ TNumber +const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number()) +// ^ TString ``` ### Unsafe -Use `Type.Unsafe(...)` to create custom schemas with user defined inference rules. +Use `Type.Unsafe(...)` to create custom schematics with user defined inference rules. ```typescript const T = Type.Unsafe({ type: 'number' }) // const T = { @@ -686,16 +780,12 @@ const T = Type.Unsafe({ type: 'number' }) // const T = { type T = Static // type T = string ``` -This function can be used to create custom schemas for validators that require specific schema representations. An example of this might be OpenAPI's `nullable` and `enum` schemas which are not provided by TypeBox. The following demonstrates using `Type.Unsafe(...)` to create these types. +The `Type.Unsafe(...)` type can be useful to express specific OpenAPI schema representations. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' -//-------------------------------------------------------------------------------------------- -// // Nullable -// -//-------------------------------------------------------------------------------------------- function Nullable(schema: T) { return Type.Unsafe | null>({ ...schema, nullable: true }) @@ -708,12 +798,7 @@ const T = Nullable(Type.String()) // const T = { type T = Static // type T = string | null - -//-------------------------------------------------------------------------------------------- -// // StringEnum -// -//-------------------------------------------------------------------------------------------- function StringEnum(values: [...T]) { return Type.Unsafe({ type: 'string', enum: values }) @@ -730,10 +815,10 @@ type T = Static // type T = 'A' | 'B' | 'C' ### Guards -Use the guard module to test if values are TypeBox types. +TypeBox provides a `TypeGuard` module that can be used for reflection and asserting values as types. ```typescript -import { TypeGuard } from '@sinclair/typebox/guard' +import { Type, TypeGuard } from '@sinclair/typebox' const T = Type.String() @@ -747,7 +832,7 @@ if(TypeGuard.TString(T)) { ### Strict -TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox internally compose types. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. +TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are used for type composition and reflection. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. ```typescript const T = Type.Object({ // const T = { @@ -776,7 +861,7 @@ const U = Type.Strict(T) // const U = { ## Values -TypeBox includes an optional values module that can be used to perform common operations on JavaScript values. This module enables one to create, check and cast values from types. It also provides functionality to check equality, clone and diff and patch JavaScript values. The value module is provided as an optional import. +TypeBox provides an optional utility module that can be used to perform common operations on JavaScript values. This module includes functionality to create, check and cast values from types as well as check equality, clone, diff and patch JavaScript values. This module is provided via optional import. ```typescript import { Value } from '@sinclair/typebox/value' @@ -786,7 +871,7 @@ import { Value } from '@sinclair/typebox/value' ### Create -Use the Create function to create a value from a TypeBox type. TypeBox will use default values if specified. +Use the Create function to create a value from a type. TypeBox will use default values if specified. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number({ default: 42 }) }) @@ -816,6 +901,20 @@ const T = Type.Object({ x: Type.Number() }) const R = Value.Check(T, { x: 1 }) // const R = true ``` + + +### Convert + +Use the Convert function to convert a value into its target type if a reasonable conversion is possible. + +```typescript +const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + +const R1 = Value.Convert(T, { x: '3.14' }) // const R1 = { x: 3.14 } + +const R2 = Value.Convert(T, { x: 'not a number' }) // const R2 = { x: 'not a number' } +``` + ### Cast @@ -918,7 +1017,7 @@ const R = [...Value.Errors(T, { x: '42' })] // const R = [{ ### Pointer -Use ValuePointer to perform mutable updates on existing values using [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) Json Pointers. +Use ValuePointer to perform mutable updates on existing values using [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) JSON Pointers. ```typescript import { ValuePointer } from '@sinclair/typebox/value' @@ -933,15 +1032,15 @@ ValuePointer.Set(A, '/z', 1) // const A = { x: 1, y: 1, ## TypeCheck -TypeBox constructs JSON Schema draft 6 compliant schematics and can be used with any validator that supports this specification. In JavaScript, an ideal validator to use is Ajv which supports draft 6 as well as more recent revisions to the specification. In addition to Ajv, TypeBox provides an optional built in type compiler which can offer faster runtime type compilation, as well as providing high performance data validation for TypeBox types only. +TypeBox types target JSON Schema draft 6 so are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for high performance compilation and value assertion. -The following sections detail using these validators. +The following sections detail using Ajv and TypeBox's compiler infrastructure. ## Ajv -The following shows the recommended setup for Ajv. +The following shows the recommended setup for Ajv. ```bash $ npm install ajv ajv-formats --save @@ -982,7 +1081,7 @@ const R = C({ x: 1, y: 2, z: 3 }) // const R = true ### TypeCompiler -The TypeCompiler is a Just-In-Time (JIT) runtime compiler that can be used to convert TypeBox types into fast validation routines. This compiler is specifically tuned for fast compilation and validation for TypeBox types only. +The TypeBox TypeCompiler is a high performance JIT compiler that transforms TypeBox types into optimized JavaScript validation routines. The compiler is tuned for fast compilation as well as fast value assertion. It is designed to serve as a validation backend that can be integrated into larger applications; but can also be used as a general purpose validator. The TypeCompiler is provided as an optional import. @@ -1002,7 +1101,7 @@ const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck Type -const BigNumber = TypeSystem.CreateType( - 'BigNumber', - (options, value) => { - if (typeof value !== 'bigint') return false - if (options.maximum !== undefined && value > options.maximum) return false - if (options.minimum !== undefined && value < options.minimum) return false - return true - } -) +const Point = TypeSystem.Type('Point', (options, value) => { + return ( + typeof value === 'object' && value !== null && + typeof value.x === 'number' && + typeof value.y === 'number' + ) +}) -//-------------------------------------------------------------------------------------------- -// -// Use the custom type like any other type -// -//-------------------------------------------------------------------------------------------- +const T = Point() -const T = BigNumber({ minimum: 10n, maximum: 20n }) // const T = { - // minimum: 10n, - // maximum: 20n, - // [Symbol(TypeBox.Kind)]: 'BigNumber' - // } +type T = Static // type T = { x: number, y: number } -const C = TypeCompiler.Compile(T) -const X = C.Check(15n) // const X = true -const Y = C.Check(5n) // const Y = false -const Z = C.Check(25n) // const Z = false +const R = Value.Check(T, { x: 1, y: 2 }) // const R = true ``` ### Formats -Use the `CreateFormat(...)` function to specify user defined string formats. The following creates a custom string format that checks for lowercase. +Use the `Format(...)` function to create a custom string format. The following creates a custom string format that checks for lowercase strings. ```typescript -//-------------------------------------------------------------------------------------------- -// -// Use TypeSystem.CreateFormat(...) to define a custom string format -// -//-------------------------------------------------------------------------------------------- +TypeSystem.Format('lowercase', value => value === value.toLowerCase()) // format should be lowercase -TypeSystem.CreateFormat('lowercase', value => value === value.toLowerCase()) +const T = Type.String({ format: 'lowercase' }) -//-------------------------------------------------------------------------------------------- -// -// Use the format by creating string types with the 'format' option +const A = Value.Check(T, 'action') // const A = true + +const B = Value.Check(T, 'ACTION') // const B = false +``` + + + +### Policies + +TypeBox validates using JSON Schema assertion policies by default. It is possible to override these policies and have TypeBox assert using TypeScript policies. The following overrides are available. + +```typescript +// Allow arrays to validate as object types (default is false) // -//-------------------------------------------------------------------------------------------- +// const A: {} = [] - allowed in TS -const T = Type.String({ format: 'lowercase' }) +TypeSystem.AllowArrayObjects = true -const A = Value.Check(T, 'action') // const A = true +// Allow numeric values to be NaN or + or - Infinity (default is false) +// +// const A: number = NaN - allowed in TS -const B = Value.Check(T, 'ACTION') // const B = false +TypeSystem.AllowNaN = true ``` @@ -1141,33 +1231,37 @@ For additional comparative benchmarks, please refer to [typescript-runtime-type- This benchmark measures compilation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/compile.ts). ```typescript -┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ -│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ -├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 2000 │ ' 451 ms' │ ' 16 ms' │ ' 28.19 x' │ -│ String │ 2000 │ ' 338 ms' │ ' 14 ms' │ ' 24.14 x' │ -│ Boolean │ 2000 │ ' 297 ms' │ ' 13 ms' │ ' 22.85 x' │ -│ Null │ 2000 │ ' 265 ms' │ ' 8 ms' │ ' 33.13 x' │ -│ RegEx │ 2000 │ ' 492 ms' │ ' 18 ms' │ ' 27.33 x' │ -│ ObjectA │ 2000 │ ' 2744 ms' │ ' 55 ms' │ ' 49.89 x' │ -│ ObjectB │ 2000 │ ' 3005 ms' │ ' 44 ms' │ ' 68.30 x' │ -│ Tuple │ 2000 │ ' 1283 ms' │ ' 26 ms' │ ' 49.35 x' │ -│ Union │ 2000 │ ' 1263 ms' │ ' 27 ms' │ ' 46.78 x' │ -│ Vector4 │ 2000 │ ' 1622 ms' │ ' 23 ms' │ ' 70.52 x' │ -│ Matrix4 │ 2000 │ ' 888 ms' │ ' 12 ms' │ ' 74.00 x' │ -│ Literal_String │ 2000 │ ' 344 ms' │ ' 14 ms' │ ' 24.57 x' │ -│ Literal_Number │ 2000 │ ' 389 ms' │ ' 8 ms' │ ' 48.63 x' │ -│ Literal_Boolean │ 2000 │ ' 374 ms' │ ' 9 ms' │ ' 41.56 x' │ -│ Array_Number │ 2000 │ ' 710 ms' │ ' 12 ms' │ ' 59.17 x' │ -│ Array_String │ 2000 │ ' 739 ms' │ ' 9 ms' │ ' 82.11 x' │ -│ Array_Boolean │ 2000 │ ' 732 ms' │ ' 7 ms' │ ' 104.57 x' │ -│ Array_ObjectA │ 2000 │ ' 3733 ms' │ ' 42 ms' │ ' 88.88 x' │ -│ Array_ObjectB │ 2000 │ ' 3602 ms' │ ' 42 ms' │ ' 85.76 x' │ -│ Array_Tuple │ 2000 │ ' 2204 ms' │ ' 20 ms' │ ' 110.20 x' │ -│ Array_Union │ 2000 │ ' 1533 ms' │ ' 24 ms' │ ' 63.88 x' │ -│ Array_Vector4 │ 2000 │ ' 2263 ms' │ ' 21 ms' │ ' 107.76 x' │ -│ Array_Matrix4 │ 2000 │ ' 1576 ms' │ ' 14 ms' │ ' 112.57 x' │ -└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ +┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ +│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ +├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ +│ Literal_String │ 1000 │ ' 260 ms' │ ' 8 ms' │ ' 32.50 x' │ +│ Literal_Number │ 1000 │ ' 198 ms' │ ' 4 ms' │ ' 49.50 x' │ +│ Literal_Boolean │ 1000 │ ' 185 ms' │ ' 5 ms' │ ' 37.00 x' │ +│ Primitive_Number │ 1000 │ ' 176 ms' │ ' 9 ms' │ ' 19.56 x' │ +│ Primitive_String │ 1000 │ ' 161 ms' │ ' 9 ms' │ ' 17.89 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 215 ms' │ ' 12 ms' │ ' 17.92 x' │ +│ Primitive_Boolean │ 1000 │ ' 133 ms' │ ' 5 ms' │ ' 26.60 x' │ +│ Primitive_Null │ 1000 │ ' 143 ms' │ ' 8 ms' │ ' 17.88 x' │ +│ Object_Unconstrained │ 1000 │ ' 1181 ms' │ ' 38 ms' │ ' 31.08 x' │ +│ Object_Constrained │ 1000 │ ' 1168 ms' │ ' 32 ms' │ ' 36.50 x' │ +│ Tuple_Primitive │ 1000 │ ' 557 ms' │ ' 16 ms' │ ' 34.81 x' │ +│ Tuple_Object │ 1000 │ ' 1119 ms' │ ' 17 ms' │ ' 65.82 x' │ +│ Composite_Intersect │ 1000 │ ' 569 ms' │ ' 22 ms' │ ' 25.86 x' │ +│ Composite_Union │ 1000 │ ' 513 ms' │ ' 23 ms' │ ' 22.30 x' │ +│ Math_Vector4 │ 1000 │ ' 802 ms' │ ' 10 ms' │ ' 80.20 x' │ +│ Math_Matrix4 │ 1000 │ ' 395 ms' │ ' 12 ms' │ ' 32.92 x' │ +│ Array_Primitive_Number │ 1000 │ ' 282 ms' │ ' 8 ms' │ ' 35.25 x' │ +│ Array_Primitive_String │ 1000 │ ' 321 ms' │ ' 5 ms' │ ' 64.20 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 364 ms' │ ' 5 ms' │ ' 72.80 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1573 ms' │ ' 18 ms' │ ' 87.39 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1270 ms' │ ' 20 ms' │ ' 63.50 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 973 ms' │ ' 18 ms' │ ' 54.06 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1253 ms' │ ' 16 ms' │ ' 78.31 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 927 ms' │ ' 20 ms' │ ' 46.35 x' │ +│ Array_Composite_Union │ 1000 │ ' 1123 ms' │ ' 16 ms' │ ' 70.19 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1068 ms' │ ' 10 ms' │ ' 106.80 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 488 ms' │ ' 7 ms' │ ' 69.71 x' │ +└────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1177,35 +1271,39 @@ This benchmark measures compilation performance for varying types. You can revie This benchmark measures validation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/check.ts). ```typescript -┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ -│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ -├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Number │ 1000000 │ ' 30 ms' │ ' 7 ms' │ ' 6 ms' │ ' 1.17 x' │ -│ String │ 1000000 │ ' 23 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Boolean │ 1000000 │ ' 22 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ -│ Null │ 1000000 │ ' 27 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ RegEx │ 1000000 │ ' 163 ms' │ ' 47 ms' │ ' 38 ms' │ ' 1.24 x' │ -│ ObjectA │ 1000000 │ ' 654 ms' │ ' 41 ms' │ ' 24 ms' │ ' 1.71 x' │ -│ ObjectB │ 1000000 │ ' 1173 ms' │ ' 59 ms' │ ' 41 ms' │ ' 1.44 x' │ -│ Tuple │ 1000000 │ ' 124 ms' │ ' 24 ms' │ ' 17 ms' │ ' 1.41 x' │ -│ Union │ 1000000 │ ' 332 ms' │ ' 26 ms' │ ' 16 ms' │ ' 1.63 x' │ -│ Recursive │ 1000000 │ ' 3129 ms' │ ' 412 ms' │ ' 102 ms' │ ' 4.04 x' │ -│ Vector4 │ 1000000 │ ' 147 ms' │ ' 26 ms' │ ' 13 ms' │ ' 2.00 x' │ -│ Matrix4 │ 1000000 │ ' 576 ms' │ ' 41 ms' │ ' 28 ms' │ ' 1.46 x' │ -│ Literal_String │ 1000000 │ ' 51 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ -│ Literal_Number │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Literal_Boolean │ 1000000 │ ' 47 ms' │ ' 21 ms' │ ' 10 ms' │ ' 2.10 x' │ -│ Array_Number │ 1000000 │ ' 490 ms' │ ' 33 ms' │ ' 18 ms' │ ' 1.83 x' │ -│ Array_String │ 1000000 │ ' 502 ms' │ ' 31 ms' │ ' 25 ms' │ ' 1.24 x' │ -│ Array_Boolean │ 1000000 │ ' 465 ms' │ ' 33 ms' │ ' 27 ms' │ ' 1.22 x' │ -│ Array_ObjectA │ 1000000 │ ' 15463 ms' │ ' 2470 ms' │ ' 2052 ms' │ ' 1.20 x' │ -│ Array_ObjectB │ 1000000 │ ' 18047 ms' │ ' 2497 ms' │ ' 2348 ms' │ ' 1.06 x' │ -│ Array_Tuple │ 1000000 │ ' 1958 ms' │ ' 99 ms' │ ' 77 ms' │ ' 1.29 x' │ -│ Array_Union │ 1000000 │ ' 5348 ms' │ ' 254 ms' │ ' 89 ms' │ ' 2.85 x' │ -│ Array_Recursive │ 1000000 │ ' 54643 ms' │ ' 8870 ms' │ ' 1158 ms' │ ' 7.66 x' │ -│ Array_Vector4 │ 1000000 │ ' 2724 ms' │ ' 105 ms' │ ' 48 ms' │ ' 2.19 x' │ -│ Array_Matrix4 │ 1000000 │ ' 13821 ms' │ ' 437 ms' │ ' 266 ms' │ ' 1.64 x' │ -└──────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ +┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ +│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ +├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ +│ Literal_String │ 1000000 │ ' 26 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ +│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ +│ Literal_Boolean │ 1000000 │ ' 19 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Primitive_Number │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ +│ Primitive_String │ 1000000 │ ' 26 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 159 ms' │ ' 45 ms' │ ' 37 ms' │ ' 1.22 x' │ +│ Primitive_Boolean │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ +│ Primitive_Null │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Object_Unconstrained │ 1000000 │ ' 809 ms' │ ' 35 ms' │ ' 30 ms' │ ' 1.17 x' │ +│ Object_Constrained │ 1000000 │ ' 1060 ms' │ ' 56 ms' │ ' 45 ms' │ ' 1.24 x' │ +│ Object_Recursive │ 1000000 │ ' 4965 ms' │ ' 397 ms' │ ' 100 ms' │ ' 3.97 x' │ +│ Tuple_Primitive │ 1000000 │ ' 159 ms' │ ' 22 ms' │ ' 16 ms' │ ' 1.38 x' │ +│ Tuple_Object │ 1000000 │ ' 658 ms' │ ' 31 ms' │ ' 27 ms' │ ' 1.15 x' │ +│ Composite_Intersect │ 1000000 │ ' 695 ms' │ ' 26 ms' │ ' 22 ms' │ ' 1.18 x' │ +│ Composite_Union │ 1000000 │ ' 503 ms' │ ' 24 ms' │ ' 19 ms' │ ' 1.26 x' │ +│ Math_Vector4 │ 1000000 │ ' 259 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1007 ms' │ ' 40 ms' │ ' 29 ms' │ ' 1.38 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 262 ms' │ ' 23 ms' │ ' 17 ms' │ ' 1.35 x' │ +│ Array_Primitive_String │ 1000000 │ ' 241 ms' │ ' 27 ms' │ ' 24 ms' │ ' 1.13 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 141 ms' │ ' 23 ms' │ ' 20 ms' │ ' 1.15 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 4976 ms' │ ' 70 ms' │ ' 67 ms' │ ' 1.04 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5234 ms' │ ' 143 ms' │ ' 120 ms' │ ' 1.19 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 19605 ms' │ ' 1909 ms' │ ' 350 ms' │ ' 5.45 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 706 ms' │ ' 39 ms' │ ' 32 ms' │ ' 1.22 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 2951 ms' │ ' 67 ms' │ ' 63 ms' │ ' 1.06 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 2969 ms' │ ' 49 ms' │ ' 44 ms' │ ' 1.11 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2191 ms' │ ' 77 ms' │ ' 41 ms' │ ' 1.88 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1164 ms' │ ' 41 ms' │ ' 25 ms' │ ' 1.64 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4903 ms' │ ' 115 ms' │ ' 99 ms' │ ' 1.16 x' │ +└────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1218,15 +1316,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ ' 65.4 kb' │ ' 32.2 kb' │ '2.03 x' │ -│ typebox/conditional │ ' 45.5 kb' │ ' 18.6 kb' │ '2.45 x' │ -│ typebox/custom │ ' 0.6 kb' │ ' 0.2 kb' │ '2.61 x' │ -│ typebox/format │ ' 0.6 kb' │ ' 0.2 kb' │ '2.66 x' │ -│ typebox/guard │ ' 23.8 kb' │ ' 11.4 kb' │ '2.08 x' │ -│ typebox/hash │ ' 4.2 kb' │ ' 1.8 kb' │ '2.30 x' │ -│ typebox/system │ ' 14.0 kb' │ ' 7.1 kb' │ '1.96 x' │ -│ typebox/value │ ' 90.0 kb' │ ' 41.8 kb' │ '2.15 x' │ -│ typebox │ ' 12.0 kb' │ ' 6.4 kb' │ '1.89 x' │ +│ typebox/compiler │ '108.8 kb' │ ' 48.9 kb' │ '2.23 x' │ +│ typebox/errors │ ' 93.2 kb' │ ' 41.5 kb' │ '2.24 x' │ +│ typebox/system │ ' 60.0 kb' │ ' 24.6 kb' │ '2.43 x' │ +│ typebox/value │ '153.5 kb' │ ' 66.7 kb' │ '2.30 x' │ +│ typebox │ ' 58.7 kb' │ ' 24.1 kb' │ '2.43 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index de1b76664..f773b012c 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -26,37 +26,28 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { ValueErrors, ValueError } from '../errors/index' -import { TypeSystem } from '../system/index' -import { TypeExtends, TypeGuard } from '../guard/index' -import { Format } from '../format/index' -import { Custom } from '../custom/index' -import { ValueHash } from '../hash/index' import * as Types from '../typebox' +import { ValueErrors, ValueErrorIterator } from '../errors/index' +import { TypeSystem } from '../system/index' +import { ValueHash } from '../value/hash' // ------------------------------------------------------------------- // CheckFunction // ------------------------------------------------------------------- - export type CheckFunction = (value: unknown) => boolean - // ------------------------------------------------------------------- // TypeCheck // ------------------------------------------------------------------- - export class TypeCheck { constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) {} - - /** Returns the generated validation code used to validate this type. */ + /** Returns the generated assertion code used to validate this type. */ public Code(): string { return this.code } - /** Returns an iterator for each error in this value. */ - public Errors(value: unknown): IterableIterator { + public Errors(value: unknown): ValueErrorIterator { return ValueErrors.Errors(this.schema, this.references, value) } - /** Returns true if the value matches the compiled type. */ public Check(value: unknown): value is Types.Static { return this.checkFunc(value) @@ -79,7 +70,6 @@ namespace Character { return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) } } - // ------------------------------------------------------------------- // Identifier // ------------------------------------------------------------------- @@ -97,7 +87,6 @@ namespace Identifier { return buffer.join('').replace(/__/g, '_') } } - // ------------------------------------------------------------------- // MemberExpression // ------------------------------------------------------------------- @@ -122,369 +111,386 @@ export namespace MemberExpression { return !Check(key) ? `${object}['${key}']` : `${object}.${key}` } } - // ------------------------------------------------------------------- // TypeCompiler // ------------------------------------------------------------------- - export class TypeCompilerUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('TypeCompiler: Unknown type') } } - +export class TypeCompilerDereferenceError extends Error { + constructor(public readonly schema: Types.TRef) { + super(`TypeCompiler: Unable to dereference schema with $id '${schema.$ref}'`) + } +} +export class TypeCompilerTypeGuardError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('TypeCompiler: Preflight validation check failed to guard for the given schema') + } +} /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { // ------------------------------------------------------------------- // Guards // ------------------------------------------------------------------- + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !globalThis.isNaN(value) + return typeof value === 'number' && globalThis.Number.isFinite(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' } - // ------------------------------------------------------------------- - // Types + // Overrides // ------------------------------------------------------------------- - function* Any(schema: Types.TAny, value: string): IterableIterator { - yield '(true)' + function IsNumberCheck(value: string): string { + return !TypeSystem.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` } - - function* Array(schema: Types.TArray, value: string): IterableIterator { - const expression = CreateExpression(schema.items, 'value') - yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))` - if (IsNumber(schema.minItems)) yield `(${value}.length >= ${schema.minItems})` - if (IsNumber(schema.maxItems)) yield `(${value}.length <= ${schema.maxItems})` - if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` + function IsObjectCheck(value: string): string { + return !TypeSystem.AllowArrayObjects ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)` } - - function* Boolean(schema: Types.TBoolean, value: string): IterableIterator { - yield `(typeof ${value} === 'boolean')` + function IsRecordCheck(value: string): string { + return !TypeSystem.AllowArrayObjects + ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` + : `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` } - - function* Constructor(schema: Types.TConstructor, value: string): IterableIterator { - yield* Visit(schema.returns, `${value}.prototype`) + function IsVoidCheck(value: string): string { + return TypeSystem.AllowVoidNull ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` } - - function* Date(schema: Types.TDate, value: string): IterableIterator { - yield `(${value} instanceof Date) && !isNaN(${value}.getTime())` - if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `(${value}.getTime() > ${schema.exclusiveMinimumTimestamp})` - if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `(${value}.getTime() < ${schema.exclusiveMaximumTimestamp})` - if (IsNumber(schema.minimumTimestamp)) yield `(${value}.getTime() >= ${schema.minimumTimestamp})` - if (IsNumber(schema.maximumTimestamp)) yield `(${value}.getTime() <= ${schema.maximumTimestamp})` + // ------------------------------------------------------------------- + // Types + // ------------------------------------------------------------------- + function* Any(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { + yield 'true' + } + function* Array(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { + const expression = CreateExpression(schema.items, references, 'value') + yield `Array.isArray(${value}) && ${value}.every(value => ${expression})` + if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` + if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` + if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` } - - function* Function(schema: Types.TFunction, value: string): IterableIterator { - yield `(typeof ${value} === 'function')` + function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof ${value} === 'bigint')` + if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` + if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` + if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` + if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` + if (IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` } - - function* Integer(schema: Types.TInteger, value: string): IterableIterator { - yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` - if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` - if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` - if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` - if (IsNumber(schema.minimum)) yield `(${value} >= ${schema.minimum})` - if (IsNumber(schema.maximum)) yield `(${value} <= ${schema.maximum})` + function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { + yield `typeof ${value} === 'boolean'` } - - function* Literal(schema: Types.TLiteral, value: string): IterableIterator { - if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { - yield `(${value} === ${schema.const})` - } else { - yield `(${value} === '${schema.const}')` - } + function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { + yield* Visit(schema.returns, references, `${value}.prototype`) } - - function* Never(schema: Types.TNever, value: string): IterableIterator { - yield `(false)` + function* Date(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { + yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())` + if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` + if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` + if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` + if (IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` } - - function* Null(schema: Types.TNull, value: string): IterableIterator { - yield `(${value} === null)` + function* Function(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { + yield `typeof ${value} === 'function'` } - - function* Number(schema: Types.TNumber, value: string): IterableIterator { - if (TypeSystem.AllowNaN) { - yield `(typeof ${value} === 'number')` - } else { - yield `(typeof ${value} === 'number' && !isNaN(${value}))` + function* Integer(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` + if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + } + function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { + if (schema.unevaluatedProperties === undefined) { + const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) + yield `${expressions.join(' && ')}` + } else if (schema.unevaluatedProperties === false) { + // prettier-ignore + const schemaKeys = Types.KeyResolver.Resolve(schema).map((key) => `'${key}'`).join(', ') + const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) + const expression1 = `Object.getOwnPropertyNames(${value}).every(key => [${schemaKeys}].includes(key))` + yield `${expressions.join(' && ')} && ${expression1}` + } else if (typeof schema.unevaluatedProperties === 'object') { + // prettier-ignore + const schemaKeys = Types.KeyResolver.Resolve(schema).map((key) => `'${key}'`).join(', ') + const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) + const expression1 = CreateExpression(schema.unevaluatedProperties, references, 'value[key]') + const expression2 = `Object.getOwnPropertyNames(${value}).every(key => [${schemaKeys}].includes(key) || ${expression1})` + yield `${expressions.join(' && ')} && ${expression2}` } - if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf} === 0)` - if (IsNumber(schema.exclusiveMinimum)) yield `(${value} > ${schema.exclusiveMinimum})` - if (IsNumber(schema.exclusiveMaximum)) yield `(${value} < ${schema.exclusiveMaximum})` - if (IsNumber(schema.minimum)) yield `(${value} >= ${schema.minimum})` - if (IsNumber(schema.maximum)) yield `(${value} <= ${schema.maximum})` } - - function* Object(schema: Types.TObject, value: string): IterableIterator { - if (TypeSystem.AllowArrayObjects) { - yield `(typeof ${value} === 'object' && ${value} !== null)` + function* Literal(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { + if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { + yield `${value} === ${schema.const}` } else { - yield `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` + yield `${value} === '${schema.const}'` } - if (IsNumber(schema.minProperties)) yield `(Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties})` - if (IsNumber(schema.maxProperties)) yield `(Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties})` - const propertyKeys = globalThis.Object.getOwnPropertyNames(schema.properties) - if (schema.additionalProperties === false) { - // Optimization: If the property key length matches the required keys length - // then we only need check that the values property key length matches that - // of the property key length. This is because exhaustive testing for values - // will occur in subsequent property tests. - if (schema.required && schema.required.length === propertyKeys.length) { - yield `(Object.getOwnPropertyNames(${value}).length === ${propertyKeys.length})` + } + function* Never(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { + yield `false` + } + function* Not(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { + const left = CreateExpression(schema.allOf[0].not, references, value) + const right = CreateExpression(schema.allOf[1], references, value) + yield `!${left} && ${right}` + } + function* Null(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { + yield `${value} === null` + } + function* Number(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { + yield IsNumberCheck(value) + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` + if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + } + + function* Object(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { + yield IsObjectCheck(value) + if (!TypeSystem.AllowArrayObjects) yield `!Array.isArray(${value})` + if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` + const schemaKeys = globalThis.Object.getOwnPropertyNames(schema.properties) + for (const schemaKey of schemaKeys) { + const memberExpression = MemberExpression.Encode(value, schemaKey) + const property = schema.properties[schemaKey] + if (schema.required && schema.required.includes(schemaKey)) { + yield* Visit(property, references, memberExpression) + if (Types.ExtendsUndefined.Check(property)) yield `('${schemaKey}' in ${value})` } else { - const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` - yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key)))` + const expression = CreateExpression(property, references, memberExpression) + yield `('${schemaKey}' in ${value} ? ${expression} : true)` } } - if (TypeGuard.TSchema(schema.additionalProperties)) { - const expression = CreateExpression(schema.additionalProperties, 'value[key]') - const keys = `[${propertyKeys.map((key) => `'${key}'`).join(', ')}]` - yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` - } - for (const propertyKey of propertyKeys) { - const memberExpression = MemberExpression.Encode(value, propertyKey) - const propertySchema = schema.properties[propertyKey] - if (schema.required && schema.required.includes(propertyKey)) { - yield* Visit(propertySchema, memberExpression) - if (TypeExtends.Undefined(propertySchema)) { - yield `('${propertyKey}' in ${value})` - } + if (schema.additionalProperties === false) { + if (schema.required && schema.required.length === schemaKeys.length) { + yield `Object.getOwnPropertyNames(${value}).length === ${schemaKeys.length}` } else { - const expression = CreateExpression(propertySchema, memberExpression) - yield `(${memberExpression} === undefined ? true : (${expression}))` + const keys = `[${schemaKeys.map((key) => `'${key}'`).join(', ')}]` + yield `Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key))` } } + if (typeof schema.additionalProperties === 'object') { + const expression = CreateExpression(schema.additionalProperties, references, 'value[key]') + const keys = `[${schemaKeys.map((key) => `'${key}'`).join(', ')}]` + yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` + } } - - function* Promise(schema: Types.TPromise, value: string): IterableIterator { + function* Promise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } - function* Record(schema: Types.TRecord, value: string): IterableIterator { - if (TypeSystem.AllowArrayObjects) { - yield `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date))` - } else { - yield `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !Array.isArray(${value}))` - } + function* Record(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { + yield IsRecordCheck(value) const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${keyPattern}/)`) yield `(Object.getOwnPropertyNames(${value}).every(key => ${local}.test(key)))` - const expression = CreateExpression(valueSchema, 'value') - yield `(Object.values(${value}).every(value => ${expression}))` + const expression = CreateExpression(valueSchema, references, 'value') + yield `Object.values(${value}).every(value => ${expression})` } - - function* Ref(schema: Types.TRef, value: string): IterableIterator { + function* Ref(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new TypeCompilerDereferenceError(schema) + const target = references[index] // Reference: If we have seen this reference before we can just yield and return // the function call. If this isn't the case we defer to visit to generate and // set the function for subsequent passes. Consider for refactor. - if (state_local_function_names.has(schema.$ref)) return yield `(${CreateFunctionName(schema.$ref)}(${value}))` - if (!state_reference_map.has(schema.$ref)) throw Error(`TypeCompiler.Ref: Cannot de-reference schema with $id '${schema.$ref}'`) - const reference = state_reference_map.get(schema.$ref)! - yield* Visit(reference, value) + if (state_local_function_names.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` + yield* Visit(target, references, value) } - - function* Self(schema: Types.TSelf, value: string): IterableIterator { + function* Self(schema: Types.TSelf, references: Types.TSchema[], value: string): IterableIterator { const func = CreateFunctionName(schema.$ref) - yield `(${func}(${value}))` + yield `${func}(${value})` } - - function* String(schema: Types.TString, value: string): IterableIterator { + function* String(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` - if (IsNumber(schema.minLength)) yield `(${value}.length >= ${schema.minLength})` - if (IsNumber(schema.maxLength)) yield `(${value}.length <= ${schema.maxLength})` + if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` + if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` if (schema.pattern !== undefined) { const local = PushLocal(`${new RegExp(schema.pattern)};`) - yield `(${local}.test(${value}))` + yield `${local}.test(${value})` } if (schema.format !== undefined) { - yield `(format('${schema.format}', ${value}))` + yield `format('${schema.format}', ${value})` } } - - function* Tuple(schema: Types.TTuple, value: string): IterableIterator { + function* Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof ${value} === 'symbol')` + } + function* Tuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { yield `(Array.isArray(${value}))` - if (schema.items === undefined) return yield `(${value}.length === 0)` + if (schema.items === undefined) return yield `${value}.length === 0` yield `(${value}.length === ${schema.maxItems})` for (let i = 0; i < schema.items.length; i++) { - const expression = CreateExpression(schema.items[i], `${value}[${i}]`) - yield `(${expression})` + const expression = CreateExpression(schema.items[i], references, `${value}[${i}]`) + yield `${expression}` } } - - function* Undefined(schema: Types.TUndefined, value: string): IterableIterator { - yield `(${value} === undefined)` + function* Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { + yield `${value} === undefined` } - - function* Union(schema: Types.TUnion, value: string): IterableIterator { - const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, value)) + function* Union(schema: Types.TUnion, references: Types.TSchema[], value: string): IterableIterator { + const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) yield `(${expressions.join(' || ')})` } - - function* Uint8Array(schema: Types.TUint8Array, value: string): IterableIterator { - yield `(${value} instanceof Uint8Array)` + function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { + yield `${value} instanceof Uint8Array` if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } - - function* Unknown(schema: Types.TUnknown, value: string): IterableIterator { - yield '(true)' + function* Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { + yield 'true' } - - function* Void(schema: Types.TVoid, value: string): IterableIterator { - yield `(${value} === null)` + function* Void(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { + yield IsVoidCheck(value) } - - function* UserDefined(schema: Types.TSchema, value: string): IterableIterator { + function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { const schema_key = `schema_key_${state_remote_custom_types.size}` state_remote_custom_types.set(schema_key, schema) - yield `(custom('${schema[Types.Kind]}', '${schema_key}', ${value}))` + yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})` } - - function* Visit(schema: T, value: string): IterableIterator { + function* Visit(schema: T, references: Types.TSchema[], value: string): IterableIterator { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any // Reference: Referenced schemas can originate from either additional schemas // or inline in the schema itself. Ideally the recursive path should align to // reference path. Consider for refactor. - if (schema.$id && !state_local_function_names.has(schema.$id)) { + if (IsString(schema.$id) && !state_local_function_names.has(schema.$id)) { state_local_function_names.add(schema.$id) const name = CreateFunctionName(schema.$id) - const body = CreateFunction(name, schema, 'value') + const body = CreateFunction(name, schema, references, 'value') PushFunction(body) - yield `(${name}(${value}))` + yield `${name}(${value})` return } - const anySchema = schema as any - switch (anySchema[Types.Kind]) { + switch (schema_[Types.Kind]) { case 'Any': - return yield* Any(anySchema, value) + return yield* Any(schema_, references_, value) case 'Array': - return yield* Array(anySchema, value) + return yield* Array(schema_, references_, value) + case 'BigInt': + return yield* BigInt(schema_, references_, value) case 'Boolean': - return yield* Boolean(anySchema, value) + return yield* Boolean(schema_, references_, value) case 'Constructor': - return yield* Constructor(anySchema, value) + return yield* Constructor(schema_, references_, value) case 'Date': - return yield* Date(anySchema, value) + return yield* Date(schema_, references_, value) case 'Function': - return yield* Function(anySchema, value) + return yield* Function(schema_, references_, value) case 'Integer': - return yield* Integer(anySchema, value) + return yield* Integer(schema_, references_, value) + case 'Intersect': + return yield* Intersect(schema_, references_, value) case 'Literal': - return yield* Literal(anySchema, value) + return yield* Literal(schema_, references_, value) case 'Never': - return yield* Never(anySchema, value) + return yield* Never(schema_, references_, value) + case 'Not': + return yield* Not(schema_, references_, value) case 'Null': - return yield* Null(anySchema, value) + return yield* Null(schema_, references_, value) case 'Number': - return yield* Number(anySchema, value) + return yield* Number(schema_, references_, value) case 'Object': - return yield* Object(anySchema, value) + return yield* Object(schema_, references_, value) case 'Promise': - return yield* Promise(anySchema, value) + return yield* Promise(schema_, references_, value) case 'Record': - return yield* Record(anySchema, value) + return yield* Record(schema_, references_, value) case 'Ref': - return yield* Ref(anySchema, value) + return yield* Ref(schema_, references_, value) case 'Self': - return yield* Self(anySchema, value) + return yield* Self(schema_, references_, value) case 'String': - return yield* String(anySchema, value) + return yield* String(schema_, references_, value) + case 'Symbol': + return yield* Symbol(schema_, references_, value) case 'Tuple': - return yield* Tuple(anySchema, value) + return yield* Tuple(schema_, references_, value) case 'Undefined': - return yield* Undefined(anySchema, value) + return yield* Undefined(schema_, references_, value) case 'Union': - return yield* Union(anySchema, value) + return yield* Union(schema_, references_, value) case 'Uint8Array': - return yield* Uint8Array(anySchema, value) + return yield* Uint8Array(schema_, references_, value) case 'Unknown': - return yield* Unknown(anySchema, value) + return yield* Unknown(schema_, references_, value) case 'Void': - return yield* Void(anySchema, value) + return yield* Void(schema_, references_, value) default: - if (!Custom.Has(anySchema[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) - return yield* UserDefined(anySchema, value) + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) + return yield* UserDefined(schema_, references_, value) } } - // ------------------------------------------------------------------- // Compiler State // ------------------------------------------------------------------- - - const state_reference_map = new Map() // tracks schemas with identifiers const state_local_variables = new Set() // local variables and functions const state_local_function_names = new Set() // local function names used call ref validators const state_remote_custom_types = new Map() // remote custom types used during compilation - function ResetCompiler() { - state_reference_map.clear() state_local_variables.clear() state_local_function_names.clear() state_remote_custom_types.clear() } - - function AddReferences(schemas: Types.TSchema[] = []) { - for (const schema of schemas) { - if (!schema.$id) throw new Error(`TypeCompiler: Referenced schemas must specify an $id.`) - if (state_reference_map.has(schema.$id)) throw new Error(`TypeCompiler: Duplicate schema $id found for '${schema.$id}'`) - state_reference_map.set(schema.$id, schema) - } - } - - function CreateExpression(schema: Types.TSchema, value: string): string { - return `(${[...Visit(schema, value)].join(' && ')})` + function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string): string { + return `(${[...Visit(schema, references, value)].join(' && ')})` } - function CreateFunctionName($id: string) { return `check_${Identifier.Encode($id)}` } - - function CreateFunction(name: string, schema: Types.TSchema, value: string): string { - const expression = [...Visit(schema, value)].map((condition) => ` ${condition}`).join(' &&\n') + function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string): string { + const expression = [...Visit(schema, references, value)].map((condition) => ` ${condition}`).join(' &&\n') return `function ${name}(value) {\n return (\n${expression}\n )\n}` } - function PushFunction(functionBody: string) { state_local_variables.add(functionBody) } - function PushLocal(expression: string) { const local = `local_${state_local_variables.size}` state_local_variables.add(`const ${local} = ${expression}`) return local } - function GetLocals() { return [...state_local_variables.values()] } - // ------------------------------------------------------------------- // Compile // ------------------------------------------------------------------- - - function Build(schema: T, references: Types.TSchema[] = []): string { + function Build(schema: T, references: Types.TSchema[]): string { ResetCompiler() - AddReferences(references) - const check = CreateFunction('check', schema, 'value') + const check = CreateFunction('check', schema, references, 'value') const locals = GetLocals() return `${locals.join('\n')}\nreturn ${check}` } - + /** Returns the generated assertion code used to validate this type. */ + export function Code(schema: T, references: Types.TSchema[] = []) { + if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) + for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) + return Build(schema, references) + } /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */ export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { - TypeGuard.Assert(schema, references) - const code = Build(schema, references) + const code = Code(schema, references) const custom_schemas = new Map(state_remote_custom_types) const compiledFunction = globalThis.Function('custom', 'format', 'hash', code) const checkFunction = compiledFunction( (kind: string, schema_key: string, value: unknown) => { - if (!Custom.Has(kind) || !custom_schemas.has(schema_key)) return false + if (!Types.TypeRegistry.Has(kind) || !custom_schemas.has(schema_key)) return false const schema = custom_schemas.get(schema_key)! - const func = Custom.Get(kind)! + const func = Types.TypeRegistry.Get(kind)! return func(schema, value) }, (format: string, value: string) => { - if (!Format.Has(format)) return false - const func = Format.Get(format)! + if (!Types.FormatRegistry.Has(format)) return false + const func = Types.FormatRegistry.Get(format)! return func(value) }, (value: unknown) => { diff --git a/src/conditional/conditional.ts b/src/conditional/conditional.ts deleted file mode 100644 index a71a8d4d2..000000000 --- a/src/conditional/conditional.ts +++ /dev/null @@ -1,114 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/conditional - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '../typebox' -import { Structural, StructuralResult } from './structural' -import { TypeGuard } from '../guard/index' - -// ------------------------------------------------------------------------ -// Extends -// ------------------------------------------------------------------------ - -export type TExtends = Types.Static extends Types.Static ? T : U - -// ------------------------------------------------------------------------ -// Exclude -// ------------------------------------------------------------------------ - -export interface TExclude extends Types.TUnion { - static: Exclude, Types.Static> -} - -// ------------------------------------------------------------------------ -// Extract -// ------------------------------------------------------------------------ - -export interface TExtract extends Types.TUnion { - static: Extract, Types.Static> -} - -/** Conditional type mapping for TypeBox types */ -export namespace Conditional { - /** (Experimental) Creates a conditional expression type */ - export function Extends(left: L, right: R, ok: T, fail: U): TExtends { - switch (Structural.Check(left, right)) { - case StructuralResult.Union: - return Types.Type.Union([Clone(ok), Clone(fail)]) as any as TExtends - case StructuralResult.True: - return Clone(ok) - case StructuralResult.False: - return Clone(fail) - } - } - - /** (Experimental) Constructs a type by excluding from UnionType all union members that are assignable to ExcludedMembers. */ - export function Exclude(unionType: T, excludedMembers: U, options: Types.SchemaOptions = {}): TExclude { - const anyOf = unionType.anyOf - .filter((schema) => { - const check = Structural.Check(schema, excludedMembers) - return !(check === StructuralResult.True || check === StructuralResult.Union) - }) - .map((schema) => Clone(schema)) - return { ...options, [Types.Kind]: 'Union', anyOf } as any - } - - /** (Experimental) Constructs a type by extracting from Type all union members that are assignable to Union. */ - export function Extract(type: T, union: U, options: Types.SchemaOptions = {}): TExtract { - if (TypeGuard.TUnion(type)) { - const anyOf = type.anyOf.filter((schema: Types.TSchema) => Structural.Check(schema, union) === StructuralResult.True).map((schema: Types.TSchema) => Clone(schema)) - return { ...options, [Types.Kind]: 'Union', anyOf } as any - } else { - const anyOf = union.anyOf.filter((schema) => Structural.Check(type, schema) === StructuralResult.True).map((schema) => Clone(schema)) - return { ...options, [Types.Kind]: 'Union', anyOf } as any - } - } - - function Clone(value: any): any { - const isObject = (object: any): object is Record => typeof object === 'object' && object !== null && !Array.isArray(object) - const isArray = (object: any): object is any[] => typeof object === 'object' && object !== null && Array.isArray(object) - if (isObject(value)) { - return Object.keys(value).reduce( - (acc, key) => ({ - ...acc, - [key]: Clone(value[key]), - }), - Object.getOwnPropertySymbols(value).reduce( - (acc, key) => ({ - ...acc, - [key]: Clone(value[key]), - }), - {}, - ), - ) - } else if (isArray(value)) { - return value.map((item: any) => Clone(item)) - } else { - return value - } - } -} diff --git a/src/conditional/index.ts b/src/conditional/index.ts deleted file mode 100644 index c6f97147d..000000000 --- a/src/conditional/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/conditional - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './conditional' -export * from './structural' diff --git a/src/conditional/structural.ts b/src/conditional/structural.ts deleted file mode 100644 index ec444955a..000000000 --- a/src/conditional/structural.ts +++ /dev/null @@ -1,579 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/conditional - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '../typebox' -import { TypeGuard } from '../guard' - -// -------------------------------------------------------------------------- -// StructuralResult -// -------------------------------------------------------------------------- - -export enum StructuralResult { - Union, - True, - False, -} - -// -------------------------------------------------------------------------- -// Structural -// -------------------------------------------------------------------------- - -/** Performs structural equivalence checks against TypeBox types. */ -export namespace Structural { - const referenceMap = new Map() - - // ------------------------------------------------------------------------ - // Rules - // ------------------------------------------------------------------------ - - function AnyUnknownOrCustomRule(right: Types.TSchema) { - // https://github.com/microsoft/TypeScript/issues/40049 - if (TypeGuard.TUnion(right) && right.anyOf.some((schema: Types.TSchema) => schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown')) return true - if (TypeGuard.TUnknown(right)) return true - if (TypeGuard.TAny(right)) return true - if (TypeGuard.TUserDefined(right)) throw Error(`Structural: Cannot structurally compare custom type '${right[Types.Kind]}'`) - return false - } - - function ObjectRightRule(left: Types.TAnySchema, right: Types.TObject) { - // type A = boolean extends {} ? 1 : 2 // additionalProperties: false - // type B = boolean extends object ? 1 : 2 // additionalProperties: true - const additionalProperties = right.additionalProperties - const propertyLength = globalThis.Object.keys(right.properties).length - return additionalProperties === false && propertyLength === 0 - } - - function UnionRightRule(left: Types.TAnySchema, right: Types.TUnion): StructuralResult { - const result = right.anyOf.some((right: Types.TSchema) => Visit(left, right) !== StructuralResult.False) - return result ? StructuralResult.True : StructuralResult.False - } - - // ------------------------------------------------------------------------ - // Records - // ------------------------------------------------------------------------ - - function RecordPattern(schema: Types.TRecord) { - return globalThis.Object.keys(schema.patternProperties)[0] as string - } - - function RecordNumberOrStringKey(schema: Types.TRecord) { - const pattern = RecordPattern(schema) - return pattern === '^.*$' || pattern === '^(0|[1-9][0-9]*)$' - } - - function RecordValue(schema: Types.TRecord) { - const pattern = RecordPattern(schema) - return schema.patternProperties[pattern] - } - - function RecordKey(schema: Types.TRecord) { - const pattern = RecordPattern(schema) - if (pattern === '^.*$') { - return Types.Type.String() - } else if (pattern === '^(0|[1-9][0-9]*)$') { - return Types.Type.Number() - } else { - const keys = pattern.slice(1, pattern.length - 1).split('|') - const schemas = keys.map((key) => (isNaN(+key) ? Types.Type.Literal(key) : Types.Type.Literal(parseFloat(key)))) - return Types.Type.Union(schemas) - } - } - - function PropertyMap(schema: Types.TObject | Types.TRecord) { - const comparable = new Map() - if (TypeGuard.TRecord(schema)) { - const propertyPattern = RecordPattern(schema as Types.TRecord) - if (propertyPattern === '^.*$' || propertyPattern === '^(0|[1-9][0-9]*)$') throw Error('Cannot extract record properties without property constraints') - const propertySchema = schema.patternProperties[propertyPattern] as Types.TSchema - const propertyKeys = propertyPattern.slice(1, propertyPattern.length - 1).split('|') - propertyKeys.forEach((propertyKey) => { - comparable.set(propertyKey, propertySchema) - }) - } else { - globalThis.Object.entries(schema.properties).forEach(([propertyKey, propertySchema]) => { - comparable.set(propertyKey, propertySchema as Types.TSchema) - }) - } - return comparable - } - - // ------------------------------------------------------------------------ - // Indexable - // ------------------------------------------------------------------------ - - function Indexable(left: Left, right: Types.TSchema): StructuralResult { - if (TypeGuard.TUnion(right)) { - return StructuralResult.False - } else { - return Visit(left, right) - } - } - - // ------------------------------------------------------------------------ - // Checks - // ------------------------------------------------------------------------ - - function Any(left: Types.TAny, right: Types.TSchema): StructuralResult { - return AnyUnknownOrCustomRule(right) ? StructuralResult.True : StructuralResult.Union - } - - function Array(left: Types.TArray, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True - if (globalThis.Object.keys(right.properties).length === 0) return StructuralResult.True - return StructuralResult.False - } else if (!TypeGuard.TArray(right)) { - return StructuralResult.False - } else if (left.items === undefined && right.items !== undefined) { - return StructuralResult.False - } else if (left.items !== undefined && right.items === undefined) { - return StructuralResult.False - } else if (left.items === undefined && right.items === undefined) { - return StructuralResult.False - } else { - const result = Visit(left.items, right.items) !== StructuralResult.False - return result ? StructuralResult.True : StructuralResult.False - } - } - - function Boolean(left: Types.TBoolean, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TBoolean(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Constructor(left: Types.TConstructor, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && globalThis.Object.keys(right.properties).length === 0) { - return StructuralResult.True - } else if (!TypeGuard.TConstructor(right)) { - return StructuralResult.False - } else if (right.parameters.length < left.parameters.length) { - return StructuralResult.False - } else { - if (Visit(left.returns, right.returns) === StructuralResult.False) { - return StructuralResult.False - } - for (let i = 0; i < left.parameters.length; i++) { - const result = Visit(right.parameters[i], left.parameters[i]) - if (result === StructuralResult.False) return StructuralResult.False - } - return StructuralResult.True - } - } - - function Date(left: Types.TDate, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TRecord(right)) { - return StructuralResult.False - } else if (TypeGuard.TDate(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Function(left: Types.TFunction, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - if (right.properties['length'] !== undefined && right.properties['length'][Types.Kind] === 'Number') return StructuralResult.True - if (globalThis.Object.keys(right.properties).length === 0) return StructuralResult.True - return StructuralResult.False - } else if (!TypeGuard.TFunction(right)) { - return StructuralResult.False - } else if (right.parameters.length < left.parameters.length) { - return StructuralResult.False - } else if (Visit(left.returns, right.returns) === StructuralResult.False) { - return StructuralResult.False - } else { - for (let i = 0; i < left.parameters.length; i++) { - const result = Visit(right.parameters[i], left.parameters[i]) - if (result === StructuralResult.False) return StructuralResult.False - } - return StructuralResult.True - } - } - - function Integer(left: Types.TInteger, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TInteger(right) || TypeGuard.TNumber(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Literal(left: Types.TLiteral, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TRecord(right)) { - if (typeof left.const === 'string') { - return Indexable(left, RecordValue(right as Types.TRecord)) - } else { - return StructuralResult.False - } - } else if (TypeGuard.TLiteral(right) && left.const === right.const) { - return StructuralResult.True - } else if (TypeGuard.TString(right) && typeof left.const === 'string') { - return StructuralResult.True - } else if (TypeGuard.TNumber(right) && typeof left.const === 'number') { - return StructuralResult.True - } else if (TypeGuard.TInteger(right) && typeof left.const === 'number') { - return StructuralResult.True - } else if (TypeGuard.TBoolean(right) && typeof left.const === 'boolean') { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Number(left: Types.TNumber, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TNumber(right)) { - return StructuralResult.True - } else if (TypeGuard.TInteger(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Null(left: Types.TNull, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TNull(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Properties(left: Map, right: Map) { - if (right.size > left.size) return StructuralResult.False - if (![...right.keys()].every((rightKey) => left.has(rightKey))) return StructuralResult.False - for (const rightKey of right.keys()) { - const leftProp = left.get(rightKey)! - const rightProp = right.get(rightKey)! - if (Visit(leftProp, rightProp) === StructuralResult.False) { - return StructuralResult.False - } - } - return StructuralResult.True - } - - function Object(left: Types.TObject, right: Types.TAnySchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - return Properties(PropertyMap(left), PropertyMap(right)) - } else if (TypeGuard.TRecord(right)) { - if (!RecordNumberOrStringKey(right as Types.TRecord)) { - return Properties(PropertyMap(left), PropertyMap(right)) - } else { - return StructuralResult.True - } - } else { - return StructuralResult.False - } - } - - function Promise(left: Types.TPromise, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - if (ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0) { - return StructuralResult.True - } else { - return StructuralResult.False - } - } else if (!TypeGuard.TPromise(right)) { - return StructuralResult.False - } else { - const result = Visit(left.item, right.item) !== StructuralResult.False - return result ? StructuralResult.True : StructuralResult.False - } - } - - function Record(left: Types.TRecord, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - if (RecordPattern(left) === '^.*$' && right[Types.Hint] === 'Record') { - return StructuralResult.True - } else if (RecordPattern(left) === '^.*$') { - return StructuralResult.False - } else { - return globalThis.Object.keys(right.properties).length === 0 ? StructuralResult.True : StructuralResult.False - } - } else if (TypeGuard.TRecord(right)) { - if (!RecordNumberOrStringKey(left as Types.TRecord) && !RecordNumberOrStringKey(right as Types.TRecord)) { - return Properties(PropertyMap(left), PropertyMap(right)) - } else if (RecordNumberOrStringKey(left as Types.TRecord) && !RecordNumberOrStringKey(right as Types.TRecord)) { - const leftKey = RecordKey(left as Types.TRecord) - const rightKey = RecordKey(right as Types.TRecord) - if (Visit(rightKey, leftKey) === StructuralResult.False) { - return StructuralResult.False - } else { - return StructuralResult.True - } - } else { - return StructuralResult.True - } - } else { - return StructuralResult.False - } - } - - function Ref(left: Types.TRef, right: Types.TSchema): StructuralResult { - if (!referenceMap.has(left.$ref)) throw Error(`Cannot locate referenced $id '${left.$ref}'`) - const resolved = referenceMap.get(left.$ref)! - return Visit(resolved, right) - } - - function Self(left: Types.TSelf, right: Types.TSchema): StructuralResult { - if (!referenceMap.has(left.$ref)) throw Error(`Cannot locate referenced self $id '${left.$ref}'`) - const resolved = referenceMap.get(left.$ref)! - return Visit(resolved, right) - } - - function String(left: Types.TString, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TRecord(right)) { - return Indexable(left, RecordValue(right)) - } else if (TypeGuard.TString(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Tuple(left: Types.TTuple, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right)) { - const result = ObjectRightRule(left, right) || globalThis.Object.keys(right.properties).length === 0 - return result ? StructuralResult.True : StructuralResult.False - } else if (TypeGuard.TRecord(right)) { - return Indexable(left, RecordValue(right)) - } else if (TypeGuard.TArray(right)) { - if (right.items === undefined) { - return StructuralResult.False - } else if (TypeGuard.TUnion(right.items) && left.items) { - const result = left.items.every((left: Types.TSchema) => UnionRightRule(left, right.items as Types.TUnion) !== StructuralResult.False) - return result ? StructuralResult.True : StructuralResult.False - } else if (TypeGuard.TAny(right.items)) { - return StructuralResult.True - } else { - return StructuralResult.False - } - } - if (!TypeGuard.TTuple(right)) return StructuralResult.False - if (left.items === undefined && right.items === undefined) return StructuralResult.True - if (left.items === undefined && right.items !== undefined) return StructuralResult.False - if (left.items !== undefined && right.items === undefined) return StructuralResult.False - if (left.items === undefined && right.items === undefined) return StructuralResult.True - if (left.minItems !== right.minItems || left.maxItems !== right.maxItems) return StructuralResult.False - for (let i = 0; i < left.items!.length; i++) { - if (Visit(left.items![i], right.items![i]) === StructuralResult.False) return StructuralResult.False - } - return StructuralResult.True - } - - function Uint8Array(left: Types.TUint8Array, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TObject(right) && ObjectRightRule(left, right)) { - return StructuralResult.True - } else if (TypeGuard.TRecord(right)) { - return Indexable(left, RecordValue(right as Types.TRecord)) - } else if (TypeGuard.TUint8Array(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Undefined(left: Types.TUndefined, right: Types.TSchema): StructuralResult { - if (AnyUnknownOrCustomRule(right)) { - return StructuralResult.True - } else if (TypeGuard.TUndefined(right)) { - return StructuralResult.True - } else if (TypeGuard.TVoid(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnion(right)) { - return UnionRightRule(left, right) - } else { - return StructuralResult.False - } - } - - function Union(left: Types.TUnion, right: Types.TSchema): StructuralResult { - if (left.anyOf.some((left: Types.TSchema) => TypeGuard.TAny(left))) { - return StructuralResult.Union - } else if (TypeGuard.TUnion(right)) { - const result = left.anyOf.every((left: Types.TSchema) => right.anyOf.some((right: Types.TSchema) => Visit(left, right) !== StructuralResult.False)) - return result ? StructuralResult.True : StructuralResult.False - } else { - const result = left.anyOf.every((left: Types.TSchema) => Visit(left, right) !== StructuralResult.False) - return result ? StructuralResult.True : StructuralResult.False - } - } - - function Unknown(left: Types.TUnknown, right: Types.TSchema): StructuralResult { - if (TypeGuard.TUnion(right)) { - const result = right.anyOf.some((right: Types.TSchema) => TypeGuard.TAny(right) || TypeGuard.TUnknown(right)) - return result ? StructuralResult.True : StructuralResult.False - } else if (TypeGuard.TAny(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnknown(right)) { - return StructuralResult.True - } else { - return StructuralResult.False - } - } - - function Void(left: Types.TVoid, right: Types.TSchema): StructuralResult { - if (TypeGuard.TUnion(right)) { - const result = right.anyOf.some((right: Types.TSchema) => TypeGuard.TAny(right) || TypeGuard.TUnknown(right)) - return result ? StructuralResult.True : StructuralResult.False - } else if (TypeGuard.TAny(right)) { - return StructuralResult.True - } else if (TypeGuard.TUnknown(right)) { - return StructuralResult.True - } else if (TypeGuard.TVoid(right)) { - return StructuralResult.True - } else { - return StructuralResult.False - } - } - - let recursionDepth = 0 - function Visit(left: Left, right: Types.TSchema): StructuralResult { - recursionDepth += 1 - if (recursionDepth >= 1000) return StructuralResult.True - if (left.$id !== undefined) referenceMap.set(left.$id!, left) - if (right.$id !== undefined) referenceMap.set(right.$id!, right) - const resolvedRight = right[Types.Kind] === 'Self' ? referenceMap.get(right.$ref)! : right - if (TypeGuard.TAny(left)) { - return Any(left, resolvedRight) - } else if (TypeGuard.TArray(left)) { - return Array(left, resolvedRight) - } else if (TypeGuard.TBoolean(left)) { - return Boolean(left, resolvedRight) - } else if (TypeGuard.TConstructor(left)) { - return Constructor(left, resolvedRight) - } else if (TypeGuard.TDate(left)) { - return Date(left, resolvedRight) - } else if (TypeGuard.TFunction(left)) { - return Function(left, resolvedRight) - } else if (TypeGuard.TInteger(left)) { - return Integer(left, resolvedRight) - } else if (TypeGuard.TLiteral(left)) { - return Literal(left, resolvedRight) - } else if (TypeGuard.TNull(left)) { - return Null(left, resolvedRight) - } else if (TypeGuard.TNumber(left)) { - return Number(left, resolvedRight) - } else if (TypeGuard.TObject(left)) { - return Object(left, resolvedRight) - } else if (TypeGuard.TPromise(left)) { - return Promise(left, resolvedRight) - } else if (TypeGuard.TRecord(left)) { - return Record(left, resolvedRight) - } else if (TypeGuard.TRef(left)) { - return Ref(left, resolvedRight) - } else if (TypeGuard.TSelf(left)) { - return Self(left, resolvedRight) - } else if (TypeGuard.TString(left)) { - return String(left, resolvedRight) - } else if (TypeGuard.TTuple(left)) { - return Tuple(left, resolvedRight) - } else if (TypeGuard.TUndefined(left)) { - return Undefined(left, resolvedRight) - } else if (TypeGuard.TUint8Array(left)) { - return Uint8Array(left, resolvedRight) - } else if (TypeGuard.TUnion(left)) { - return Union(left, resolvedRight) - } else if (TypeGuard.TUnknown(left)) { - return Unknown(left, resolvedRight) - } else if (TypeGuard.TVoid(left)) { - return Void(left, resolvedRight) - } else if (TypeGuard.TUserDefined(left)) { - throw Error(`Structural: Cannot structurally compare custom type '${left[Types.Kind]}'`) - } else { - throw Error(`Structural: Unknown left operand '${left[Types.Kind]}'`) - } - } - - /** Structurally tests if the left schema extends the right. */ - export function Check(left: Types.TSchema, right: Types.TSchema): StructuralResult { - referenceMap.clear() - recursionDepth = 0 - return Visit(left, right) - } -} diff --git a/src/custom/custom.ts b/src/custom/custom.ts deleted file mode 100644 index f1d2cfcfb..000000000 --- a/src/custom/custom.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/custom - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export type CustomValidationFunction = (schema: TSchema, value: unknown) => boolean - -/** Provides functions to create user defined types */ -export namespace Custom { - const customs = new Map>() - - /** Clears all user defined types */ - export function Clear() { - return customs.clear() - } - - /** Returns true if this user defined type exists */ - export function Has(kind: string) { - return customs.has(kind) - } - - /** Sets a validation function for a user defined type */ - export function Set(kind: string, func: CustomValidationFunction) { - customs.set(kind, func) - } - - /** Gets a custom validation function for a user defined type */ - export function Get(kind: string) { - return customs.get(kind) - } -} diff --git a/src/custom/index.ts b/src/custom/index.ts deleted file mode 100644 index b96208819..000000000 --- a/src/custom/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/custom - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './custom' diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 0cbf89419..780b6d7f7 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -25,23 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------*/ - import * as Types from '../typebox' import { TypeSystem } from '../system/index' -import { TypeExtends } from '../guard/extends' -import { Format } from '../format/index' -import { Custom } from '../custom/index' -import { ValueHash } from '../hash/index' +import { ValueHash } from '../value/hash' // ------------------------------------------------------------------- // ValueErrorType // ------------------------------------------------------------------- - export enum ValueErrorType { Array, ArrayMinItems, ArrayMaxItems, ArrayUniqueItems, + BigInt, + BigIntMultipleOf, + BigIntExclusiveMinimum, + BigIntExclusiveMaximum, + BigIntMinimum, + BigIntMaximum, Boolean, Date, DateExclusiveMinimumTimestamp, @@ -55,8 +56,11 @@ export enum ValueErrorType { IntegerExclusiveMaximum, IntegerMinimum, IntegerMaximum, + Intersect, + IntersectUnevaluatedProperties, Literal, Never, + Not, Null, Number, NumberMultipleOf, @@ -78,6 +82,7 @@ export enum ValueErrorType { StringPattern, StringFormatUnknown, StringFormat, + Symbol, TupleZeroLength, TupleLength, Undefined, @@ -88,11 +93,9 @@ export enum ValueErrorType { Void, Custom, } - // ------------------------------------------------------------------- // ValueError // ------------------------------------------------------------------- - export interface ValueError { type: ValueErrorType schema: Types.TSchema @@ -100,33 +103,77 @@ export interface ValueError { value: unknown message: string } - +// ------------------------------------------------------------------- +// ValueErrorIterator +// ------------------------------------------------------------------- +export class ValueErrorIterator { + constructor(private readonly iterator: IterableIterator) {} + public [Symbol.iterator]() { + return this.iterator + } + /** Returns the first value error or undefined if no errors */ + public First(): ValueError | undefined { + const next = this.iterator.next() + return next.done ? undefined : next.value + } +} // ------------------------------------------------------------------- // ValueErrors // ------------------------------------------------------------------- - export class ValueErrorsUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueErrors: Unknown type') } } - +export class ValueErrorsDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueErrors: Unable to dereference schema with $id '${schema.$ref}'`) + } +} /** Provides functionality to generate a sequence of errors against a TypeBox type. */ export namespace ValueErrors { + // ---------------------------------------------------------------------- + // Guards + // ---------------------------------------------------------------------- + function IsObject(value: unknown): value is Record { + const result = typeof value === 'object' && value !== null + return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) + } + function IsRecordObject(value: unknown): value is Record { + return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) + } + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !isNaN(value) + const result = typeof value === 'number' + return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) } - + function IsInteger(value: unknown): value is number { + return globalThis.Number.isInteger(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsVoid(value: unknown): value is void { + const result = value === undefined + return TypeSystem.AllowVoidNull ? result || value === null : result + } + function IsDefined(value: unknown): value is T { + return value !== undefined + } + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} - function* Array(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!globalThis.Array.isArray(value)) { return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } } - if (IsNumber(schema.minItems) && !(value.length >= schema.minItems)) { + if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } } - if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { + if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } } // prettier-ignore @@ -137,176 +184,204 @@ export namespace ValueErrors { yield* Visit(schema.items, references, `${path}/${i}`, value[i]) } } - + function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsBigInt(value)) { + return yield { type: ValueErrorType.BigInt, schema, path, value, message: `Expected bigint` } + } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === globalThis.BigInt(0))) { + yield { type: ValueErrorType.BigIntMultipleOf, schema, path, value, message: `Expected bigint to be a multiple of ${schema.multipleOf}` } + } + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.BigIntExclusiveMinimum, schema, path, value, message: `Expected bigint to be greater than ${schema.exclusiveMinimum}` } + } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.BigIntExclusiveMaximum, schema, path, value, message: `Expected bigint to be less than ${schema.exclusiveMaximum}` } + } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + yield { type: ValueErrorType.BigIntMinimum, schema, path, value, message: `Expected bigint to be greater or equal to ${schema.minimum}` } + } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield { type: ValueErrorType.BigIntMaximum, schema, path, value, message: `Expected bigint to be less or equal to ${schema.maximum}` } + } + } function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(typeof value === 'boolean')) { return yield { type: ValueErrorType.Boolean, schema, path, value, message: `Expected boolean` } } } - function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { yield* Visit(schema.returns, references, path, value.prototype) } - function* Date(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value instanceof globalThis.Date)) { return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } } - if (isNaN(value.getTime())) { + if (!globalThis.isFinite(value.getTime())) { return yield { type: ValueErrorType.Date, schema, path, value, message: `Invalid Date` } } - if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } } - if (IsNumber(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } } - if (IsNumber(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { + if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } } - if (IsNumber(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } } } - function* Function(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(typeof value === 'function')) { return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } } } - function* Integer(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { + if (!IsInteger(value)) { return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } } - if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } } - if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } } - if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } } - if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } } - if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } } } - + function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { + for (const subschema of schema.allOf) { + const next = Visit(subschema, references, path, value).next() + if (!next.done) { + yield next.value + yield { type: ValueErrorType.Intersect, schema, path, value, message: `Expected all sub schemas to be valid` } + return + } + } + if (schema.unevaluatedProperties === false) { + const schemaKeys = Types.KeyResolver.Resolve(schema) + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + for (const valueKey of valueKeys) { + if (!schemaKeys.includes(valueKey)) { + yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Unexpected property` } + } + } + } + if (typeof schema.unevaluatedProperties === 'object') { + const schemaKeys = Types.KeyResolver.Resolve(schema) + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + for (const valueKey of valueKeys) { + if (!schemaKeys.includes(valueKey)) { + const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() + if (!next.done) { + yield next.value + yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Invalid additional property` } + return + } + } + } + } + } function* Literal(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value === schema.const)) { const error = typeof schema.const === 'string' ? `'${schema.const}'` : schema.const return yield { type: ValueErrorType.Literal, schema, path, value, message: `Expected ${error}` } } } - function* Never(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } } - + function* Not(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (Visit(schema.allOf[0].not, references, path, value).next().done === true) { + yield { type: ValueErrorType.Not, schema, path, value, message: `Value should not validate` } + } + yield* Visit(schema.allOf[1], references, path, value) + } function* Null(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value === null)) { return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } } } - function* Number(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (TypeSystem.AllowNaN) { - if (!(typeof value === 'number')) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } - } - } else { - if (!(typeof value === 'number' && !isNaN(value))) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } - } + if (!IsNumber(value)) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } - if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } } - if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } } - if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } } - if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } } - if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { yield { type: ValueErrorType.NumberMinumum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } } } - function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (TypeSystem.AllowArrayObjects) { - if (!(typeof value === 'object' && value !== null)) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } - } else { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } + if (!IsObject(value)) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } } - if (IsNumber(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } - if (IsNumber(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } } - const propertyNames = globalThis.Object.getOwnPropertyNames(schema.properties) - if (schema.additionalProperties === false) { - for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { - if (!propertyNames.includes(propertyName)) { - yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${propertyName}`, value: value[propertyName], message: `Unexpected property` } + const requiredKeys = globalThis.Array.isArray(schema.required) ? schema.required : ([] as string[]) + const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) + const unknownKeys = globalThis.Object.getOwnPropertyNames(value) + for (const knownKey of knownKeys) { + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { + yield { type: ValueErrorType.ObjectRequiredProperties, schema: property, path: `${path}/${knownKey}`, value: undefined, message: `Expected required property` } + } + } else { + if (knownKey in value) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) } } } - if (schema.required && schema.required.length > 0) { - const propertyNames = globalThis.Object.getOwnPropertyNames(value) - for (const requiredKey of schema.required) { - if (propertyNames.includes(requiredKey)) continue - yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } - } + for (const requiredKey of requiredKeys) { + if (unknownKeys.includes(requiredKey)) continue + yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } } - if (typeof schema.additionalProperties === 'object') { - for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { - if (propertyNames.includes(propertyName)) continue - yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${propertyName}`, value[propertyName]) + if (schema.additionalProperties === false) { + for (const valueKey of unknownKeys) { + if (!knownKeys.includes(valueKey)) { + yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${valueKey}`, value: value[valueKey], message: `Unexpected property` } + } } } - for (const propertyKey of propertyNames) { - const propertySchema = schema.properties[propertyKey] - if (schema.required && schema.required.includes(propertyKey)) { - yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) - if (TypeExtends.Undefined(schema) && !(propertyKey in value)) { - yield { type: ValueErrorType.ObjectRequiredProperties, schema: propertySchema, path: `${path}/${propertyKey}`, value: undefined, message: `Expected required property` } - } - } else { - if (value[propertyKey] !== undefined) { - yield* Visit(propertySchema, references, `${path}/${propertyKey}`, value[propertyKey]) - } + if (typeof schema.additionalProperties === 'object') { + for (const valueKey of unknownKeys) { + if (knownKeys.includes(valueKey)) continue + yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey]) } } } - function* Promise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(typeof value === 'object' && typeof value.then === 'function')) { yield { type: ValueErrorType.Promise, schema, path, value, message: `Expected Promise` } } } - function* Record(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (TypeSystem.AllowArrayObjects) { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } - } else { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date) && !globalThis.Array.isArray(value))) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } + if (!IsRecordObject(value)) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected record object` } } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) @@ -320,27 +395,26 @@ export namespace ValueErrors { yield* Visit(valueSchema, references, `${path}/${propKey}`, propValue) } } - function* Ref(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueErrors.Ref: Cannot find schema with $id '${schema.$ref}'.`) - yield* Visit(reference, references, path, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueErrorsDereferenceError(schema) + const target = references[index] + yield* Visit(target, references, path, value) } - function* Self(schema: Types.TSelf, references: Types.TSchema[], path: string, value: any): IterableIterator { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueErrors.Self: Cannot find schema with $id '${schema.$ref}'.`) - yield* Visit(reference, references, path, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueErrorsDereferenceError(schema) + const target = references[index] + yield* Visit(target, references, path, value) } - function* String(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'string')) { + if (!IsString(value)) { return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } } - if (IsNumber(schema.minLength) && !(value.length >= schema.minLength)) { + if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } } - if (IsNumber(schema.maxLength) && !(value.length <= schema.maxLength)) { + if (IsDefined(schema.maxLength) && !(value.length <= schema.maxLength)) { yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } } if (schema.pattern !== undefined) { @@ -350,17 +424,21 @@ export namespace ValueErrors { } } if (schema.format !== undefined) { - if (!Format.Has(schema.format)) { + if (!Types.FormatRegistry.Has(schema.format)) { yield { type: ValueErrorType.StringFormatUnknown, schema, path, value, message: `Unknown string format '${schema.format}'` } } else { - const format = Format.Get(schema.format)! + const format = Types.FormatRegistry.Get(schema.format)! if (!format(value)) { yield { type: ValueErrorType.StringFormat, schema, path, value, message: `Expected string to match format '${schema.format}'` } } } } } - + function* Symbol(schema: Types.TSymbol, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(typeof value === 'symbol')) { + return yield { type: ValueErrorType.Symbol, schema, path, value, message: 'Expected symbol' } + } + } function* Tuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!globalThis.Array.isArray(value)) { return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } @@ -378,13 +456,11 @@ export namespace ValueErrors { yield* Visit(schema.items[i], references, `${path}/${i}`, value[i]) } } - function* Undefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value === undefined)) { yield { type: ValueErrorType.Undefined, schema, path, value, message: `Expected undefined` } } } - function* Union(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { const errors: ValueError[] = [] for (const inner of schema.anyOf) { @@ -399,91 +475,94 @@ export namespace ValueErrors { yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } } } - function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value instanceof globalThis.Uint8Array)) { return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } } - if (IsNumber(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { + if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } } - if (IsNumber(schema.minByteLength) && !(value.length >= schema.minByteLength)) { + if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } } } - function* Unknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} - function* Void(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === null)) { - return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected null` } + if (!IsVoid(value)) { + return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected void` } } } - function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { - const func = Custom.Get(schema[Types.Kind])! - if (!func(schema, value)) { + const check = Types.TypeRegistry.Get(schema[Types.Kind])! + if (!check(schema, value)) { return yield { type: ValueErrorType.Custom, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } } } - function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { - const anyReferences = schema.$id === undefined ? references : [schema, ...references] - const anySchema = schema as any - switch (anySchema[Types.Kind]) { + const references_ = IsDefined(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { case 'Any': - return yield* Any(anySchema, anyReferences, path, value) + return yield* Any(schema_, references_, path, value) case 'Array': - return yield* Array(anySchema, anyReferences, path, value) + return yield* Array(schema_, references_, path, value) + case 'BigInt': + return yield* BigInt(schema_, references_, path, value) case 'Boolean': - return yield* Boolean(anySchema, anyReferences, path, value) + return yield* Boolean(schema_, references_, path, value) case 'Constructor': - return yield* Constructor(anySchema, anyReferences, path, value) + return yield* Constructor(schema_, references_, path, value) case 'Date': - return yield* Date(anySchema, anyReferences, path, value) + return yield* Date(schema_, references_, path, value) case 'Function': - return yield* Function(anySchema, anyReferences, path, value) + return yield* Function(schema_, references_, path, value) case 'Integer': - return yield* Integer(anySchema, anyReferences, path, value) + return yield* Integer(schema_, references_, path, value) + case 'Intersect': + return yield* Intersect(schema_, references_, path, value) case 'Literal': - return yield* Literal(anySchema, anyReferences, path, value) + return yield* Literal(schema_, references_, path, value) case 'Never': - return yield* Never(anySchema, anyReferences, path, value) + return yield* Never(schema_, references_, path, value) + case 'Not': + return yield* Not(schema_, references_, path, value) case 'Null': - return yield* Null(anySchema, anyReferences, path, value) + return yield* Null(schema_, references_, path, value) case 'Number': - return yield* Number(anySchema, anyReferences, path, value) + return yield* Number(schema_, references_, path, value) case 'Object': - return yield* Object(anySchema, anyReferences, path, value) + return yield* Object(schema_, references_, path, value) case 'Promise': - return yield* Promise(anySchema, anyReferences, path, value) + return yield* Promise(schema_, references_, path, value) case 'Record': - return yield* Record(anySchema, anyReferences, path, value) + return yield* Record(schema_, references_, path, value) case 'Ref': - return yield* Ref(anySchema, anyReferences, path, value) + return yield* Ref(schema_, references_, path, value) case 'Self': - return yield* Self(anySchema, anyReferences, path, value) + return yield* Self(schema_, references_, path, value) case 'String': - return yield* String(anySchema, anyReferences, path, value) + return yield* String(schema_, references_, path, value) + case 'Symbol': + return yield* Symbol(schema_, references_, path, value) case 'Tuple': - return yield* Tuple(anySchema, anyReferences, path, value) + return yield* Tuple(schema_, references_, path, value) case 'Undefined': - return yield* Undefined(anySchema, anyReferences, path, value) + return yield* Undefined(schema_, references_, path, value) case 'Union': - return yield* Union(anySchema, anyReferences, path, value) + return yield* Union(schema_, references_, path, value) case 'Uint8Array': - return yield* Uint8Array(anySchema, anyReferences, path, value) + return yield* Uint8Array(schema_, references_, path, value) case 'Unknown': - return yield* Unknown(anySchema, anyReferences, path, value) + return yield* Unknown(schema_, references_, path, value) case 'Void': - return yield* Void(anySchema, anyReferences, path, value) + return yield* Void(schema_, references_, path, value) default: - if (!Custom.Has(anySchema[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) - return yield* UserDefined(anySchema, anyReferences, path, value) + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) + return yield* UserDefined(schema_, references_, path, value) } } - - export function* Errors(schema: T, references: Types.TSchema[], value: any): IterableIterator { - yield* Visit(schema, references, '', value) + export function Errors(schema: T, references: Types.TSchema[], value: any): ValueErrorIterator { + const iterator = Visit(schema, references, '', value) + return new ValueErrorIterator(iterator) } } diff --git a/src/format/format.ts b/src/format/format.ts deleted file mode 100644 index 9bf384637..000000000 --- a/src/format/format.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/format - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export type FormatValidationFunction = (value: string) => boolean - -/** Provides functions to create user defined string formats */ -export namespace Format { - const formats = new Map() - - /** Clears all user defined string formats */ - export function Clear() { - return formats.clear() - } - - /** Returns true if the user defined string format exists */ - export function Has(format: string) { - return formats.has(format) - } - - /** Sets a validation function for a user defined string format */ - export function Set(format: string, func: FormatValidationFunction) { - formats.set(format, func) - } - - /** Gets a validation function for a user defined string format */ - export function Get(format: string) { - return formats.get(format) - } -} diff --git a/src/format/index.ts b/src/format/index.ts deleted file mode 100644 index 58e298c28..000000000 --- a/src/format/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/format - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './format' diff --git a/src/guard/extends.ts b/src/guard/extends.ts deleted file mode 100644 index 441a87dca..000000000 --- a/src/guard/extends.ts +++ /dev/null @@ -1,46 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/guard - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, dTribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '../typebox' - -export namespace TypeExtends { - /** - * This function returns true if the given schema is undefined, either directly or - * through union composition. This check is required on object property types of - * undefined, where an additional `'x' in value` check is required to determine - * the keys existence. - */ - export function Undefined(schema: Types.TSchema): boolean { - if (schema[Types.Kind] === 'Undefined') return true - if (schema[Types.Kind] === 'Union') { - const union = schema as Types.TUnion - return union.anyOf.some((schema) => Undefined(schema)) - } - return false - } -} diff --git a/src/guard/guard.ts b/src/guard/guard.ts deleted file mode 100644 index 87d4bc1a8..000000000 --- a/src/guard/guard.ts +++ /dev/null @@ -1,487 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/guard - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, dTribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Custom } from '../custom/index' -import * as Types from '../typebox' - -export class TypeGuardUnknownTypeError extends Error { - constructor(public readonly schema: unknown) { - super('TypeGuard: Unknown type') - } -} - -/** Provides functionality to test if values are TypeBox types */ -export namespace TypeGuard { - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value) - } - - function IsArray(value: unknown): value is any[] { - return typeof value === 'object' && value !== null && Array.isArray(value) - } - - function IsPattern(value: unknown): value is string { - try { - new RegExp(value as string) - return true - } catch { - return false - } - } - - function IsControlCharacterFree(value: unknown): value is string { - if (typeof value !== 'string') return false - for (let i = 0; i < value.length; i++) { - const code = value.charCodeAt(i) - if ((code >= 7 && code <= 13) || code === 27 || code === 127) { - return false - } - } - return true - } - - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - - function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !isNaN(value) - } - - function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - - function IsOptionalNumber(value: unknown): value is number | undefined { - return value === undefined || (value !== undefined && IsNumber(value)) - } - - function IsOptionalBoolean(value: unknown): value is boolean | undefined { - return value === undefined || (value !== undefined && IsBoolean(value)) - } - - function IsOptionalString(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value)) - } - - function IsOptionalPattern(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) - } - - function IsOptionalFormat(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) - } - - function IsOptionalSchema(value: unknown): value is boolean | undefined { - return value === undefined || TSchema(value) - } - - /** Returns true if the given schema is TAny */ - export function TAny(schema: unknown): schema is Types.TAny { - return IsObject(schema) && schema[Types.Kind] === 'Any' && IsOptionalString(schema.$id) - } - - /** Returns true if the given schema is TArray */ - export function TArray(schema: unknown): schema is Types.TArray { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Array' && - schema.type === 'array' && - IsOptionalString(schema.$id) && - TSchema(schema.items) && - IsOptionalNumber(schema.minItems) && - IsOptionalNumber(schema.maxItems) && - IsOptionalBoolean(schema.uniqueItems) - ) - } - - /** Returns true if the given schema is TBoolean */ - export function TBoolean(schema: unknown): schema is Types.TBoolean { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Boolean' && - schema.type === 'boolean' && - IsOptionalString(schema.$id) - ) - } - - /** Returns true if the given schema is TConstructor */ - export function TConstructor(schema: unknown): schema is Types.TConstructor { - // prettier-ignore - if (!( - IsObject(schema) && - schema[Types.Kind] === 'Constructor' && - schema.type === 'object' && - schema.instanceOf === 'Constructor' && - IsOptionalString(schema.$id) && - IsArray(schema.parameters) && - TSchema(schema.returns)) - ) { - return false - } - for (const parameter of schema.parameters) { - if (!TSchema(parameter)) return false - } - return true - } - - /** Returns true if the given schema is TDate */ - export function TDate(schema: unknown): schema is Types.TDate { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Date' && - schema.type === 'object' && - schema.instanceOf === 'Date' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minimumTimestamp) && - IsOptionalNumber(schema.maximumTimestamp) && - IsOptionalNumber(schema.exclusiveMinimumTimestamp) && - IsOptionalNumber(schema.exclusiveMaximumTimestamp) - ) - } - - /** Returns true if the given schema is TFunction */ - export function TFunction(schema: unknown): schema is Types.TFunction { - // prettier-ignore - if (!( - IsObject(schema) && - schema[Types.Kind] === 'Function' && - schema.type === 'object' && - schema.instanceOf === 'Function' && - IsOptionalString(schema.$id) && - IsArray(schema.parameters) && - TSchema(schema.returns)) - ) { - return false - } - for (const parameter of schema.parameters) { - if (!TSchema(parameter)) return false - } - return true - } - - /** Returns true if the given schema is TInteger */ - export function TInteger(schema: unknown): schema is Types.TInteger { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Integer' && - schema.type === 'integer' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.multipleOf) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.exclusiveMaximum) - ) - } - - /** Returns true if the given schema is TLiteral */ - export function TLiteral(schema: unknown): schema is Types.TLiteral { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Literal' && - IsOptionalString(schema.$id) && - ( - IsString(schema.const) || - IsNumber(schema.const) || - IsBoolean(schema.const) - ) - ) - } - - /** Returns true if the given schema is TNever */ - export function TNever(schema: unknown): schema is Types.TNever { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Never' && - IsArray(schema.allOf) && - schema.allOf.length === 2 && - IsObject(schema.allOf[0]) && - IsString(schema.allOf[0].type) && - schema.allOf[0].type === 'boolean' && - schema.allOf[0].const === false && - IsObject(schema.allOf[1]) && - IsString(schema.allOf[1].type) && - schema.allOf[1].type === 'boolean' && - schema.allOf[1].const === true - ) - } - - /** Returns true if the given schema is TNull */ - export function TNull(schema: unknown): schema is Types.TNull { - return IsObject(schema) && schema[Types.Kind] === 'Null' && schema.type === 'null' && IsOptionalString(schema.$id) - } - - /** Returns true if the given schema is TNumber */ - export function TNumber(schema: unknown): schema is Types.TNumber { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Number' && - schema.type === 'number' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.multipleOf) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.exclusiveMaximum) - ) - } - - /** Returns true if the given schema is TObject */ - export function TObject(schema: unknown): schema is Types.TObject { - if ( - !( - IsObject(schema) && - schema[Types.Kind] === 'Object' && - schema.type === 'object' && - IsOptionalString(schema.$id) && - IsObject(schema.properties) && - (IsOptionalBoolean(schema.additionalProperties) || IsOptionalSchema(schema.additionalProperties)) && - IsOptionalNumber(schema.minProperties) && - IsOptionalNumber(schema.maxProperties) - ) - ) { - return false - } - for (const [key, value] of Object.entries(schema.properties)) { - if (!IsControlCharacterFree(key)) return false - if (!TSchema(value)) return false - } - return true - } - - /** Returns true if the given schema is TPromise */ - export function TPromise(schema: unknown): schema is Types.TPromise { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Promise' && - schema.type === 'object' && - schema.instanceOf === 'Promise' && - IsOptionalString(schema.$id) && - TSchema(schema.item) - ) - } - - /** Returns true if the given schema is TRecord */ - export function TRecord(schema: unknown): schema is Types.TRecord { - // prettier-ignore - if (!( - IsObject(schema) && - schema[Types.Kind] === 'Record' && - schema.type === 'object' && - IsOptionalString(schema.$id) && - schema.additionalProperties === false && - IsObject(schema.patternProperties)) - ) { - return false - } - const keys = Object.keys(schema.patternProperties) - if (keys.length !== 1) { - return false - } - if (!IsPattern(keys[0])) { - return false - } - if (!TSchema(schema.patternProperties[keys[0]])) { - return false - } - return true - } - - /** Returns true if the given schema is TSelf */ - export function TSelf(schema: unknown): schema is Types.TSelf { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Self' && - IsOptionalString(schema.$id) && - IsString(schema.$ref) - ) - } - - /** Returns true if the given schema is TRef */ - export function TRef(schema: unknown): schema is Types.TRef { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Ref' && - IsOptionalString(schema.$id) && - IsString(schema.$ref) - ) - } - - /** Returns true if the given schema is TString */ - export function TString(schema: unknown): schema is Types.TString { - return ( - IsObject(schema) && - schema[Types.Kind] === 'String' && - schema.type === 'string' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minLength) && - IsOptionalNumber(schema.maxLength) && - IsOptionalPattern(schema.pattern) && - IsOptionalFormat(schema.format) - ) - } - - /** Returns true if the given schema is TTuple */ - export function TTuple(schema: unknown): schema is Types.TTuple { - // prettier-ignore - if (!( - IsObject(schema) && - schema[Types.Kind] === 'Tuple' && - schema.type === 'array' && - IsOptionalString(schema.$id) && - IsNumber(schema.minItems) && - IsNumber(schema.maxItems) && - schema.minItems === schema.maxItems) - ) { - return false - } - if (schema.items === undefined && schema.additionalItems === undefined && schema.minItems === 0) { - return true - } - if (!IsArray(schema.items)) { - return false - } - for (const inner of schema.items) { - if (!TSchema(inner)) return false - } - return true - } - - /** Returns true if the given schema is TUndefined */ - export function TUndefined(schema: unknown): schema is Types.TUndefined { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Undefined' && - schema.type === 'null' && - schema.typeOf === 'Undefined' && - IsOptionalString(schema.$id) - ) - } - - /** Returns true if the given schema is TUnion */ - export function TUnion(schema: unknown): schema is Types.TUnion { - // prettier-ignore - if (!( - IsObject(schema) && - schema[Types.Kind] === 'Union' && - IsArray(schema.anyOf) && - IsOptionalString(schema.$id)) - ) { - return false - } - for (const inner of schema.anyOf) { - if (!TSchema(inner)) return false - } - return true - } - - /** Returns true if the given schema is TUint8Array */ - export function TUint8Array(schema: unknown): schema is Types.TUint8Array { - return ( - IsObject(schema) && - schema[Types.Kind] === 'Uint8Array' && - schema.type === 'object' && - IsOptionalString(schema.$id) && - schema.instanceOf === 'Uint8Array' && - IsOptionalNumber(schema.minByteLength) && - IsOptionalNumber(schema.maxByteLength) - ) - } - - /** Returns true if the given schema is TUnknown */ - export function TUnknown(schema: unknown): schema is Types.TUnknown { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Unknown' && - IsOptionalString(schema.$id) - ) - } - - /** Returns true if the given schema is TVoid */ - export function TVoid(schema: unknown): schema is Types.TVoid { - // prettier-ignore - return ( - IsObject(schema) && - schema[Types.Kind] === 'Void' && - schema.type === 'null' && - schema.typeOf === 'Void' && - IsOptionalString(schema.$id) - ) - } - - /** Returns true if the given schema is a registered user defined type */ - export function TUserDefined(schema: unknown): schema is Types.TSchema { - return IsObject(schema) && IsString(schema[Types.Kind]) && Custom.Has(schema[Types.Kind]) - } - - /** Returns true if the given schema is TSchema */ - export function TSchema(schema: unknown): schema is Types.TSchema { - return ( - TAny(schema) || - TArray(schema) || - TBoolean(schema) || - TConstructor(schema) || - TDate(schema) || - TFunction(schema) || - TInteger(schema) || - TLiteral(schema) || - TNever(schema) || - TNull(schema) || - TNumber(schema) || - TObject(schema) || - TPromise(schema) || - TRecord(schema) || - TSelf(schema) || - TRef(schema) || - TString(schema) || - TTuple(schema) || - TUndefined(schema) || - TUnion(schema) || - TUint8Array(schema) || - TUnknown(schema) || - TVoid(schema) || - TUserDefined(schema) - ) - } - - /** Asserts if this schema and associated references are valid. */ - export function Assert(schema: T, references: Types.TSchema[] = []) { - if (!TSchema(schema)) throw new TypeGuardUnknownTypeError(schema) - for (const schema of references) { - if (!TSchema(schema)) throw new TypeGuardUnknownTypeError(schema) - } - } -} diff --git a/src/guard/index.ts b/src/guard/index.ts deleted file mode 100644 index 036419519..000000000 --- a/src/guard/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/guards - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './guard' -export * from './extends' diff --git a/src/system/system.ts b/src/system/system.ts index 1a637f8aa..598f6cc09 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,40 +26,47 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Kind, Type } from '../typebox' -import { Custom } from '../custom/index' -import { Format } from '../format/index' +import * as Types from '../typebox' export class TypeSystemDuplicateTypeKind extends Error { constructor(kind: string) { - super(`Duplicate kind '${kind}' detected`) + super(`Duplicate type kind '${kind}' detected`) } } export class TypeSystemDuplicateFormat extends Error { constructor(kind: string) { - super(`Duplicate format '${kind}' detected`) + super(`Duplicate string format '${kind}' detected`) } } - /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { - /** Sets whether arrays should be treated as kinds of objects. The default is `false` */ + /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ export let AllowArrayObjects: boolean = false - - /** Sets whether numeric checks should consider NaN a valid number type. The default is `false` */ + /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ export let AllowNaN: boolean = false - - /** Creates a custom type */ - export function CreateType(kind: string, callback: (options: Options, value: unknown) => boolean) { - if (Custom.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) - Custom.Set(kind, callback) - return (options: Partial = {}) => Type.Unsafe({ ...options, [Kind]: kind }) + /** Sets whether `null` should validate for void types. The default is `false` */ + export let AllowVoidNull: boolean = false + /** Creates a new type */ + export function Type(kind: string, check: (options: Options, value: unknown) => boolean) { + if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) + Types.TypeRegistry.Set(kind, check) + return (options: Partial = {}) => Types.Type.Unsafe({ ...options, [Types.Kind]: kind }) } - - /** Creates a custom string format */ - export function CreateFormat(format: string, callback: (value: string) => boolean) { - if (Format.Has(format)) throw new TypeSystemDuplicateFormat(format) - Format.Set(format, callback) - return callback + /** Creates a new string format */ + export function Format(format: F, check: (value: string) => boolean): F { + if (Types.FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format) + Types.FormatRegistry.Set(format, check) + return format + } + // ------------------------------------------------------------------------ + // Deprecated + // ------------------------------------------------------------------------ + /** @deprecated Use `TypeSystem.Type()` instead. */ + export function CreateType(kind: string, check: (options: Options, value: unknown) => boolean) { + return Type(kind, check) + } + /** @deprecated Use `TypeSystem.Format()` instead. */ + export function CreateFormat(format: F, check: (value: string) => boolean): F { + return Format(format, check) } } diff --git a/src/tsconfig.json b/src/tsconfig.json index 52cb52269..c78307cfa 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "conditional/index.ts", "custom/index.ts", "format/index.ts", "guard/index.ts", "hash/index.ts", "value/index.ts", "typebox.ts"] + "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "value/index.ts", "typebox.ts"] } diff --git a/src/typebox.ts b/src/typebox.ts index e5fb7ff5d..6c8796938 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -27,26 +27,32 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ // -------------------------------------------------------------------------- -// Symbols +// Compositing Symbols // -------------------------------------------------------------------------- - -export const Kind = Symbol.for('TypeBox.Kind') -export const Hint = Symbol.for('TypeBox.Hint') export const Modifier = Symbol.for('TypeBox.Modifier') - +export const Hint = Symbol.for('TypeBox.Hint') +export const Kind = Symbol.for('TypeBox.Kind') +// -------------------------------------------------------------------------- +// Helpers +// -------------------------------------------------------------------------- +export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never +export type TupleToUnion = { [K in keyof T]: T[K] }[number] +export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never +export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never +export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] +export type Assert = T extends E ? T : never +export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never +export type Ensure = T extends infer U ? U : never // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- - export type TModifier = TReadonlyOptional | TOptional | TReadonly export type TReadonly = T & { [Modifier]: 'Readonly' } export type TOptional = T & { [Modifier]: 'Optional' } export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOptional' } - // -------------------------------------------------------------------------- -// Schema +// TSchema // -------------------------------------------------------------------------- - export interface SchemaOptions { $schema?: string /** Id for this schema */ @@ -57,294 +63,309 @@ export interface SchemaOptions { description?: string /** Default value for this schema */ default?: any - /** Example values matching this schema. */ + /** Example values matching this schema */ examples?: any [prop: string]: any } - -export interface TSchema extends SchemaOptions { +export interface TKind { [Kind]: string - [Hint]?: string +} +export interface TSchema extends SchemaOptions, TKind { [Modifier]?: string + [Hint]?: string params: unknown[] static: unknown } - // -------------------------------------------------------------------------- // TAnySchema // -------------------------------------------------------------------------- - export type TAnySchema = | TSchema | TAny | TArray + | TBigInt | TBoolean | TConstructor | TDate | TEnum | TFunction | TInteger + | TIntersect | TLiteral + | TNot | TNull | TNumber | TObject | TPromise | TRecord - | TSelf | TRef + | TSelf | TString + | TSymbol | TTuple | TUndefined | TUnion | TUint8Array | TUnknown | TVoid - // -------------------------------------------------------------------------- // TNumeric // -------------------------------------------------------------------------- - -export interface NumericOptions extends SchemaOptions { - exclusiveMaximum?: number - exclusiveMinimum?: number - maximum?: number - minimum?: number - multipleOf?: number -} - export type TNumeric = TInteger | TNumber - +export interface NumericOptions extends SchemaOptions { + exclusiveMaximum?: N + exclusiveMinimum?: N + maximum?: N + minimum?: N + multipleOf?: N +} // -------------------------------------------------------------------------- -// Any +// TAny // -------------------------------------------------------------------------- - export interface TAny extends TSchema { [Kind]: 'Any' static: any } - // -------------------------------------------------------------------------- -// Array +// TArray // -------------------------------------------------------------------------- - export interface ArrayOptions extends SchemaOptions { uniqueItems?: boolean minItems?: number maxItems?: number } - export interface TArray extends TSchema, ArrayOptions { [Kind]: 'Array' - static: Array> + static: Static[] type: 'array' items: T } - // -------------------------------------------------------------------------- -// Boolean +// TBigInt +// -------------------------------------------------------------------------- +export interface TBigInt extends TSchema, NumericOptions { + [Kind]: 'BigInt' + static: bigint + type: 'null' + typeOf: 'BigInt' +} +// -------------------------------------------------------------------------- +// TBoolean // -------------------------------------------------------------------------- - export interface TBoolean extends TSchema { [Kind]: 'Boolean' static: boolean type: 'boolean' } - // -------------------------------------------------------------------------- -// Constructor +// TConstructorParameters // -------------------------------------------------------------------------- - export type TConstructorParameters> = TTuple - +// -------------------------------------------------------------------------- +// TInstanceType +// -------------------------------------------------------------------------- export type TInstanceType> = T['returns'] - -export type StaticContructorParameters = [...{ [K in keyof T]: T[K] extends TSchema ? Static : never }] - +// -------------------------------------------------------------------------- +// TComposite +// -------------------------------------------------------------------------- +export type TCompositeUnion = Ensure> +// note: we need to take the left and right as the accumulator is assigned for multiple composite property sets with missing properties. +export type TCompositeUnionLeft = { + [K in keyof T['properties']]: K extends keyof Acc ? TCompositeUnion : T['properties'][K] +} +export type TCompositeUnionRight = { + [K in keyof Acc]: K extends keyof T['properties'] ? TCompositeUnion : Acc[K] +} +export type TCompositeUnionObject = Evaluate & TCompositeUnionRight> +// prettier-ignore +export type TCompositeProperties = + T extends [...infer L, infer R] ? TCompositeProperties, TCompositeUnionObject, Acc>> : + T extends [] ? Acc : + never +export type TComposite = Ensure>> +// -------------------------------------------------------------------------- +// TConstructor +// -------------------------------------------------------------------------- +export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] export interface TConstructor extends TSchema { [Kind]: 'Constructor' - static: new (...param: StaticContructorParameters) => Static + static: new (...param: TConstructorParameterArray) => Static type: 'object' instanceOf: 'Constructor' parameters: T returns: U } - // -------------------------------------------------------------------------- -// Date +// TDate // -------------------------------------------------------------------------- - export interface DateOptions extends SchemaOptions { exclusiveMaximumTimestamp?: number exclusiveMinimumTimestamp?: number maximumTimestamp?: number minimumTimestamp?: number } - export interface TDate extends TSchema, DateOptions { [Kind]: 'Date' static: Date type: 'object' instanceOf: 'Date' } - // -------------------------------------------------------------------------- -// Enum +// TEnum // -------------------------------------------------------------------------- - export interface TEnumOption { type: 'number' | 'string' const: T } - +export type TEnumStatic> = T[keyof T] export interface TEnum = Record> extends TSchema { [Kind]: 'Union' - static: T[keyof T] + static: TEnumStatic anyOf: TLiteral[] } - // -------------------------------------------------------------------------- -// Function +// TExtends // -------------------------------------------------------------------------- - -export type TParameters = TTuple - -export type TReturnType = T['returns'] - -export type StaticFunctionParameters = [...{ [K in keyof T]: T[K] extends TSchema ? Static : never }] - +// prettier-ignore +export type TExtends = + (Static extends Static ? T : U) extends infer O ? + UnionToTuple extends [infer X, infer Y] ? TUnion<[Assert, Assert]> : Assert + : never +// -------------------------------------------------------------------------- +// TExclude +// -------------------------------------------------------------------------- +// prettier-ignore +export type TExcludeArray = Assert> extends Static ? never : T[K] +}[number]>, TSchema[]> extends infer R ? TUnionResult> : never +export type TExclude = T extends TUnion ? TExcludeArray : T extends U ? TNever : T +// -------------------------------------------------------------------------- +// TExtract +// -------------------------------------------------------------------------- +// prettier-ignore +export type TExtractArray = Assert> extends Static ? T[K] : never +}[number]>, TSchema[]> extends infer R ? TUnionResult> : never +export type TExtract = T extends TUnion ? TExtractArray : T extends U ? T : TNever +// -------------------------------------------------------------------------- +// TFunction +// -------------------------------------------------------------------------- +export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] export interface TFunction extends TSchema { [Kind]: 'Function' - static: (...param: StaticFunctionParameters) => Static + static: (...param: TFunctionParameters) => Static type: 'object' instanceOf: 'Function' parameters: T returns: U } - // -------------------------------------------------------------------------- -// Integer +// TInteger // -------------------------------------------------------------------------- - -export interface TInteger extends TSchema, NumericOptions { +export interface TInteger extends TSchema, NumericOptions { [Kind]: 'Integer' static: number type: 'integer' } - // -------------------------------------------------------------------------- -// Intersect +// TIntersect // -------------------------------------------------------------------------- - -export type IntersectReduce = T extends [infer A, ...infer B] ? IntersectReduce : I extends object ? I : {} - -// note: rename to IntersectStatic in next minor release -export type IntersectEvaluate = { [K in keyof T]: T[K] extends TSchema ? Static : never } - -export type IntersectProperties = { - [K in keyof T]: T[K] extends TObject ? P : {} +export type TUnevaluatedProperties = undefined | TSchema | boolean +export interface IntersectOptions extends SchemaOptions { + unevaluatedProperties?: TUnevaluatedProperties } - -export interface TIntersect extends TObject { - static: IntersectReduce> - properties: IntersectReduce> +export type TIntersectStatic = TupleToIntersect<{ [K in keyof T]: Static, P> }> +export interface TIntersect extends TSchema, IntersectOptions { + [Kind]: 'Intersect' + type?: 'object' + static: TIntersectStatic + allOf: [...T] } - // -------------------------------------------------------------------------- -// KeyOf +// TKeyOf // -------------------------------------------------------------------------- - -export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never -export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never -export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] -export type UnionStringLiteralToTuple = T extends TUnion ? { [I in keyof L]: L[I] extends TLiteral ? C : never } : never -export type UnionLiteralsFromObject = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never - -// export type TKeyOf = { [K in ObjectPropertyKeys]: TLiteral } extends infer R ? UnionToTuple : never - -export interface TKeyOf extends TUnion> {} - +// prettier-ignore +export type TKeyOfTuple = { + [K in keyof Static]: TLiteral> +} extends infer U + ? UnionToTuple> // optional yields undefined keys + : never +// prettier-ignore +export type TKeyOf = ( + T extends TIntersect ? TKeyOfTuple : + T extends TUnion ? TKeyOfTuple : + T extends TObject ? TKeyOfTuple : + [] +) extends infer R ? TUnionResult> : never // -------------------------------------------------------------------------- -// Literal +// TLiteral // -------------------------------------------------------------------------- - -export type TLiteralValue = string | number | boolean - +export type TLiteralValue = string | number | boolean // | bigint - supported but variant disable due to potential numeric type conflicts export interface TLiteral extends TSchema { [Kind]: 'Literal' static: T const: T } - // -------------------------------------------------------------------------- -// Never +// TNever // -------------------------------------------------------------------------- - export interface TNever extends TSchema { [Kind]: 'Never' static: never allOf: [{ type: 'boolean'; const: false }, { type: 'boolean'; const: true }] } - // -------------------------------------------------------------------------- -// Null +// TNot +// -------------------------------------------------------------------------- +export type TNotStatic<_ extends TSchema = TSchema, T extends TSchema = TSchema> = Static +export interface TNot extends TSchema { + [Kind]: 'Not' + static: TNotStatic + allOf: [{ not: Not }, T] +} +// -------------------------------------------------------------------------- +// TNull // -------------------------------------------------------------------------- - export interface TNull extends TSchema { [Kind]: 'Null' static: null type: 'null' } - // -------------------------------------------------------------------------- -// Number +// TNumber // -------------------------------------------------------------------------- - -export interface TNumber extends TSchema, NumericOptions { +export interface TNumber extends TSchema, NumericOptions { [Kind]: 'Number' static: number type: 'number' } - // -------------------------------------------------------------------------- -// Object +// TObject // -------------------------------------------------------------------------- - export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? K : never }[keyof T] export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> - // prettier-ignore -export type PropertiesReducer> = ( +export type PropertiesReducer> = Evaluate<( Readonly>>> & Readonly>> & Partial>> & Required>> -) extends infer O ? { [K in keyof O]: O[K] } : never - +)> // prettier-ignore -export type PropertiesReduce = PropertiesReducer +export type PropertiesReduce = PropertiesReducer }> - -export type TRecordProperties, T extends TSchema> = Static extends string ? { [X in Static]: T } : never - -export interface TProperties { - [key: string]: TSchema -} - +export type TProperties = Record export type ObjectProperties = T extends TObject ? U : never - export type ObjectPropertyKeys = T extends TObject ? keyof U : never - export type TAdditionalProperties = undefined | TSchema | boolean - export interface ObjectOptions extends SchemaOptions { additionalProperties?: TAdditionalProperties minProperties?: number maxProperties?: number } - export interface TObject extends TSchema, ObjectOptions { [Kind]: 'Object' static: PropertiesReduce @@ -353,117 +374,128 @@ export interface TObject extends TSchema, O properties: T required?: string[] } - // -------------------------------------------------------------------------- -// Omit +// TOmit // -------------------------------------------------------------------------- - -export interface TOmit[]> extends TObject, ObjectOptions { - static: Omit, Properties[number]> - properties: T extends TObject ? Omit : never -} - +export type TOmitArray = Assert<{ [K2 in keyof T]: TOmit, K> }, TSchema[]> +export type TOmitProperties = Evaluate, TProperties>> +// prettier-ignore +export type TOmit = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + T // -------------------------------------------------------------------------- -// Partial +// TParameters // -------------------------------------------------------------------------- - -export interface TPartial extends TObject { - static: Partial> - properties: { - // prettier-ignore - [K in keyof T['properties']]: - T['properties'][K] extends TReadonlyOptional ? TReadonlyOptional : - T['properties'][K] extends TReadonly ? TReadonlyOptional : - T['properties'][K] extends TOptional ? TOptional : - TOptional - } -} - +export type TParameters = TTuple // -------------------------------------------------------------------------- -// Pick +// TPartial // -------------------------------------------------------------------------- - -// export interface TPick[]> extends TObject, ObjectOptions { -// static: Pick, Properties[number]> -// properties: ObjectProperties -// } - -export type TPick[]> = TObject<{ - [K in Properties[number]]: T['properties'][K] -}> - +export type TPartialArray = Assert<{ [K in keyof T]: TPartial> }, TSchema[]> +// prettier-ignore +export type TPartialProperties = Evaluate ? TReadonlyOptional : + T[K] extends TReadonly ? TReadonlyOptional : + T[K] extends TOptional ? TOptional : + TOptional +}, TProperties>> +// prettier-ignore +export type TPartial = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + T // -------------------------------------------------------------------------- -// Promise +// TPick // -------------------------------------------------------------------------- +export type TPickArray = { [K2 in keyof T]: TPick, K> } +// Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, +// we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. +// prettier-ignore +export type TPickProperties = + Pick, keyof T>> extends infer R ? ({ + [K in keyof R]: Assert extends TSchema ? R[K] : never + }): never +// prettier-ignore +export type TPick = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + T +// -------------------------------------------------------------------------- +// TPromise +// -------------------------------------------------------------------------- +export type TPromiseStatic = Promise> export interface TPromise extends TSchema { [Kind]: 'Promise' - static: Promise> + static: TPromiseStatic type: 'object' instanceOf: 'Promise' item: TSchema } - // -------------------------------------------------------------------------- -// Record +// TRecord // -------------------------------------------------------------------------- - export type TRecordKey = TString | TNumeric | TUnion[]> - +export type TRecordPropertiesFromUnionLiteral[]>, T extends TSchema> = Static extends string ? { [X in Static]: T } : never +export type TRecordPropertiesFromLiteral, T extends TSchema> = Evaluate<{ [K2 in K['const']]: T }> +export type TRecordStatic = Record, Static> export interface TRecord extends TSchema { [Kind]: 'Record' - static: Record, Static> + static: TRecordStatic type: 'object' patternProperties: { [pattern: string]: T } additionalProperties: false } - // -------------------------------------------------------------------------- -// Recursive +// TRecursive // -------------------------------------------------------------------------- - export interface TSelf extends TSchema { [Kind]: 'Self' static: this['params'][0] $ref: string } - export type TRecursiveReduce = Static]> - export interface TRecursive extends TSchema { static: TRecursiveReduce } - // -------------------------------------------------------------------------- -// Ref +// TRef // -------------------------------------------------------------------------- - +export type TRefStatic = Static export interface TRef extends TSchema { [Kind]: 'Ref' - static: Static + static: TRefStatic $ref: string } - // -------------------------------------------------------------------------- -// Required +// TReturnType // -------------------------------------------------------------------------- - -export interface TRequired> extends TObject { - static: Required> - properties: { - // prettier-ignore - [K in keyof T['properties']]: - T['properties'][K] extends TReadonlyOptional ? TReadonly : - T['properties'][K] extends TReadonly ? TReadonly : - T['properties'][K] extends TOptional ? U : - T['properties'][K] - } -} - +export type TReturnType = T['returns'] // -------------------------------------------------------------------------- -// String +// TRequired +// -------------------------------------------------------------------------- +export type TRequiredArray = Assert<{ [K in keyof T]: TRequired> }, TSchema[]> +// prettier-ignore +export type TRequiredProperties = Evaluate ? TReadonly : + T[K] extends TReadonly ? TReadonly : + T[K] extends TOptional ? U : + T[K] +}, TProperties>> +// prettier-ignore +export type TRequired = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + T +// -------------------------------------------------------------------------- +// TString // -------------------------------------------------------------------------- - export type StringFormatOption = | 'date-time' | 'time' @@ -483,7 +515,6 @@ export type StringFormatOption = | 'json-pointer' | 'relative-json-pointer' | 'regex' - export interface StringOptions extends SchemaOptions { minLength?: number maxLength?: number @@ -492,529 +523,1689 @@ export interface StringOptions extends SchemaOptions { contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64' contentMediaType?: string } - export interface TString extends TSchema, StringOptions { [Kind]: 'String' static: string type: 'string' } - // -------------------------------------------------------------------------- -// Tuple +// TSymbol // -------------------------------------------------------------------------- - -export type TupleToArray> = T extends TTuple ? R : never - +export type SymbolValue = string | number | undefined +export interface TSymbol extends TSchema, SchemaOptions { + [Kind]: 'Symbol' + static: symbol + type: 'null' + typeOf: 'Symbol' +} +// -------------------------------------------------------------------------- +// TTuple +// -------------------------------------------------------------------------- +export type TTupleIntoArray> = T extends TTuple ? Assert : never +export type TTupleStatic = { + [K in keyof T]: T[K] extends TSchema ? Static : T[K] +} export interface TTuple extends TSchema { [Kind]: 'Tuple' - static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } + static: TTupleStatic type: 'array' items?: T additionalItems?: false minItems: number maxItems: number } - // -------------------------------------------------------------------------- -// Undefined +// TUndefined // -------------------------------------------------------------------------- - export interface TUndefined extends TSchema { [Kind]: 'Undefined' static: undefined type: 'null' typeOf: 'Undefined' } - // -------------------------------------------------------------------------- -// Union +// TUnionOfLiteral +// -------------------------------------------------------------------------- +export type TUnionOfLiteralArray[]> = { [K in keyof T]: Assert['const'] }[number] +export type TUnionOfLiteral[]>> = TUnionOfLiteralArray +// -------------------------------------------------------------------------- +// TUnionResult - Used by Extract, Exclude and KeyOf for normalized union unwrap +// -------------------------------------------------------------------------- +export type TUnionResult = T extends [] ? TNever : T extends [infer S] ? S : TUnion +// -------------------------------------------------------------------------- +// TUnion // -------------------------------------------------------------------------- - export interface TUnion extends TSchema { [Kind]: 'Union' static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] anyOf: T } - -// ------------------------------------------------------------------------- -// Uint8Array -// ------------------------------------------------------------------------- - +// -------------------------------------------------------------------------- +// TUint8Array +// -------------------------------------------------------------------------- export interface Uint8ArrayOptions extends SchemaOptions { maxByteLength?: number minByteLength?: number } - export interface TUint8Array extends TSchema, Uint8ArrayOptions { [Kind]: 'Uint8Array' static: Uint8Array instanceOf: 'Uint8Array' type: 'object' } - // -------------------------------------------------------------------------- -// Unknown +// TUnknown // -------------------------------------------------------------------------- - export interface TUnknown extends TSchema { [Kind]: 'Unknown' static: unknown } - // -------------------------------------------------------------------------- -// Unsafe +// TUnsafe // -------------------------------------------------------------------------- - export interface UnsafeOptions extends SchemaOptions { [Kind]?: string } - export interface TUnsafe extends TSchema { [Kind]: string static: T } - // -------------------------------------------------------------------------- -// Void +// TVoid // -------------------------------------------------------------------------- - export interface TVoid extends TSchema { [Kind]: 'Void' static: void type: 'null' typeOf: 'Void' } - // -------------------------------------------------------------------------- // Static // -------------------------------------------------------------------------- - -/** Creates a static type from a TypeBox type */ +/** Creates a TypeScript static type from a TypeBox type */ export type Static = (T & { params: P })['static'] // -------------------------------------------------------------------------- -// TypeBuilder +// TypeRegistry // -------------------------------------------------------------------------- - -let TypeOrdinal = 0 - -export class TypeBuilder { - // ---------------------------------------------------------------------- - // Modifiers - // ---------------------------------------------------------------------- - - /** Creates a readonly optional property */ - public ReadonlyOptional(item: T): TReadonlyOptional { - return { [Modifier]: 'ReadonlyOptional', ...item } +export type TypeRegistryValidationFunction = (schema: TSchema, value: unknown) => boolean +/** A registry for user defined types */ +export namespace TypeRegistry { + const map = new Map>() + /** Returns the entries in this registry */ + export function Entries() { + return new Map(map) } - - /** Creates a readonly property */ - public Readonly(item: T): TReadonly { - return { [Modifier]: 'Readonly', ...item } + /** Clears all user defined types */ + export function Clear() { + return map.clear() } - - /** Creates a optional property */ - public Optional(item: T): TOptional { - return { [Modifier]: 'Optional', ...item } + /** Returns true if this registry contains this kind */ + export function Has(kind: string) { + return map.has(kind) } - - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - - /** `Standard` Creates a any type */ - public Any(options: SchemaOptions = {}): TAny { - return this.Create({ ...options, [Kind]: 'Any' }) + /** Sets a validation function for a user defined type */ + export function Set(kind: string, func: TypeRegistryValidationFunction) { + map.set(kind, func) } - - /** `Standard` Creates a array type */ - public Array(items: T, options: ArrayOptions = {}): TArray { - return this.Create({ ...options, [Kind]: 'Array', type: 'array', items }) + /** Gets a custom validation function for a user defined type */ + export function Get(kind: string) { + return map.get(kind) } - - /** `Standard` Creates a boolean type */ - public Boolean(options: SchemaOptions = {}): TBoolean { - return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) +} +// -------------------------------------------------------------------------- +// TypeRegistry +// -------------------------------------------------------------------------- +export type FormatRegistryValidationFunction = (value: string) => boolean +/** A registry for user defined string formats */ +export namespace FormatRegistry { + const map = new Map() + /** Returns the entries in this registry */ + export function Entries() { + return new Map(map) } - - /** `Extended` Creates a tuple type from this constructors parameters */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { - return this.Tuple([...schema.parameters], { ...options }) + /** Clears all user defined string formats */ + export function Clear() { + return map.clear() } - - /** `Extended` Creates a constructor type */ - public Constructor, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor, U> - - /** `Extended` Creates a constructor type */ - public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor - - /** `Extended` Creates a constructor type */ - public Constructor(parameters: any, returns: any, options: SchemaOptions = {}) { - if (parameters[Kind] === 'Tuple') { - const inner = parameters.items === undefined ? [] : parameters.items - return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: inner, returns }) - } else if (globalThis.Array.isArray(parameters)) { - return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters, returns }) - } else { - throw new Error('TypeBuilder.Constructor: Invalid parameters') - } + /** Returns true if the user defined string format exists */ + export function Has(format: string) { + return map.has(format) } - - /** `Extended` Creates a Date type */ - public Date(options: DateOptions = {}): TDate { - return this.Create({ ...options, [Kind]: 'Date', type: 'object', instanceOf: 'Date' }) + /** Sets a validation function for a user defined string format */ + export function Set(format: string, func: FormatRegistryValidationFunction) { + map.set(format, func) } - - /** `Standard` Creates a enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { - const values = Object.keys(item) - .filter((key) => isNaN(key as any)) - .map((key) => item[key]) as T[keyof T][] - const anyOf = values.map((value) => (typeof value === 'string' ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) - return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'Enum', anyOf }) + /** Gets a validation function for a user defined string format */ + export function Get(format: string) { + return map.get(format) } - - /** `Extended` Creates a function type */ - public Function, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction, U> - - /** `Extended` Creates a function type */ - public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction - - /** `Extended` Creates a function type */ - public Function(parameters: any, returns: any, options: SchemaOptions = {}) { - if (parameters[Kind] === 'Tuple') { - const inner = parameters.items === undefined ? [] : parameters.items - return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: inner, returns }) - } else if (globalThis.Array.isArray(parameters)) { - return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters, returns }) - } else { - throw new Error('TypeBuilder.Function: Invalid parameters') - } +} +// -------------------------------------------------------------------------- +// TypeGuard +// -------------------------------------------------------------------------- +export class TypeGuardUnknownTypeError extends Error { + constructor(public readonly schema: unknown) { + super('TypeGuard: Unknown type') } - - /** `Extended` Creates a type from this constructors instance type */ - public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { - return { ...options, ...this.Clone(schema.returns) } +} +/** Provides functions to test if JavaScript values are TypeBox types */ +export namespace TypeGuard { + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value) } - - /** `Standard` Creates a integer type */ - public Integer(options: NumericOptions = {}): TInteger { - return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) + function IsArray(value: unknown): value is any[] { + return typeof value === 'object' && value !== null && Array.isArray(value) } - - /** `Standard` Creates a intersect type. */ - public Intersect(objects: [...T], options: ObjectOptions = {}): TIntersect { - const isOptional = (schema: TSchema) => (schema[Modifier] && schema[Modifier] === 'Optional') || schema[Modifier] === 'ReadonlyOptional' - const [required, optional] = [new Set(), new Set()] - for (const object of objects) { - for (const [key, schema] of Object.entries(object.properties)) { - if (isOptional(schema)) optional.add(key) - } - } - for (const object of objects) { - for (const key of Object.keys(object.properties)) { - if (!optional.has(key)) required.add(key) - } + function IsPattern(value: unknown): value is string { + try { + new RegExp(value as string) + return true + } catch { + return false } - const properties = {} as Record - for (const object of objects) { - for (const [key, schema] of Object.entries(object.properties)) { - properties[key] = properties[key] === undefined ? schema : { [Kind]: 'Union', anyOf: [properties[key], { ...schema }] } + } + function IsControlCharacterFree(value: unknown): value is string { + if (typeof value !== 'string') return false + for (let i = 0; i < value.length; i++) { + const code = value.charCodeAt(i) + if ((code >= 7 && code <= 13) || code === 27 || code === 127) { + return false } } - if (required.size > 0) { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] }) - } else { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) - } + return true } - - /** `Standard` Creates a keyof type */ - public KeyOf(object: T, options: SchemaOptions = {}): TKeyOf { - const items = Object.keys(object.properties).map((key) => this.Create({ ...options, [Kind]: 'Literal', type: 'string', const: key })) - return this.Create({ ...options, [Kind]: 'Union', [Hint]: 'KeyOf', anyOf: items }) + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' } - - /** `Standard` Creates a literal type. */ - public Literal(value: T, options: SchemaOptions = {}): TLiteral { - return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) + function IsString(value: unknown): value is string { + return typeof value === 'string' } - - /** `Standard` Creates a never type */ - public Never(options: SchemaOptions = {}): TNever { - return this.Create({ - ...options, - [Kind]: 'Never', - allOf: [ - { type: 'boolean', const: false }, - { type: 'boolean', const: true }, - ], - }) + function IsNumber(value: unknown): value is number { + return typeof value === 'number' && globalThis.Number.isFinite(value) } - - /** `Standard` Creates a null type */ - public Null(options: SchemaOptions = {}): TNull { - return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' } - - /** `Standard` Creates a number type */ - public Number(options: NumericOptions = {}): TNumber { - return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) + function IsOptionalBigInt(value: unknown): value is bigint | undefined { + return value === undefined || (value !== undefined && IsBigInt(value)) } - - /** `Standard` Creates an object type */ - public Object(properties: T, options: ObjectOptions = {}): TObject { - const property_names = Object.keys(properties) - const optional = property_names.filter((name) => { - const property = properties[name] as TModifier - const modifier = property[Modifier] - return modifier && (modifier === 'Optional' || modifier === 'ReadonlyOptional') - }) - const required = property_names.filter((name) => !optional.includes(name)) - if (required.length > 0) { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required }) - } else { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) + function IsOptionalNumber(value: unknown): value is number | undefined { + return value === undefined || (value !== undefined && IsNumber(value)) + } + function IsOptionalBoolean(value: unknown): value is boolean | undefined { + return value === undefined || (value !== undefined && IsBoolean(value)) + } + function IsOptionalString(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value)) + } + function IsOptionalPattern(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) + } + function IsOptionalFormat(value: unknown): value is string | undefined { + return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) + } + function IsOptionalSchema(value: unknown): value is boolean | undefined { + return value === undefined || TSchema(value) + } + /** Returns true if the given schema is TAny */ + export function TAny(schema: unknown): schema is TAny { + return TKind(schema) && schema[Kind] === 'Any' && IsOptionalString(schema.$id) + } + /** Returns true if the given schema is TArray */ + export function TArray(schema: unknown): schema is TArray { + return ( + TKind(schema) && + schema[Kind] === 'Array' && + schema.type === 'array' && + IsOptionalString(schema.$id) && + TSchema(schema.items) && + IsOptionalNumber(schema.minItems) && + IsOptionalNumber(schema.maxItems) && + IsOptionalBoolean(schema.uniqueItems) + ) + } + /** Returns true if the given schema is TSymbol */ + export function TBigInt(schema: unknown): schema is TBigInt { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'BigInt' && + schema.type === 'null' && + schema.typeOf === 'BigInt' && + IsOptionalString(schema.$id) && + IsOptionalBigInt(schema.multipleOf) && + IsOptionalBigInt(schema.minimum) && + IsOptionalBigInt(schema.maximum) && + IsOptionalBigInt(schema.exclusiveMinimum) && + IsOptionalBigInt(schema.exclusiveMaximum) + ) + } + /** Returns true if the given schema is TBoolean */ + export function TBoolean(schema: unknown): schema is TBoolean { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Boolean' && + schema.type === 'boolean' && + IsOptionalString(schema.$id) + ) + } + /** Returns true if the given schema is TConstructor */ + export function TConstructor(schema: unknown): schema is TConstructor { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Constructor' && + schema.type === 'object' && + schema.instanceOf === 'Constructor' && + IsOptionalString(schema.$id) && + IsArray(schema.parameters) && + TSchema(schema.returns)) + ) { + return false + } + for (const parameter of schema.parameters) { + if (!TSchema(parameter)) return false } + return true + } + /** Returns true if the given schema is TDate */ + export function TDate(schema: unknown): schema is TDate { + return ( + TKind(schema) && + schema[Kind] === 'Date' && + schema.type === 'object' && + schema.instanceOf === 'Date' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minimumTimestamp) && + IsOptionalNumber(schema.maximumTimestamp) && + IsOptionalNumber(schema.exclusiveMinimumTimestamp) && + IsOptionalNumber(schema.exclusiveMaximumTimestamp) + ) + } + /** Returns true if the given schema is TFunction */ + export function TFunction(schema: unknown): schema is TFunction { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Function' && + schema.type === 'object' && + schema.instanceOf === 'Function' && + IsOptionalString(schema.$id) && + IsArray(schema.parameters) && + TSchema(schema.returns)) + ) { + return false + } + for (const parameter of schema.parameters) { + if (!TSchema(parameter)) return false + } + return true + } + /** Returns true if the given schema is TInteger */ + export function TInteger(schema: unknown): schema is TInteger { + return ( + TKind(schema) && + schema[Kind] === 'Integer' && + schema.type === 'integer' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.multipleOf) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMinimum) && + IsOptionalNumber(schema.exclusiveMaximum) + ) + } + /** Returns true if the given schema is TIntersect */ + export function TIntersect(schema: unknown): schema is TIntersect { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Intersect' && + IsArray(schema.allOf) && + IsOptionalString(schema.type) && + (IsOptionalBoolean(schema.unevaluatedProperties) || IsOptionalSchema(schema.unevaluatedProperties)) && + IsOptionalString(schema.$id)) + ) { + return false + } + if ('type' in schema && schema.type !== 'object') { + return false + } + for (const inner of schema.allOf) { + if (!TSchema(inner)) return false + } + return true + } + /** Returns true if the given schema is TKind */ + export function TKind(schema: unknown): schema is Record { + return IsObject(schema) && Kind in schema && typeof (schema as any)[Kind] === 'string' // TS 4.1.5: any required for symbol indexer + } + /** Returns true if the given schema is TLiteral */ + export function TLiteral(schema: unknown): schema is TLiteral { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Literal' && + IsOptionalString(schema.$id) && + ( + IsString(schema.const) || + IsNumber(schema.const) || + IsBoolean(schema.const) || + IsBigInt(schema.const) + ) + ) + } + /** Returns true if the given schema is TNever */ + export function TNever(schema: unknown): schema is TNever { + return TKind(schema) && schema[Kind] === 'Never' && IsObject(schema.not) && globalThis.Object.getOwnPropertyNames(schema.not).length === 0 + } + /** Returns true if the given schema is TNot */ + export function TNot(schema: unknown): schema is TNot { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Not' && + IsArray(schema.allOf) && + schema.allOf.length === 2 && + IsObject(schema.allOf[0]) && + TSchema(schema.allOf[0].not) && + TSchema(schema.allOf[1]) + ) + } + /** Returns true if the given schema is TNull */ + export function TNull(schema: unknown): schema is TNull { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Null' && + schema.type === 'null' && + IsOptionalString(schema.$id) + ) + } + /** Returns true if the given schema is TNumber */ + export function TNumber(schema: unknown): schema is TNumber { + return ( + TKind(schema) && + schema[Kind] === 'Number' && + schema.type === 'number' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.multipleOf) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMinimum) && + IsOptionalNumber(schema.exclusiveMaximum) + ) + } + /** Returns true if the given schema is TObject */ + export function TObject(schema: unknown): schema is TObject { + if ( + !( + TKind(schema) && + schema[Kind] === 'Object' && + schema.type === 'object' && + IsOptionalString(schema.$id) && + IsObject(schema.properties) && + (IsOptionalBoolean(schema.additionalProperties) || IsOptionalSchema(schema.additionalProperties)) && + IsOptionalNumber(schema.minProperties) && + IsOptionalNumber(schema.maxProperties) + ) + ) { + return false + } + for (const [key, value] of Object.entries(schema.properties)) { + if (!IsControlCharacterFree(key)) return false + if (!TSchema(value)) return false + } + return true + } + /** Returns true if the given schema is TPromise */ + export function TPromise(schema: unknown): schema is TPromise { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Promise' && + schema.type === 'object' && + schema.instanceOf === 'Promise' && + IsOptionalString(schema.$id) && + TSchema(schema.item) + ) + } + /** Returns true if the given schema is TRecord */ + export function TRecord(schema: unknown): schema is TRecord { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Record' && + schema.type === 'object' && + IsOptionalString(schema.$id) && + schema.additionalProperties === false && + IsObject(schema.patternProperties)) + ) { + return false + } + const keys = Object.keys(schema.patternProperties) + if (keys.length !== 1) { + return false + } + if (!IsPattern(keys[0])) { + return false + } + if (!TSchema(schema.patternProperties[keys[0]])) { + return false + } + return true + } + /** Returns true if the given schema is TSelf */ + export function TSelf(schema: unknown): schema is TSelf { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Self' && + IsOptionalString(schema.$id) && + IsString(schema.$ref) + ) + } + /** Returns true if the given schema is TRef */ + export function TRef(schema: unknown): schema is TRef { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Ref' && + IsOptionalString(schema.$id) && + IsString(schema.$ref) + ) + } + /** Returns true if the given schema is TString */ + export function TString(schema: unknown): schema is TString { + return ( + TKind(schema) && + schema[Kind] === 'String' && + schema.type === 'string' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minLength) && + IsOptionalNumber(schema.maxLength) && + IsOptionalPattern(schema.pattern) && + IsOptionalFormat(schema.format) + ) } - /** `Standard` Creates a new object type whose keys are omitted from the given source type */ - public Omit[]>>(schema: T, keys: K, options?: ObjectOptions): TOmit> - - /** `Standard` Creates a new object type whose keys are omitted from the given source type */ - public Omit[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TOmit + /** Returns true if the given schema is TSymbol */ + export function TSymbol(schema: unknown): schema is TSymbol { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Symbol' && + schema.type === 'null' && + schema.typeOf === 'Symbol' && + IsOptionalString(schema.$id) + ) + } - /** `Standard` Creates a new object type whose keys are omitted from the given source type */ - public Omit(schema: any, keys: any, options: ObjectOptions = {}) { - const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys - const next = { ...this.Clone(schema), ...options, [Hint]: 'Omit' } - if (next.required) { - next.required = next.required.filter((key: string) => !select.includes(key as any)) - if (next.required.length === 0) delete next.required + /** Returns true if the given schema is TTuple */ + export function TTuple(schema: unknown): schema is TTuple { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Tuple' && + schema.type === 'array' && + IsOptionalString(schema.$id) && + IsNumber(schema.minItems) && + IsNumber(schema.maxItems) && + schema.minItems === schema.maxItems) + ) { + return false + } + if (schema.items === undefined && schema.additionalItems === undefined && schema.minItems === 0) { + return true + } + if (!IsArray(schema.items)) { + return false } - for (const key of Object.keys(next.properties)) { - if (select.includes(key as any)) delete next.properties[key] + for (const inner of schema.items) { + if (!TSchema(inner)) return false } - return this.Create(next) + return true } - - /** `Extended` Creates a tuple type from this functions parameters */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { - return Type.Tuple(schema.parameters, { ...options }) + /** Returns true if the given schema is TUndefined */ + export function TUndefined(schema: unknown): schema is TUndefined { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Undefined' && + schema.type === 'null' && + schema.typeOf === 'Undefined' && + IsOptionalString(schema.$id) + ) + } + /** Returns true if the given schema is TUnion */ + export function TUnion(schema: unknown): schema is TUnion { + // prettier-ignore + if (!( + TKind(schema) && + schema[Kind] === 'Union' && + IsArray(schema.anyOf) && + IsOptionalString(schema.$id)) + ) { + return false + } + for (const inner of schema.anyOf) { + if (!TSchema(inner)) return false + } + return true + } + /** Returns true if the given schema is TUnion[]> */ + export function TUnionLiteral(schema: unknown): schema is TUnion[]> { + return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema) && typeof schema.const === 'string') } - /** `Standard` Creates an object type whose properties are all optional */ - public Partial(schema: T, options: ObjectOptions = {}): TPartial { - const next = { ...this.Clone(schema), ...options, [Hint]: 'Partial' } - delete next.required - for (const key of Object.keys(next.properties)) { - const property = next.properties[key] - const modifer = property[Modifier] - switch (modifer) { - case 'ReadonlyOptional': - property[Modifier] = 'ReadonlyOptional' - break - case 'Readonly': - property[Modifier] = 'ReadonlyOptional' - break - case 'Optional': - property[Modifier] = 'Optional' - break - default: - property[Modifier] = 'Optional' - break + /** Returns true if the given schema is TUint8Array */ + export function TUint8Array(schema: unknown): schema is TUint8Array { + return TKind(schema) && schema[Kind] === 'Uint8Array' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.instanceOf === 'Uint8Array' && IsOptionalNumber(schema.minByteLength) && IsOptionalNumber(schema.maxByteLength) + } + /** Returns true if the given schema is TUnknown */ + export function TUnknown(schema: unknown): schema is TUnknown { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Unknown' && + IsOptionalString(schema.$id) + ) + } + /** Returns true if the given schema is TVoid */ + export function TVoid(schema: unknown): schema is TVoid { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Void' && + schema.type === 'null' && + schema.typeOf === 'Void' && + IsOptionalString(schema.$id) + ) + } + /** Returns true if this schema has the ReadonlyOptional modifier */ + export function TReadonlyOptional(schema: T): schema is TReadonlyOptional { + return IsObject(schema) && schema[Modifier] === 'ReadonlyOptional' + } + /** Returns true if this schema has the Readonly modifier */ + export function TReadonly(schema: T): schema is TReadonly { + return IsObject(schema) && schema[Modifier] === 'Readonly' + } + /** Returns true if this schema has the Optional modifier */ + export function TOptional(schema: T): schema is TOptional { + return IsObject(schema) && schema[Modifier] === 'Optional' + } + /** Returns true if the given schema is TSchema */ + export function TSchema(schema: unknown): schema is TSchema { + return ( + typeof schema === 'object' && + (TAny(schema) || + TArray(schema) || + TBoolean(schema) || + TBigInt(schema) || + TConstructor(schema) || + TDate(schema) || + TFunction(schema) || + TInteger(schema) || + TIntersect(schema) || + TLiteral(schema) || + TNever(schema) || + TNot(schema) || + TNull(schema) || + TNumber(schema) || + TObject(schema) || + TPromise(schema) || + TRecord(schema) || + TSelf(schema) || + TRef(schema) || + TString(schema) || + TSymbol(schema) || + TTuple(schema) || + TUndefined(schema) || + TUnion(schema) || + TUint8Array(schema) || + TUnknown(schema) || + TVoid(schema) || + (TKind(schema) && TypeRegistry.Has(schema[Kind] as any))) + ) + } +} +// -------------------------------------------------------------------------- +// ExtendsUndefined +// -------------------------------------------------------------------------- +/** Fast undefined check used for properties of type undefined */ +export namespace ExtendsUndefined { + export function Check(schema: TSchema): boolean { + if (schema[Kind] === 'Undefined') return true + if (schema[Kind] === 'Union') { + const union = schema as TUnion + return union.anyOf.some((schema) => Check(schema)) + } + return false + } +} +// -------------------------------------------------------------------------- +// TypeExtends +// -------------------------------------------------------------------------- +export enum TypeExtendsResult { + Union, + True, + False, +} +export namespace TypeExtends { + // -------------------------------------------------------------------------- + // IntoBooleanResult + // -------------------------------------------------------------------------- + function IntoBooleanResult(result: TypeExtendsResult) { + return result === TypeExtendsResult.False ? TypeExtendsResult.False : TypeExtendsResult.True + } + // -------------------------------------------------------------------------- + // Any + // -------------------------------------------------------------------------- + function AnyRight(left: TSchema, right: TAny) { + return TypeExtendsResult.True + } + function Any(left: TAny, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) return TypeExtendsResult.True + if (TypeGuard.TUnion(right)) return TypeExtendsResult.Union + if (TypeGuard.TUnknown(right)) return TypeExtendsResult.True + if (TypeGuard.TAny(right)) return TypeExtendsResult.True + return TypeExtendsResult.Union + } + // -------------------------------------------------------------------------- + // Array + // -------------------------------------------------------------------------- + function ArrayRight(left: TSchema, right: TArray) { + if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False + if (TypeGuard.TAny(left)) return TypeExtendsResult.Union + if (TypeGuard.TNever(left)) return TypeExtendsResult.True + return TypeExtendsResult.False + } + function Array(left: TArray, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True + if (!TypeGuard.TArray(right)) return TypeExtendsResult.False + return IntoBooleanResult(Visit(left.items, right.items)) + } + // -------------------------------------------------------------------------- + // BigInt + // -------------------------------------------------------------------------- + function BigInt(left: TBigInt, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TBigInt(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Boolean + // -------------------------------------------------------------------------- + function BooleanRight(left: TSchema, right: TBoolean) { + if (TypeGuard.TLiteral(left) && typeof left.const === 'boolean') return TypeExtendsResult.True + return TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Boolean(left: TBoolean, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TBoolean(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Constructor + // -------------------------------------------------------------------------- + function Constructor(left: TConstructor, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (!TypeGuard.TConstructor(right)) return TypeExtendsResult.False + if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False + if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { + return TypeExtendsResult.False + } + return IntoBooleanResult(Visit(left.returns, right.returns)) + } + // -------------------------------------------------------------------------- + // Date + // -------------------------------------------------------------------------- + function Date(left: TDate, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TDate(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Function + // -------------------------------------------------------------------------- + function Function(left: TFunction, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (!TypeGuard.TFunction(right)) return TypeExtendsResult.False + if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False + if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { + return TypeExtendsResult.False + } + return IntoBooleanResult(Visit(left.returns, right.returns)) + } + // -------------------------------------------------------------------------- + // Integer + // -------------------------------------------------------------------------- + function IntegerRight(left: TSchema, right: TInteger) { + if (TypeGuard.TLiteral(left) && typeof left.const === 'number') return TypeExtendsResult.True + return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Integer(left: TInteger, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Intersect + // -------------------------------------------------------------------------- + function IntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { + return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Intersect(left: TIntersect, right: TSchema) { + return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Literal + // -------------------------------------------------------------------------- + function IsLiteralString(schema: TLiteral) { + return typeof schema.const === 'string' + } + function IsLiteralNumber(schema: TLiteral) { + return typeof schema.const === 'number' + } + function IsLiteralBoolean(schema: TLiteral) { + return typeof schema.const === 'boolean' + } + function Literal(left: TLiteral, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + if (TypeGuard.TString(right)) return StringRight(left, right) + if (TypeGuard.TNumber(right)) return NumberRight(left, right) + if (TypeGuard.TInteger(right)) return IntegerRight(left, right) + if (TypeGuard.TBoolean(right)) return BooleanRight(left, right) + return TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Never + // -------------------------------------------------------------------------- + function NeverRight(left: TSchema, right: TNever) { + return TypeExtendsResult.False + } + function Never(left: TNever, right: TSchema) { + return TypeExtendsResult.True + } + // -------------------------------------------------------------------------- + // Null + // -------------------------------------------------------------------------- + function Null(left: TNull, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TNull(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Number + // -------------------------------------------------------------------------- + function NumberRight(left: TSchema, right: TNumber) { + if (TypeGuard.TLiteral(left) && IsLiteralNumber(left)) return TypeExtendsResult.True + return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Number(left: TNumber, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Object + // -------------------------------------------------------------------------- + function IsObjectPropertyCount(schema: TObject, count: number) { + return globalThis.Object.keys(schema.properties).length === count + } + function IsObjectStringLike(schema: TObject) { + return IsObjectArrayLike(schema) + } + function IsObjectSymbolLike(schema: TObject) { + // prettier-ignore + return IsObjectPropertyCount(schema, 0) || ( + IsObjectPropertyCount(schema, 1) && 'description' in schema.properties && TypeGuard.TUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && (( + TypeGuard.TString(schema.properties.description.anyOf[0]) && + TypeGuard.TUndefined(schema.properties.description.anyOf[1]) + ) || ( + TypeGuard.TString(schema.properties.description.anyOf[1]) && + TypeGuard.TUndefined(schema.properties.description.anyOf[0]) + )) + ) + } + function IsObjectNumberLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) + } + function IsObjectBooleanLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) + } + function IsObjectBigIntLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) + } + function IsObjectDateLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) + } + function IsObjectUint8ArrayLike(schema: TObject) { + return IsObjectArrayLike(schema) + } + function IsObjectFunctionLike(schema: TObject) { + const length = Type.Number() + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) + } + function IsObjectConstructorLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) + } + function IsObjectArrayLike(schema: TObject) { + const length = Type.Number() + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) + } + function IsObjectPromiseLike(schema: TObject) { + const then = Type.Function([Type.Any()], Type.Any()) + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'then' in schema.properties && IntoBooleanResult(Visit(schema.properties['then'], then)) === TypeExtendsResult.True) + } + // -------------------------------------------------------------------------- + // Property + // -------------------------------------------------------------------------- + function Property(left: TSchema, right: TSchema) { + if (Visit(left, right) === TypeExtendsResult.False) return TypeExtendsResult.False + if (TypeGuard.TOptional(left) && !TypeGuard.TOptional(right)) return TypeExtendsResult.False + return TypeExtendsResult.True + } + function ObjectRight(left: TSchema, right: TObject) { + if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False + if (TypeGuard.TAny(left)) return TypeExtendsResult.Union + if (TypeGuard.TNever(left)) return TypeExtendsResult.True + if (TypeGuard.TLiteral(left) && IsLiteralString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True + if (TypeGuard.TLiteral(left) && IsLiteralNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True + if (TypeGuard.TLiteral(left) && IsLiteralBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True + if (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) return TypeExtendsResult.True + if (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) return TypeExtendsResult.True + if (TypeGuard.TString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True + if (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) return TypeExtendsResult.True + if (TypeGuard.TNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True + if (TypeGuard.TInteger(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True + if (TypeGuard.TBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True + if (TypeGuard.TUint8Array(left) && IsObjectUint8ArrayLike(right)) return TypeExtendsResult.True + if (TypeGuard.TDate(left) && IsObjectDateLike(right)) return TypeExtendsResult.True + if (TypeGuard.TConstructor(left) && IsObjectConstructorLike(right)) return TypeExtendsResult.True + if (TypeGuard.TFunction(left) && IsObjectFunctionLike(right)) return TypeExtendsResult.True + if (TypeGuard.TRecord(left) && TypeGuard.TString(RecordKey(left))) { + // When expressing a Record with literal key values, the Record is converted into a Object with + // the Hint assigned as `Record`. This is used to invert the extends logic. + return right[Hint] === 'Record' ? TypeExtendsResult.True : TypeExtendsResult.False + } + if (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) { + return IsObjectPropertyCount(right, 0) ? TypeExtendsResult.True : TypeExtendsResult.False + } + return TypeExtendsResult.False + } + function Object(left: TObject, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + if (!TypeGuard.TObject(right)) return TypeExtendsResult.False + for (const key of globalThis.Object.keys(right.properties)) { + if (!(key in left.properties)) return TypeExtendsResult.False + if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { + return TypeExtendsResult.False } } - return this.Create(next) + return TypeExtendsResult.True } - - /** `Standard` Creates a new object type whose keys are picked from the given source type */ - public Pick[]>>(schema: T, keys: K, options?: ObjectOptions): TPick> - - /** `Standard` Creates a new object type whose keys are picked from the given source type */ - public Pick[]>(schema: T, keys: readonly [...K], options?: ObjectOptions): TPick - - /** `Standard` Creates a new object type whose keys are picked from the given source type */ - public Pick[]>(schema: any, keys: any, options: ObjectOptions = {}) { - const select: readonly string[] = keys[Kind] === 'Union' ? keys.anyOf.map((schema: TLiteral) => schema.const) : keys - const next = { ...this.Clone(schema), ...options, [Hint]: 'Pick' } - if (next.required) { - next.required = next.required.filter((key: any) => select.includes(key)) - if (next.required.length === 0) delete next.required + // -------------------------------------------------------------------------- + // Promise + // -------------------------------------------------------------------------- + function Promise(left: TPromise, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right) && IsObjectPromiseLike(right)) return TypeExtendsResult.True + if (!TypeGuard.TPromise(right)) return TypeExtendsResult.False + return IntoBooleanResult(Visit(left.item, right.item)) + } + // -------------------------------------------------------------------------- + // Record + // -------------------------------------------------------------------------- + function RecordKey(schema: TRecord) { + if ('^(0|[1-9][0-9]*)$' in schema.patternProperties) return Type.Number() + if ('^.*$' in schema.patternProperties) return Type.String() + throw Error('TypeExtends: Cannot get record key') + } + function RecordValue(schema: TRecord) { + if ('^(0|[1-9][0-9]*)$' in schema.patternProperties) return schema.patternProperties['^(0|[1-9][0-9]*)$'] + if ('^.*$' in schema.patternProperties) return schema.patternProperties['^.*$'] + throw Error('TypeExtends: Cannot get record value') + } + function RecordRight(left: TSchema, right: TRecord) { + const Key = RecordKey(right) + const Value = RecordValue(right) + if (TypeGuard.TLiteral(left) && IsLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) return TypeExtendsResult.True + if (TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) + if (TypeGuard.TString(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) + if (TypeGuard.TArray(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) + if (TypeGuard.TObject(left)) { + for (const key of globalThis.Object.keys(left.properties)) { + if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { + return TypeExtendsResult.False + } + } + return TypeExtendsResult.True } - for (const key of Object.keys(next.properties)) { - if (!select.includes(key as any)) delete next.properties[key] + return TypeExtendsResult.False + } + function Record(left: TRecord, right: TSchema) { + const Value = RecordValue(left) + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (!TypeGuard.TRecord(right)) return TypeExtendsResult.False + return Visit(Value, RecordValue(right)) + } + // -------------------------------------------------------------------------- + // String + // -------------------------------------------------------------------------- + function StringRight(left: TSchema, right: TString) { + if (TypeGuard.TLiteral(left) && typeof left.const === 'string') return TypeExtendsResult.True + return TypeGuard.TString(left) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function String(left: TString, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TString(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Symbol + // -------------------------------------------------------------------------- + function Symbol(left: TSymbol, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TSymbol(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Tuple + // -------------------------------------------------------------------------- + function TupleRight(left: TSchema, right: TTuple) { + if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False + if (TypeGuard.TAny(left)) return TypeExtendsResult.Union + if (TypeGuard.TNever(left)) return TypeExtendsResult.True + return TypeExtendsResult.False + } + function IsArrayOfTuple(left: TTuple, right: TSchema) { + return TypeGuard.TArray(right) && left.items !== undefined && left.items.every((schema) => Visit(schema, right.items) === TypeExtendsResult.True) + } + function Tuple(left: TTuple, right: TSchema): TypeExtendsResult { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True + if (TypeGuard.TArray(right) && IsArrayOfTuple(left, right)) return TypeExtendsResult.True + if (!TypeGuard.TTuple(right)) return TypeExtendsResult.False + if ((left.items === undefined && right.items !== undefined) || (left.items !== undefined && right.items === undefined)) return TypeExtendsResult.False + if (left.items === undefined && right.items === undefined) return TypeExtendsResult.True + return left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Uint8Array + // -------------------------------------------------------------------------- + function Uint8Array(left: TUint8Array, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + return TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Undefined + // -------------------------------------------------------------------------- + function Undefined(left: TUndefined, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TNever(right)) return NeverRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (TypeGuard.TRecord(right)) return RecordRight(left, right) + if (TypeGuard.TVoid(right)) return VoidRight(left, right) + return TypeGuard.TUndefined(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Union + // -------------------------------------------------------------------------- + function UnionRight(left: TSchema, right: TUnion): TypeExtendsResult { + return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Union(left: TUnion, right: TSchema) { + return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Unknown + // -------------------------------------------------------------------------- + function UnknownRight(left: TSchema, right: TUnknown) { + return TypeExtendsResult.True + } + function Unknown(left: TUnknown, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TString(right)) return StringRight(left, right) + if (TypeGuard.TNumber(right)) return NumberRight(left, right) + if (TypeGuard.TInteger(right)) return IntegerRight(left, right) + if (TypeGuard.TBoolean(right)) return BooleanRight(left, right) + if (TypeGuard.TArray(right)) return ArrayRight(left, right) + if (TypeGuard.TTuple(right)) return TupleRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + return TypeGuard.TUnknown(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + // -------------------------------------------------------------------------- + // Void + // -------------------------------------------------------------------------- + function VoidRight(left: TSchema, right: TVoid) { + if (TypeGuard.TUndefined(left)) return TypeExtendsResult.True + return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Void(left: TVoid, right: TSchema) { + if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + if (TypeGuard.TUnion(right)) return UnionRight(left, right) + if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) + if (TypeGuard.TAny(right)) return AnyRight(left, right) + if (TypeGuard.TObject(right)) return ObjectRight(left, right) + return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False + } + function Visit(left: TSchema, right: TSchema): TypeExtendsResult { + if (TypeGuard.TAny(left)) return Any(left, right) + if (TypeGuard.TArray(left)) return Array(left, right) + if (TypeGuard.TBigInt(left)) return BigInt(left, right) + if (TypeGuard.TBoolean(left)) return Boolean(left, right) + if (TypeGuard.TConstructor(left)) return Constructor(left, right) + if (TypeGuard.TDate(left)) return Date(left, right) + if (TypeGuard.TFunction(left)) return Function(left, right) + if (TypeGuard.TInteger(left)) return Integer(left, right) + if (TypeGuard.TIntersect(left)) return Intersect(left, right) + if (TypeGuard.TLiteral(left)) return Literal(left, right) + if (TypeGuard.TNever(left)) return Never(left, right) + if (TypeGuard.TNull(left)) return Null(left, right) + if (TypeGuard.TNumber(left)) return Number(left, right) + if (TypeGuard.TRecord(left)) return Record(left, right) + if (TypeGuard.TString(left)) return String(left, right) + if (TypeGuard.TSymbol(left)) return Symbol(left, right) + if (TypeGuard.TObject(left)) return Object(left, right) + if (TypeGuard.TTuple(left)) return Tuple(left, right) + if (TypeGuard.TPromise(left)) return Promise(left, right) + if (TypeGuard.TUint8Array(left)) return Uint8Array(left, right) + if (TypeGuard.TUndefined(left)) return Undefined(left, right) + if (TypeGuard.TUnion(left)) return Union(left, right) + if (TypeGuard.TUnknown(left)) return Unknown(left, right) + if (TypeGuard.TVoid(left)) return Void(left, right) + throw Error(`TypeExtends: Unknown left type operand '${left[Kind]}'`) + } + export function Extends(left: TSchema, right: TSchema): TypeExtendsResult { + return Visit(left, right) + } +} +// -------------------------------------------------------------------------- +// TypeClone +// -------------------------------------------------------------------------- +/** Specialized Clone for Types */ +export namespace TypeClone { + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null + } + function IsArray(value: unknown): value is unknown[] { + return globalThis.Array.isArray(value) + } + function Array(value: unknown[]) { + return (value as any).map((value: unknown) => Visit(value as any)) + } + function Object(value: Record) { + const clonedProperties = globalThis.Object.getOwnPropertyNames(value).reduce((acc, key) => { + return { ...acc, [key]: Visit(value[key]) } + }, {}) + const clonedSymbols = globalThis.Object.getOwnPropertySymbols(value).reduce((acc, key) => { + return { ...acc, [key]: Visit(value[key as any]) } + }, {}) + return { ...clonedProperties, ...clonedSymbols } + } + function Visit(value: unknown): any { + if (IsArray(value)) return Array(value) + if (IsObject(value)) return Object(value) + return value + } + /** Clones a type. This function will omit non-self referential identifiers on the cloned type. */ + export function Clone(schema: T, options: SchemaOptions): T { + return { ...Visit(schema), ...options } + } +} +// -------------------------------------------------------------------------- +// ObjectMap +// -------------------------------------------------------------------------- +export namespace ObjectMap { + function Intersect(schema: TIntersect, callback: (object: TObject) => TObject) { + return Type.Intersect( + schema.allOf.map((inner) => Visit(inner, callback)), + { ...schema }, + ) + } + function Union(schema: TUnion, callback: (object: TObject) => TObject) { + return Type.Union( + schema.anyOf.map((inner) => Visit(inner, callback)), + { ...schema }, + ) + } + function Object(schema: TObject, callback: (object: TObject) => TObject) { + return callback(schema) + } + function Visit(schema: TSchema, callback: (object: TObject) => TObject): TSchema { + if (TypeGuard.TIntersect(schema)) return Intersect(schema, callback) + if (TypeGuard.TUnion(schema)) return Union(schema, callback) + if (TypeGuard.TObject(schema)) return Object(schema, callback) + return schema + } + export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { + return { ...Visit(TypeClone.Clone(schema, {}), callback), ...options } as unknown as T + } +} +// -------------------------------------------------------------------------- +// KeyResolver +// -------------------------------------------------------------------------- +export namespace KeyResolver { + function IsKeyable(schema: TSchema) { + return TypeGuard.TIntersect(schema) || TypeGuard.TUnion(schema) || (TypeGuard.TObject(schema) && globalThis.Object.getOwnPropertyNames(schema.properties).length > 0) + } + function Intersect(schema: TIntersect) { + return [...schema.allOf.filter((schema) => IsKeyable(schema)).reduce((set, schema) => Visit(schema).map((key) => set.add(key))[0], new Set())] + } + function Union(schema: TUnion) { + const sets = schema.anyOf.filter((schema) => IsKeyable(schema)).map((inner) => Visit(inner)) + return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] + } + function Object(schema: TObject) { + return globalThis.Object.keys(schema.properties) + } + function Visit(schema: TSchema): string[] { + if (TypeGuard.TIntersect(schema)) return Intersect(schema) + if (TypeGuard.TUnion(schema)) return Union(schema) + if (TypeGuard.TObject(schema)) return Object(schema) + return [] + } + export function Resolve(schema: T) { + return Visit(schema) + } +} +// -------------------------------------------------------------------------- +// TypeOrdinal: Used for auto $id generation +// -------------------------------------------------------------------------- +let TypeOrdinal = 0 +// -------------------------------------------------------------------------- +// TypeBuilder +// -------------------------------------------------------------------------- +export class TypeBuilder { + /** `[Utility]` Creates a schema without `static` and `params` types */ + protected Create(schema: Omit): T { + return schema as any + } + /** `[Standard]` Omits compositing symbols from this schema */ + public Strict(schema: T): T { + return JSON.parse(JSON.stringify(schema)) + } +} +// -------------------------------------------------------------------------- +// StandardTypeBuilder +// -------------------------------------------------------------------------- +export class StandardTypeBuilder extends TypeBuilder { + // ------------------------------------------------------------------------ + // Modifiers + // ------------------------------------------------------------------------ + /** `[Modifier]` Creates a Optional property */ + public Optional(schema: T): TOptional { + return { [Modifier]: 'Optional', ...TypeClone.Clone(schema, {}) } + } + /** `[Modifier]` Creates a ReadonlyOptional property */ + public ReadonlyOptional(schema: T): TReadonlyOptional { + return { [Modifier]: 'ReadonlyOptional', ...TypeClone.Clone(schema, {}) } + } + /** `[Modifier]` Creates a Readonly object or property */ + public Readonly(schema: T): TReadonly { + return { [Modifier]: 'Readonly', ...schema } + } + // ------------------------------------------------------------------------ + // Types + // ------------------------------------------------------------------------ + /** `[Standard]` Creates an Any type */ + public Any(options: SchemaOptions = {}): TAny { + return this.Create({ ...options, [Kind]: 'Any' }) + } + /** `[Standard]` Creates an Array type */ + public Array(items: T, options: ArrayOptions = {}): TArray { + return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Clone(items, {}) }) + } + /** `[Standard]` Creates a Boolean type */ + public Boolean(options: SchemaOptions = {}): TBoolean { + return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) + } + /** `[Standard]` Creates a Composite object type that will union any overlapping properties of the given object array */ + public Composite(schemas: [...T], options?: ObjectOptions): TComposite { + const properties = {} as TProperties + for (const object of schemas) { + for (const [key, property] of globalThis.Object.entries(object.properties)) { + properties[key] = key in properties ? this.Union([properties[key], property]) : TypeClone.Clone(property, {}) + } } - return this.Create(next) + return this.Object(properties, options) as TComposite } - - /** `Extended` Creates a Promise type */ - public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'object', instanceOf: 'Promise', item }) + /** `[Standard]` Creates a Enum type */ + public Enum>(item: T, options: SchemaOptions = {}): TEnum { + // prettier-ignore + const values = globalThis.Object.keys(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] + const anyOf = values.map((value) => (typeof value === 'string' ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) + return this.Create({ ...options, [Kind]: 'Union', anyOf }) + } + /** `[Standard]` A conditional type expression that will return the true type if the left type extends the right */ + public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { + switch (TypeExtends.Extends(left, right)) { + case TypeExtendsResult.Union: + return this.Union([TypeClone.Clone(trueType, options), TypeClone.Clone(falseType, options)]) as any as TExtends + case TypeExtendsResult.True: + return TypeClone.Clone(trueType, options) as TExtends + case TypeExtendsResult.False: + return TypeClone.Clone(falseType, options) as TExtends + } + } + /** `[Standard]` Excludes from the left type any type that is not assignable to the right */ + public Exclude(left: L, right: R, options: SchemaOptions = {}): TExclude { + if (TypeGuard.TUnion(left)) { + const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) === TypeExtendsResult.False) + return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExclude + } else { + return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? this.Never(options) : TypeClone.Clone(left, options)) as any + } + } + /** `[Standard]` Extracts from left left any type that is assignable to the right */ + public Extract(left: L, right: R, options: SchemaOptions = {}): TExtract { + if (TypeGuard.TUnion(left)) { + const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) !== TypeExtendsResult.False) + return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExtract + } else { + return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? TypeClone.Clone(left, options) : this.Never(options)) as any + } + } + /** `[Standard]` Creates an Integer type */ + public Integer(options: NumericOptions = {}): TInteger { + return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) + } + /** `[Standard]` Creates a Intersect type */ + public Intersect(allOf: [], options?: SchemaOptions): TNever + /** `[Standard]` Creates a Intersect type */ + public Intersect(allOf: [...T], options?: SchemaOptions): T[0] + // /** `[Standard]` Creates a Intersect type */ + public Intersect(allOf: [...T], options?: IntersectOptions): TIntersect + public Intersect(allOf: TSchema[], options: IntersectOptions = {}) { + if (allOf.length === 0) return Type.Never() + if (allOf.length === 1) return TypeClone.Clone(allOf[0], options) + const objects = allOf.every((schema) => TypeGuard.TObject(schema)) + const cloned = allOf.map((schema) => TypeClone.Clone(schema, {})) + const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) ? { unevaluatedProperties: TypeClone.Clone(options.unevaluatedProperties, {}) } : {} + if (options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects) { + return this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) + } else { + return this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: cloned }) + } + } + /** `[Standard]` Creates a KeyOf type */ + public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { + const keys = KeyResolver.Resolve(schema) + // prettier-ignore + const keyof = keys.length === 0 ? this.Never(options) : this.Union(keys.map((key) => this.Literal(key)), options) + return keyof as TKeyOf + } + /** `[Standard]` Creates a Literal type */ + public Literal(value: T, options: SchemaOptions = {}): TLiteral { + return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) + } + /** `[Standard]` Creates a Never type */ + public Never(options: SchemaOptions = {}): TNever { + return this.Create({ ...options, [Kind]: 'Never', not: {} }) + } + /** `[Standard]` Creates a Not type. The first argument is the disallowed type, the second is the allowed. */ + public Not(not: N, schema: T, options?: SchemaOptions): TNot { + return this.Create({ ...options, [Kind]: 'Not', allOf: [{ not: TypeClone.Clone(not, {}) }, TypeClone.Clone(schema, {})] }) + } + /** `[Standard]` Creates a Null type */ + public Null(options: SchemaOptions = {}): TNull { + return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) + } + /** `[Standard]` Creates a Number type */ + public Number(options: NumericOptions = {}): TNumber { + return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) + } + /** `[Standard]` Creates an Object type */ + public Object(properties: T, options: ObjectOptions = {}): TObject { + const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) + const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key]) || TypeGuard.TReadonlyOptional(properties[key])) + const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) + const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Clone(options.additionalProperties, {}) } : {} + const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(properties[key], {}) }), {} as TProperties) + if (requiredKeys.length > 0) { + return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) + } else { + return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) + } + } + /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit + /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit> + /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit + /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + public Omit(schema: T, key: K, options?: SchemaOptions): TOmit + public Omit(schema: TSchema, unresolved: unknown, options: SchemaOptions = {}): any { + // prettier-ignore + const keys = + TypeGuard.TUnionLiteral(unresolved) ? unresolved.anyOf.map((schema) => schema.const) : + TypeGuard.TLiteral(unresolved) ? [unresolved.const] : + TypeGuard.TNever(unresolved) ? [] : + (unresolved as string[]) + // prettier-ignore + return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { + if (schema.required) { + schema.required = schema.required.filter((key: string) => !keys.includes(key as any)) + if (schema.required.length === 0) delete schema.required + } + for (const key of globalThis.Object.keys(schema.properties)) { + if (keys.includes(key as any)) delete schema.properties[key] + } + return this.Create(schema) + }, options) } - /** `Standard` Creates an object whose properties are derived from the given string literal union. */ - public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> - - /** `Standard` Creates a record type */ + /** `[Standard]` Creates a mapped type where all properties are Optional */ + public Partial(schema: T, options: ObjectOptions = {}): TPartial { + function Apply(schema: TSchema) { + // prettier-ignore + switch (schema[Modifier]) { + case 'ReadonlyOptional': schema[Modifier] = 'ReadonlyOptional'; break; + case 'Readonly': schema[Modifier] = 'ReadonlyOptional'; break; + case 'Optional': schema[Modifier] = 'Optional'; break; + default: schema[Modifier] = 'Optional'; break; + } + } + // prettier-ignore + return ObjectMap.Map>(TypeClone.Clone(schema, {}), (schema) => { + delete schema.required + globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) + return schema + }, options) + } + /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick + /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick> + /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + public Pick>(schema: T, key: K, options?: SchemaOptions): TPick + /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + public Pick(schema: T, key: K, options?: SchemaOptions): TPick + public Pick(schema: TSchema, unresolved: unknown, options: SchemaOptions = {}): any { + // prettier-ignore + const keys = + TypeGuard.TUnionLiteral(unresolved) ? unresolved.anyOf.map((schema) => schema.const) : + TypeGuard.TLiteral(unresolved) ? [unresolved.const] : + TypeGuard.TNever(unresolved) ? [] : + (unresolved as string[]) + // prettier-ignore + return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { + if (schema.required) { + schema.required = schema.required.filter((key: any) => keys.includes(key)) + if (schema.required.length === 0) delete schema.required + } + for (const key of globalThis.Object.keys(schema.properties)) { + if (!keys.includes(key as any)) delete schema.properties[key] + } + return this.Create(schema) + }, options) + } + /** `[Standard]` Creates an Object type from the given Literal Union */ + public Record[]>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> + /** `[Standard]` Creates an Object type from the given Literal Union */ + public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> + /** `[Standard]` Creates a Record type */ public Record(key: K, schema: T, options?: ObjectOptions): TRecord - - /** `Standard` Creates a record type */ - public Record(key: any, value: any, options: ObjectOptions = {}) { - // If string literal union return TObject with properties extracted from union. - if (key[Kind] === 'Union') { - return this.Object( - key.anyOf.reduce((acc: any, literal: any) => { - return { ...acc, [literal.const]: value } - }, {}), - { ...options, [Hint]: 'Record' }, - ) + public Record(key: any, schema: TSchema, options: ObjectOptions = {}) { + if (TypeGuard.TLiteral(key)) { + if (typeof key.const === 'string' || typeof key.const === 'number') { + return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options) + } else throw Error('TypeBuilder: Record key can only be derived from literals of number or string') + } + if (TypeGuard.TUnion(key)) { + if (key.anyOf.every((schema) => TypeGuard.TLiteral(schema) && (typeof schema.const === 'string' || typeof schema.const === 'number'))) { + const properties = key.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) + return this.Object(properties, { ...options, [Hint]: 'Record' }) + } else throw Error('TypeBuilder: Record key can only be derived from union literal of number or string') } - // otherwise return TRecord with patternProperties const pattern = ['Integer', 'Number'].includes(key[Kind]) ? '^(0|[1-9][0-9]*)$' : key[Kind] === 'String' && key.pattern ? key.pattern : '^.*$' return this.Create({ ...options, [Kind]: 'Record', type: 'object', - patternProperties: { [pattern]: value }, + patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false, }) } - - /** `Standard` Creates recursive type */ + /** `[Standard]` Creates a Recursive type */ public Recursive(callback: (self: TSelf) => T, options: SchemaOptions = {}): TRecursive { - if (options.$id === undefined) options.$id = `T${TypeOrdinal++}` + if (options.$id === undefined) (options as any).$id = `T${TypeOrdinal++}` const self = callback({ [Kind]: 'Self', $ref: `${options.$id}` } as any) self.$id = options.$id return this.Create({ ...options, ...self } as any) } - - /** `Standard` Creates a reference type. The referenced type must contain a $id. */ + /** `[Standard]` Creates a Ref type. The referenced type must contain a $id */ public Ref(schema: T, options: SchemaOptions = {}): TRef { - if (schema.$id === undefined) throw Error('TypeBuilder.Ref: Referenced schema must specify an $id') + if (schema.$id === undefined) throw Error('StandardTypeBuilder.Ref: Target type must specify an $id') return this.Create({ ...options, [Kind]: 'Ref', $ref: schema.$id! }) } - - /** `Standard` Creates a string type from a regular expression */ - public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { - return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern: regex.source }) - } - - /** `Standard` Creates an object type whose properties are all required */ - public Required(schema: T, options: SchemaOptions = {}): TRequired { - const next = { ...this.Clone(schema), ...options, [Hint]: 'Required' } - next.required = Object.keys(next.properties) - for (const key of Object.keys(next.properties)) { - const property = next.properties[key] - const modifier = property[Modifier] - switch (modifier) { - case 'ReadonlyOptional': - property[Modifier] = 'Readonly' - break - case 'Readonly': - property[Modifier] = 'Readonly' - break - case 'Optional': - delete property[Modifier] - break - default: - delete property[Modifier] - break + /** `[Standard]` Creates a mapped type where all properties are Required */ + public Required(schema: T, options: SchemaOptions = {}): TRequired { + function Apply(schema: TSchema) { + // prettier-ignore + switch (schema[Modifier]) { + case 'ReadonlyOptional': schema[Modifier] = 'Readonly'; break + case 'Readonly': schema[Modifier] = 'Readonly'; break + case 'Optional': delete schema[Modifier]; break + default: delete schema[Modifier]; break } } - return this.Create(next) - } - - /** `Extended` Creates a type from this functions return type */ - public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { - return { ...options, ...this.Clone(schema.returns) } - } - - /** Removes Kind and Modifier symbol property keys from this schema */ - public Strict(schema: T): T { - return JSON.parse(JSON.stringify(schema)) + // prettier-ignore + return ObjectMap.Map>(TypeClone.Clone(schema, {}), (schema) => { + schema.required = globalThis.Object.keys(schema.properties) + globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) + return schema + }, options) } - - /** `Standard` Creates a string type */ + /** `[Standard]` Creates a String type */ public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } - - /** `Standard` Creates a tuple type */ + /** `[Standard]` Creates a Tuple type */ public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { - const additionalItems = false - const minItems = items.length - const maxItems = items.length - const schema = (items.length > 0 ? { ...options, [Kind]: 'Tuple', type: 'array', items, additionalItems, minItems, maxItems } : { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any + const [additionalItems, minItems, maxItems] = [false, items.length, items.length] + const clonedItems = items.map((item) => TypeClone.Clone(item, {})) + // prettier-ignore + const schema = (items.length > 0 ? + { ...options, [Kind]: 'Tuple', type: 'array', items: clonedItems, additionalItems, minItems, maxItems } : + { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any return this.Create(schema) } - - /** `Extended` Creates a undefined type */ - public Undefined(options: SchemaOptions = {}): TUndefined { - return this.Create({ ...options, [Kind]: 'Undefined', type: 'null', typeOf: 'Undefined' }) - } - - /** `Standard` Creates a union type */ - public Union(items: [], options?: SchemaOptions): TNever - - /** `Standard` Creates a union type */ - public Union(items: [...T], options?: SchemaOptions): TUnion - - /** `Standard` Creates a union type */ - public Union(items: [...T], options: SchemaOptions = {}) { - return items.length === 0 ? Type.Never({ ...options }) : this.Create({ ...options, [Kind]: 'Union', anyOf: items }) - } - - /** `Extended` Creates a Uint8Array type */ - public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', instanceOf: 'Uint8Array' }) + /** `[Standard]` Creates a Union type */ + public Union(anyOf: [], options?: SchemaOptions): TNever + /** `[Standard]` Creates a Union type */ + public Union(anyOf: [...T], options?: SchemaOptions): T[0] + /** `[Standard]` Creates a Union type */ + public Union(anyOf: [...T], options?: SchemaOptions): TUnion + public Union(anyOf: TSchema[], options: SchemaOptions = {}) { + if (anyOf.length === 0) return this.Never(options) + if (anyOf.length === 1) return this.Create(TypeClone.Clone(anyOf[0], options)) + const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema, {})) + return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) } - - /** `Standard` Creates an unknown type */ + /** `[Standard]` Creates an Unknown type */ public Unknown(options: SchemaOptions = {}): TUnknown { return this.Create({ ...options, [Kind]: 'Unknown' }) } - - /** `Standard` Creates a user defined schema that infers as type T */ + /** `[Standard]` Creates a Unsafe type that infers for the generic argument */ public Unsafe(options: UnsafeOptions = {}): TUnsafe { return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) } - - /** `Extended` Creates a void type */ - public Void(options: SchemaOptions = {}): TVoid { - return this.Create({ ...options, [Kind]: 'Void', type: 'null', typeOf: 'Void' }) +} +// -------------------------------------------------------------------------- +// TypeBuilder +// -------------------------------------------------------------------------- +export class ExtendedTypeBuilder extends StandardTypeBuilder { + /** `[Extended]` Creates a BigInt type */ + public BigInt(options: NumericOptions = {}): TBigInt { + return this.Create({ ...options, [Kind]: 'BigInt', type: 'null', typeOf: 'BigInt' }) } - - /** Use this function to return TSchema with static and params omitted */ - protected Create(schema: Omit): T { - return schema as any + /** `[Extended]` Extracts the ConstructorParameters from the given Constructor type */ + public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + return this.Tuple([...schema.parameters], { ...options }) } - - /** Clones the given value */ - protected Clone(value: any): any { - const isObject = (object: any): object is Record => typeof object === 'object' && object !== null && !Array.isArray(object) - const isArray = (object: any): object is any[] => typeof object === 'object' && object !== null && Array.isArray(object) - if (isObject(value)) { - return Object.keys(value).reduce( - (acc, key) => ({ - ...acc, - [key]: this.Clone(value[key]), - }), - Object.getOwnPropertySymbols(value).reduce( - (acc, key: any) => ({ - ...acc, - [key]: this.Clone(value[key]), - }), - {}, - ), - ) - } else if (isArray(value)) { - return value.map((item: any) => this.Clone(item)) + /** `[Extended]` Creates a Constructor type */ + public Constructor, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor, U> + /** `[Extended]` Creates a Constructor type */ + public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor + public Constructor(parameters: any, returns: any, options: SchemaOptions = {}) { + const clonedReturns = TypeClone.Clone(returns, {}) + if (TypeGuard.TTuple(parameters)) { + const clonedParameters = parameters.items === undefined ? [] : parameters.items.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) + } else if (globalThis.Array.isArray(parameters)) { + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) + } else { + throw new Error('ExtendedTypeBuilder.Constructor: Invalid parameters') + } + } + /** `[Extended]` Creates a Date type */ + public Date(options: DateOptions = {}): TDate { + return this.Create({ ...options, [Kind]: 'Date', type: 'object', instanceOf: 'Date' }) + } + /** `[Extended]` Creates a Function type */ + public Function, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction, U> + /** `[Extended]` Creates a Function type */ + public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction + public Function(parameters: any, returns: any, options: SchemaOptions = {}) { + const clonedReturns = TypeClone.Clone(returns, {}) + if (TypeGuard.TTuple(parameters)) { + const clonedParameters = parameters.items === undefined ? [] : parameters.items.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) + } else if (globalThis.Array.isArray(parameters)) { + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) } else { - return value + throw new Error('ExtendedTypeBuilder.Function: Invalid parameters') } } + /** `[Extended]` Extracts the InstanceType from the given Constructor */ + public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + return TypeClone.Clone(schema.returns, options) + } + /** `[Extended]` Extracts the Parameters from the given Function type */ + public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + return this.Tuple(schema.parameters, { ...options }) + } + /** `[Extended]` Creates a Promise type */ + public Promise(item: T, options: SchemaOptions = {}): TPromise { + return this.Create({ ...options, [Kind]: 'Promise', type: 'object', instanceOf: 'Promise', item: TypeClone.Clone(item, {}) }) + } + /** `[Extended]` Creates a regular expression type */ + public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { + return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern: regex.source }) + } + /** `[Extended]` Extracts the ReturnType from the given Function */ + public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return TypeClone.Clone(schema.returns, options) + } + /** `[Extended]` Creates a Symbol type */ + public Symbol(options?: SchemaOptions): TSymbol { + return this.Create({ ...options, [Kind]: 'Symbol', type: 'null', typeOf: 'Symbol' }) + } + /** `[Extended]` Creates a Undefined type */ + public Undefined(options: SchemaOptions = {}): TUndefined { + return this.Create({ ...options, [Kind]: 'Undefined', type: 'null', typeOf: 'Undefined' }) + } + /** `[Extended]` Creates a Uint8Array type */ + public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', instanceOf: 'Uint8Array' }) + } + /** `[Extended]` Creates a Void type */ + public Void(options: SchemaOptions = {}): TVoid { + return this.Create({ ...options, [Kind]: 'Void', type: 'null', typeOf: 'Void' }) + } } -/** JSON Schema Type Builder with Static Type Resolution for TypeScript */ -export const Type = new TypeBuilder() +/** JSON Schema TypeBuilder with Static Resolution for TypeScript */ +export const StandardType = new StandardTypeBuilder() + +/** JSON Schema TypeBuilder with Static Resolution for TypeScript */ +export const Type = new ExtendedTypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast.ts index 1a12d1333..67e936a29 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -30,7 +30,6 @@ import * as Types from '../typebox' import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueClone } from './clone' -import { Custom } from '../custom/index' // ---------------------------------------------------------------------------------------------- // Errors @@ -60,7 +59,11 @@ export class ValueCastUnknownTypeError extends Error { super('ValueCast: Unknown type') } } - +export class ValueCastDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueCast: Unable to dereference schema with $id '${schema.$ref}'`) + } +} // ---------------------------------------------------------------------------------------------- // The following will score a schema against a value. For objects, the score is the tally of // points awarded for each property of the value. Property points are (1.0 / propertyCount) @@ -109,93 +112,18 @@ export namespace ValueCast { // ---------------------------------------------------------------------------------------------- // Guards // ---------------------------------------------------------------------------------------------- + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) + } function IsArray(value: unknown): value is unknown[] { return typeof value === 'object' && globalThis.Array.isArray(value) } - function IsDate(value: unknown): value is Date { - return typeof value === 'object' && value instanceof globalThis.Date - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } function IsNumber(value: unknown): value is number { return typeof value === 'number' && !isNaN(value) } - function IsStringNumeric(value: unknown): value is string { - return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) - } - function IsValueToString(value: unknown): value is { toString: () => string } { - return IsBigInt(value) || IsBoolean(value) || IsNumber(value) - } - function IsValueTrue(value: unknown): value is true { - return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === globalThis.BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) - } - function IsValueFalse(value: unknown): value is true { - return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === globalThis.BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) - } - function IsTimeStringWithTimeZone(value: unknown): value is string { - return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) - } - function IsTimeStringWithoutTimeZone(value: unknown): value is string { - return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) - } - function IsDateTimeStringWithTimeZone(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) - } - function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) - } - function IsDateString(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) - } - - // ---------------------------------------------------------------------------------------------- - // Convert - // ---------------------------------------------------------------------------------------------- - function TryConvertString(value: unknown) { - return IsValueToString(value) ? value.toString() : value - } - function TryConvertNumber(value: unknown) { - return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : value - } - function TryConvertInteger(value: unknown) { - return IsStringNumeric(value) ? parseInt(value) : IsValueTrue(value) ? 1 : value - } - function TryConvertBoolean(value: unknown) { - return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value - } - function TryConvertDate(value: unknown) { - // note: this function may return an invalid dates for the regex tests - // above. Invalid dates will however be checked during the casting - // function and will return a epoch date if invalid. Consider better - // string parsing for the iso dates in future revisions. - return IsDate(value) - ? value - : IsNumber(value) - ? new globalThis.Date(value) - : IsValueTrue(value) - ? new globalThis.Date(1) - : IsStringNumeric(value) - ? new globalThis.Date(parseInt(value)) - : IsTimeStringWithoutTimeZone(value) - ? new globalThis.Date(`1970-01-01T${value}.000Z`) - : IsTimeStringWithTimeZone(value) - ? new globalThis.Date(`1970-01-01T${value}`) - : IsDateTimeStringWithoutTimeZone(value) - ? new globalThis.Date(`${value}.000Z`) - : IsDateTimeStringWithTimeZone(value) - ? new globalThis.Date(value) - : IsDateString(value) - ? new globalThis.Date(`${value}T00:00:00.000Z`) - : value + function IsString(value: unknown): value is string { + return typeof value === 'string' } - // ---------------------------------------------------------------------------------------------- // Cast // ---------------------------------------------------------------------------------------------- @@ -213,9 +141,11 @@ export namespace ValueCast { if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) return unique } + function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { - const conversion = TryConvertBoolean(value) - return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) @@ -228,31 +158,36 @@ export namespace ValueCast { return result } function Date(schema: Types.TDate, references: Types.TSchema[], value: any): any { - const conversion = TryConvertDate(value) - return ValueCheck.Check(schema, references, conversion) ? ValueClone.Clone(conversion) : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): any { - const conversion = TryConvertInteger(value) - return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any { + const created = ValueCreate.Create(schema, references) + const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value + return ValueCheck.Check(schema, references, mapped) ? mapped : ValueCreate.Create(schema, references) } function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Never(schema: Types.TNever, references: Types.TSchema[], value: any): any { throw new ValueCastNeverTypeError(schema) } + function Not(schema: Types.TNot, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema.allOf[1], references) + } function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): any { - const conversion = TryConvertNumber(value) - return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Object(schema: Types.TObject, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (ValueCheck.Check(schema, references, value)) return value if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) const required = new Set(schema.required || []) const result = {} as Record @@ -285,18 +220,22 @@ export namespace ValueCast { return result } function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): any { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new ValueCastReferenceTypeError(schema) - return Visit(reference, references, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCastDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) } function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): any { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new ValueCastReferenceTypeError(schema) - return Visit(reference, references, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCastDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) } function String(schema: Types.TString, references: Types.TSchema[], value: any): any { - const conversion = TryConvertString(value) - return ValueCheck.Check(schema, references, conversion) ? conversion : ValueCreate.Create(schema, references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + } + function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) @@ -323,61 +262,69 @@ export namespace ValueCast { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const anyReferences = schema.$id === undefined ? references : [schema, ...references] - const anySchema = schema as any + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any switch (schema[Types.Kind]) { case 'Any': - return Any(anySchema, anyReferences, value) + return Any(schema_, references_, value) case 'Array': - return Array(anySchema, anyReferences, value) + return Array(schema_, references_, value) + case 'BigInt': + return BigInt(schema_, references_, value) case 'Boolean': - return Boolean(anySchema, anyReferences, value) + return Boolean(schema_, references_, value) case 'Constructor': - return Constructor(anySchema, anyReferences, value) + return Constructor(schema_, references_, value) case 'Date': - return Date(anySchema, anyReferences, value) + return Date(schema_, references_, value) case 'Function': - return Function(anySchema, anyReferences, value) + return Function(schema_, references_, value) case 'Integer': - return Integer(anySchema, anyReferences, value) + return Integer(schema_, references_, value) + case 'Intersect': + return Intersect(schema_, references_, value) case 'Literal': - return Literal(anySchema, anyReferences, value) + return Literal(schema_, references_, value) case 'Never': - return Never(anySchema, anyReferences, value) + return Never(schema_, references_, value) + case 'Not': + return Not(schema_, references_, value) case 'Null': - return Null(anySchema, anyReferences, value) + return Null(schema_, references_, value) case 'Number': - return Number(anySchema, anyReferences, value) + return Number(schema_, references_, value) case 'Object': - return Object(anySchema, anyReferences, value) + return Object(schema_, references_, value) case 'Promise': - return Promise(anySchema, anyReferences, value) + return Promise(schema_, references_, value) case 'Record': - return Record(anySchema, anyReferences, value) + return Record(schema_, references_, value) case 'Ref': - return Ref(anySchema, anyReferences, value) + return Ref(schema_, references_, value) case 'Self': - return Self(anySchema, anyReferences, value) + return Self(schema_, references_, value) case 'String': - return String(anySchema, anyReferences, value) + return String(schema_, references_, value) + case 'Symbol': + return Symbol(schema_, references_, value) case 'Tuple': - return Tuple(anySchema, anyReferences, value) + return Tuple(schema_, references_, value) case 'Undefined': - return Undefined(anySchema, anyReferences, value) + return Undefined(schema_, references_, value) case 'Union': - return Union(anySchema, anyReferences, value) + return Union(schema_, references_, value) case 'Uint8Array': - return Uint8Array(anySchema, anyReferences, value) + return Uint8Array(schema_, references_, value) case 'Unknown': - return Unknown(anySchema, anyReferences, value) + return Unknown(schema_, references_, value) case 'Void': - return Void(anySchema, anyReferences, value) + return Void(schema_, references_, value) default: - if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCastUnknownTypeError(anySchema) - return UserDefined(anySchema, anyReferences, value) + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) + return UserDefined(schema_, references_, value) } } - export function Cast(schema: T, references: [...R], value: any): Types.Static { - return schema.$id === undefined ? Visit(schema, references, value) : Visit(schema, [schema, ...references], value) + export function Cast(schema: T, references: Types.TSchema[], value: any): Types.Static { + return Visit(schema, references, ValueClone.Clone(value)) } } diff --git a/src/value/check.ts b/src/value/check.ts index 6149cb7f6..15c06bbe8 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -28,156 +28,197 @@ THE SOFTWARE. import * as Types from '../typebox' import { TypeSystem } from '../system/index' -import { TypeExtends } from '../guard/extends' -import { Format } from '../format/index' -import { Custom } from '../custom/index' -import { ValueHash } from '../hash/index' +import { ValueHash } from './hash' +// ------------------------------------------------------------------------- +// Errors +// ------------------------------------------------------------------------- export class ValueCheckUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super(`ValueCheck: ${schema[Types.Kind] ? `Unknown type '${schema[Types.Kind]}'` : 'Unknown type'}`) } } - +export class ValueCheckDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueCheck: Unable to dereference schema with $id '${schema.$ref}'`) + } +} export namespace ValueCheck { - // -------------------------------------------------------- + // ---------------------------------------------------------------------- // Guards - // -------------------------------------------------------- - + // ---------------------------------------------------------------------- + function IsObject(value: unknown): value is Record { + const result = typeof value === 'object' && value !== null + return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) + } + function IsRecordObject(value: unknown): value is Record { + return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) + } + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !isNaN(value) + const result = typeof value === 'number' + return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) } - - // -------------------------------------------------------- - // Guards - // -------------------------------------------------------- - + function IsInteger(value: unknown): value is number { + return globalThis.Number.isInteger(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsVoid(value: unknown): value is void { + const result = value === undefined + return TypeSystem.AllowVoidNull ? result || value === null : result + } + function IsDefined(value: unknown): value is T { + return value !== undefined + } + // ---------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------- function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { return true } - function Array(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { if (!globalThis.Array.isArray(value)) { return false } - if (IsNumber(schema.minItems) && !(value.length >= schema.minItems)) { + if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { return false } - if (IsNumber(schema.maxItems) && !(value.length <= schema.maxItems)) { + if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { return false } // prettier-ignore if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Create(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { return false } - return value.every((val) => Visit(schema.items, references, val)) + return value.every((value) => Visit(schema.items, references, value)) + } + function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { + if (!IsBigInt(value)) { + return false + } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === globalThis.BigInt(0))) { + return false + } + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + return false + } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + return false + } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + return false + } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + return false + } + return true } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { return typeof value === 'boolean' } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { return Visit(schema.returns, references, value.prototype) } - function Date(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { if (!(value instanceof globalThis.Date)) { return false } - if (isNaN(value.getTime())) { + if (!IsNumber(value.getTime())) { return false } - if (IsNumber(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { return false } - if (IsNumber(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { return false } - if (IsNumber(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { + if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { return false } - if (IsNumber(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { return false } return true } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { return typeof value === 'function' } - function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'number' && globalThis.Number.isInteger(value))) { + if (!IsInteger(value)) { return false } - if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } - if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } - if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { return false } - if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { return false } return true } - + function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): boolean { + if (!schema.allOf.every((schema) => Visit(schema, references, value))) { + return false + } else if (schema.unevaluatedProperties === false) { + const schemaKeys = Types.KeyResolver.Resolve(schema) + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + return valueKeys.every((key) => schemaKeys.includes(key)) + } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { + const schemaKeys = Types.KeyResolver.Resolve(schema) + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + return valueKeys.every((key) => schemaKeys.includes(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) + } else { + return true + } + } function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { return value === schema.const } - function Never(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { return false } - + function Not(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { + return !Visit(schema.allOf[0].not, references, value) && Visit(schema.allOf[1], references, value) + } function Null(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { return value === null } - function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { - if (TypeSystem.AllowNaN) { - if (!(typeof value === 'number')) { - return false - } - } else { - if (!(typeof value === 'number' && !isNaN(value))) { - return false - } + if (!IsNumber(value)) { + return false } - if (IsNumber(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } - if (IsNumber(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (IsNumber(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } - if (IsNumber(schema.minimum) && !(value >= schema.minimum)) { + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { return false } - if (IsNumber(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { return false } return true } - function Object(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { - if (TypeSystem.AllowArrayObjects) { - if (!(typeof value === 'object' && value !== null)) { - return false - } - } else { - if (!(typeof value === 'object' && value !== null && !globalThis.Array.isArray(value))) { - return false - } + if (!IsObject(value)) { + return false } if (IsNumber(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { return false @@ -185,61 +226,43 @@ export namespace ValueCheck { if (IsNumber(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { return false } - const propertyKeys = globalThis.Object.getOwnPropertyNames(schema.properties) - if (schema.additionalProperties === false) { - // optimization: If the property key length matches the required keys length - // then we only need check that the values property key length matches that - // of the property key length. This is because exhaustive testing for values - // will occur in subsequent property tests. - if (schema.required && schema.required.length === propertyKeys.length && !(globalThis.Object.getOwnPropertyNames(value).length === propertyKeys.length)) { - return false - } else { - if (!globalThis.Object.getOwnPropertyNames(value).every((key) => propertyKeys.includes(key))) { + const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) + for (const knownKey of knownKeys) { + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { + if (!Visit(property, references, value[knownKey])) { return false } - } - } - if (typeof schema.additionalProperties === 'object') { - for (const objectKey of globalThis.Object.getOwnPropertyNames(value)) { - if (propertyKeys.includes(objectKey)) continue - if (!Visit(schema.additionalProperties as Types.TSchema, references, value[objectKey])) { + if (Types.ExtendsUndefined.Check(property)) { + return knownKey in value + } + } else { + if (knownKey in value && !Visit(property, references, value[knownKey])) { return false } } } - for (const propertyKey of propertyKeys) { - const propertySchema = schema.properties[propertyKey] - if (schema.required && schema.required.includes(propertyKey)) { - if (!Visit(propertySchema, references, value[propertyKey])) { - return false - } - if (TypeExtends.Undefined(propertySchema)) { - return propertyKey in value - } + if (schema.additionalProperties === false) { + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + // optimization: value is valid if schemaKey length matches the valueKey length + if (schema.required && schema.required.length === knownKeys.length && valueKeys.length === knownKeys.length) { + return true } else { - if (value[propertyKey] !== undefined) { - if (!Visit(propertySchema, references, value[propertyKey])) { - return false - } - } + return valueKeys.every((valueKey) => knownKeys.includes(valueKey)) } + } else if (typeof schema.additionalProperties === 'object') { + const valueKeys = globalThis.Object.getOwnPropertyNames(value) + return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as Types.TSchema, references, value[key])) + } else { + return true } - return true } - function Promise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { return typeof value === 'object' && typeof value.then === 'function' } - function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (TypeSystem.AllowArrayObjects) { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date))) { - return false - } - } else { - if (!(typeof value === 'object' && value !== null && !(value instanceof globalThis.Date) && !globalThis.Array.isArray(value))) { - return false - } + if (!IsRecordObject(value)) { + return false } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) @@ -251,41 +274,45 @@ export namespace ValueCheck { } return true } - function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueCheck.Ref: Cannot find schema with $id '${schema.$ref}'.`) - return Visit(reference, references, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCheckDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) } - function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): boolean { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueCheck.Self: Cannot find schema with $id '${schema.$ref}'.`) - return Visit(reference, references, value) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCheckDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) } - function String(schema: Types.TString, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'string')) { + if (!IsString(value)) { return false } - if (IsNumber(schema.minLength)) { + if (IsDefined(schema.minLength)) { if (!(value.length >= schema.minLength)) return false } - if (IsNumber(schema.maxLength)) { + if (IsDefined(schema.maxLength)) { if (!(value.length <= schema.maxLength)) return false } - if (schema.pattern !== undefined) { + if (IsDefined(schema.pattern)) { const regex = new RegExp(schema.pattern) if (!regex.test(value)) return false } - if (schema.format !== undefined) { - if (!Format.Has(schema.format)) return false - const func = Format.Get(schema.format)! + if (IsDefined(schema.format)) { + if (!Types.FormatRegistry.Has(schema.format)) return false + const func = Types.FormatRegistry.Get(schema.format)! return func(value) } return true } - + function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'symbol')) { + return false + } + return true + } function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { if (!globalThis.Array.isArray(value)) { return false @@ -304,103 +331,102 @@ export namespace ValueCheck { } return true } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { return value === undefined } - function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { return schema.anyOf.some((inner) => Visit(inner, references, value)) } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { if (!(value instanceof globalThis.Uint8Array)) { return false } - if (IsNumber(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { + if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { return false } - if (IsNumber(schema.minByteLength) && !(value.length >= schema.minByteLength)) { + if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { return false } return true } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { return true } - function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { - return value === null + return IsVoid(value) } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { - if (!Custom.Has(schema[Types.Kind])) return false - const func = Custom.Get(schema[Types.Kind])! + if (!Types.TypeRegistry.Has(schema[Types.Kind])) return false + const func = Types.TypeRegistry.Get(schema[Types.Kind])! return func(schema, value) } - function Visit(schema: T, references: Types.TSchema[], value: any): boolean { - const anyReferences = schema.$id === undefined ? references : [schema, ...references] - const anySchema = schema as any - switch (anySchema[Types.Kind]) { + const references_ = IsDefined(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { case 'Any': - return Any(anySchema, anyReferences, value) + return Any(schema_, references_, value) case 'Array': - return Array(anySchema, anyReferences, value) + return Array(schema_, references_, value) + case 'BigInt': + return BigInt(schema_, references_, value) case 'Boolean': - return Boolean(anySchema, anyReferences, value) + return Boolean(schema_, references_, value) case 'Constructor': - return Constructor(anySchema, anyReferences, value) + return Constructor(schema_, references_, value) case 'Date': - return Date(anySchema, anyReferences, value) + return Date(schema_, references_, value) case 'Function': - return Function(anySchema, anyReferences, value) + return Function(schema_, references_, value) case 'Integer': - return Integer(anySchema, anyReferences, value) + return Integer(schema_, references_, value) + case 'Intersect': + return Intersect(schema_, references_, value) case 'Literal': - return Literal(anySchema, anyReferences, value) + return Literal(schema_, references_, value) case 'Never': - return Never(anySchema, anyReferences, value) + return Never(schema_, references_, value) + case 'Not': + return Not(schema_, references_, value) case 'Null': - return Null(anySchema, anyReferences, value) + return Null(schema_, references_, value) case 'Number': - return Number(anySchema, anyReferences, value) + return Number(schema_, references_, value) case 'Object': - return Object(anySchema, anyReferences, value) + return Object(schema_, references_, value) case 'Promise': - return Promise(anySchema, anyReferences, value) + return Promise(schema_, references_, value) case 'Record': - return Record(anySchema, anyReferences, value) + return Record(schema_, references_, value) case 'Ref': - return Ref(anySchema, anyReferences, value) + return Ref(schema_, references_, value) case 'Self': - return Self(anySchema, anyReferences, value) + return Self(schema_, references_, value) case 'String': - return String(anySchema, anyReferences, value) + return String(schema_, references_, value) + case 'Symbol': + return Symbol(schema_, references_, value) case 'Tuple': - return Tuple(anySchema, anyReferences, value) + return Tuple(schema_, references_, value) case 'Undefined': - return Undefined(anySchema, anyReferences, value) + return Undefined(schema_, references_, value) case 'Union': - return Union(anySchema, anyReferences, value) + return Union(schema_, references_, value) case 'Uint8Array': - return Uint8Array(anySchema, anyReferences, value) + return Uint8Array(schema_, references_, value) case 'Unknown': - return Unknown(anySchema, anyReferences, value) + return Unknown(schema_, references_, value) case 'Void': - return Void(anySchema, anyReferences, value) + return Void(schema_, references_, value) default: - if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCheckUnknownTypeError(anySchema) - return UserDefined(anySchema, anyReferences, value) + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCheckUnknownTypeError(schema_) + return UserDefined(schema_, references_, value) } } - // ------------------------------------------------------------------------- // Check // ------------------------------------------------------------------------- - - export function Check(schema: T, references: [...R], value: any): boolean { - return schema.$id === undefined ? Visit(schema, references, value) : Visit(schema, [schema, ...references], value) + export function Check(schema: T, references: Types.TSchema[], value: any): boolean { + return Visit(schema, references, value) } } diff --git a/src/value/clone.ts b/src/value/clone.ts index 82bf94b04..e8d3d2185 100644 --- a/src/value/clone.ts +++ b/src/value/clone.ts @@ -32,24 +32,19 @@ export namespace ValueClone { function Array(value: ArrayType): any { return value.map((element: any) => Clone(element)) } - function Date(value: Date): any { return new globalThis.Date(value.toISOString()) } - function Object(value: ObjectType): any { const keys = [...globalThis.Object.keys(value), ...globalThis.Object.getOwnPropertySymbols(value)] return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) } - function TypedArray(value: TypedArrayType): any { return value.slice() } - function Value(value: ValueType): any { return value } - export function Clone(value: T): T { if (Is.Date(value)) { return Date(value) diff --git a/src/value/convert.ts b/src/value/convert.ts new file mode 100644 index 000000000..987ef038e --- /dev/null +++ b/src/value/convert.ts @@ -0,0 +1,342 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../typebox' +import { ValueClone } from './clone' + +// ---------------------------------------------------------------------------------------------- +// Errors +// ---------------------------------------------------------------------------------------------- +export class ValueConvertUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueConvert: Unknown type') + } +} +export class ValueConvertDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueConvert: Unable to dereference schema with $id '${schema.$ref}'`) + } +} +export namespace ValueConvert { + // ---------------------------------------------------------------------------------------------- + // Guards + // ---------------------------------------------------------------------------------------------- + function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) + } + function IsArray(value: unknown): value is unknown[] { + return typeof value === 'object' && globalThis.Array.isArray(value) + } + function IsDate(value: unknown): value is Date { + return typeof value === 'object' && value instanceof globalThis.Date + } + function IsSymbol(value: unknown): value is symbol { + return typeof value === 'symbol' + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + function IsNumber(value: unknown): value is number { + return typeof value === 'number' && !isNaN(value) + } + function IsStringNumeric(value: unknown): value is string { + return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) + } + function IsValueToString(value: unknown): value is { toString: () => string } { + return IsBigInt(value) || IsBoolean(value) || IsNumber(value) + } + function IsValueTrue(value: unknown): value is true { + return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === globalThis.BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) + } + function IsValueFalse(value: unknown): value is true { + return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === globalThis.BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) + } + function IsTimeStringWithTimeZone(value: unknown): value is string { + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + } + function IsTimeStringWithoutTimeZone(value: unknown): value is string { + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + } + function IsDateTimeStringWithTimeZone(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + } + function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + } + function IsDateString(value: unknown): value is string { + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) + } + // ---------------------------------------------------------------------------------------------- + // Convert + // ---------------------------------------------------------------------------------------------- + function TryConvertLiteralString(value: unknown, target: string) { + const conversion = TryConvertString(value) + return conversion === target ? conversion : value + } + function TryConvertLiteralNumber(value: unknown, target: number) { + const conversion = TryConvertNumber(value) + return conversion === target ? conversion : value + } + function TryConvertLiteralBoolean(value: unknown, target: boolean) { + const conversion = TryConvertBoolean(value) + return conversion === target ? conversion : value + } + function TryConvertLiteral(schema: Types.TLiteral, value: unknown) { + if (typeof schema.const === 'string') { + return TryConvertLiteralString(value, schema.const) + } else if (typeof schema.const === 'number') { + return TryConvertLiteralNumber(value, schema.const) + } else if (typeof schema.const === 'boolean') { + return TryConvertLiteralBoolean(value, schema.const) + } else { + return ValueClone.Clone(value) + } + } + function TryConvertBoolean(value: unknown) { + return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value + } + function TryConvertBigInt(value: unknown) { + return IsStringNumeric(value) ? globalThis.BigInt(parseInt(value)) : IsNumber(value) ? globalThis.BigInt(value | 0) : IsValueFalse(value) ? 0 : IsValueTrue(value) ? 1 : value + } + + function TryConvertString(value: unknown) { + return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value + } + function TryConvertNumber(value: unknown) { + return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value + } + function TryConvertInteger(value: unknown) { + return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value + } + function TryConvertNull(value: unknown) { + return IsString(value) && value.toLowerCase() === 'null' ? null : value + } + function TryConvertUndefined(value: unknown) { + return IsString(value) && value === 'undefined' ? undefined : value + } + function TryConvertDate(value: unknown) { + // note: this function may return an invalid dates for the regex tests + // above. Invalid dates will however be checked during the casting + // function and will return a epoch date if invalid. Consider better + // string parsing for the iso dates in future revisions. + return IsDate(value) + ? value + : IsNumber(value) + ? new globalThis.Date(value) + : IsValueTrue(value) + ? new globalThis.Date(1) + : IsValueFalse(value) + ? new globalThis.Date(0) + : IsStringNumeric(value) + ? new globalThis.Date(parseInt(value)) + : IsTimeStringWithoutTimeZone(value) + ? new globalThis.Date(`1970-01-01T${value}.000Z`) + : IsTimeStringWithTimeZone(value) + ? new globalThis.Date(`1970-01-01T${value}`) + : IsDateTimeStringWithoutTimeZone(value) + ? new globalThis.Date(`${value}.000Z`) + : IsDateTimeStringWithTimeZone(value) + ? new globalThis.Date(value) + : IsDateString(value) + ? new globalThis.Date(`${value}T00:00:00.000Z`) + : value + } + + // ---------------------------------------------------------------------------------------------- + // Cast + // ---------------------------------------------------------------------------------------------- + function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { + return value + } + function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { + if (IsArray(value)) { + return value.map((value) => Visit(schema.items, references, value)) + } + return value + } + function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): unknown { + return TryConvertBigInt(value) + } + function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { + return TryConvertBoolean(value) + } + function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): unknown { + return ValueClone.Clone(value) + } + function Date(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { + return TryConvertDate(value) + } + function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): unknown { + return value + } + function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { + return TryConvertInteger(value) + } + function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { + return value + } + function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { + return TryConvertLiteral(schema, value) + } + function Never(schema: Types.TNever, references: Types.TSchema[], value: any): unknown { + return value + } + function Null(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { + return TryConvertNull(value) + } + function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): unknown { + return TryConvertNumber(value) + } + function Object(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { + if (IsObject(value)) + return globalThis.Object.keys(schema.properties).reduce((acc, key) => { + return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } + }, value) + return value + } + function Promise(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + return value + } + function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { + return value + } + function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueConvertDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) + } + function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): unknown { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueConvertDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) + } + function String(schema: Types.TString, references: Types.TSchema[], value: any): unknown { + return TryConvertString(value) + } + function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { + return value + } + function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { + if (IsArray(value) && schema.items !== undefined) { + return value.map((value, index) => { + return index < schema.items!.length ? Visit(schema.items![index], references, value) : value + }) + } + return value + } + function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { + return TryConvertUndefined(value) + } + function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { + return value + } + function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): unknown { + return value + } + function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): unknown { + return value + } + function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): unknown { + return value + } + function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + return value + } + export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + case 'Any': + return Any(schema_, references_, value) + case 'Array': + return Array(schema_, references_, value) + case 'BigInt': + return BigInt(schema_, references_, value) + case 'Boolean': + return Boolean(schema_, references_, value) + case 'Constructor': + return Constructor(schema_, references_, value) + case 'Date': + return Date(schema_, references_, value) + case 'Function': + return Function(schema_, references_, value) + case 'Integer': + return Integer(schema_, references_, value) + case 'Intersect': + return Intersect(schema_, references_, value) + case 'Literal': + return Literal(schema_, references_, value) + case 'Never': + return Never(schema_, references_, value) + case 'Null': + return Null(schema_, references_, value) + case 'Number': + return Number(schema_, references_, value) + case 'Object': + return Object(schema_, references_, value) + case 'Promise': + return Promise(schema_, references_, value) + case 'Record': + return Record(schema_, references_, value) + case 'Ref': + return Ref(schema_, references_, value) + case 'Self': + return Self(schema_, references_, value) + case 'String': + return String(schema_, references_, value) + case 'Symbol': + return Symbol(schema_, references_, value) + case 'Tuple': + return Tuple(schema_, references_, value) + case 'Undefined': + return Undefined(schema_, references_, value) + case 'Union': + return Union(schema_, references_, value) + case 'Uint8Array': + return Uint8Array(schema_, references_, value) + case 'Unknown': + return Unknown(schema_, references_, value) + case 'Void': + return Void(schema_, references_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) + return UserDefined(schema_, references_, value) + } + } + export function Convert(schema: T, references: Types.TSchema[], value: any): unknown { + return Visit(schema, references, ValueClone.Clone(value)) + } +} diff --git a/src/value/create.ts b/src/value/create.ts index 980d180d8..a2a0e1392 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -27,33 +27,55 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { Custom } from '../custom/index' +import { ValueCheck } from './check' +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- export class ValueCreateUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueCreate: Unknown type') } } - export class ValueCreateNeverTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueCreate: Never types cannot be created') } } - +export class ValueCreateIntersectTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCreate: Can only create values for intersected objects and non-varying primitive types. Consider using a default value.') + } +} +export class ValueCreateDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TSelf) { + super(`ValueCreate: Unable to dereference schema with $id '${schema.$ref}'`) + } +} +// -------------------------------------------------------------------------- +// ValueCreate +// -------------------------------------------------------------------------- export namespace ValueCreate { + // -------------------------------------------------------- + // Guards + // -------------------------------------------------------- + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + // -------------------------------------------------------- + // Types + // -------------------------------------------------------- function Any(schema: Types.TAny, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return {} } } - function Array(schema: Types.TArray, references: Types.TSchema[]): any { if (schema.uniqueItems === true && schema.default === undefined) { throw new Error('ValueCreate.Array: Arrays with uniqueItems require a default value') - } else if (schema.default !== undefined) { + } else if ('default' in schema) { return schema.default } else if (schema.minItems !== undefined) { return globalThis.Array.from({ length: schema.minItems }).map((item) => { @@ -63,17 +85,22 @@ export namespace ValueCreate { return [] } } - + function BigInt(schema: Types.TBigInt, references: Types.TSchema[]): any { + if ('default' in schema) { + return schema.default + } else { + return globalThis.BigInt(0) + } + } function Boolean(schema: Types.TBoolean, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return false } } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { const value = ValueCreate.Create(schema.returns, references) as any @@ -91,9 +118,8 @@ export namespace ValueCreate { } } } - function Date(schema: Types.TDate, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.minimumTimestamp !== undefined) { return new globalThis.Date(schema.minimumTimestamp) @@ -101,17 +127,15 @@ export namespace ValueCreate { return new globalThis.Date(0) } } - function Function(schema: Types.TFunction, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return () => ValueCreate.Create(schema.returns, references) } } - function Integer(schema: Types.TInteger, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.minimum !== undefined) { return schema.minimum @@ -119,29 +143,41 @@ export namespace ValueCreate { return 0 } } - + function Intersect(schema: Types.TIntersect, references: Types.TSchema[]): any { + if ('default' in schema) { + return schema.default + } else { + const value = schema.type === 'object' ? schema.allOf.reduce((acc, schema) => ({ ...acc, ...(Visit(schema, references) as any) }), {}) : schema.allOf.reduce((_, schema) => Visit(schema, references), undefined as any) + if (!ValueCheck.Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) + return value + } + } function Literal(schema: Types.TLiteral, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return schema.const } } - function Never(schema: Types.TNever, references: Types.TSchema[]): any { throw new ValueCreateNeverTypeError(schema) } - + function Not(schema: Types.TNot, references: Types.TSchema[]): any { + if ('default' in schema) { + return schema.default + } else { + return Visit(schema.allOf[1], references) + } + } function Null(schema: Types.TNull, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return null } } - function Number(schema: Types.TNumber, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.minimum !== undefined) { return schema.minimum @@ -149,9 +185,8 @@ export namespace ValueCreate { return 0 } } - function Object(schema: Types.TObject, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { const required = new Set(schema.required) @@ -163,18 +198,16 @@ export namespace ValueCreate { ) } } - function Promise(schema: Types.TPromise, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return globalThis.Promise.resolve(ValueCreate.Create(schema.item, references)) } } - function Record(schema: Types.TRecord, references: Types.TSchema[]): any { const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (!(keyPattern === '^.*$' || keyPattern === '^(0|[1-9][0-9]*)$')) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') @@ -185,42 +218,41 @@ export namespace ValueCreate { return {} } } - function Ref(schema: Types.TRef, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueCreate.Ref: Cannot find schema with $id '${schema.$ref}'.`) - return Visit(reference, references) + const index = references.findIndex((foreign) => foreign.$id === schema.$id) + if (index === -1) throw new ValueCreateDereferenceError(schema) + const target = references[index] + return Visit(target, references) } } - function Self(schema: Types.TSelf, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { - const reference = references.find((reference) => reference.$id === schema.$ref) - if (reference === undefined) throw new Error(`ValueCreate.Self: Cannot locate schema with $id '${schema.$ref}'`) - return Visit(reference, references) + const index = references.findIndex((foreign) => foreign.$id === schema.$id) + if (index === -1) throw new ValueCreateDereferenceError(schema) + const target = references[index] + return Visit(target, references) } } - function String(schema: Types.TString, references: Types.TSchema[]): any { if (schema.pattern !== undefined) { - if (schema.default === undefined) { + if (!('default' in schema)) { throw new Error('ValueCreate.String: String types with patterns must specify a default value') } else { return schema.default } } else if (schema.format !== undefined) { - if (schema.default === undefined) { + if (!('default' in schema)) { throw new Error('ValueCreate.String: String types with formats must specify a default value') } else { return schema.default } } else { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.minLength !== undefined) { return globalThis.Array.from({ length: schema.minLength }) @@ -231,9 +263,17 @@ export namespace ValueCreate { } } } - + function Symbol(schema: Types.TString, references: Types.TSchema[]): any { + if ('default' in schema) { + return schema.default + } else if ('value' in schema) { + return globalThis.Symbol.for(schema.value) + } else { + return globalThis.Symbol() + } + } function Tuple(schema: Types.TTuple, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } if (schema.items === undefined) { @@ -242,17 +282,15 @@ export namespace ValueCreate { return globalThis.Array.from({ length: schema.minItems }).map((_, index) => ValueCreate.Create((schema.items as any[])[index], references)) } } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return undefined } } - function Union(schema: Types.TUnion, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.anyOf.length === 0) { throw new Error('ValueCreate.Union: Cannot create Union with zero variants') @@ -261,7 +299,7 @@ export namespace ValueCreate { } } function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else if (schema.minByteLength !== undefined) { return new globalThis.Uint8Array(schema.minByteLength) @@ -269,90 +307,92 @@ export namespace ValueCreate { return new globalThis.Uint8Array(0) } } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { return {} } } - function Void(schema: Types.TVoid, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { - return null + return void 0 } } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[]): any { - if (schema.default !== undefined) { + if ('default' in schema) { return schema.default } else { throw new Error('ValueCreate.UserDefined: User defined types must specify a default value') } } - /** Creates a value from the given schema. If the schema specifies a default value, then that value is returned. */ - export function Visit(schema: T, references: Types.TSchema[]): Types.Static { - const anyReferences = schema.$id === undefined ? references : [schema, ...references] - const anySchema = schema as any - - switch (anySchema[Types.Kind]) { + export function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { case 'Any': - return Any(anySchema, anyReferences) + return Any(schema_, references_) case 'Array': - return Array(anySchema, anyReferences) + return Array(schema_, references_) + case 'BigInt': + return BigInt(schema_, references_) case 'Boolean': - return Boolean(anySchema, anyReferences) + return Boolean(schema_, references_) case 'Constructor': - return Constructor(anySchema, anyReferences) + return Constructor(schema_, references_) case 'Date': - return Date(anySchema, anyReferences) + return Date(schema_, references_) case 'Function': - return Function(anySchema, anyReferences) + return Function(schema_, references_) case 'Integer': - return Integer(anySchema, anyReferences) + return Integer(schema_, references_) + case 'Intersect': + return Intersect(schema_, references_) case 'Literal': - return Literal(anySchema, anyReferences) + return Literal(schema_, references_) case 'Never': - return Never(anySchema, anyReferences) + return Never(schema_, references_) + case 'Not': + return Not(schema_, references_) case 'Null': - return Null(anySchema, anyReferences) + return Null(schema_, references_) case 'Number': - return Number(anySchema, anyReferences) + return Number(schema_, references_) case 'Object': - return Object(anySchema, anyReferences) + return Object(schema_, references_) case 'Promise': - return Promise(anySchema, anyReferences) + return Promise(schema_, references_) case 'Record': - return Record(anySchema, anyReferences) + return Record(schema_, references_) case 'Ref': - return Ref(anySchema, anyReferences) + return Ref(schema_, references_) case 'Self': - return Self(anySchema, anyReferences) + return Self(schema_, references_) case 'String': - return String(anySchema, anyReferences) + return String(schema_, references_) + case 'Symbol': + return Symbol(schema_, references_) case 'Tuple': - return Tuple(anySchema, anyReferences) + return Tuple(schema_, references_) case 'Undefined': - return Undefined(anySchema, anyReferences) + return Undefined(schema_, references_) case 'Union': - return Union(anySchema, anyReferences) + return Union(schema_, references_) case 'Uint8Array': - return Uint8Array(anySchema, anyReferences) + return Uint8Array(schema_, references_) case 'Unknown': - return Unknown(anySchema, anyReferences) + return Unknown(schema_, references_) case 'Void': - return Void(anySchema, anyReferences) + return Void(schema_, references_) default: - if (!Custom.Has(anySchema[Types.Kind])) throw new ValueCreateUnknownTypeError(anySchema) - return UserDefined(anySchema, anyReferences) + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCreateUnknownTypeError(schema_) + return UserDefined(schema_, references_) } } - - export function Create(schema: T, references: [...R]): Types.Static { + export function Create(schema: T, references: Types.TSchema[]): Types.Static { return Visit(schema, references) } } diff --git a/src/value/equal.ts b/src/value/equal.ts index 841cba47a..ba3e2424c 100644 --- a/src/value/equal.ts +++ b/src/value/equal.ts @@ -36,25 +36,20 @@ export namespace ValueEqual { if (leftKeys.length !== rightKeys.length) return false return leftKeys.every((key) => Equal(left[key], right[key])) } - function Date(left: Date, right: unknown): any { return Is.Date(right) && left.getTime() === right.getTime() } - function Array(left: ArrayType, right: unknown): any { if (!Is.Array(right) || left.length !== right.length) return false return left.every((value, index) => Equal(value, right[index])) } - function TypedArray(left: TypedArrayType, right: unknown): any { if (!Is.TypedArray(right) || left.length !== right.length || globalThis.Object.getPrototypeOf(left).constructor.name !== globalThis.Object.getPrototypeOf(right).constructor.name) return false return left.every((value, index) => Equal(value, right[index])) } - function Value(left: ValueType, right: unknown): any { return left === right } - export function Equal(left: T, right: unknown): right is T { if (Is.Object(left)) { return Object(left, right) diff --git a/src/hash/hash.ts b/src/value/hash.ts similarity index 83% rename from src/hash/hash.ts rename to src/value/hash.ts index dbe5123a2..5231ecce3 100644 --- a/src/hash/hash.ts +++ b/src/value/hash.ts @@ -31,7 +31,6 @@ export class ValueHashError extends Error { super(`Hash: Unable to hash value`) } } - export namespace ValueHash { enum ByteMarker { Undefined, @@ -43,123 +42,122 @@ export namespace ValueHash { Array, Date, Uint8Array, + Symbol, + BigInt, } // ---------------------------------------------------- // State // ---------------------------------------------------- - let Hash = globalThis.BigInt('14695981039346656037') const [Prime, Size] = [globalThis.BigInt('1099511628211'), globalThis.BigInt('2') ** globalThis.BigInt('64')] const Bytes = globalThis.Array.from({ length: 256 }).map((_, i) => globalThis.BigInt(i)) const F64 = new globalThis.Float64Array(1) const F64In = new globalThis.DataView(F64.buffer) const F64Out = new globalThis.Uint8Array(F64.buffer) - // ---------------------------------------------------- // Guards // ---------------------------------------------------- - function IsDate(value: unknown): value is Date { return value instanceof globalThis.Date } - function IsUint8Array(value: unknown): value is Uint8Array { return value instanceof globalThis.Uint8Array } - function IsArray(value: unknown): value is Array { return globalThis.Array.isArray(value) } - function IsBoolean(value: unknown): value is boolean { return typeof value === 'boolean' } - function IsNull(value: unknown): value is null { return value === null } - function IsNumber(value: unknown): value is number { return typeof value === 'number' } - + function IsSymbol(value: unknown): value is symbol { + return typeof value === 'symbol' + } + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } function IsObject(value: unknown): value is Record { return typeof value === 'object' && value !== null && !IsArray(value) && !IsDate(value) && !IsUint8Array(value) } - function IsString(value: unknown): value is string { return typeof value === 'string' } - function IsUndefined(value: unknown): value is undefined { return value === undefined } - // ---------------------------------------------------- // Encoding // ---------------------------------------------------- - function Array(value: Array) { - Fnv1A64(ByteMarker.Array) + FNV1A64(ByteMarker.Array) for (const item of value) { Visit(item) } } - function Boolean(value: boolean) { - Fnv1A64(ByteMarker.Boolean) - Fnv1A64(value ? 1 : 0) + FNV1A64(ByteMarker.Boolean) + FNV1A64(value ? 1 : 0) + } + function BigInt(value: bigint) { + FNV1A64(ByteMarker.BigInt) + F64In.setBigInt64(0, value) + for (const byte of F64Out) { + FNV1A64(byte) + } } - function Date(value: Date) { - Fnv1A64(ByteMarker.Date) + FNV1A64(ByteMarker.Date) Visit(value.getTime()) } - function Null(value: null) { - Fnv1A64(ByteMarker.Null) + FNV1A64(ByteMarker.Null) } - function Number(value: number) { - Fnv1A64(ByteMarker.Number) + FNV1A64(ByteMarker.Number) F64In.setFloat64(0, value) for (const byte of F64Out) { - Fnv1A64(byte) + FNV1A64(byte) } } - function Object(value: Record) { - Fnv1A64(ByteMarker.Object) + FNV1A64(ByteMarker.Object) for (const key of globalThis.Object.keys(value).sort()) { Visit(key) Visit(value[key]) } } - function String(value: string) { - Fnv1A64(ByteMarker.String) + FNV1A64(ByteMarker.String) for (let i = 0; i < value.length; i++) { - Fnv1A64(value.charCodeAt(i)) + FNV1A64(value.charCodeAt(i)) } } - + function Symbol(value: symbol) { + FNV1A64(ByteMarker.Symbol) + Visit(value.description) + } function Uint8Array(value: Uint8Array) { - Fnv1A64(ByteMarker.Uint8Array) + FNV1A64(ByteMarker.Uint8Array) for (let i = 0; i < value.length; i++) { - Fnv1A64(value[i]) + FNV1A64(value[i]) } } - function Undefined(value: undefined) { - return Fnv1A64(ByteMarker.Undefined) + return FNV1A64(ByteMarker.Undefined) } - function Visit(value: any) { if (IsArray(value)) { Array(value) } else if (IsBoolean(value)) { Boolean(value) + } else if (IsBigInt(value)) { + BigInt(value) } else if (IsDate(value)) { Date(value) } else if (IsNull(value)) { @@ -170,6 +168,8 @@ export namespace ValueHash { Object(value) } else if (IsString(value)) { String(value) + } else if (IsSymbol(value)) { + Symbol(value) } else if (IsUint8Array(value)) { Uint8Array(value) } else if (IsUndefined(value)) { @@ -178,12 +178,10 @@ export namespace ValueHash { throw new ValueHashError(value) } } - - function Fnv1A64(byte: number) { + function FNV1A64(byte: number) { Hash = Hash ^ Bytes[byte] Hash = (Hash * Prime) % Size } - /** Creates a FNV1A-64 non cryptographic hash of the given value */ export function Create(value: unknown) { Hash = globalThis.BigInt('14695981039346656037') diff --git a/src/value/index.ts b/src/value/index.ts index 5e48c738f..5598e30ab 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -26,7 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { ValueError, ValueErrorType } from '../errors/index' +export { ValueError, ValueErrorIterator, ValueErrorType } from '../errors/index' +export { ValueHash } from './hash' export { Edit, Insert, Update, Delete } from './delta' export * from './pointer' export * from './value' diff --git a/src/value/value.ts b/src/value/value.ts index 373005b7a..b76700865 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -27,11 +27,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { ValueErrors, ValueError } from '../errors/index' -import { ValueHash } from '../hash/index' +import { ValueErrors, ValueErrorIterator, ValueError } from '../errors/index' +import { ValueHash } from './hash' import { ValueEqual } from './equal' import { ValueCast } from './cast' import { ValueClone } from './clone' +import { ValueConvert } from './convert' import { ValueCreate } from './create' import { ValueCheck } from './check' import { ValueDelta, Edit } from './delta' @@ -46,7 +47,6 @@ export namespace Value { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] return ValueCast.Cast(schema, references, value) } - /** Creates a value from the given type */ export function Create(schema: T, references: [...R]): Types.Static /** Creates a value from the given type */ @@ -55,7 +55,6 @@ export namespace Value { const [schema, references] = args.length === 2 ? [args[0], args[1]] : [args[0], []] return ValueCreate.Create(schema, references) } - /** Returns true if the value matches the given type. */ export function Check(schema: T, references: [...R], value: unknown): value is Types.Static /** Returns true if the value matches the given type. */ @@ -64,36 +63,38 @@ export namespace Value { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] return ValueCheck.Check(schema, references, value) } - + /** Converts any type mismatched values to their target type if a conversion is possible. */ + export function Convert(schema: T, references: [...R], value: unknown): unknown + /** Converts any type mismatched values to their target type if a conversion is possible. */ + export function Convert(schema: T, value: unknown): unknown + export function Convert(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + return ValueConvert.Convert(schema, references, value) + } /** Returns a structural clone of the given value */ export function Clone(value: T): T { return ValueClone.Clone(value) } - /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, references: [...R], value: unknown): IterableIterator + export function Errors(schema: T, references: [...R], value: unknown): ValueErrorIterator /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, value: unknown): IterableIterator - export function* Errors(...args: any[]) { + export function Errors(schema: T, value: unknown): ValueErrorIterator + export function Errors(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - yield* ValueErrors.Errors(schema, references, value) + return ValueErrors.Errors(schema, references, value) as ValueErrorIterator } - /** Returns true if left and right values are structurally equal */ export function Equal(left: T, right: unknown): right is T { return ValueEqual.Equal(left, right) } - /** Returns edits to transform the current value into the next value */ export function Diff(current: unknown, next: unknown): Edit[] { return ValueDelta.Diff(current, next) } - /** Returns a FNV1A-64 non cryptographic hash of the given value */ export function Hash(value: unknown): bigint { return ValueHash.Create(value) } - /** Returns a new value with edits applied to the given value */ export function Patch(current: unknown, edits: Edit[]): T { return ValueDelta.Patch(current, edits) as T diff --git a/test/runtime/compiler/any.ts b/test/runtime/compiler/any.ts index 7519db00f..949147d7f 100644 --- a/test/runtime/compiler/any.ts +++ b/test/runtime/compiler/any.ts @@ -30,4 +30,12 @@ describe('type/compiler/Any', () => { const T = Type.Any() Ok(T, undefined) }) + it('Should validate bigint', () => { + const T = Type.Any() + Ok(T, BigInt(1)) + }) + it('Should validate symbol', () => { + const T = Type.Any() + Ok(T, Symbol(1)) + }) }) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index adff196f9..a2ccad658 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -39,10 +39,10 @@ describe('type/compiler/Array', () => { ]) }) - it('Should not validate for an array of intersection types when passing additionalProperties false', () => { + it('Should not validate for an array of composite types when passing additionalProperties false', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.String() }) - const C = Type.Intersect([A, B], { additionalProperties: false }) + const C = Type.Composite([A, B], { additionalProperties: false }) const T = Type.Array(C) Fail(T, [ { a: 'hello', b: 'hello' }, diff --git a/test/runtime/compiler/bigint.ts b/test/runtime/compiler/bigint.ts new file mode 100644 index 000000000..484573395 --- /dev/null +++ b/test/runtime/compiler/bigint.ts @@ -0,0 +1,84 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/BigInt', () => { + it('Should not validate number', () => { + const T = Type.BigInt() + Fail(T, 3.14) + }) + it('Should not validate NaN', () => { + const T = Type.BigInt() + Fail(T, NaN) + }) + it('Should not validate +Infinity', () => { + const T = Type.BigInt() + Fail(T, Infinity) + }) + it('Should not validate -Infinity', () => { + const T = Type.BigInt() + Fail(T, -Infinity) + }) + it('Should not validate integer', () => { + const T = Type.BigInt() + Fail(T, 1) + }) + it('Should not validate string', () => { + const T = Type.BigInt() + Fail(T, 'hello') + }) + it('Should not validate boolean', () => { + const T = Type.BigInt() + Fail(T, true) + }) + it('Should not validate array', () => { + const T = Type.BigInt() + Fail(T, [1, 2, 3]) + }) + it('Should not validate object', () => { + const T = Type.BigInt() + Fail(T, { a: 1, b: 2 }) + }) + it('Should not validate null', () => { + const T = Type.BigInt() + Fail(T, null) + }) + it('Should not validate undefined', () => { + const T = Type.BigInt() + Fail(T, undefined) + }) + it('Should not validate symbol', () => { + const T = Type.BigInt() + Fail(T, Symbol()) + }) + it('Should validate bigint', () => { + const T = Type.BigInt() + Ok(T, BigInt(1)) + }) + it('Should validate minimum', () => { + const T = Type.BigInt({ minimum: BigInt(10) }) + Fail(T, BigInt(9)) + Ok(T, BigInt(10)) + }) + + it('Should validate maximum', () => { + const T = Type.BigInt({ maximum: BigInt(10) }) + Ok(T, BigInt(10)) + Fail(T, BigInt(11)) + }) + + it('Should validate Date exclusiveMinimum', () => { + const T = Type.BigInt({ exclusiveMinimum: BigInt(10) }) + Fail(T, BigInt(10)) + Ok(T, BigInt(11)) + }) + + it('Should validate Date exclusiveMaximum', () => { + const T = Type.BigInt({ exclusiveMaximum: BigInt(10) }) + Ok(T, BigInt(9)) + Fail(T, BigInt(10)) + }) + + it('Should not validate NaN', () => { + Fail(Type.Number(), NaN) + }) +}) diff --git a/test/runtime/compiler/boolean.ts b/test/runtime/compiler/boolean.ts index c0d7997fb..03f01d890 100644 --- a/test/runtime/compiler/boolean.ts +++ b/test/runtime/compiler/boolean.ts @@ -37,4 +37,12 @@ describe('type/compiler/Boolean', () => { const T = Type.Boolean() Fail(T, undefined) }) + it('Should not validate bigint', () => { + const T = Type.Boolean() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Boolean() + Fail(T, Symbol(1)) + }) }) diff --git a/test/runtime/compiler/composite.ts b/test/runtime/compiler/composite.ts new file mode 100644 index 000000000..5635e45fa --- /dev/null +++ b/test/runtime/compiler/composite.ts @@ -0,0 +1,73 @@ +import { Type, Static } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/Composite', () => { + it('Should compose two objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Composite([A, B], { additionalProperties: false }) + Ok(T, { a: 'hello', b: 42 }) + }) + it('Should compose with partial', () => { + const A = Type.Partial(Type.Object({ a: Type.Number() })) + const B = Type.Partial(Type.Object({ b: Type.Number() })) + const P = Type.Composite([A, B], { additionalProperties: false }) + Ok(P, { a: 1, b: 2 }) + }) + it('Should compose with overlapping same type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Number() }) + const P = Type.Composite([A, B]) + Ok(P, { a: 1 }) + Fail(P, { a: 'hello' }) + Fail(P, {}) + }) + it('Should compose with overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const T = Type.Composite([A, B]) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Fail(T, {}) + }) + it('Should compose with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const C = Type.Object({ a: Type.Boolean() }) + const D = Type.Object({ a: Type.Null() }) + const T = Type.Composite([A, B, C, D]) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Ok(T, { a: false }) + Ok(T, { a: null }) + Fail(T, { a: [] }) + Fail(T, {}) + }) + it('Should pick from composited type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Composite([A, B, C]) + const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) + }) + it('Should omit from composited type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Composite([A, B, C]) + const P = Type.Omit(T, ['z'], { additionalProperties: false }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should compose nested object properties', () => { + const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) + const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const T = Type.Composite([A, B]) + Ok(T, { x: { x: 1 } }) + Ok(T, { x: { x: 'hello' } }) + Fail(T, { x: { x: false } }) + }) +}) diff --git a/test/runtime/compiler/custom.ts b/test/runtime/compiler/custom.ts index 4cba073d9..4380a9e82 100644 --- a/test/runtime/compiler/custom.ts +++ b/test/runtime/compiler/custom.ts @@ -1,10 +1,8 @@ -import { Custom } from '@sinclair/typebox/custom' -import { Type, Kind } from '@sinclair/typebox' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Custom', () => { - Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') - + TypeRegistry.Set('BigInt', (schema, value) => typeof value === 'bigint') it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) Ok(T, 1n) diff --git a/test/runtime/compiler/date.ts b/test/runtime/compiler/date.ts index 943bfa0e2..dfda22522 100644 --- a/test/runtime/compiler/date.ts +++ b/test/runtime/compiler/date.ts @@ -34,6 +34,14 @@ describe('type/compiler/Date', () => { const T = Type.Date() Ok(T, new Date()) }) + it('Should not validate bigint', () => { + const T = Type.Date() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Date() + Fail(T, Symbol(1)) + }) it('Should not validate Date if is invalid', () => { const T = Type.Date() Fail(T, new Date('not-a-valid-date')) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index a842d535a..d04e227f4 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,6 +1,8 @@ import './any' import './array' +import './bigint' import './boolean' +import './composite' import './custom' import './date' import './unicode' @@ -11,6 +13,7 @@ import './keyof' import './literal' import './modifier' import './never' +import './not' import './null' import './number' import './object' @@ -26,6 +29,7 @@ import './ref' import './regex' import './required' import './string' +import './symbol' import './tuple' import './uint8array' import './union' diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts index 1d3b85dbb..598aa04b4 100644 --- a/test/runtime/compiler/integer.ts +++ b/test/runtime/compiler/integer.ts @@ -6,7 +6,18 @@ describe('type/compiler/Integer', () => { const T = Type.Integer() Fail(T, 3.14) }) - + it('Should not validate NaN', () => { + const T = Type.Integer() + Fail(T, NaN) + }) + it('Should not validate +Infinity', () => { + const T = Type.Integer() + Fail(T, Infinity) + }) + it('Should not validate -Infinity', () => { + const T = Type.Integer() + Fail(T, -Infinity) + }) it('Should validate integer', () => { const T = Type.Integer() Ok(T, 1) @@ -40,7 +51,14 @@ describe('type/compiler/Integer', () => { const T = Type.Integer() Fail(T, undefined) }) - + it('Should not validate bigint', () => { + const T = Type.Integer() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Integer() + Fail(T, Symbol(1)) + }) it('Should validate minimum', () => { const T = Type.Integer({ minimum: 10 }) Fail(T, 9) @@ -64,8 +82,4 @@ describe('type/compiler/Integer', () => { Ok(T, 9) Fail(T, 10) }) - - it('Should not validate NaN', () => { - Fail(Type.Integer(), NaN) - }) }) diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index 5cb524514..610dd7f30 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -2,82 +2,82 @@ import { Type, Static } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Intersect', () => { - it('Should intersect two objects', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Intersect([A, B], { additionalProperties: false }) - Ok(T, { a: 'hello', b: 42 }) + it('Should intersect number and number', () => { + const A = Type.Number() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Ok(T, 1) }) - - it('Should intersect with partial', () => { - const A = Type.Partial(Type.Object({ a: Type.Number() })) - const B = Type.Partial(Type.Object({ b: Type.Number() })) - const P = Type.Intersect([A, B], { additionalProperties: false }) - Ok(P, { a: 1, b: 2 }) - // ok(P, { a: 1 }) - // ok(P, { b: 1 }) - // ok(P, {}) - // fail(P, { c: 1 }) + it('Should not intersect string and number', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Fail(T, 1) + Fail(T, '1') }) - - it('Should intersect with overlapping same type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.Number() }) - const P = Type.Intersect([A, B]) - Ok(P, { a: 1 }) - Fail(P, { a: 'hello' }) - Fail(P, {}) + it('Should intersect two objects', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 1 }) }) - - it('Should intersect with overlapping varying type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.String() }) - const T = Type.Intersect([A, B]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Fail(T, {}) + it('Should not intersect two objects with internal additionalProperties false', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const T = Type.Intersect([A, B], {}) + Fail(T, { x: 1, y: 1 }) }) - - it('Should intersect with deeply nest overlapping varying type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.String() }) - const C = Type.Object({ a: Type.Boolean() }) - const D = Type.Object({ a: Type.Null() }) - const T = Type.Intersect([A, B, C, D]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Ok(T, { a: false }) - Ok(T, { a: null }) - Fail(T, { a: [] }) - Fail(T, {}) + it('Should intersect two objects and mandate required properties', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 1 }) + Fail(T, { x: 1 }) + Fail(T, { y: 1 }) }) - - it('Should pick from intersected type', () => { + it('Should intersect two objects with unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) - const C = Type.Object({ z: Type.Number() }) - const T = Type.Intersect([A, B, C]) - const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) - Ok(P, { x: 1, y: 1 }) - Fail(P, { x: 1, y: 1, z: 1 }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 2, z: 1 }) }) - - it('Should omit from intersected type', () => { + it('Should intersect two objects and restrict unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) - const C = Type.Object({ z: Type.Number() }) - const T = Type.Intersect([A, B, C]) - const P = Type.Omit(T, ['z'], { additionalProperties: false }) - Ok(P, { x: 1, y: 1 }) - Fail(P, { x: 1, y: 1, z: 1 }) + const T = Type.Intersect([A, B], { unevaluatedProperties: false }) + Fail(T, { x: 1, y: 2, z: 1 }) }) - - it('Should intersect nested object properties', () => { - const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) - const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + it('Should intersect two objects and allow unevaluated properties of number', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) + Ok(T, { x: 1, y: 2, z: 3 }) + Fail(T, { x: 1, y: 2, z: '1' }) + }) + it('Should intersect two union objects with overlapping properties of the same type', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.Number() })]) + const T = Type.Intersect([A, B]) + Ok(T, { x: 1 }) + Fail(T, { x: '1' }) + }) + it('Should not intersect two union objects with overlapping properties of varying types', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.String() })]) + const T = Type.Intersect([A, B]) + Fail(T, { x: 1 }) + Fail(T, { x: '1' }) + }) + it('Should intersect two union objects with non-overlapping properties', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ y: Type.Number() })]) + const T = Type.Intersect([A, B]) + Ok(T, { x: 1, y: 1 }) + }) + it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => { + const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })]) + const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })]) const T = Type.Intersect([A, B]) - Ok(T, { x: { x: 1 } }) - Ok(T, { x: { x: 'hello' } }) - Fail(T, { x: { x: false } }) + Fail(T, { x: 1, y: 1 }) }) }) diff --git a/test/runtime/compiler/literal.ts b/test/runtime/compiler/literal.ts index a7251b789..ce81439d8 100644 --- a/test/runtime/compiler/literal.ts +++ b/test/runtime/compiler/literal.ts @@ -10,12 +10,10 @@ describe('type/compiler/Literal', () => { const T = Type.Literal('hello') Ok(T, 'hello') }) - it('Should validate literal boolean', () => { const T = Type.Literal(true) Ok(T, true) }) - it('Should not validate invalid literal number', () => { const T = Type.Literal(42) Fail(T, 43) @@ -28,13 +26,11 @@ describe('type/compiler/Literal', () => { const T = Type.Literal(false) Fail(T, true) }) - it('Should validate literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) Ok(T, 42) Ok(T, 'hello') }) - it('Should not validate invalid literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) Fail(T, 43) diff --git a/test/runtime/compiler/never.ts b/test/runtime/compiler/never.ts index a19036774..6cc53bb93 100644 --- a/test/runtime/compiler/never.ts +++ b/test/runtime/compiler/never.ts @@ -6,17 +6,14 @@ describe('type/compiler/Never', () => { const T = Type.Never() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Never() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Never() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Never() Fail(T, [1, 2, 3]) @@ -33,4 +30,12 @@ describe('type/compiler/Never', () => { const T = Type.Never() Fail(T, undefined) }) + it('Should not validate bigint', () => { + const T = Type.Never() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Never() + Fail(T, Symbol(1)) + }) }) diff --git a/test/runtime/compiler/not.ts b/test/runtime/compiler/not.ts new file mode 100644 index 000000000..27a07c5ba --- /dev/null +++ b/test/runtime/compiler/not.ts @@ -0,0 +1,38 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/Not', () => { + it('Should validate with not number', () => { + const T = Type.Not(Type.Number(), Type.String()) + Fail(T, 1) + Ok(T, 'A') + }) + it('Should validate with union left', () => { + // prettier-ignore + const T = Type.Not(Type.Union([ + Type.Literal('A'), + Type.Literal('B'), + Type.Literal('C') + ]), Type.String()) + Fail(T, 'A') + Fail(T, 'B') + Fail(T, 'C') + Ok(T, 'D') + }) + it('Should validate with union right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Union([ + Type.String(), + Type.Boolean() + ])) + Fail(T, 1) + Ok(T, 'A') + Ok(T, true) + }) + it('Should not validate with symmetric left right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Number()) + Fail(T, 1) + Fail(T, true) // not a number, but not a number either? + }) +}) diff --git a/test/runtime/compiler/null.ts b/test/runtime/compiler/null.ts index 1253ea64a..fcc8257fa 100644 --- a/test/runtime/compiler/null.ts +++ b/test/runtime/compiler/null.ts @@ -6,34 +6,36 @@ describe('type/compiler/Null', () => { const T = Type.Null() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Null() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Null() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Null() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Null() Fail(T, { a: 1, b: 2 }) }) - it('Should not validate null', () => { const T = Type.Null() Ok(T, null) }) - it('Should not validate undefined', () => { const T = Type.Null() Fail(T, undefined) }) + it('Should not validate bigint', () => { + const T = Type.Null() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Null() + Fail(T, Symbol(1)) + }) }) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts index 90a229f64..59081b8e3 100644 --- a/test/runtime/compiler/number.ts +++ b/test/runtime/compiler/number.ts @@ -6,12 +6,22 @@ describe('type/compiler/Number', () => { const T = Type.Number() Ok(T, 3.14) }) - + it('Should not validate NaN', () => { + const T = Type.Number() + Fail(T, NaN) + }) + it('Should not validate +Infinity', () => { + const T = Type.Number() + Fail(T, Infinity) + }) + it('Should not validate -Infinity', () => { + const T = Type.Number() + Fail(T, -Infinity) + }) it('Should validate integer', () => { const T = Type.Number() Ok(T, 1) }) - it('Should not validate string', () => { const T = Type.Number() Fail(T, 'hello') @@ -36,32 +46,32 @@ describe('type/compiler/Number', () => { const T = Type.Number() Fail(T, undefined) }) - + it('Should not validate bigint', () => { + const T = Type.Number() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Number() + Fail(T, Symbol(1)) + }) it('Should validate minimum', () => { const T = Type.Number({ minimum: 10 }) Fail(T, 9) Ok(T, 10) }) - it('Should validate maximum', () => { const T = Type.Number({ maximum: 10 }) Ok(T, 10) Fail(T, 11) }) - it('Should validate Date exclusiveMinimum', () => { const T = Type.Number({ exclusiveMinimum: 10 }) Fail(T, 10) Ok(T, 11) }) - it('Should validate Date exclusiveMaximum', () => { const T = Type.Number({ exclusiveMaximum: 10 }) Ok(T, 9) Fail(T, 10) }) - - it('Should not validate NaN', () => { - Fail(Type.Number(), NaN) - }) }) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 5a4bbe1e7..699b533b0 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -6,27 +6,34 @@ describe('type/compiler/Object', () => { const T = Type.Object({}) Fail(T, 42) }) - it('Should not validate a string', () => { const T = Type.Object({}) Fail(T, 'hello') }) - it('Should not validate a boolean', () => { const T = Type.Object({}) Fail(T, true) }) - it('Should not validate a null', () => { const T = Type.Object({}) Fail(T, null) }) - + it('Should not validate undefined', () => { + const T = Type.Object({}) + Fail(T, undefined) + }) + it('Should not validate bigint', () => { + const T = Type.Object({}) + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Object({}) + Fail(T, Symbol(1)) + }) it('Should not validate an array', () => { const T = Type.Object({}) Fail(T, [1, 2]) }) - it('Should validate with correct property values', () => { const T = Type.Object({ a: Type.Number(), @@ -43,7 +50,6 @@ describe('type/compiler/Object', () => { e: { x: 10, y: 20 }, }) }) - it('Should not validate with incorrect property values', () => { const T = Type.Object({ a: Type.Number(), @@ -60,7 +66,6 @@ describe('type/compiler/Object', () => { e: { x: 10, y: 20 }, }) }) - it('Should allow additionalProperties by default', () => { const T = Type.Object({ a: Type.Number(), @@ -72,7 +77,6 @@ describe('type/compiler/Object', () => { c: true, }) }) - it('Should not allow an empty object if minProperties is set to 1', () => { const T = Type.Object( { @@ -85,7 +89,6 @@ describe('type/compiler/Object', () => { Ok(T, { b: 'hello' }) Fail(T, {}) }) - it('Should not allow 3 properties if maxProperties is set to 2', () => { const T = Type.Object( { @@ -103,7 +106,6 @@ describe('type/compiler/Object', () => { c: true, }) }) - it('Should not allow additionalProperties if additionalProperties is false', () => { const T = Type.Object( { @@ -118,7 +120,6 @@ describe('type/compiler/Object', () => { c: true, }) }) - it('Should not allow properties for an empty object when additionalProperties is false', () => { const T = Type.Object({}, { additionalProperties: false }) Ok(T, {}) @@ -139,7 +140,6 @@ describe('type/compiler/Object', () => { '!@#$%^&*(': 4, }) }) - it('Should validate schema additional properties of string', () => { const T = Type.Object( { @@ -227,4 +227,16 @@ describe('type/compiler/Object', () => { Ok(T, { x: undefined }) Ok(T, {}) }) + it('Should not check undefined for optional property of number', () => { + const T = Type.Object({ x: Type.Optional(Type.Number()) }) + Ok(T, { x: 1 }) + Ok(T, {}) + Fail(T, { x: undefined }) + }) + it('Should check undefined for optional property of undefined', () => { + const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) + Fail(T, { x: 1 }) + Ok(T, {}) + Ok(T, { x: undefined }) + }) }) diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts index 42634995f..9d4ed2723 100644 --- a/test/runtime/compiler/omit.ts +++ b/test/runtime/compiler/omit.ts @@ -57,4 +57,30 @@ describe('type/compiler/Omit', () => { Ok(T, { z: 0 }) Fail(T, { x: 0, y: 0, z: 0 }) }) + it('Should support Omit of Literal', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, Type.Literal('x')) + Ok(T, { y: 1, z: 1 }) + Fail(T, { x: 1, y: 1, z: 1 }) + }) + it('Should support Omit of Never', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Omit(A, Type.Never()) + Fail(T, { y: 1, z: 1 }) + Ok(T, { x: 1, y: 1, z: 1 }) + }) }) diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts index e6c9eb805..dd8f234b6 100644 --- a/test/runtime/compiler/pick.ts +++ b/test/runtime/compiler/pick.ts @@ -57,4 +57,30 @@ describe('type/compiler/Pick', () => { Ok(T, { x: 0, y: 0 }) Fail(T, { x: 0, y: 0, z: 0 }) }) + it('Should support Pick of Literal', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, Type.Literal('x')) + Ok(T, { x: 1 }) + Fail(T, { x: 1, y: 1, z: 1 }) + }) + it('Should support Pick of Never', () => { + const A = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { additionalProperties: false }, + ) + const T = Type.Pick(A, Type.Never()) + Fail(T, { x: 1, y: 1, z: 1 }) + Ok(T, {}) + }) }) diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index d73a14848..d12e65daa 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -136,4 +136,12 @@ describe('type/compiler/Record', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) + it('Should fail record with Date', () => { + const T = Type.Record(Type.String(), Type.String()) + Fail(T, new Date()) + }) + it('Should fail record with Uint8Array', () => { + const T = Type.Record(Type.String(), Type.String()) + Fail(T, new Uint8Array()) + }) }) diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index 632ac0fe7..ef23a872e 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -45,26 +45,38 @@ describe('type/compiler/Ref', () => { }) it('Should de-reference object property schema', () => { - const R = Type.Object( + const T = Type.Object( { name: Type.String(), }, { $id: 'R' }, ) - const T = Type.Object( + const R = Type.Object( { x: Type.Number(), y: Type.Number(), z: Type.Number(), - r: Type.Optional(Type.Ref(R)), + r: Type.Optional(Type.Ref(T)), }, { $id: 'T' }, ) - Ok(T, { x: 1, y: 2, z: 3 }, [R]) - Ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) - Fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) - Fail(T, { x: 1, y: 2, z: 3, r: {} }, [R]) + Ok(R, { x: 1, y: 2, z: 3 }, [T]) + Ok(R, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [T]) + Fail(R, { x: 1, y: 2, z: 3, r: { name: 1 } }, [T]) + Fail(R, { x: 1, y: 2, z: 3, r: {} }, [T]) + }) + + it('Should reference recursive schema', () => { + const T = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const R = Type.Ref(T) + Ok(R, { id: '', nodes: [{ id: '', nodes: [] }] }, [T]) + Fail(R, { id: '', nodes: [{ id: 1, nodes: [] }] }, [T]) }) }) diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts index baff1e18e..0f43dc025 100644 --- a/test/runtime/compiler/string.ts +++ b/test/runtime/compiler/string.ts @@ -30,7 +30,14 @@ describe('type/compiler/String', () => { const T = Type.String() Fail(T, undefined) }) - + it('Should not validate bigint', () => { + const T = Type.String() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.String() + Fail(T, Symbol(1)) + }) it('Should validate string format as email', () => { const T = Type.String({ format: 'email' }) Ok(T, 'name@domain.com') diff --git a/test/runtime/compiler/symbol.ts b/test/runtime/compiler/symbol.ts new file mode 100644 index 000000000..fcca5a548 --- /dev/null +++ b/test/runtime/compiler/symbol.ts @@ -0,0 +1,42 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/Symbol', () => { + it('Should not validate a boolean', () => { + const T = Type.Symbol() + Fail(T, true) + Fail(T, false) + }) + it('Should not validate a number', () => { + const T = Type.Symbol() + Fail(T, 1) + }) + it('Should not validate a string', () => { + const T = Type.Symbol() + Fail(T, 'true') + }) + it('Should not validate an array', () => { + const T = Type.Symbol() + Fail(T, [true]) + }) + it('Should not validate an object', () => { + const T = Type.Symbol() + Fail(T, {}) + }) + it('Should not validate an null', () => { + const T = Type.Symbol() + Fail(T, null) + }) + it('Should not validate an undefined', () => { + const T = Type.Symbol() + Fail(T, undefined) + }) + it('Should not validate bigint', () => { + const T = Type.Symbol() + Fail(T, BigInt(1)) + }) + it('Should not validate symbol', () => { + const T = Type.Symbol() + Ok(T, Symbol(1)) + }) +}) diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index c242768a3..070d6092b 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -1,7 +1,6 @@ import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value } from '@sinclair/typebox/value' -import { TSchema } from '@sinclair/typebox' -import { Format } from 'src/format' +import { TSchema, FormatRegistry } from '@sinclair/typebox' // ------------------------------------------------------------------------- // Test Formats: https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts @@ -59,9 +58,9 @@ function isDateTime(str: string, strictTimeZone?: boolean): boolean { // Use Formats // ------------------------------------------------------------------------- -Format.Set('email', (value) => EMAIL.test(value)) -Format.Set('uuid', (value) => UUID.test(value)) -Format.Set('date-time', (value) => isDateTime(value, true)) +FormatRegistry.Set('email', (value) => EMAIL.test(value)) +FormatRegistry.Set('uuid', (value) => UUID.test(value)) +FormatRegistry.Set('date-time', (value) => isDateTime(value, true)) export function Ok(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) diff --git a/test/runtime/compiler/void.ts b/test/runtime/compiler/void.ts index a5e80992e..4963c0e4e 100644 --- a/test/runtime/compiler/void.ts +++ b/test/runtime/compiler/void.ts @@ -6,34 +6,32 @@ describe('type/compiler/Void', () => { const T = Type.Void() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Void() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Void() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Void() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Void() Fail(T, { a: 1, b: 2 }) }) - it('Should validate null', () => { - const T = Type.Null() - Ok(T, null) + const T = Type.Void() + Fail(T, null) }) - - it('Should not validate undefined', () => { + it('Should validate undefined', () => { + const T = Type.Void() + Ok(T, undefined) + }) + it('Should validate void 0', () => { const T = Type.Void() - Fail(T, undefined) + Ok(T, void 0) }) }) diff --git a/test/runtime/conditional/any.ts b/test/runtime/conditional/any.ts deleted file mode 100644 index b450886d9..000000000 --- a/test/runtime/conditional/any.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Any', () => { - it('Should extend Any', () => { - type T = any extends any ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - it('Should extend Unknown', () => { - type T = any extends unknown ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = any extends string ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.String()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Boolean', () => { - type T = any extends boolean ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Number', () => { - type T = any extends number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Number()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Integer', () => { - type T = any extends number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Array 1', () => { - type T = any extends Array ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Array 2', () => { - type T = any extends Array ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Tuple', () => { - type T = any extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Object 1', () => { - type T = any extends object ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Object 2', () => { - type T = any extends {} ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Object 3', () => { - type T = any extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Union 1', () => { - type T = any extends number | string ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Union 2', () => { - type T = any extends any | number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - it('Should extend Null', () => { - type T = any extends null ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Null()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Undefined', () => { - type T = any extends undefined ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.Union) - }) - it('Should extend Void', () => { - type T = any extends void ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Void()) - Assert.deepEqual(R, StructuralResult.Union) - }) - - it('Should extend Date', () => { - type T = any extends Date ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Date()) - Assert.deepEqual(R, StructuralResult.Union) - }) -}) diff --git a/test/runtime/conditional/array.ts b/test/runtime/conditional/array.ts deleted file mode 100644 index f9ca50ab3..000000000 --- a/test/runtime/conditional/array.ts +++ /dev/null @@ -1,316 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Array', () => { - // ---------------------------------------------- - // Generic Varying - // ---------------------------------------------- - - it('Should extend Array Varying 1', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array Varying 2', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array Varying 3', () => { - type T = Array extends Array ? 1 : 2 // 1 - const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array Varying 4', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Number()), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ---------------------------------------------- - // Any - // ---------------------------------------------- - it('Should extend Any', () => { - type T = Array extends any ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = Array extends unknown ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = Array extends string ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = Array extends boolean ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = Array extends number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = Array extends number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array 2', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array 3', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Tuple', () => { - type T = Array extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = Array extends object ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = Array extends {} ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 3', () => { - type T = Array extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 4', () => { - type T = Array extends { length: '1' } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 5', () => { - type T = Array extends { length: number } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 1', () => { - type T = Array extends number | string ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = Array extends any | number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 4', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 5', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = Array extends null ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = Array extends undefined ? 1 : 2 - const R = Structural.Check(Type.Array(Type.Any()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ---------------------------------------------- - // Constrained - // ---------------------------------------------- - - it('Should extend constrained Any', () => { - type T = Array extends any ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Unknown', () => { - type T = Array extends unknown ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained String', () => { - type T = Array extends string ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Boolean', () => { - type T = Array extends boolean ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Number', () => { - type T = Array extends number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Integer', () => { - type T = Array extends number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Array 1', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Array 2', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Array 3', () => { - type T = Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Tuple', () => { - type T = Array extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Object 1', () => { - type T = Array extends object ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Object 2', () => { - type T = Array extends {} ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Object 3', () => { - type T = Array extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Object 4', () => { - type T = Array extends { length: '1' } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Object 5', () => { - type T = Array extends { length: number } ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 1', () => { - type T = Array extends number | string ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Union 2', () => { - type T = Array extends any | number ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 3', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 4', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 5', () => { - type T = Array extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Null', () => { - type T = Array extends null ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Undefined', () => { - type T = Array extends undefined ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Void', () => { - type T = Array extends void ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = Array extends Date ? 1 : 2 - const R = Structural.Check(Type.Array(Type.String()), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/boolean.ts b/test/runtime/conditional/boolean.ts deleted file mode 100644 index 4699a972b..000000000 --- a/test/runtime/conditional/boolean.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Boolean', () => { - it('Should extend Any', () => { - type T = boolean extends any ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = boolean extends string ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = boolean extends boolean ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Number', () => { - type T = boolean extends number ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = boolean extends number ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = boolean extends Array ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = boolean extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = boolean extends Record ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = boolean extends {} ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = boolean extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = boolean extends object ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = boolean extends number | string ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = boolean extends any | number ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = boolean extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = boolean extends null ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = boolean extends undefined ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = boolean extends void ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = boolean extends Date ? 1 : 2 - const R = Structural.Check(Type.Boolean(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/constructor.ts b/test/runtime/conditional/constructor.ts deleted file mode 100644 index 38cd73be6..000000000 --- a/test/runtime/conditional/constructor.ts +++ /dev/null @@ -1,250 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Constructor', () => { - it('Should extend Function', () => { - type T = (new () => number) extends () => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Constructor 1', () => { - type T = (new () => number) extends new () => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 2', () => { - type T = (new () => any) extends new () => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 3', () => { - type T = (new () => number) extends new () => any ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 4', () => { - type T = (new (a: number) => number) extends new () => any ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Constructor 5', () => { - type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 6', () => { - type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Constructor 7', () => { - type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Constructor 8', () => { - type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 9', () => { - type T = (new () => number) extends new () => any ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 9', () => { - type T = (new () => any) extends new () => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 10', () => { - type T = (new () => Array) extends new () => object ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - it('Should extend Constructor 11', () => { - type T = (new () => Array) extends new () => object ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 12', () => { - type T = (new () => object) extends new () => Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Constructor 13', () => { - type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 14', () => { - type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 - const R = Structural.Check(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 15', () => { - type T = (new () => any) extends new () => unknown ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Constructor 16', () => { - type T = (new () => unknown) extends new () => any ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Any', () => { - type T = (new () => number) extends any ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = (new () => number) extends string ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = (new () => number) extends boolean ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = (new () => number) extends number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = (new () => number) extends number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = (new () => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 2', () => { - type T = (new () => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 3', () => { - type T = (new () => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = (new () => number) extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = (() => number) extends Record ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = (new () => number) extends object ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = (new () => number) extends {} ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 3', () => { - type T = (new () => number) extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 4', () => { - type T = (new () => number) extends { length: '1' } ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = (new () => number) extends number | string ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = (new () => number) extends any | number ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = (new () => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 4', () => { - type T = (new () => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 5', () => { - type T = (new () => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = (new () => number) extends null ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = (new () => number) extends undefined ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = (new () => number) extends void ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = (new () => number) extends Date ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/date.ts b/test/runtime/conditional/date.ts deleted file mode 100644 index 29920d23a..000000000 --- a/test/runtime/conditional/date.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Date', () => { - it('Should extend Any', () => { - type T = Date extends any ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = Date extends unknown ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = Date extends string ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = Date extends boolean ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = Date extends number ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = Date extends number ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = Date extends Array ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Tuple', () => { - type T = Date extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = Date extends Record ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = Date extends {} ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = Date extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = Date extends object ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = Date extends number | string ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = Date extends any | number ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = Date extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = Date extends null ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = Date extends undefined ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = Date extends void ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = Date extends Date ? 1 : 2 - const R = Structural.Check(Type.Date(), Type.Date()) - Assert.deepEqual(R, StructuralResult.True) - }) -}) diff --git a/test/runtime/conditional/function.ts b/test/runtime/conditional/function.ts deleted file mode 100644 index 64eb9f080..000000000 --- a/test/runtime/conditional/function.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Function', () => { - it('Should extend Constructor 1', () => { - type T = (() => number) extends new () => number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Function 1', () => { - type T = (() => number) extends () => number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Function([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 2', () => { - type T = (() => any) extends () => number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 3', () => { - type T = (() => number) extends () => any ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 4', () => { - type T = ((a: number) => number) extends () => any ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Function 5', () => { - type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 6', () => { - type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Function 7', () => { - type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Function 8', () => { - type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 9', () => { - type T = (() => number) extends () => any ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Function([], Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 9', () => { - type T = (() => any) extends () => number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 10', () => { - type T = (() => Array) extends () => object ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 11', () => { - type T = (() => Array) extends () => object ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 12', () => { - type T = (() => object) extends () => Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Function 13', () => { - type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 14', () => { - type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 - const R = Structural.Check(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 15', () => { - type T = (() => any) extends () => unknown ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Function 16', () => { - type T = (() => unknown) extends () => any ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Any', () => { - type T = (() => number) extends any ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = (() => number) extends string ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = (() => number) extends boolean ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = (() => number) extends number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = (() => number) extends number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = (() => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 2', () => { - type T = (() => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 3', () => { - type T = (() => number) extends Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = (() => number) extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = (() => number) extends Record ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = (() => number) extends object ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = (() => number) extends {} ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 3', () => { - type T = (() => number) extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 4', () => { - type T = (() => number) extends { length: '1' } ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 5', () => { - type T = (() => number) extends { length: number } ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 1', () => { - type T = (() => number) extends number | string ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = (() => number) extends any | number ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = (() => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 4', () => { - type T = (() => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 5', () => { - type T = (() => number) extends any | Array ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = (() => number) extends null ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = (() => number) extends undefined ? 1 : 2 - const R = Structural.Check(Type.Function([], Type.Number()), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = (() => number) extends Date ? 1 : 2 - const R = Structural.Check(Type.Constructor([], Type.Number()), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/integer.ts b/test/runtime/conditional/integer.ts deleted file mode 100644 index 78b1bca8e..000000000 --- a/test/runtime/conditional/integer.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Integer', () => { - it('Should extend Any', () => { - type T = number extends any ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = number extends unknown ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = number extends string ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = number extends boolean ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = number extends number ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Number()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Integer', () => { - type T = number extends number ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array', () => { - type T = number extends Array ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = number extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = number extends Record ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = number extends {} ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = number extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = number extends object ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = number extends number | string ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number extends any | number ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = number extends null ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = number extends undefined ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = number extends undefined ? 1 : 2 - const R = Structural.Check(Type.Integer(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = number extends Date ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/literal.ts b/test/runtime/conditional/literal.ts deleted file mode 100644 index a699ebf42..000000000 --- a/test/runtime/conditional/literal.ts +++ /dev/null @@ -1,330 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Literal', () => { - // ------------------------------------------------------------------- - // String Literal - // ------------------------------------------------------------------- - it('Should extend Any (String)', () => { - type T = 'hello' extends any ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown (String)', () => { - type T = 'hello' extends unknown ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String (String)', () => { - type T = 'hello' extends string ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.String()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Boolean (String)', () => { - type T = 'hello' extends boolean ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number (String)', () => { - type T = 'hello' extends number ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer (String)', () => { - type T = 'hello' extends number ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array (String)', () => { - type T = 'hello' extends Array ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Tuple (String)', () => { - type T = 'hello' extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Object 1 (String)', () => { - type T = 'hello' extends {} ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2 (String)', () => { - type T = 'hello' extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3 (String)', () => { - type T = 'hello' extends object ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1 (String)', () => { - type T = 'hello' extends number | string ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2 (String)', () => { - type T = 'hello' extends any | number ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3 (String)', () => { - type T = 'hello' extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null (String)', () => { - type T = 'hello' extends null ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined (String)', () => { - type T = 'hello' extends undefined ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ------------------------------------------------------------------- - // Number Literal - // ------------------------------------------------------------------- - - it('Should extend Any (Number)', () => { - type T = 10 extends any ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown (Number)', () => { - type T = 10 extends unknown ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String (Number)', () => { - type T = 10 extends string ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean (Number)', () => { - type T = 10 extends boolean ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number (Number)', () => { - type T = 10 extends number ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Number()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Integer (Number)', () => { - type T = 10 extends number ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Integer()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array (Number)', () => { - type T = 10 extends Array ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Tuple (Number)', () => { - type T = 10 extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Object 1 (Number)', () => { - type T = 10 extends {} ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2 (Number)', () => { - type T = 10 extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3 (Number)', () => { - type T = 10 extends object ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1 (Number)', () => { - type T = 10 extends number | string ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2 (Number)', () => { - type T = 10 extends any | number ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3 (Number)', () => { - type T = 10 extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null (Number)', () => { - type T = 10 extends null ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined (Number)', () => { - type T = 10 extends undefined ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ------------------------------------------------------------------- - // Boolean Literal - // ------------------------------------------------------------------- - - it('Should extend Any (Boolean)', () => { - type T = true extends any ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown (Boolean)', () => { - type T = true extends unknown ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String (Boolean)', () => { - type T = true extends string ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean (Boolean)', () => { - type T = true extends boolean ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Number (Boolean)', () => { - type T = true extends number ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer (Boolean)', () => { - type T = true extends number ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array (Boolean)', () => { - type T = true extends Array ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple (Boolean)', () => { - type T = true extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 1', () => { - type T = 'hello' extends Record ? 1 : 2 - const R = Structural.Check(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 2', () => { - type T = 10 extends Record ? 1 : 2 - const R = Structural.Check(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 3', () => { - type T = true extends Record ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1 (Boolean)', () => { - type T = true extends {} ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2 (Boolean)', () => { - type T = true extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3 (Boolean)', () => { - type T = true extends object ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1 (Boolean)', () => { - type T = true extends number | string ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2 (Boolean)', () => { - type T = true extends any | number ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3 (Boolean)', () => { - type T = true extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null (Boolean)', () => { - type T = true extends null ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined (Boolean)', () => { - type T = true extends undefined ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = true extends void ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = true extends Date ? 1 : 2 - const R = Structural.Check(Type.Literal(true), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/null.ts b/test/runtime/conditional/null.ts deleted file mode 100644 index 1b1fc9661..000000000 --- a/test/runtime/conditional/null.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Null', () => { - it('Should extend Any', () => { - type T = null extends any ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = null extends unknown ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = null extends string ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = null extends boolean ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = null extends number ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = null extends number ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = null extends Array ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = null extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = null extends Record ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = null extends {} ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 2', () => { - type T = null extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = null extends object ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = null extends number | string ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = null extends any | number ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = null extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = null extends null ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Null()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Undefined', () => { - type T = null extends undefined ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = null extends void ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = null extends Date ? 1 : 2 - const R = Structural.Check(Type.Null(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/number.ts b/test/runtime/conditional/number.ts deleted file mode 100644 index 7cb136f35..000000000 --- a/test/runtime/conditional/number.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Number', () => { - it('Should extend Any', () => { - type T = number extends any ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = number extends unknown ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = number extends string ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = number extends boolean ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = number extends number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Number()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Integer', () => { - type T = number extends number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array', () => { - type T = number extends Array ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = number extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = number extends Record ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = number extends {} ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = number extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = number extends object ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = number extends number | string ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number extends any | number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = number extends null ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = number extends undefined ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = number extends void ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = number extends Date ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/object.ts b/test/runtime/conditional/object.ts deleted file mode 100644 index fb327cc40..000000000 --- a/test/runtime/conditional/object.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Object', () => { - // ---------------------------------------------------------- - // Record - // ---------------------------------------------------------- - - it('Should extend Record 1', () => { - type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 2', () => { - type T = { a: number; b: number } extends Record ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.String(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 3', () => { - type T = { a: number; b: number } extends Record ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.Number(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 4', () => { - type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 5', () => { - type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.String(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 6', () => { - type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 - const A = Type.Object({ a: Type.Number(), b: Type.Number() }) - const B = Type.Record(Type.Number(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) - }) - - // ---------------------------------------------------------- - // Standard - // ---------------------------------------------------------- - - it('Should extend Any', () => { - type T = { a: number } extends any ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = { a: number } extends string ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = { a: number } extends boolean ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = { a: number } extends number ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = { a: number } extends number ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = { a: number } extends Array ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Tuple', () => { - type T = { a: number } extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Object 1', () => { - type T = { a: number } extends {} ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = { a: number } extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = { a: number } extends object ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 1', () => { - type T = { a: number } extends number | string ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = { a: number } extends any | number ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = { a: number } extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = { a: number } extends null ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = { a: number } extends undefined ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = { a: number } extends void ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = { a: number } extends Date ? 1 : 2 - const R = Structural.Check(Type.Object({ a: Type.Number() }), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/promise.ts b/test/runtime/conditional/promise.ts deleted file mode 100644 index 68ad4b99d..000000000 --- a/test/runtime/conditional/promise.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Promise', () => { - // ---------------------------------------------- - // Generic Varying - // ---------------------------------------------- - - it('Should extend Promise Varying 1', () => { - type T = Promise extends Promise ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Promise(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Promise Varying 2', () => { - type T = Promise extends Promise ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.String()), Type.Promise(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Promise Varying 3', () => { - type T = Promise extends Promise ? 1 : 2 // 1 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Promise(Type.String())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Promise Varying 4', () => { - type T = Promise extends Promise ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Promise(Type.String())) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ---------------------------------------------- - // Any - // ---------------------------------------------- - - it('Should extend Any', () => { - type T = Promise extends any ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = Promise extends unknown ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = Promise extends string ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = Promise extends boolean ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = Promise extends number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = Promise extends number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = Promise extends Array ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = Promise extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = Promise extends Record ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = Promise extends {} ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = Promise extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = Promise extends object ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 1', () => { - type T = Promise extends number | string ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = Promise extends any | number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = Promise extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = Promise extends null ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = Promise extends undefined ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - // ---------------------------------------------- - // Constrained - // ---------------------------------------------- - - it('Should extend constrained Any', () => { - type T = Promise extends any ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Unknown', () => { - type T = Promise extends unknown ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained String', () => { - type T = Promise extends string ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Boolean', () => { - type T = Promise extends boolean ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Number', () => { - type T = Promise extends number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Any()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Integer', () => { - type T = Promise extends number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Array', () => { - type T = Promise extends Array ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Tuple', () => { - type T = Promise extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Object 1', () => { - type T = Promise extends {} ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Object 2', () => { - type T = Promise extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Object 3', () => { - type T = Promise extends object ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 1', () => { - type T = Promise extends number | string ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Union 2', () => { - type T = Promise extends any | number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend constrained Union 2', () => { - type T = Promise extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Null', () => { - type T = Promise extends null ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend constrained Undefined', () => { - type T = Promise extends undefined ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = Promise extends void ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = Promise extends Date ? 1 : 2 - const R = Structural.Check(Type.Promise(Type.Number()), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/string.ts b/test/runtime/conditional/string.ts deleted file mode 100644 index 783a25e91..000000000 --- a/test/runtime/conditional/string.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/String', () => { - it('Should extend Any', () => { - type T = string extends any ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = string extends unknown ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = string extends string ? 1 : 2 - const R = Structural.Check(Type.String(), Type.String()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Boolean', () => { - type T = string extends boolean ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = string extends number ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = string extends number ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = string extends Array ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = string extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 1', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 2', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 3', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 4', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 5', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Union([Type.Number(), Type.String()]))) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 6', () => { - type T = string extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = string extends {} ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = string extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = string extends object ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = number extends number | string ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number extends any | number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = number extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = number extends null ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = number extends undefined ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = number extends void ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = number extends Date ? 1 : 2 - const R = Structural.Check(Type.Number(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/tuple.ts b/test/runtime/conditional/tuple.ts deleted file mode 100644 index dcd96163d..000000000 --- a/test/runtime/conditional/tuple.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Tuple', () => { - it('Should extend Any', () => { - type T = [string, number] extends any ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = [string, number] extends string ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = [string, number] extends boolean ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = [string, number] extends number ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = [string, number] extends number ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = [string, number] extends Array ? 1 : 2 // 1 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array 2', () => { - type T = [string, number] extends Array ? 1 : 2 // 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 3', () => { - type T = [string, number] extends Array ? 1 : 2 // 1 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Array 4', () => { - type T = [string, number] extends Array ? 1 : 2 // 1 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Tuple 1', () => { - type T = [string, number] extends [string, number] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Tuple 2', () => { - type T = [string, number] extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple 3', () => { - type T = [string, any] extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple 4', () => { - type T = [string, number] extends [string, any] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Tuple 5', () => { - type T = [string, unknown] extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple 6', () => { - type T = [string, number] extends [string, unknown] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Tuple 7', () => { - type T = [] extends [string, number] ? 1 : 2 - const R = Structural.Check(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple 8', () => { - type T = [string, number] extends [] ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 1', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 2', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 3', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Record 4', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 5', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Record(Type.Number(), Type.Union([Type.Number(), Type.String()]))) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record 6', () => { - type T = [string, number] extends Record ? 1 : 2 - const R = Structural.Check(Type.String(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = [string, number] extends {} ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = [string, number] extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = [string, number] extends object ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 1', () => { - type T = [string, number] extends number | string ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = [string, number] extends any | number ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = [string, number] extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = [string, number] extends null ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = [string, number] extends undefined ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = [string, number] extends void ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = [string, number] extends Date ? 1 : 2 - const R = Structural.Check(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/uint8array.ts b/test/runtime/conditional/uint8array.ts deleted file mode 100644 index 984082d01..000000000 --- a/test/runtime/conditional/uint8array.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Uint8Array', () => { - it('Should extend Any', () => { - type T = Uint8Array extends any ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = Uint8Array extends unknown ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = Uint8Array extends string ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = Uint8Array extends boolean ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = Uint8Array extends number ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = Uint8Array extends number ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = Uint8Array extends Array ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = Uint8Array extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Record', () => { - type T = Uint8Array extends Record ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 1', () => { - type T = Uint8Array extends {} ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = Uint8Array extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = Uint8Array extends object ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = Uint8Array extends number | string ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = Uint8Array extends any | number ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = Uint8Array extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = Uint8Array extends null ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = Uint8Array extends undefined ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = Uint8Array extends void ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = Uint8Array extends Date ? 1 : 2 - const R = Structural.Check(Type.Uint8Array(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/undefined.ts b/test/runtime/conditional/undefined.ts deleted file mode 100644 index 2a03f11ba..000000000 --- a/test/runtime/conditional/undefined.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Undefined', () => { - it('Should extend Any', () => { - type T = undefined extends any ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = undefined extends string ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = undefined extends boolean ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = undefined extends number ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = undefined extends number ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = undefined extends Array ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = undefined extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = undefined extends {} ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 2', () => { - type T = undefined extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = undefined extends object ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = undefined extends number | string ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = undefined extends any | number ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = undefined extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = undefined extends null ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = undefined extends undefined ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Void', () => { - type T = undefined extends void ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Void()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Date', () => { - type T = undefined extends Date ? 1 : 2 - const R = Structural.Check(Type.Undefined(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/union.ts b/test/runtime/conditional/union.ts deleted file mode 100644 index bcd0da241..000000000 --- a/test/runtime/conditional/union.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Union', () => { - it('Should extend Any', () => { - type T = number | string extends any ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = number | string extends string ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = number | string extends boolean ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = number | string extends number ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = number | string extends number ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array', () => { - type T = number | string extends Array ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - it('Should extend Tuple', () => { - type T = number | string extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = number | string extends {} ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Object 2', () => { - type T = number | string extends { a: 10 } ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = number | string extends object ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = number | string extends number | string ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 2', () => { - type T = number | string extends any | number ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = number | string extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 4', () => { - type T = any | boolean extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - - it('Should extend Union 5', () => { - type T = any | string extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - - it('Should extend Union 6', () => { - type T = any | {} extends {} ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - - it('Should extend Union 7', () => { - type T = any extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.Union) - }) - - it('Should extend Union 8', () => { - type T = unknown | string extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 9', () => { - type T = unknown extends boolean | number ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Null', () => { - type T = number | string extends null ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = number | string extends undefined ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = number | string extends void ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void 2', () => { - type T = number | string | void extends void ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = number | string | void extends Date ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date 2', () => { - type T = Date | number | string | void extends Date ? 1 : 2 - const R = Structural.Check(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/unknown.ts b/test/runtime/conditional/unknown.ts deleted file mode 100644 index 51b0e2908..000000000 --- a/test/runtime/conditional/unknown.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Unknown', () => { - it('Should extend Any', () => { - type T = unknown extends any ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = unknown extends unknown ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = unknown extends string ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = unknown extends boolean ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = unknown extends number ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = unknown extends number ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = unknown extends Array ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 2', () => { - type T = unknown extends Array ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = unknown extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = unknown extends object ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 2', () => { - type T = unknown extends {} ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = unknown extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = unknown extends number | string ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = unknown extends any | number ? 1 : 2 // 1 - const R = Structural.Check(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = unknown extends unknown | number ? 1 : 2 // 1 - const R = Structural.Check(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 4', () => { - type T = unknown extends unknown | any ? 1 : 2 // 1 - const R = Structural.Check(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = unknown extends null ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = unknown extends undefined ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = unknown extends void ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Date', () => { - type T = unknown extends Date ? 1 : 2 - const R = Structural.Check(Type.Unknown(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/conditional/void.ts b/test/runtime/conditional/void.ts deleted file mode 100644 index 6e1ece4ca..000000000 --- a/test/runtime/conditional/void.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' - -describe('conditional/structural/Void', () => { - it('Should extend Any', () => { - type T = void extends any ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Unknown', () => { - type T = void extends unknown ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend String', () => { - type T = void extends string ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.String()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Boolean', () => { - type T = void extends boolean ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Number', () => { - type T = void extends number ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Integer', () => { - type T = void extends number ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 1', () => { - type T = void extends Array ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Array 2', () => { - type T = void extends Array ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Tuple', () => { - type T = void extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = void extends object ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 2', () => { - type T = void extends {} ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Object({})) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 3', () => { - type T = void extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 1', () => { - type T = void extends number | string ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Union 2', () => { - type T = void extends any | number ? 1 : 2 // 1 - const R = Structural.Check(Type.Void(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 3', () => { - type T = void extends unknown | number ? 1 : 2 // 1 - const R = Structural.Check(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Union 4', () => { - type T = void extends unknown | any ? 1 : 2 // 1 - const R = Structural.Check(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Null', () => { - type T = void extends null ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Undefined', () => { - type T = void extends undefined ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Void', () => { - type T = void extends void ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Void()) - Assert.deepEqual(R, StructuralResult.True) - }) - - it('Should extend Date', () => { - type T = void extends Date ? 1 : 2 - const R = Structural.Check(Type.Void(), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) - }) -}) diff --git a/test/runtime/errors/index.ts b/test/runtime/errors/index.ts index 3443ef957..41d3ac147 100644 --- a/test/runtime/errors/index.ts +++ b/test/runtime/errors/index.ts @@ -1 +1 @@ -// todo: implement once errors are standardized +import './iterator' diff --git a/test/runtime/errors/iterator.ts b/test/runtime/errors/iterator.ts new file mode 100644 index 000000000..3a61c3ad9 --- /dev/null +++ b/test/runtime/errors/iterator.ts @@ -0,0 +1,32 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrors } from '@sinclair/typebox/errors' +import { Assert } from '../assert' + +describe('errors/ValueErrorIterator', () => { + it('Should return undefined for non error', () => { + const R = ValueErrors.Errors(Type.Number(), [], 1).First() + Assert.equal(R, undefined) + }) + it('Should return a value error when error', () => { + const { type, path, message } = ValueErrors.Errors(Type.Number(), [], '').First()! + Assert.isTypeOf(type, 'number') + Assert.isTypeOf(path, 'string') + Assert.isTypeOf(message, 'string') + }) + it('Should yield empty array for non error', () => { + const R = [...ValueErrors.Errors(Type.Number(), [], 1)] + Assert.equal(R.length, 0) + }) + it('Should yield array with 1 error when error', () => { + const R = [...ValueErrors.Errors(Type.Number(), [], 'foo')] + Assert.equal(R.length, 1) + }) + it('Should yield array with N errors when error', () => { + // prettier-ignore + const R = [...ValueErrors.Errors(Type.Object({ + x: Type.Number(), + y: Type.Number() + }), [], {})] // require object to invoke internal check + Assert.equal(R.length > 1, true) + }) +}) diff --git a/test/runtime/format/format.ts b/test/runtime/format/format.ts deleted file mode 100644 index e867d5a41..000000000 --- a/test/runtime/format/format.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Format } from '@sinclair/typebox/format' -import { Assert } from '../assert/index' - -describe('format/Format', () => { - it('Should set format', () => { - Format.Set('test#format1', () => true) - }) - - it('Should get format', () => { - Format.Set('test#format2', () => true) - const format = Format.Get('test#format2') - Assert.equal(typeof format, 'function') - }) - - it('Should return true if exists', () => { - Format.Set('test#format3', () => true) - Assert.equal(Format.Has('test#format3'), true) - }) - - it('Should clear formats', () => { - Format.Set('test#format4', () => true) - Format.Clear() - Assert.equal(Format.Has('test#format4'), false) - }) -}) diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 6bd775e76..5a8bca7ab 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -1,9 +1,6 @@ import './compiler/index' -import './conditional/index' import './errors/index' -import './format/index' -import './guard/index' -import './hash/index' import './schema/index' import './system/index' +import './type/index' import './value/index' diff --git a/test/runtime/schema/composite.ts b/test/runtime/schema/composite.ts new file mode 100644 index 000000000..cca8cac71 --- /dev/null +++ b/test/runtime/schema/composite.ts @@ -0,0 +1,94 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../assert' +import { Ok, Fail } from './validate' + +describe('type/schema/Composite', () => { + it('Should compose two objects', () => { + const A = Type.Object({ a: Type.String() }) + const B = Type.Object({ b: Type.Number() }) + const T = Type.Composite([A, B], { additionalProperties: false }) + Ok(T, { a: 'hello', b: 42 }) + }) + + it('Should compose with partial', () => { + const A = Type.Partial(Type.Object({ a: Type.Number() })) + const B = Type.Partial(Type.Object({ b: Type.Number() })) + const P = Type.Composite([A, B], { additionalProperties: false }) + Ok(P, { a: 1, b: 2 }) + Ok(P, { a: 1 }) + Ok(P, { b: 1 }) + Ok(P, {}) + Fail(P, { c: 1 }) + }) + + it('Should compose with overlapping same type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Number() }) + const P = Type.Composite([A, B]) + Ok(P, { a: 1 }) + Fail(P, { a: 'hello' }) + Fail(P, {}) + }) + + it('Should compose with overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const T = Type.Composite([A, B]) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Fail(T, {}) + }) + + it('Should compose with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.String() }) + const C = Type.Object({ a: Type.Boolean() }) + const D = Type.Object({ a: Type.Null() }) + const T = Type.Composite([A, B, C, D]) + Ok(T, { a: 1 }) + Ok(T, { a: 'hello' }) + Ok(T, { a: false }) + Ok(T, { a: null }) + Fail(T, { a: [] }) + Fail(T, {}) + }) + + it('Should pick from composited type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Composite([A, B, C]) + const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should omit from composited type', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const C = Type.Object({ z: Type.Number() }) + const T = Type.Composite([A, B, C]) + const P = Type.Omit(T, ['z'], { additionalProperties: false }) + Ok(P, { x: 1, y: 1 }) + Fail(P, { x: 1, y: 1, z: 1 }) + }) + + it('Should compose nested object properties', () => { + const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) + const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const T = Type.Composite([A, B]) + Ok(T, { x: { x: 1 } }) + Ok(T, { x: { x: 'hello' } }) + Fail(T, { x: { x: false } }) + }) + + // todo: move to composition / type guard spec + it('Should compose and produce the same schema', () => { + const T = Type.Object({ + field: Type.Optional(Type.String()), + }) + const A = Type.Composite([T]) + const B = Type.Composite([T]) + Assert.deepEqual(A, B) + }) +}) diff --git a/test/runtime/schema/index.ts b/test/runtime/schema/index.ts index d0053aa86..705b1d169 100644 --- a/test/runtime/schema/index.ts +++ b/test/runtime/schema/index.ts @@ -1,6 +1,7 @@ import './any' import './array' import './boolean' +import './composite' import './date' import './enum' import './integer' @@ -9,6 +10,7 @@ import './keyof' import './literal' import './modifier' import './never' +import './not' import './null' import './number' import './object' diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts index e1671dbc3..d3de6c365 100644 --- a/test/runtime/schema/intersect.ts +++ b/test/runtime/schema/intersect.ts @@ -1,94 +1,83 @@ -import { Type } from '@sinclair/typebox' -import { Assert } from '../assert' +import { Type, Static } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/schema/Intersect', () => { - it('Should intersect two objects', () => { - const A = Type.Object({ a: Type.String() }) - const B = Type.Object({ b: Type.Number() }) - const T = Type.Intersect([A, B], { additionalProperties: false }) - Ok(T, { a: 'hello', b: 42 }) + it('Should intersect number and number', () => { + const A = Type.Number() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Ok(T, 1) }) - - it('Should intersect with partial', () => { - const A = Type.Partial(Type.Object({ a: Type.Number() })) - const B = Type.Partial(Type.Object({ b: Type.Number() })) - const P = Type.Intersect([A, B], { additionalProperties: false }) - Ok(P, { a: 1, b: 2 }) - Ok(P, { a: 1 }) - Ok(P, { b: 1 }) - Ok(P, {}) - Fail(P, { c: 1 }) + it('Should not intersect string and number', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Fail(T, 1) + Fail(T, '1') }) - - it('Should intersect with overlapping same type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.Number() }) - const P = Type.Intersect([A, B]) - Ok(P, { a: 1 }) - Fail(P, { a: 'hello' }) - Fail(P, {}) + it('Should intersect two objects', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 1 }) }) - - it('Should intersect with overlapping varying type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.String() }) - const T = Type.Intersect([A, B]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Fail(T, {}) + it('Should not intersect two objects with internal additionalProperties false', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const T = Type.Intersect([A, B], {}) + Fail(T, { x: 1, y: 1 }) }) - - it('Should intersect with deeply nest overlapping varying type', () => { - const A = Type.Object({ a: Type.Number() }) - const B = Type.Object({ a: Type.String() }) - const C = Type.Object({ a: Type.Boolean() }) - const D = Type.Object({ a: Type.Null() }) - const T = Type.Intersect([A, B, C, D]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Ok(T, { a: false }) - Ok(T, { a: null }) - Fail(T, { a: [] }) - Fail(T, {}) + it('Should intersect two objects and mandate required properties', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 1 }) + Fail(T, { x: 1 }) + Fail(T, { y: 1 }) }) - - it('Should pick from intersected type', () => { + it('Should intersect two objects with unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) - const C = Type.Object({ z: Type.Number() }) - const T = Type.Intersect([A, B, C]) - const P = Type.Pick(T, ['x', 'y'], { additionalProperties: false }) - Ok(P, { x: 1, y: 1 }) - Fail(P, { x: 1, y: 1, z: 1 }) + const T = Type.Intersect([A, B], {}) + Ok(T, { x: 1, y: 2, z: 1 }) }) - - it('Should omit from intersected type', () => { + it('Should intersect two objects and restrict unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) - const C = Type.Object({ z: Type.Number() }) - const T = Type.Intersect([A, B, C]) - const P = Type.Omit(T, ['z'], { additionalProperties: false }) - Ok(P, { x: 1, y: 1 }) - Fail(P, { x: 1, y: 1, z: 1 }) + const T = Type.Intersect([A, B], { unevaluatedProperties: false }) + Fail(T, { x: 1, y: 2, z: 1 }) }) - - it('Should intersect nested object properties', () => { - const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) - const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + it('Should intersect two objects and allow unevaluated properties of number', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) + Ok(T, { x: 1, y: 2, z: 3 }) + Fail(T, { x: 1, y: 2, z: '1' }) + }) + it('Should intersect two union objects with overlapping properties of the same type', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.Number() })]) const T = Type.Intersect([A, B]) - Ok(T, { x: { x: 1 } }) - Ok(T, { x: { x: 'hello' } }) - Fail(T, { x: { x: false } }) + Ok(T, { x: 1 }) + Fail(T, { x: '1' }) }) - - // todo: move to composition / type guard spec - it('Should intersect and produce the same schema', () => { - const T = Type.Object({ - field: Type.Optional(Type.String()), - }) - const A = Type.Intersect([T]) - const B = Type.Intersect([T]) - Assert.deepEqual(A, B) + it('Should not intersect two union objects with overlapping properties of varying types', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.String() })]) + const T = Type.Intersect([A, B]) + Fail(T, { x: 1 }) + Fail(T, { x: '1' }) + }) + it('Should intersect two union objects with non-overlapping properties', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ y: Type.Number() })]) + const T = Type.Intersect([A, B]) + Ok(T, { x: 1, y: 1 }) + }) + it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => { + const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })]) + const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })]) + const T = Type.Intersect([A, B]) + Fail(T, { x: 1, y: 1 }) }) }) diff --git a/test/runtime/schema/not.ts b/test/runtime/schema/not.ts new file mode 100644 index 000000000..c8ec36faf --- /dev/null +++ b/test/runtime/schema/not.ts @@ -0,0 +1,38 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/schema/Not', () => { + it('Should validate with not number', () => { + const T = Type.Not(Type.Number(), Type.String()) + Fail(T, 1) + Ok(T, 'A') + }) + it('Should validate with union left', () => { + // prettier-ignore + const T = Type.Not(Type.Union([ + Type.Literal('A'), + Type.Literal('B'), + Type.Literal('C') + ]), Type.String()) + Fail(T, 'A') + Fail(T, 'B') + Fail(T, 'C') + Ok(T, 'D') + }) + it('Should validate with union right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Union([ + Type.String(), + Type.Boolean() + ])) + Fail(T, 1) + Ok(T, 'A') + Ok(T, true) + }) + it('Should not validate with symmetric left right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Number()) + Fail(T, 1) + Fail(T, true) // not a number, but not a number either? + }) +}) diff --git a/test/runtime/schema/validate.ts b/test/runtime/schema/validate.ts index 7390db7bb..6e9cf4eac 100644 --- a/test/runtime/schema/validate.ts +++ b/test/runtime/schema/validate.ts @@ -1,4 +1,4 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' import { TSchema } from '@sinclair/typebox' diff --git a/test/runtime/system/AllowArrayObjects.ts b/test/runtime/system/AllowArrayObjects.ts deleted file mode 100644 index 064139813..000000000 --- a/test/runtime/system/AllowArrayObjects.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Ok, Fail } from '../compiler/validate' -import { TypeSystem } from '@sinclair/typebox/system' -import { Type } from '@sinclair/typebox' - -describe('system/TypeSystem/AllowArrayObjects', () => { - before(() => { - TypeSystem.AllowArrayObjects = true - }) - after(() => { - TypeSystem.AllowArrayObjects = false - }) - // --------------------------------------------------------------- - // Object - // --------------------------------------------------------------- - it('Should validate arrays with empty objects', () => { - const T = Type.Object({}) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays with objects with length property', () => { - const T = Type.Object({ length: Type.Number() }) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays with objects with additionalProperties false when array has no elements', () => { - const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) - Ok(T, []) - }) - it('Should not validate arrays with objects with additionalProperties false when array has elements', () => { - const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) - Fail(T, [0, 1, 2]) - }) - it('Should not validate arrays with objects when length property is string', () => { - const T = Type.Object({ length: Type.String() }) - Fail(T, [0, 1, 2]) - }) - // --------------------------------------------------------------- - // Record - // --------------------------------------------------------------- - it('Should validate arrays as Records with String Keys', () => { - const T = Type.Record(Type.String(), Type.Number()) - Ok(T, [0, 1, 2]) - }) - it('Should not validate arrays as Records with Number Keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) - Fail(T, [0, 1, 2]) - }) - it('Should not validate arrays as Records with Object Values', () => { - const T = Type.Record( - Type.String(), - Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }), - ) - Ok(T, [ - { x: 1, y: 1, z: 1 }, - { x: 1, y: 1, z: 1 }, - { x: 1, y: 1, z: 1 }, - ]) - }) -}) diff --git a/test/runtime/system/AllowNaN.ts b/test/runtime/system/AllowNaN.ts deleted file mode 100644 index 4795e7936..000000000 --- a/test/runtime/system/AllowNaN.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Ok, Fail } from '../compiler/validate' -import { TypeSystem } from '@sinclair/typebox/system' -import { Type } from '@sinclair/typebox' - -describe('system/TypeSystem/AllowNaN', () => { - before(() => { - TypeSystem.AllowNaN = true - }) - after(() => { - TypeSystem.AllowNaN = false - }) - // --------------------------------------------------------------- - // Number - // --------------------------------------------------------------- - it('Should validate number with NaN', () => { - const T = Type.Number() - Ok(T, NaN) - }) - - // --------------------------------------------------------------- - // Integer - // - // Note: The Number.isInteger() test will fail for NaN. Because - // of this we cannot reasonably override NaN handling for integers. - // --------------------------------------------------------------- - it('Should not validate integer with NaN', () => { - const T = Type.Integer() - Fail(T, NaN) - }) -}) diff --git a/test/runtime/system/CreateFormat.ts b/test/runtime/system/CreateFormat.ts deleted file mode 100644 index 751eb34e8..000000000 --- a/test/runtime/system/CreateFormat.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Ok, Fail } from '../compiler/validate' -import { Assert } from '../assert/index' -import { TypeSystem } from '@sinclair/typebox/system' -import { Type } from '@sinclair/typebox' - -describe('system/TypeSystem/CreateFormat', () => { - it('Should create and validate a format', () => { - TypeSystem.CreateFormat('CreateFormat0', (value) => value === value.toLowerCase()) - const T = Type.String({ format: 'CreateFormat0' }) - Ok(T, 'action') - Fail(T, 'ACTION') - }) - it('Should throw if registering the same format twice', () => { - TypeSystem.CreateFormat('CreateFormat1', (value) => true) - Assert.throws(() => TypeSystem.CreateFormat('CreateFormat1', (value) => true)) - }) -}) diff --git a/test/runtime/system/CreateType.ts b/test/runtime/system/CreateType.ts deleted file mode 100644 index 60626b7d2..000000000 --- a/test/runtime/system/CreateType.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Ok, Fail } from '../compiler/validate' -import { Assert } from '../assert/index' -import { TypeSystem } from '@sinclair/typebox/system' - -describe('system/TypeSystem/CreateType', () => { - it('Should create and validate a type', () => { - type BigNumberOptions = { minimum?: bigint; maximum?: bigint } - const BigNumber = TypeSystem.CreateType('CreateType0', (options, value) => { - if (typeof value !== 'bigint') return false - if (options.maximum !== undefined && value > options.maximum) return false - if (options.minimum !== undefined && value < options.minimum) return false - return true - }) - const T = BigNumber({ minimum: 10n, maximum: 20n }) - Ok(T, 15n) - Fail(T, 5n) - Fail(T, 25n) - }) - it('Should throw if registering the same type twice', () => { - TypeSystem.CreateType('CreateType1', () => true) - Assert.throws(() => TypeSystem.CreateType('CreateType1', () => true)) - }) -}) diff --git a/test/runtime/system/index.ts b/test/runtime/system/index.ts index 72bdb9c85..a3fc40210 100644 --- a/test/runtime/system/index.ts +++ b/test/runtime/system/index.ts @@ -1,4 +1 @@ -import './AllowArrayObjects' -import './AllowNaN' -import './CreateFormat' -import './CreateType' +import './system' diff --git a/test/runtime/system/system.ts b/test/runtime/system/system.ts new file mode 100644 index 000000000..5c41eda2c --- /dev/null +++ b/test/runtime/system/system.ts @@ -0,0 +1,169 @@ +import { Ok, Fail } from '../compiler/validate' +import { Assert } from '../assert/index' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystem/AllowNaN', () => { + before(() => { + TypeSystem.AllowNaN = true + }) + after(() => { + TypeSystem.AllowNaN = false + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should validate number with NaN', () => { + const T = Type.Number() + Ok(T, NaN) + }) + it('Should validate number with +Infinity', () => { + const T = Type.Number() + Ok(T, Infinity) + }) + it('Should validate number with -Infinity', () => { + const T = Type.Number() + Ok(T, -Infinity) + }) + // --------------------------------------------------------------- + // Integer + // + // Note: The Number.isInteger() test will fail for NaN. Because + // of this we cannot reasonably override NaN handling for integers. + // --------------------------------------------------------------- + it('Should not validate integer with NaN', () => { + const T = Type.Integer() + Fail(T, NaN) + }) + it('Should not validate integer with +Infinity', () => { + const T = Type.Integer() + Fail(T, Infinity) + }) + it('Should not validate integer with -Infinity', () => { + const T = Type.Integer() + Fail(T, -Infinity) + }) + // --------------------------------------------------------------- + // BigInt + // + // Note: We expect failures here as bigint isn't IEEE754 + // --------------------------------------------------------------- + it('Should not validate bigint with NaN', () => { + const T = Type.BigInt() + Fail(T, NaN) + }) + it('Should not validate bigint with +Infinity', () => { + const T = Type.BigInt() + Fail(T, Infinity) + }) + it('Should not validate bigint with -Infinity', () => { + const T = Type.BigInt() + Fail(T, -Infinity) + }) +}) + +describe('system/TypeSystem/AllowArrayObjects', () => { + before(() => { + TypeSystem.AllowArrayObjects = true + }) + after(() => { + TypeSystem.AllowArrayObjects = false + }) + // --------------------------------------------------------------- + // Object + // --------------------------------------------------------------- + it('Should validate arrays with empty objects', () => { + const T = Type.Object({}) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with length property', () => { + const T = Type.Object({ length: Type.Number() }) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with additionalProperties false when array has no elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Ok(T, []) + }) + it('Should not validate arrays with objects with additionalProperties false when array has elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Fail(T, [0, 1, 2]) + }) + it('Should not validate arrays with objects when length property is string', () => { + const T = Type.Object({ length: Type.String() }) + Fail(T, [0, 1, 2]) + }) + // --------------------------------------------------------------- + // Record + // --------------------------------------------------------------- + it('Should validate arrays as Records with String Keys', () => { + const T = Type.Record(Type.String(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should not validate arrays as Records with Number Keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + Fail(T, [0, 1, 2]) + }) + it('Should not validate arrays as Records with Object Values', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Ok(T, [ + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + ]) + }) +}) +describe('system/TypeSystem/AllowVoidNull', () => { + before(() => { + TypeSystem.AllowVoidNull = true + }) + after(() => { + TypeSystem.AllowVoidNull = false + }) + // --------------------------------------------------------------- + // Object + // --------------------------------------------------------------- + it('Should validate with null', () => { + const T = Type.Void() + Ok(T, null) + }) +}) + +describe('system/TypeSystem/Format', () => { + it('Should create and validate a format', () => { + TypeSystem.Format('CreateFormat0', (value) => value === value.toLowerCase()) + const T = Type.String({ format: 'CreateFormat0' }) + Ok(T, 'action') + Fail(T, 'ACTION') + }) + it('Should throw if registering the same format twice', () => { + TypeSystem.Format('CreateFormat1', (value) => true) + Assert.throws(() => TypeSystem.Format('CreateFormat1', (value) => true)) + }) +}) + +describe('system/TypeSystem/Type', () => { + it('Should create and validate a type', () => { + type BigNumberOptions = { minimum?: bigint; maximum?: bigint } + const BigNumber = TypeSystem.Type('CreateType0', (options, value) => { + if (typeof value !== 'bigint') return false + if (options.maximum !== undefined && value > options.maximum) return false + if (options.minimum !== undefined && value < options.minimum) return false + return true + }) + const T = BigNumber({ minimum: 10n, maximum: 20n }) + Ok(T, 15n) + Fail(T, 5n) + Fail(T, 25n) + }) + it('Should throw if registering the same type twice', () => { + TypeSystem.Type('CreateType1', () => true) + Assert.throws(() => TypeSystem.Type('CreateType1', () => true)) + }) +}) diff --git a/test/runtime/type/clone/clone.ts b/test/runtime/type/clone/clone.ts new file mode 100644 index 000000000..36568003b --- /dev/null +++ b/test/runtime/type/clone/clone.ts @@ -0,0 +1,147 @@ +// -------------------------------------------------------------------- +// $id deletion was omitted from 0.26.0 to reduce complexity overhead. +// -------------------------------------------------------------------- + +// import { Type } from '@sinclair/typebox' +// import { Assert } from '../../assert' + +// describe('type/Clone', () => { +// it('Should retain source type $id for cloned objects', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Object({ x: S, y: S }) +// Assert.equal(T.properties.x.$id, undefined) +// Assert.equal(T.properties.y.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should retain source type $id when composing objects with cloned arrays', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Function([S], S) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Array', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Array(S) +// Assert.equal(T.items.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Composite', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Composite([Type.Object({ a: S }), Type.Object({ b: S })]) +// Assert.equal(T.properties.a.$id, undefined) +// Assert.equal(T.properties.b.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Constructor', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Constructor([S], S) +// Assert.equal(T.parameters[0].$id, undefined) +// Assert.equal(T.returns.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Function', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Function([S], S) +// Assert.equal(T.parameters[0].$id, undefined) +// Assert.equal(T.returns.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Intersect', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Intersect([S, S]) +// Assert.equal(T.allOf[0].$id, undefined) +// Assert.equal(T.allOf[1].$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Intersect with unevaluatedProperties', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Intersect([S, S], { unevaluatedProperties: S }) +// // @ts-ignore +// Assert.equal(T.unevaluatedProperties.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Not', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Not(S, S) +// Assert.equal(T.allOf[0].not.$id, undefined) +// Assert.equal(T.allOf[1].$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Object', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Object({ x: S, y: S }) +// Assert.equal(T.properties.x.$id, undefined) +// Assert.equal(T.properties.y.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for nested Object', () => { +// const S = Type.String({ $id: 'S' }) +// const N = Type.Object({ s: S }, { $id: 'N' }) +// const T = Type.Object({ x: S, y: S, z: N }) +// Assert.equal(T.properties.x.$id, undefined) +// Assert.equal(T.properties.y.$id, undefined) +// Assert.equal(T.properties.z.$id, undefined) +// Assert.equal(T.properties.z.properties.s.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Object additionalProperties', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Object({}, { additionalProperties: S }) +// // @ts-ignore +// Assert.equal(T.additionalProperties!.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Promise', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Promise(S) +// Assert.equal(T.item.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Record', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Record(Type.String(), S) +// Assert.equal(T.patternProperties['^.*$'].$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Tuple', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Tuple([S, S]) +// Assert.equal(T.items![0]!.$id, undefined) +// Assert.equal(T.items![1]!.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should remove cloned $id for Union', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Union([S, S]) +// Assert.equal(T.anyOf[0]!.$id, undefined) +// Assert.equal(T.anyOf[1]!.$id, undefined) +// Assert.equal(S.$id, 'S') +// }) +// it('Should retain cloned $id for wrapped Recursive 1', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Object({ +// x: Type.Recursive( +// (Self) => +// Type.Object({ +// self: Type.Optional(Self), +// }), +// { $id: 'RecursiveClone' }, +// ), +// }) +// Assert.equal(T.properties.x.$id, 'RecursiveClone') +// Assert.equal(S.$id, 'S') +// }) +// it('Should retain cloned $id for wrapped Recursive 2', () => { +// const S = Type.String({ $id: 'S' }) +// const T = Type.Tuple([ +// Type.Recursive( +// (Self) => +// Type.Object({ +// self: Type.Optional(Self), +// }), +// { $id: 'RecursiveClone' }, +// ), +// ]) +// Assert.equal(T.items![0].$id, 'RecursiveClone') +// Assert.equal(S.$id, 'S') +// }) +// }) diff --git a/test/runtime/type/clone/index.ts b/test/runtime/type/clone/index.ts new file mode 100644 index 000000000..cd798158e --- /dev/null +++ b/test/runtime/type/clone/index.ts @@ -0,0 +1 @@ +import './clone' diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts new file mode 100644 index 000000000..f67300b2e --- /dev/null +++ b/test/runtime/type/extends/any.ts @@ -0,0 +1,98 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Any', () => { + it('Should extend Any', () => { + type T = any extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown', () => { + type T = any extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = any extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Boolean', () => { + type T = any extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Number', () => { + type T = any extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Integer', () => { + type T = any extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Array 1', () => { + type T = any extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Array 2', () => { + type T = any extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Tuple', () => { + type T = any extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Object 1', () => { + type T = any extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Object 2', () => { + type T = any extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Object 3', () => { + type T = any extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 1', () => { + type T = any extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 2', () => { + type T = any extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Null', () => { + type T = any extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Undefined', () => { + type T = any extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Void', () => { + type T = any extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + + it('Should extend Date', () => { + type T = any extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) +}) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts new file mode 100644 index 000000000..973c7457f --- /dev/null +++ b/test/runtime/type/extends/array.ts @@ -0,0 +1,316 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Array', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + + it('Should extend Array Varying 1', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array Varying 2', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array Varying 3', () => { + type T = Array extends Array ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array Varying 4', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ---------------------------------------------- + // Any + // ---------------------------------------------- + it('Should extend Any', () => { + type T = Array extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = Array extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = Array extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = Array extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 1', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array 2', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array 3', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Tuple', () => { + type T = Array extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = Array extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = Array extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 3', () => { + type T = Array extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 4', () => { + type T = Array extends { length: '1' } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 5', () => { + type T = Array extends { length: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 1', () => { + type T = Array extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = Array extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 4', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 5', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = Array extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = Array extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ---------------------------------------------- + // Constrained + // ---------------------------------------------- + + it('Should extend constrained Any', () => { + type T = Array extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Unknown', () => { + type T = Array extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained String', () => { + type T = Array extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Boolean', () => { + type T = Array extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Number', () => { + type T = Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Integer', () => { + type T = Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Array 1', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Array 2', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Array 3', () => { + type T = Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Tuple', () => { + type T = Array extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Object 1', () => { + type T = Array extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Object 2', () => { + type T = Array extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Object 3', () => { + type T = Array extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Object 4', () => { + type T = Array extends { length: '1' } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Object 5', () => { + type T = Array extends { length: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 1', () => { + type T = Array extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Union 2', () => { + type T = Array extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 3', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 4', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 5', () => { + type T = Array extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Null', () => { + type T = Array extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Undefined', () => { + type T = Array extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Void', () => { + type T = Array extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = Array extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/bigint.ts b/test/runtime/type/extends/bigint.ts new file mode 100644 index 000000000..779cb8424 --- /dev/null +++ b/test/runtime/type/extends/bigint.ts @@ -0,0 +1,101 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/BigInt', () => { + it('Should extend Any', () => { + type T = bigint extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown', () => { + type T = bigint extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = bigint extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = bigint extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = bigint extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = bigint extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array', () => { + type T = bigint extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = bigint extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Record', () => { + type T = bigint extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = bigint extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = bigint extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1', () => { + type T = bigint extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 2', () => { + type T = bigint extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3', () => { + type T = bigint extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 4', () => { + type T = bigint extends boolean | bigint ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Null', () => { + type T = bigint extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = bigint extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = bigint extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = bigint extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.BigInt(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts new file mode 100644 index 000000000..290eee289 --- /dev/null +++ b/test/runtime/type/extends/boolean.ts @@ -0,0 +1,107 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Boolean', () => { + it('Should extend Any', () => { + type T = boolean extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = boolean extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = boolean extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Number', () => { + type T = boolean extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = boolean extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = boolean extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = boolean extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = boolean extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = boolean extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = boolean extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = boolean extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = boolean extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = boolean extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = boolean extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = boolean extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = boolean extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = boolean extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts new file mode 100644 index 000000000..5746dbab4 --- /dev/null +++ b/test/runtime/type/extends/constructor.ts @@ -0,0 +1,250 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Constructor', () => { + it('Should extend Function', () => { + type T = (new () => number) extends () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Constructor 1', () => { + type T = (new () => number) extends new () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 2', () => { + type T = (new () => any) extends new () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 3', () => { + type T = (new () => number) extends new () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 4', () => { + type T = (new (a: number) => number) extends new () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Constructor 5', () => { + type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 6', () => { + type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Constructor 7', () => { + type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Constructor 8', () => { + type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 9', () => { + type T = (new () => number) extends new () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 9', () => { + type T = (new () => any) extends new () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 10', () => { + type T = (new () => Array) extends new () => object ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Constructor 11', () => { + type T = (new () => Array) extends new () => object ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 12', () => { + type T = (new () => object) extends new () => Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Constructor 13', () => { + type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 14', () => { + type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 15', () => { + type T = (new () => any) extends new () => unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Constructor 16', () => { + type T = (new () => unknown) extends new () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Any', () => { + type T = (new () => number) extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = (new () => number) extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = (new () => number) extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = (new () => number) extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = (new () => number) extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 1', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 2', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 3', () => { + type T = (new () => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = (new () => number) extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = (() => number) extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = (new () => number) extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = (new () => number) extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 3', () => { + type T = (new () => number) extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 4', () => { + type T = (new () => number) extends { length: '1' } ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = (new () => number) extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = (new () => number) extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 4', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 5', () => { + type T = (new () => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = (new () => number) extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = (new () => number) extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = (new () => number) extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = (new () => number) extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts new file mode 100644 index 000000000..53e7bfc55 --- /dev/null +++ b/test/runtime/type/extends/date.ts @@ -0,0 +1,112 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Date', () => { + it('Should extend Any', () => { + type T = Date extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = Date extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = Date extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = Date extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = Date extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = Date extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = Date extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = Date extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = Date extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = Date extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = Date extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = Date extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = Date extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = Date extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null', () => { + type T = Date extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = Date extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = Date extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = Date extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Date(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) +}) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts new file mode 100644 index 000000000..93abeb0d4 --- /dev/null +++ b/test/runtime/type/extends/function.ts @@ -0,0 +1,251 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Function', () => { + it('Should extend Constructor 1', () => { + type T = (() => number) extends new () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Function 1', () => { + type T = (() => number) extends () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 2', () => { + type T = (() => any) extends () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 3', () => { + type T = (() => number) extends () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 4', () => { + type T = ((a: number) => number) extends () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Function 5', () => { + type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 6', () => { + type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Function 7', () => { + type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Function 8', () => { + type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 9', () => { + type T = (() => number) extends () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 9', () => { + type T = (() => any) extends () => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 10', () => { + type T = (() => Array) extends () => object ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 11', () => { + type T = (() => Array) extends () => object ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 12', () => { + type T = (() => object) extends () => Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Function 13', () => { + type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 14', () => { + type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 15', () => { + type T = (() => any) extends () => unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Function 16', () => { + type T = (() => unknown) extends () => any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Any', () => { + type T = (() => number) extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = (() => number) extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = (() => number) extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = (() => number) extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = (() => number) extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 1', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 2', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 3', () => { + type T = (() => number) extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = (() => number) extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = (() => number) extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = (() => number) extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = (() => number) extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 3', () => { + type T = (() => number) extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 4', () => { + type T = (() => number) extends { length: '1' } ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 5', () => { + type T = (() => number) extends { length: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 1', () => { + type T = (() => number) extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = (() => number) extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 4', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 5', () => { + type T = (() => number) extends any | Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = (() => number) extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = (() => number) extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = (() => number) extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/conditional/index.ts b/test/runtime/type/extends/index.ts similarity index 91% rename from test/runtime/conditional/index.ts rename to test/runtime/type/extends/index.ts index 1f7a88c56..ae680a5a0 100644 --- a/test/runtime/conditional/index.ts +++ b/test/runtime/type/extends/index.ts @@ -1,5 +1,6 @@ import './any' import './array' +import './bigint' import './boolean' import './constructor' import './date' @@ -12,6 +13,7 @@ import './object' import './promise' import './record' import './string' +import './symbol' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts new file mode 100644 index 000000000..844e1ffdb --- /dev/null +++ b/test/runtime/type/extends/integer.ts @@ -0,0 +1,113 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Integer', () => { + it('Should extend Any', () => { + type T = number extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = number extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = number extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = number extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = number extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Integer', () => { + type T = number extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array', () => { + type T = number extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = number extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = number extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = number extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = number extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = number extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Integer(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts new file mode 100644 index 000000000..71377e5e8 --- /dev/null +++ b/test/runtime/type/extends/literal.ts @@ -0,0 +1,312 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Literal', () => { + // ------------------------------------------------------------------- + // String Literal + // ------------------------------------------------------------------- + it('Should extend Any (String)', () => { + type T = 'hello' extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown (String)', () => { + type T = 'hello' extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String (String)', () => { + type T = 'hello' extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Boolean (String)', () => { + type T = 'hello' extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number (String)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer (String)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array (String)', () => { + type T = 'hello' extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple (String)', () => { + type T = 'hello' extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1 (String)', () => { + type T = 'hello' extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2 (String)', () => { + type T = 'hello' extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1 (String)', () => { + type T = 'hello' extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2 (String)', () => { + type T = 'hello' extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3 (String)', () => { + type T = 'hello' extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null (String)', () => { + type T = 'hello' extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined (String)', () => { + type T = 'hello' extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ------------------------------------------------------------------- + // Number Literal + // ------------------------------------------------------------------- + + it('Should extend Any (Number)', () => { + type T = 10 extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown (Number)', () => { + type T = 10 extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String (Number)', () => { + type T = 10 extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean (Number)', () => { + type T = 10 extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number (Number)', () => { + type T = 10 extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Integer (Number)', () => { + type T = 10 extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array (Number)', () => { + type T = 10 extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple (Number)', () => { + type T = 10 extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1 (Number)', () => { + type T = 10 extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2 (Number)', () => { + type T = 10 extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1 (Number)', () => { + type T = 10 extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2 (Number)', () => { + type T = 10 extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3 (Number)', () => { + type T = 10 extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null (Number)', () => { + type T = 10 extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined (Number)', () => { + type T = 10 extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ------------------------------------------------------------------- + // Boolean Literal + // ------------------------------------------------------------------- + + it('Should extend Any (Boolean)', () => { + type T = true extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown (Boolean)', () => { + type T = true extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String (Boolean)', () => { + type T = true extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean (Boolean)', () => { + type T = true extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Number (Boolean)', () => { + type T = true extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer (Boolean)', () => { + type T = true extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array (Boolean)', () => { + type T = true extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple (Boolean)', () => { + type T = true extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record 1', () => { + type T = 'hello' extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Record 2', () => { + type T = 10 extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record 3', () => { + type T = true extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1 (Boolean)', () => { + type T = true extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2 (Boolean)', () => { + type T = true extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1 (Boolean)', () => { + type T = true extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2 (Boolean)', () => { + type T = true extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3 (Boolean)', () => { + type T = true extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null (Boolean)', () => { + type T = true extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined (Boolean)', () => { + type T = true extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = true extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = true extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts new file mode 100644 index 000000000..82931c96d --- /dev/null +++ b/test/runtime/type/extends/null.ts @@ -0,0 +1,119 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Null', () => { + it('Should extend Any', () => { + type T = null extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = null extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = null extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = null extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = null extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = null extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = null extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = null extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = null extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = null extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 2', () => { + type T = null extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 3', () => { + type T = null extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = null extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = null extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = null extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null', () => { + type T = null extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Undefined', () => { + type T = null extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = null extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = null extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Null(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts new file mode 100644 index 000000000..06e893f9d --- /dev/null +++ b/test/runtime/type/extends/number.ts @@ -0,0 +1,113 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Number', () => { + it('Should extend Any', () => { + type T = number extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = number extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = number extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = number extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = number extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Integer', () => { + type T = number extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Array', () => { + type T = number extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = number extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = number extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = number extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = number extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = number extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts new file mode 100644 index 000000000..b5a2973ed --- /dev/null +++ b/test/runtime/type/extends/object.ts @@ -0,0 +1,177 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Object', () => { + // ---------------------------------------------------------- + // Object + // ---------------------------------------------------------- + it('Should extend Object 1', () => { + type T = { x: number; y: number } extends { x: number } ? 1 : 2 + const A = Type.Object({ x: Type.Number(), y: Type.Number() }) + const B = Type.Object({ x: Type.Number() }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = { x: number } extends { x: number; y: number } ? 1 : 2 + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 3', () => { + type T = { x: number; y: string } extends { x: number } ? 1 : 2 + const A = Type.Object({ x: Type.Number(), y: Type.String() }) + const B = Type.Object({ x: Type.Number() }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 4', () => { + type T = { x: number } extends { x: number; y: string } ? 1 : 2 + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ x: Type.Number(), y: Type.String() }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 5', () => { + type T = { x: number | string } extends { x: number } ? 1 : 2 + const A = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) + const B = Type.Object({ x: Type.Number() }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 6', () => { + type T = { x: number } extends { x: number | string } ? 1 : 2 + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + // ---------------------------------------------------------- + // Record + // ---------------------------------------------------------- + it('Should extend Record 2', () => { + type T = { a: number; b: number } extends Record ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.String(), Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 3', () => { + type T = { a: number; b: number } extends Record ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Number(), Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 4', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 5', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.String(), Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 6', () => { + type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 + const A = Type.Object({ a: Type.Number(), b: Type.Number() }) + const B = Type.Record(Type.Number(), Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + // ---------------------------------------------------------- + // Standard + // ---------------------------------------------------------- + it('Should extend Any', () => { + type T = { a: number } extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = { a: number } extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = { a: number } extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = { a: number } extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = { a: number } extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array', () => { + type T = { a: number } extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = { a: number } extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = { a: number } extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = { a: number } extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 3', () => { + type T = { a: number } extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 1', () => { + type T = { a: number } extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 2', () => { + type T = { a: number } extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 2', () => { + type T = { a: number } extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Null', () => { + type T = { a: number } extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = { a: number } extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = { a: number } extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = { a: number } extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts new file mode 100644 index 000000000..b8c6a24db --- /dev/null +++ b/test/runtime/type/extends/promise.ts @@ -0,0 +1,251 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Promise', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + + it('Should extend Promise Varying 1', () => { + type T = Promise extends Promise ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Promise Varying 2', () => { + type T = Promise extends Promise ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Promise Varying 3', () => { + type T = Promise extends Promise ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Promise Varying 4', () => { + type T = Promise extends Promise ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ---------------------------------------------- + // Any + // ---------------------------------------------- + + it('Should extend Any', () => { + type T = Promise extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = Promise extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = Promise extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = Promise extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = Promise extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = Promise extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = Promise extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = Promise extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = Promise extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = Promise extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = Promise extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 3', () => { + type T = Promise extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 1', () => { + type T = Promise extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = Promise extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = Promise extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null', () => { + type T = Promise extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = Promise extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + // ---------------------------------------------- + // Constrained + // ---------------------------------------------- + + it('Should extend constrained Any', () => { + type T = Promise extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Unknown', () => { + type T = Promise extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained String', () => { + type T = Promise extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Boolean', () => { + type T = Promise extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Number', () => { + type T = Promise extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Integer', () => { + type T = Promise extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Array', () => { + type T = Promise extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Tuple', () => { + type T = Promise extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Object 1', () => { + type T = Promise extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Object 2', () => { + type T = Promise extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Object 3', () => { + type T = Promise extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 1', () => { + type T = Promise extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Union 2', () => { + type T = Promise extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend constrained Union 2', () => { + type T = Promise extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Null', () => { + type T = Promise extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend constrained Undefined', () => { + type T = Promise extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = Promise extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = Promise extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/conditional/record.ts b/test/runtime/type/extends/record.ts similarity index 58% rename from test/runtime/conditional/record.ts rename to test/runtime/type/extends/record.ts index 396878420..74a7920a8 100644 --- a/test/runtime/conditional/record.ts +++ b/test/runtime/type/extends/record.ts @@ -1,94 +1,94 @@ -import { Structural, StructuralResult } from '@sinclair/typebox/conditional' +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' -describe('conditional/structural/Record', () => { +describe('type/extends/Record', () => { it('Should extend Record 1', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) // ----- @@ -97,32 +97,32 @@ describe('conditional/structural/Record', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = Structural.Check(A, B) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(A, B) + Assert.deepEqual(R, TypeExtendsResult.True) }) // ------------------------------------------------------------------- @@ -131,109 +131,103 @@ describe('conditional/structural/Record', () => { it('Should extend Any', () => { type T = Record extends any ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Any()) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Record extends string ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.String()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Record extends number ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Number()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Integer()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, StructuralResult.False) - }) - - it('Should extend Object 1', () => { - type T = Record extends object ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, StructuralResult.True) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = Record extends null ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Null()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Record extends void ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Void()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 - const R = Structural.Check(Type.Record(Type.Number(), Type.Number()), Type.Date()) - Assert.deepEqual(R, StructuralResult.False) + const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts new file mode 100644 index 000000000..1ba4aa486 --- /dev/null +++ b/test/runtime/type/extends/string.ts @@ -0,0 +1,131 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/String', () => { + it('Should extend Any', () => { + type T = string extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = string extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = string extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Boolean', () => { + type T = string extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = string extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = string extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = string extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = string extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record 1', () => { + type T = string extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Record 2', () => { + type T = string extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Record 3', () => { + type T = string extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Record 4', () => { + type T = string extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = string extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = string extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = number extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = number extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Number(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/symbol.ts b/test/runtime/type/extends/symbol.ts new file mode 100644 index 000000000..402de3d61 --- /dev/null +++ b/test/runtime/type/extends/symbol.ts @@ -0,0 +1,106 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Symbol', () => { + it('Should extend Any', () => { + type T = symbol extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown', () => { + type T = symbol extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = symbol extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = symbol extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = symbol extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = symbol extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array', () => { + type T = symbol extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = symbol extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Record', () => { + type T = symbol extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = symbol extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = symbol extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1', () => { + type T = symbol extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 2', () => { + type T = symbol extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3', () => { + type T = symbol extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 4', () => { + type T = symbol extends boolean | symbol ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Null', () => { + type T = symbol extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = symbol extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = symbol extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = symbol extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Symbol', () => { + type T = symbol extends symbol ? 1 : 2 + const R = TypeExtends.Extends(Type.Symbol(), Type.Symbol()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) +}) diff --git a/test/runtime/type/extends/tuple.ts b/test/runtime/type/extends/tuple.ts new file mode 100644 index 000000000..15486a6a6 --- /dev/null +++ b/test/runtime/type/extends/tuple.ts @@ -0,0 +1,161 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Tuple', () => { + it('Should extend Any', () => { + type T = [string, number] extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = [string, number] extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = [string, number] extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = [string, number] extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = [string, number] extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array 1', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Array 2', () => { + type T = [string, number] extends Array ? 1 : 2 // 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array 3', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Array 4', () => { + type T = [string, number] extends Array ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Tuple 1', () => { + type T = [string, number] extends [string, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Tuple 2', () => { + type T = [string, number] extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple 3', () => { + type T = [string, any] extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple 4', () => { + type T = [string, number] extends [string, any] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Tuple 5', () => { + type T = [string, unknown] extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple 6', () => { + type T = [string, number] extends [string, unknown] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Tuple 7', () => { + type T = [] extends [string, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple 8', () => { + type T = [string, number] extends [] ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Record 1', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 2', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 3', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Record 4', () => { + type T = [string, number] extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = [string, number] extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = [string, number] extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 3', () => { + type T = [string, number] extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 1', () => { + type T = [string, number] extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 2', () => { + type T = [string, number] extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 2', () => { + type T = [string, number] extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Null', () => { + type T = [string, number] extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = [string, number] extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = [string, number] extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = [string, number] extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts new file mode 100644 index 000000000..756a93d53 --- /dev/null +++ b/test/runtime/type/extends/uint8array.ts @@ -0,0 +1,113 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Uint8Array', () => { + it('Should extend Any', () => { + type T = Uint8Array extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = Uint8Array extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = Uint8Array extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = Uint8Array extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = Uint8Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = Uint8Array extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = Uint8Array extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = Uint8Array extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Record', () => { + type T = Uint8Array extends Record ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 1', () => { + type T = Uint8Array extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Object 2', () => { + type T = Uint8Array extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = Uint8Array extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = Uint8Array extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = Uint8Array extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null', () => { + type T = Uint8Array extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = Uint8Array extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = Uint8Array extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = Uint8Array extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts new file mode 100644 index 000000000..46a4061a5 --- /dev/null +++ b/test/runtime/type/extends/undefined.ts @@ -0,0 +1,107 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Undefined', () => { + it('Should extend Any', () => { + type T = undefined extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = undefined extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = undefined extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = undefined extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = undefined extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array', () => { + type T = undefined extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = undefined extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = undefined extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 2', () => { + type T = undefined extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 3', () => { + type T = undefined extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = undefined extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = undefined extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 2', () => { + type T = undefined extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Null', () => { + type T = undefined extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = undefined extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Void', () => { + type T = undefined extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Date', () => { + type T = undefined extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/union.ts b/test/runtime/type/extends/union.ts new file mode 100644 index 000000000..ab749f76b --- /dev/null +++ b/test/runtime/type/extends/union.ts @@ -0,0 +1,136 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Union', () => { + it('Should extend Any', () => { + type T = number | string extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = number | string extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = number | string extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = number | string extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = number | string extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array', () => { + type T = number | string extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = number | string extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = number | string extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = number | string extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1', () => { + type T = number | string extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 2', () => { + type T = number | string extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3', () => { + type T = number | string extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 4', () => { + type T = any | boolean extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 5', () => { + type T = any | string extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 6', () => { + type T = any | {} extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 7', () => { + type T = any extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.Union) + }) + it('Should extend Union 8', () => { + type T = unknown | string extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 9', () => { + type T = unknown extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Null', () => { + type T = number | string extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = number | string extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = number | string extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void 2', () => { + type T = number | string | void extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = number | string | void extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date 2', () => { + type T = Date | number | string | void extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend BigInt', () => { + type T = bigint | number | string | void extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Symbol', () => { + type T = symbol | number | string | void extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts new file mode 100644 index 000000000..c37f4c65f --- /dev/null +++ b/test/runtime/type/extends/unknown.ts @@ -0,0 +1,119 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Unknown', () => { + it('Should extend Any', () => { + type T = unknown extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = unknown extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = unknown extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = unknown extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = unknown extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = unknown extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 1', () => { + type T = unknown extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 2', () => { + type T = unknown extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = unknown extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = unknown extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 2', () => { + type T = unknown extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = unknown extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = unknown extends any | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = unknown extends unknown | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 4', () => { + type T = unknown extends unknown | any ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = unknown extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = unknown extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = unknown extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Date', () => { + type T = unknown extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts new file mode 100644 index 000000000..d800fb08d --- /dev/null +++ b/test/runtime/type/extends/void.ts @@ -0,0 +1,125 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Void', () => { + it('Should extend Any', () => { + type T = void extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Unknown', () => { + type T = void extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend String', () => { + type T = void extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Boolean', () => { + type T = void extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Number', () => { + type T = void extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Integer', () => { + type T = void extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 1', () => { + type T = void extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Array 2', () => { + type T = void extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Tuple', () => { + type T = void extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 1', () => { + type T = void extends object ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 2', () => { + type T = void extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Object({})) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Object 3', () => { + type T = void extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 1', () => { + type T = void extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Union 2', () => { + type T = void extends any | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 3', () => { + type T = void extends unknown | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Union 4', () => { + type T = void extends unknown | any ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Null', () => { + type T = void extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Undefined', () => { + type T = void extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + + it('Should extend Void', () => { + type T = void extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Void()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + + it('Should extend Date', () => { + type T = void extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Void(), Type.Date()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/guard/any.ts b/test/runtime/type/guard/any.ts similarity index 83% rename from test/runtime/guard/any.ts rename to test/runtime/type/guard/any.ts index 039fa56ef..8f8e4d86c 100644 --- a/test/runtime/guard/any.ts +++ b/test/runtime/type/guard/any.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TAny', () => { it('Should guard for TAny', () => { diff --git a/test/runtime/guard/array.ts b/test/runtime/type/guard/array.ts similarity index 94% rename from test/runtime/guard/array.ts rename to test/runtime/type/guard/array.ts index 402614610..a6bec8803 100644 --- a/test/runtime/guard/array.ts +++ b/test/runtime/type/guard/array.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TArray', () => { it('Should guard for TArray', () => { diff --git a/test/runtime/type/guard/bigint.ts b/test/runtime/type/guard/bigint.ts new file mode 100644 index 000000000..12be1911b --- /dev/null +++ b/test/runtime/type/guard/bigint.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TBigInt', () => { + it('Should guard for TBigInt', () => { + const R = TypeGuard.TBigInt(Type.BigInt()) + Assert.equal(R, true) + }) + it('Should not guard for TBigInt', () => { + const R = TypeGuard.TBigInt(null) + Assert.equal(R, false) + }) + it('Should not guard for BigInt with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TBigInt(Type.BigInt({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/boolean.ts b/test/runtime/type/guard/boolean.ts similarity index 84% rename from test/runtime/guard/boolean.ts rename to test/runtime/type/guard/boolean.ts index 71680414f..5368dedb2 100644 --- a/test/runtime/guard/boolean.ts +++ b/test/runtime/type/guard/boolean.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TBoolean', () => { it('Should guard for TBoolean', () => { diff --git a/test/runtime/guard/constructor.ts b/test/runtime/type/guard/constructor.ts similarity index 93% rename from test/runtime/guard/constructor.ts rename to test/runtime/type/guard/constructor.ts index 9bb2d4b37..813594c0c 100644 --- a/test/runtime/guard/constructor.ts +++ b/test/runtime/type/guard/constructor.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TConstructor', () => { it('Should guard for TConstructor', () => { diff --git a/test/runtime/guard/date.ts b/test/runtime/type/guard/date.ts similarity index 92% rename from test/runtime/guard/date.ts rename to test/runtime/type/guard/date.ts index 14132a7bd..b14cd4600 100644 --- a/test/runtime/guard/date.ts +++ b/test/runtime/type/guard/date.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TDate', () => { it('Should guard for TDate', () => { diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts new file mode 100644 index 000000000..be3e62261 --- /dev/null +++ b/test/runtime/type/guard/exclude.ts @@ -0,0 +1,24 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TExclude', () => { + it('Should extract string from number', () => { + const T = Type.Exclude(Type.String(), Type.Number()) + Assert.deepEqual(TypeGuard.TString(T), true) + }) + it('Should extract string from string', () => { + const T = Type.Exclude(Type.String(), Type.String()) + Assert.deepEqual(TypeGuard.TNever(T), true) + }) + it('Should extract string | number | boolean from string', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + Assert.deepEqual(TypeGuard.TUnion(T), true) + Assert.deepEqual(TypeGuard.TNumber(T.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + }) + it('Should extract string | number | boolean from string | boolean', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) + Assert.deepEqual(TypeGuard.TNumber(T), true) + }) +}) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts new file mode 100644 index 000000000..f1caa3737 --- /dev/null +++ b/test/runtime/type/guard/extract.ts @@ -0,0 +1,24 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TExtract', () => { + it('Should extract string from number', () => { + const T = Type.Extract(Type.String(), Type.Number()) + Assert.deepEqual(TypeGuard.TNever(T), true) + }) + it('Should extract string from string', () => { + const T = Type.Extract(Type.String(), Type.String()) + Assert.deepEqual(TypeGuard.TString(T), true) + }) + it('Should extract string | number | boolean from string', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + Assert.deepEqual(TypeGuard.TString(T), true) + }) + it('Should extract string | number | boolean from string | boolean', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) + Assert.deepEqual(TypeGuard.TUnion(T), true) + Assert.deepEqual(TypeGuard.TString(T.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + }) +}) diff --git a/test/runtime/guard/function.ts b/test/runtime/type/guard/function.ts similarity index 93% rename from test/runtime/guard/function.ts rename to test/runtime/type/guard/function.ts index 102ac4f14..e70b67d57 100644 --- a/test/runtime/guard/function.ts +++ b/test/runtime/type/guard/function.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TFunction', () => { it('Should guard for TFunction', () => { diff --git a/test/runtime/guard/index.ts b/test/runtime/type/guard/index.ts similarity index 78% rename from test/runtime/guard/index.ts rename to test/runtime/type/guard/index.ts index 037d13781..d66cdf0f5 100644 --- a/test/runtime/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -1,11 +1,16 @@ import './any' import './array' +import './bigint' import './boolean' import './constructor' import './date' +import './exclude' +import './extract' import './function' import './integer' import './literal' +import './intersect' +import './not' import './null' import './number' import './object' @@ -14,6 +19,7 @@ import './record' import './ref' import './self' import './string' +import './symbol' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/guard/integer.ts b/test/runtime/type/guard/integer.ts similarity index 93% rename from test/runtime/guard/integer.ts rename to test/runtime/type/guard/integer.ts index 72558e07e..28976d20e 100644 --- a/test/runtime/guard/integer.ts +++ b/test/runtime/type/guard/integer.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TInteger', () => { it('Should guard for TInteger', () => { diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/intersect.ts new file mode 100644 index 000000000..907b4a72d --- /dev/null +++ b/test/runtime/type/guard/intersect.ts @@ -0,0 +1,32 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TUnion', () => { + it('Should guard for TIntersect', () => { + const R = TypeGuard.TIntersect( + Type.Intersect([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.equal(R, true) + }) + it('Should not guard for TIntersect', () => { + const R = TypeGuard.TIntersect( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/literal.ts b/test/runtime/type/guard/literal.ts similarity index 91% rename from test/runtime/guard/literal.ts rename to test/runtime/type/guard/literal.ts index df4d39e24..674c9dd40 100644 --- a/test/runtime/guard/literal.ts +++ b/test/runtime/type/guard/literal.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TLiteral', () => { it('Should guard for TLiteral of String', () => { diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/not.ts new file mode 100644 index 000000000..0bbc9d5ec --- /dev/null +++ b/test/runtime/type/guard/not.ts @@ -0,0 +1,23 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TNot', () => { + it('Should guard for TNot', () => { + const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String())) + Assert.equal(R, true) + }) + it('Should not guard for TNot 1', () => { + const R = TypeGuard.TNot(Type.Not(null as any, Type.String())) + Assert.equal(R, false) + }) + it('Should not guard for TNot 2', () => { + const R = TypeGuard.TNot(Type.Not(Type.String(), null as any)) + Assert.equal(R, false) + }) + it('Should not guard for TNot with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String()), { $id: 1 }) + Assert.equal(R, true) + }) +}) diff --git a/test/runtime/guard/null.ts b/test/runtime/type/guard/null.ts similarity index 83% rename from test/runtime/guard/null.ts rename to test/runtime/type/guard/null.ts index d853e112f..11656ab0c 100644 --- a/test/runtime/guard/null.ts +++ b/test/runtime/type/guard/null.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TNull', () => { it('Should guard for TNull', () => { diff --git a/test/runtime/guard/number.ts b/test/runtime/type/guard/number.ts similarity index 93% rename from test/runtime/guard/number.ts rename to test/runtime/type/guard/number.ts index eb6141606..c325b27b4 100644 --- a/test/runtime/guard/number.ts +++ b/test/runtime/type/guard/number.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TNumber', () => { it('Should guard for TNumber', () => { diff --git a/test/runtime/guard/object.ts b/test/runtime/type/guard/object.ts similarity index 96% rename from test/runtime/guard/object.ts rename to test/runtime/type/guard/object.ts index 867774afc..2cdb5044c 100644 --- a/test/runtime/guard/object.ts +++ b/test/runtime/type/guard/object.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TObject', () => { it('Should guard for TObject', () => { diff --git a/test/runtime/guard/promise.ts b/test/runtime/type/guard/promise.ts similarity index 91% rename from test/runtime/guard/promise.ts rename to test/runtime/type/guard/promise.ts index fdfbfb184..eeedaf2f0 100644 --- a/test/runtime/guard/promise.ts +++ b/test/runtime/type/guard/promise.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TPromise', () => { it('Should guard for TPromise', () => { diff --git a/test/runtime/guard/record.ts b/test/runtime/type/guard/record.ts similarity index 66% rename from test/runtime/guard/record.ts rename to test/runtime/type/guard/record.ts index 92ab62c00..2418c0e3d 100644 --- a/test/runtime/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TRecord', () => { it('Should guard for TRecord', () => { @@ -45,9 +45,14 @@ describe('type/guard/TRecord', () => { Assert.equal(R, false) }) - it('Should not guard for TRecord with invalid literal key', () => { - const K = Type.Union([Type.Literal('hello\nworld')]) - const R = TypeGuard.TRecord(Type.Record(K, Type.Number())) - Assert.equal(R, false) + it('Transform: Should should transform to TObject for single literal union value', () => { + const K = Type.Union([Type.Literal('ok')]) + const R = TypeGuard.TObject(Type.Record(K, Type.Number())) + Assert.equal(R, true) + }) + it('Transform: Should should transform to TObject for multi literal union value', () => { + const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const R = TypeGuard.TObject(Type.Record(K, Type.Number())) + Assert.equal(R, true) }) }) diff --git a/test/runtime/guard/ref.ts b/test/runtime/type/guard/ref.ts similarity index 85% rename from test/runtime/guard/ref.ts rename to test/runtime/type/guard/ref.ts index 6a4a8e883..e76680cf7 100644 --- a/test/runtime/guard/ref.ts +++ b/test/runtime/type/guard/ref.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TRef', () => { it('Should guard for TRef', () => { diff --git a/test/runtime/guard/self.ts b/test/runtime/type/guard/self.ts similarity index 85% rename from test/runtime/guard/self.ts rename to test/runtime/type/guard/self.ts index 824d77ee8..d9059c1ba 100644 --- a/test/runtime/guard/self.ts +++ b/test/runtime/type/guard/self.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TSelf', () => { it('Should guard for TSelf', () => { diff --git a/test/runtime/guard/string.ts b/test/runtime/type/guard/string.ts similarity index 91% rename from test/runtime/guard/string.ts rename to test/runtime/type/guard/string.ts index a3cc7c224..24b33dd1e 100644 --- a/test/runtime/guard/string.ts +++ b/test/runtime/type/guard/string.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TString', () => { it('Should guard for TString', () => { diff --git a/test/runtime/type/guard/symbol.ts b/test/runtime/type/guard/symbol.ts new file mode 100644 index 000000000..7683f099e --- /dev/null +++ b/test/runtime/type/guard/symbol.ts @@ -0,0 +1,19 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TSymbol', () => { + it('Should guard for TSymbol', () => { + const R = TypeGuard.TSymbol(Type.Symbol()) + Assert.equal(R, true) + }) + it('Should not guard for TSymbol', () => { + const R = TypeGuard.TSymbol(null) + Assert.equal(R, false) + }) + it('Should not guard for TSymbol with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.TSymbol(Type.Symbol({ $id: 1 })) + Assert.equal(R, false) + }) +}) diff --git a/test/runtime/guard/tuple.ts b/test/runtime/type/guard/tuple.ts similarity index 88% rename from test/runtime/guard/tuple.ts rename to test/runtime/type/guard/tuple.ts index 935426e24..8e549aa57 100644 --- a/test/runtime/guard/tuple.ts +++ b/test/runtime/type/guard/tuple.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TTuple', () => { it('Should guard for TTuple', () => { diff --git a/test/runtime/guard/uint8array.ts b/test/runtime/type/guard/uint8array.ts similarity index 90% rename from test/runtime/guard/uint8array.ts rename to test/runtime/type/guard/uint8array.ts index 0143c3f47..cd4c741e7 100644 --- a/test/runtime/guard/uint8array.ts +++ b/test/runtime/type/guard/uint8array.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TUint8Array', () => { it('Should guard for TUint8Array', () => { diff --git a/test/runtime/guard/undefined.ts b/test/runtime/type/guard/undefined.ts similarity index 84% rename from test/runtime/guard/undefined.ts rename to test/runtime/type/guard/undefined.ts index 0b7f471c8..cc8bbeea3 100644 --- a/test/runtime/guard/undefined.ts +++ b/test/runtime/type/guard/undefined.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TUndefined', () => { it('Should guard for TUndefined', () => { diff --git a/test/runtime/guard/union.ts b/test/runtime/type/guard/union.ts similarity index 63% rename from test/runtime/guard/union.ts rename to test/runtime/type/guard/union.ts index b26465a2e..16fc75d41 100644 --- a/test/runtime/guard/union.ts +++ b/test/runtime/type/guard/union.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TUnion', () => { it('Should guard for TUnion', () => { @@ -22,6 +22,7 @@ describe('type/guard/TUnion', () => { }) it('Should guard for TUnion with invalid $id', () => { const R = TypeGuard.TUnion( + // @ts-ignore Type.Union( [ Type.Object({ @@ -63,4 +64,23 @@ describe('type/guard/TUnion', () => { ) Assert.equal(R, false) }) + it('Transform: Should transform to never for zero length union', () => { + const T = Type.Union([]) + const R = TypeGuard.TNever(T) + Assert.equal(R, true) + }) + it('Transform: Should unwrap union type for array of length === 1', () => { + const T = Type.Union([Type.String()]) + const R = TypeGuard.TString(T) + Assert.equal(R, true) + }) + it('Transform: Should retain union if array length > 1', () => { + const T = Type.Union([Type.String(), Type.Number()]) + const R1 = TypeGuard.TUnion(T) + const R2 = TypeGuard.TString(T.anyOf[0]) + const R3 = TypeGuard.TNumber(T.anyOf[1]) + Assert.equal(R1, true) + Assert.equal(R2, true) + Assert.equal(R3, true) + }) }) diff --git a/test/runtime/guard/unknown.ts b/test/runtime/type/guard/unknown.ts similarity index 84% rename from test/runtime/guard/unknown.ts rename to test/runtime/type/guard/unknown.ts index d23f2c267..4d3e5980c 100644 --- a/test/runtime/guard/unknown.ts +++ b/test/runtime/type/guard/unknown.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TUnknown', () => { it('Should guard for TUnknown', () => { diff --git a/test/runtime/guard/void.ts b/test/runtime/type/guard/void.ts similarity index 83% rename from test/runtime/guard/void.ts rename to test/runtime/type/guard/void.ts index d513f5f2a..839719a2f 100644 --- a/test/runtime/guard/void.ts +++ b/test/runtime/type/guard/void.ts @@ -1,6 +1,6 @@ -import { TypeGuard } from '@sinclair/typebox/guard' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' +import { Assert } from '../../assert/index' describe('type/guard/TVoid', () => { it('Should guard for TVoid', () => { diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts new file mode 100644 index 000000000..2ec254a35 --- /dev/null +++ b/test/runtime/type/index.ts @@ -0,0 +1,5 @@ +import './clone/index' +import './extends/index' +import './guard/index' +import './registry/index' +import './normal/index' diff --git a/test/runtime/type/normal/exclude.ts b/test/runtime/type/normal/exclude.ts new file mode 100644 index 000000000..fd0234242 --- /dev/null +++ b/test/runtime/type/normal/exclude.ts @@ -0,0 +1,21 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Exclude', () => { + it('Normalize 1', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + const R = TypeGuard.TUnion(T) + Assert.deepEqual(R, true) + }) + it('Normalize 2', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.String()) + const R = TypeGuard.TNumber(T) + Assert.deepEqual(R, true) + }) + it('Normalize 3', () => { + const T = Type.Exclude(Type.Union([Type.String()]), Type.String()) + const R = TypeGuard.TNever(T) + Assert.deepEqual(R, true) + }) +}) diff --git a/test/runtime/type/normal/extract.ts b/test/runtime/type/normal/extract.ts new file mode 100644 index 000000000..bb3a3d09d --- /dev/null +++ b/test/runtime/type/normal/extract.ts @@ -0,0 +1,31 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Extract', () => { + it('Normalize 1', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Number()])) + const R = TypeGuard.TUnion(T) + Assert.deepEqual(R, true) + }) + it('Normalize 2', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + const R = TypeGuard.TString(T) + Assert.deepEqual(R, true) + }) + it('Normalize 3', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.String()) + const R = TypeGuard.TString(T) + Assert.deepEqual(R, true) + }) + it('Normalize 4', () => { + const T = Type.Extract(Type.Union([Type.String()]), Type.String()) + const R = TypeGuard.TString(T) + Assert.deepEqual(R, true) + }) + it('Normalize 5', () => { + const T = Type.Extract(Type.Union([]), Type.String()) + const R = TypeGuard.TNever(T) + Assert.deepEqual(R, true) + }) +}) diff --git a/test/runtime/type/normal/index.ts b/test/runtime/type/normal/index.ts new file mode 100644 index 000000000..d0d7e13fd --- /dev/null +++ b/test/runtime/type/normal/index.ts @@ -0,0 +1,5 @@ +import './exclude' +import './extract' +import './intersect' +import './record' +import './union' diff --git a/test/runtime/type/normal/intersect.ts b/test/runtime/type/normal/intersect.ts new file mode 100644 index 000000000..95accfd2d --- /dev/null +++ b/test/runtime/type/normal/intersect.ts @@ -0,0 +1,21 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Intersect', () => { + it('Normalize 1', () => { + const T = Type.Intersect([Type.Number(), Type.String()]) + const R = TypeGuard.TIntersect(T) + Assert.deepEqual(R, true) + }) + it('Normalize 2', () => { + const T = Type.Intersect([Type.Number()]) + const R = TypeGuard.TNumber(T) + Assert.deepEqual(R, true) + }) + it('Normalize 3', () => { + const T = Type.Intersect([]) + const R = TypeGuard.TNever(T) + Assert.deepEqual(R, true) + }) +}) diff --git a/test/runtime/type/normal/record.ts b/test/runtime/type/normal/record.ts new file mode 100644 index 000000000..53337902f --- /dev/null +++ b/test/runtime/type/normal/record.ts @@ -0,0 +1,12 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Record', () => { + it('Normalize 1', () => { + const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Record(K, Type.String()) + const R = TypeGuard.TObject(T) + Assert.deepEqual(R, true) + }) +}) diff --git a/test/runtime/type/normal/union.ts b/test/runtime/type/normal/union.ts new file mode 100644 index 000000000..44e5a7ece --- /dev/null +++ b/test/runtime/type/normal/union.ts @@ -0,0 +1,21 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Union', () => { + it('Normalize 1', () => { + const T = Type.Union([Type.Number(), Type.String()]) + const R = TypeGuard.TUnion(T) + Assert.deepEqual(R, true) + }) + it('Normalize 2', () => { + const T = Type.Union([Type.Number()]) + const R = TypeGuard.TNumber(T) + Assert.deepEqual(R, true) + }) + it('Normalize 3', () => { + const T = Type.Union([]) + const R = TypeGuard.TNever(T) + Assert.deepEqual(R, true) + }) +}) diff --git a/test/runtime/type/registry/format.ts b/test/runtime/type/registry/format.ts new file mode 100644 index 000000000..7f04d15d3 --- /dev/null +++ b/test/runtime/type/registry/format.ts @@ -0,0 +1,25 @@ +import { FormatRegistry } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('format/Format', () => { + it('Should set format', () => { + FormatRegistry.Set('test#format1', () => true) + }) + + it('Should get format', () => { + FormatRegistry.Set('test#format2', () => true) + const format = FormatRegistry.Get('test#format2') + Assert.equal(typeof format, 'function') + }) + + it('Should return true if exists', () => { + FormatRegistry.Set('test#format3', () => true) + Assert.equal(FormatRegistry.Has('test#format3'), true) + }) + + it('Should clear formats', () => { + FormatRegistry.Set('test#format4', () => true) + FormatRegistry.Clear() + Assert.equal(FormatRegistry.Has('test#format4'), false) + }) +}) diff --git a/test/runtime/format/index.ts b/test/runtime/type/registry/index.ts similarity index 100% rename from test/runtime/format/index.ts rename to test/runtime/type/registry/index.ts diff --git a/test/runtime/value/cast/any.ts b/test/runtime/value/cast/any.ts index 155b2fcaa..a294da43f 100644 --- a/test/runtime/value/cast/any.ts +++ b/test/runtime/value/cast/any.ts @@ -34,19 +34,16 @@ describe('value/cast/Any', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result.getTime(100), 100) }) - it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/array.ts b/test/runtime/value/cast/array.ts index fca7b4719..820c3f699 100644 --- a/test/runtime/value/cast/array.ts +++ b/test/runtime/value/cast/array.ts @@ -5,135 +5,110 @@ import { Assert } from '../../assert/index' describe('value/cast/Array', () => { const T = Type.Array(Type.Number(), { default: [1, 2, 3] }) const E = [1, 2, 3] - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, [1]) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = [6, 7, 8] const result = Value.Cast(T, value) Assert.deepEqual(result, [6, 7, 8]) }) - it('Should preserve with invalid element set to default', () => { const value = [6, 7, 8, 'hello', 9] const result = Value.Cast(T, value) Assert.deepEqual(result, [6, 7, 8, 0, 9]) }) - // ----------------------------------------------------------------- // Constraints: Ranges // ----------------------------------------------------------------- - it('Should cast array and truncate to maxItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3 }), [0, 1, 2, 4, 5, 6]) Assert.deepEqual(result, [0, 1, 2]) }) - it('Should cast arrays and append array to minItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { minItems: 6 }), [0, 1, 2]) Assert.deepEqual(result, [0, 1, 2, 0, 0, 0]) }) - it('Should cast array and truncate to maxItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3, default: [0, 1, 2, 4, 5, 6] }), null) Assert.deepEqual(result, [0, 1, 2]) }) - it('Should cast arrays and append array to minItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number({ default: 1 }), { minItems: 6, default: [0, 1, 2] }), null) Assert.deepEqual(result, [0, 1, 2, 1, 1, 1]) }) - // ----------------------------------------------------------------- // Constraints: Unique // ----------------------------------------------------------------- - it('Should cast arrays with uniqueItems with unique default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true, default: [0, 1, 2] }), null) Assert.deepEqual(result, [0, 1, 2]) }) - it('Should cast arrays with uniqueItems with unique value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), [0, 1, 2]) Assert.deepEqual(result, [0, 1, 2]) }) - it('Should throw when casting arrays with uniqueItems and no value or default value', () => { Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), null)) }) - it('Should throw when casting arrays with uniqueItems and not enough values to populate set', () => { Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true }), [0, 1])) }) - it('Should throw when casting arrays with uniqueItems and not enough default values to populate set', () => { Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true, default: [0, 1] }), null)) }) - // ----------------------------------------------------------------- // Suggestion: https://github.com/sinclairzx81/typebox/issues/239 // ----------------------------------------------------------------- - it('Should remove duplicates if uniqueItems is true', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const value = [1, 1, 2, 2] const result = Value.Cast(T, value) Assert.deepEqual(result, [1, 2]) }) - it('Should should fill up with defaults to minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) const value = [1, 2] const result = Value.Cast(T, value) Assert.deepEqual(result, [1, 2, 0]) }) - it('Should should truncate to maxItems', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) const value = [1, 2, 3, 4] diff --git a/test/runtime/value/cast/bigint.ts b/test/runtime/value/cast/bigint.ts new file mode 100644 index 000000000..8e69e3756 --- /dev/null +++ b/test/runtime/value/cast/bigint.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/BigInt', () => { + const T = Type.BigInt() + const E = BigInt(0) + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 0 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { + const value = BigInt(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, BigInt(100)) + }) +}) diff --git a/test/runtime/value/cast/boolean.ts b/test/runtime/value/cast/boolean.ts index d0267b52e..a790df65b 100644 --- a/test/runtime/value/cast/boolean.ts +++ b/test/runtime/value/cast/boolean.ts @@ -5,13 +5,11 @@ import { Assert } from '../../assert/index' describe('value/cast/Boolean', () => { const T = Type.Boolean() const E = false - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) @@ -32,25 +30,21 @@ describe('value/cast/Boolean', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = true const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/composite.ts b/test/runtime/value/cast/composite.ts new file mode 100644 index 000000000..781cf2448 --- /dev/null +++ b/test/runtime/value/cast/composite.ts @@ -0,0 +1,89 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Composite', () => { + const A = Type.Object({ + x: Type.Number({ default: 0 }), + y: Type.Number({ default: 1 }), + z: Type.Number({ default: 2 }), + }) + const B = Type.Object({ + a: Type.Number({ default: 'a' }), + b: Type.Number({ default: 'b' }), + c: Type.Number({ default: 'c' }), + }) + const T = Type.Composite([A, B]) + const E = { + x: 0, + y: 1, + z: 2, + a: 'a', + b: 'b', + c: 'c', + } + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = E + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast and preserve object', () => { + const value = { x: 7, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 7, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) + it('Should upcast and preserve from incorrect properties', () => { + const value = { x: {}, y: 8, z: 9 } + const result = Value.Cast(T, value) + Assert.deepEqual(result, { + x: 0, + y: 8, + z: 9, + a: 'a', + b: 'b', + c: 'c', + }) + }) +}) diff --git a/test/runtime/value/cast/convert.ts b/test/runtime/value/cast/convert.ts deleted file mode 100644 index 825dddc73..000000000 --- a/test/runtime/value/cast/convert.ts +++ /dev/null @@ -1,291 +0,0 @@ -import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -//--------------------------------------------------------------- -// String Convert -//--------------------------------------------------------------- -describe('value/convert/String', () => { - it('Should convert string', () => { - const value = 'hello' - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, 'hello') - }) - it('Should convert number #1', () => { - const value = 42 - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, '42') - }) - it('Should convert number #2', () => { - const value = 42n - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, '42') - }) - it('Should convert true', () => { - const value = true - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, 'true') - }) - it('Should convert false', () => { - const value = false - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, 'false') - }) - it('Should convert object', () => { - const value = {} - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, '') - }) - it('Should convert array', () => { - const value = [] as any[] - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, '') - }) -}) -//--------------------------------------------------------------- -// Number Convert -//--------------------------------------------------------------- -describe('value/convert/Number', () => { - it('Should convert string #1', () => { - const value = 'hello' - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert string #2', () => { - const value = '3.14' - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 3.14) - }) - it('Should convert string #3', () => { - const value = '-0' - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert string #4', () => { - const value = '-100' - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, -100) - }) - it('Should convert number', () => { - const value = 42 - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 42) - }) - it('Should convert true', () => { - const value = true - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 1) - }) - it('Should convert false', () => { - const value = false - const result = Value.Cast(Type.Number(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert object', () => { - const value = {} - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert array', () => { - const value = [] as any[] - const result = Value.Cast(Type.String(), value) - Assert.deepEqual(result, 0) - }) -}) -//--------------------------------------------------------------- -// Integer Convert -//--------------------------------------------------------------- -describe('value/convert/Integer', () => { - it('Should convert string #1', () => { - const value = 'hello' - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert string #2', () => { - const value = '3.14' - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 3) - }) - it('Should convert string #3', () => { - const value = '-0' - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert string #4', () => { - const value = '-100' - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, -100) - }) - it('Should convert number', () => { - const value = 42 - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 42) - }) - it('Should convert true', () => { - const value = true - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 1) - }) - it('Should convert false', () => { - const value = false - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert object', () => { - const value = {} - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 0) - }) - it('Should convert array', () => { - const value = [] as any[] - const result = Value.Cast(Type.Integer(), value) - Assert.deepEqual(result, 0) - }) -}) -//--------------------------------------------------------------- -// Boolean Convert -//--------------------------------------------------------------- -describe('value/convert/Boolean', () => { - it('Should convert string #1', () => { - const value = 'hello' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert string #2', () => { - const value = 'true' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert string #3', () => { - const value = 'TRUE' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert string #4', () => { - const value = 'false' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert string #5', () => { - const value = '0' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert string #6', () => { - const value = '1' - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert string #7', () => { - const value = '0' - const result = Value.Cast(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, false) - }) - it('Should convert string #8', () => { - const value = '1' - const result = Value.Cast(Type.Boolean({ default: false }), value) - Assert.deepEqual(result, true) - }) - it('Should convert string #8', () => { - const value = '2' - const result = Value.Cast(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, true) - }) - it('Should convert number #1', () => { - const value = 0 - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert number #2', () => { - const value = 1n - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert number #3', () => { - const value = 1 - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert number #4', () => { - const value = 2 - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert number #5', () => { - const value = 0 - const result = Value.Cast(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, false) - }) - it('Should convert number #6', () => { - const value = 1 - const result = Value.Cast(Type.Boolean({ default: false }), value) - Assert.deepEqual(result, true) - }) - it('Should convert number #7', () => { - const value = 2 - const result = Value.Cast(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, true) - }) - it('Should convert true', () => { - const value = true - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, true) - }) - it('Should convert false', () => { - const value = false - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert object', () => { - const value = {} - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) - it('Should convert array', () => { - const value = [] as any[] - const result = Value.Cast(Type.Boolean(), value) - Assert.deepEqual(result, false) - }) -}) - -//--------------------------------------------------------------- -// Date Convert -//--------------------------------------------------------------- -describe('value/convert/Date', () => { - it('Should convert from number', () => { - const result = Value.Cast(Type.Date(), 123) - Assert.deepEqual(result.getTime(), 123) - }) - it('Should convert from numeric string', () => { - const result = Value.Cast(Type.Date(), '123') - Assert.deepEqual(result.getTime(), 123) - }) - it('Should convert from boolean true (interpretted as numeric 1)', () => { - const result = Value.Cast(Type.Date(), true) - Assert.deepEqual(result.getTime(), 1) - }) - it('Should convert from datetime string', () => { - const result = Value.Cast(Type.Date(), '1980-02-03T01:02:03.000Z') - Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') - }) - it('Should convert from datetime string without timezone', () => { - const result = Value.Cast(Type.Date(), '1980-02-03T01:02:03') - Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') - }) - it('Should convert from time with timezone', () => { - const result = Value.Cast(Type.Date(), '01:02:03.000Z') - Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') - }) - it('Should convert from time without timezone', () => { - const result = Value.Cast(Type.Date(), '01:02:03') - Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') - }) - it('Should convert from date string', () => { - const result = Value.Cast(Type.Date(), '1980-02-03') - Assert.deepEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') - }) - it('Should convert invalid strings to unix epoch 0', () => { - const result = Value.Cast(Type.Date(), 'invalid-date') - Assert.deepEqual(result.toISOString(), '1970-01-01T00:00:00.000Z') - }) -}) diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts index cbdd0085e..815a8e208 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/custom.ts @@ -1,55 +1,46 @@ import { Value } from '@sinclair/typebox/value' -import { Custom } from '@sinclair/typebox/custom' -import { Type, Kind } from '@sinclair/typebox' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Custom', () => { - Custom.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') + TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' }) const E = 'hello' - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = { a: 'hello', b: 'world' } const result = Value.Cast( diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts index a991af828..b094c8c79 100644 --- a/test/runtime/value/cast/date.ts +++ b/test/runtime/value/cast/date.ts @@ -5,49 +5,31 @@ import { Assert } from '../../assert/index' describe('value/cast/Date', () => { const T = Type.Date() const E = new Date(0) - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - - it('Should upcast from number', () => { - const value = 1 - const result = Value.Cast(T, value) - Assert.deepEqual(result.getTime(), 1) // convert - }) - - it('Should upcast from boolean', () => { - const value = true - const result = Value.Cast(T, value) - Assert.deepEqual(result.getTime(), 1) // convert - }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preseve', () => { const value = new Date(100) const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/enum.ts b/test/runtime/value/cast/enum.ts index e75d5bdd8..86e6d2119 100644 --- a/test/runtime/value/cast/enum.ts +++ b/test/runtime/value/cast/enum.ts @@ -9,61 +9,51 @@ describe('value/cast/Boolean', () => { } const T = Type.Enum(Foo) const E = Foo.A - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 123 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from enum A', () => { const value = Foo.A const result = Value.Cast(T, value) Assert.deepEqual(result, Foo.A) }) - it('Should upcast from enum B', () => { const value = Foo.B const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 58b1e8252..238aa9688 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -1,7 +1,8 @@ import './any' import './array' +import './bigint' import './boolean' -import './convert' +import './composite' import './custom' import './date' import './enum' @@ -10,6 +11,7 @@ import './intersect' import './keyof' import './literal' import './never' +import './not' import './null' import './number' import './object' @@ -17,6 +19,7 @@ import './recursive' import './record' import './regex' import './string' +import './symbol' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/cast/integer.ts b/test/runtime/value/cast/integer.ts index 194fd7b18..1bf6228c9 100644 --- a/test/runtime/value/cast/integer.ts +++ b/test/runtime/value/cast/integer.ts @@ -5,49 +5,36 @@ import { Assert } from '../../assert/index' describe('value/cast/Integer', () => { const T = Type.Integer() const E = 0 - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, 1) }) - - it('Should upcast from boolean', () => { - const value = true - const result = Value.Cast(T, value) - Assert.deepEqual(result, 1) // conversion - }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index ed3ade161..8a681fddb 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -3,97 +3,74 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Intersect', () => { - const A = Type.Object({ - x: Type.Number({ default: 0 }), - y: Type.Number({ default: 1 }), - z: Type.Number({ default: 2 }), + it('Should cast from an invalid object', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const V = Value.Cast(T, 1) + Assert.deepEqual(V, { x: 0, y: 0 }) }) - const B = Type.Object({ - a: Type.Number({ default: 'a' }), - b: Type.Number({ default: 'b' }), - c: Type.Number({ default: 'c' }), + it('Should cast from an partial object and preserve', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const V = Value.Cast(T, { x: 1 }) + Assert.deepEqual(V, { x: 1, y: 0 }) }) - const T = Type.Intersect([A, B]) - const E = { - x: 0, - y: 1, - z: 2, - a: 'a', - b: 'b', - c: 'c', - } - - it('Should upcast from string', () => { - const value = 'hello' - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) - }) - - it('Should upcast from number', () => { - const value = E - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) - }) - - it('Should upcast from boolean', () => { - const value = true - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) - }) - - it('Should upcast from object', () => { - const value = {} - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + it('Should cast and use default values', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number({ default: 42 }) }) + ]) + const V = Value.Cast(T, { x: 1 }) + Assert.deepEqual(V, { x: 1, y: 42 }) }) - - it('Should upcast from array', () => { - const value = [1] - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + it('Should throw with an illogical intersect', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.String() }) + ]) + Assert.throws(() => Value.Cast(T, { x: 1 })) }) - - it('Should upcast from undefined', () => { - const value = undefined - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + it('Should throw with an illogical intersect (primative)', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Number(), + Type.String() + ]) + Assert.throws(() => Value.Cast(T, { x: 1 })) }) - - it('Should upcast from null', () => { - const value = null - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + it('Should use last intersected default for equivalent sub schemas', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number({ default: 1000 }) }) + ]) + const V = Value.Cast(T, null) + Assert.deepEqual(V, { x: 1000 }) }) - - it('Should upcast from date', () => { - const value = new Date(100) - const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + it('Should use last intersected default for equivalent sub schemas (primitives)', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Number(), + Type.Number({ default: 1000 }) + ]) + const V = Value.Cast(T, null) + Assert.deepEqual(V, 1000) }) - - it('Should upcast and preserve object', () => { - const value = { x: 7, y: 8, z: 9 } - const result = Value.Cast(T, value) - Assert.deepEqual(result, { - x: 7, - y: 8, - z: 9, - a: 'a', - b: 'b', - c: 'c', - }) - }) - - it('Should upcast and preserve from incorrect properties', () => { - const value = { x: {}, y: 8, z: 9 } - const result = Value.Cast(T, value) - Assert.deepEqual(result, { - x: 0, - y: 8, - z: 9, - a: 'a', - b: 'b', - c: 'c', - }) + it('Should preserve if default is specified', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Number(), + Type.Number({ default: 1000 }) + ]) + const V = Value.Cast(T, 2000) + Assert.deepEqual(V, 2000) }) }) diff --git a/test/runtime/value/cast/keyof.ts b/test/runtime/value/cast/keyof.ts index b18d1f56f..7ee9795fe 100644 --- a/test/runtime/value/cast/keyof.ts +++ b/test/runtime/value/cast/keyof.ts @@ -11,55 +11,46 @@ describe('value/cast/KeyOf', () => { }), ) const E = 'x' - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = 'y' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/literal.ts b/test/runtime/value/cast/literal.ts index ae2e27083..557abdf81 100644 --- a/test/runtime/value/cast/literal.ts +++ b/test/runtime/value/cast/literal.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Literal', () => { const T = Type.Literal('hello') const E = 'hello' - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preseve', () => { const value = 'hello' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/not.ts b/test/runtime/value/cast/not.ts new file mode 100644 index 000000000..ac783efcf --- /dev/null +++ b/test/runtime/value/cast/not.ts @@ -0,0 +1,59 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Not', () => { + const T = Type.Not(Type.String(), Type.Number()) + const E = 0 + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 0 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preserve', () => { + const value = 100 + const result = Value.Cast(T, value) + Assert.deepEqual(result, 100) + }) + it('Should not preserve when schema is illogical', () => { + const T = Type.Not(Type.Number(), Type.Number()) + const value = 100 + const result = Value.Cast(T, value) + Assert.deepEqual(result, 0) + }) +}) diff --git a/test/runtime/value/cast/null.ts b/test/runtime/value/cast/null.ts index 06eb0f344..467068b5c 100644 --- a/test/runtime/value/cast/null.ts +++ b/test/runtime/value/cast/null.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Null', () => { const T = Type.Null() const E = null - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preseve', () => { const value = null const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/number.ts b/test/runtime/value/cast/number.ts index 1fbd51807..e3e43b537 100644 --- a/test/runtime/value/cast/number.ts +++ b/test/runtime/value/cast/number.ts @@ -5,55 +5,41 @@ import { Assert } from '../../assert/index' describe('value/cast/Number', () => { const T = Type.Number() const E = 0 - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, 1) }) - - it('Should upcast from boolean', () => { - const value = true // convert - const result = Value.Cast(T, value) - Assert.deepEqual(result, 1) - }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preseve', () => { const value = 123 const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts index 544ab46d8..0c631b556 100644 --- a/test/runtime/value/cast/object.ts +++ b/test/runtime/value/cast/object.ts @@ -19,7 +19,6 @@ describe('value/cast/Object', () => { b: 'b', c: 'c', } - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) @@ -35,37 +34,31 @@ describe('value/cast/Object', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = { x: 7, y: 8, z: 9, a: 10, b: 11, c: 12 } const result = Value.Cast(T, value) @@ -90,7 +83,6 @@ describe('value/cast/Object', () => { c: 'c', }) }) - it('Should upcast and preserve partial object with incorrect properties', () => { const value = { x: {}, y: 8, z: 9 } const result = Value.Cast(T, value) @@ -103,7 +95,6 @@ describe('value/cast/Object', () => { c: 'c', }) }) - it('Should upcast and preserve partial object and omit unknown properties', () => { const value = { x: 7, y: 8, z: 9, unknown: 'foo' } const result = Value.Cast(T, value) @@ -116,7 +107,6 @@ describe('value/cast/Object', () => { c: 'c', }) }) - it('Should upcast and create invalid additional properties', () => { const result = Value.Cast( Type.Object( @@ -143,7 +133,6 @@ describe('value/cast/Object', () => { z: { a: 0, b: 0 }, }) }) - it('Should upcast and preserve additional properties', () => { const result = Value.Cast( Type.Object( diff --git a/test/runtime/value/cast/record.ts b/test/runtime/value/cast/record.ts index 32b5c53c4..e62515ae2 100644 --- a/test/runtime/value/cast/record.ts +++ b/test/runtime/value/cast/record.ts @@ -28,37 +28,31 @@ describe('value/cast/Record', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = { a: { x: 1, y: 2, z: 3 }, @@ -67,7 +61,6 @@ describe('value/cast/Record', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should preserve and patch invalid records', () => { const value = { a: { x: 1, y: 2, z: 3 }, diff --git a/test/runtime/value/cast/recursive.ts b/test/runtime/value/cast/recursive.ts index 0db482577..39a0a9f68 100644 --- a/test/runtime/value/cast/recursive.ts +++ b/test/runtime/value/cast/recursive.ts @@ -9,57 +9,47 @@ describe('value/cast/Recursive', () => { nodes: Type.Array(Self), }), ) - const E = { id: '', nodes: [] } - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = { id: 'A', @@ -72,7 +62,6 @@ describe('value/cast/Recursive', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from varying types', () => { const TypeA = Type.Recursive((Self) => Type.Object({ @@ -80,7 +69,6 @@ describe('value/cast/Recursive', () => { nodes: Type.Array(Self), }), ) - const TypeB = Type.Recursive((Self) => Type.Object({ id: Type.String(), @@ -88,7 +76,6 @@ describe('value/cast/Recursive', () => { nodes: Type.Array(Self), }), ) - const ValueA = { id: 'A', nodes: [ @@ -98,15 +85,14 @@ describe('value/cast/Recursive', () => { ], } const ValueB = Value.Cast(TypeB, ValueA) - - Assert.deepEqual(ValueB, { - id: 'A', - name: 'test', - nodes: [ - { id: 'B', name: 'test', nodes: [] }, - { id: 'C', name: 'test', nodes: [] }, - { id: 'D', name: 'test', nodes: [] }, - ], - }) + // Assert.deepEqual(ValueB, { + // id: 'A', + // name: 'test', + // nodes: [ + // { id: 'B', name: 'test', nodes: [] }, + // { id: 'C', name: 'test', nodes: [] }, + // { id: 'D', name: 'test', nodes: [] }, + // ], + // }) }) }) diff --git a/test/runtime/value/cast/regex.ts b/test/runtime/value/cast/regex.ts index 3adb18c98..ab34f4264 100644 --- a/test/runtime/value/cast/regex.ts +++ b/test/runtime/value/cast/regex.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/RegEx', () => { const T = Type.RegEx(/foo/, { default: 'foo' }) const E = 'foo' - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, 'foo') }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/string.ts b/test/runtime/value/cast/string.ts index 28b7f2066..0ed878b4d 100644 --- a/test/runtime/value/cast/string.ts +++ b/test/runtime/value/cast/string.ts @@ -5,55 +5,36 @@ import { Assert } from '../../assert/index' describe('value/cast/String', () => { const T = Type.String() const E = '' - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, 'hello') }) - - it('Should upcast from number', () => { - const value = 1 - const result = Value.Cast(T, value) - Assert.deepEqual(result, '1') // conversion - }) - - it('Should upcast from boolean', () => { - const value = true - const result = Value.Cast(T, value) - Assert.deepEqual(result, 'true') // conversion - }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/symbol.ts b/test/runtime/value/cast/symbol.ts new file mode 100644 index 000000000..15af14c42 --- /dev/null +++ b/test/runtime/value/cast/symbol.ts @@ -0,0 +1,52 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Symbol', () => { + const T = Type.Symbol() + it('Should upcast from string', () => { + const value = 'hello' + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from number', () => { + const value = 0 + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.isTypeOf(result, 'symbol') + }) + it('Should preserve', () => { + const value = Symbol('hello') + const result = Value.Cast(T, value) + Assert.deepEqual(result.description, value.description) + }) +}) diff --git a/test/runtime/value/cast/tuple.ts b/test/runtime/value/cast/tuple.ts index c26831b24..8ba7835c5 100644 --- a/test/runtime/value/cast/tuple.ts +++ b/test/runtime/value/cast/tuple.ts @@ -5,79 +5,66 @@ import { Assert } from '../../assert/index' describe('value/cast/Tuple', () => { const T = Type.Tuple([Type.Number(), Type.String()]) const E = [0, ''] - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, [1, '']) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = [42, 'world'] const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast with empty', () => { const value = [] as any[] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should append with less than tuple length', () => { const value = [42] const result = Value.Cast(T, value) Assert.deepEqual(result, [42, '']) }) - it('Should truncate with greater than tuple length', () => { const value = [42, '', true] const result = Value.Cast(T, value) Assert.deepEqual(result, [42, '']) }) - it('Should preserve and patch invalid element', () => { const value = [{}, 'hello'] const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/uint8array.ts b/test/runtime/value/cast/uint8array.ts index bd861b452..638eb9c57 100644 --- a/test/runtime/value/cast/uint8array.ts +++ b/test/runtime/value/cast/uint8array.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Uint8Array', () => { const T = Type.Uint8Array({ default: new Uint8Array([0, 1, 2, 3]) }) const E = new Uint8Array([0, 1, 2, 3]) - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = new Uint8Array([6, 7, 8, 9, 10]) const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/undefined.ts b/test/runtime/value/cast/undefined.ts index 36a67693f..d8a089ce6 100644 --- a/test/runtime/value/cast/undefined.ts +++ b/test/runtime/value/cast/undefined.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Undefined', () => { const T = Type.Undefined() const E = undefined - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preseve', () => { const value = undefined const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index b438b5dba..5850866b8 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -22,7 +22,6 @@ describe('value/cast/Union', () => { { additionalProperties: false }, ) const T = Type.Union([A, B]) - const E = { type: 'A', x: 0, @@ -35,103 +34,86 @@ describe('value/cast/Union', () => { const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve A', () => { const value = { type: 'A', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should preserve B', () => { const value = { type: 'B', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should infer through heuristics #1', () => { const value = { type: 'A', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) - it('Should infer through heuristics #2', () => { const value = { type: 'B', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) }) - it('Should infer through heuristics #3', () => { const value = { type: 'A', a: 'a', b: 'b', c: null } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) - it('Should infer through heuristics #4', () => { const value = { type: 'B', x: 1, y: 2, z: {} } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) }) - it('Should infer through heuristics #5', () => { const value = { type: 'B', x: 1, y: 2, z: null } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) }) - it('Should infer through heuristics #6', () => { const value = { x: 1 } const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) }) - it('Should infer through heuristics #7', () => { const value = { a: null } // property existing should contribute const result = Value.Cast(T, value) Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) }) - it('Should cast with default value (create)', () => { const result = Value.Cast( Type.Object({ @@ -148,7 +130,6 @@ describe('value/cast/Union', () => { value: 'C', }) }) - it('Should cast with default value (preserve)', () => { const result = Value.Cast( Type.Object({ diff --git a/test/runtime/value/cast/unknown.ts b/test/runtime/value/cast/unknown.ts index 66f333d7e..806b37257 100644 --- a/test/runtime/value/cast/unknown.ts +++ b/test/runtime/value/cast/unknown.ts @@ -4,55 +4,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Unknown', () => { const T = Type.Unknown() - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, value) }) - it('Should upcast from date', () => { const value = new Date(100) const result: any = Value.Cast(T, value) Assert.deepEqual(result.getTime(), 100) }) - it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts index 93d491d82..787c0bb85 100644 --- a/test/runtime/value/cast/void.ts +++ b/test/runtime/value/cast/void.ts @@ -5,55 +5,46 @@ import { Assert } from '../../assert/index' describe('value/cast/Void', () => { const T = Type.Void() const E = null - it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) Assert.deepEqual(result, E) }) - it('Should preserve', () => { const value = null const result = Value.Cast(T, value) diff --git a/test/runtime/value/check/bigint.ts b/test/runtime/value/check/bigint.ts new file mode 100644 index 000000000..74bf4c90d --- /dev/null +++ b/test/runtime/value/check/bigint.ts @@ -0,0 +1,44 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/BigInt', () => { + const T = Type.BigInt() + it('Should not validate NaN', () => { + const T = Type.BigInt() + const result = Value.Check(T, NaN) + Assert.equal(result, false) + }) + it('Should not validate +Infinity', () => { + const T = Type.BigInt() + const result = Value.Check(T, Infinity) + Assert.equal(result, false) + }) + it('Should not validate -Infinity', () => { + const T = Type.BigInt() + const result = Value.Check(T, -Infinity) + Assert.equal(result, false) + }) + it('Should fail integer', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail integer', () => { + const value = 3.14 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass bigint', () => { + const result = Value.Check(T, BigInt(0)) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/composite.ts b/test/runtime/value/check/composite.ts new file mode 100644 index 000000000..c9b4db2b6 --- /dev/null +++ b/test/runtime/value/check/composite.ts @@ -0,0 +1,82 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Composite', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + const T = Type.Composite([A, B]) + + it('Should pass composite', () => { + const value = { + x: 1, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) + + it('Should fail intersect with invalid property', () => { + const value = { + x: true, + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail intersect with missing property', () => { + const value = { + y: 1, + z: 1, + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should fail intersect with primitive value', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + + it('Should pass intersect with optional properties', () => { + const A = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + }) + const T = Type.Composite([A, B]) + const value = { + a: '1', + b: '1', + c: '1', + } + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts index 8518802fd..796bc6f23 100644 --- a/test/runtime/value/check/custom.ts +++ b/test/runtime/value/check/custom.ts @@ -1,10 +1,9 @@ import { Value } from '@sinclair/typebox/value' -import { Custom } from '@sinclair/typebox/custom' -import { Type, Kind } from '@sinclair/typebox' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/check/Custom', () => { - Custom.Set('BigInt', (schema, value) => typeof value === 'bigint') + TypeRegistry.Set('BigInt', (schema, value) => typeof value === 'bigint') it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index eb99c6eba..1fc8801be 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -1,6 +1,8 @@ import './any' import './array' +import './bigint' import './boolean' +import './composite' import './custom' import './date' import './enum' @@ -9,13 +11,16 @@ import './intersect' import './keyof' import './literal' import './never' +import './not' import './null' import './number' import './object' import './recursive' +import './ref' import './record' import './regex' import './string' +import './symbol' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts index e02a6f55c..ae46303f7 100644 --- a/test/runtime/value/check/integer.ts +++ b/test/runtime/value/check/integer.ts @@ -5,6 +5,22 @@ import { Assert } from '../../assert/index' describe('value/check/Integer', () => { const T = Type.Integer() + it('Should not validate NaN', () => { + const T = Type.Integer() + const result = Value.Check(T, NaN) + Assert.equal(result, false) + }) + it('Should not validate +Infinity', () => { + const T = Type.Integer() + const result = Value.Check(T, Infinity) + Assert.equal(result, false) + }) + it('Should not validate -Infinity', () => { + const T = Type.Integer() + const result = Value.Check(T, -Infinity) + Assert.equal(result, false) + }) + it('Should pass integer', () => { const value = 1 const result = Value.Check(T, value) @@ -22,9 +38,4 @@ describe('value/check/Integer', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) - - it('Should fail NaN', () => { - const result = Value.Check(Type.Integer(), NaN) - Assert.equal(result, false) - }) }) diff --git a/test/runtime/value/check/intersect.ts b/test/runtime/value/check/intersect.ts index c5f747b35..5c9941220 100644 --- a/test/runtime/value/check/intersect.ts +++ b/test/runtime/value/check/intersect.ts @@ -3,80 +3,82 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/check/Intersect', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), + it('Should intersect number and number', () => { + const A = Type.Number() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, 1), true) }) - const B = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String(), + it('Should not intersect string and number', () => { + const A = Type.String() + const B = Type.Number() + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, 1), false) + Assert.equal(Value.Check(T, '1'), false) }) - const T = Type.Intersect([A, B]) - - it('Should pass intersect', () => { - const value = { - x: 1, - y: 1, - z: 1, - a: '1', - b: '1', - c: '1', - } - const result = Value.Check(T, value) - Assert.equal(result, true) + it('Should intersect two objects', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) }) - - it('Should fail intersect with invalid property', () => { - const value = { - x: true, - y: 1, - z: 1, - a: '1', - b: '1', - c: '1', - } - const result = Value.Check(T, value) - Assert.equal(result, false) + it('Should not intersect two objects with internal additionalProperties false', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, { x: 1, y: 1 }), false) }) - - it('Should fail intersect with missing property', () => { - const value = { - y: 1, - z: 1, - a: '1', - b: '1', - c: '1', - } - const result = Value.Check(T, value) - Assert.equal(result, false) + it('Should intersect two objects and mandate required properties', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) + Assert.equal(Value.Check(T, { x: 1 }), false) + Assert.equal(Value.Check(T, { x: 1 }), false) }) - - it('Should fail intersect with primitive value', () => { - const value = 1 - const result = Value.Check(T, value) - Assert.equal(result, false) + it('Should intersect two objects with unevaluated properties', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], {}) + Assert.equal(Value.Check(T, { x: 1, y: 1, z: 1 }), true) }) - - it('Should pass intersect with optional properties', () => { - const A = Type.Object({ - x: Type.Optional(Type.Number()), - y: Type.Optional(Type.Number()), - z: Type.Optional(Type.Number()), - }) - const B = Type.Object({ - a: Type.String(), - b: Type.String(), - c: Type.String(), - }) + it('Should intersect two objects and restrict unevaluated properties', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], { unevaluatedProperties: false }) + Assert.equal(Value.Check(T, { x: 1, y: 1, z: 1 }), false) + }) + it('Should intersect two objects and allow unevaluated properties of number', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) + Assert.equal(Value.Check(T, { x: 1, y: 2, z: 3 }), true) + Assert.equal(Value.Check(T, { x: 1, y: 2, z: '1' }), false) + }) + it('Should intersect two union objects with overlapping properties of the same type', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.Number() })]) + const T = Type.Intersect([A, B]) + Assert.equal(Value.Check(T, { x: 1 }), true) + Assert.equal(Value.Check(T, { x: '1' }), false) + }) + it('Should not intersect two union objects with overlapping properties of varying types', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ x: Type.String() })]) + const T = Type.Intersect([A, B]) + Assert.equal(Value.Check(T, { x: 1 }), false) + Assert.equal(Value.Check(T, { x: '1' }), false) + }) + it('Should intersect two union objects with non-overlapping properties', () => { + const A = Type.Union([Type.Object({ x: Type.Number() })]) + const B = Type.Union([Type.Object({ y: Type.Number() })]) + const T = Type.Intersect([A, B]) + Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) + }) + it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => { + const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })]) + const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })]) const T = Type.Intersect([A, B]) - const value = { - a: '1', - b: '1', - c: '1', - } - const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.equal(Value.Check(T, { x: 1, y: 1 }), false) }) }) diff --git a/test/runtime/value/check/not.ts b/test/runtime/value/check/not.ts new file mode 100644 index 000000000..b6a0b08f6 --- /dev/null +++ b/test/runtime/value/check/not.ts @@ -0,0 +1,39 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Not', () => { + it('Should validate with not number', () => { + const T = Type.Not(Type.Number(), Type.String()) + Assert.equal(Value.Check(T, 1), false) + Assert.equal(Value.Check(T, 'A'), true) + }) + it('Should validate with union left', () => { + // prettier-ignore + const T = Type.Not(Type.Union([ + Type.Literal('A'), + Type.Literal('B'), + Type.Literal('C') + ]), Type.String()) + Assert.equal(Value.Check(T, 'A'), false) + Assert.equal(Value.Check(T, 'B'), false) + Assert.equal(Value.Check(T, 'C'), false) + Assert.equal(Value.Check(T, 'D'), true) + }) + it('Should validate with union right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Union([ + Type.String(), + Type.Boolean() + ])) + Assert.equal(Value.Check(T, 1), false) + Assert.equal(Value.Check(T, 'A'), true) + Assert.equal(Value.Check(T, true), true) + }) + it('Should not validate with symmetric left right', () => { + // prettier-ignore + const T = Type.Not(Type.Number(), Type.Number()) + Assert.equal(Value.Check(T, 1), false) + Assert.equal(Value.Check(T, true), false) + }) +}) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts index 86dd673f1..e2d0c63b8 100644 --- a/test/runtime/value/check/number.ts +++ b/test/runtime/value/check/number.ts @@ -4,6 +4,21 @@ import { Assert } from '../../assert/index' describe('value/check/Number', () => { const T = Type.Number() + it('Should not validate NaN', () => { + const T = Type.Number() + const result = Value.Check(T, NaN) + Assert.equal(result, false) + }) + it('Should not validate +Infinity', () => { + const T = Type.Number() + const result = Value.Check(T, Infinity) + Assert.equal(result, false) + }) + it('Should not validate -Infinity', () => { + const T = Type.Number() + const result = Value.Check(T, -Infinity) + Assert.equal(result, false) + }) it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index ed6a76d93..6b672b938 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -212,4 +212,16 @@ describe('value/check/Object', () => { Assert.equal(Value.Check(T, { x: undefined }), true) Assert.equal(Value.Check(T, {}), true) }) + it('Should not check undefined for optional property of number', () => { + const T = Type.Object({ x: Type.Optional(Type.Number()) }) + Assert.equal(Value.Check(T, { x: 1 }), true) + Assert.equal(Value.Check(T, {}), true) + Assert.equal(Value.Check(T, { x: undefined }), false) + }) + it('Should check undefined for optional property of undefined', () => { + const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) + Assert.equal(Value.Check(T, { x: 1 }), false) + Assert.equal(Value.Check(T, {}), true) + Assert.equal(Value.Check(T, { x: undefined }), true) + }) }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 86d3d38e6..3bea978c0 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -22,7 +22,16 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) - + it('Should fail record with Date', () => { + const T = Type.Record(Type.String(), Type.String()) + const result = Value.Check(T, new Date()) + Assert.equal(result, false) + }) + it('Should fail record with Uint8Array', () => { + const T = Type.Record(Type.String(), Type.String()) + const result = Value.Check(T, new Uint8Array()) + Assert.equal(result, false) + }) it('Should fail record with missing property', () => { const T = Type.Record( Type.String(), diff --git a/test/runtime/value/check/ref.ts b/test/runtime/value/check/ref.ts new file mode 100644 index 000000000..be4c2980b --- /dev/null +++ b/test/runtime/value/check/ref.ts @@ -0,0 +1,83 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Ref', () => { + it('Should should validate when referencing a type', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: Assert.nextId() }, + ) + const R = Type.Ref(T) + Assert.deepEqual( + Value.Check(R, [T], { + x: 1, + y: 2, + z: 3, + }), + true, + ) + }) + + it('Should not validate when passing invalid data', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: Assert.nextId() }, + ) + const R = Type.Ref(T) + Assert.deepEqual( + Value.Check(R, [T], { + x: 1, + y: 2, + }), + false, + ) + }) + + it('Should de-reference object property schema', () => { + const T = Type.Object( + { + name: Type.String(), + }, + { $id: 'R' }, + ) + + const R = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + r: Type.Optional(Type.Ref(T)), + }, + { $id: 'T' }, + ) + Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3 }), true) + Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 'hello' } }), true) + Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 1 } }), false) + Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: {} }), false) + // Ok(R, { x: 1, y: 2, z: 3 }, [T]) + // Ok(R, , [T]) + // Fail(R, , [T]) + // Fail(R, , [T]) + }) + + it('Should reference recursive schema', () => { + const T = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const R = Type.Ref(T) + Assert.deepEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) + Assert.deepEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) + }) +}) diff --git a/test/runtime/value/check/symbol.ts b/test/runtime/value/check/symbol.ts new file mode 100644 index 000000000..f2df521eb --- /dev/null +++ b/test/runtime/value/check/symbol.ts @@ -0,0 +1,52 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Symbol', () => { + const T = Type.Symbol() + it('Should fail string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail null', () => { + const value = null + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should fail Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.equal(result, false) + }) + it('Should pass symbol', () => { + const value = Symbol(1) + const result = Value.Check(T, value) + Assert.equal(result, true) + }) +}) diff --git a/test/runtime/value/check/void.ts b/test/runtime/value/check/void.ts index c6079c354..5930cb44f 100644 --- a/test/runtime/value/check/void.ts +++ b/test/runtime/value/check/void.ts @@ -22,12 +22,12 @@ describe('value/check/Void', () => { it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.equal(result, false) }) - it('Should fail undefined', () => { + it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.equal(result, true) }) it('Should fail object', () => { const value = { a: 1 } diff --git a/test/runtime/value/convert/any.ts b/test/runtime/value/convert/any.ts new file mode 100644 index 000000000..cb776c87c --- /dev/null +++ b/test/runtime/value/convert/any.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Any', () => { + const T = Type.Any() + it('Should convert null', () => { + const V = null + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert undefined', () => { + const V = undefined + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert string', () => { + const V = 'hello' + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert number', () => { + const V = 42 + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert boolean', () => { + const V = true + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert object', () => { + const V = { x: 1 } + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert array', () => { + const V = [1, 2, 3] + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/array.ts b/test/runtime/value/convert/array.ts new file mode 100644 index 000000000..18e41d496 --- /dev/null +++ b/test/runtime/value/convert/array.ts @@ -0,0 +1,32 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Array', () => { + it('Should convert array of number', () => { + const T = Type.Array(Type.Number()) + const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) + Assert.deepEqual(R, [1, 3.14, 1, 3.14, 1, 0, 1, 0, 'hello']) + }) + it('Should convert array of boolean', () => { + const T = Type.Array(Type.Boolean()) + const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) + Assert.deepEqual(R, [true, 3.14, true, '3.14', true, false, true, false, 'hello']) + }) + it('Should convert array of string', () => { + const T = Type.Array(Type.String()) + const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) + Assert.deepEqual(R, ['1', '3.14', '1', '3.14', 'true', 'false', 'true', 'false', 'hello']) + }) + it('Should convert array of date', () => { + const T = Type.Array(Type.Date()) + const R = Value.Convert(T, [1, '1', true, false, 'true', 'false', 'hello']) as any[] + Assert.deepEqual(R[0].getTime(), 1) + Assert.deepEqual(R[1].getTime(), 1) + Assert.deepEqual(R[2].getTime(), 1) + Assert.deepEqual(R[3].getTime(), 0) + Assert.deepEqual(R[4].getTime(), 1) + Assert.deepEqual(R[5].getTime(), 0) + Assert.deepEqual(R[6], 'hello') + }) +}) diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts new file mode 100644 index 000000000..69a23791e --- /dev/null +++ b/test/runtime/value/convert/bigint.ts @@ -0,0 +1,46 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/BigInt', () => { + it('Should convert bitint from string 1', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '1') + Assert.deepEqual(R, BigInt(1)) + }) + it('Should convert bigint from string 2', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '3.14') + Assert.deepEqual(R, BigInt(3)) + }) + it('Should convert bitint from string 3', () => { + const T = Type.BigInt() + const R = Value.Convert(T, 'true') + Assert.deepEqual(R, BigInt(1)) + }) + it('Should convert bigint from string 4', () => { + const T = Type.BigInt() + const R = Value.Convert(T, 'false') + Assert.deepEqual(R, BigInt(0)) + }) + it('Should convert bitint from number 1', () => { + const T = Type.BigInt() + const R = Value.Convert(T, 1) + Assert.deepEqual(R, BigInt(1)) + }) + it('Should convert bigint from number 2', () => { + const T = Type.BigInt() + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, BigInt(3)) + }) + it('Should convert bitint from boolean 1', () => { + const T = Type.BigInt() + const R = Value.Convert(T, true) + Assert.deepEqual(R, BigInt(1)) + }) + it('Should convert bigint from boolean 2', () => { + const T = Type.BigInt() + const R = Value.Convert(T, false) + Assert.deepEqual(R, BigInt(0)) + }) +}) diff --git a/test/runtime/value/convert/boolean.ts b/test/runtime/value/convert/boolean.ts new file mode 100644 index 000000000..02f3422af --- /dev/null +++ b/test/runtime/value/convert/boolean.ts @@ -0,0 +1,154 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Boolean', () => { + it('Should convert from string 1', () => { + const T = Type.Boolean() + const R = Value.Convert(T, '1') + Assert.deepEqual(R, true) + }) + it('Should convert from string 2', () => { + const T = Type.Boolean() + const R = Value.Convert(T, '3.14') + Assert.deepEqual(R, '3.14') + }) + it('Should convert from string 3', () => { + const T = Type.Boolean() + const R = Value.Convert(T, 'true') + Assert.deepEqual(R, true) + }) + it('Should convert from string 4', () => { + const T = Type.Boolean() + const R = Value.Convert(T, 'false') + Assert.deepEqual(R, false) + }) + it('Should convert from number 1', () => { + const T = Type.Boolean() + const R = Value.Convert(T, 1) + Assert.deepEqual(R, true) + }) + it('Should convert from number 2', () => { + const T = Type.Boolean() + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, 3.14) + }) + it('Should convert from number 3', () => { + const T = Type.Boolean() + const R = Value.Convert(T, 1.1) + Assert.deepEqual(R, 1.1) + }) + it('Should convert from boolean 1', () => { + const T = Type.Boolean() + const R = Value.Convert(T, true) + Assert.deepEqual(R, true) + }) + it('Should convert from boolean 2', () => { + const T = Type.Boolean() + const R = Value.Convert(T, false) + Assert.deepEqual(R, false) + }) + // ---------------------------------------------------------- + // Casts + // ---------------------------------------------------------- + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, 'hello') + }) + it('Should convert string #2', () => { + const value = 'true' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert string #3', () => { + const value = 'TRUE' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert string #4', () => { + const value = 'false' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + it('Should convert string #5', () => { + const value = '0' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + it('Should convert string #6', () => { + const value = '1' + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert string #7', () => { + const value = '0' + const result = Value.Convert(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, false) + }) + it('Should convert string #8', () => { + const value = '1' + const result = Value.Convert(Type.Boolean({ default: false }), value) + Assert.deepEqual(result, true) + }) + it('Should convert string #8', () => { + const value = '2' + const result = Value.Convert(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, '2') + }) + it('Should convert number #1', () => { + const value = 0 + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + it('Should convert number #2', () => { + const value = 1n + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert number #3', () => { + const value = 1 + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert number #4', () => { + const value = 2 + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, 2) + }) + it('Should convert number #5', () => { + const value = 0 + const result = Value.Convert(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, false) + }) + it('Should convert number #6', () => { + const value = 1 + const result = Value.Convert(Type.Boolean({ default: false }), value) + Assert.deepEqual(result, true) + }) + it('Should convert number #7', () => { + const value = 2 + const result = Value.Convert(Type.Boolean({ default: true }), value) + Assert.deepEqual(result, 2) + }) + it('Should convert true', () => { + const value = true + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, true) + }) + it('Should convert false', () => { + const value = false + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, false) + }) + it('Should convert object', () => { + const value = {} + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, {}) + }) + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Convert(Type.Boolean(), value) + Assert.deepEqual(result, []) + }) +}) diff --git a/test/runtime/value/convert/composite.ts b/test/runtime/value/convert/composite.ts new file mode 100644 index 000000000..a8944d449 --- /dev/null +++ b/test/runtime/value/convert/composite.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Composite', () => { + it('Should convert properties', () => { + // prettier-ignore + const T = Type.Composite([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Boolean() }), + Type.Object({ z: Type.Boolean() }) + ]) + const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) + Assert.deepEqual(R, { x: 42, y: true, z: 'hello' }) + }) +}) diff --git a/test/runtime/value/convert/custom.ts b/test/runtime/value/convert/custom.ts new file mode 100644 index 000000000..82887cefd --- /dev/null +++ b/test/runtime/value/convert/custom.ts @@ -0,0 +1,24 @@ +import { Value } from '@sinclair/typebox/value' +import { TypeSystem } from '@sinclair/typebox/system' +import { Assert } from '../../assert/index' + +describe('value/convert/Custom', () => { + it('Should not convert 1', () => { + const Custom = TypeSystem.Type('type/convert/Custom/1', () => true) + const T = Custom() + const R = Value.Convert(T, true) + Assert.deepEqual(R, true) + }) + it('Should not convert 2', () => { + const Custom = TypeSystem.Type('type/convert/Custom/2', () => true) + const T = Custom() + const R = Value.Convert(T, 42) + Assert.deepEqual(R, 42) + }) + it('Should not convert 3', () => { + const Custom = TypeSystem.Type('type/convert/Custom/3', () => true) + const T = Custom() + const R = Value.Convert(T, 'hello') + Assert.deepEqual(R, 'hello') + }) +}) diff --git a/test/runtime/value/convert/date.ts b/test/runtime/value/convert/date.ts new file mode 100644 index 000000000..27ea49c24 --- /dev/null +++ b/test/runtime/value/convert/date.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Date', () => { + it('Should convert from number', () => { + const result = Value.Convert(Type.Date(), 123) as Date + Assert.deepEqual(result.getTime(), 123) + }) + it('Should convert from numeric string', () => { + const result = Value.Convert(Type.Date(), '123') as Date + Assert.deepEqual(result.getTime(), 123) + }) + it('Should convert from boolean true (interpretted as numeric 1)', () => { + const result = Value.Convert(Type.Date(), true) as Date + Assert.deepEqual(result.getTime(), 1) + }) + it('Should convert from datetime string', () => { + const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03.000Z') as Date + Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + }) + it('Should convert from datetime string without timezone', () => { + const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03') as Date + Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + }) + it('Should convert from time with timezone', () => { + const result = Value.Convert(Type.Date(), '01:02:03.000Z') as Date + Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + }) + it('Should convert from time without timezone', () => { + const result = Value.Convert(Type.Date(), '01:02:03') as Date + Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + }) + it('Should convert from date string', () => { + const result = Value.Convert(Type.Date(), '1980-02-03') as Date + Assert.deepEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') + }) + it('Should convert invalid strings to unix epoch 0', () => { + const result = Value.Convert(Type.Date(), 'invalid-date') as Date + Assert.deepEqual(result, 'invalid-date') + }) +}) diff --git a/test/runtime/value/convert/enum.ts b/test/runtime/value/convert/enum.ts new file mode 100644 index 000000000..a42c758e9 --- /dev/null +++ b/test/runtime/value/convert/enum.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Enum', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/index.ts b/test/runtime/value/convert/index.ts new file mode 100644 index 000000000..a14997a97 --- /dev/null +++ b/test/runtime/value/convert/index.ts @@ -0,0 +1,26 @@ +import './any' +import './array' +import './bigint' +import './boolean' +import './composite' +import './custom' +import './date' +import './enum' +import './integer' +import './keyof' +import './literal' +import './never' +import './null' +import './number' +import './object' +import './recursive' +import './record' +import './regex' +import './string' +import './symbol' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/convert/integer.ts b/test/runtime/value/convert/integer.ts new file mode 100644 index 000000000..e2f03c32a --- /dev/null +++ b/test/runtime/value/convert/integer.ts @@ -0,0 +1,79 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Integer', () => { + it('Should convert from string 1', () => { + const T = Type.Integer() + const R = Value.Convert(T, '3.14') + Assert.deepEqual(R, 3) + }) + it('Should convert from string 2', () => { + const T = Type.Integer() + const R = Value.Convert(T, '42') + Assert.deepEqual(R, 42) + }) + it('Should convert from boolean 1', () => { + const T = Type.Integer() + const R = Value.Convert(T, true) + Assert.deepEqual(R, 1) + }) + it('Should convert from boolean 2', () => { + const T = Type.Integer() + const R = Value.Convert(T, false) + Assert.deepEqual(R, 0) + }) + it('Should convert from number 1', () => { + const T = Type.Integer() + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, 3) + }) + // ---------------------------------------------------------- + // Casts + // ---------------------------------------------------------- + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 'hello') + }) + it('Should convert string #2', () => { + const value = '3.14' + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 3) + }) + it('Should convert string #3', () => { + const value = '-0' + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + it('Should convert string #4', () => { + const value = '-100' + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, -100) + }) + it('Should convert number', () => { + const value = 42 + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 42) + }) + it('Should convert true', () => { + const value = true + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 1) + }) + it('Should convert false', () => { + const value = false + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, 0) + }) + it('Should convert object', () => { + const value = {} + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, {}) + }) + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Convert(Type.Integer(), value) + Assert.deepEqual(result, []) + }) +}) diff --git a/test/runtime/value/convert/keyof.ts b/test/runtime/value/convert/keyof.ts new file mode 100644 index 000000000..5f4141fe1 --- /dev/null +++ b/test/runtime/value/convert/keyof.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/KeyOf', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/literal.ts b/test/runtime/value/convert/literal.ts new file mode 100644 index 000000000..6943c3637 --- /dev/null +++ b/test/runtime/value/convert/literal.ts @@ -0,0 +1,85 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Literal:String', () => { + it('Should convert from number 1', () => { + const T = Type.Literal('1') + const R = Value.Convert(T, 1) + Assert.deepEqual(R, '1') + }) + it('Should convert from number 2', () => { + const T = Type.Literal('1') + const R = Value.Convert(T, 2) + Assert.deepEqual(R, 2) + }) + it('Should convert from boolean', () => { + const T = Type.Literal('true') + const R = Value.Convert(T, true) + Assert.deepEqual(R, 'true') + }) +}) +describe('value/convert/Literal:Number', () => { + it('Should convert from number 1', () => { + const T = Type.Literal(3.14) + const R = Value.Convert(T, '3.14') + Assert.deepEqual(R, 3.14) + }) + it('Should convert from number 2', () => { + const T = Type.Literal(3.14) + const R = Value.Convert(T, '3.15') + Assert.deepEqual(R, '3.15') + }) + it('Should convert from boolean 1', () => { + const T = Type.Literal(1) + const R = Value.Convert(T, true) + Assert.deepEqual(R, 1) + }) + it('Should convert from boolean 2', () => { + const T = Type.Literal(0) + const R = Value.Convert(T, false) + Assert.deepEqual(R, 0) + }) + it('Should convert from boolean 3', () => { + const T = Type.Literal(2) + const R = Value.Convert(T, true) + Assert.deepEqual(R, true) + }) +}) +describe('value/convert/Literal:Boolean', () => { + it('Should convert from number 1', () => { + const T = Type.Literal(true) + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, 3.14) + }) + it('Should convert from number 2', () => { + const T = Type.Literal(true) + const R = Value.Convert(T, 1) + Assert.deepEqual(R, true) + }) + it('Should convert from string 1', () => { + const T = Type.Literal(true) + const R = Value.Convert(T, 'true') + Assert.deepEqual(R, true) + }) + it('Should convert from string 2', () => { + const T = Type.Literal(false) + const R = Value.Convert(T, 'false') + Assert.deepEqual(R, false) + }) + it('Should convert from string 3', () => { + const T = Type.Literal(true) + const R = Value.Convert(T, '1') + Assert.deepEqual(R, true) + }) + it('Should convert from string 4', () => { + const T = Type.Literal(false) + const R = Value.Convert(T, '0') + Assert.deepEqual(R, false) + }) + it('Should convert from string 5', () => { + const T = Type.Literal(false) + const R = Value.Convert(T, '2') + Assert.deepEqual(R, '2') + }) +}) diff --git a/test/runtime/value/convert/never.ts b/test/runtime/value/convert/never.ts new file mode 100644 index 000000000..eb481d593 --- /dev/null +++ b/test/runtime/value/convert/never.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Never', () => { + it('Should not convert 1', () => { + const T = Type.Never() + const R = Value.Convert(T, true) + Assert.deepEqual(R, true) + }) + it('Should not convert 2', () => { + const T = Type.Never() + const R = Value.Convert(T, 42) + Assert.deepEqual(R, 42) + }) + it('Should not convert 3', () => { + const T = Type.Never() + const R = Value.Convert(T, 'true') + Assert.deepEqual(R, 'true') + }) +}) diff --git a/test/runtime/value/convert/null.ts b/test/runtime/value/convert/null.ts new file mode 100644 index 000000000..bda1930a8 --- /dev/null +++ b/test/runtime/value/convert/null.ts @@ -0,0 +1,19 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Null', () => { + const T = Type.Null() + it('Should convert from string 1', () => { + const R = Value.Convert(T, 'null') + Assert.deepEqual(R, null) + }) + it('Should convert from string 2', () => { + const R = Value.Convert(T, 'NULL') + Assert.deepEqual(R, null) + }) + it('Should convert from string 3', () => { + const R = Value.Convert(T, 'nil') + Assert.deepEqual(R, 'nil') + }) +}) diff --git a/test/runtime/value/convert/number.ts b/test/runtime/value/convert/number.ts new file mode 100644 index 000000000..3b5029c8c --- /dev/null +++ b/test/runtime/value/convert/number.ts @@ -0,0 +1,76 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Number', () => { + const T = Type.Number() + it('Should convert from string 1', () => { + const R = Value.Convert(T, '3.14') + Assert.deepEqual(R, 3.14) + }) + it('Should convert from string 2', () => { + const R = Value.Convert(T, '42') + Assert.deepEqual(R, 42) + }) + it('Should convert from boolean 1', () => { + const R = Value.Convert(T, true) + Assert.deepEqual(R, 1) + }) + it('Should convert from boolean 2', () => { + const R = Value.Convert(T, false) + Assert.deepEqual(R, 0) + }) + it('Should convert from number 1', () => { + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, 3.14) + }) + + // ---------------------------------------------------------- + // Casts + // ---------------------------------------------------------- + it('Should convert string #1', () => { + const value = 'hello' + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 'hello') + }) + it('Should convert string #2', () => { + const value = '3.14' + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 3.14) + }) + it('Should convert string #3', () => { + const value = '-0' + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 0) + }) + it('Should convert string #4', () => { + const value = '-100' + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, -100) + }) + it('Should convert number', () => { + const value = 42 + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 42) + }) + it('Should convert true', () => { + const value = true + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 1) + }) + it('Should convert false', () => { + const value = false + const result = Value.Convert(Type.Number(), value) + Assert.deepEqual(result, 0) + }) + it('Should convert object', () => { + const value = {} + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, {}) + }) + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, []) + }) +}) diff --git a/test/runtime/value/convert/object.ts b/test/runtime/value/convert/object.ts new file mode 100644 index 000000000..2c85d60f2 --- /dev/null +++ b/test/runtime/value/convert/object.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Object', () => { + it('Should convert properties', () => { + // prettier-ignore + const T = Type.Object({ + x: Type.Number(), + y: Type.Boolean(), + z: Type.Boolean() + }) + const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) + Assert.deepEqual(R, { x: 42, y: true, z: 'hello' }) + }) +}) diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts new file mode 100644 index 000000000..bfeb7129c --- /dev/null +++ b/test/runtime/value/convert/record.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Record', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/recursive.ts b/test/runtime/value/convert/recursive.ts new file mode 100644 index 000000000..5cae25261 --- /dev/null +++ b/test/runtime/value/convert/recursive.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Recursive', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/regex.ts b/test/runtime/value/convert/regex.ts new file mode 100644 index 000000000..8f4efad92 --- /dev/null +++ b/test/runtime/value/convert/regex.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/RegEx', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/string.ts b/test/runtime/value/convert/string.ts new file mode 100644 index 000000000..b20c3ccf7 --- /dev/null +++ b/test/runtime/value/convert/string.ts @@ -0,0 +1,69 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/String', () => { + const T = Type.String() + it('Should convert from number 1', () => { + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, '3.14') + }) + it('Should convert from number 2', () => { + const R = Value.Convert(T, 3) + Assert.deepEqual(R, '3') + }) + it('Should convert from boolean 1', () => { + const R = Value.Convert(T, true) + Assert.deepEqual(R, 'true') + }) + it('Should convert from boolean 2', () => { + const R = Value.Convert(T, false) + Assert.deepEqual(R, 'false') + }) + it('Should convert from bigint', () => { + const R = Value.Convert(T, BigInt(12345)) + Assert.deepEqual(R, '12345') + }) + it('Should convert from symbol', () => { + const R = Value.Convert(T, Symbol(12345)) + Assert.deepEqual(R, '12345') + }) + // ---------------------------------------------------------- + // Casts + // ---------------------------------------------------------- + it('Should convert string', () => { + const value = 'hello' + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, 'hello') + }) + it('Should convert number #1', () => { + const value = 42 + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, '42') + }) + it('Should convert number #2', () => { + const value = 42n + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, '42') + }) + it('Should convert true', () => { + const value = true + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, 'true') + }) + it('Should convert false', () => { + const value = false + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, 'false') + }) + it('Should convert object', () => { + const value = {} + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, {}) + }) + it('Should convert array', () => { + const value = [] as any[] + const result = Value.Convert(Type.String(), value) + Assert.deepEqual(result, []) + }) +}) diff --git a/test/runtime/value/convert/symbol.ts b/test/runtime/value/convert/symbol.ts new file mode 100644 index 000000000..fe2a158e3 --- /dev/null +++ b/test/runtime/value/convert/symbol.ts @@ -0,0 +1,15 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Symbol', () => { + const T = Type.Symbol() + it('Should convert from number 1', () => { + const R = Value.Convert(T, 3.14) + Assert.deepEqual(R, '3.14') + }) + it('Should convert from number 2', () => { + const R = Value.Convert(T, 3) + Assert.deepEqual(R, '3') + }) +}) diff --git a/test/runtime/value/convert/tuple.ts b/test/runtime/value/convert/tuple.ts new file mode 100644 index 000000000..eafc01c42 --- /dev/null +++ b/test/runtime/value/convert/tuple.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Tuple', () => { + it('Should convert from Array 1', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Convert(T, ['1', 'true']) + Assert.deepEqual(R, [1, true]) + }) + it('Should convert from Array 2', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Convert(T, ['1']) + Assert.deepEqual(R, [1]) + }) + it('Should convert from Array 3', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Convert(T, ['1', '2', '3']) + Assert.deepEqual(R, [1, 2, '3']) + }) +}) diff --git a/test/runtime/value/convert/uint8array.ts b/test/runtime/value/convert/uint8array.ts new file mode 100644 index 000000000..bd6ac1fb0 --- /dev/null +++ b/test/runtime/value/convert/uint8array.ts @@ -0,0 +1,9 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Uint8Array', () => { + it('Should convert from Array', () => { + const T = Type.Uint8Array() + }) +}) diff --git a/test/runtime/value/convert/undefined.ts b/test/runtime/value/convert/undefined.ts new file mode 100644 index 000000000..130f4177e --- /dev/null +++ b/test/runtime/value/convert/undefined.ts @@ -0,0 +1,15 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Undefined', () => { + const T = Type.Undefined() + it('Should convert from string 1', () => { + const R = Value.Convert(T, 'undefined') + Assert.deepEqual(R, undefined) + }) + it('Should convert from string 2', () => { + const R = Value.Convert(T, 'hello') + Assert.deepEqual(R, 'hello') + }) +}) diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts new file mode 100644 index 000000000..5b1f2f3a5 --- /dev/null +++ b/test/runtime/value/convert/union.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Union', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/convert/unknown.ts b/test/runtime/value/convert/unknown.ts new file mode 100644 index 000000000..0b8cea1f9 --- /dev/null +++ b/test/runtime/value/convert/unknown.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Unknown', () => { + const T = Type.Unknown() + it('Should convert null', () => { + const V = null + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert undefined', () => { + const V = undefined + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert string', () => { + const V = 'hello' + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert number', () => { + const V = 42 + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert boolean', () => { + const V = true + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert object', () => { + const V = { x: 1 } + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) + it('Should convert array', () => { + const V = [1, 2, 3] + const R = Value.Convert(T, V) + Assert.deepEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/void.ts b/test/runtime/value/convert/void.ts new file mode 100644 index 000000000..a977b79d5 --- /dev/null +++ b/test/runtime/value/convert/void.ts @@ -0,0 +1,7 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Void', () => { + it('Should convert', () => {}) +}) diff --git a/test/runtime/value/create/bigint.ts b/test/runtime/value/create/bigint.ts new file mode 100644 index 000000000..616058107 --- /dev/null +++ b/test/runtime/value/create/bigint.ts @@ -0,0 +1,14 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/BigInt', () => { + it('Should create value', () => { + const T = Type.BigInt() + Assert.deepEqual(Value.Create(T), BigInt(0)) + }) + it('Should create default', () => { + const T = Type.BigInt({ default: true }) + Assert.deepEqual(Value.Create(T), true) + }) +}) diff --git a/test/runtime/value/create/boolean.ts b/test/runtime/value/create/boolean.ts index 824641724..9315f20b6 100644 --- a/test/runtime/value/create/boolean.ts +++ b/test/runtime/value/create/boolean.ts @@ -8,7 +8,7 @@ describe('value/create/Boolean', () => { Assert.deepEqual(Value.Create(T), false) }) it('Should create default', () => { - const T = Type.Any({ default: true }) + const T = Type.Boolean({ default: true }) Assert.deepEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/composite.ts b/test/runtime/value/create/composite.ts new file mode 100644 index 000000000..96995304a --- /dev/null +++ b/test/runtime/value/create/composite.ts @@ -0,0 +1,66 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Composite', () => { + it('Should create value', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.Number(), + b: Type.Number(), + c: Type.Number(), + }) + const T = Type.Composite([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + a: 0, + b: 0, + c: 0, + }) + }) + it('Should create default', () => { + const A = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + z: Type.Number({ default: 3 }), + }) + const B = Type.Object({ + a: Type.Number({ default: 4 }), + b: Type.Number({ default: 5 }), + c: Type.Number({ default: 6 }), + }) + const T = Type.Composite([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + a: 4, + b: 5, + c: 6, + }) + }) + it('Should create default and omit optionals', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const B = Type.Object({ + a: Type.Optional(Type.Number()), + b: Type.Optional(Type.Number()), + c: Type.Optional(Type.Number()), + }) + const T = Type.Composite([A, B]) + Assert.deepEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + }) + }) +}) diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts index 90ca03152..1a3ca5424 100644 --- a/test/runtime/value/create/custom.ts +++ b/test/runtime/value/create/custom.ts @@ -1,17 +1,16 @@ import { Value } from '@sinclair/typebox/value' -import { Type, Kind } from '@sinclair/typebox' -import { Custom } from '@sinclair/typebox/custom' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/create/Custom', () => { it('Should create custom value with default', () => { - Custom.Set('CustomCreate1', () => true) + TypeRegistry.Set('CustomCreate1', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) Assert.deepEqual(Value.Create(T), 'hello') }) it('Should throw when no default value is specified', () => { - Custom.Set('CustomCreate2', () => true) + TypeRegistry.Set('CustomCreate2', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate2' }) Assert.throws(() => Value.Create(T)) }) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 2e06b6f0f..fb947093e 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -1,6 +1,8 @@ import './any' import './array' +import './bigint' import './boolean' +import './composite' import './custom' import './constructor' import './enum' @@ -10,13 +12,16 @@ import './intersect' import './keyof' import './literal' import './never' +import './not' import './null' import './number' import './object' -import './rec' +import './recursive' +import './ref' import './record' import './regex' import './string' +import './symbol' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/create/intersect.ts b/test/runtime/value/create/intersect.ts index efa66c5b1..344ad5b90 100644 --- a/test/runtime/value/create/intersect.ts +++ b/test/runtime/value/create/intersect.ts @@ -4,63 +4,37 @@ import { Assert } from '../../assert/index' describe('value/create/Intersect', () => { it('Should create value', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }) - const B = Type.Object({ - a: Type.Number(), - b: Type.Number(), - c: Type.Number(), - }) - const T = Type.Intersect([A, B]) - Assert.deepEqual(Value.Create(T), { - x: 0, - y: 0, - z: 0, - a: 0, - b: 0, - c: 0, - }) + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const R = Value.Create(T) + + Assert.deepEqual(R, { x: 0, y: 0 }) + }) + it('Should create value with default', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 100 }) }), Type.Object({ y: Type.Number({ default: 200 }) })]) + const R = Value.Create(T) + Assert.deepEqual(R, { x: 100, y: 200 }) + }) + it('Should create for overlapping intersection', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number(), y: Type.Number() })]) + const R = Value.Create(T) + Assert.deepEqual(R, { x: 0, y: 0 }) + }) + it('Should create with last intersected overlapping default', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 1 }) }), Type.Object({ x: Type.Number({ default: 2 }), y: Type.Number() })]) + const R = Value.Create(T) + Assert.deepEqual(R, { x: 2, y: 0 }) + }) + it('Should throw for non-constructable intersection 1', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) + Assert.throws(() => Value.Create(T)) }) - it('Should create default', () => { - const A = Type.Object({ - x: Type.Number({ default: 1 }), - y: Type.Number({ default: 2 }), - z: Type.Number({ default: 3 }), - }) - const B = Type.Object({ - a: Type.Number({ default: 4 }), - b: Type.Number({ default: 5 }), - c: Type.Number({ default: 6 }), - }) - const T = Type.Intersect([A, B]) - Assert.deepEqual(Value.Create(T), { - x: 1, - y: 2, - z: 3, - a: 4, - b: 5, - c: 6, - }) + it('Should throw for non-constructable intersection 2', () => { + const T = Type.Intersect([Type.String(), Type.Number()]) + Assert.throws(() => Value.Create(T)) }) - it('Should create default and omit optionals', () => { - const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }) - const B = Type.Object({ - a: Type.Optional(Type.Number()), - b: Type.Optional(Type.Number()), - c: Type.Optional(Type.Number()), - }) - const T = Type.Intersect([A, B]) - Assert.deepEqual(Value.Create(T), { - x: 0, - y: 0, - z: 0, - }) + it('Should not throw for non-constructable intersection with default', () => { + const T = Type.Intersect([Type.String(), Type.Number()], { default: 'hello' }) + const R = Value.Create(T) + Assert.deepEqual(R, 'hello') }) }) diff --git a/test/runtime/value/create/not.ts b/test/runtime/value/create/not.ts new file mode 100644 index 000000000..132d4eb9e --- /dev/null +++ b/test/runtime/value/create/not.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Not', () => { + it('Should create value', () => { + const T = Type.Not(Type.String(), Type.Number()) + const R = Value.Create(T) + Assert.equal(R, 0) + }) + it('Should create value with default inner', () => { + const T = Type.Not(Type.String(), Type.Number({ default: 100 })) + const R = Value.Create(T) + Assert.equal(R, 100) + }) + it('Should create value with default outer', () => { + const T = Type.Not(Type.String(), Type.Number(), { default: 100 }) + const R = Value.Create(T) + Assert.equal(R, 100) + }) +}) diff --git a/test/runtime/value/create/rec.ts b/test/runtime/value/create/recursive.ts similarity index 100% rename from test/runtime/value/create/rec.ts rename to test/runtime/value/create/recursive.ts diff --git a/test/runtime/value/create/ref.ts b/test/runtime/value/create/ref.ts new file mode 100644 index 000000000..621236c98 --- /dev/null +++ b/test/runtime/value/create/ref.ts @@ -0,0 +1,30 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Ref', () => { + it('Should throw if target is undefined', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: 'T', default: 'target' }, + ) + const R = Type.Ref(T) + Assert.throws(() => Value.Create(R)) + }) + it('Should create ref default if ref default is defined', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { $id: 'T', default: 'target' }, + ) + const R = Type.Ref(T, { default: 'override' }) + Assert.deepEqual(Value.Create(R), 'override') // terminated at R default value + }) +}) diff --git a/test/runtime/value/create/symbol.ts b/test/runtime/value/create/symbol.ts new file mode 100644 index 000000000..da3ba1c3a --- /dev/null +++ b/test/runtime/value/create/symbol.ts @@ -0,0 +1,15 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Symbol', () => { + it('Should create value', () => { + const T = Type.Symbol() + const V = Value.Create(T) + Assert.deepEqual(typeof V, 'symbol') + }) + it('Should create default', () => { + const T = Type.Symbol({ default: true }) + Assert.deepEqual(Value.Create(T), true) + }) +}) diff --git a/test/runtime/hash/hash.ts b/test/runtime/value/hash/hash.ts similarity index 69% rename from test/runtime/hash/hash.ts rename to test/runtime/value/hash/hash.ts index dd508fce4..45b255342 100644 --- a/test/runtime/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -1,21 +1,19 @@ -import { ValueHash } from '@sinclair/typebox/hash' -import { Assert } from '../assert/index' +import { ValueHash } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' -describe('Hash', () => { +describe('value/hash/Hash', () => { it('Should hash number', () => { Assert.equal('bigint', typeof ValueHash.Create(1)) const A = ValueHash.Create(1) const B = ValueHash.Create(2) Assert.notEqual(A, B) }) - it('Should hash string', () => { Assert.equal('bigint', typeof ValueHash.Create('hello')) const A = ValueHash.Create('hello') const B = ValueHash.Create('world') Assert.notEqual(A, B) }) - it('Should hash boolean', () => { Assert.equal('bigint', typeof ValueHash.Create(true)) Assert.equal('bigint', typeof ValueHash.Create(false)) @@ -23,25 +21,18 @@ describe('Hash', () => { const B = ValueHash.Create(false) Assert.notEqual(A, B) }) - - it('Should not hash bigint', () => { - Assert.throws(() => ValueHash.Create(1n)) - }) - it('Should hash null', () => { Assert.equal('bigint', typeof ValueHash.Create(null)) const A = ValueHash.Create(null) const B = ValueHash.Create(undefined) Assert.notEqual(A, B) }) - it('Should hash array', () => { Assert.equal('bigint', typeof ValueHash.Create([0, 1, 2, 3])) const A = ValueHash.Create([0, 1, 2, 3]) const B = ValueHash.Create([0, 2, 2, 3]) Assert.notEqual(A, B) }) - it('Should hash object 1', () => { // prettier-ignore Assert.equal('bigint', typeof ValueHash.Create({ x: 1, y: 2 })) @@ -64,31 +55,57 @@ describe('Hash', () => { const B = ValueHash.Create({ x: 1, y: [0, 1, 2] }) Assert.notEqual(A, B) }) - it('Should hash object 5', () => { const A = ValueHash.Create({ x: 1, y: undefined }) const B = ValueHash.Create({ x: 2, y: undefined }) Assert.notEqual(A, B) }) - it('Should hash Date', () => { Assert.equal('bigint', typeof ValueHash.Create(new Date())) const A = ValueHash.Create(new Date(1)) const B = ValueHash.Create(new Date(2)) Assert.notEqual(A, B) }) - it('Should hash Uint8Array', () => { Assert.equal('bigint', typeof ValueHash.Create(new Uint8Array([0, 1, 2, 3]))) const A = ValueHash.Create(new Uint8Array([0, 1, 2, 3])) const B = ValueHash.Create(new Uint8Array([0, 2, 2, 3])) Assert.notEqual(A, B) }) - it('Should hash undefined', () => { Assert.equal('bigint', typeof ValueHash.Create(undefined)) const A = ValueHash.Create(undefined) const B = ValueHash.Create(null) Assert.notEqual(A, B) }) + it('Should hash symbol 1', () => { + Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + const A = ValueHash.Create(Symbol(1)) + const B = ValueHash.Create(Symbol()) + Assert.notEqual(A, B) + }) + it('Should hash symbol 2', () => { + Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + const A = ValueHash.Create(Symbol(1)) + const B = ValueHash.Create(Symbol(2)) + Assert.notEqual(A, B) + }) + it('Should hash symbol 2', () => { + Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + const A = ValueHash.Create(Symbol(1)) + const B = ValueHash.Create(Symbol(1)) + Assert.equal(A, B) + }) + it('Should hash bigint 1', () => { + Assert.equal('bigint', typeof ValueHash.Create(BigInt(1))) + const A = ValueHash.Create(BigInt(1)) + const B = ValueHash.Create(BigInt(2)) + Assert.notEqual(A, B) + }) + it('Should hash bigint 2', () => { + Assert.equal('bigint', typeof ValueHash.Create(BigInt(1))) + const A = ValueHash.Create(BigInt(1)) + const B = ValueHash.Create(BigInt(1)) + Assert.equal(A, B) + }) }) diff --git a/test/runtime/hash/index.ts b/test/runtime/value/hash/index.ts similarity index 100% rename from test/runtime/hash/index.ts rename to test/runtime/value/hash/index.ts diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index 2ca9e7172..ad07f09f0 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -1,7 +1,9 @@ import './cast' import './check' import './clone' +import './convert' import './create' import './delta' import './equal' +import './hash' import './pointer' diff --git a/test/static/assert.ts b/test/static/assert.ts index b4ffbd33f..4b8763847 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -1,8 +1,8 @@ import { Static, TSchema } from '@sinclair/typebox' -export function Expect(schema: T) { +export function Expect>(schema: T) { return { - ToInfer: >() => {}, - ToBe: () => {}, + ToInfer: () => {}, + ToBe: () => {}, } } diff --git a/test/static/bigint.ts b/test/static/bigint.ts new file mode 100644 index 000000000..a18b437ba --- /dev/null +++ b/test/static/bigint.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.BigInt()).ToInfer() diff --git a/test/static/composite.ts b/test/static/composite.ts new file mode 100644 index 000000000..be5596d2d --- /dev/null +++ b/test/static/composite.ts @@ -0,0 +1,19 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + const A = Type.Object({ + A: Type.String(), + B: Type.String(), + }) + const B = Type.Object({ + A: Type.Number(), + B: Type.Number(), + }) + const T = Type.Composite([A, B]) + + Expect(T).ToInfer<{ + A: string | number + B: string | number + }>() +} diff --git a/test/static/exclude.ts b/test/static/exclude.ts new file mode 100644 index 000000000..b0a76354c --- /dev/null +++ b/test/static/exclude.ts @@ -0,0 +1,15 @@ +import { Type } from '@sinclair/typebox' +import { Expect } from './assert' + +{ + const T = Type.Exclude(Type.String(), Type.String()) + Expect(T).ToBe() +} +{ + const T = Type.Exclude(Type.String(), Type.Number()) + Expect(T).ToBe() +} +{ + const T = Type.Exclude(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) + Expect(T).ToBe() +} diff --git a/test/static/extract.ts b/test/static/extract.ts new file mode 100644 index 000000000..7cc603709 --- /dev/null +++ b/test/static/extract.ts @@ -0,0 +1,15 @@ +import { Type } from '@sinclair/typebox' +import { Expect } from './assert' + +{ + const T = Type.Extract(Type.String(), Type.String()) + Expect(T).ToBe() +} +{ + const T = Type.Extract(Type.String(), Type.Number()) + Expect(T).ToBe() +} +{ + const T = Type.Extract(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) + Expect(T).ToBe() +} diff --git a/test/static/index.ts b/test/static/index.ts index cc84ce321..8666ca223 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -1,10 +1,14 @@ import './any' import './array' +import './bigint' import './boolean' +import './composite' import './date' import './constructor-parameters' import './constructor' import './emum' +import './extract' +import './exclude' import './function' import './instance-type' import './intersect' @@ -13,6 +17,7 @@ import './literal' import './modifier' import './namespace' import './never' +import './not' import './null' import './number' import './object' @@ -31,6 +36,7 @@ import './required' import './return-type' import './strict' import './string' +import './symbol' import './tuple' import './union' import './unknown' diff --git a/test/static/intersect.ts b/test/static/intersect.ts index fcbb1000b..b7cf181b6 100644 --- a/test/static/intersect.ts +++ b/test/static/intersect.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TOptional, TString } from '@sinclair/typebox' +import { Type, TOptional, TString, Static } from '@sinclair/typebox' { const A = Type.Object({ @@ -38,14 +38,17 @@ import { Type, TOptional, TString } from '@sinclair/typebox' // https://github.com/sinclairzx81/typebox/issues/113 // https://github.com/sinclairzx81/typebox/issues/187 -// { -// const A = Type.Object({ A: Type.String() }) -// const B = Type.Object({ B: Type.String() }) -// const C = Type.Object({ C: Type.String() }) -// const T = Type.Intersect([A, Type.Union([B, C])]) -// type T = Static -// const _0: T = { A: '', B: '' } -// const _1: T = { A: '', C: '' } -// const _3: T = { A: '', B: '', C: '' } -// Expect(T).ToBe<{ A: string } & ({ B: string, } | { C: string })>() -// } +{ + const A = Type.Object({ A: Type.String() }) + const B = Type.Object({ B: Type.String() }) + const C = Type.Object({ C: Type.String() }) + const T = Type.Intersect([A, Type.Union([B, C])]) + type T = Static + const _0: T = { A: '', B: '' } + const _1: T = { A: '', C: '' } + const _3: T = { A: '', B: '', C: '' } + // invert equivelence (expect true both cases) + type T1 = T extends { A: string } & ({ B: string } | { C: string }) ? true : false + type T2 = { A: string } & ({ B: string } | { C: string }) extends T ? true : false + Expect(T).ToBe<{ A: string } & ({ B: string } | { C: string })>() // solved! +} diff --git a/test/static/keyof.ts b/test/static/keyof.ts index 62d626057..ff353b7fa 100644 --- a/test/static/keyof.ts +++ b/test/static/keyof.ts @@ -55,3 +55,27 @@ import { Type } from '@sinclair/typebox' ) Expect(T).ToInfer<'C'>() } +{ + { + const A = Type.Object({ type: Type.Literal('A') }) + const B = Type.Object({ type: Type.Literal('B') }) + const C = Type.Object({ type: Type.Literal('C') }) + const Union = Type.Union([A, B, C]) + const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Intersect([Union, Extended]) + + const K1 = Type.KeyOf(T) + + Expect(K1).ToInfer<'type' | 'x' | 'y' | 'z'>() + + const P = Type.Omit(T, ['type', 'x']) + + const K2 = Type.KeyOf(P) + + Expect(K2).ToInfer<'y' | 'z'>() + } +} diff --git a/test/static/not.ts b/test/static/not.ts new file mode 100644 index 000000000..ea157157a --- /dev/null +++ b/test/static/not.ts @@ -0,0 +1,7 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + const T = Type.Not(Type.Number(), Type.String()) + Expect(T).ToInfer() +} diff --git a/test/static/omit.ts b/test/static/omit.ts index c3f726889..98b93ba57 100644 --- a/test/static/omit.ts +++ b/test/static/omit.ts @@ -54,3 +54,51 @@ import { Type, Static } from '@sinclair/typebox' C: string }>() } +{ + const A = Type.Object({ type: Type.Literal('A') }) + const B = Type.Object({ type: Type.Literal('B') }) + const C = Type.Object({ type: Type.Literal('C') }) + const Union = Type.Union([A, B, C]) + const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Intersect([Union, Extended]) + + Expect(T).ToInfer< + ( + | { + type: 'A' + } + | { + type: 'B' + } + | { + type: 'C' + } + ) & { + x: number + y: number + z: number + } + >() + + const P = Type.Omit(T, ['type', 'x']) + + Expect(P).ToInfer< + ({} | {} | {}) & { + y: number + z: number + } + >() + + const O = Type.Partial(P) + + Expect(O).ToInfer< + ({} | {} | {}) & { + y?: number | undefined + z?: number | undefined + } + >() +} diff --git a/test/static/partial.ts b/test/static/partial.ts index 348c64613..a9e591f94 100644 --- a/test/static/partial.ts +++ b/test/static/partial.ts @@ -17,3 +17,56 @@ import { Type, Static } from '@sinclair/typebox' C?: string }>() } +{ + { + const A = Type.Object({ type: Type.Literal('A') }) + const B = Type.Object({ type: Type.Literal('B') }) + const C = Type.Object({ type: Type.Literal('C') }) + const Union = Type.Union([A, B, C]) + const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + + const T = Type.Intersect([Union, Extended]) + + Expect(T).ToInfer< + ( + | { + type: 'A' + } + | { + type: 'B' + } + | { + type: 'C' + } + ) & { + x: number + y: number + z: number + } + >() + + const P = Type.Partial(T) + + Expect(P).ToInfer< + ( + | { + type?: 'A' | undefined + } + | { + type?: 'B' | undefined + } + | { + type?: 'C' | undefined + } + ) & { + x?: number | undefined + y?: number | undefined + z?: number | undefined + } + >() + } +} diff --git a/test/static/pick.ts b/test/static/pick.ts index 0ae10cd93..f9d825571 100644 --- a/test/static/pick.ts +++ b/test/static/pick.ts @@ -57,3 +57,73 @@ import { Type, Static } from '@sinclair/typebox' B: string }>() } +{ + const A = Type.Object({ type: Type.Literal('A') }) + const B = Type.Object({ type: Type.Literal('B') }) + const C = Type.Object({ type: Type.Literal('C') }) + const Union = Type.Union([A, B, C]) + const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Intersect([Union, Extended]) + + Expect(T).ToInfer< + ( + | { + type: 'A' + } + | { + type: 'B' + } + | { + type: 'C' + } + ) & { + x: number + y: number + z: number + } + >() + + const K = Type.KeyOf(T) + + Expect(K).ToInfer<'type' | 'x' | 'y' | 'z'>() + + const P = Type.Pick(T, ['type', 'x']) + + Expect(P).ToInfer< + ( + | { + type: 'A' + } + | { + type: 'B' + } + | { + type: 'C' + } + ) & { + x: number + } + >() + + const O = Type.Partial(P) + + Expect(O).ToInfer< + ( + | { + type?: 'A' | undefined + } + | { + type?: 'B' | undefined + } + | { + type?: 'C' | undefined + } + ) & { + x?: number | undefined + } + >() +} diff --git a/test/static/required.ts b/test/static/required.ts index c3b0ca726..30aa9ca1d 100644 --- a/test/static/required.ts +++ b/test/static/required.ts @@ -20,3 +20,57 @@ Expect(Type.RegEx(/foo/)).ToInfer() C: string }>() } +{ + { + const A = Type.Object({ type: Type.Literal('A') }) + const B = Type.Object({ type: Type.Literal('B') }) + const C = Type.Object({ type: Type.Literal('C') }) + const Union = Type.Union([A, B, C]) + const Extended = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Intersect([Union, Extended]) + + const P = Type.Partial(T) + + Expect(P).ToInfer< + ( + | { + type?: 'A' | undefined + } + | { + type?: 'B' | undefined + } + | { + type?: 'C' | undefined + } + ) & { + x?: number | undefined + y?: number | undefined + z?: number | undefined + } + >() + + const R = Type.Required(P) + + Expect(R).ToInfer< + ( + | { + type: 'A' + } + | { + type: 'B' + } + | { + type: 'C' + } + ) & { + x: number + y: number + z: number + } + >() + } +} diff --git a/test/static/symbol.ts b/test/static/symbol.ts new file mode 100644 index 000000000..633541438 --- /dev/null +++ b/test/static/symbol.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Symbol()).ToInfer() diff --git a/tsconfig.json b/tsconfig.json index d660b0d6d..47541b26d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,20 +13,8 @@ "@sinclair/typebox/compiler": [ "src/compiler/index.ts" ], - "@sinclair/typebox/conditional": [ - "src/conditional/index.ts" - ], - "@sinclair/typebox/custom": [ - "src/custom/index.ts" - ], - "@sinclair/typebox/format": [ - "src/format/index.ts" - ], - "@sinclair/typebox/guard": [ - "src/guard/index.ts" - ], - "@sinclair/typebox/hash": [ - "src/hash/index.ts" + "@sinclair/typebox/errors": [ + "src/errors/index.ts" ], "@sinclair/typebox/system": [ "src/system/index.ts" diff --git a/typebox.png b/typebox.png index 0df53e5c5f5f2e423565e564ab3a01aa6a845c3e..ed053e1f3df23df4eca1b25d94b0df721437a1e9 100644 GIT binary patch literal 946728 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�@ZrwxSO97vBsYf{{jfU( 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 z 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%0D_qOlSO~8l$4<98g-Mltb*5H+5 zqCVCT%o}gKaeM6JzRcgp`l1&;PnWWa?I*sRH#tw->wWIKz4(V-y1oAgd|+<=K@MUk zcx!A;HvHk_=YHm=w}0@2$Lp7VZ}E#WeytjR{=l8-pbO#-GtbTH1W2fGtBrd>mOea*=FKqxMB z85lgiHkeIrrqphP*z+HAJ@z@dj6@Otfcvq%uLo>BE!Yj|31e(H`!$i&#~V|vS&^DA zgqba31d=%hx{ma3|+DT>< ziy91daO@|GLD0}a*4TfL$0!Ejsv)tKkO<;3A+dIIT=kG+4imE(1(0-IyBax=_!JDs zgD$Qi)2%Tzpbqpc*9Keql*t3_+=NHWV8sSQ&K-Kse67~LwF;$x?J}@1J?j?{c}Nq3 z;7uPrY-xymYrw+chKx|wTakgbsm&;n%J9f-M8k_wIE2gebJC3RWb65m-uG#&BYxw? zKaA!N;zN{*QfuGkASsOE+KxstI8a}D+Z_v)h-}s3;i7AlN#141bHhrc^<;hg{u^}r zi%bd?KVD#XVl<3Id+F$hMz|84RvSl`0XBYdBb>h2IWYv#F^uQ9nQ~4_O`N>LFEP4U zTo36O8y|m@2$O8FiY3g=-*(aB;Cwx#YPD8pnq?+vYf1MToD0*^IKZbqj;yim?3Wk6 zSjHwkaA;mbm&7dF`Nj5h3w7|J$irhcGbT>@;lYq*+zGZ~+xw_zK8#>4(XfS^cCK4$ znE0{Hyw)zVRjVLzOp)Q~j5sw~19bvaezvRm#26E+vcX_HN&to9bnOL% zcztqQb86Gf)v8%z5sbcY{crZ5k-KZ6{%R3LH$r0?MhNDYy+h2{rU*>a$PvvAg&`ps zzLOf}AC~+?>r5 zXSI5PF&+_WFeWk77Jon7XA_>toW3^h!&kC2P}MglZtBxI$LTw6?X~A0WGu&89m~`= zKDkNgHB>9`Ljo)Ntl1lE#|Dd<3|AQV!VXRh{9TVZk93WiheNtIxeZTTbUuwO`qsiB z!b8sq<>tMt{GwU`eWR{9NPcXo>(rFo?xF?L1ee8bK6R1C`Du^6=V$7YapVhNI`ETb z9tekr>&=Dg)!Pfdc|vQ`BvZ~w!@h4YShaqva4 z`x$3#XJwcrNsEJd*jMwYxw-zj>vesEicl$p)UCa z9(;T#Nym4v@tpbDpYzDW3wkJLO&;rT>>FC~_?6FBz3Ttm{^>t^rSs+8Rel$~xaPV5 z4$aYp$#9;x!Q_Pjdp~SW|MXrv9oA1v}y>yYO{?Gk~U)Uc1fW)v^rbJ`9{CxM+)A-&>vgnoUb`2@E{K!=-97!rHyXc zt#`8J9d6vb;=#@JV%QdWef6%s>aMiM|0u`rw3RdR_Sy5GcKm40H$3whskh6(Y|B`@DM=)_bn8VVlgoveefI0XxpYvwkn$cI)=VU+`$Xi|}9R`&Zv<6Jivf z{*_v}xlWpg>+gR3_PLM#g6-+g{wDo$_XXu3454w3{d4z!;PyQ~@I%`NT;b+lv3I=i zm%Ho0J9vNcpZ~=6m0$AM?b5BcXpSNeQQ7Jh8n(1#I(_A5OcCT*Na^I1xd)nV;+4ylWM z*h~W#Jsixp3hl9qJwlJx*1QOGY#JMx{UOFOaU6V1O18seaE>mmUAc!No;e1|3r@(H z8{Z&3&?)vnFADL}7A|_2!31`!p#*>a#UD1Qg}Dz}^a;8`%ilN>c#rKUjm3uX#G$~; z4xw$wVSLZjrF#2e5MH$b)(;6BLh+Kr@R87SK6dz@Hn5U0K06*YW^6q09RTmh_i;D{ zx4C5RYl7iGuCOdj{A1VpW8wP|i=>@RV$Jyk&ioCUU_d%@D#VI6ddC{W>S{~;Hs`)7 z6Ku2hoj6%d%-Gkj`$2K>iil70%{I36FeMG(2Mu;U4`sN6XpFu;j>WN&<0HPP8-+g2 zE}Z+LC=JJ2>v42;IKwksET4^iWOH;uHD$d_Ufn+X0pf@}`m~R>yr9y4ah6_*9d*d} zCP(Wdi5=%EcoH{yK4p;g7yevF<9E&%i7DL~%z)0xWHcq=f`l>5qMfWfu{)BB7P9oxP+5?IFri+9~(m22|LG6Ot8#*x+GpJ?oH5NfgXEjK@Gck{dK2@IjUGpC;&F#=rr1teST%RMbkIHXI=IP2 z12xo%m0WCv;i^m4F3#4$A^+`{c`-^jj30BIkDof~*ruPj_@p6C1DIoC&Zhz=9PP}S zkUW2+fm<3mBxc5@S!an_r!n`Kx%XO0{&T<1(4MA0M3roXo8RE`-wGw$BLse3T?tOTt-mcs6Dr3)0fM3-|41emUf7Un7`hqmM z@eZpTw?|7jx%7;DDt^d}r>)ue&SFSt_C%EBc7<6Q1y-?Txyz(}jYLnvjNn0OM+Y z07E}!;h2&s4`1vNfY{g(;(34mj?|(OdKjt3UU%Kj*n<{_yRM zugkbocf#sHmqVX@>&5__G_2Bg8TNx=F4JeWNmz}epf-$=_fh$ZchF<&w(xhqsv4u? zs{B!rak8!{St2uSw zc-=SVyY-I0r+(d&w?F!$Kk&z5vFD@Uyt~Z(DSL5P*KqP)b{r2J73QQKHzTRI-2HGo z2Koiw2y<@X81pKV?$%r1yFL9WPu9ox-!}AS&hS$M+Yj~2qx{+P11(s(esRTee5g@{ z^rg}UobYDd>t?RxPyN~_Z9o39AKBimcUd||*??~k?eQ^>g$Fe9@Dj`Iy1QMs{jLA) zGq!KkJCXTh?j3T!*S+t%efx`V-abGd`{j6Z!%6Lk-+pss|E_nvYkTPr-n>2Ysb6c3 z-0;1`Q=|QvBY$wwSjKa1(VLgMdn~e!9ox)tGTd|6(?cJQ(S5lr1^$<{ zeO;r7y$EnjkrAz?y|#^%*Z6U+-Ze?NmQOL3Zv_IO>*r?obDk(RSXj8` z4S-U`BcG$_*dG&isWTWJH4pqHu+zSl$D~GUNGDv*h?eZABhG&96)5ZBOt#(!#}dXN zGkb!euROMF!NDi-WMuvaJB#dWC*w|G*s(6{FD)U{w<6E=_Cm+?iwp*;`T4qPY%Ggw zy=v|;5H(#F$)>Z>=Y&pk=efs9LQ&7x_fC2Uy9{Q=201YGuR!3%fflZb6AySFxJ(S{Uo1zRu!rak=zVS1UQ+t-Ag=RJF3I-NQBX$<#o*-}LgtCtmz#4oCzL_7rBi zgKT|Gth52{@EytQ8JiCn0gVA6;A|hZGpClvr1=2a{LmmzBX9Kv%e+UE-PDSK3CrH= zG~?t6z%-N9c<`%fG?Cof;vmt;%cVwVc{0Duw{o3z76_BK%YsLt2mjhu-*Tlx9!r^C z=dN*&dvft#0ZGrE)Go&?b+De}3MgWTV`DlmVqxUA$IH-8>PFrjx^l9eHbg3~Oy0~b zwhq7x#~cgzaIAA~>Gu;MgCEUwrf*Dmmje4C?(>N~BEm0gMHh}<59A#lF{3uA4|^^c zV?Q7ZYZYYd=NOdFj)%>VQWuf-#6AAhy-XdJ9q}4cQ8HeY0ps1yd%cPlvKG9$O=*p z{x#K{%bVK)Xz-7c^!Xu*ulR~58XG!y=vsllWF|wcLDiD^f;$&kP;|`{%5kQCKGjOb z_n3jh;hyy34|j6p80NDS+z8|*8L^xJYhH1F>Zg9j?{;zw0pQboQjLi_sZ^(s=IoCT z4kz9f=$M0XCCq_YG2Z*$Tejc%?cdf}Yt9AKSw?>T6-SqVJa`8q3CQk_+r^W4vQxcFZ&WGdP6wdLY)SFEs_3So0_b`=jltzELYbwLQi-#q1cLV{1(+KdP(ayZQdL z-kJ82ANYRFm%gJ>H;`;^sN}{uKQE56f{rnc{c<)&KY(y;3NS^KhLw zr!dw-bK>VdywIOlKdU;AHPv;Esw zy~^K3c;so6&K2<;pmUWy@6H6>OBn~*^bBy6K} ztQnP8b={jAGiYAI)4yY5#ACvF;2S%#FD4w|>Dt*jUQ0%2Fz{i>xtf(m-w#{hcW44$ z5@JY&t=lAC4kOhLgFPS1>$4j<{+gF7=rJ{t<4|#8?}n%SE0$cMtQxM|%9o zBh4ACKH}&{q@BzG_rB+yT^n60k$%<}POS<1cyy&$t6=`8Jux{}oC9({@CjG^BWwLK zHZf;~yy&=1U4VdxMQq)%8?ju&NXI4v=TOoTt!jyvh}bC6!ZjA<8r-@&PGcM>Kq5D? zP^Lz?0X0VB)8bW1T=+rCF=kILefWIu8iplMxzQVxNnz?BrNs87yAvNg)L^4Ku01GF zi{xRw`ktpFj{2<`w$2q9&9RV8)41`ww>@&35KoQV96LjEH&(`tha8XI6p)7rwe3twViA|Nd*Qk zGt#cpB-xpEI4~$4_G;>8oTV=q6y;D(#@oET{?;le`(#vGs!AikO>8z0#sn$j;@2f{G0)|&&q z$m^kjO|;3oVt}L`iDBH~HlgLf;+&k-_l?J~x8q^sx)5V{ge_wP!`d8zY1tUBoEA2c z_)$li>!3Y&e%NAyjlMQu)Al)^94B7w!p){98?pC`oQSc<7AobV-uN_AxP@QA_~kuX zeTox({D$eE@j{$65Ogd|c~7&M1)|%TM}r(}Q8!oX07ieX*|R_#Me0i~MsV)MDqeGf z=^5F=S0`|@pRsnt1TuChyoBaYnm*Ac!Y8`vuMN7`q*_ z0`gD%5Xg(_)Fqs#lWce~JWjUM#(tH>*mW^y$%AN-Y_VTaBh(OE6uXY$`R!X?_+9?R zRnN~sN%zRvdc*1liq1_Be!mBAk9_3cR&1gZFwaqpc%kfg%)17;d64`yeiPu=rh_}uNLn{G;C)I_#@z@uD(ag|@x|C@jFZ?`{w!yEH1J8mRR74tD#5h5_; z)Z881NcJt5#jo_8fBH^+-`yMKC>{6ae6vdXb&iH3`DaZyAL002djk@#97E*74Gqrk z9q;uZHua-s>tiQc6MU>W&ns7L|MmA?x816naK~EEZ%I%2?_+9CEK{YIzQudqyZw-7 zhs`v&@vE?BKF$$n%P#50pWord4NUnMPkoo4n>()Ywq2ARKRm$1Fz-CHVtl-lFF%Sw z4LJUD4$xk)HFP_!iAaDRqBR;ta^iasIp%o4OSZL_-66#tNAlJq@dDV`?5k%#)F$d~ zTX1aZiuN`d~>lpX{kAGu>!wtIMc+G3{?yslm=G45i&T~N)96@l)@#c5lxm@8} z9AY2&F)l~GSMEpkYqX@unhzcZwwj&rnNvML=FxSYTc_aZ<``$ud#3H z>YahiHRtMYf6=#XZ+`Qe$;ceb6E-x)l_e&4d|5fisW2R)@sMOL-6IJ88PHXm7yhP0 z_8I^158S*x=NrC$d&{5vi8$su8f%*K)4n`TIfWO$%6?({n2-Cb+s!}xGXL7Ie@)Q{ z2j3ZImt5!*^DX*r!e>71>$ZRLvX}ZS#7EXW$DH7jh26p@GY3LZC`DHYH#gd9pY)~3 zf2+&Awnh*T?#UAkbFf>W<3U4A+&m&hW^-ziJDAp#8)zjD>4>o}?rAC|w$IWAVA#(ue?&qVGQ>g%| z=K9Uvemm)@UvR+2wXqpAPI0nN0M|!67&SKf)8^wkMga0(EvqR=9uAI_HpuInVnI26 z@rBpUZLa~iX=3C&J5C*{!wi4ogKK(Im*p{feD-reY?0YdKDgQ6+u)+7S!djZI`Z(_ zeGw-opF5=hv(4AE8YCxT%y|S71KsD!qaXfVBbcFUF6d~krQm?dcw`y}Q=QRSA2V%1 zt0Fc?%Di~kz@fbA<@zJnLz(aT!#L;5%x^IqLpG~TcaH$A@Ulu9bo+Y2ld+lE7CY)v z8{qSYUTmjLs_=kp`5PSo0V2Enfq&%M#cv?H^Od1+fx(vMFv$EsV#1frpZTCAcC2he z?Ktn0FLQatR;R(J&7NN+iQ$oA`8>uRqH5S{GrTSF*viR_KGx@BtX1b5ffQ%V1ZG_@ z)5nP*29KU=IoE^RmiTI%LZo1zQ4Ztc*FsDj6#3O7ENKbIf#$@y2*4(<0Ak{R;3|g0 za)VMA9!TZ|fmTh><{|=@V!PL3p_qOWV8{y@qZ^}r_IfJtr@2tTskXMkPh33_#tgs4 zb3UX8$IwHS*1D33`n~X8^_Dyh$JjXLDb5ktb-T>K_)@t|*z|=9rXdv0HiVVPbwwZt zUnuhAX$-goi|vjlVg4IvUGrbbRd1_RGhTdcB@^C%C=^s9y001BWNklqA^U@}$H1tYh-?Ff82NW5Gby`hTp0D>Jeo8BAi>UfU)Ya3^CfiJ+})z#wmJ zvc@Ht#?xbFAbP~ua~@btzO47!Ex8Oime$3(`XFZw5I1WewZUuj*pQHEAS0k1N1oVG z=a}`oa|~f=4$jpWk-WhprEDJ;bl1r!|p@7a&+^IKDe57&OI$t?fVr!d4qgdgcQbY#l z_$eg+3zu4;7nHM7> zEPYyQ&G~?vi0ejTby{bvLF~xyc6}c2;%&E^cev*VH_$f*_ROnqcEEGRnqyyOxXSOq z(sI_^wE6oBhp)i&5!`p{-8=8tZo22aY*PGzv#dEC_vG!BFwYm1otr?em%NJ8xaG%~ zO+LztmYa82fKl%C@4j-^Uw(cut9{mo>a61Ak2D~OsS=8B=79Vd zQ%8Q;b=OB;{_Lq!e5-?H_@(}gb1vg~(mu9`jZ80V;Kj~K8$aXAOvi4IojOf?5>dmq z94|+RFY>#xu|MmCe6P@UzGs!iV?M6G+ud}$J#2gJYkxyO{-Ez{^f5&J0?T;45DM+Z zySXZLi?L5Kts%+13b5NYk0 z`{;ch@Nv&`H^CH9<|eI64Bo2b4# z?{dm+-SAu2>W+U_7m>hd0!*= z6V`AtPDp z0Y<2Z#%kfH>{yh;54;rvz-5ozX*v!^D(Ey~IC=;;_r%-t1GA0izKkOqH~SfHY`rs3 zVnxjS)T4+RpNK#{BRcU(U1DQAjEADfJKYA6v5Z8**alC2H8{FZ8`hTTYhxcmOm3ae zlB_&fWgh&VTam8ZCqDQ&E)%^RqW0E#o#uoev5`ZmeqcGCB%V_#S%aL9Xtrmshv(fu zYXw9qu-Z8<+eoFBF6)tN01P890ph^Le|Te$nwk#=el+Jyw~vWwt!&0D^=53ZePcHZ zSx=EWmb?3U#xvQWh;3j0VZY;Kt}K(#=Lzw49VDa<^n2cFOot&K819QV~b$bj$X zhDOV$TdvWkJ@gDaCExsO1PyVG7_W4VvkHNXBKUH| zrojO#c%D3sa#1lc*im?P1DpsJs~Eaixl<=Y{p*6UCvox9J{Lvu$}WJG|_;s8-$aD!$PRdxB*|f@vj;44Ii3V&I`iS30`ac+b&dB76Qp! zP6F(V!xo4A1tAjVsCIAB`pwYNLiAo}E;;U~Tie~lfhr5p{iO9rxiCb$wUZL=HQnZLJT^m3!d?&p%rC z^iSqJYPM;17-DOuI%qM1(s>7%Sc#VizL3!?7v3$$I~SbTUJP7f??`+H)6|rCL^ko- zan8iBeSG_3@^-#k%GmJYqMhS0>#0iufB5b55TMGA^5#LbtIwU^ZolfHe1#d(+9i9( z_xcvA;pTY6sobd~cGpBA3j^c)*IqWmyIlMcHXZBduDWLXwr_v&_SZh~6SntzzxQ(< zU_hs>d32AQI^6Hi-EaHqCp~_9+Sh-h)*LTv@nYRjNqFVO05R>0qpK`CV{r&B3{Mdl?lGXRH4-EcyZnxcb+x8J9kSFKq_JggebbG^!I6Jn z2&nIB$lm5UhJ<^LYYEH41s-pC>sz-6yzgJkdX)A`t(>~Qa6!MgdkdJXA${D_A8k~i zsWo(HUZdI{Yf=pMd^h7KJ>rvNr*Zx4JpS0(hXkGDtO3c3UwiK*hezWeqfet0^y*g| z#yo0QdB^y=k)KgiKh|W>wAFb`>!W@X#V*A-;x~Twdp-#ge)Zci$)gsBoDRHq?f>^) z<508HR-V`#d0@Bv)Mrh9;0J!-_Trmgyglx*U$p(!Z@p$a$49mO+|z1MU)=!LdZBJd z|HeJms4=+l^4q`j+c^#-^mCGMunj-)rq8@^-k2IGntv1px%mr69fu;*p8J|MacG`b zo|muzY;tGb?PH^Dd-=;>zJ1;oJjS^^3+!t;y{PyqM9{iB^UmxKuFBuy9IHsTap-n4K zMn~JxB6XLKd;h^7{K59skN=YGw}0z5w`+J_L<6RFK1V4R$M9`Fw4HM-Y-%4r2g^2l za*`c$;W}X{d)Fg5APE+l)Q7R9wgW%$l8;rEG0t`G@U?2}rK1_i{oMu)^P<1sI3g@* zm=O|Oqi8a()`SdQgLz#^nd`iHDYX#Q=jaaC)P+}L>j$u zz>{J4&haDJn4<}gI-=v8L}Mqp=ade8L<2ll7+{_A+~^8t+Y+(WY%nmSa>88?qiADu zPBLoAD`=KmYFFEvn-raM&*@5zisB2Ft}}GZgU5sG4*qcIbJ@a7{lTRCTwU82o&*m= zOTCdDa?n%Xn5@2H+KYHnVY^^ZFpkUG`&vlW-pdKDSoz+FYyk?FumLxhSJV+DTnz2K zfeDJxO?&>2jR&IvXPMb?d?bfY*vf{!WNzv--zAeGIMgRxAXYp4mMxtLgAD_Y!Bua# z;-J=$;P@d)<|z7ZU1#b1uTD}j3+V85uCh3CqS9FP&;zgf);uz{XPY?+Hwe^cu$>=T z9wh^hdA^pv1BE;R@Z_Ha8Jo?F8`DBOk^6)kKJ!Ud3^ymcCH`4SjImu>Ry23PelC1U7v{RTu3McG;Hi?tUPgq<;q7f z?00y!z=Jv5=a`FKA-YD=3ezusG^Tdc(ip^(S)XWNu;ftDgM$rOpY%n{+(K4Lw-Z0v zubS1ueCPXiB=f;Q($1WDWlrIo8s2$lzPE_@ZLXWJY2=*xb}iKkh^`&k%3f@|p0J3H z9y<2o1J8HX zHo>U{F}cCm^}z)I_24|R-Zg~(p@k>0p_ctYy}IZw=pAXd-=SlJ`Bn3t9Qeoa?)n5j z^U8CR3?(NH&9=URgInt4T!h0%RBa_&V~;PaaA(Mak7B;)Mc=(W?|IMKuD|{UN%qz_ zNRIi?=bd1W_~b`yfBrkZcYE_&-YRay%W>lx!ETNt{NX?zyh|{-A!EhpT&;gke8QLM zrq=sfW<4=+qz>L?-x|Z=b+3E<_QNmH$JjJ4;P|_IS+lAImodCR<-4))yt?mjY~q1) zykl<7xiZ4@@Q(ou7e9Y1AJ!V@YU;`Q;G?eu^Y?K{=Bd#bVezN~G5DoZSx^Me1GB+= z5=>=Mk2z&+==nxse9m8U-Zvfj#Z&BQ#iifc(5F?GeV$AFV)qVR z?ZH*+M7%kt;NNy7EOeE#{ zMJA~XzyY5eeHNv@dS2i>#4%&^SvNjT4FTfL_1JHY;I(dCOZptbLCNtb9p7Jxp1OdR z>!BT2&rqs`6tba%onO$(I@o$9)lH}W_PXEQzWDP$cYD!yf8X|@f9YYqG1>fD%kt~K zys8%>sx?38tKT9l`u1%r%RvChWm%lesY|JpI*Vh;7@>3!`Wzr2VUR{Y(ft z1~>n#$*fQRu$53ix`z7A9}B?sxTD2rCuAAUVozWj#(ngP5(Vawc9x67+mj)-S|E#v=8W$PkO8wZQ zgI|sVsafwbGrVn?_lXZ50!f*A!f?j&vkzmn;~X+J=Eyx3Z_ZQH<-D$0;ZYxIa9-yP z@OaaLpC9;Rv&QIX^j*gav8BF_q`v+wjIBl-pQMstL}LZ zlk@ky=ly(8@Pf;Q0UmO5Rwk)4E%8DWVC)7tO*Xzd0PS>9@7e*rv5}g?um|u<|j6Ozdyjc)Kqf#o)r{y1U)o z4{K#lk(qcdbp1k$+6>W@Fmd-f5DVD}FJy|9+JUZGaucI%boe^=iP8GO1mA0~y&VfwX5iPGl@2Bx z?2CDSsI7-kC9|D2$&FE?(@zfU!LmJZ^=RDqX{`EP==|cKRvmuHdhiH4 zIK|A~ZuV3#`|~1UE- zN0?9zHjHSV>u55Ty!lx$Ht@@dH}mWof?=3o!VNp$q||!7;qLEaAOGU+0cWO4e05^y z$;%f=Kuzo#0O-bfXE7drokMi;3@2)^J2cEjEII3N@f136E;*f|vF z=^8Faf#Y|u@kGyaiN+T%-liI;Z(6xA=NQ7_q8o*=)H}8wC*7lKKkF#gSD(9} zkKoN8y~Ldh3}SO!@t0ZfQw_yosQKqf-{%jWW1MubEW<=Lo&(nc9pn?=H#0dONJo9V z{-nG?&mq6~OTW7P=U@Jn?PEXgV>9pKk~joTYYuC#NcXzey|(AQ;2XCmeC1PgL7p4y zcifrpt5YJWkFZz^`1Kftr&k9#NO!~aciaB<-}!XC=*Y*A8>6w0`_yFH-u<3;Z{P84 z-?`m*<2{9&U#liBzq^r%(PRH^GjY&du56|z;F9Mt9%GvMQfGPHO&^tHUhdSqa2!%Q z-dRG78?L{8UC`SPJ3lv}K3ySd+Wqg4g{~PJhLsuSe~UTq?t!R=kW3W8xcy z8mehi{MuSPoak5I{n3v84VpuP$tQKydgM40_IYlWJJ8vj@jK;=?5<*-%YKLol?H{QAT&Ufh%%Rkh?apZFXJ~;Ajc-;_J06wOw1IX)90wYm7`gwLimScqH zaG#33R=CNd^NL>VWtm4twd&W0^%l1(p^KlzL@`1Qq zvGW=^_yX-f;OZwvWo4@|*?a5F0((RV_yl1=bO?S^9DjdY2$iV-?Tf`MV zG1PG)!+{%$&Vm=c2S~FZz=Z`~*dFj+)#utHCheVk?~e&ScU~OH-0O`zH8M~486E0E zECr($ZH?agBDZ#Ih|h6l&aOQc;ZKiN{$SwCdST6-*Nv<9x#@cI!3G^RmvoNi4;{TR ziyVBK`6&)9Th{8>TH*x8F!5@*@Z^Xl$3;OUk(@dKPu&>b>qcra^MTwq?NfUVo!e0J z=uDji5M8nZFA3103pNbw!$Pf0_SNjYm@5}=?&{?-_ zw$Wgh1m7mwAgAxzIdCkUuN28YvFu~4@}zzeXv2|l{?hFieSynI5S*}tpaTh@8#`pa zDAzzIxO_pcEF|CGJw(CggiaLugb-N9^vSU=>flj%ip>Q+Hxe#gx=j!rq$%@4KUGB14ZEfaQ`tc6`VU?&$f) zj}Y+drM*#~wU3R~7B4bg-+3aIKYppj9>al7Hk~hCkTUP}fv?xFaUDiIS~9MmMcOnE z^Nm6aweC7+bAN#Q%*zR-R`z%iKI>b54 z13sJwKK#QUwteJBK5YA?U-}h2fAqNuY}Q=;XWbH5T>TCpc<7?z&Z}mBcWw2quK1Nx^eU;ROy)wnn#wjatDeX)IhmI^Iv7>qr> zhHl8IskDj-svm)miMGuLY)6^+m1BsmTtVVO^lE)97#sb!9y|O^Dozt`57^umSLPG| ze6_E>`@rn}r*>pCbWtsT4;bqa^hj1*IUQp(_ECS7UDP~rym9RE{GnGDL{>j*v+=x# zkn6JNgZzkH`*QfRbhR~xH_52JEkOBVvUCilH3q{g>5q0teQ}Tb+G2ODZsL6ABOkfF z@+bcJ_O`e1Lm5ur`SO}3FKjXs#W?!6-@Rj#qOkc<7|v^V>SVz4PM)V(ft^(w>K*V} zM-A+GNj$%6OFBQN2+|lj{(UX(6CTG6M?TNF((8`_+}NEZ={a9@6^?#p*Rn{s1H>MddgOZAIt52J`Hbd{Dv*YwDnyz>{Aq&byX$UeWW~)4t}3+Y_Js z)a_IM=BN7)4)(c=;|ZN{SffTma<-x8D-wSBmwsV;$`c>I-Ju`N;(YyXy)%>e^x5Y{ zADmKe=9{k1-Rk7JfgBa7wNowb&}T+@=Zk;zhvNyi$(8BcV zL8&$R>L6KeVF)MAu{poO-{*C}b&Ow9U~=G|%LVK-;JnxCAN;s^<&2pFp(j@65t!7L zUi$cSZH!c7*0ZbPc;N^xh}aiL!KMb_q6aVUBIUVQ&sn;0b@kO+Uu0?k_FxA`yX8k8 zxtlt0XT4w2^SgbeAU4lE?ETbYnFbhW$Dzjj9__Jp5bvmI-bv5=_Qo1E&4DSmkL+n+ zwSxFk8_OJXo_l4H^_tvBHR`M_u#Ke+c~R5a42V(-pL;x<;T15 zAl7tTM~jf`t$4fS*aK;|rw44<#m;pHB>$zaee#AMe>~tgXMU|66KpSfzF5l$neVK{ zpeK4+uqO{}@!cb(egk1S#{=_Qw)n!)zUr3eC4pRbA?xkdi>y7ZNe&DdbNp~ka=V5^ z!JX|P0=ImlWclie0GHJhjFwTu6`bJy7SHu1~#QgY|I+@H3P zeeswNe6nJOsbt9WZf9;{m<}dp6ua* zj7ZqhV#7W)$($s%xwFB}n|gXA$zIE-4d>jH001BWNklS@$3oWkQpmlNs8(!q8H^PZ9_3yxXnO&PUzr~+_l%)x@C1rdh1{lAR83$%S%S>3to0Yz0&R6zp* zk*5STN~4sR7&Q--(4;jn(e^<=MG=yri6o{)Kq(0VqCgN7h8Ujmkl>@Ej!vRNC()>c z;XvDATDy%zh$sp|0rjsZRXu-mesk@8?*Gq7*1c!1wZ6yv=J&0=_t|IPbIvsfw3Exs zJrkD`8-bA-A6vcCoi5XNAD!E7y6Iz%S^kc5fb$$baHlnY=OH;ilaVcaFff|B4KrD( z**_{S$0A$A+p*sAsUs>kO0Sn!dk;BZU zzR3#%>GaFHHSTulJ$-ZShL3)z*5Z_X)JBpvXG1g3z?_Sj8sVp?K8edpHQ*-=Zg?gq zkKwg!7<0j;gWkFG_VGLWBV@PUa+B8=9SF|MgJ3rTn8bq%ZJA`6T1#rZl^BiE9^DM? zoPpQcIB;z6;$xv4Bix*1|MN{R)zl5Hhq#eR!2V%oV^$+gn@697u&dg2rZ(hv@DCOj zLcmULOf=>gxm53rz2z3|e>Z$oUc*T^zKP{m5!5Tk|K6*P!bt2S!PfjNr_O}HPJ_i9 zHqjx)A3a!{ua20EFV?w_uRb8|jpsh9bMw4#2`CUJE&j2I!Yni#j_Sx6K5NZPVoc11 zeJu9gHzmuQ{VrS~o!s=*3_6B+fx|hPcb)NK%1@}?SE)Bo%J2Z3IAAjvtl<{!sjHRR zgd=`>gp#m%(&VO=V(SHSu(6Fn`{EVrxZfkI-%U5&q$g%>zU9KC7bC~A*j^jUyw4|= zW1yrpEg3boV?bTHWQv ze4Cr!joEsVB5lou_u&tJczeb(zH7VX=9{)#ohR$fKo{G(0Z7f+7ue^rPiQ~C8GW!C zBeNabd5PRn&zL_Vkel5VAIvK**ENE^L-E{=)Q{tz@aY-j*Om%&->sB>2x;W~ALPw#BO)yBqbNll@Om29F!yTqr~Twz+{(KlWc* z{VVUhas6(!DyOfGX~$Ed!Q1x>^fYjfV+)TP%no%bE++N!So@{-qbX{2#Xt7g(`s|| zHP>v{e(vXNKl9T+6@_)&K;R}`ZtxuAV$^yphZ$gJ9wL3Kj-8wQao{LPqc2qK57$E- zyr0W2y;QQtCzWFV-&^vLUi-6CCaw&LeQcc1`J?{jx9E?(QWWoOK1UyPobS0|d`{mn zN)31c>6=&5nTLX=24Tfb$>L+cc>$b{>js6-M+ZN@yBsbH<0AcM-tTn1zx*q{(tML& zj^!iQ#5}Ui_-Mm-h5q6%{M+q0-}UtEoX+j&Tt(fy;~e3ApZHYEy42Y3G+S9rY~W=> z4&{59Uc6ndzrk^X=!P5h9jt1pE9lc^5cp_`a#(WXvDT>-#~n4rf1K6f@7yGnx96-w zt;h_G9mSFHWx4}Mjc?L5!nvDu?&jPqpw!3?#=?zI!3Q&YPqF|r=k(U7wU6auDKmp; zza6Y3VXj0=&cO!pGVNE~AiDY1y?#3vJgAfQ9{>eyJ`RyJC|@H^4vP(W+9!Ufeayj4 zW4P$oIjhs0yOFsU=8h#&L}`m$)3Ow`}qmgQk4IKLjvO z@1f?)@t6HCA7SMkVmIG<+xD?@xsKxHQS)0dHR#&Gz5dlFAJxl_BOQ?Nmn@6NAO_8i zy2+8X@%d`h81c?dj*(l$@A{8Xzr;?wj)~iIN}#*N4j)?i!##8IBra`?G(uK2ZG4{+-s-0LTHcH9P-*Qd=hErz<2%GSU{iz?e99o=)z~E*s z`ylHIKl1`Xa~d6WYb`3Tjvl@H!+IrlBI$D}=1q!Ypof zl)P|YOiQsDsF1u{pamcY2TU^gYmBU2?715YfYePaiS8^dFC(556$4od(u+V~jH4In z6mwOjT+qW`Kibu$=PzrTxgt`H9k}5y8=B|p!928_xFcihy0{V)8aHr~E5v+YNa1&3 z$)1|=>s*Q}Kl2$qJe)sv+GD^BZv5(Iw7}WktcZod^inhN4(7O8I-YEKp==^(Wovx( zZi*NGnd8Hnm&b^0?tOtMaK?KGG1MGUIAlw#VTi@vaJFv0*kHf$J0&GQ@;l2H9Sx4r z@#4W`j2YkAXJs1I&Wt^S7M!dZh=X8UCf?`N-=TNXT+`IVkMdJg>57tgDh>Z0i8qK zGEQ2JD}bEEY^(9q9;!Vr_>FNhcfR=Bj-Z-ojNHe9?Zki!c;3N#@benR+?*2wi_>uy9$R=2kH2c>d~&IUx1Eczf_e9=tvN3179n_BC$`1A*;%X8ncY9Gpz+rFZLm*Y`YgyX#d~ zS)7g0&)=v9)P#3(z3pvp*MHIclO)A;f#c;|MP8l@A%FSRA5_?l^^xl-!Qw`c@;v6- z;N1sULqAVYD+IjS3*fa4kdHMO98>=>n z&GL<7rzbg8c&q=*D{G)*Q#j;t_6AmAsL9!SRF?3Gf7S-wSy}U3byf|@we75Z<&9Ur z_SJtPzYFQf)yFLLBR3ftPh-vnyrZ;?&f`1#(-D_3cyE9E+qXaa;0K-aI$ot?A9QOs z7&G115V`gp0f>`X#ZO!3S5_iN&5*?|yM}G#&pGTW9Yde@m;ds1H+`oc&vVu(zqYgW ztGqoQN&U3X_zT-l|HMyF9PJ~Dq7gvwp^7UxFvcMRd4ZI_FW2Q?50L2^Tn0X^MJ;v8D#p z1nm02$8m7gJ_M+rOOP8+*{a9hpHz~bF&MPeCC9eELr{9oS0DVq2Yi0lXOISG9O@W% z>&m74M`azy_K&~u4co^)`VqgFR(aIe*Z9>7Y}KV-Dh2HzoLgrjV zp0w^;nY*57^y}}k1ap!zzC2zjHe z3K+G|{s*WcB(!~5Tl2Ls`f1qs-MKa9`e$s%WYuVRTRiYSek341F?ybLZjx~vCK_8g zWilR;thrIWaZO}szPL!hK2fD}rC?r1^=fQc*F*_uuF@4>{_yg-fXh0md55BFDLHzt z2xHVHAgnouXkc6N!@ztrNUrQxdG1yO+u;s9dKl&Pyj|@R#?i-6&s|#h{oQcQB{jv~ zH=WfdM~I}MV@58?gR;OQ^5lY1a?~lt&D^x%$9%jAr}(56%TEi1562KUDE)3kai(s@ zAfVbF&(myhJk!ioK0Ri}JzQ!Fq?3z)`kVLYsjJWDygrjBwP8=_T4M`tYvxaiV9{K~ zKOW)1IVbx8D9N|B{Lu|J=EV)U)9qBvd6|cNOlxXj49W5?ZDFdO*^eYtOlvt(9m}B$J4+jwGsfvv~ z4qy+WiEnqtewQ9IhzO(pHZWz4hP(?e=KSPiHc>(6hStov<1Px)oq3K8%axhZ+Hb1E_-$ekFKx@BJ1>d9& z^6m!?ILUC+eTa{kJayxb7gGA6KKQjZKEalh!yaOT>3G_Z;GubD9AVaskz}b4HMH}= zYmaYW{CyVEOm%`VC$V)wq2P@B(P4nd#R17h^XxnH4vmSL*;(k?LaVYF_oI)eX|YEN zNMOjzE&G%ZByYCKtoiXoLsT++5R)0;$GI)X63)C(bOI+1cpTq!!%1`Na8SZNjyRt>`3Z%M_1@08FZ&qBX`UAjv1BWkeLMb3rav-V za&bN`TV6NaeDn7F=e=*BruF)&zx%hghd%UE_UCMDqCMGqUzCm<|Kv~pWP8E)y~u0Kb2clB z4-cp&zr9DpKUML$9{uD)fHj8L%#ZmoMwMgN&cRo`9o5gpb`1dybM7&k@xwhfa3jOS zNKEh-59xxezIB((&2vTdj-y_VwTVH^PIcWch)e6mJ&F0Fu#z3I!Z#d?AHKWYac<1P zU|ii12l;B0k?N7S8hzt*URiBys7*0z-+A@NzP`K*{JrmaFU~rQ^L$H{eW9+{yvU$s zyCW34IMp2I|M8c8dHadCzEw#?yJ|ef#z1NPAy4P-LMVSzQ$O4~_jM^iIE(v%AlH1C z>AT|Ytam}*#Jga5=dBDLaQ_GBzk0u9d(cB3qIbaOX#XkUN7t53epb!TfwQz#S@tqYveattwMzaTGx@SF1qV?FQn zT}^r;$@UNa;ZwJ-dHfUf35%@Vk!w*0e05!e_Kqhmd(M5IGqru`GzacPZy|(jER%S_&K0cGbh)CMQ}ZwwdFRG%o4@MWHl!T2c#k- zEj*YbA)cG;;K_`0a<2cQ1A+&KGxj;GBMlXJ>zoI!d^K5i*kf7F3>`y`oNtpaz|q*2 zp_;jw-|&L7?5Lj|th1?Y!zEDOXA?O%DDgV@z}557trzf`pV%FJHFhZlXUf{6{^ZbOB!?Mjju5(9fVUcaIfmQvxcKA5X;)o?{oAs@p zcmUbf7ECoG5jUe-kDNH&AHyY_lNEmTQm<*a0k`AD0)F7tE_uVl-0(Gz>5u5H%=&d!3((lZF&5+{~y z$;m#lXTS9|o5ua>38U57IM@?J`IvBG=lCFw_CcNigq!m^V`hEGpFnE++U(9`QV zCU$v22~FboCK~<@l)sDzjvF0nva#Qo7~2L$up7J*6C;h(`9;Uzn!n`q17ln}TM(b} zHwN}K*1!=QBnt^AZWQ?hoTxG zBK>ef2)4W^51ZH-Kt4RFB6ke{sZ zfF`b6q7Vr^7aoa__>p@*til%AIPdZq-^dP}ywAniXw;G)2&wJZ$!*=dmX3FqT&j;! zQycHQT&d9}K``+5Pi0oK1{_kZ4jvOvF+*Z{>Fqc5HYcGW3qAX_E=$G$+uF>aHz%7D z>T-)Le2E#knXxI9`82O^+m*(cJne*v`i@C1l&S%W{AnRdO}x+Oii7yDOPfTH$R|s~ z8cE=XHkvTB7dCM;j>l3o9+u?LHLmT-M|!Uh{CIbn*0nb&eY{8x7O}vpZScbnxnI~r z(nL)_()bL$)fq!G;{rUEs}68cV~b60kn{F&;OCgJ4>qzQkbT5QM&3uvBP#SU8QkKl z2OphnTqo_kEwNXNFY$`6HFbRKE3~O|v7zKUd~K40bDInNka7?WiIGQu zWS>+lUpbIV?GpB6i=A!9hH=|b?)4`Hg5aZll$*+)4`uViW?_^NncLa1K%rUd_|hx> zF?KBE<@2!WFTILwyI=m*cWm!`$2KmVrwY}kuZ`wZk(HnG8tw09P`kIpfIEQ8o z=j4t5W%|#TP&t!|bGD@vjdRLlmn)l5ao!0&0&*%weN#J*)14oD_C{^OwKC>;F5i!H z_?B1JG;{F#sQkHiaErcr-uTD3C9Bi8X#+OCNve_)aj6d0s5$`H7D&GUi7Ga*e>wHG*)2CAD?< zv5xQkZ|~Jd@xNmG`G51T^G6D)rCPr5Y0aF|0T;u2-t%5qz|t4ozYTw| z+Sb^KNjf@S6j*n#mPF70ffsL2 z{N|^uKfK^qZeA-wG}eJf?G7RjxdC|XqaL;Wi2h6ZLmu)l-;@ZaSRQ#nJb5N26d^~y z?4ZrQX*rq4Cg(6Ok8N29d}QrfFe-+9J+HkcGzzB;9b5XmK(t`+G$yK)lz|5}Hm28r zVIZm}MQp({}X@w6=thzI-&Vi8)Su z##U%f4D%ZwGVu>KV+KZ_Yq}26Iwi++90IF_=YhZDWFK9@VyG>(g7fkCfnM@u?}s1w zJSN1dD}4RHA44?|Q^(H3i8#>*(>w>1F6$8=Ok00*8O#%eOva)1Lqbyg%Be=iv>!RM z|Fi#;S$s$s9sF9$hNADIe#{wjP3ks5?T7ttz=!`nUQV2dmZ&Yq(e z$BbmN@WBgz$M9_CGp56SAvspooOmICm1$<6ZSAu*wLimutg&fLjya%Sb(O2@sGgFL zAGKjcTqDP9)cR%r0)rU%yUkoyL|9zWfe_AUq|a{@qesSZHWoVX@tO!MNras+nePCJ z(;K$rQ2UG7hBg${kj(-p=&{4enTKYv*p~54sy*eDulrW`K&KA!Oo{; z{)kQ0K0!4uXFr@d!Qr1y)f@ynlZ zR$g3WVtQ;`$h0oy-u;m%!|1>@9N30Wxv8W!VpGJ9b0CykZK!#51JAqy_+j$^Nd!Mt1z{4Ni-d zYq(g-?1UmQY+eiXp3}`hHY=|o%A7|&TF3>6UNGuO2F~FPA8IQeoST5rZtCW_+}qZU0B0u z&Pii|U}w&n*awJtga@-kb3F`_T9}G4#=@r3wJzg1E-|vrIE`KT%6zq*v%TdjA85=J zLqjbOOo$v$l48ySKbF$D%AOarKqc|Xn`6t%-2k$10D29;p|{M=03@d5skc!h_ZB@L z$(t9^3gDY@kkKEKI3jM>JAa<}UC-J6)z7?TyZ62CZ5+^Pt;?~GM&C=TcddNU7k|xZI1e~_BV<_b#KRYM*`bLV} zz{NYPi*ni@bcK~4^{6Q8Xyz-6Fjg}m!gJYp*bAy2PQV(zh9zaW*d^_nzp`|9}~Pjd0o+Td~J2Skm$d~HB+ z^aRF>1wV()1#Z?LYoD5u1GaeLteE7a;dU{aDUbj8-Y2U7Y2=XgNK%*+bt#2NyF z7nCp!q{G=w(lxP{d2<{TlTas(^bRuL+>k-)j@P~hMllu!K^bUsLr=rkb(^s%4YA5? z=-OKADVuQ7zQ*|(z>-@Wi>;dROv1wmHP7#w;uQbgWjPoyTHt8M+|e+%Y;d8KFXZQPRn7Ip zdl1$fQDi#AG~|Mhp4_-7K_@VERF1Y%_ADE(RWnF%D;D*Mkyzo%O&HhQm}Jo!YluBD zMZv>_mI8kJ+DkA|k9;dl9AOBTSdhY}1X6|0o z+HiFND!$Hlco=hHy53k3PtA6NVVUK;047di+3+yS+LcexYeTk6GI<2$m<#JYZ9>qQ z5d+kI^)oi;q%p+vM0v%y2=M7g@X*@I{%| zIEanw2ntt@Ykm;8ZrJb&gZw5BMxeQYokeq>P*`$dl3E)_#>n`^)i12ngVJ>{|1t3k zFyj)%Rt!W#*sgJ)j2BIzHDhh534V={d7fM&hj(m-l&uGM#;cPHbg~JLz97kq*&a*U zrLncC@TU(sRSy1usyB-oM})?$=NDrmk(0TqcWw2o%i1zC5A~#b2mc{cjl=3s^*vN? zdGlMhC;$B?ZFkW}4Uf&WmY{W_uk7k;uHK$?-P5;k_=dml?;RoT#rhH>zK01OY`7tK zAKh4d^kcqceM~X6JVY_S@GkzjbLY0_KlcUxxT9atj}2a_hyyT5shw29=EX?yUA<1u z^k8H^K>;tUlV8_7`rH)By13SIZ5{BbuZycW9H_f5&{T)gPwtuTvdKYyX{upwCFXdP#$dI_Z|OzI7i^MojDK9;y-3gZzWI2{~!I4{sEbA=R4ncC!JhfR5FyMU$nYbn)4sODXQF6vqQ!R zrXA~=HB(JxYjfGzQWGCr1p!a~Itq+(yo^DB4-c;sSW`!I#xjNla^gY%$b&qBML>r|kOZp;uH-)UM%w&Cm3>GGu-z}Ch0${r zJZyuV20V@P;3z@mHaf-&&d>LnVdIVsant)b(brqTl&#N0ySGdO4opzHWxim8r@2t| zU|TE=i-(w*unZUI@zpArm+8fD?8?=B5abi*foyt~qxBmeke%4b67s_JObs2`n{vmm zt!Xe%eNzV_qH7}#)1I@CAJmd_O`UzoIys{U)9;?Ax&p~LAkQ6%N1W8W&lT8BX7+C$ z<&*iomRPreS$i@Uc* zxWUp_QD|h4vG#==J7ij+cgZN!RkuqTuvo^%l0mlC09_8BWWF*p ze(FuaUF!fIifP4X>TwpzINBWZ+)WPt!t!(L)X=$L2nU@UeSzA!Z8VuU<`%ND+v|=V zlk;1pvrioJSjMq2tYo_f2I0Nulw8Acn9F2kSH9o~C^au)_8G7&S5JlQ>rD=LKU>yc zTC8a8JwC)qY-||y`YnHq`+B(2LPc0jFn!J%NTpVp!L?K{{u!ccL&9bCq# zLwcS7Z6keiLVq&$NfOWIow4W6J@&;lFL?M_Wa=zqQ$RdieDa+X!Ae~E91l%}V-XV zjKEbL&^!~u5k51X=(1Bp?15kdGImoN>l~d8x=gHRj34WSV&+OVM$-?JWkQJV7)SMq z&G5vgShDue{CX@5yt?kgHnMon2fy&w2!F>xuL;>2(@xeCsj=MSk6N3!#E&B2yDn5} zhpljPF6{-8jy~_(7|AKGzhz@&dmJ~bmLt5dUB2AdIRLAN{l=#ltS7o)z+E#sHX0(w zwzcQwoaLAtTI?b)mey6C5=^Z%!nfz?i*#e=6|a2t_9c)0qV3Z@{nH%d0JnzWHQdF8 z8#)hv_`|oq^r%N}zxtnkUHnWIqUOl_4&7{d?(?qOuF-!Tg*)wtV?h>Y$@$)@cm2k1 zY`^x7Uzc%?L*MlP4+p`3l-x2$oR;H(7xEJh!sb*9`qVs<)<3-RvCLa_BZ_%mz&WaJ zFx{%S)I9U7#UUrf!3TN2za{gDj9<*d-AR~D6AD#Cvh^RT^qkMTV$Qa5J}d8>eVF~} z*qon4<>>j*K;t8>^U(VJyMO;5w)gzk_eAGCi99)HOoYi}jz-D%`T$Q2?{fJSe%I)^ zb2sRw?yaO3b_YHKlln4g!&r?iob1fZ_0GAAb3gxO(=WoX6+d5qb53=n?vwei@u2x8 z>mknBdYr8j&pVjI!FAHu`=w&=$0wL@QC*5VUXnF;e3Ln+7B}piA-dZ`h^IjNONSp% z1KfxLTQ8GjFPWTpA#vrEcO{*LmC?T4dF(PqJU8Cu^2_~?BSM;ULjDUd_2c+;o~{4% zrzpXB@Pi&Kak!&*mYT8#Q1i7iJ@0Nb*AUJyWBi(P5+zHmlQ1}gA@en-aKXpc__*69 zavyuLdB6i6xNb_j_JiLs@%H>UZ{!9bbvBYPkZ}%T-bP&WL?lj(!GQ}7%(*GRzQBJ3 z*1~O{`xpPx_Rn7M!tFlyxle8~%GDT#2nP6PxsmqKu6*0qr#8%4*Sp{2?%TIN?U~yr z-~WNzOJDeX!q5#1I7xqp_}tDPYIKf3re7}!m^{eCGUpBfILyP7u_xHHR)^JQjmob* zn0>v33-X|7%t#y7wD#E0tP>ssa`ZfG#STm5{^A< z1~hgA-_=1h9&Gfx{Ku6JR~`gGKhYc$s_f7FW0q(J6MHpBj7T$L%gXa5Jc)_UvBn## zmN#*ko2YTDv+c+=_6>>g#|X^4>kwgUrXeutf{B{eWILok1VWwFL#{C()9k8o&C7Wi zV!v!NSv5D1WOA--j|+0xqLY#5>pBG}~T!qpfvkp7=J_OnpGipJN5v3P8vFL}5 z<6~+a8$H59L->#KSP$@tjWKH57_s&S@%0{`y5NTvDAqsMVa0ZOWdt!}haoob@cGv$ zU?KCwu~TPLk8rlB{b7A1$CmTGpOLaqGveaI z^U(6w02m&+DK2x2rdWkRo0~FINZ9~@lePi#T;m8)7>iVcX;H=|Vgqd^`cNfiu-S%z z27^&sbkTaGjbjY;P)IJY>l~kW)*Fn8xOndtc4T@GDG<(XE~IsAvQwdOOq_gyq7Jnm zvBPG;pwz>4tJ=00VL1&OK8N3};wgJ+U2bT(A;gG;cXIpF-1Hs9%<2~BK zndm~o-hr3Zno44w)GJrXh~YMF)%h5%j{L-R|Eti6izd_+fILBg!G#HY7CvDnl*K@x z=4aw~YzfNd5j*2x$FCo2U{kE|KX@6}PjIytM`+qcGCYp;L4vOO=sdHOxa1i*pv>{A zJd?`EEA}Ig>6tD0`%=d8zQ9~HY0k@Q1r$&Ap2=H5G3A91Iggu^67*pVbb7Ep$jMgm zJ=PlI4`^Isck8XUZ`WP-T-^-#s1dT(9y-5_vIPf**j#bN727ku`#Y`RCdeJSfx!z) z{`(g<7Qf*0{^ItC&w7Mk^d2#B2)DgAh&I<>fBp77*F9S}{5QQ_FQ$0;VGby#Ce-QJ zxQV#pxd!1|o5sfGI0oi6UbHKW5%F~x_KA!2?lD-P;YQ&lb9|#i77c5i|N2(GiHaxh zQp*kIy@qJ>+QShY$1gx}mydruW}a5gowvsfF2umk-#e-tu=hU!F;3#t=h$P{x-q`_ z=3BOpeE7qX$x7>yYqyoDBCucO0giJgE@QOP<@n@+P<8SjG1n5^M~T|PBH?(nVTCt_(u|) zORl)`3Ia<{m)?`Z<`c<}`%&LP??lG8`cLGK8S8xad)#BY{EEvpgCK<<$K;O%H3uvE z;9NV#3<%q1&~qI7EH9q;LjyHnl_xjqH$#8_sBA#sCI)iXOY8C_U-D?(6yE~y|lK*g*!D5XMFqMSY`>$-h2|D7lN1E`4YXGMf~-Sys!G&$8X>NgFm$0 zS6_j^brTJY#F|7vxd6A@^zPjo^bz;Qq_02yS)b~ObGN(QZF~IJKXH4(KYy9OUwW#p z8^@{XopeC4c5cRP%_E}R)e1-}d`)P4y1Ex~E(%*%$@&mdYGV9Bw7ow>@SYd3cA zpbRUPF{w8}&xoztH{oe|yKwyJ46EMpnVe~pUF?RWbHI++>RR`syZJleqcq@)(|`lp zKhV^LeEm?6jN2nm4TnNN1%hw#puQS=heR4I4{U%SI9WGqydhQ_IXDjqA}>~qthcUw z!lC}|2jc*1{)wO3g{&!!E9Cf1KT)kSPD!?R62Z!IE2emdS6Jbh{ir$FMi|D#!+41X z0?jhX?bBQ)E;97syVv~K5^>f6h1AFz?|kbhkvEUvM_#|soLr>ZTX+!27*encIAViM z@9r?}TCNmoAMc#UfKqc!$epnstDaMrJwvQ>osm&|IrsFu2$u0=(2&g&ALBEaT4y-< zN#@wXNIqGo9WRKBpJDJxtjhWC*u>!vmu`#5kn@f-Pqa{KfLdek-Kd13(6&e;AhIxN z3^Pt4k+~QQXlJk-lSCzFObN=@vGJ+E!{KQen-qz#erRI~nx_S~Gxx+Qs33?L|D&fC zlZ*N@oN5_7J3A-lS+r=vBG#5rp9D!h%=K&8>STa?8Y9<7y*Wyq7qiJZ7ws5N9Pnf5 z?a`mQ7Yi#|Yao@}49>t$K-ltwYEddR^$tY>$N~`jg=Nq4V%D=esk~jc<9z3tDA*(r zT*OJ@-3E@yEu47)#HH7ewf&BhIHSYLd15UYX3nL<-4C5(j$W4GO;3CTQz&C0m`Ay$ zqPDb8_Lt0w>-oY@Y*QNOta8?baC;N7^9YvXqr@4JF^hyE;tt9vru%+U-T|_;9m6b7 z?uiQ^-g-<8f*s`Gggx@^xBHq0(bn=0(}@C}hFlaGdc@>p`{w4}QMHK?_<3{3RxUvh-U_yTHf*!ahJB(`fdkYvjo4edDID;C|_9%D&Zj;(9# z>P_Eq^B~Xsz&l*4TTB8YZcc9nKMsmJyv-~5V8t}ts;@7I%GvtJD39w>6D&p+QF+C# zW*{9K7>eo#?3RAGVT;^TPxc&`Vjm1%An5#$Bt+8js=@eJms9(g0^a8iyYf7O^=>B5 zJGOWI=5KAk{7e7Q{}7}xK|PN5{b-jz@PQB9zVRC#CxpDjCgZC+qU=o!TTl~k@Slh0ERH+VXS1rZT1PNn^Iq}!*_A^THu>T{>`m6 z_#s3=(NxH-v3N$KU0hq~02H4GDCbnMzE^kF-`lHg%mL>loup+@peAY|va z#dC6VRQT%#0Q~bVH=&3K@4iZILU%5o7_S;`q~69y7=`5@_4&sdsS$Rq39-2lmWgxm zyGmwEW%wwhU*PK<(eZJ18cREC3_k#phX5t_SX)#S*f&e9LLfO}DcR?^;N^$IBDF)- zTOXJQO{4Q#YLj)ZopFZ``zEblpiL~^;nf?}$NwOk`VtF%>J_&24pkHrD>7a4wsSCg#qP25+UlRDn5FNe>bRAh8{qMUHrjCW z;IOHVI&<^yv#x!V&j4iV=SSOS{k#@t4L@$|?F}8xJk)dZ%J$0Baw%e=T8UpCF6rWj z-XF=sM-x6R^YBM}rvLEJ3d0xQV_WkP`zEz{h9xz3-NzUEo`-oZ4;Mw?b>`CeE$5 z+_qhEr#o-|RPVxj>i_;7+uiSe_vA%APbnPfcuX!}@Ui#b`+xtx?YYl>*7iYtO#`u3 z&B0My{Ku#I+Fq`Acz*F?9=pBj4R6`5yxTSU&PWXTO;yStQpJkQ>4fXlthU5R^zb~6 z;gpsni@Bua;7QiPNXCPdW`kYK`q3|59!JxBtGB%GMSci~v0QD`wPFgUZRf zW23~ZwgyEnfw@^Rk>m<2_`ujkTi%_@VN7!;Mf*tYq~X-U{<1I#D3N1p{zhiD%K=Pm z?ZXPjn1=mo1L=7UM!D=TAD5bVI=;Xy+XcK2u%$Y+@%+G{YXx?6_>MgSDfn3=H1Z7% z^=+7iqwHkA$G~r%4{Rhe^}d2)U~_EJe3KKlyGV+CLJodzvG2&I-2rABaK%mYffIP+v}MmM0L zv&)zVBfa@sT}&ezZBjYJ3{fZ~3pfn0mK0@6@6W!E4>Y<$>heWm*D(klyyCE9#1}tn)boQKvY=#JpCMtJ*cJESBd}rwhd9yi zoWUQ?Iwv0adSdOq*>QH+1xJC6YVpvNv43{2%&ma16yzRTtn#9CKc|tCA1K{0#x-(J zwDAksxk=}^guuqfZjRsUp8b8>2mbU=Ew3Ma^TfVmQexk^v%cT$>%QUZwrlQwO>&V9 z?~HoVH$8EC;Da8V7v{39Z~eioj4|zx^`Bgy{XNeWSH7fPdR;jP4g%I^4N?qqhd+7P zF8O+niH#%kjQyxM5@6T)!V=D;@Znw>1~$m--{n+(gVxl$GBYAN`L0XXU)Z z^PvxYsL~)gUAl=YM`dDXAN0)+gJdnJ8aN!Ac+oh=`fSCujdYH~n0-%}v^6Ft{nnTB zPaeeMIluVylGzyRyt9cHJN`J1L@OW0juUKc)f&MQZ)#7!)=3&}%yC1;GCY+JaS}`K z1XZ1V1IoG_7u=xZIH32t!ZgmD^<^K>O{+_#L(aaXcVyA~#-48e@-cRfFCI!HI`EaJ zUfPn7Y%bj%_>hMT-ud~++)eJQHMd)JL-W>KZgCkXO>=+GAH2sm2ji1E)Vcc@eeKcl zPl5jC-}*A~(oGIxQ9l%H*at}gu^xRsi29WhV z7rpcq+gE(`*KT*c>y=)+@Rt(KhG|ZK0z19Nx9G;~Z~xZ+y?xUYzGnN`|KlgO@A$|6 zXnX%3|FPG_#%DIiI#pG?%XEHz_#+;%z4nc7-R^V02lxZzALnMai268T{}L%y3<|D2 zweSEm>8BPb!EL-r#ZHxv2i-|4A7@#NsokoHJZk6Td?BPZCMOxk2+i9efd%}_5xpel z>Y?n4Z9B9xC=UAyY1fZ&ZH&>46gJa#kLi-i0*q+E!QZsBbxpCb|84_pTqaLE7?vmo zo--K#i4iezqJvn*UO(uT?*=@Nn#Pd z8umW5#?SjGiJVa6$=Vr%jT&>W zqxMC|TFHbr_>l{&w(8y7msv!|?JzuwmK!?anvBQ0nZ=qWuI#9k!sK}5x#;Y$)*WjOpUzWD>X}F7$rIoJwdVAx7k{7&kzA3= zvL{0dyV?*((uA$GJeJiR{XF(c;5eT2kT+J6D5m2H$k-AVt8?p%WB;8`tmlL^K=o%e zCag7QH!h5#VS|tPG-vhb%ik9mCNy$^SwEmXnRyXx*Jy_vp@l2ka&%5I z>N>?o0@?J1g^Uf^i}*|=8~NHXtk7ntO;_pa9UFH^9G_lTC-w;$qtT~qaJQp2YITx~ z3wGx?wMe(KTQ4NW34OSv_G?F%qGL#!+ln*mdcsf2qYu2SkCQn~%wR1r+gU%g#MQ!@ z$j=!_-?fU348{kp)(xj-9Qy$(-#DjEjNwfWejkwM-2(Z=$Yab>m%&7EYyq-_a4YX6 zFXmyb(P2!d_D>*v^*4#(;2Y#S#xafWY@iG$nGBRItO!)!r;b$Wa&@kqA7<~RT| zzDHD3AXuFm3ue;e#Ez{@9Jp_#1^7Zo^wst4Ep& zaeNlH_QWWZA{<_fIv#OrYP;fBm;mNJ*p0D5i@Bfs*3F8>lSRlMO!h-tSyH@-@Ik+n@c}hqhP!i`V*} zDh!l6#~ne(;X^JLZTGs@y|$-6?VqgrU8XPKf85{u8vU2i%j~ssf|H2^ta4`jBZt?# z=JkG&d$GSalZ&!^gmgW>5LE4mp_y~CoQ=zuwQeZl;B)jl#7DPQn>}~YyS?tRT_hPF zd*?gWE|SfqchbJEKK0<{&?WlE6td%;o!DNYkEaSx1J8|X%*&099sgK`7z(NzH@Zov zca>dosc_}MT;8jx<*l8Bn2)z_l<@l*U!1aBr%*3KYh zKk~IU4qyVdSUiVQGzkuJVx>0VHwIjIU_*U)YUyi$+F$@jFn#Q*kH^m00UT5^0I}GI zju)vl)^{`J3$kC*`Mp; zUVP!lkFJQ43fnY?aCS=;6DJ87k{yxzjM*84Abn^Ct>p9MWkyz5jsuL;uL7Fk(GE;K z2Ah8QT9G*2mP^vZx6X|9z+XR(r@8Hr!`lfk96h{nFi%~a!NGI7aL(Jh8Kax#vG9b; zfBOQS#wd88@a9VsFc*&nlH6hsOgR~B=0UX9hWHSJoSc{AYtvpz_;FH!#cH0fS-8!# zc%Fj8RTfR1-Q1^6o#&riBt~Un7H)arsFT}V)CG2O1{jiZT>C=8IV^#r-uY*ps;06B z-N6?>oV`}WQL`@tE1fN~)qL|sT*$*7Y#7@<{)x8p%{+DnvSR=e4~`P*mwB5pHANEq zsoK~u^G#C|$cs39!D3DEF`DB^A(;^*jBdbfgr^#LfNxnit#Jm%FHf6;IvA^dLG#uz z<&_)K4F)OvI*$Xh&=r41(tEBz_NaZ`FoBmHZRv4hf;omZVLbDRW4^j-8Bga`$t!okqDl+4M%ZoH4<6@3r*B%);k2}+Y zy+N=re{`vj5QlTl7n<{`Vwx#4UNvDDtolV={P2QAZtid#O$@&>#(MAv$DDkZL~_YM z!lH;CubBCxMmS`hvR~z5_-LEB+OC3tuNrby4LF#($%Wlp-ulzq-~QWQx_!pOKXZ;N ziPf4b<72+ed4c&wU;Nj%2R!fr+k4;p-tF1Xe#Ulh{rBHJ9>|Kxb5oO%Uoq+bK<_;I z@i+ZTzmVtq^-c`2)lYLGjXn>8j6Vy$p;CXC~jSQZwxtCjG-i_rS;jvHY zy;k~Wu}8#j!6^H$$0 z$0v%pnBzaR{-dXU>vrw6*ZRNWVozS>+`4uh+6&c29IB+h?$CGV~o{M{IFt=Z} zSKRfg?MGk#V;f&fu*b?ifLZH;*Q8=^aM6L{V^U>-FLTx)#)%P0w8ZCF)0YAG#`vdw z#>2N4yyyq@&o@5V=fs6O!H#vS4h$M5-28j{FZ|o>x@SH^$BfoP$1ES!W}pAv-~G?q zQ=a&Q?T7Ran;tOvxgPRu4`Emz<1}a9QF@>I-e-I954~c0$%|gF{j0bBxa{)Xnmz~F z6C&Wj$ivkmD;6<$Ze;GN)L!%CB{$2g^^p8ruPBTwfW(TK4F<}`ZNL*F$3$RP?S#cR z+*lJeZRmqj;K>CN(kf~!)D>%4P7YwkmR?N;VWI&sBO2A@p_%cJPQpFkgrh;s*CuRe z0<*aWzP^?SK48(N%IC)&t}(WuZ0u=FKtrQ7LPxCh&7$j?xlO#O-XM}VH&Ln`C<{wm z&9m6U9iJHn*b{joH#smobsC_j9XWCu7`eo&9<_8#6O&}Ijut=T55K{m&|{ORr#uFn zZlD_mX2T{%p_=-b-xP zqj|1P-7VC_JUglL*juM_8rWRlah1S6x94L=VU8pD5=dR4+GBa*2M(t8F*__UTfUww zJdnU}w~V-!VXb)nUOJfsc3%S=oJZ?5For`W2O_kNqn-}m469#$Cx#d$rfVms=G?i> z9ccD32`)WVO504rYX>0-WS~G;b`CJ)37*|)Y-9$}(87sk>K6LEhzS278^ZBGwh~@9 zpPH0(VImINHSnKk$iu`;Lx9NQ>x*uBe1}|dz>tybxmeFdH8#Dt!G(ohjr^GqhKDCW z>{YNBObqfHJB`QNPuz~{*!GZ;emXel2TAK>&hWJqPUELhZt}^8x{NP7wStpRw+DWkIPBh1z8?>Iqw)nM*Mxh7>Y6g z57z1|6a)Jz+87yq#<8?{Hehy|wHB1_UK4a_2{%rj1fQWV#w=qmLoWD&;jZz#?i&5n z%sKNS*9qFryueU_!eews#Z+@WQa}2>R3vC(`9=wG)i77Zt%}_2q-mc~Y!ho?Ba~fh zX{^a>@mn!%JR3ZqqaQi(7&q%ZCkQrxo@vEid)BhSM-LTq4~Pdlwy~^Y-)OwB zL%i270Omu81LN=kGk%2VA5iSYM(P`Hx@G&`=l;ODRkYNl`IU zf7$qk93OS%op5|t+?CtS`ezz86SkW$h@FROqVJ&NdwfBy$$VtEv` z#gE3kV+)Y!(P-75*VpQdf9;BO?9n;#k{qSw7Z{t@UNj(+>Me@mG8c(dPeOpe%T-(k(zC&2JO z@|avc&d02^&-fn-sc75;^E)OfB29L;YhNH5?@}cGr8>{BKk{zlfBv!`_D9y)CszYo zacJc`Q_tbhM?S_q_V}rx3w7))G9J4Y|D1*W^&)+Y`}4o>uWi>o_xXM|bdN9l%7wTc z55nFF2UyO5Kld{~y*>9?-z{71_sYTFw<#L8aRXgD_%*F<}22K6pEgZ=7TA99ll1<}3`j3OrGp+;?G@Pm&-8-&Bc(ZH|d{B(_GPK=bG3EhBV=~8oWPY7n z#pobz49#iqq8-eF!9Har`NtsjPCPqlb5gSk=?_87L-W@8w0;NEiDUi3$~qHgL7Ljk znm|G4wPyDJ_+|}bUk+YBBMQvF+N=o4IX1}ErpqK-LSu9ETo84k-m+_*hPAYe@RVL# zT5vGVoPJ-~U|4bpnDle4kP4xsWtKG?4Y*QA#&~QEqA_z~nzIK!-*_HZZ4dq$`??9I z5MdO53>SQl5yML%J|{2KR_Hx8{%Sz@4PM~GtFqy|QZNTR()Jsns^A$OU>sZXr7nIR zRF6yKx&avDxS7Yn`)n~OHI8PwKn~;u&?G2YgAd1r99)qfdn?AC@rqmh0JxizRY*uO z74Xiqs)6T;uKGf@oJKqg2y0VFn5^c3SKF{*$Zqw$lSJ!YDn zibY3`N{Y$XE`g9@$8lZo_lW?XTuf`UIBBnDs+K_BYLj&nsD2m!3EkN7iziWu2bVqf zT8yLh*nv4ZtC;LIAT-aINk^{sOB?{~h42CmX?d5InKkZ-59||@H8~3jj^FtpUyn^h z0H!re$H{qVd}K9DBWrwo#|l@!h%^SC1OlI2bMb-c!m)1mClzu8!>g&WbBBxxVbO-O znqzt3>G^gMuPyO$o!Ib!KWy{-C`B>n2{Uz94megLAb)E3uFBeOyq`P40OW54zpn}oX^ znFCyGIUC_A7k6IPvG;Q2R%eL zn(ps+0Olsz93Rq^XY;dvQVLebV9x_g`)N)t$SKOv7f~@8(;CZT)trAO@C9G^SGJ$~ zKYvEH$2@Ir`$Rn-8;evUxUA=j&jN`fyr$$?;R8qH@bL{-X_L=*$d|?qrMJKRZMymV zecQPk&gGqQTpKx-a+e*a;l$XZHP6MzXsO2x)8Tk%__Z_?T)Q}N7*rc_0;Qk{-58mnV9*rA-#%YG2Z>|cW)2ky9Iq99MR6N zO);_Jrph(?hZoQPf$w*YyH0C7Wp|#kMW@u<&ZL1>k0^JH*q&|&7i)pBpYm?xV9S4i zSG{DnbTdBU-JqLrxAKos{G5#~=W6|7&*#QkW-Wd5kGJgV?%5Nc^bfXgeDYKD2a~(m zH#zRy&gMYg<-u{u$Fbk^`d4kQeEG|jbymU^5RW@<*Sn8(-QBMUc>nwVcze>}YQ zlja9Po>TA}o|5^Ubn2n(YXwG3cnIAc!xSb;@L z?C!VZ#GWWQ7;5WH8H2yG!M7ar{H;?|t{3y#f@>wcX?*1d7O@7_1J^==9aGVL9@`~# zK*ED_Hoe&yf46OI+h<+C6wkwXl|Ka=0s~k%X^3DLXz6JKuFfNEa7i(d1P85s!Qezj zKiZ%K7uk|~plAR}Y9Dg}pBp&2AZH=4pyA@J>|_LWO&MEpvT05tWLumQD`x232H#i+ z5DSl}42@G;^pdlqh6gqP$9|a&*g)B9>Bcq4@*{qOTZh+~HYXQ+>Q0~BTL%jOlz8ma zXI-dgWD8(ou609?uYki~c${`7#)&snwYB|V4~6BgfiL7-FDY?SOHJ+QaXDC5pYpW_ zXlGPosg=I3!^JJ7z}hkYcmrdTQIq zKupW1dFLV*o9NAlCvRfX;N&53kl!X$VGMCpUOVit2WG~xADqR--dOIo>|^o?PF`S6 zP|IVit%Cv21q(h)6R=6qWjN}v*GVY$CTPd{+wEvi5-0((5_9JIojQ498$-!F0O2*q zy;TKB-*rvCgqgsKg@xrBAwAZ$^#-!?_Q=e~UK$Rs)FJo@Gc}$IPrD_mx5s&DSq6}? zi`BJaEvHG9Js^^m7ix)Im&H#q1t|aU#IOi^Es*9r*s4XMMm%usJU2H&FKUn3WIu5V z%uFvF4Px2sOUqw+@goPmxF3vTJLcx~5@==c={c%Cb7TZZF%m{T^L&L}WsHfI+Ayv+ zj%eUD@#3`V=;tfN=itE8&q8l@kd2ccx`!`2YUf|X7$hqU{{(Jo= z%u7K^tu|--3t^k46sry-cu2H$JIk|jFps*%Ji!|q-nI6h^$xx0;G1&+FB~|>yy*>Z z-ah~HzaYo*1=g5YZO8c39L}5vG|+={Rriz3>sLMVV(k38U!{`GW%rGbZ(JzeQNPEV z`pIdR?Ff6UU~%K`ZNK=7K2Gw{b&g5SJF1BqvGR@v-ub7G{i=o+>m7jCJ?Ht`W3}&H zdF7P>mru^7nOHs7@ay8EV%(3ITGQ9G9 zF{U^e*B(Gi5qrj1sRgdx>Z`VmOci;|#{tV?)<^=^lex{%sSS-Sq_~kn z5)KfFxsKn+vrom|oTTQMN$m|Yh_YE1V;swh7dIKzKSMYgS5vbg2V{r+-pG={uQ|2H zDYXKRNNKFRZ&s2Q!ZGsDB5E+)s-q{i`yGcEs{!CeCdP_oaS8A)cHT+u`&WUDBUZ)~02`ci)U?TBF!V&~6fIc} zA#4xr{GA6QeSumu|BKE)PB2u z)kQ|x4_Fh}1g|6F#$l|R z574hCOR2%S-TxYM;=(p0%o}?@xNvS5^JFpt?BPi`0Ntf$j6ZFcFq6z;S36oKmN~Zg z(YsDEE*D;OeNZnfdTfoW3QTO^510Cb?J<8G1jPy5J@d$R-i^`zLO$q@*e-=++;n>P@BJPX!a(1d$T?PdXi9zF@oT@lz5jiG zSZMdNs~E52V@$@d+w%$6#&7H$bo5#q`x1XCsp*>dI$@t>gpa@A9sc;LZ!G3wvg3|T z>@HN_+qVyX-~-!hU-=5Zo0R<-f8PK*u=0Ow*3Ca&P+y^U(Y@+*Z{EK2%m0pV{=pow zKY9QFAOJ~3K~%RK%Hu5OqnvZ<2Nd&JPydeXU%uhT^lm#s>T3ve{^Jw;=vN7pPIJ=8 zH_Z5W@RPsc@%o1$zq;M5?+$DY8uJ+II1UasIxhBqaDT*SKXQBRo8G$p`G@_5?REkx z7VjQ${oLgbT0A|uO1r(UCW?av<@Pde0n#47V6N0X>Bg>`M9WoqDLZ}Xh>VDI7G zlYZ{i+-|WhnKf%F<-TG=Y{k%E3>_1>1}pE0k4g2oGaomN)a2wj&a$-4#6g|mz7H?& z6OewG z^Bofl)YITg>=8QlaIzi~+4vaLz&|DxZQ!&urnKN`;#y&EL2~lg@q!s2*#_!#E5Ds) zayRn4HjHsR;j}=YvO6xmscC|vuL<&Sj;tK3H!$|LgHtM1eR|2HXUx`7us_YnGi)3~ z`U>KkxpRpo{@9V89zK4FVAJu(mRcQTsd?g*%NQi2jLX-)h6vlr>A?Y)u?gU3glyt5 z+;{?!a(^WZYdhqLHj%XrVn8(|NC}^yCx(mApcJR$QHSJA(DjkOR3y>bI<_%nH9W*1 z@u~2cwiW}`=*txmu{^h)$;_qL;ZV}lJ8TLYS-0pwOY;affvmAr^$n(V+IYrh#yQc) zxBP4ylW=D2^#M4}UTCI=)_g50h$eWDBRwti%rNVt{`5S$3$5D95LtFIL)nfTQj!EU zj)soFMoMEq;-u~KjE?)jJ}$4*oK42@mp-Ai-EkRLzd|2)z~$2m0*RBEGv7)q2ERH#}QqGm! zSChAqwt{;?xJTF&*I480gB6VNPp?1sTC;tm*W?$8C{CJarh( z;l_)ryfe8@aorvF(L@<+*R*jI(0GH5kM|cyxMX2c={Q3KX^0Oe#=Ol>ve0n{{+^A>co5!f5Hz-I6L-@Sg`j+*Y|$!%RCR?v_tCH3A6G>ZnM-UHA09T3e??C1;K?WIM&RzU@^A` z7T7J3O(P@Ex*%RQ8hE_tFG;6)Vq}hgVw=WnPF@Rfc#;c7r_Tkq1#X07%n^DScODv_ z{K+LTFll@UGB39hMvsig-W)>$^p_Zobz1K>J5J@htX^Gm=1V`qJzni=L*H?HbHm`b zfBUzOm%Z$#a}&c?TCrouXQut+%U*W;;xD|`HwcY6>CzSNy12yJ^!A(0%8gaqYd3}4 zWy^N6L*ICNEgIvG^oQ5x&kYU!{q#?K@+Zrk^~_oqznI);bv`2iqjB1!>Uj6Rk!-hR zyxR+9;p}#oHfOA>{*6~TInj1G<9!~Bt2`w2KAta{c3=-_@7Mmk|4`Goh>1Hq?1d2^~1P8Ozms#4$b>F8FKlbtS zjkSTYT<)G~!?A!B^Y!Rm=_Kf2=4~gY8Ek6XBw6ri>Djt2^2xn{(MVzbW8R#ZGWLE6 z46T9M7kkacM~>!b`@!}CqDy|py1@n>%NKp%Y#CmWA?Ry-#&9J>eJ2&iTc=hTWSyjy zEe(77EZy+MVDQFNF|d`>$PM64_K_7+2H1tm_>8apiD|#`*^#O{Wf{o>B6>In_lhw{ zi__TH*5({<`2nZ(VN63~m)2d}sH2!ot!6zO4_W-qZL_{qAgu(fi$?xagG)Yer4{oW z1tYn5AF*989)J*pJMRsCjZn)Vve=xUXZ7)CkEhmoj#<$%g$F2Iq8OMsA z+91Xw2{a&i1=A?DKxdG#ZGiIyNw9(jUKY&Y-9SvuoCFXLBpsoNHkRn5)syrPVip+$ z^$3bZ_GHv#-MbhmQsc%jMQQQU%L1d}NvP(W2*f6{{2;4;^j2qRsHuVQ!g}zes~_Re zF>kEcW=kZR?aOP=8krvWa^cO`wiD2}^7p$NjHEGh{9M1pAJsy>;4)ZEf@vzB7qI16 z44_jXc*)l5M>sr*em3@>*bgV^v5Rf&!gcpa^h)rjmF7Ni$k9}fx9!zM@2NJ(r+ynK zxs*>aS|8Wj?vSV!T#Xk-vRZp3L~GGxd+ zHo;Fw#TW+Q+iv*5nE1r;7=w$3ZsLqVa|6e@A_u{JI);toUYl|?cRVJ!%)t&xoLC-a48W+_FUtiB;tY+{)>$1;{c_%+7c>19qsW;T4=Hi9Z{W*w5$U>U z!Pt2e)!= z^0i;5n|}}b3j}!jlLxH*f}Rmy4)Qd8NyG2H?QMBsipAtw#H(?4j=QdIyUY=%HpQ(S zI&42uLsR`n@~z$&16&zCT#J0vogZbKd^36W6YRZTDZXE1GS|=E8grL1E@wtg zyVRXA@U1`gwWF_9WxL%z#;ec)-|>F^dp^x^rrs888+>*yD_;FJ+YTLP^o@_+$SwY&5B}x%&wkctA20afmmL4`r+>Qs^_Gt` zR|doJ690@v%<8~?+gsmqJn<|4_VM~(|JCCbJ%_Ra`B;aIx7_oGE7m#ZMDHmIw`!*2 z{vmyn^WXoPCmpYR`OEYLP9G%HbVR>jG8#j$SgwKX4}d4GVN_0$PtIr+wpCmHAK~%dz|8w`oyoC<0hGY8KZqjB#A*j ztYdA7ijA?IROZN5Eo>wSkbqc;kOqU1n@{|#(gxGEVzR0o0|zD;iI;wHlp$jy!Efqm zdw}=)4u3{OB^RE z$K5v;Fm{E2bIbY_J~^ePRukNC+Gt$2R5=J>(#$i%%x4^Vtad+xNsPvEsS`iyGnaxf z7U=Y#A;GKluhdF!K-n7RmW&j*U44ak<17;y8^VHkGKbDka_;qm7gq5}E>}X3u${48 z*ab|805pK(74@uzDP3&p-1w)m@TY_k_TgAhu5-$%{5BeP@fyjhjOviJ+9HsqttQl{ zj5oQrUIvzIR^03Dn#!1R(N?d3e%rge=ZW8%lJ~RnjG22USZhE2P+}YKJqkWXW zeoR@Lc(G$L+A%b3bLrjCD!Hz@WS zJI2c)dh%XrHpaMCZd{S09=1m>w_Xgp=Ry=dlG{cDzq1QN+i!7jubnYtay7hbB^B$w z;^I59cny9gYrA~T<#WswYCPcxk=0J)jHF|*#2broOSjc`VTGs3VnlL!EZnj46W6Be z#VwIiZwv?DK5!0PrtG(i-~`3vvem{nL_}x7r{&YlF1)wMAkPRfc^a zJolU3ZkLa%x*fk`{5fa!X1ec}5<3G)xRqCw2sDr4&DNs{Lq;Xgfo)j46tG z?2SLUbskGYn!9n0*J_)@@w(sqt>ZPX`dRKs~OYK%1=XTb!KJ6Q`b9#LHZm~xmnVTls zzr@u07kYnduxpYXZD+(@xVN1P^2Xfd9q&Ek{ZZfgGvDoD(s^z6Ib-Cjmp${^f=Aou zwwJ6p=-E%c_37X0EIAi(LxdYU#P@^E)apYY{NVBPKlgL~xAI;C6stYfw8pKRK4Syg z=6YTHYTMq%vjMl*h_LCrc*rYbOxtylWV_pO-XSl(@%S0v{_Xm9>-+StuetF>UHH~- zaQG4`A?jPG?>PRO&-)w4KYQNyAD^UmnetB3H0fnuZjHUi!j3ubH2nAf-@iNlyDxph z@#Z(b(eb1)4C_J5I!xWv`TRJ#=&1>Y>ctD(Yr0|kv~Tza$B)1CCH^+ytFiIhTlM9N zj_0V4)i(tHgRg(;@pb>`srr1w+q`eU$m>7HQGM|-Ey%z{kK(x&7;Fu*H1-YFXx1t2 z_79+}z;h)umSNN1W5dfJGRZRtH}{DUF7)v{{fS8{o+t2TV1n^ARW=^1PT}A#>q(}3 zY$iGL_@>8}YrTPS9Bk%Au;i&3aM&>C-Dk#;(6Qvd^R15FJk-g=KJ+yEO^+D};I|=j zD=;xM?i4j#D)87SSVZgCj0M)8fX1TZ^s&Fzk569pbDs7|zWDNBtrk^7O+2&?aI%WE=K8fSL#w@6?+{&pqxX6l zO97^pSLYzEa2musxV9Pp-EUvFi#$YX%oi1&FY23j%_@cx?{oOc;~XWm?0=4hfMN;X zIgKeejmMzl#@i7qOKhI|a$iX2z9d_NF6u%9Wm~BRX^Bz*qF-5j$acj9hP{0Xpgze> zQjQ@D8;$Q|P;OcD69*_4WK%SbeZbs#j9tr?C}x>RGOunzd$Bx5N5Ey`2GNrMai zE4IXz$u94P=s`}NvegBTofogc!hSG|?NG8S9&+NC{n(@P2S+<@0Ein-+(Z~qx{1lK z^<_+wE9M%(S33KLL;Z<)HtafwS!|sWvloZdf+B(Z=rh z)fYZ_aq1VWl@C@h#}=7>vz8zXTgi)N+3+KLxZ{^N79bplblFF+-;zx%4DB1Ag=gd8 zA{)!O0OaI?&)_@Mj1ih?K#XuwMd;NboLaLpg5e^sjvi`R&lONI$H#bdghxhf#Esc~ zto@FWx|;}LW{xtS$6r;jlkn*CfVw7jsO!LJ;hN9)vJw#ufq zRq@2jH%0Zztor%ZzPcYWd??w@C zubAZD`t9F!-2MA^`y!(GyJo{`#n8O@S6|&e|A4-2^O?{1F5QeFhTd`X@I(HPU@l(V z%!d(N*q-%|6uLQ<8V^IqkS{N^_ju)O?AajO8?|YDc3;Mw@Ae&Er-Sgc4zI@ABVW}m zJTFPGb{odn&~rX|zrOV2+0T0R@$#3yLjOUin-+>qT*qcJIP8b4CpQaz?Kgh?_@Nj7 zaBl9{u#B^Q%)oElGv-;Ndenw_Gj9&`<$7YA+3$AtANyk8sBgUXwP`F8&(iX5n-rqF zQn!9fEPm!R0QR2@id}xS9aHDDkBxIt$K<^@)mVLP(RGY^k9F0ry47F7+oo5B-Wx3A zSo@V<{^jFW{-0kWnDzx-m-u+&nA1&(IiC{aCcVRCANY}G>e$*Yu-}|4Re0;OM@Z)1Y?qmJ0unx3sh*3U>_c3!m z@ANl*_1_+U`w3ro-20w)AGhj*_8!*1@Ukxb0s(w@m`968Z)|w+vo&rO>5)1J22Q=)(9J~dsSoScmry^+)-(%WIvp?+Q%&p`Gb0L!@?=55XRj2o`@N-HLh$hD}VZN zE7ZbV3Y-^JlK~Cs*3P+kj|N`J7l;M2yzxSxwN}oPyxLUQ*a;)e$qAa*pBIOVoDw_Hz{ZwqQ1$GHIjKag?POrLJ)0VA>;Q&_6EIf% zm&pvUlM%)zQDN84I81EF#uB`OzbwaOq&APyvaqLOttX&>+QYexQ_A8HDe^@fhFSC1 zv^xX&w%9B8iH;YtDYVz(-BO z9%zi)8Y3ivYuRZcRicrXxsNk2P+QE6D{IpQz=K$CF_S-b!Np*4i}l*pHa5h&%yk(K z$5csDK^MQF{TE!@O>%TnHg;o86_(sF3UUU_Xyo|rI4=5x!H`UwZR!~Ji}+lCTZ}~; zxV&K0ey7g^a-#+sAZrQZfvq(Bo&W`s7~%dHMuUSNUgc8**K>ng*ySaXwO^K?AdcWNoT z#>A{Ki;)GW)F~OQ`yTp?)$0X}8|JoBE*?jmzZlKf7E+$HYvhzrWUaxVE{Tm%{&LMe zGw#4l4f5GV81p;e8wq2|FV!`g$ISgl3-aL)3j~~I?A|bv%=tPdahWg64o*JiaAIM= zY}Jn!R!D@;lU6U}l5_kzZkW!?F||pZ9@byK&Ngu;7yMSzzZ-diM9~bEZDz8paa0SclhmN>=&R9zSHS zd)@1gfAdSflDK|91QnQl%5^`nNPSjg!j^a9w0`^{D&JzN59t4>JMGil%fmberLvj2 z`{VSr-FXU)#j=k%nO--m|w^QQ9Jtk^e(=qf6KQWFV$x?@|j>P7@zIt z8w%csB^pM#$&iwBtbX@%zDM8o`eJ?V*?Vqya|e7Ldt-VdKG;-#C0)B;#j5R&-SJi3 z&H-KL?d#W`5a$Jp7M>_0dg*)47&HEy(2X(4)%Nz1899M_~sbzluPvMW44CGpfz8~=m{Ep-Mp8LK2 z!$9kfPit2H&f8Z`f2Y35{jdC2|Ml_h`j=pRK7;q6n^la{HWh#oBurthBU>B#+E=#6 zNIoNxfzREO5BrY&HwR;oa2s(oCN|=}_VVGa`FhSC`*_0DNHBM6?+GdiVsakG@d&5o zRGi@yHg+FFyt00M?N%JG`-)@41~ot1C8;%!Y;5T+h?9SGp>--5Wj|~5*qJ1vc~BR8 zVO&d|CmINh)Xqi)i4TtGkq!}T?XwUDD@N9}F5#Eg=~fRqOsYdpnBqHsB}X`NyjWKo zCN*?z-d}f2CUee=}dGLzS9+#a% z=GYRqEm+Y>DnR?pT43i|H&*kdC%78ht8t=e5;1a2;bPmLZI2nG)Q%in8#`UzhcPxA z!ACttO93AyvtPkeb3{Cb_If^tPi6Rxxs1&K5BnLkDcNq=Z~oz(INe7*tDn$lT_eP) zMSK>K&BnDAsdB`hMlRk!S}mB$Md#DKfsd{a*PqUog z7}@BoVr=SG9wAabc%I%oH*sGQ+nd(TVjFE*5e@C)4M7xQbTvWhL5=XPrD<8#tYtdK2$irzF zp;H5w0Bu92xFN?3E*NQJTJ)0k$#MJ&vwEA!Vd4aL;!RiE#Utj4RX&Py?b<`@1J-Nn z8VEbJ2ogH4ZDUJ}?Cy8ot#`LQu(+IYZ6LXN`M{M%=c*#(;g(}$Ba=0^MV^XynE@X-}Kbu zCx7xMN!s|=^fjKo!Nq#0aSZagfA%jI)rMObd@0B?zw6n@6CVF1$8Z1EZyq1izkKEz zWA+2t->B_ec75d&dRJYHv8(N_xLTjicN=yF-d5H<9WvuzUT{& z`|f?uq-|iTfrG6%UW(1~F*mT~HaN z?RJ&P{UVpj_dc8=*>f#kWu4a-Y#Q?Dw9o6<;T^NT_HTdX`0_9QqT`Lf^Y4$_bnYQ{ z6Xr%>+`NDAN0M7_)0brY=}$jyzvIr2xX;vuzHu27m*eVZUiRb1*L>CAMd0(Cj>vFg zca#IyBsbrD3+lwDF8FI7PNQe-k)z2Q0>gmTuhg2Lk>e z(B@d3Y`t;Wv}t_XZMPl&$!GkTb+ao}U0drRuMVL&Wn>Qbz!-O?o#;n=^ISt&@c zsK^F=VouEpYkNSjpU4xmYh?QPV~e2#=<-MBL=OM+wXLk|uyV0;XPPtyc4|Qhhm*Kz z#o)RvJR(e<+H(dIX=E6X?->uB^D+Mw74d5IW$m5EXuPjDsHBr%LV|08>oe6 zTnDiX{~e>}L+`a-G@R$j$!-Qy5GKPJ1cpvniY5*fV@PQND_Wv@Cs|LPCmeq37&G!73lKbWZ$86=F?elonK5}d7n@MWv=bLexLbogKZTF79rV_!}zy$z!0u|`P9sc)>gu$Uj`wI9RdC5AS!0rFKZeuxw&@YB+slh zIiL3jo9&o8^soWQ$J+@F>se?T4JEKKlwBbKHh#Ze)<7^CY$( zbz`1!d#1J@vAabw!q%)kQu_?ITW`7j_<(J?u7pl3L_K|Dwn{$WnOt)GP->sec47Ym8(Y}L23?3i;@Po-g0Ns}`^!kjfw!~oE)-~+>x?VWD z?}Z*RYQSLw*0LR4^rx{v2aJr_Izd?ZLW{&y8XrYY7)0{3jk+w@Xfv8GY=BFSfQ{;`qiV zF}dmUzW3ededSBO=n4K8@ek?W?)`^I`B4kM=!KhpXeY;H{_xb!pNpq+*$3tSn`<;&iT*QcEUxM1#mY$M=iyivLh%P*Ld&adXHgj&6-}BCU zj-SyNWqjqAeaZ26zT(S{``>r3Ki}`XJ%(@4h*fm_AJ7*S?J}&c@`-0K&{1Fh757Je z=wBY+^!5L%c1Hd^zc;J!=Z}F#GG|_{@Cg5`OF78etJK_N1;EHWb39&85t~>)`mbJm zeDgp0hsS&0{q9v0b4<|KtvnicvY0|Vq??KTSOLegJVkf0a@>&97H)MPJOo?zw5XXR0^`>;WmTH*4oS8^&T_! znw3+%U2g($Q;{;UC-iQ=e?6IM2^+c+b)MR;`#o|}OgrPkID|Jc?8 z|Gc)Ugg!Y`G6MN8z8Y*=%kw;fbkoSrye&!&ReQgZbz4lvMnYoITF+H~Y51{1PFL$3 zFKxvapR#FRJpGvqqXaTI#-m-q%3|ySo|7x%f_f}}S_nZ9y<``@iRZhJ{%cX@>t>++ zk#DS?-P6Yg$>&s!$vq5Cj;~;)bhcMz^jSRDC%IAE^_x_!|dp*D%;IBTduef)7-wpG46j>8li zhj=-; zwOmev0YM0Cy`8{STk(mVOHk8At^m0)J_*Dl>BTvcv1QUIQA}KfYUSNLQZv?)7e+FW zITx#rD?y@WOLnbE@{!!!)p3mA-hyaM4EqSBJwbwIW32R`jsJ|gM3&aXb{K3Jw%2_A zCA2Y{UtCTAxnAUeopaqVT@y1V%jTYO)+e&&0eZ)Yw{20#zyth zB!bqDzMNwj>v3~!ESCUTm%|Y!j_cpjOazw_zG$9??AoBP^_WRrODLe2~Dx8HTw z@uVj``MCS;cj{dq`oQ?4MhdYvU)GkiQgiEU$Y|R5nIGO&wI6SeN7uA|>T!)(@CkO2 zBg)grFP_N7`x~l>{V3(bIsD!4x$pQ}U-;$6KmO*gKR)lT|Fz?>kA19-_PAGT;kkUd z5%bP>zVmp_cRyF-e8B#k`tripXZ({8qmYF}iI7ih%uUI7-^13U)L^nr9!=P`&>bvL zHSRisz}#OFA=}*!of}yAw;yqFnC|0IFIh47n6;U-Jmq}+pl)K`|Ni$Kcir{2~0e%BxP<|Y}q<~()ipyin5FTXz5aASdb`S`SkpVX)t{p$| z6F+tQ_`m+~NpI}ZEI4=Zhs@$jr3sfEyz!N;u8R13j*B1Qh-?w4rys(3(;MEP8;18Cx8M0_ z%Hr0w$*fy^GUkTq3!nde#}9n}^8~AXPw{xC8fq|dql!6u0-%h>nX!x>;ookt_X906 zJxW3|l=bg5nb%s!|7&0Se;rTxhhKL*{X3pI397{xIgO?WSYP7*X11>~L|4%a=9elx?ZT;6$dS zKAXlv&oWefjl2ZNn25Vwr>^}YWqGuBh4vV@;_sFbOt5$Ule>vfAFPKIQ*7fE9%mf0 z8r-wH$fu4{CkO0=CpjvIW~AS)hbdqdiTc#eqHRLo_lkNe(PZ@On4n5J?bICpY_zv-(lRsd;0mcMcWadgd-S8LdNt^;v+nw;gr zP`~6r(9qd75v&hP)Dy>Mw&!A#n&WE|jgu=qP$S+34HMuM68GVm+9D9odD3_OT-x@P zC~7sw&;nk~$J~bi9dlzSY_KGQoyiAlO(3OG5Fw2 zGG0rP$}bwVU@cvQ=T^SmcP?y%!w>J2x~6wS{lCBR>&N4t@OO?c`l7$#4}$mc0?Nuk z1MOp^{(Hao&f}#&@~@Bk?tj0r*B6d#oKw#bZ6;=#MR6TwIO!3nmf1s@fdhZn4Tmis z)0>M`>r2;t@ZjsXlKI_IIBl)ESYJCn@SzVKuX)YS9e+(X0NaYqe6MBYtRe5hdBF=` ze7yVJ@6|<#zFSl0*N2B|ZY-U76eD>7st2_jGA$!wE3L|YPd##>5*-|hS3Y*T!G?D< zz2X%=qkolrU+QTauas^K=Df%YEO@w9syCm#*D>bAVBg?4>`f>5a{fU^9@u+~y=vnK z*y}+}-t*q~9&dT$n~&eU>vzTFK3_yh?6VFrl6UNRw-5ExcM7tpHX% zg}}PQ1KG{;=U;-EzfIrHeAip=(w9H{h<$Ir;|}fbA9wu4|NJi;pZRA#(>DUi0bI7; z6A6WE#Y{Y3e99_&E5@_m+CTiv2_*B&1wK|MUvPsb1h;;xcHgb~hkB>gOMdvJ$6Ma^ zCK)Ox9kZc0Yh@0ouIXKlZ$F;<+<$)j$xr=M&m}Fa*OD2W{D%;Mcs;?K+?ad4W#<_3 zXMPjs39UtMAQzS@1w*~;7*hv&=dRwE>_-~svm9Me2e3T_a6I18IQHX*94!~x4?g(N z@uuJTo#XYt@f+r%%N#;UpBuN2;sbbyAq?csJam9bvk)KEK(;h5Vs1)CeJy{P32Bc5oaFAZB{o23M4};a3PqmS+^5G@~W*M4@^%C_PfRm?>aPuY;Df5rhQfb z&5xMK9528tHX7qDgADM+wC9^d8BWFuKGr?vgOBCJ17I~Q;Q*)xnnp6btPOtm3(}{R z2V;04w~Qlmc<#1Ap4ecF5B~TCmTfBP(Qri)J=WNV@1@L;#=@qtW6zct<6(Z`vbai* z#I?nE3X-+xJQ(&g>EI2`$@^U6bDfldoDyr|O27zNZ#)NwT4w$6w+*~W$X#N3j{iKE zyDp_Fh3ELGIci8OJK~^Nyb{dzK_17!YW-|h-0=r!ME0LhfL}kCY}F=7+YU?TEE#UZ zshUK+9}HelW0L^MBiAaC`}$EzyBL$Y=Gd^m6#keDfg^V?DYE94Fj3P+{;hB8faao& zP3!6U%D{8_&3Mp*yUV2@4u8zWraqJQX^B8~Zp0l8_Vz*24){FU=JAT|H z5q9$-#5G+U=xK)=fD;rFIAcigA;X1f1hF)@@d5~NiQ?176I67;tGt9A!{ik+rZzGD zz<8|SkAj4LJ2%F}I@6WA3~&XuMQU98I-Vqhg)Qv8z?nojPGIB5^B&>4BM@k%Y6W@# zWUm-A8s3T^893gW2v}SgU%qf&_{4*ky&&M=o`Ib?vWeTg90LQj7!H?+5?_iTZxgBf zSE@MJaMf-MoRfSU2ASe|ztC`2-gbdT>)ewA5C0QNQq*Ik*h3 zHb5YxW=zk*HU8tHZq;*Go4`m{&PI|k{PcsjFKQ=-)s_(>*s1HpmtW=1x%6UMclWkS z0L?Wpo*bMLB3CgS(COE8LOwN;!`+7IuKYQ-;g`^)V@&fkGJ0Ig3eCUJk z_YD9z(0Fm>x>DO|9S)_6R}Qr-my|X8$b{VT80!J`i?ku;4!It1;8-*lKx`j*>jDAm zhmmV95S#LXTyplM&-_n6{doGf{crj}{Ev>MWN2+SzCu!c@Ev}VzvV4&J^tkjUSyeb zld5%;>G&r@p;-qfBqvHkB0ceKFHyoha@>CVZJuDY7Rtl;?A*$+vX{U7mB-J%=I8Nr zT#7>j;+faGm9=aK=Li6x_P04 z3k?29<_iV&U~wbDH>#Uvz7O2g0$*|>sRlhzzM&5q+ksKb-4d36_&5R%a zKYzjx89HLH05+)oJp@=Sv1Kc=ciiz&zQOQ;5B#CtrK4|KX}};Y+4lN6zO3&wu{%;fBcVx7~5v%KAR=LEl6JpJs{-XE0*dHU{2$ zleIeMOCP9Ag+rz{v^8pUiO2DV+oQzqc7G-tUm)>eorn4Y1zKR`9B<^@h}6w8F7?Xa zd&BVX>T$UPvShZ&RNJgqif4=KxIW6~Mg=Apol`z=|NHcf!4DjF-hTUWzuxu6zmB&~ z3x_-;^mUDUri}&Ed0Z|ktSp?JW2F)7oZ0*L>x^-6Jl{N2A#PGE_U#AtWlVXQz&@h$ zArk`Rckv*%q3jwxp#Apu?s~J2xkq^VL90NlX6({i3<0;SB{$Ew=}GRO_8I|y(IQl= znIQBQeAaU*&5@d2ZS|*y=3;}?6LUpB9?%;7hu?ag_mk5fe&-BJACvYOxeUPP@vvUN zQBPyqapNZQom)5!o^#6hT=Vg@&Z$m73_9DLKjYv=&kLQN8yjt}t??&( zW4lLIrTPpfCLL<8Tl$*%*pn{xvsvtPyzsoRa-KYgH zxMfk+$-DaozV-)5_FfwYM&6p?YlG-*I<~~uxwy5_bmhzQM!Ai<&Gs0wuqya?r$%1y zI`4(RV&v3y=a|}-D_Cl>MM7Kau&%3Jy~r<9FsGikeSVhSVc}9lkD109joZu(Lhvfo z+Z!1e{7Qhi_j~fu&)P?9Zx6%zyobJV4P!8%&T!xe)OJ>bL7do1;n+LOSv#AawwlH} za*sCw!>$8{4l!Zya14^1W?ORLHOY0?Uz5Y|#1>EIHe}CO1S8%Sj2N=J5Uq((%z_)% z{=bVnk>tYGzWD25Py*)nB1p*;E>EP!wJ!uif`y!BQYX>uk^_zjBzcUTF^2eBNSQhL zfusW;)Yy!`Rnb_j@)enZoSYv9i{K2Gq2^<^c1D|L$7*2Qc~D1s!`qQbvpjcd@7!B0 z>OZvPq$VLJr`BRbo`abh47RZP4oc*?xFIwb_W5gbQ!=gb2b1R9i06s7gmoTc;Le)@ zxhEEJywSm{o%!^VTr)jvc^_lwII z=uZy)_qa-kKs4tCcnV+$=c|!dnvgQI-`Fg6(sg|1sGAzbj-z7$Azxk}oPW*EjpIl7 zl1b_<4#X&)dCA_qkdB^+XRP6GerVKY;|@gW?5jaAVRWU9Ze6Uj9`Yx1zYrJ~Vwao8 zzJWv#OdFMH#(N~tEEk4zB`5HIZGL!KKJd! z1`=){7*Z4GAE8~NkO}z&WP1F9n-S*v(Y{VThLwVtkA2Kzj%WVUe{y`nCwzkPS$b@#5Q_lTbM!B_?+-s0q1ZH~W5%lf;!~JC2D%Uf}xOdYq3- zestK-oWJ_Bj;G8w!F00Y#gFz#)!<>@JaD$chniLwdcv7wa*7byaF#DMu*%6eAVG1o zf*NNnCccY?9$Pl_;?WB>aZ3C^5T0Y^nr`?!^w6#T7eihE55V+*q*2S}j$|S@59{Ph zO|EgWhqYjHv+8YdNFMd6b zAP;QM4Ki}kykg-rMNCKTIu3=?nuisY01Lg`FqYke4?UurynI$BI>nHf*x@st0XBP_ zEtNSp5apCO8Uky*#;`@p=ezZ~)E4qLKWkmN_}f-}?(t>-03ZNKL_t*86RUk|ir8b~ zi>Gq{%TgkdQu<+n#Co33%!~7dq~15wbGE|AbwLB@!jQo<`x}}K5kb~SbvpMwjM1oB z8LY;NCjK#MC}gzJnQJ7@5shRS$F}hko2n$wlbD?*oym?`>ERj~@6yk52T=@u8BZ;E z4kkBKW867n8;=YF89NR(p^H%~Bhe#k*GvYJ6j zKVCI+k^sD|wJz8!9`fggKwnE@j@>059F1(WJx2IR3=Y{(%&aA~$AWm$PpXM+s4Z4p zu~~L8xtJ4$bD^PRXs%=DPC6o@=h3lsYqM)=0mm3LKC{#%{FnO;2jnM!Cg))Q;S8IB z_iRBZSlL*w%^pAFSOl6eVS?ta00Zjdc*gFeU-lB@3`-4B@Cz0ospr~1cWw%V1?0N6 zOmofdk@Q=Rju|;y{Q8bX;xw=FS}_$TnVoCP+_K5pcx*ZcZ1{0ThiJLl4DSe-EPBk{HP}>{??7U?|Ppz@hRiBI`*rb@6af zj7Y6YoW%s&3vK)@irhGowhvG3+Hju?Gg2^q<2I@kySY17`RutewqMxvU4!(FJG#jr zoF$_qG;F*BfUPHx>r zh{I>1TJ|XYD;hC_yK%FKxmBr$d60KxGEu`>4PNLqMUqgn4wCeRV%D>AY;7%%;1~lu zV{tLh1>n>falvgKdCGU@O`b&!jAr@;n|SUAGw+r-t7U#njlVk6Nm9g|7l&ZVc7HN1 z#W*l)OKq6p;~K)pW_T|myAw9x09Cue z4PJ~}M-pjNuanj*eoJ~$MEuryt_e>K9CF;kIeZgQim^)`nc5tIgVLvZ^|VG^FyHAI-N^RMRkz#UZH zk77<3Ne<`UpoP?kH6&wwORwH3^_=HCUSq{V}m|FQ<9TV?YfA!anSO4rU zI0i4~IBNY)4H=usaK)E@2?TYmjbeC=4bQo}XcuPo2|U_HXWz=szWmysnr);Q+!RnJ zlvy#}@&hAoW*}8;-*}`z{Q5;Y9;yKwWL!I_IaXA%6Tb*?(z=WfW6Zt5iD7a^gjYEm z)isFB=MowD*y6^^+%WJ95n{>r5pK?q)9etzN^Zps^PV5V$1}ZS3OB5Cv&5j<$Ohro z*1jo+q@yisrDK4@fc4_te|#%37Th$V)h}KFbe>U~Hpk64OlFAlsUZAy#MHcOr1`jaNhEi zZ;y(JEJ&T-lq>6LUI0o-s2rQroBcSUInHe|I}YgP=woGeCKj>Ns{S7ay)qdc2|ohx@<8)~_4osIGW$r?I6@t_Bl5u#vZ&T4&Cx;2EnI z-y2r5R;4aP{5o#gqsXU9D149D0K~? zQ96_XBKBO6Q#2!Xonj%RiS0SF>st`BT@*9DG%iP@PF&kj)y6@9?sR2LsR`a%(wnyx zQ-krdXr+u(u!6ZrSl%^geYIQOJYpqVvA z`%l8=HiXbOJ49fbsN-oYAKPfYZelLbGvethjJSyt`XKIl@R7n}C|y|>>NqqEf$e!r zSO;>j=dXMAh!G149!|amZ+{PqCO=n#~?F<5TOf15z%vM~CGp zA3Md_4s(nC<@8oIE|sP z)pG&WC*9?Y-Dy5J7otK1$M&1U6c2)1So5t_a?MWV+MwlEk2Y+XI%fuxhj+TZ(yIYZ z&9gnSy%}2@U6g6Jie_QX)P$=GKQ6>_@i-TCE)F~9cJa63%+5P=*O{>)wb=^dHR{|d zWh_wE!!L4DdAZs-h%KNwj2sLP*#v_=(Ag8o-=KW?*+ z#bs@?u4c68`2?p>+5cKA;VMvLyRavUIU8%@E)@eP3D4|(RUEBvm}8HAXlFH}Zw#9l z!wBIpacU1Q##neiu>KN<7(I@9lEXe2sZVoo?sg~ec|sh-=}hVru@L$N(GlOg9EgRyILO`jKZOG4RLoE+N*8E0661E>ha zD_6g&uv9me@{?X;V6c!4j^COx>k7 zH)D>CJQv>?FFa7~&%n-(AKvqlbM)n9Dy-d-RmMF2mYt7_QG?u@XN*9z4zXGJnfsl#o#yb8DzqQ%a%>%5^&=HUmi9fA-B}E0eTY< zbuJL$fGvLdwqf09+5$Mo`WkyC<=J@DjjJi6Q5XI6uhz)2n#945ewv1*Y?Z{!eXQD4 zNU}!;4h?Lt#kv4QuLv?&YtQ)`*&MS(TIX0DgFXtjG_YKcavOW= zY(ecp(}iGdDTw_Noi%ckjN|v3`Y>TVbPVYD@wI}4Wq`eqlM4)l$=M1nwQ-&V$4&>D z!^n=lH-C*2h2?%OEpB;u0p|s}*KYCwSvGwK6EH+GGw)NZGq%!L;Xs}QA6LGmje~z;`9_)|g2F+&&YyG5 z@KV`+;Alzf}^nC(ll{eR-~TRM=L%2u_GF| zXz|e~L(2i;COF>+JqeBNSr7c|0;YmCFXXP#=;2drJRBb*ngG1!>R0_RmeYRBIM8xr zz#~`|Ym@VKUe`Fj3o*FhO+31dP~xK01FPg=D+^nF%aI7nTqh}9se}8;hq2odVzcEw zPR23b*bnAh37rt5?zzim4MOdtU%ZIp+!G{Gv%ZmsD<(Y5Vek!<%K!~QrA@w!%(ZeW zlXr%*jVIA#pExx^OHBXKfEXdMo(H-9xOM(0Xn#d;CN@2}fagdf7ibty4QIu#;~e!~ zBPfIunGx%3NT$9ch%+&;^_%i?}edXnE zTy4;wu;hs>7$E?1>?C$O?PJG22ad7i?|UP~mNE9#4TR%#y!W<60EubkxFo`sfpEhM zv*P!44zljAn6roUj|$lGheDps^lo}-$6z@CtRH^c&=Ho0n@MQYo}pHwF72_cAF^Fe zj1nhd$JCZsNjf_j7|i{sRSP6QW5NT!%y3cM;FtjRepqE)@?9BO0aka3>KA50Y!QM5 zud`j^5jzsuOkR#5ANniS7ND^WpyZ{N+&yr@iHTL3Z|aOK5-Q=T+>JK5C#U*IQ|_{B z-5uN*so%1ZEWFM&3WLdFI!By%Ydku0VeI*=j~NcqmP1%3rr!x7Ow`T6HL+X3cb@oq zE>%-3UUYgI2hVnFEcJzzgV;$zdSdD)p_t$m9?1DwjbU68cMERbcGm_QUl?xQh1qIg zAK5zgaPimx=L_`6hHm{4f=NvMxE|(1E-32-7ko2sZ@m=fTrSfZqLgcsoMc4ID#yov zA=!lXF%4}txyPD!on1(ogk3=KT@PuHlGA7>Vq{G0D<%5WG-$?-P(~xud`59VuHV!U zfB+INcC&9tZ0-p6e2}Z9M$|L&2JswxKbtWi99MeS=0%|+ZJ5Ctoawr+c#j$mT*$et zpoZ5bN!rJ_lY_aDU>flem7{yv+e~sO`@Anj)?*y$fFVqrJce8M5n?wS<2=L{;Yq<0 zD4L7o_{za{Jumns6gqHt^)@e>oZo=?okQ3NF#!NgILp|Ws}0r?S|?K|nX_$}?TvHO z8}+tZd`GD+ zc6=6IVecwyOPyl;WIfP?7~P5YI>|=R`%WR zef;7hnvC;h6w6Jn+8^g!NP6Dy1V(Ia6boru>IJ9VJVr`f8G7KHec}QqdBz6Fn6oDE!`4{X`yoR&q9D?i zRCEyYIefeuwl!!D_&Mu1jE}1z1hZ3Il!kH`=FUc9a}X#05k*+cZAq}>3}$fh zgHAS6b&q?#Su$(kA%EMBtEM129K9adFgRQCw06~sbixz==)y;FW{R9~kAc)6&kLYC zg(=g*KrW8fJNE$#(}b{%O)fU$hSm>PMZ>*r;WH*@r%@WV{(-kd_BC+jmLz8kt3apn+{1ORo>87}xlhQc$b3RUZ`~gS}12w*|L1!VX z!UIfT%O`7V^~hoyHw>j?V!_xEPd~X1AA9M+bUkC{ZFpY#8%wsPw+! A|RLFM4O_ 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%0D6${Pcq+tJHh?RYA#{UyuGWFqa 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 N002ovPDHLkV1hI>{(S%d literal 706199 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

    j3mntg~O|MOBB#m&!nX+mGz`D}qPfT}@Y$ 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

      +d3m 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

      GGVBez91dH74;v+i7-pCDKWQtFM0Kchu=PP z;;(m}tx`@#l%S@WZy$=Kg>c?-F+7!)Dg z!D2#Skh&zx`L%iCBw*%gw_wTN=JPV#fLk)NP8A0~(3+NjuYWy|;RFiATioT`Gy z4cObouky>B^a#m3OXSMdLDIMUmM6TH{P2K!Zb(<%ulDQUD)?lw*T`>CH0iP#myraZ zlwFVyqQuYCe4Uu&aIUjrj!5Nm;!0Wlozv&DGt0=ZJs~B(BhnGt-(fcYoF)ik&343AN_t(#U_Vlb?;e} zFZ;2v7o)kBlhqurPVTvNAkPM2yrg!2I|K?I3%^yp>AU8-PF*Wh;0-Op-P zj=EvT%@c0P)e@!jTnM1G7a{F5uLi}6hF{|Y?{L%37mRpbkwTtJI4Wb~yrzVz``Gfd z*bJRbz|DOf8ZDCSkg>d*JyeVMqoQ?9`$`!%E8OoJ1 zN?REi2$6xR01@(D<^i#v=d1(DrKDi!e3CpQz~1DwHDtZ-6&amq8lNdRH%cl5xJOQm zOl!Y=nCVw`31a^?SWWE>|4aVaMO(jr6&}sZr>2^ejFC9u)-m05WPP(Zgr@>EFDD%Z*QtY_ zi0{;-*PUfN{8E%x)*r!6-vA$;2K2o)4QBFZ9Yh$Y-|`t+f@7jE;AaU zZt9ND&vrSvE7AMiBa(X=24bHzxT-2Mq`&hTWY-{0vkDVW zd%T$1EG3{jX${(b^RiIx!z8Uq_C}?9+YuY3GvbDWcp}CWUTcrq6AJT9)m?6TXkQ4& z)kL8cPWL_MKNxP(7QqZdA!Yu?H)=|>qdHZcGOE422 z?t3cPE^~@J@Kp+(UuG1Bx;U^Ji^S=WS3Z2@OH=xmzKNk`Oe4nWaBR*h`mIx9hsTzb$I_oTF=6y5`;YF4N&IUv^=8afBv z<_;n`&JR$^mu-(rP90QC+~gwO;tLG9W58os6Qs>ileP53c% zQ{%B4x;X9ODVH?rl^x4y)-6(A+#0=aqU~k11B$ge? z$Ei!No!9HmuqZbAK45G)OjgskDRjs{pOvW$(3c~}`-cQKt?e9>9||4|Rx#-ud_l)a ziEZXBdnqWw9w`YtDRDb>EOrVPaM}b0!%u$={TzRE|1S9t+FL0nEiRoZT|;Lb{rqB5 z9qdB7On>>CDla>11`vNc64yr%K0ncT<`47pC#(MFkR-Vaf}GjTDmHz^dD(+OXLdN0 zve%BhmxMp4cqJgK$oq6t&=fQPQDY&~{SoKK~;re7KO9I)(VqaVuqTe-xSy zW`lYYBA63OWN(&j_X`1~gzdH)2F$a$)LTQ*2M;xs40z{jxn*Mdb$a32r!j(J zxW($_a#8AfpSx4%?>{DpnH;ka`nUD^t+$p9yQ5GdP5#t+v_9|Yy9Xtw-YGak zvTH}<2xg6eJk1bOS%&=lcdly~Eu|71q{v_lP{&Spvt!k;mY^SMh18nraCR$x6**xQ zEsXoLMv8LPYcz=`4qPH8)gSCW>3aHy>b$t%$EXr;C*%2ƒn11F&)+RK_z2EQgi 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%0A?DVB*DsVJi&ocpa!u>6&!4&M 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 From 138f222ced27fbe56fcf181cd9aec4805f11f67a Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 22 Mar 2023 02:29:28 +0900 Subject: [PATCH 071/369] Revision 0.26.0 --- src/typebox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typebox.ts b/src/typebox.ts index 6c8796938..bff6d38ab 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1882,7 +1882,7 @@ export class StandardTypeBuilder extends TypeBuilder { return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? this.Never(options) : TypeClone.Clone(left, options)) as any } } - /** `[Standard]` Extracts from left left any type that is assignable to the right */ + /** `[Standard]` Extracts from the left type any type that is assignable to the right */ public Extract(left: L, right: R, options: SchemaOptions = {}): TExtract { if (TypeGuard.TUnion(left)) { const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) !== TypeExtendsResult.False) From 4379e312a499832408081333804112e503d36936 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 23 Mar 2023 00:16:24 +0900 Subject: [PATCH 072/369] Revision 0.26.0 --- src/typebox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typebox.ts b/src/typebox.ts index bff6d38ab..92e57a117 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1733,7 +1733,7 @@ export namespace TypeClone { if (IsObject(value)) return Object(value) return value } - /** Clones a type. This function will omit non-self referential identifiers on the cloned type. */ + /** Clones a type. */ export function Clone(schema: T, options: SchemaOptions): T { return { ...Visit(schema), ...options } } From 5df81a9c8a2361329612d29b3a349f9eb27f3914 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 23 Mar 2023 00:21:08 +0900 Subject: [PATCH 073/369] Revision 0.26.0 --- example/trpc/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/trpc/readme.md b/example/trpc/readme.md index e321ae253..5a2b36158 100644 --- a/example/trpc/readme.md +++ b/example/trpc/readme.md @@ -81,7 +81,7 @@ import { TRPCError } from '@trpc/server' export function RpcType(schema: T) { return (value: unknown) => { if (Value.Check(schema, value)) return value - const { path, message } = check.Errors(value).First()! + const { path, message } = Value.Errors(schema, value).First()! throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) } } From 528903882f266836bbb427fa487980a837524e6f Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 23 Mar 2023 04:05:18 +0900 Subject: [PATCH 074/369] Remove Unused Object Array Check (#348) --- package.json | 2 +- src/compiler/compiler.ts | 1 - src/value/check.ts | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8043b31b7..079fe206a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.0", + "version": "0.26.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index f773b012c..30a15e5f0 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -253,7 +253,6 @@ export namespace TypeCompiler { function* Object(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { yield IsObjectCheck(value) - if (!TypeSystem.AllowArrayObjects) yield `!Array.isArray(${value})` if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const schemaKeys = globalThis.Object.getOwnPropertyNames(schema.properties) diff --git a/src/value/check.ts b/src/value/check.ts index 15c06bbe8..59db8e22d 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -220,10 +220,10 @@ export namespace ValueCheck { if (!IsObject(value)) { return false } - if (IsNumber(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { return false } - if (IsNumber(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { return false } const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) From 1cbb5c60cc9fbdd601b35a3873a2c43cd8338b29 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 23 Mar 2023 17:14:45 +0900 Subject: [PATCH 075/369] Composite Evaluate (#349) --- changelog/0.26.2.md | 38 +++++++++++++++ package.json | 2 +- readme.md | 25 +++++----- src/typebox.ts | 73 +++++++++++++++++++--------- test/runtime/compiler/composite.ts | 41 ++++++++++------ test/runtime/schema/composite.ts | 42 ++++++++-------- test/runtime/type/guard/composite.ts | 27 ++++++++++ test/runtime/type/guard/index.ts | 1 + test/static/composite.ts | 63 ++++++++++++++++++++++-- 9 files changed, 233 insertions(+), 79 deletions(-) create mode 100644 changelog/0.26.2.md create mode 100644 test/runtime/type/guard/composite.ts diff --git a/changelog/0.26.2.md b/changelog/0.26.2.md new file mode 100644 index 000000000..4e046809f --- /dev/null +++ b/changelog/0.26.2.md @@ -0,0 +1,38 @@ +## [0.26.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.26.2) + +Updates: + +- [331](https://github.com/sinclairzx81/typebox/pull/349) Revert 0.25.0 Intersect logic for Composite + +Notes: + +This PR reverts the logic on Type.Composite back to 0.25.0 Type.Intersect due to excessive type instantiation issues. On 0.26.0, Type.Composite attempted to take a union for overlapping properties, however due to the sophistication required to type map the unions for overlapping properties, this has resulted in type instantiation problems for some users upgrading to 0.26.0. + +As such, 0.26.2 reverts back to the 0.25.0 interpretation, but applies type mappings more inline with TS's interpretation of an overlapping varying property types. In the examples below, the type `C` is the evaluated type for Type.Composite. Note that TS will not union for overlapping properties and instead evaluate `never`. The 0.26.2 implementation falls inline with this evaluation. + +```typescript +{ // evaluation case 1: non-varying + type T = { a: number } & { a: number } + + type C = {[K in keyof T]: T[K] } // type C = { a: number } +} + +{ // evaluation case 2: varying + type T = { a: number } & { a: string } + + type C = {[K in keyof T]: T[K] } // type C = { a: never } +} + +{ // evaluation case 3: single optional + type T = { a?: number } & { a: number } + + type C = {[K in keyof T]: T[K] } // type C = { a: number } +} + +{ // evaluation case 4: all optional + type T = { a?: number } & { a?: number } + + type C = {[K in keyof T]: T[K] } // type C = { a: number | undefined } +} +``` +Note: the Type.Composite is intended to be a temporary type which can be replaced with a more general `Type.Mapped` in future revisions of TypeBox. As the infrastructure to support mapped types does not exist, users can use Type.Composite to partially replicate mapped type evaluation for composited object types only. \ No newline at end of file diff --git a/package.json b/package.json index 079fe206a..9160c0b84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.1", + "version": "0.26.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 2d199c156..b26946d92 100644 --- a/readme.md +++ b/readme.md @@ -229,13 +229,13 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Null() │ type T = null │ const T = { │ -│ │ │ type: 'null' │ +│ │ │ type: 'null' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Literal(42) │ type T = 42 │ const T = { │ -│ │ │ const: 42, │ -│ │ │ type: 'number' │ +│ │ │ const: 42, │ +│ │ │ type: 'number' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -326,19 +326,16 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Composite([ │ type T = { │ const T = { │ -│ Type.Object({ │ x: number | string │ type: 'object', │ -│ x: Type.Number() │ y: number │ properties: { │ -│ }), │ } │ x: { │ -│ Type.Object({ │ │ anyOf: [ │ -│ x: Type.String() │ │ { type: 'number' }, │ -│ y: Type.Number() │ │ { type: 'string' } │ -│ }) │ │ ] │ -│ ]) │ │ }, │ -│ │ │ y: { │ +│ Type.Object({ │ x: number │ type: 'object', │ +│ x: Type.Number() │ y: number │ required: ['x', 'y'], │ +│ }), │ } │ properties: { │ +│ Type.Object({ │ │ x: { │ +│ y: Type.Number() │ │ type: 'number' │ +│ }) │ │ }, │ +│ ]) │ │ y: { │ │ │ │ type: 'number' │ │ │ │ } │ -│ │ │ }, │ -│ │ │ required: ['x', 'y'] │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ diff --git a/src/typebox.ts b/src/typebox.ts index 92e57a117..de0334021 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -168,21 +168,14 @@ export type TInstanceType> = T['retur // -------------------------------------------------------------------------- // TComposite // -------------------------------------------------------------------------- -export type TCompositeUnion = Ensure> -// note: we need to take the left and right as the accumulator is assigned for multiple composite property sets with missing properties. -export type TCompositeUnionLeft = { - [K in keyof T['properties']]: K extends keyof Acc ? TCompositeUnion : T['properties'][K] +export type TCompositeEvaluateArray = { [K in keyof T]: T[K] extends TSchema ? Static : never } +export type TCompositeArray = { [K in keyof T]: T[K] extends TObject ? P : {} } +export type TCompositeProperties = Evaluate : I extends object ? I : {}> +export interface TComposite extends TObject { + [Hint]: 'Composite' // Hint is required to differentiate between object | intersection on pick, omit, required, partial and keyof + static: Evaluate>> + properties: TCompositeProperties> } -export type TCompositeUnionRight = { - [K in keyof Acc]: K extends keyof T['properties'] ? TCompositeUnion : Acc[K] -} -export type TCompositeUnionObject = Evaluate & TCompositeUnionRight> -// prettier-ignore -export type TCompositeProperties = - T extends [...infer L, infer R] ? TCompositeProperties, TCompositeUnionObject, Acc>> : - T extends [] ? Acc : - never -export type TComposite = Ensure>> // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- @@ -275,6 +268,7 @@ export interface IntersectOptions extends SchemaOptions { unevaluatedProperties?: TUnevaluatedProperties } export type TIntersectStatic = TupleToIntersect<{ [K in keyof T]: Static, P> }> + export interface TIntersect extends TSchema, IntersectOptions { [Kind]: 'Intersect' type?: 'object' @@ -292,6 +286,7 @@ export type TKeyOfTuple = { : never // prettier-ignore export type TKeyOf = ( + T extends TComposite ? TKeyOfTuple : T extends TIntersect ? TKeyOfTuple : T extends TUnion ? TKeyOfTuple : T extends TObject ? TKeyOfTuple : @@ -312,7 +307,7 @@ export interface TLiteral extends TSche export interface TNever extends TSchema { [Kind]: 'Never' static: never - allOf: [{ type: 'boolean'; const: false }, { type: 'boolean'; const: true }] + not: {} } // -------------------------------------------------------------------------- // TNot @@ -381,6 +376,7 @@ export type TOmitArray = Assert<{ [K2 export type TOmitProperties = Evaluate, TProperties>> // prettier-ignore export type TOmit = + T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -392,6 +388,7 @@ export type TParameters = TTuple // -------------------------------------------------------------------------- // TPartial // -------------------------------------------------------------------------- +export type TPartialObjectArray = Assert<{ [K in keyof T]: TPartial> }, TObject[]> export type TPartialArray = Assert<{ [K in keyof T]: TPartial> }, TSchema[]> // prettier-ignore export type TPartialProperties = Evaluate = Evaluate }, TProperties>> // prettier-ignore -export type TPartial = +export type TPartial = + T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -420,11 +418,11 @@ export type TPickProperties = }): never // prettier-ignore export type TPick = + T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : T - // -------------------------------------------------------------------------- // TPromise // -------------------------------------------------------------------------- @@ -489,6 +487,7 @@ export type TRequiredProperties = Evaluate> // prettier-ignore export type TRequired = + T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -1845,15 +1844,41 @@ export class StandardTypeBuilder extends TypeBuilder { public Boolean(options: SchemaOptions = {}): TBoolean { return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) } - /** `[Standard]` Creates a Composite object type that will union any overlapping properties of the given object array */ - public Composite(schemas: [...T], options?: ObjectOptions): TComposite { - const properties = {} as TProperties - for (const object of schemas) { - for (const [key, property] of globalThis.Object.entries(object.properties)) { - properties[key] = key in properties ? this.Union([properties[key], property]) : TypeClone.Clone(property, {}) + /** `[Standard]` Creates a Composite object type. */ + public Composite(objects: [...T], options?: ObjectOptions): TComposite { + const isOptionalAll = (objects: TObject[], key: string) => objects.every((object) => !(key in object.properties) || IsOptional(object.properties[key])) + const IsOptional = (schema: TSchema) => TypeGuard.TOptional(schema) || TypeGuard.TReadonlyOptional(schema) + const [required, optional] = [new Set(), new Set()] + for (const object of objects) { + for (const key of globalThis.Object.getOwnPropertyNames(object.properties)) { + if (isOptionalAll(objects, key)) optional.add(key) + } + } + for (const object of objects) { + for (const key of globalThis.Object.getOwnPropertyNames(object.properties)) { + if (!optional.has(key)) required.add(key) } } - return this.Object(properties, options) as TComposite + const properties = {} as Record + for (const object of objects) { + for (const [key, schema] of Object.entries(object.properties)) { + const property = TypeClone.Clone(schema, {}) + if (!optional.has(key)) delete property[Modifier] + if (key in properties) { + const left = TypeExtends.Extends(properties[key], property) !== TypeExtendsResult.False + const right = TypeExtends.Extends(property, properties[key]) !== TypeExtendsResult.False + if (!left && !right) properties[key] = Type.Never() + if (!left && right) properties[key] = property + } else { + properties[key] = property + } + } + } + if (required.size > 0) { + return this.Create({ ...options, [Kind]: 'Object', [Hint]: 'Composite', type: 'object', properties, required: [...required] }) + } else { + return this.Create({ ...options, [Kind]: 'Object', [Hint]: 'Composite', type: 'object', properties }) + } } /** `[Standard]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { diff --git a/test/runtime/compiler/composite.ts b/test/runtime/compiler/composite.ts index 5635e45fa..48466589d 100644 --- a/test/runtime/compiler/composite.ts +++ b/test/runtime/compiler/composite.ts @@ -1,4 +1,4 @@ -import { Type, Static } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Composite', () => { @@ -13,33 +13,45 @@ describe('type/compiler/Composite', () => { const B = Type.Partial(Type.Object({ b: Type.Number() })) const P = Type.Composite([A, B], { additionalProperties: false }) Ok(P, { a: 1, b: 2 }) + Ok(P, { a: 1 }) + Ok(P, { b: 1 }) + Ok(P, {}) + Fail(P, { a: 1, b: 2, c: 3 }) + Fail(P, { c: 1 }) }) it('Should compose with overlapping same type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Number() }) const P = Type.Composite([A, B]) Ok(P, { a: 1 }) - Fail(P, { a: 'hello' }) - Fail(P, {}) + Fail(P, { a: '1' }) }) - it('Should compose with overlapping varying type', () => { + it('Should not compose with overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const T = Type.Composite([A, B]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) + Fail(T, { a: 1 }) + Fail(T, { a: 'hello' }) Fail(T, {}) }) it('Should compose with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Object({ c: Type.Boolean() }) + const D = Type.Object({ d: Type.Null() }) + const T = Type.Composite([A, B, C, D]) + Ok(T, { a: 1, b: 'hello', c: true, d: null }) + }) + it('Should not compose with deeply nest overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const C = Type.Object({ a: Type.Boolean() }) const D = Type.Object({ a: Type.Null() }) const T = Type.Composite([A, B, C, D]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Ok(T, { a: false }) - Ok(T, { a: null }) + Fail(T, { a: 1 }) + Fail(T, { a: 'hello' }) + Fail(T, { a: false }) + Fail(T, { a: null }) Fail(T, { a: [] }) Fail(T, {}) }) @@ -61,13 +73,12 @@ describe('type/compiler/Composite', () => { Ok(P, { x: 1, y: 1 }) Fail(P, { x: 1, y: 1, z: 1 }) }) - it('Should compose nested object properties', () => { const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) - const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const B = Type.Object({ y: Type.Object({ x: Type.String() }) }) const T = Type.Composite([A, B]) - Ok(T, { x: { x: 1 } }) - Ok(T, { x: { x: 'hello' } }) - Fail(T, { x: { x: false } }) + Ok(T, { x: { x: 1 }, y: { x: '' } }) + Fail(T, { x: { x: '1' }, y: { x: '' } }) + Fail(T, { x: { x: 1 }, y: { x: 1 } }) }) }) diff --git a/test/runtime/schema/composite.ts b/test/runtime/schema/composite.ts index cca8cac71..9e19cb26c 100644 --- a/test/runtime/schema/composite.ts +++ b/test/runtime/schema/composite.ts @@ -9,7 +9,6 @@ describe('type/schema/Composite', () => { const T = Type.Composite([A, B], { additionalProperties: false }) Ok(T, { a: 'hello', b: 42 }) }) - it('Should compose with partial', () => { const A = Type.Partial(Type.Object({ a: Type.Number() })) const B = Type.Partial(Type.Object({ b: Type.Number() })) @@ -18,41 +17,45 @@ describe('type/schema/Composite', () => { Ok(P, { a: 1 }) Ok(P, { b: 1 }) Ok(P, {}) + Fail(P, { a: 1, b: 2, c: 3 }) Fail(P, { c: 1 }) }) - it('Should compose with overlapping same type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Number() }) const P = Type.Composite([A, B]) Ok(P, { a: 1 }) - Fail(P, { a: 'hello' }) - Fail(P, {}) + Fail(P, { a: '1' }) }) - - it('Should compose with overlapping varying type', () => { + it('Should not compose with overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const T = Type.Composite([A, B]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) + Fail(T, { a: 1 }) + Fail(T, { a: 'hello' }) Fail(T, {}) }) - it('Should compose with deeply nest overlapping varying type', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ b: Type.String() }) + const C = Type.Object({ c: Type.Boolean() }) + const D = Type.Object({ d: Type.Null() }) + const T = Type.Composite([A, B, C, D]) + Ok(T, { a: 1, b: 'hello', c: true, d: null }) + }) + it('Should not compose with deeply nest overlapping varying type', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.String() }) const C = Type.Object({ a: Type.Boolean() }) const D = Type.Object({ a: Type.Null() }) const T = Type.Composite([A, B, C, D]) - Ok(T, { a: 1 }) - Ok(T, { a: 'hello' }) - Ok(T, { a: false }) - Ok(T, { a: null }) + Fail(T, { a: 1 }) + Fail(T, { a: 'hello' }) + Fail(T, { a: false }) + Fail(T, { a: null }) Fail(T, { a: [] }) Fail(T, {}) }) - it('Should pick from composited type', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) @@ -62,7 +65,6 @@ describe('type/schema/Composite', () => { Ok(P, { x: 1, y: 1 }) Fail(P, { x: 1, y: 1, z: 1 }) }) - it('Should omit from composited type', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) @@ -72,16 +74,14 @@ describe('type/schema/Composite', () => { Ok(P, { x: 1, y: 1 }) Fail(P, { x: 1, y: 1, z: 1 }) }) - it('Should compose nested object properties', () => { const A = Type.Object({ x: Type.Object({ x: Type.Number() }) }) - const B = Type.Object({ x: Type.Object({ x: Type.String() }) }) + const B = Type.Object({ y: Type.Object({ x: Type.String() }) }) const T = Type.Composite([A, B]) - Ok(T, { x: { x: 1 } }) - Ok(T, { x: { x: 'hello' } }) - Fail(T, { x: { x: false } }) + Ok(T, { x: { x: 1 }, y: { x: '' } }) + Fail(T, { x: { x: '1' }, y: { x: '' } }) + Fail(T, { x: { x: 1 }, y: { x: 1 } }) }) - // todo: move to composition / type guard spec it('Should compose and produce the same schema', () => { const T = Type.Object({ diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts new file mode 100644 index 000000000..2ace75790 --- /dev/null +++ b/test/runtime/type/guard/composite.ts @@ -0,0 +1,27 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TComposite', () => { + it('Should guard for distinct properties', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + Assert.equal(TypeGuard.TNumber(T.properties.x), true) + Assert.equal(TypeGuard.TNumber(T.properties.y), true) + }) + it('Should guard for overlapping properties', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) + Assert.equal(TypeGuard.TNumber(T.properties.x), true) + }) + it('Should guard for illogical intersection and yield never', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) + Assert.equal(TypeGuard.TNever(T.properties.x), true) + }) + it('Should not produce optional property if all properties are not optional', () => { + const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) + Assert.equal(TypeGuard.TOptional(T.properties.x), false) + }) + it('Should produce optional property if all properties are optional', () => { + const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) })]) + Assert.equal(TypeGuard.TOptional(T.properties.x), true) + }) +}) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index d66cdf0f5..991f457ee 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -2,6 +2,7 @@ import './any' import './array' import './bigint' import './boolean' +import './composite' import './constructor' import './date' import './exclude' diff --git a/test/static/composite.ts b/test/static/composite.ts index be5596d2d..fcbc5c4a9 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -1,19 +1,74 @@ import { Expect } from './assert' -import { Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' +// Overlapping - Non Varying { const A = Type.Object({ + A: Type.Number(), + }) + const B = Type.Object({ + A: Type.Number(), + }) + const T = Type.Composite([A, B]) + + Expect(T).ToInfer<{ + A: number + }>() +} +// Overlapping - Varying +{ + const A = Type.Object({ + A: Type.Number(), + }) + const B = Type.Object({ A: Type.String(), - B: Type.String(), + }) + const T = Type.Composite([A, B]) + + Expect(T).ToInfer<{ + A: never + }>() +} +// Overlapping Single Optional +{ + const A = Type.Object({ + A: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + A: Type.Number(), + }) + const T = Type.Composite([A, B]) + + Expect(T).ToInfer<{ + A: number + }>() +} +// Overlapping All Optional +{ + const A = Type.Object({ + A: Type.Optional(Type.Number()), }) const B = Type.Object({ + A: Type.Optional(Type.Number()), + }) + const T = Type.Composite([A, B]) + + Expect(T).ToInfer<{ + A: number | undefined + }>() +} +// Distinct Properties +{ + const A = Type.Object({ A: Type.Number(), + }) + const B = Type.Object({ B: Type.Number(), }) const T = Type.Composite([A, B]) Expect(T).ToInfer<{ - A: string | number - B: string | number + A: number + B: number }>() } From e16fceb02f6871709c5bdde455a57942848eb8ab Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 23 Mar 2023 18:01:24 +0900 Subject: [PATCH 076/369] Composite Evaluate --- readme.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/readme.md b/readme.md index b26946d92..494f001e9 100644 --- a/readme.md +++ b/readme.md @@ -325,15 +325,15 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Composite([ │ type T = { │ const T = { │ +│ const T = Type.Composite([ │ type I = { │ const T = { │ │ Type.Object({ │ x: number │ type: 'object', │ -│ x: Type.Number() │ y: number │ required: ['x', 'y'], │ -│ }), │ } │ properties: { │ -│ Type.Object({ │ │ x: { │ +│ x: Type.Number() │ } & { │ required: ['x', 'y'], │ +│ }), │ y: number │ properties: { │ +│ Type.Object({ │ } │ x: { │ │ y: Type.Number() │ │ type: 'number' │ -│ }) │ │ }, │ -│ ]) │ │ y: { │ -│ │ │ type: 'number' │ +│ }) │ type T = { │ }, │ +│ ]) │ [K in keyof I]: I[K] │ y: { │ +│ │ } │ type: 'number' │ │ │ │ } │ │ │ │ } │ │ │ │ } │ From 4ae9125b5503ec2e804334f7bcebe0d5d8d179de Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 24 Mar 2023 07:18:57 +0900 Subject: [PATCH 077/369] Composite Evaluate --- changelog/0.26.2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/0.26.2.md b/changelog/0.26.2.md index 4e046809f..052f4bd7d 100644 --- a/changelog/0.26.2.md +++ b/changelog/0.26.2.md @@ -32,7 +32,7 @@ As such, 0.26.2 reverts back to the 0.25.0 interpretation, but applies type mapp { // evaluation case 4: all optional type T = { a?: number } & { a?: number } - type C = {[K in keyof T]: T[K] } // type C = { a: number | undefined } + type C = {[K in keyof T]: T[K] } // type C = { a?: number | undefined } } ``` -Note: the Type.Composite is intended to be a temporary type which can be replaced with a more general `Type.Mapped` in future revisions of TypeBox. As the infrastructure to support mapped types does not exist, users can use Type.Composite to partially replicate mapped type evaluation for composited object types only. \ No newline at end of file +Note: the Type.Composite is intended to be a temporary type which can be replaced with a more general `Type.Mapped` in future revisions of TypeBox. As the infrastructure to support mapped types does not exist, users can use Type.Composite to partially replicate mapped type evaluation for composited object types only. From 39afc1fbaa318469c893c1e21fb645adccd62d3f Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 24 Mar 2023 13:55:07 +0900 Subject: [PATCH 078/369] Exact Optional Property Types (#352) --- package.json | 2 +- src/compiler/compiler.ts | 31 +++++++++++++------------ src/errors/errors.ts | 33 ++++++++++++++++----------- src/system/system.ts | 11 +++++++++ src/value/check.ts | 32 +++++++++++++++----------- test/runtime/compiler/object.ts | 6 ++--- test/runtime/system/system.ts | 36 ++++++++++++++++++++++++++++++ test/runtime/value/check/object.ts | 4 ++-- 8 files changed, 109 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 9160c0b84..dfeab5f08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.2", + "version": "0.26.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 30a15e5f0..aee1fa24e 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -144,10 +144,10 @@ export namespace TypeCompiler { return typeof value === 'string' } // ------------------------------------------------------------------- - // Overrides + // Polices // ------------------------------------------------------------------- - function IsNumberCheck(value: string): string { - return !TypeSystem.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` + function IsExactOptionalProperty(value: string, key: string, expression: string) { + return TypeSystem.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${value}.${key} !== undefined ? ${expression} : true)` } function IsObjectCheck(value: string): string { return !TypeSystem.AllowArrayObjects ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)` @@ -157,6 +157,9 @@ export namespace TypeCompiler { ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` : `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` } + function IsNumberCheck(value: string): string { + return !TypeSystem.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` + } function IsVoidCheck(value: string): string { return TypeSystem.AllowVoidNull ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` } @@ -255,29 +258,29 @@ export namespace TypeCompiler { yield IsObjectCheck(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` - const schemaKeys = globalThis.Object.getOwnPropertyNames(schema.properties) - for (const schemaKey of schemaKeys) { - const memberExpression = MemberExpression.Encode(value, schemaKey) - const property = schema.properties[schemaKey] - if (schema.required && schema.required.includes(schemaKey)) { + const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) + for (const knownKey of knownKeys) { + const memberExpression = MemberExpression.Encode(value, knownKey) + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { yield* Visit(property, references, memberExpression) - if (Types.ExtendsUndefined.Check(property)) yield `('${schemaKey}' in ${value})` + if (Types.ExtendsUndefined.Check(property)) yield `('${knownKey}' in ${value})` } else { const expression = CreateExpression(property, references, memberExpression) - yield `('${schemaKey}' in ${value} ? ${expression} : true)` + yield IsExactOptionalProperty(value, knownKey, expression) } } if (schema.additionalProperties === false) { - if (schema.required && schema.required.length === schemaKeys.length) { - yield `Object.getOwnPropertyNames(${value}).length === ${schemaKeys.length}` + if (schema.required && schema.required.length === knownKeys.length) { + yield `Object.getOwnPropertyNames(${value}).length === ${knownKeys.length}` } else { - const keys = `[${schemaKeys.map((key) => `'${key}'`).join(', ')}]` + const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]` yield `Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key))` } } if (typeof schema.additionalProperties === 'object') { const expression = CreateExpression(schema.additionalProperties, references, 'value[key]') - const keys = `[${schemaKeys.map((key) => `'${key}'`).join(', ')}]` + const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]` yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } } diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 780b6d7f7..51476a648 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -135,6 +135,24 @@ export namespace ValueErrors { // ---------------------------------------------------------------------- // Guards // ---------------------------------------------------------------------- + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + function IsInteger(value: unknown): value is number { + return globalThis.Number.isInteger(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsDefined(value: unknown): value is T { + return value !== undefined + } + // ---------------------------------------------------------------------- + // Policies + // ---------------------------------------------------------------------- + function IsExactOptionalProperty(value: Record, key: string) { + return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + } function IsObject(value: unknown): value is Record { const result = typeof value === 'object' && value !== null return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) @@ -142,26 +160,15 @@ export namespace ValueErrors { function IsRecordObject(value: unknown): value is Record { return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } function IsNumber(value: unknown): value is number { const result = typeof value === 'number' return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) } - function IsInteger(value: unknown): value is number { - return globalThis.Number.isInteger(value) - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } function IsVoid(value: unknown): value is void { const result = value === undefined return TypeSystem.AllowVoidNull ? result || value === null : result } - function IsDefined(value: unknown): value is T { - return value !== undefined - } + // ---------------------------------------------------------------------- // Types // ---------------------------------------------------------------------- @@ -351,7 +358,7 @@ export namespace ValueErrors { yield { type: ValueErrorType.ObjectRequiredProperties, schema: property, path: `${path}/${knownKey}`, value: undefined, message: `Expected required property` } } } else { - if (knownKey in value) { + if (IsExactOptionalProperty(value, knownKey)) { yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) } } diff --git a/src/system/system.ts b/src/system/system.ts index 598f6cc09..a7dda5804 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -38,14 +38,24 @@ export class TypeSystemDuplicateFormat extends Error { super(`Duplicate string format '${kind}' detected`) } } + /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { + // ------------------------------------------------------------------------ + // Assertion Policies + // ------------------------------------------------------------------------ + /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ + export let ExactOptionalPropertyTypes: boolean = false /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ export let AllowArrayObjects: boolean = false /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ export let AllowNaN: boolean = false /** Sets whether `null` should validate for void types. The default is `false` */ export let AllowVoidNull: boolean = false + + // ------------------------------------------------------------------------ + // String Formats and Types + // ------------------------------------------------------------------------ /** Creates a new type */ export function Type(kind: string, check: (options: Options, value: unknown) => boolean) { if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) @@ -58,6 +68,7 @@ export namespace TypeSystem { Types.FormatRegistry.Set(format, check) return format } + // ------------------------------------------------------------------------ // Deprecated // ------------------------------------------------------------------------ diff --git a/src/value/check.ts b/src/value/check.ts index 59db8e22d..c30e4064e 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -47,6 +47,24 @@ export namespace ValueCheck { // ---------------------------------------------------------------------- // Guards // ---------------------------------------------------------------------- + function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + function IsInteger(value: unknown): value is number { + return globalThis.Number.isInteger(value) + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + function IsDefined(value: unknown): value is T { + return value !== undefined + } + // ---------------------------------------------------------------------- + // Policies + // ---------------------------------------------------------------------- + function IsExactOptionalProperty(value: Record, key: string) { + return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + } function IsObject(value: unknown): value is Record { const result = typeof value === 'object' && value !== null return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) @@ -54,26 +72,14 @@ export namespace ValueCheck { function IsRecordObject(value: unknown): value is Record { return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } function IsNumber(value: unknown): value is number { const result = typeof value === 'number' return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) } - function IsInteger(value: unknown): value is number { - return globalThis.Number.isInteger(value) - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } function IsVoid(value: unknown): value is void { const result = value === undefined return TypeSystem.AllowVoidNull ? result || value === null : result } - function IsDefined(value: unknown): value is T { - return value !== undefined - } // ---------------------------------------------------------------------- // Types // ---------------------------------------------------------------------- @@ -237,7 +243,7 @@ export namespace ValueCheck { return knownKey in value } } else { - if (knownKey in value && !Visit(property, references, value[knownKey])) { + if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { return false } } diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 699b533b0..e6faf0837 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -227,16 +227,16 @@ describe('type/compiler/Object', () => { Ok(T, { x: undefined }) Ok(T, {}) }) - it('Should not check undefined for optional property of number', () => { + it('Should check undefined for optional property of number', () => { const T = Type.Object({ x: Type.Optional(Type.Number()) }) Ok(T, { x: 1 }) + Ok(T, { x: undefined }) // allowed by default Ok(T, {}) - Fail(T, { x: undefined }) }) it('Should check undefined for optional property of undefined', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) Fail(T, { x: 1 }) - Ok(T, {}) Ok(T, { x: undefined }) + Ok(T, {}) }) }) diff --git a/test/runtime/system/system.ts b/test/runtime/system/system.ts index 5c41eda2c..dc7c51df5 100644 --- a/test/runtime/system/system.ts +++ b/test/runtime/system/system.ts @@ -3,6 +3,42 @@ import { Assert } from '../assert/index' import { TypeSystem } from '@sinclair/typebox/system' import { Type } from '@sinclair/typebox' +describe('system/TypeSystem/ExactOptionalPropertyTypes', () => { + before(() => { + TypeSystem.ExactOptionalPropertyTypes = true + }) + after(() => { + TypeSystem.ExactOptionalPropertyTypes = false + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should not validate optional number', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + }) + Ok(T, {}) + Ok(T, { x: 1 }) + Fail(T, { x: undefined }) + }) + it('Should not validate undefined', () => { + const T = Type.Object({ + x: Type.Optional(Type.Undefined()), + }) + Ok(T, {}) + Fail(T, { x: 1 }) + Ok(T, { x: undefined }) + }) + it('Should validate optional number | undefined', () => { + const T = Type.Object({ + x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])), + }) + Ok(T, {}) + Ok(T, { x: 1 }) + Ok(T, { x: undefined }) + }) +}) + describe('system/TypeSystem/AllowNaN', () => { before(() => { TypeSystem.AllowNaN = true diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index 6b672b938..6a06962de 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -212,11 +212,11 @@ describe('value/check/Object', () => { Assert.equal(Value.Check(T, { x: undefined }), true) Assert.equal(Value.Check(T, {}), true) }) - it('Should not check undefined for optional property of number', () => { + it('Should check undefined for optional property of number', () => { const T = Type.Object({ x: Type.Optional(Type.Number()) }) Assert.equal(Value.Check(T, { x: 1 }), true) + Assert.equal(Value.Check(T, { x: undefined }), true) // allowed by default Assert.equal(Value.Check(T, {}), true) - Assert.equal(Value.Check(T, { x: undefined }), false) }) it('Should check undefined for optional property of undefined', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) From 89564f12c4804a8cc44657459add16afced5f022 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 26 Mar 2023 17:17:19 +0900 Subject: [PATCH 079/369] Enhanced Member Expression Check (#354) --- package.json | 2 +- src/compiler/compiler.ts | 58 ++++++++++++++++----------------- test/runtime/compiler/object.ts | 4 +++ 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index dfeab5f08..a3f37c50a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.3", + "version": "0.26.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index aee1fa24e..9f546db63 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -60,14 +60,38 @@ namespace Character { export function DollarSign(code: number) { return code === 36 } - export function Underscore(code: number) { + export function IsUnderscore(code: number) { return code === 95 } - export function Numeric(code: number) { + export function IsAlpha(code: number) { + return (code >= 64 && code <= 90) || (code >= 97 && code <= 122) + } + export function IsNumeric(code: number) { return code >= 48 && code <= 57 } - export function Alpha(code: number) { - return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) +} +// ------------------------------------------------------------------- +// MemberExpression +// ------------------------------------------------------------------- +namespace MemberExpression { + function IsFirstCharacterNumeric(value: string) { + if (value.length === 0) return false + return Character.IsNumeric(value.charCodeAt(0)) + } + function IsAccessor(value: string) { + if (IsFirstCharacterNumeric(value)) return false + for (let i = 0; i < value.length; i++) { + const code = value.charCodeAt(i) + const check = Character.IsAlpha(code) || Character.IsNumeric(code) || Character.DollarSign(code) || Character.IsUnderscore(code) + if (!check) return false + } + return true + } + function EscapeHyphen(key: string) { + return key.replace(/'/g, "\\'") + } + export function Encode(object: string, key: string) { + return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']` } } // ------------------------------------------------------------------- @@ -78,7 +102,7 @@ namespace Identifier { const buffer: string[] = [] for (let i = 0; i < $id.length; i++) { const code = $id.charCodeAt(i) - if (Character.Numeric(code) || Character.Alpha(code)) { + if (Character.IsNumeric(code) || Character.IsAlpha(code)) { buffer.push($id.charAt(i)) } else { buffer.push(`_${code}_`) @@ -88,30 +112,6 @@ namespace Identifier { } } // ------------------------------------------------------------------- -// MemberExpression -// ------------------------------------------------------------------- -export namespace MemberExpression { - function Check(propertyName: string) { - if (propertyName.length === 0) return false - { - const code = propertyName.charCodeAt(0) - if (!(Character.DollarSign(code) || Character.Underscore(code) || Character.Alpha(code))) { - return false - } - } - for (let i = 1; i < propertyName.length; i++) { - const code = propertyName.charCodeAt(i) - if (!(Character.DollarSign(code) || Character.Underscore(code) || Character.Alpha(code) || Character.Numeric(code))) { - return false - } - } - return true - } - export function Encode(object: string, key: string) { - return !Check(key) ? `${object}['${key}']` : `${object}.${key}` - } -} -// ------------------------------------------------------------------- // TypeCompiler // ------------------------------------------------------------------- export class TypeCompilerUnknownTypeError extends Error { diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index e6faf0837..0d83ab294 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -132,12 +132,16 @@ describe('type/compiler/Object', () => { '0-leading': Type.Literal(2), '$-leading': Type.Literal(3), '!@#$%^&*(': Type.Literal(4), + 'node-mirror:release': Type.Literal(5), // issue: 353 + "a'a": Type.Literal(6), }) Ok(T, { 'with-hyphen': 1, '0-leading': 2, '$-leading': 3, '!@#$%^&*(': 4, + 'node-mirror:release': 5, + "a'a": 6, }) }) it('Should validate schema additional properties of string', () => { From ff2a63c7349facff5403d83809148f8ae3599565 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 27 Mar 2023 12:26:44 +0900 Subject: [PATCH 080/369] Enhanced Member Expression Check (#357) --- package.json | 2 +- src/compiler/compiler.ts | 2 +- test/runtime/compiler/object.ts | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a3f37c50a..ccd4b86f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.4", + "version": "0.26.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9f546db63..62ef4f679 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -147,7 +147,7 @@ export namespace TypeCompiler { // Polices // ------------------------------------------------------------------- function IsExactOptionalProperty(value: string, key: string, expression: string) { - return TypeSystem.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${value}.${key} !== undefined ? ${expression} : true)` + return TypeSystem.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)` } function IsObjectCheck(value: string): string { return !TypeSystem.AllowArrayObjects ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)` diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 0d83ab294..b45f9a8b6 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -132,16 +132,20 @@ describe('type/compiler/Object', () => { '0-leading': Type.Literal(2), '$-leading': Type.Literal(3), '!@#$%^&*(': Type.Literal(4), - 'node-mirror:release': Type.Literal(5), // issue: 353 - "a'a": Type.Literal(6), + 'node-mirror:release:0': Type.Literal(5), // issue: 353 + 'node-mirror:release:1': Type.Optional(Type.Literal(6)), // issue: 356 + 'node-mirror:release:2': Type.Union([Type.Literal(7), Type.Undefined()]), // key known + "a'a": Type.Literal(8), }) Ok(T, { 'with-hyphen': 1, '0-leading': 2, '$-leading': 3, '!@#$%^&*(': 4, - 'node-mirror:release': 5, - "a'a": 6, + 'node-mirror:release:0': 5, + 'node-mirror:release:1': 6, + 'node-mirror:release:2': 7, + "a'a": 8, }) }) it('Should validate schema additional properties of string', () => { From e9f63d897b824a083a66a354eda33a1bf7738eca Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 27 Mar 2023 13:48:36 +0900 Subject: [PATCH 081/369] Inverse Assert for Compiler and Value Check Alignment (#359) --- test/runtime/compiler/validate.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index 070d6092b..b85a91c20 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -96,6 +96,9 @@ export function Ok(schema: T, data: unknown, references: any[ export function Fail(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) + if (result !== Value.Check(schema, references, data)) { + throw Error('Compiler and Value Check disparity') + } if (result === false) { const errors = [...Value.Errors(schema, references, data)] if (errors.length === 0) throw Error('expected at least 1 error') From e8b213c390d850e2a161b28f41586fde6882a37f Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 28 Mar 2023 23:50:49 +0900 Subject: [PATCH 082/369] Record and Union Conversion Logic (#361) --- package.json | 2 +- src/value/convert.ts | 16 ++++++++++++++-- test/runtime/value/convert/record.ts | 6 +++++- test/runtime/value/convert/union.ts | 19 ++++++++++++++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ccd4b86f1..c9a2ab8fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.5", + "version": "0.26.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert.ts b/src/value/convert.ts index 987ef038e..b40ab967b 100644 --- a/src/value/convert.ts +++ b/src/value/convert.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { ValueClone } from './clone' +import { ValueCheck } from './check' // ---------------------------------------------------------------------------------------------- // Errors @@ -129,7 +130,6 @@ export namespace ValueConvert { function TryConvertBigInt(value: unknown) { return IsStringNumeric(value) ? globalThis.BigInt(parseInt(value)) : IsNumber(value) ? globalThis.BigInt(value | 0) : IsValueFalse(value) ? 0 : IsValueTrue(value) ? 1 : value } - function TryConvertString(value: unknown) { return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value } @@ -229,7 +229,13 @@ export namespace ValueConvert { return value } function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { - return value + const propertyKey = globalThis.Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[propertyKey] + const result = {} as Record + for (const [propKey, propValue] of globalThis.Object.entries(value)) { + result[propKey] = Visit(property, references, propValue) + } + return result } function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { const index = references.findIndex((foreign) => foreign.$id === schema.$ref) @@ -261,6 +267,12 @@ export namespace ValueConvert { return TryConvertUndefined(value) } function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { + for (const subschema of schema.anyOf) { + const converted = Visit(subschema, references, value) + if (ValueCheck.Check(subschema, references, converted)) { + return converted + } + } return value } function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): unknown { diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts index bfeb7129c..641476f17 100644 --- a/test/runtime/value/convert/record.ts +++ b/test/runtime/value/convert/record.ts @@ -3,5 +3,9 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/convert/Record', () => { - it('Should convert', () => {}) + it('Should convert record value to numeric', () => { + const T = Type.Record(Type.String(), Type.Number()) + const V = Value.Convert(T, { x: '42', y: '24', z: 'hello' }) + Assert.deepEqual(V, { x: 42, y: '24', z: 'hello' }) + }) }) diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index 5b1f2f3a5..7fd75a17a 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -3,5 +3,22 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/convert/Union', () => { - it('Should convert', () => {}) + it('Should convert union variant', () => { + const T = Type.Object({ + x: Type.Union([Type.Number(), Type.Null()]), + }) + const V1 = Value.Convert(T, { x: '42' }) + const V2 = Value.Convert(T, { x: 'null' }) + const V3 = Value.Convert(T, { x: 'hello' }) + Assert.deepEqual(V1, { x: 42 }) + Assert.deepEqual(V2, { x: null }) + Assert.deepEqual(V3, { x: 'hello' }) + }) + it('Should convert first variant in ambiguous conversion', () => { + const T = Type.Object({ + x: Type.Union([Type.Boolean(), Type.Number()]), + }) + const V1 = Value.Convert(T, { x: '1' }) + Assert.deepEqual(V1, { x: true }) + }) }) From 069c95e2aa455b294550da9c67e1b780ce53b7e4 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 29 Mar 2023 13:18:30 +0900 Subject: [PATCH 083/369] Additional Record Constraints (#363) --- package.json | 2 +- src/compiler/compiler.ts | 3 ++- src/errors/errors.ts | 6 ++++++ src/value/check.ts | 6 ++++++ test/runtime/compiler/record.ts | 34 ++++++++++++++---------------- test/runtime/schema/record.ts | 34 ++++++++++++++---------------- test/runtime/value/check/record.ts | 24 ++++++++++++++------- 7 files changed, 63 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index c9a2ab8fb..836dbee68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.6", + "version": "0.26.7", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 62ef4f679..1d269f98a 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -287,9 +287,10 @@ export namespace TypeCompiler { function* Promise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } - function* Record(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { yield IsRecordCheck(value) + if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${keyPattern}/)`) yield `(Object.getOwnPropertyNames(${value}).every(key => ${local}.test(key)))` diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 51476a648..521343938 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -390,6 +390,12 @@ export namespace ValueErrors { if (!IsRecordObject(value)) { return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected record object` } } + if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + } + if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } + } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { diff --git a/src/value/check.ts b/src/value/check.ts index c30e4064e..bf3f42909 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -270,6 +270,12 @@ export namespace ValueCheck { if (!IsRecordObject(value)) { return false } + if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + return false + } + if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + return false + } const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index d12e65daa..428c35f92 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -6,24 +6,36 @@ describe('type/compiler/Record', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3 }) }) - it('Should validate when all property keys are strings', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) - + it('Should not validate when below minProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 }) + Ok(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3 }) + }) + it('Should not validate when above maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 }) + Ok(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }) + }) + it('Should not validate with illogical minProperties | maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 }) + Fail(T, { a: 1, b: 2, c: 3 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }) + }) it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) Ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) - it('Should not validate when specifying string union literals when additionalProperties is false', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) - it('Should validate for keyof records', () => { const T = Type.Object({ a: Type.String(), @@ -33,7 +45,6 @@ describe('type/compiler/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number()) Ok(R, { a: 1, b: 2, c: 3 }) }) - it('Should not validate for unknown key via keyof', () => { const T = Type.Object({ a: Type.String(), @@ -43,7 +54,6 @@ describe('type/compiler/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) - it('Should should validate when specifying regular expressions', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) @@ -53,7 +63,6 @@ describe('type/compiler/Record', () => { op_c: 3, }) }) - it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) @@ -63,21 +72,17 @@ describe('type/compiler/Record', () => { aop_c: 3, }) }) - // ------------------------------------------------------------ // Integer Keys // ------------------------------------------------------------ - it('Should validate when all property keys are integers', () => { const T = Type.Record(Type.Integer(), Type.Number()) Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) - it('Should validate when all property keys are integers, but one property is a string with varying type', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) - it('Should not validate if passing a leading zeros for integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { @@ -87,7 +92,6 @@ describe('type/compiler/Record', () => { '03': 4, }) }) - it('Should not validate if passing a signed integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { @@ -97,21 +101,17 @@ describe('type/compiler/Record', () => { '-3': 4, }) }) - // ------------------------------------------------------------ // Number Keys // ------------------------------------------------------------ - it('Should validate when all property keys are numbers', () => { const T = Type.Record(Type.Number(), Type.Number()) Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) - it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) - it('Should not validate if passing a leading zeros for numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { @@ -121,7 +121,6 @@ describe('type/compiler/Record', () => { '03': 4, }) }) - it('Should not validate if passing a signed numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { @@ -131,7 +130,6 @@ describe('type/compiler/Record', () => { '-3': 4, }) }) - it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index e9f2de88b..bacc80f42 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -6,24 +6,36 @@ describe('type/schema/Record', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3 }) }) - it('Should validate when all property keys are strings', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3, '0': 4 }) }) - + it('Should not validate when below minProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 }) + Ok(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3 }) + }) + it('Should not validate when above maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 }) + Ok(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }) + }) + it('Should not validate with illogical minProperties | maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 }) + Fail(T, { a: 1, b: 2, c: 3 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4 }) + Fail(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }) + }) it('Should validate when specifying string union literals when additionalProperties is true', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number()) Ok(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) - it('Should not validate when specifying string union literals when additionalProperties is false', () => { const K = Type.Union([Type.Literal('a'), Type.Literal('b'), Type.Literal('c')]) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { a: 1, b: 2, c: 3, d: 'hello' }) }) - it('Should validate for keyof records', () => { const T = Type.Object({ a: Type.String(), @@ -33,7 +45,6 @@ describe('type/schema/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number()) Ok(R, { a: 1, b: 2, c: 3 }) }) - it('Should not validate for unknown key via keyof', () => { const T = Type.Object({ a: Type.String(), @@ -43,7 +54,6 @@ describe('type/schema/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) - it('Should should validate when specifying regular expressions', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) @@ -53,7 +63,6 @@ describe('type/schema/Record', () => { op_c: 3, }) }) - it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) const T = Type.Record(K, Type.Number()) @@ -63,21 +72,17 @@ describe('type/schema/Record', () => { aop_c: 3, }) }) - // ------------------------------------------------------------ // Integer Keys // ------------------------------------------------------------ - it('Should validate when all property keys are integers', () => { const T = Type.Record(Type.Integer(), Type.Number()) Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) - it('Should validate when all property keys are integers, but one property is a string with varying type', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) - it('Should not validate if passing a leading zeros for integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { @@ -87,7 +92,6 @@ describe('type/schema/Record', () => { '03': 4, }) }) - it('Should not validate if passing a signed integers keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) Fail(T, { @@ -97,21 +101,17 @@ describe('type/schema/Record', () => { '-3': 4, }) }) - // ------------------------------------------------------------ // Number Keys // ------------------------------------------------------------ - it('Should validate when all property keys are numbers', () => { const T = Type.Record(Type.Number(), Type.Number()) Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) - it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) - it('Should not validate if passing a leading zeros for numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { @@ -121,7 +121,6 @@ describe('type/schema/Record', () => { '03': 4, }) }) - it('Should not validate if passing a signed numeric keys', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { @@ -131,7 +130,6 @@ describe('type/schema/Record', () => { '-3': 4, }) }) - it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { const T = Type.Record(Type.Number(), Type.Number()) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 3bea978c0..2df12220d 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -22,6 +22,22 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) + it('Should fail when below minProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 }) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + }) + it('Should fail when above maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 }) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + }) + it('Should fail with illogical minProperties | maxProperties', () => { + const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 }) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), false) + Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + }) it('Should fail record with Date', () => { const T = Type.Record(Type.String(), Type.String()) const result = Value.Check(T, new Date()) @@ -50,7 +66,6 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) - it('Should fail record with invalid property', () => { const T = Type.Record( Type.String(), @@ -70,7 +85,6 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) - it('Should pass record with optional property', () => { const T = Type.Record( Type.String(), @@ -89,7 +103,6 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) - it('Should pass record with optional property', () => { const T = Type.Record( Type.String(), @@ -108,11 +121,9 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) - // ------------------------------------------------- // Number Key // ------------------------------------------------- - it('Should pass record with number key', () => { const T = Type.Record(Type.Number(), Type.String()) const value = { @@ -134,11 +145,9 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) - // ------------------------------------------------- // Integer Key // ------------------------------------------------- - it('Should pass record with integer key', () => { const T = Type.Record(Type.Integer(), Type.String()) const value = { @@ -149,7 +158,6 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, true) }) - it('Should not pass record with invalid integer key', () => { const T = Type.Record(Type.Integer(), Type.String()) const value = { From 8550c9d4b7692f8b989766509265634f136ff076 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Mar 2023 14:21:32 +0900 Subject: [PATCH 084/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 494f001e9..1aeec6580 100644 --- a/readme.md +++ b/readme.md @@ -447,7 +447,7 @@ The following table lists the Standard TypeBox types. These types are fully comp │ const A = Type.Object({ │ type A = { │ const T = { │ │ x: Type.Number(), │ x: number, │ $ref: 'A' │ │ y: Type.Number() │ y: number │ } │ -│ }, { $id: 'T' }) | } │ │ +│ }, { $id: 'A' }) | } │ │ │ │ │ │ │ const T = Type.Ref(A) │ type T = A │ │ │ │ │ │ From c5d5018bbb45953444816047447f310eccb9a5e5 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Mar 2023 14:23:07 +0900 Subject: [PATCH 085/369] Documentation --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index 1aeec6580..265d408e4 100644 --- a/readme.md +++ b/readme.md @@ -444,12 +444,12 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const A = Type.Object({ │ type A = { │ const T = { │ -│ x: Type.Number(), │ x: number, │ $ref: 'A' │ +│ const T = Type.Object({ │ type T = { │ const R = { │ +│ x: Type.Number(), │ x: number, │ $ref: 'T' │ │ y: Type.Number() │ y: number │ } │ -│ }, { $id: 'A' }) | } │ │ +│ }, { $id: 'T' }) | } │ │ │ │ │ │ -│ const T = Type.Ref(A) │ type T = A │ │ +│ const R = Type.Ref(T) │ type R = T │ │ │ │ │ │ │ │ │ │ │ │ │ │ From 31d0e8b1e1f3275bb15f1239cbeb849f4459c837 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Mar 2023 17:01:08 +0900 Subject: [PATCH 086/369] Support Recursive Type Code Generation --- codegen/typescript-to-typebox.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/codegen/typescript-to-typebox.ts b/codegen/typescript-to-typebox.ts index a42f11750..f2167c538 100644 --- a/codegen/typescript-to-typebox.ts +++ b/codegen/typescript-to-typebox.ts @@ -35,6 +35,12 @@ import * as ts from 'typescript' /** Generates TypeBox types from TypeScript code */ export namespace TypeScriptToTypeBox { + function isRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { + function find(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { + return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.getText()) || node.getChildren().some((node) => find(decl, node)) + } + return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => find(decl, node)) : decl.members.some((node) => find(decl, node)) + } function isReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined } @@ -110,6 +116,7 @@ export namespace TypeScriptToTypeBox { } function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true + const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => node.types.map((node) => node.getText())) : [] if (node.typeParameters) { useGenerics = true const exports = isExport(node) ? 'export ' : '' @@ -118,13 +125,17 @@ export namespace TypeScriptToTypeBox { const names = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') const members = node.members.map((member) => Collect(member)).join(',\n') const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Object({\n${members}\n})` + const rawTypeExpression = isRecursiveType(node) ? `Type.Recursive(${node.name.getText()} => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` + const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` } else { const exports = isExport(node) ? 'export ' : '' const members = node.members.map((member) => Collect(member)).join(',\n') const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const typeDeclaration = `${exports}const ${node.name.getText()} = Type.Object({\n${members}\n})` + const rawTypeExpression = isRecursiveType(node) ? `Type.Recursive(${node.name.getText()} => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` + const typeDeclaration = `${exports}const ${node.name.getText()} = ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` } } @@ -138,13 +149,15 @@ export namespace TypeScriptToTypeBox { const names = node.typeParameters.map((param) => Collect(param)).join(', ') const type = Collect(node.type) const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${type}` + const typeDeclaration = isRecursiveType(node) + ? `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Recursive(${node.name.getText()} => ${type})` + : `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${type}` yield `${staticDeclaration}\n${typeDeclaration}` } else { const exports = isExport(node) ? 'export ' : '' const type = Collect(node.type) const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const typeDeclaration = `${exports}const ${node.name.getText()} = ${type}` + const typeDeclaration = isRecursiveType(node) ? `${exports}const ${node.name.getText()} = Type.Recursive(${node.name.getText()} => ${type})` : `${exports}const ${node.name.getText()} = ${type}` yield `${staticDeclaration}\n${typeDeclaration}` } } From d79e53fd6f7f17840f88c87989ad263ba7c8ef52 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Mar 2023 18:22:14 +0900 Subject: [PATCH 087/369] Support Recursive Type Code Generation --- codegen/typescript-to-typebox.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/codegen/typescript-to-typebox.ts b/codegen/typescript-to-typebox.ts index f2167c538..2fbba9181 100644 --- a/codegen/typescript-to-typebox.ts +++ b/codegen/typescript-to-typebox.ts @@ -116,7 +116,8 @@ export namespace TypeScriptToTypeBox { } function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true - const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => node.types.map((node) => node.getText())) : [] + const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => Collect(node)) : [] + console.log('123', heritage) if (node.typeParameters) { useGenerics = true const exports = isExport(node) ? 'export ' : '' @@ -161,6 +162,17 @@ export namespace TypeScriptToTypeBox { yield `${staticDeclaration}\n${typeDeclaration}` } } + function* HeritageClause(node: ts.HeritageClause): IterableIterator { + const types = node.types.map((node) => Collect(node)) + if (types.length === 1) return yield types[0] + yield `Type.Intersect([${types.join(', ')}])` + } + function* ExpressionWithTypeArguments(node: ts.ExpressionWithTypeArguments): IterableIterator { + const name = Collect(node.expression) + const typeArguments = node.typeArguments === undefined ? [] : node.typeArguments.map((node) => Collect(node)) + // todo: default type argument (resolve `= number` from `type Foo`) + return yield typeArguments.length === 0 ? `${name}` : `${name}(${typeArguments.join(', ')})` + } function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { yield node.name.getText() } @@ -283,6 +295,10 @@ export namespace TypeScriptToTypeBox { return yield* UnionTypeNode(node) } else if (ts.isTypeOperatorNode(node)) { return yield* TypeOperatorNode(node) + } else if (ts.isHeritageClause(node)) { + return yield* HeritageClause(node) + } else if (ts.isExpressionWithTypeArguments(node)) { + return yield* ExpressionWithTypeArguments(node) } else if (ts.isTypeParameterDeclaration(node)) { return yield* TypeParameterDeclaration(node) } else if (ts.isParenthesizedTypeNode(node)) { @@ -295,6 +311,8 @@ export namespace TypeScriptToTypeBox { return yield* ClassDeclaration(node) } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) + } else if (ts.isIdentifier(node)) { + return yield node.getText() } else if (node.kind === ts.SyntaxKind.ExportKeyword) { return yield `export` } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { From b9db76267915eafb9db9b7f46d8aee465ae70cff Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 29 Mar 2023 18:23:20 +0900 Subject: [PATCH 088/369] Support Recursive Type Code Generation --- codegen/typescript-to-typebox.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/codegen/typescript-to-typebox.ts b/codegen/typescript-to-typebox.ts index 2fbba9181..5320aa418 100644 --- a/codegen/typescript-to-typebox.ts +++ b/codegen/typescript-to-typebox.ts @@ -117,7 +117,6 @@ export namespace TypeScriptToTypeBox { function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => Collect(node)) : [] - console.log('123', heritage) if (node.typeParameters) { useGenerics = true const exports = isExport(node) ? 'export ' : '' From e4d7378f890b65df9ebd0a7a0801424f06274be4 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 30 Mar 2023 03:10:59 +0900 Subject: [PATCH 089/369] TypeGuard for Raw TUnsafe (#365) --- package.json | 2 +- src/typebox.ts | 9 +++++++++ test/runtime/compiler/partial.ts | 19 +++++++++++++++++- test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/unsafe.ts | 32 +++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test/runtime/type/guard/unsafe.ts diff --git a/package.json b/package.json index 836dbee68..aa248455b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.7", + "version": "0.26.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index de0334021..9887da23e 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1112,6 +1112,14 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } + /** Returns true if the given schema is a raw TUnsafe */ + export function TUnsafe(schema: unknown): schema is TUnsafe { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'Unsafe' + ) + } /** Returns true if the given schema is TVoid */ export function TVoid(schema: unknown): schema is TVoid { // prettier-ignore @@ -1165,6 +1173,7 @@ export namespace TypeGuard { TUnion(schema) || TUint8Array(schema) || TUnknown(schema) || + TUnsafe(schema) || TVoid(schema) || (TKind(schema) && TypeRegistry.Has(schema[Kind] as any))) ) diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 6f11fb90b..e295aa6b9 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -1,4 +1,5 @@ -import { Type, Modifier } from '@sinclair/typebox' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type, Kind, Modifier } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -49,4 +50,20 @@ describe('type/compiler/Partial', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) + it('Should support partial properties of raw TUnsafe', () => { + // https://github.com/sinclairzx81/typebox/issues/364 + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) + strictEqual(T.required, undefined) + }) + it('Should not support partial properties of unknown TUnsafe', () => { + // https://github.com/sinclairzx81/typebox/issues/364 + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) + strictEqual(T.required![0], 'x') + }) + it('Should support partial properties of custom TUnsafe', () => { + // https://github.com/sinclairzx81/typebox/issues/364 + const U = TypeSystem.Type('CustomPartialType', () => true) + const T = Type.Partial(Type.Object({ x: U() })) + strictEqual(T.required, undefined) + }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 991f457ee..28cc9f5c8 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -26,4 +26,5 @@ import './uint8array' import './undefined' import './union' import './unknown' +import './unsafe' import './void' diff --git a/test/runtime/type/guard/unsafe.ts b/test/runtime/type/guard/unsafe.ts new file mode 100644 index 000000000..8cb296441 --- /dev/null +++ b/test/runtime/type/guard/unsafe.ts @@ -0,0 +1,32 @@ +import { Kind, TypeGuard, TypeRegistry } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TUnsafe', () => { + it('Should guard raw TUnsafe', () => { + const T = Type.Unsafe({ x: 1 }) + const R = TypeGuard.TUnsafe(T) + Assert.equal(R, true) + }) + it('Should guard raw TUnsafe as TSchema', () => { + const T = Type.Unsafe({ x: 1 }) + const R = TypeGuard.TSchema(T) + Assert.equal(R, true) + }) + it('Should guard override TUnsafe as TSchema when registered', () => { + TypeRegistry.Set('type/guard/TUnsafe/Type1', () => true) + const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type1' }) + const R = TypeGuard.TSchema(T) + Assert.equal(R, true) + }) + it('Should not guard TUnsafe with unregistered kind', () => { + const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type2' }) + const R = TypeGuard.TUnsafe(T) + Assert.equal(R, false) + }) + it('Should not guard for TString', () => { + const T = Type.String() + const R = TypeGuard.TUnsafe(T) + Assert.equal(R, false) + }) +}) From 53f7d55ecab20267661e6fe3621b6014baa04bcd Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 30 Mar 2023 05:22:22 +0900 Subject: [PATCH 090/369] Support Recursive Type Generation --- codegen/typescript-to-typebox.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codegen/typescript-to-typebox.ts b/codegen/typescript-to-typebox.ts index 5320aa418..ea3eead7e 100644 --- a/codegen/typescript-to-typebox.ts +++ b/codegen/typescript-to-typebox.ts @@ -318,6 +318,8 @@ export namespace TypeScriptToTypeBox { return yield `Type.KeyOf()` } else if (node.kind === ts.SyntaxKind.NumberKeyword) { return yield `Type.Number()` + } else if (node.kind === ts.SyntaxKind.BigIntKeyword) { + return yield `Type.BigInt()` } else if (node.kind === ts.SyntaxKind.StringKeyword) { return yield `Type.String()` } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { From a123d05487ba005fca8062d5e54d0fdf0d4aace4 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 2 Apr 2023 23:55:44 +0900 Subject: [PATCH 091/369] Legacy Intersect Implementation --- example/legacy/index.ts | 29 ++++++++++++++++ example/legacy/intersect.ts | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 example/legacy/index.ts create mode 100644 example/legacy/intersect.ts diff --git a/example/legacy/index.ts b/example/legacy/index.ts new file mode 100644 index 000000000..79f17933b --- /dev/null +++ b/example/legacy/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/legacy + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './intersect' \ No newline at end of file diff --git a/example/legacy/intersect.ts b/example/legacy/intersect.ts new file mode 100644 index 000000000..d2fbd9545 --- /dev/null +++ b/example/legacy/intersect.ts @@ -0,0 +1,66 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/legacy + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TObject, ObjectOptions, Modifier, Kind, TSchema, Static } from '@sinclair/typebox' + +export type IntersectLegacyReduce = T extends [infer A, ...infer B] ? IntersectLegacyReduce : I extends object ? I : {} +export type IntersectLegacyEvaluate = { [K in keyof T]: T[K] extends TSchema ? Static : never } +export type IntersectLegacyProperties = { + [K in keyof T]: T[K] extends TObject ? P : {} +} +export interface TIntersectLegacy extends TObject { + static: IntersectLegacyReduce> + properties: IntersectLegacyReduce> +} + +/** `Legacy` Creates a legacy intersect type. */ +export function IntersectLegacy(objects: [...T], options: ObjectOptions = {}): TIntersectLegacy { + const isOptional = (schema: TSchema) => (schema[Modifier] && schema[Modifier] === 'Optional') || schema[Modifier] === 'ReadonlyOptional' + const [required, optional] = [new Set(), new Set()] + for (const object of objects) { + for (const [key, schema] of Object.entries(object.properties)) { + if (isOptional(schema)) optional.add(key) + } + } + for (const object of objects) { + for (const key of Object.keys(object.properties)) { + if (!optional.has(key)) required.add(key) + } + } + const properties = {} as Record + for (const object of objects) { + for (const [key, schema] of Object.entries(object.properties)) { + properties[key] = properties[key] === undefined ? schema : { [Kind]: 'Union', anyOf: [properties[key], { ...schema }] } + } + } + if (required.size > 0) { + return { ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] } as any + } else { + return { ...options, [Kind]: 'Object', type: 'object', properties } as any + } +} From efb0cd425fb2e948203a943ca5ce20971d829955 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 4 Apr 2023 06:04:33 +0900 Subject: [PATCH 092/369] Enhanced Compiler Code Generation --- codegen/tsconfig.json | 4 - {codegen => example/codegen}/formatter.ts | 35 +++-- {codegen => example/codegen}/index.ts | 0 example/codegen/tsconfig.json | 4 + .../codegen}/typebox-to-typescript.ts | 0 .../codegen}/typebox-to-zod.ts | 0 .../codegen}/typescript-to-json-schema.ts | 145 +++++++----------- .../codegen}/typescript-to-typebox.ts | 82 ++++++---- hammer.mjs | 2 +- tsconfig.json | 3 - 10 files changed, 139 insertions(+), 136 deletions(-) delete mode 100644 codegen/tsconfig.json rename {codegen => example/codegen}/formatter.ts (62%) rename {codegen => example/codegen}/index.ts (100%) create mode 100644 example/codegen/tsconfig.json rename {codegen => example/codegen}/typebox-to-typescript.ts (100%) rename {codegen => example/codegen}/typebox-to-zod.ts (100%) rename {codegen => example/codegen}/typescript-to-json-schema.ts (79%) rename {codegen => example/codegen}/typescript-to-typebox.ts (83%) diff --git a/codegen/tsconfig.json b/codegen/tsconfig.json deleted file mode 100644 index 9f4a7e200..000000000 --- a/codegen/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../tsconfig.json", - "files": ["index.ts"] -} diff --git a/codegen/formatter.ts b/example/codegen/formatter.ts similarity index 62% rename from codegen/formatter.ts rename to example/codegen/formatter.ts index 68d8cd440..ffa6b58c2 100644 --- a/codegen/formatter.ts +++ b/example/codegen/formatter.ts @@ -27,19 +27,36 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export namespace Formatter { - // Code formatter + export function Includes(line: string, token: string) { + return line.includes(token) + } + function IntentBefore(line: string) { + if (Includes(line, '({') && Includes(line, '})')) return 0 + if (Includes(line, '([') && Includes(line, '])')) return 0 + if (Includes(line, '{') && Includes(line, '}')) return 0 + if (Includes(line, '])')) return -1 + if (Includes(line, '})')) return -1 + if (Includes(line, '}')) return -1 + if (Includes(line, ']')) return -1 + return 0 + } + function IndentAfter(line: string) { + if (Includes(line, '({') && Includes(line, '})')) return 0 + if (Includes(line, '([') && Includes(line, '])')) return 0 + if (Includes(line, '{') && Includes(line, '}')) return 0 + if (Includes(line, '([')) return 1 + if (Includes(line, '({')) return 1 + if (Includes(line, '{')) return 1 + if (Includes(line, '[')) return 1 + return 0 + } export function Format(input: string): string { - function count(line: string, opens: string[]) { - const codes = opens.map((open) => open.charCodeAt(0)) - // prettier-ignore - return line.split('').map((char) => char.charCodeAt(0)).reduce((acc, current) => codes.includes(current) ? acc + 1 : acc, 0) - } - let indent = 0 const output: string[] = [] + let indent = 0 for (const line of input.split('\n').map((n) => n.trim())) { - indent -= count(line, ['}']) + indent += IntentBefore(line) output.push(`${''.padStart(indent * 2, ' ')}${line}`) - indent += count(line, ['{']) + indent += IndentAfter(line) } return output.join('\n') } diff --git a/codegen/index.ts b/example/codegen/index.ts similarity index 100% rename from codegen/index.ts rename to example/codegen/index.ts diff --git a/example/codegen/tsconfig.json b/example/codegen/tsconfig.json new file mode 100644 index 000000000..6710ecd93 --- /dev/null +++ b/example/codegen/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "files": ["index.ts"] +} diff --git a/codegen/typebox-to-typescript.ts b/example/codegen/typebox-to-typescript.ts similarity index 100% rename from codegen/typebox-to-typescript.ts rename to example/codegen/typebox-to-typescript.ts diff --git a/codegen/typebox-to-zod.ts b/example/codegen/typebox-to-zod.ts similarity index 100% rename from codegen/typebox-to-zod.ts rename to example/codegen/typebox-to-zod.ts diff --git a/codegen/typescript-to-json-schema.ts b/example/codegen/typescript-to-json-schema.ts similarity index 79% rename from codegen/typescript-to-json-schema.ts rename to example/codegen/typescript-to-json-schema.ts index 2f198dbcb..3318fa63d 100644 --- a/codegen/typescript-to-json-schema.ts +++ b/example/codegen/typescript-to-json-schema.ts @@ -29,17 +29,6 @@ THE SOFTWARE. import { Formatter } from './formatter' import * as ts from 'typescript' -// -------------------------------------------------------------------------- -// Errors -// -------------------------------------------------------------------------- -export class TypeScriptToJsonSchemaNonExpressable extends Error { - constructor(type: string) { - super(`TypeScriptToJsonSchema: Cannot express syntax type '${type}'`) - } -} -// -------------------------------------------------------------------------- -// Transform -// -------------------------------------------------------------------------- export namespace TypeScriptToJsonSchema { function isReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined @@ -101,16 +90,16 @@ export namespace TypeScriptToJsonSchema { }` } function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { - throw new TypeScriptToJsonSchemaNonExpressable('TypeOperatorNode') + return yield UnknownType('TypeOperatorNode') } function* Parameter(node: ts.ParameterDeclaration): IterableIterator { yield Collect(node.type) } function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { - throw new TypeScriptToJsonSchemaNonExpressable('FunctionTypeNode') + return yield UnknownType('FunctionTypeNode') } function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { - throw new TypeScriptToJsonSchemaNonExpressable('ConstructorTypeNode') + return yield UnknownType('ConstructorTypeNode') } function* EnumMember(node: ts.EnumMember): IterableIterator { if (node.initializer) { @@ -136,20 +125,19 @@ export namespace TypeScriptToJsonSchema { if (node.typeParameters) { const exports = isExport(node) ? 'export ' : '' const members = node.members.map((member) => Collect(member)).join(',\n') - const required = node.members - .filter((member) => member.questionToken === undefined) - .map((member) => `'${member.name?.getText()}'`) - .join(',\n') - const parameters = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') - const definition = `${exports}const ${node.name.getText()} = (${parameters}) => { - type: 'object, + // prettier-ignore + const required = node.members.filter((member) => member.questionToken === undefined).map((member) => `'${member.name?.getText()}'`).join(',\n') + const constraints = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') + const definition = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ({ + type: 'object', properties: { ${members} }, required: [ ${required} ], - }` + })` yield `${definition}` } else { const exports = isExport(node) ? 'export ' : '' @@ -170,13 +158,13 @@ export namespace TypeScriptToJsonSchema { yield `${definition}` } } - function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { if (node.typeParameters) { const exports = isExport(node) ? 'export ' : '' - const parameters = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const constraints = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') + const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') const type = Collect(node.type) - const definition = `${exports}const ${node.name.getText()} = (${parameters}) => ${type}` + const definition = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => (${type})` yield `${definition}` } else { const exports = isExport(node) ? 'export ' : '' @@ -185,62 +173,53 @@ export namespace TypeScriptToJsonSchema { yield `${definition}` } } - function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { yield node.name.getText() } - function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { yield Collect(node.type) } - function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { - throw new TypeScriptToJsonSchemaNonExpressable('RestTypeNode') + return yield UnknownType('RestTypeNode') } - function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - throw new TypeScriptToJsonSchemaNonExpressable('ConditionalTypeNode') + return yield UnknownType('ConditionalTypeNode') } - function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { const name = node.typeName.getText() const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' if (name === 'Array') { - return yield `{ - type: 'array', - items: ${args} - }` + return yield `{ type: 'array', items: ${args} }` } else if (name === 'Record') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Record') + return yield UnknownType('Record') } else if (name === 'Partial') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Partial') + return yield UnknownType('Partial') } else if (name === 'Uint8Array') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Uint8Array') + return yield UnknownType('Uint8Array') } else if (name === 'Required') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Required') + return yield UnknownType('Required') } else if (name === 'Omit') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Omit') + return yield UnknownType('Omit') } else if (name === 'Pick') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Pick') + return yield UnknownType('Pick') } else if (name === 'Promise') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Promise') + return yield UnknownType('Promise') } else if (name === 'ReturnType') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:ReturnType') + return yield UnknownType('ReturnType') } else if (name === 'InstanceType') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:InstanceType') + return yield UnknownType('InstanceType') } else if (name === 'Parameters') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Parameters') + return yield UnknownType('Parameters') } else if (name === 'ConstructorParameters') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:ConstructorParameters') + return yield UnknownType('ConstructorParameters') } else if (name === 'Exclude') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Exclude') + return yield UnknownType('Exclude') } else if (name === 'Extract') { - throw new TypeScriptToJsonSchemaNonExpressable('TypeReferenceNode:Extract') + return yield UnknownType('Extract') } else { return yield `${name}${args}` } } - function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { const members = node.members.map((member) => Collect(member)).join(',\n') const required = node.members @@ -257,7 +236,6 @@ export namespace TypeScriptToJsonSchema { ] }` } - function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { const text = node.getText() if (text === 'null') @@ -268,23 +246,24 @@ export namespace TypeScriptToJsonSchema { const: ${node.getText()} }` } - function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { yield node.getText() } - function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { yield node.getText() } - function Collect(node: ts.Node | undefined): string { return `${[...Visit(node)].join('')}` } - function CollectNewLine(node: ts.Node | undefined): string { return [...Visit(node)].join('\n\n') } - + function UnknownType(name: string = 'Unknown'): string { + return `{ not: { /* unsupported type '${name}' */ } }` + } + function KnownType(type: string): string { + return ['{', `type: '${type}'`, '}'].join('\n') + } function* Visit(node: ts.Node | undefined): IterableIterator { if (node === undefined) return if (ts.isSourceFile(node)) { @@ -333,49 +312,32 @@ export namespace TypeScriptToJsonSchema { return yield* ClassDeclaration(node) } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) + } else if (ts.isIdentifier(node)) { + return yield node.getText() + } else if (node.kind === ts.SyntaxKind.ExportKeyword) { + return yield `export` } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { - throw new TypeScriptToJsonSchemaNonExpressable('KeyOfKeyword') + return yield UnknownType() } else if (node.kind === ts.SyntaxKind.NumberKeyword) { - return yield `{ - type: 'number' - }` + return yield KnownType('number') + } else if (node.kind === ts.SyntaxKind.BigIntKeyword) { + return yield UnknownType() } else if (node.kind === ts.SyntaxKind.StringKeyword) { - return yield `{ - type: 'string' - }` + return yield KnownType('string') } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { - return yield `{ - type: 'boolean' - }` + return yield KnownType('boolean') } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { - throw new TypeScriptToJsonSchemaNonExpressable('UndefinedKeyword') + return yield UnknownType('undefined') } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { - return yield `{ - - }` + return yield `{ }` } else if (node.kind === ts.SyntaxKind.AnyKeyword) { - return yield `{ - - }` + return yield `{ }` } else if (node.kind === ts.SyntaxKind.NeverKeyword) { - return yield `{ - anyOf: [ - { - type: 'boolean', - const: true - }, - { - type: 'boolean', - const: false - } - ] - }` + return yield `{ not: { } }` } else if (node.kind === ts.SyntaxKind.NullKeyword) { - return yield `{ - type: 'null' - }` + return yield KnownType('null') } else if (node.kind === ts.SyntaxKind.VoidKeyword) { - throw new TypeScriptToJsonSchemaNonExpressable('KeyOfKeyword') + return yield UnknownType('void') } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { return } else if (node.kind === ts.SyntaxKind.SyntaxList) { @@ -388,12 +350,11 @@ export namespace TypeScriptToJsonSchema { return yield node.getText() } } - - /** Generates TypeBox types from TypeScript interface and type definitions */ + /** Generates JsonSchema schematics from TypeScript interface and type definitions */ export function Generate(typescriptCode: string) { const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) const declarations = CollectNewLine(source) const types = Formatter.Format(declarations) return [types].join('\n') } -} +} \ No newline at end of file diff --git a/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts similarity index 83% rename from codegen/typescript-to-typebox.ts rename to example/codegen/typescript-to-typebox.ts index ea3eead7e..97f5b46d7 100644 --- a/codegen/typescript-to-typebox.ts +++ b/example/codegen/typescript-to-typebox.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ + import { Formatter } from './formatter' import * as ts from 'typescript' @@ -35,22 +36,30 @@ import * as ts from 'typescript' /** Generates TypeBox types from TypeScript code */ export namespace TypeScriptToTypeBox { - function isRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { - function find(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { - return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.getText()) || node.getChildren().some((node) => find(decl, node)) - } - return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => find(decl, node)) : decl.members.some((node) => find(decl, node)) + // tracked for recursive types and used to associate This type references + let recursiveDeclaration: ts.TypeAliasDeclaration | ts.InterfaceDeclaration | null = null + // tracked for injecting typebox import statements + let useImports = false + // tracked for injecting TSchema import statements + let useGenerics = false + // tracked for each generated type. + const typeNames = new Set() + function FindRecursiveParent(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { + return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.typeName.getText()) || node.getChildren().some((node) => FindRecursiveParent(decl, node)) + } + function IsRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { + return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => FindRecursiveParent(decl, node)) : decl.members.some((node) => FindRecursiveParent(decl, node)) } - function isReadonlyProperty(node: ts.PropertySignature): boolean { + function IsReadonlyProperty(node: ts.PropertySignature): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined } - function isOptionalProperty(node: ts.PropertySignature) { + function IsOptionalProperty(node: ts.PropertySignature) { return node.questionToken !== undefined } - function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { + function IsExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined } - function isNamespace(node: ts.ModuleDeclaration) { + function IsNamespace(node: ts.ModuleDeclaration) { return node.flags === ts.NodeFlags.Namespace } function* SourceFile(node: ts.SourceFile): IterableIterator { @@ -59,7 +68,7 @@ export namespace TypeScriptToTypeBox { } } function* PropertySignature(node: ts.PropertySignature): IterableIterator { - const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] + const [readonly, optional] = [IsReadonlyProperty(node), IsOptionalProperty(node)] const type = Collect(node.type) if (readonly && optional) { return yield `${node.name.getText()}: Type.ReadonlyOptional(${type})` @@ -107,59 +116,68 @@ export namespace TypeScriptToTypeBox { yield `Type.Constructor([${parameters}], ${returns})` } function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { - const exports = isExport(node) ? 'export ' : '' - const name = node.name.getText() + useImports = true + const exports = IsExport(node) ? 'export ' : '' const members = node.members.map((member) => member.getText()).join(', ') - const enumType = `${exports}enum ${name}Enum { ${members} }` - const type = `${exports}const ${name} = Type.Enum(${name}Enum)` + const enumType = `${exports}enum ${node.name.getText()}Enum { ${members} }` + const type = `${exports}const ${node.name.getText()} = Type.Enum(${node.name.getText()}Enum)` yield [enumType, '', type].join('\n') + typeNames.add(node.name.getText()) } function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true + const isRecursiveType = IsRecursiveType(node) + if (isRecursiveType) recursiveDeclaration = node const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => Collect(node)) : [] if (node.typeParameters) { useGenerics = true - const exports = isExport(node) ? 'export ' : '' + const exports = IsExport(node) ? 'export ' : '' const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') const names = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') const members = node.members.map((member) => Collect(member)).join(',\n') const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const rawTypeExpression = isRecursiveType(node) ? `Type.Recursive(${node.name.getText()} => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` } else { - const exports = isExport(node) ? 'export ' : '' + const exports = IsExport(node) ? 'export ' : '' const members = node.members.map((member) => Collect(member)).join(',\n') const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const rawTypeExpression = isRecursiveType(node) ? `Type.Recursive(${node.name.getText()} => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` const typeDeclaration = `${exports}const ${node.name.getText()} = ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` } + typeNames.add(node.name.getText()) + recursiveDeclaration = null } function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { useImports = true + const isRecursiveType = IsRecursiveType(node) + if (isRecursiveType) recursiveDeclaration = node if (node.typeParameters) { useGenerics = true - const exports = isExport(node) ? 'export ' : '' + const exports = IsExport(node) ? 'export ' : '' const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') const names = node.typeParameters.map((param) => Collect(param)).join(', ') const type = Collect(node.type) const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const typeDeclaration = isRecursiveType(node) - ? `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Recursive(${node.name.getText()} => ${type})` + const typeDeclaration = isRecursiveType + ? `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Recursive(This => ${type})` : `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${type}` yield `${staticDeclaration}\n${typeDeclaration}` } else { - const exports = isExport(node) ? 'export ' : '' + const exports = IsExport(node) ? 'export ' : '' const type = Collect(node.type) const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const typeDeclaration = isRecursiveType(node) ? `${exports}const ${node.name.getText()} = Type.Recursive(${node.name.getText()} => ${type})` : `${exports}const ${node.name.getText()} = ${type}` + const typeDeclaration = isRecursiveType ? `${exports}const ${node.name.getText()} = Type.Recursive(This => ${type})` : `${exports}const ${node.name.getText()} = ${type}` yield `${staticDeclaration}\n${typeDeclaration}` } + typeNames.add(node.name.getText()) + recursiveDeclaration = null } function* HeritageClause(node: ts.HeritageClause): IterableIterator { const types = node.types.map((node) => Collect(node)) @@ -190,6 +208,9 @@ export namespace TypeScriptToTypeBox { } function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { const name = node.typeName.getText() + if (name === 'T') { + console.log(ts.SyntaxKind[node.kind]) + } const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' if (name === 'Array') { return yield `Type.Array${args}` @@ -199,6 +220,8 @@ export namespace TypeScriptToTypeBox { return yield `Type.Partial${args}` } else if (name === 'Uint8Array') { return yield `Type.Uint8Array()` + } else if (name === 'Date') { + return yield `Type.Date()` } else if (name === 'Required') { return yield `Type.Required${args}` } else if (name === 'Omit') { @@ -219,6 +242,12 @@ export namespace TypeScriptToTypeBox { return yield `Type.Exclude${args}` } else if (name === 'Extract') { return yield `Type.Extract${args}` + } else if (recursiveDeclaration !== null && FindRecursiveParent(recursiveDeclaration, node)) { + return yield `This` + } else if (typeNames.has(name)) { + return yield `${name}${args}` + } else if (name in globalThis) { + return yield `Type.Never(/* Unsupported Type '${name}' */)` } else { return yield `${name}${args}` } @@ -233,8 +262,8 @@ export namespace TypeScriptToTypeBox { yield `Type.Literal(${node.getText()})` } function* ModuleDeclaration(node: ts.ModuleDeclaration): IterableIterator { - const export_specifier = isExport(node) ? 'export ' : '' - const module_specifier = isNamespace(node) ? 'namespace' : 'module' + const export_specifier = IsExport(node) ? 'export ' : '' + const module_specifier = IsNamespace(node) ? 'namespace' : 'module' yield `${export_specifier}${module_specifier} ${node.name.getText()} {` yield* Visit(node.body) yield `}` @@ -348,10 +377,9 @@ export namespace TypeScriptToTypeBox { return yield node.getText() } } - let useImports = false - let useGenerics = false /** Generates TypeBox types from TypeScript interface and type definitions */ export function Generate(typescriptCode: string) { + typeNames.clear() useImports = false useGenerics = false const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) diff --git a/hammer.mjs b/hammer.mjs index 0957385eb..8cf594e67 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -12,7 +12,7 @@ export async function clean() { // Format // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark codegen') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') } // ------------------------------------------------------------------------------- diff --git a/tsconfig.json b/tsconfig.json index 47541b26d..0932a6a80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,9 +7,6 @@ "declaration": true, "baseUrl": ".", "paths": { - "@sinclair/typebox/codegen": [ - "codegen/index.ts" - ], "@sinclair/typebox/compiler": [ "src/compiler/index.ts" ], From b7fa512e4823b99b0d6519296d92360c7e2f99c9 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 4 Apr 2023 08:58:20 +0900 Subject: [PATCH 093/369] String Template Literal Code Generation --- example/codegen/typescript-to-typebox.ts | 82 +++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/example/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts index 97f5b46d7..c8fc4732f 100644 --- a/example/codegen/typescript-to-typebox.ts +++ b/example/codegen/typescript-to-typebox.ts @@ -44,6 +44,9 @@ export namespace TypeScriptToTypeBox { let useGenerics = false // tracked for each generated type. const typeNames = new Set() + // ---------------------------------------------------------------------------------------------------- + // TypeScript AST Queries + // ---------------------------------------------------------------------------------------------------- function FindRecursiveParent(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.typeName.getText()) || node.getChildren().some((node) => FindRecursiveParent(decl, node)) } @@ -62,6 +65,76 @@ export namespace TypeScriptToTypeBox { function IsNamespace(node: ts.ModuleDeclaration) { return node.flags === ts.NodeFlags.Namespace } + // ---------------------------------------------------------------------------------------------------- + // String Template Literals: Entry TemplateLiteralTypeNode + // + // String Template Literals will evaluate as Regular Expressions. These require a distinct code path + // specifically for union, string, number and literal types embedded in the template literal. These + // embedded types would otherwise be expressed as TUnion, TString, TNumber and TLiteral respectively. + // ---------------------------------------------------------------------------------------------------- + function* TemplatedLiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { + function dequote(value: string) { + const match = value.match(/^(['"])(.*)\1$/); + return match ? match[2] : value; + } + function escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\\\$&') + } + yield escape(dequote(node.literal.getText())) + } + function * TemplatedUnionTypeNode(node: ts.UnionTypeNode) { + const tokens = node.types.map((type) => TemplateCollect(type)).join('|') + yield `(${tokens})` + } + function * TemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { + for(const inner of node.getChildren()) { + yield TemplateCollect(inner) + } + } + function * TemplateHead(node: ts.TemplateHead) { + yield node.text + } + function * TemplateTail(node: ts.TemplateTail) { + yield node.text + } + function * TemplateVisit(node: ts.Node | undefined): IterableIterator { + if(node === undefined) return + if (ts.isUnionTypeNode(node)) { + return yield* TemplatedUnionTypeNode(node) + } else if(ts.isTemplateLiteralTypeSpan(node)) { + return yield* TemplateLiteralTypeSpan(node) + } else if(ts.isTemplateHead(node)) { + return yield* TemplateHead(node) + } else if(ts.isTemplateTail(node)) { + return yield* TemplateTail(node) + } else if(ts.isLiteralTypeNode(node)) { + return yield* TemplatedLiteralTypeNode(node) + } else if (node.kind === ts.SyntaxKind.NumberKeyword) { + yield `(0|[1-9][0-9]*)` + } else if (node.kind === ts.SyntaxKind.StringKeyword) { + yield `(.*)` + } else if (node.kind === ts.SyntaxKind.SyntaxList) { + for (const child of node.getChildren()) { + yield* TemplateVisit(child) + } + return + } else { + console.log('Unhandled:', ts.SyntaxKind[node.kind]) + } + } + function TemplateCollect(node: ts.Node | undefined): string { + return `${[...TemplateVisit(node)].join('')}` + } + function * TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { + const buffer: string[] = [] + for(const inner of node.getChildren()) { + buffer.push(TemplateCollect(inner)) + } + yield `Type.String({ pattern: '^${buffer.join('')}\$' })` + } + // ---------------------------------------------------------------------------------------------------- + // TypeScript To TypeBox Type Transformation + // ---------------------------------------------------------------------------------------------------- function* SourceFile(node: ts.SourceFile): IterableIterator { for (const next of node.getChildren()) { yield* Visit(next) @@ -101,6 +174,9 @@ export namespace TypeScriptToTypeBox { const type = Collect(node.type) yield `Type.KeyOf(${type})` } + if(node.operator === ts.SyntaxKind.ReadonlyKeyword) { + yield Collect(node.type) + } } function* Parameter(node: ts.ParameterDeclaration): IterableIterator { yield Collect(node.type) @@ -307,8 +383,8 @@ export namespace TypeScriptToTypeBox { return yield* TypeReferenceNode(node) } else if (ts.isTypeLiteralNode(node)) { return yield* TypeLiteralNode(node) - } else if (ts.isLiteralTypeNode(node)) { - return yield* LiteralTypeNode(node) + } else if(ts.isLiteralTypeNode(node)) { + return yield *LiteralTypeNode(node) } else if (ts.isModuleDeclaration(node)) { return yield* ModuleDeclaration(node) } else if (ts.isModuleBlock(node)) { @@ -321,6 +397,8 @@ export namespace TypeScriptToTypeBox { return yield* IntersectionTypeNode(node) } else if (ts.isUnionTypeNode(node)) { return yield* UnionTypeNode(node) + } else if (ts.isTemplateLiteralTypeNode(node)) { + return yield * TemplateLiteralTypeNode(node) } else if (ts.isTypeOperatorNode(node)) { return yield* TypeOperatorNode(node) } else if (ts.isHeritageClause(node)) { From 749a6d04af2c57f86ae26428e8786882fee377d9 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 4 Apr 2023 11:34:14 +0900 Subject: [PATCH 094/369] Indexer, Readonly Tuple and String Literal Types --- example/codegen/typescript-to-typebox.ts | 230 ++++++++++++++--------- 1 file changed, 140 insertions(+), 90 deletions(-) diff --git a/example/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts index c8fc4732f..d2b9d0d6d 100644 --- a/example/codegen/typescript-to-typebox.ts +++ b/example/codegen/typescript-to-typebox.ts @@ -26,115 +26,145 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ - import { Formatter } from './formatter' import * as ts from 'typescript' -// -------------------------------------------------------------------------- -// Transform -// -------------------------------------------------------------------------- - -/** Generates TypeBox types from TypeScript code */ -export namespace TypeScriptToTypeBox { - // tracked for recursive types and used to associate This type references - let recursiveDeclaration: ts.TypeAliasDeclaration | ts.InterfaceDeclaration | null = null - // tracked for injecting typebox import statements - let useImports = false - // tracked for injecting TSchema import statements - let useGenerics = false - // tracked for each generated type. - const typeNames = new Set() - // ---------------------------------------------------------------------------------------------------- - // TypeScript AST Queries - // ---------------------------------------------------------------------------------------------------- - function FindRecursiveParent(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { - return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.typeName.getText()) || node.getChildren().some((node) => FindRecursiveParent(decl, node)) +// ------------------------------------------------------------------------- +// [StringTemplateLiteral] +// +// Specialized code path for evaluating string template literals as regular +// expressions. This is distinct from typical code paths which would +// otherwise yield types of TSchema inside the generated regular expression. +// ------------------------------------------------------------------------- +namespace StringTemplateLiteral { + function DereferenceNode(reference: ts.TypeReferenceNode): ts.Node | undefined { + function find(node: ts.Node): ts.Node | undefined { + if (node.getText() === reference.getText()) return node.parent + for (const inner of node.getChildren()) { + const result = find(inner) + if (result) return result + } + return undefined + } + return find(reference.getSourceFile()) } - function IsRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { - return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => FindRecursiveParent(decl, node)) : decl.members.some((node) => FindRecursiveParent(decl, node)) + function Dequote(value: string) { + const match = value.match(/^(['"`])(.*)\1$/) + return match ? match[2] : value } - function IsReadonlyProperty(node: ts.PropertySignature): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined + function Escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\\\$&') } - function IsOptionalProperty(node: ts.PropertySignature) { - return node.questionToken !== undefined + function* LiteralTypeNode(node: ts.LiteralTypeNode) { + yield Escape(Dequote(node.literal.getText())) } - function IsExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined + function* UnionTypeNode(node: ts.UnionTypeNode) { + const tokens = node.types.map((type) => Collect(type)).join('|') + yield `(${tokens})` } - function IsNamespace(node: ts.ModuleDeclaration) { - return node.flags === ts.NodeFlags.Namespace + function* StringLiteral(node: ts.StringLiteral) { + yield Dequote(node.getText()) } - // ---------------------------------------------------------------------------------------------------- - // String Template Literals: Entry TemplateLiteralTypeNode - // - // String Template Literals will evaluate as Regular Expressions. These require a distinct code path - // specifically for union, string, number and literal types embedded in the template literal. These - // embedded types would otherwise be expressed as TUnion, TString, TNumber and TLiteral respectively. - // ---------------------------------------------------------------------------------------------------- - function* TemplatedLiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { - function dequote(value: string) { - const match = value.match(/^(['"])(.*)\1$/); - return match ? match[2] : value; - } - function escape(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\\\$&') - } - yield escape(dequote(node.literal.getText())) + function* TypeReference(node: ts.TypeReferenceNode) { + const target = DereferenceNode(node) + if (target === undefined) return + yield Collect(target) } - function * TemplatedUnionTypeNode(node: ts.UnionTypeNode) { - const tokens = node.types.map((type) => TemplateCollect(type)).join('|') - yield `(${tokens})` + function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration) { + yield Collect(node.type) } - function * TemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { - for(const inner of node.getChildren()) { - yield TemplateCollect(inner) + function* LiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { + for (const inner of node.getChildren()) { + yield Collect(inner) } } - function * TemplateHead(node: ts.TemplateHead) { + function* TemplateHead(node: ts.TemplateHead) { + yield node.text + } + function* TemplateMiddle(node: ts.TemplateMiddle) { yield node.text } - function * TemplateTail(node: ts.TemplateTail) { + function* TemplateTail(node: ts.TemplateTail) { yield node.text } - function * TemplateVisit(node: ts.Node | undefined): IterableIterator { - if(node === undefined) return + function* Visit(node: ts.Node | undefined): IterableIterator { + if (node === undefined) return if (ts.isUnionTypeNode(node)) { - return yield* TemplatedUnionTypeNode(node) - } else if(ts.isTemplateLiteralTypeSpan(node)) { - return yield* TemplateLiteralTypeSpan(node) - } else if(ts.isTemplateHead(node)) { + return yield* UnionTypeNode(node) + } else if (ts.isStringLiteral(node)) { + return yield* StringLiteral(node) + } else if (ts.isTemplateLiteralTypeSpan(node)) { + return yield* LiteralTypeSpan(node) + } else if (ts.isTypeReferenceNode(node)) { + return yield* TypeReference(node) + } else if (ts.isTypeAliasDeclaration(node)) { + return yield* TypeAliasDeclaration(node) + } else if (ts.isTemplateHead(node)) { return yield* TemplateHead(node) - } else if(ts.isTemplateTail(node)) { + } else if (ts.isTemplateMiddle(node)) { + return yield* TemplateMiddle(node) + } else if (ts.isTemplateTail(node)) { return yield* TemplateTail(node) - } else if(ts.isLiteralTypeNode(node)) { - return yield* TemplatedLiteralTypeNode(node) - } else if (node.kind === ts.SyntaxKind.NumberKeyword) { + } else if (ts.isLiteralTypeNode(node)) { + return yield* LiteralTypeNode(node) + } else if (node.kind === ts.SyntaxKind.NumberKeyword) { yield `(0|[1-9][0-9]*)` - } else if (node.kind === ts.SyntaxKind.StringKeyword) { + } else if (node.kind === ts.SyntaxKind.StringKeyword) { yield `(.*)` } else if (node.kind === ts.SyntaxKind.SyntaxList) { for (const child of node.getChildren()) { - yield* TemplateVisit(child) + yield* Visit(child) } return } else { - console.log('Unhandled:', ts.SyntaxKind[node.kind]) + console.log('StringTemplateLiteral Unhandled:', ts.SyntaxKind[node.kind]) } } - function TemplateCollect(node: ts.Node | undefined): string { - return `${[...TemplateVisit(node)].join('')}` + function Collect(node: ts.Node | undefined): string { + return `${[...Visit(node)].join('')}` } - function * TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { + export function* TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { const buffer: string[] = [] - for(const inner of node.getChildren()) { - buffer.push(TemplateCollect(inner)) + for (const inner of node.getChildren()) { + buffer.push(Collect(inner)) } yield `Type.String({ pattern: '^${buffer.join('')}\$' })` } - // ---------------------------------------------------------------------------------------------------- - // TypeScript To TypeBox Type Transformation - // ---------------------------------------------------------------------------------------------------- +} +// ------------------------------------------------------------------------- +// [TypeScriptToTypeBox] +// +// The following are code generation paths for types of TSchema. +// ------------------------------------------------------------------------- +/** Generates TypeBox types from TypeScript code */ +export namespace TypeScriptToTypeBox { + // tracked for recursive types and used to associate This type references + let recursiveDeclaration: ts.TypeAliasDeclaration | ts.InterfaceDeclaration | null = null + // tracked for injecting typebox import statements + let useImports = false + // tracked for injecting TSchema import statements + let useGenerics = false + // tracked for each generated type. + const typeNames = new Set() + + function FindRecursiveParent(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { + return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.typeName.getText()) || node.getChildren().some((node) => FindRecursiveParent(decl, node)) + } + function IsRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { + return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => FindRecursiveParent(decl, node)) : decl.members.some((node) => FindRecursiveParent(decl, node)) + } + function IsReadonlyProperty(node: ts.PropertySignature): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined + } + function IsOptionalProperty(node: ts.PropertySignature) { + return node.questionToken !== undefined + } + function IsExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { + return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined + } + function IsNamespace(node: ts.ModuleDeclaration) { + return node.flags === ts.NodeFlags.Namespace + } function* SourceFile(node: ts.SourceFile): IterableIterator { for (const next of node.getChildren()) { yield* Visit(next) @@ -174,7 +204,7 @@ export namespace TypeScriptToTypeBox { const type = Collect(node.type) yield `Type.KeyOf(${type})` } - if(node.operator === ts.SyntaxKind.ReadonlyKeyword) { + if (node.operator === ts.SyntaxKind.ReadonlyKeyword) { yield Collect(node.type) } } @@ -200,6 +230,25 @@ export namespace TypeScriptToTypeBox { yield [enumType, '', type].join('\n') typeNames.add(node.name.getText()) } + // Collects members on behalf of InterfaceDeclaration and TypeLiteralNode. This function may yield an object + // with additionalProperties if we find an indexer in the members set. + function CollectObjectMembers(members: ts.NodeArray): string { + const properties = members.filter((member) => !ts.isIndexSignatureDeclaration(member)) + const indexers = members.filter((member) => ts.isIndexSignatureDeclaration(member)) + const propertyCollect = properties.map((property) => Collect(property)).join(',\n') + const indexer = indexers.length > 0 ? Collect(indexers[indexers.length - 1]) : '' + if (properties.length === 0 && indexer.length > 0) { + return `{},\n{\nadditionalProperties: ${indexer}\n }` + } else if (properties.length > 0 && indexer.length > 0) { + return `{\n${propertyCollect}\n},\n{\nadditionalProperties: ${indexer}\n }` + } else { + return `{\n${propertyCollect}\n}` + } + } + function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { + const members = CollectObjectMembers(node.members) + yield* `Type.Object(${members})` + } function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { useImports = true const isRecursiveType = IsRecursiveType(node) @@ -211,17 +260,17 @@ export namespace TypeScriptToTypeBox { const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') const names = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') - const members = node.members.map((member) => Collect(member)).join(',\n') + const members = CollectObjectMembers(node.members) const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object(${members}))` : `Type.Object(${members})` const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` } else { const exports = IsExport(node) ? 'export ' : '' - const members = node.members.map((member) => Collect(member)).join(',\n') + const members = CollectObjectMembers(node.members) const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object({\n${members}\n}))` : `Type.Object({\n${members}\n})` + const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object(${members}))` : `Type.Object(${members})` const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` const typeDeclaration = `${exports}const ${node.name.getText()} = ${typeExpression}` yield `${staticDeclaration}\n${typeDeclaration}` @@ -282,11 +331,14 @@ export namespace TypeScriptToTypeBox { const falseType = Collect(node.falseType) yield `Type.Extends(${checkType}, ${extendsType}, ${trueType}, ${falseType})` } + function* isIndexSignatureDeclaration(node: ts.IndexSignatureDeclaration) { + // note: we ignore the key and just return the type. this is a mismatch between + // object and record types. Address in TypeBox by unifying validation paths + // for objects and record types. + yield Collect(node.type) + } function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { const name = node.typeName.getText() - if (name === 'T') { - console.log(ts.SyntaxKind[node.kind]) - } const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' if (name === 'Array') { return yield `Type.Array${args}` @@ -328,10 +380,6 @@ export namespace TypeScriptToTypeBox { return yield `${name}${args}` } } - function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { - const members = node.members.map((member) => Collect(member)).join(',\n') - yield `Type.Object({\n${members}\n})` - } function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { const text = node.getText() if (text === 'null') return yield `Type.Null()` @@ -383,8 +431,8 @@ export namespace TypeScriptToTypeBox { return yield* TypeReferenceNode(node) } else if (ts.isTypeLiteralNode(node)) { return yield* TypeLiteralNode(node) - } else if(ts.isLiteralTypeNode(node)) { - return yield *LiteralTypeNode(node) + } else if (ts.isLiteralTypeNode(node)) { + return yield* LiteralTypeNode(node) } else if (ts.isModuleDeclaration(node)) { return yield* ModuleDeclaration(node) } else if (ts.isModuleBlock(node)) { @@ -398,7 +446,7 @@ export namespace TypeScriptToTypeBox { } else if (ts.isUnionTypeNode(node)) { return yield* UnionTypeNode(node) } else if (ts.isTemplateLiteralTypeNode(node)) { - return yield * TemplateLiteralTypeNode(node) + return yield* StringTemplateLiteral.TemplateLiteralTypeNode(node) } else if (ts.isTypeOperatorNode(node)) { return yield* TypeOperatorNode(node) } else if (ts.isHeritageClause(node)) { @@ -417,6 +465,8 @@ export namespace TypeScriptToTypeBox { return yield* ClassDeclaration(node) } else if (ts.isConditionalTypeNode(node)) { return yield* ConditionalTypeNode(node) + } else if (ts.isIndexSignatureDeclaration(node)) { + return yield* isIndexSignatureDeclaration(node) } else if (ts.isIdentifier(node)) { return yield node.getText() } else if (node.kind === ts.SyntaxKind.ExportKeyword) { From d4858dd815ba664bfac0983e8f2c8dc413c24756 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 9 Apr 2023 01:10:29 +0900 Subject: [PATCH 095/369] Revision 0.27.0 (#371) --- benchmark/measurement/module/cases.ts | 13 + changelog/0.27.0.md | 152 +++++ example/codegen/typebox-to-typescript.ts | 8 +- example/codegen/typebox-to-zod.ts | 6 +- example/codegen/typescript-to-typebox.ts | 321 +++------ example/index.ts | 2 +- hammer.mjs | 3 - package-lock.json | 18 +- package.json | 4 +- readme.md | 280 +++++--- src/compiler/compiler.ts | 19 +- src/errors/errors.ts | 31 +- src/typebox.ts | 498 +++++++++++--- src/value/cast.ts | 25 +- src/value/check.ts | 26 +- src/value/convert.ts | 23 +- src/value/create.ts | 44 +- test/runtime/compiler/index.ts | 1 + test/runtime/compiler/recursive.ts | 12 +- test/runtime/compiler/template-literal.ts | 186 ++++++ test/runtime/schema/index.ts | 1 + test/runtime/schema/recursive.ts | 12 +- test/runtime/schema/template-literal.ts | 186 ++++++ test/runtime/type/extends/any.ts | 2 - test/runtime/type/extends/array.ts | 51 -- test/runtime/type/extends/boolean.ts | 16 - test/runtime/type/extends/constructor.ts | 39 -- test/runtime/type/extends/date.ts | 16 - test/runtime/type/extends/function.ts | 40 -- test/runtime/type/extends/index.ts | 1 + test/runtime/type/extends/integer.ts | 17 - test/runtime/type/extends/literal.ts | 47 -- test/runtime/type/extends/null.ts | 18 - test/runtime/type/extends/number.ts | 17 - test/runtime/type/extends/promise.ts | 41 -- test/runtime/type/extends/record.ts | 33 - test/runtime/type/extends/string.ts | 20 - test/runtime/type/extends/template-literal.ts | 162 +++++ test/runtime/type/extends/uint8array.ts | 17 - test/runtime/type/extends/undefined.ts | 16 - test/runtime/type/extends/unknown.ts | 18 - test/runtime/type/extends/void.ts | 19 - test/runtime/type/guard/exclude.ts | 66 ++ test/runtime/type/guard/extract.ts | 72 +++ test/runtime/type/guard/index.ts | 3 +- test/runtime/type/guard/self.ts | 22 - test/runtime/type/guard/template-literal.ts | 47 ++ test/runtime/type/guard/this.ts | 22 + test/runtime/type/index.ts | 3 +- .../type/{normal => normalize}/exclude.ts | 0 .../type/{normal => normalize}/extract.ts | 0 .../type/{normal => normalize}/index.ts | 0 .../type/{normal => normalize}/intersect.ts | 0 .../type/{normal => normalize}/record.ts | 0 .../type/{normal => normalize}/union.ts | 0 test/runtime/type/registry/format.ts | 5 +- test/runtime/type/registry/index.ts | 1 + test/runtime/type/registry/type.ts | 22 + test/runtime/type/template/finite.ts | 61 ++ test/runtime/type/template/generate.ts | 199 ++++++ test/runtime/type/template/index.ts | 4 + test/runtime/type/template/parser.ts | 607 ++++++++++++++++++ test/runtime/type/template/pattern.ts | 145 +++++ test/runtime/value/cast/custom.ts | 7 +- test/runtime/value/cast/index.ts | 1 + test/runtime/value/cast/recursive.ts | 12 +- test/runtime/value/cast/template-literal.ts | 53 ++ test/runtime/value/check/index.ts | 1 + test/runtime/value/check/recursive.ts | 4 +- test/runtime/value/check/template-literal.ts | 187 ++++++ test/runtime/value/create/index.ts | 1 + test/runtime/value/create/recursive.ts | 8 +- test/runtime/value/create/template-literal.ts | 35 + test/static/exclude.ts | 71 +- test/static/extract.ts | 72 ++- test/static/index.ts | 1 + test/static/template-literal.ts | 44 ++ 77 files changed, 3251 insertions(+), 986 deletions(-) create mode 100644 changelog/0.27.0.md create mode 100644 test/runtime/compiler/template-literal.ts create mode 100644 test/runtime/schema/template-literal.ts create mode 100644 test/runtime/type/extends/template-literal.ts delete mode 100644 test/runtime/type/guard/self.ts create mode 100644 test/runtime/type/guard/template-literal.ts create mode 100644 test/runtime/type/guard/this.ts rename test/runtime/type/{normal => normalize}/exclude.ts (100%) rename test/runtime/type/{normal => normalize}/extract.ts (100%) rename test/runtime/type/{normal => normalize}/index.ts (100%) rename test/runtime/type/{normal => normalize}/intersect.ts (100%) rename test/runtime/type/{normal => normalize}/record.ts (100%) rename test/runtime/type/{normal => normalize}/union.ts (100%) create mode 100644 test/runtime/type/registry/type.ts create mode 100644 test/runtime/type/template/finite.ts create mode 100644 test/runtime/type/template/generate.ts create mode 100644 test/runtime/type/template/index.ts create mode 100644 test/runtime/type/template/parser.ts create mode 100644 test/runtime/type/template/pattern.ts create mode 100644 test/runtime/value/cast/template-literal.ts create mode 100644 test/runtime/value/check/template-literal.ts create mode 100644 test/runtime/value/create/template-literal.ts create mode 100644 test/static/template-literal.ts diff --git a/benchmark/measurement/module/cases.ts b/benchmark/measurement/module/cases.ts index e59f281f2..36ec2d7d9 100644 --- a/benchmark/measurement/module/cases.ts +++ b/benchmark/measurement/module/cases.ts @@ -34,6 +34,19 @@ export namespace Cases { export const Object_Constrained = Type.Object(Object_Unconstrained.properties, { additionalProperties: false, }) + + export const Object_Vector3 = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + + export const Object_Box3D = Type.Object({ + scale: Object_Vector3, + position: Object_Vector3, + rotate: Object_Vector3, + pivot: Object_Vector3, + }) export const Object_Recursive = Type.Recursive( (Recursive) => Type.Object({ diff --git a/changelog/0.27.0.md b/changelog/0.27.0.md new file mode 100644 index 000000000..be87bbf32 --- /dev/null +++ b/changelog/0.27.0.md @@ -0,0 +1,152 @@ +## [0.27.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.27.0) + +## Overview + +Revision 0.27.0 adds support for runtime [Template Literal Types](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html). This revision does not include any functional breaking changes but does rename some public type aliases. As such this revision requires a minor semver increment. + +## Contents + +- Enhancements + - [Template Literal Types](#Template-Literal-Types) + - [TemplateLiteralParser](#TemplateLiteralParser) + - [WorkBench](#WorkBench) +- Breaking Changes + - [TSelf renamed to TThis](#TSelf-renamed-to-TThis) + + + + +## Template Literal Types + +Revision 0.27.0 adds support for Template Literal types. These types operate as a form of computed `TUnion>`. TypeBox encodes template literals using a subset of [ECMA 262](https://json-schema.org/understanding-json-schema/reference/regular_expressions.html) regular erxpressions which are applied to `pattern` properties of type `string`. This encoding enables JSON Schema validators to assert using existing regular expression checks. + +### TypeScript + +TypeScript defines Template Literals using back tick quoted strings which may include embedded union groups. + +```typescript +type T = `option${'A'|'B'}` // type T = 'optionA' | 'optionB' + +type R = Record // type R = { + // optionA: string + // optionB: string + // } +``` + +### TypeBox + +TypeBox defines Template Literals using the `TemplateLiteral` function. This function accepts a sequence of TLiteral, TString, TNumber, TInteger and TBigInt which describe a sequence concatenations. The embedded TUnion type defines an option group which can later be expanded into a set of `TLiteral`. This expansion enables Template Literal types to also be used as Record keys. + +```typescript +const T = Type.TemplateLiteral([ // const T = { + Type.Literal('option'), // pattern: '^option(A|B)$', + Type.Union([ // type: 'string' + Type.Literal('A'), // } + Type.Literal('B') + ]) +]) + +const R = Type.Record(T, Type.String()) // const R = { + // type: 'object', + // required: ['optionA', 'optionB'], + // properties: { + // optionA: { + // type: 'string' + // }, + // optionB: { + // type: 'string' + // } + // } + // } + +type T = Static // type T = 'optionA' | 'optionB' + +type R = Static // type R = { + // optionA: string + // optionB: string + // } +``` + +## TemplateLiteralParser + +Template Literal types are encoded as `string` patterns. Because these types also need to act as composable union types, Revision 0.27.0 includes an expression parser / generator system specifically for regular expressions. This system is used during composition to allow templates to compose with other types, but can also be used in isolation to generate string sequences for the supported expression grammar. This functionality may be provided as standard on the `Value.*` sub module in subsequent revisions. + +The following generates a 8-bit binary sequence for the given expression. + +```typescript + +import { TemplateLiteralParser, TemplateLiteralGenerator, TemplateLiteralFinite } from '@sinclair/typebox' + +const Bit = `(0|1)` // bit union +const Byte = `${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}${Bit}` // byte sequence + +const E = TemplateLiteralParser.Parse(Byte) // parsed expression tree +const F = TemplateLiteralFinite.Check(E) // is the expression finite? +const S = [...TemplateLiteralGenerator.Generate(E)] // generate sequence + +// const S = [ // computed sequence +// '00000000', '00000001', '00000010', '00000011', '00000100', +// '00000101', '00000110', '00000111', '00001000', '00001001', +// '00001010', '00001011', '00001100', '00001101', '00001110', +// '00001111', '00010000', '00010001', '00010010', '00010011', +// '00010100', '00010101', '00010110', '00010111', '00011000', +// '00011001', '00011010', '00011011', '00011100', '00011101', +// '00011110', '00011111', '00100000', '00100001', '00100010', +// '00100011', '00100100', '00100101', '00100110', '00100111', +// '00101000', '00101001', '00101010', '00101011', '00101100', +// '00101101', '00101110', '00101111', '00110000', '00110001', +// '00110010', '00110011', '00110100', '00110101', '00110110', +// '00110111', '00111000', '00111001', '00111010', '00111011', +// '00111100', '00111101', '00111110', '00111111', '01000000', +// '01000001', '01000010', '01000011', '01000100', '01000101', +// '01000110', '01000111', '01001000', '01001001', '01001010', +// '01001011', '01001100', '01001101', '01001110', '01001111', +// '01010000', '01010001', '01010010', '01010011', '01010100', +// '01010101', '01010110', '01010111', '01011000', '01011001', +// '01011010', '01011011', '01011100', '01011101', '01011110', +// '01011111', '01100000', '01100001', '01100010', '01100011', +// ... 156 more items +// ] +``` + + + +## Workbench + +To assist with TypeScript alignment and to prototype new features. A new web based compiler tool has been written that allows interactive cross compiling between TypeScript and TypeBox. This tool will be enhanced seperately from the TypeBox project, but can be used to quickly generate TypeBox type definitions from existing TypeScript types. + +[TypeBox Workbench Application](https://sinclairzx81.github.io/typebox-workbench) + +[TypeBox Workbench Project](https://github.com/sinclairzx81/typebox-workbench) + + + + +## Breaking Changes + +The following are breaking changes in Revision 0.27.0 + + + +## TSelf renamed to TThis + +This rename is to align with TypeScript interfaces. Unlike `type` aliases, TypeScript `interface` types include a implicit `this` type. This change relates specifically to TypeBox's current Recursive type which passes the `TThis` parameter via callback. The `TThis` parameter can be seen as analogous to the implicit TypeScript interface `this`. + +Consider the following. + +```typescript +// type T = { id: string, nodes: this[] } // error: no implicit this + +interface Node { // ok: this is implicit for interfaces + id: string, + nodes: this[] +} + +const T = Type.Recursive(This => // `This` === implicit 'this' for interface + Type.Object({ // + id: Type.String(), // Should `Recursive` be renamed to `Interface`? + nodes: Type.Array(This) + }) +) +``` +Future revisions may rename `Recurisve` to `Interface`, but for now, just the `TSelf` has been renamed. \ No newline at end of file diff --git a/example/codegen/typebox-to-typescript.ts b/example/codegen/typebox-to-typescript.ts index 6629c6022..f7f76713a 100644 --- a/example/codegen/typebox-to-typescript.ts +++ b/example/codegen/typebox-to-typescript.ts @@ -90,7 +90,7 @@ export namespace TypeBoxToTypeScript { function Record(schema: Types.TRecord) { for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { const type = Visit(value) - if (key === '^(0|[1-9][0-9]*)$') { + if (key === Types.PatternNumberExact) { return `Record` } else { return `Record` @@ -101,7 +101,7 @@ export namespace TypeBoxToTypeScript { function Ref(schema: Types.TRef) { return schema.$ref } - function Self(schema: Types.TSelf) { + function This(schema: Types.TThis) { return schema.$ref } function Tuple(schema: Types.TTuple) { @@ -155,10 +155,10 @@ export namespace TypeBoxToTypeScript { return Record(schema) } else if (Types.TypeGuard.TRef(schema)) { return Ref(schema) - } else if (Types.TypeGuard.TSelf(schema)) { - return Self(schema) } else if (Types.TypeGuard.TString(schema)) { return String(schema) + } else if (Types.TypeGuard.TThis(schema)) { + return This(schema) } else if (Types.TypeGuard.TTuple(schema)) { return Tuple(schema) } else if (Types.TypeGuard.TUint8Array(schema)) { diff --git a/example/codegen/typebox-to-zod.ts b/example/codegen/typebox-to-zod.ts index 84d84706d..bfcdc4b0c 100644 --- a/example/codegen/typebox-to-zod.ts +++ b/example/codegen/typebox-to-zod.ts @@ -151,7 +151,7 @@ export namespace TypeBoxToZod { if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) return schema.$ref } - function Self(schema: Types.TSelf) { + function This(schema: Types.TThis) { if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) recursive_set.add(schema.$ref) return schema.$ref @@ -208,10 +208,10 @@ export namespace TypeBoxToZod { return Record(schema) } else if (Types.TypeGuard.TRef(schema)) { return Ref(schema) - } else if (Types.TypeGuard.TSelf(schema)) { - return Self(schema) } else if (Types.TypeGuard.TString(schema)) { return String(schema) + } else if (Types.TypeGuard.TThis(schema)) { + return This(schema) } else if (Types.TypeGuard.TTuple(schema)) { return Tuple(schema) } else if (Types.TypeGuard.TUint8Array(schema)) { diff --git a/example/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts index d2b9d0d6d..953f8a2be 100644 --- a/example/codegen/typescript-to-typebox.ts +++ b/example/codegen/typescript-to-typebox.ts @@ -29,112 +29,8 @@ THE SOFTWARE. import { Formatter } from './formatter' import * as ts from 'typescript' -// ------------------------------------------------------------------------- -// [StringTemplateLiteral] -// -// Specialized code path for evaluating string template literals as regular -// expressions. This is distinct from typical code paths which would -// otherwise yield types of TSchema inside the generated regular expression. -// ------------------------------------------------------------------------- -namespace StringTemplateLiteral { - function DereferenceNode(reference: ts.TypeReferenceNode): ts.Node | undefined { - function find(node: ts.Node): ts.Node | undefined { - if (node.getText() === reference.getText()) return node.parent - for (const inner of node.getChildren()) { - const result = find(inner) - if (result) return result - } - return undefined - } - return find(reference.getSourceFile()) - } - function Dequote(value: string) { - const match = value.match(/^(['"`])(.*)\1$/) - return match ? match[2] : value - } - function Escape(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\\\$&') - } - function* LiteralTypeNode(node: ts.LiteralTypeNode) { - yield Escape(Dequote(node.literal.getText())) - } - function* UnionTypeNode(node: ts.UnionTypeNode) { - const tokens = node.types.map((type) => Collect(type)).join('|') - yield `(${tokens})` - } - function* StringLiteral(node: ts.StringLiteral) { - yield Dequote(node.getText()) - } - function* TypeReference(node: ts.TypeReferenceNode) { - const target = DereferenceNode(node) - if (target === undefined) return - yield Collect(target) - } - function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration) { - yield Collect(node.type) - } - function* LiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { - for (const inner of node.getChildren()) { - yield Collect(inner) - } - } - function* TemplateHead(node: ts.TemplateHead) { - yield node.text - } - function* TemplateMiddle(node: ts.TemplateMiddle) { - yield node.text - } - function* TemplateTail(node: ts.TemplateTail) { - yield node.text - } - function* Visit(node: ts.Node | undefined): IterableIterator { - if (node === undefined) return - if (ts.isUnionTypeNode(node)) { - return yield* UnionTypeNode(node) - } else if (ts.isStringLiteral(node)) { - return yield* StringLiteral(node) - } else if (ts.isTemplateLiteralTypeSpan(node)) { - return yield* LiteralTypeSpan(node) - } else if (ts.isTypeReferenceNode(node)) { - return yield* TypeReference(node) - } else if (ts.isTypeAliasDeclaration(node)) { - return yield* TypeAliasDeclaration(node) - } else if (ts.isTemplateHead(node)) { - return yield* TemplateHead(node) - } else if (ts.isTemplateMiddle(node)) { - return yield* TemplateMiddle(node) - } else if (ts.isTemplateTail(node)) { - return yield* TemplateTail(node) - } else if (ts.isLiteralTypeNode(node)) { - return yield* LiteralTypeNode(node) - } else if (node.kind === ts.SyntaxKind.NumberKeyword) { - yield `(0|[1-9][0-9]*)` - } else if (node.kind === ts.SyntaxKind.StringKeyword) { - yield `(.*)` - } else if (node.kind === ts.SyntaxKind.SyntaxList) { - for (const child of node.getChildren()) { - yield* Visit(child) - } - return - } else { - console.log('StringTemplateLiteral Unhandled:', ts.SyntaxKind[node.kind]) - } - } - function Collect(node: ts.Node | undefined): string { - return `${[...Visit(node)].join('')}` - } - export function* TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { - const buffer: string[] = [] - for (const inner of node.getChildren()) { - buffer.push(Collect(inner)) - } - yield `Type.String({ pattern: '^${buffer.join('')}\$' })` - } -} // ------------------------------------------------------------------------- // [TypeScriptToTypeBox] -// -// The following are code generation paths for types of TSchema. // ------------------------------------------------------------------------- /** Generates TypeBox types from TypeScript code */ export namespace TypeScriptToTypeBox { @@ -195,6 +91,26 @@ export namespace TypeScriptToTypeBox { const types = node.types.map((type) => Collect(type)).join(',\n') yield `Type.Union([\n${types}\n])` } + // ------------------------------------------------------------------------- + // TemplateLiteral + // ------------------------------------------------------------------------- + function* TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { + const collect = node.getChildren().map(node => Collect(node)) + yield `Type.TemplateLiteral([${collect.join('')}])` + } + function* TemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { + const collect = node.getChildren().map(node => Collect(node)) + yield collect.join(', ') + } + function* TemplateHead(node: ts.TemplateHead) { + yield `Type.Literal('${node.text}'), ` + } + function* TemplateMiddle(node: ts.TemplateMiddle) { + yield `Type.Literal('${node.text}'), ` + } + function* TemplateTail(node: ts.TemplateTail) { + yield `Type.Literal('${node.text}')` + } function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') yield `Type.Intersect([\n${types}\n])` @@ -340,45 +256,25 @@ export namespace TypeScriptToTypeBox { function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { const name = node.typeName.getText() const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' - if (name === 'Array') { - return yield `Type.Array${args}` - } else if (name === 'Record') { - return yield `Type.Record${args}` - } else if (name === 'Partial') { - return yield `Type.Partial${args}` - } else if (name === 'Uint8Array') { - return yield `Type.Uint8Array()` - } else if (name === 'Date') { - return yield `Type.Date()` - } else if (name === 'Required') { - return yield `Type.Required${args}` - } else if (name === 'Omit') { - return yield `Type.Omit${args}` - } else if (name === 'Pick') { - return yield `Type.Pick${args}` - } else if (name === 'Promise') { - return yield `Type.Promise${args}` - } else if (name === 'ReturnType') { - return yield `Type.ReturnType${args}` - } else if (name === 'InstanceType') { - return yield `Type.InstanceType${args}` - } else if (name === 'Parameters') { - return yield `Type.Parameters${args}` - } else if (name === 'ConstructorParameters') { - return yield `Type.ConstructorParameters${args}` - } else if (name === 'Exclude') { - return yield `Type.Exclude${args}` - } else if (name === 'Extract') { - return yield `Type.Extract${args}` - } else if (recursiveDeclaration !== null && FindRecursiveParent(recursiveDeclaration, node)) { - return yield `This` - } else if (typeNames.has(name)) { - return yield `${name}${args}` - } else if (name in globalThis) { - return yield `Type.Never(/* Unsupported Type '${name}' */)` - } else { - return yield `${name}${args}` - } + if (name === 'Array') return yield `Type.Array${args}` + if (name === 'Record') return yield `Type.Record${args}` + if (name === 'Partial') return yield `Type.Partial${args}` + if (name === 'Uint8Array') return yield `Type.Uint8Array()` + if (name === 'Date') return yield `Type.Date()` + if (name === 'Required') return yield `Type.Required${args}` + if (name === 'Omit') return yield `Type.Omit${args}` + if (name === 'Pick') return yield `Type.Pick${args}` + if (name === 'Promise') return yield `Type.Promise${args}` + if (name === 'ReturnType') return yield `Type.ReturnType${args}` + if (name === 'InstanceType') return yield `Type.InstanceType${args}` + if (name === 'Parameters') return yield `Type.Parameters${args}` + if (name === 'ConstructorParameters') return yield `Type.ConstructorParameters${args}` + if (name === 'Exclude') return yield `Type.Exclude${args}` + if (name === 'Extract') return yield `Type.Extract${args}` + if (recursiveDeclaration !== null && FindRecursiveParent(recursiveDeclaration, node)) return yield `This` + if (typeNames.has(name)) return yield `${name}${args}` + if (name in globalThis) return yield `Type.Never(/* Unsupported Type '${name}' */)` + return yield `${name}${args}` } function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { const text = node.getText() @@ -411,99 +307,60 @@ export namespace TypeScriptToTypeBox { } function* Visit(node: ts.Node | undefined): IterableIterator { if (node === undefined) return - if (ts.isSourceFile(node)) { - return yield* SourceFile(node) - } else if (ts.isInterfaceDeclaration(node)) { - return yield* InterfaceDeclaration(node) - } else if (ts.isTypeAliasDeclaration(node)) { - return yield* TypeAliasDeclaration(node) - } else if (ts.isParameter(node)) { - return yield* Parameter(node) - } else if (ts.isFunctionTypeNode(node)) { - return yield* FunctionTypeNode(node) - } else if (ts.isConstructorTypeNode(node)) { - return yield* ConstructorTypeNode(node) - } else if (ts.isEnumDeclaration(node)) { - return yield* EnumDeclaration(node) - } else if (ts.isPropertySignature(node)) { - return yield* PropertySignature(node) - } else if (ts.isTypeReferenceNode(node)) { - return yield* TypeReferenceNode(node) - } else if (ts.isTypeLiteralNode(node)) { - return yield* TypeLiteralNode(node) - } else if (ts.isLiteralTypeNode(node)) { - return yield* LiteralTypeNode(node) - } else if (ts.isModuleDeclaration(node)) { - return yield* ModuleDeclaration(node) - } else if (ts.isModuleBlock(node)) { - return yield* ModuleBlock(node) - } else if (ts.isArrayTypeNode(node)) { - return yield* ArrayTypeNode(node) - } else if (ts.isTupleTypeNode(node)) { - return yield* TupleTypeNode(node) - } else if (ts.isIntersectionTypeNode(node)) { - return yield* IntersectionTypeNode(node) - } else if (ts.isUnionTypeNode(node)) { - return yield* UnionTypeNode(node) - } else if (ts.isTemplateLiteralTypeNode(node)) { - return yield* StringTemplateLiteral.TemplateLiteralTypeNode(node) - } else if (ts.isTypeOperatorNode(node)) { - return yield* TypeOperatorNode(node) - } else if (ts.isHeritageClause(node)) { - return yield* HeritageClause(node) - } else if (ts.isExpressionWithTypeArguments(node)) { - return yield* ExpressionWithTypeArguments(node) - } else if (ts.isTypeParameterDeclaration(node)) { - return yield* TypeParameterDeclaration(node) - } else if (ts.isParenthesizedTypeNode(node)) { - return yield* ParenthesizedTypeNode(node) - } else if (ts.isRestTypeNode(node)) { - return yield* RestTypeNode(node) - } else if (ts.isFunctionDeclaration(node)) { - return yield* FunctionDeclaration(node) - } else if (ts.isClassDeclaration(node)) { - return yield* ClassDeclaration(node) - } else if (ts.isConditionalTypeNode(node)) { - return yield* ConditionalTypeNode(node) - } else if (ts.isIndexSignatureDeclaration(node)) { - return yield* isIndexSignatureDeclaration(node) - } else if (ts.isIdentifier(node)) { - return yield node.getText() - } else if (node.kind === ts.SyntaxKind.ExportKeyword) { - return yield `export` - } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { - return yield `Type.KeyOf()` - } else if (node.kind === ts.SyntaxKind.NumberKeyword) { - return yield `Type.Number()` - } else if (node.kind === ts.SyntaxKind.BigIntKeyword) { - return yield `Type.BigInt()` - } else if (node.kind === ts.SyntaxKind.StringKeyword) { - return yield `Type.String()` - } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { - return yield `Type.Boolean()` - } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { - return yield `Type.Undefined()` - } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { - return yield `Type.Unknown()` - } else if (node.kind === ts.SyntaxKind.AnyKeyword) { - return yield `Type.Any()` - } else if (node.kind === ts.SyntaxKind.NeverKeyword) { - return yield `Type.Never()` - } else if (node.kind === ts.SyntaxKind.NullKeyword) { - return yield `Type.Null()` - } else if (node.kind === ts.SyntaxKind.VoidKeyword) { - return yield `Type.Void()` - } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { - return - } else if (node.kind === ts.SyntaxKind.SyntaxList) { + if (ts.isSourceFile(node)) return yield* SourceFile(node) + if (ts.isInterfaceDeclaration(node)) return yield* InterfaceDeclaration(node) + if (ts.isTypeAliasDeclaration(node)) return yield* TypeAliasDeclaration(node) + if (ts.isParameter(node)) return yield* Parameter(node) + if (ts.isFunctionTypeNode(node)) return yield* FunctionTypeNode(node) + if (ts.isConstructorTypeNode(node)) return yield* ConstructorTypeNode(node) + if (ts.isEnumDeclaration(node)) return yield* EnumDeclaration(node) + if (ts.isPropertySignature(node)) return yield* PropertySignature(node) + if (ts.isTypeReferenceNode(node)) return yield* TypeReferenceNode(node) + if (ts.isTypeLiteralNode(node)) return yield* TypeLiteralNode(node) + if (ts.isLiteralTypeNode(node)) return yield* LiteralTypeNode(node) + if (ts.isModuleDeclaration(node)) return yield* ModuleDeclaration(node) + if (ts.isModuleBlock(node)) return yield* ModuleBlock(node) + if (ts.isArrayTypeNode(node)) return yield* ArrayTypeNode(node) + if (ts.isTupleTypeNode(node)) return yield* TupleTypeNode(node) + if (ts.isIntersectionTypeNode(node)) return yield* IntersectionTypeNode(node) + if (ts.isUnionTypeNode(node)) return yield* UnionTypeNode(node) + if (ts.isTemplateLiteralTypeNode(node)) return yield* TemplateLiteralTypeNode(node) + if (ts.isTemplateLiteralTypeSpan(node)) return yield* TemplateLiteralTypeSpan(node) + if (ts.isTemplateHead(node)) return yield* TemplateHead(node) + if (ts.isTemplateMiddle(node)) return yield* TemplateMiddle(node) + if (ts.isTemplateTail(node)) return yield* TemplateTail(node) + if (ts.isTypeOperatorNode(node)) return yield* TypeOperatorNode(node) + if (ts.isHeritageClause(node)) return yield* HeritageClause(node) + if (ts.isExpressionWithTypeArguments(node)) return yield* ExpressionWithTypeArguments(node) + if (ts.isTypeParameterDeclaration(node)) return yield* TypeParameterDeclaration(node) + if (ts.isParenthesizedTypeNode(node)) return yield* ParenthesizedTypeNode(node) + if (ts.isRestTypeNode(node)) return yield* RestTypeNode(node) + if (ts.isFunctionDeclaration(node)) return yield* FunctionDeclaration(node) + if (ts.isClassDeclaration(node)) return yield* ClassDeclaration(node) + if (ts.isConditionalTypeNode(node)) return yield* ConditionalTypeNode(node) + if (ts.isIndexSignatureDeclaration(node)) return yield* isIndexSignatureDeclaration(node) + if (ts.isIdentifier(node)) return yield node.getText() + if (node.kind === ts.SyntaxKind.ExportKeyword) return yield `export` + if (node.kind === ts.SyntaxKind.KeyOfKeyword) return yield `Type.KeyOf()` + if (node.kind === ts.SyntaxKind.NumberKeyword) return yield `Type.Number()` + if (node.kind === ts.SyntaxKind.BigIntKeyword) return yield `Type.BigInt()` + if (node.kind === ts.SyntaxKind.StringKeyword) return yield `Type.String()` + if (node.kind === ts.SyntaxKind.BooleanKeyword) return yield `Type.Boolean()` + if (node.kind === ts.SyntaxKind.UndefinedKeyword) return yield `Type.Undefined()` + if (node.kind === ts.SyntaxKind.UnknownKeyword) return yield `Type.Unknown()` + if (node.kind === ts.SyntaxKind.AnyKeyword) return yield `Type.Any()` + if (node.kind === ts.SyntaxKind.NeverKeyword) return yield `Type.Never()` + if (node.kind === ts.SyntaxKind.NullKeyword) return yield `Type.Null()` + if (node.kind === ts.SyntaxKind.VoidKeyword) return yield `Type.Void()` + if (node.kind === ts.SyntaxKind.EndOfFileToken) return + if (node.kind === ts.SyntaxKind.SyntaxList) { for (const child of node.getChildren()) { yield* Visit(child) } return - } else { - console.log('Unhandled:', ts.SyntaxKind[node.kind]) - return yield node.getText() } + console.log('Unhandled:', ts.SyntaxKind[node.kind]) + return yield node.getText() } /** Generates TypeBox types from TypeScript interface and type definitions */ export function Generate(typescriptCode: string) { @@ -521,4 +378,4 @@ export namespace TypeScriptToTypeBox { const types = Formatter.Format(typeDeclarations) return [imports, '', types].join('\n') } -} +} \ No newline at end of file diff --git a/example/index.ts b/example/index.ts index ed52156bd..c5ad5a144 100644 --- a/example/index.ts +++ b/example/index.ts @@ -37,4 +37,4 @@ console.log(C.Code()) // Check: Value // ----------------------------------------------------------- -console.log(C.Check(V)) +console.log(C.Check(V)) \ No newline at end of file diff --git a/hammer.mjs b/hammer.mjs index 8cf594e67..e47b88f82 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -7,21 +7,18 @@ import { readFileSync } from 'fs' export async function clean() { await folder('target').delete() } - // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') } - // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- export async function start(example = 'index') { await shell(`hammer run example/${example}.ts --dist target/example/${example}`) } - // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index e013c6885..64dbe8148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@sinclair/typebox", - "version": "0.26.0", + "version": "0.27.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.26.0", + "version": "0.27.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", - "ajv": "^8.11.2", + "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "chai": "^4.3.6", "mocha": "^9.2.2", @@ -75,9 +75,9 @@ "dev": true }, "node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -1582,9 +1582,9 @@ "dev": true }, "ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", diff --git a/package.json b/package.json index aa248455b..6b792d3e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.26.8", + "version": "0.27.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,7 +37,7 @@ "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", - "ajv": "^8.11.2", + "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "chai": "^4.3.6", "mocha": "^9.2.2", diff --git a/readme.md b/readme.md index 265d408e4..b5cb73687 100644 --- a/readme.md +++ b/readme.md @@ -81,6 +81,7 @@ License MIT - [References](#types-references) - [Recursive](#types-recursive) - [Conditional](#types-conditional) + - [Template Literal](#types-template-literal) - [Guards](#types-guards) - [Unsafe](#types-unsafe) - [Strict](#types-strict) @@ -264,12 +265,12 @@ The following table lists the Standard TypeBox types. These types are fully comp │ Type.Number(), │ │ type: 'array', │ │ Type.Number() │ │ items: [{ │ │ ]) │ │ type: 'number' │ -│ │ │ }, { │ -│ │ │ type: 'number' │ -│ │ │ }], │ -│ │ │ additionalItems: false, │ -│ │ │ minItems: 2, │ -│ │ │ maxItems: 2 │ +│ │ │ }, { │ +│ │ │ type: 'number' │ +│ │ │ }], │ +│ │ │ additionalItems: false, │ +│ │ │ minItems: 2, │ +│ │ │ maxItems: 2 │ │ │ │ } │ │ │ │ │ │ │ │ │ @@ -321,6 +322,7 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ y: { │ │ │ │ type: 'number' │ │ │ │ } │ +│ │ │ } │ │ │ │ }] │ │ │ │ } │ │ │ │ │ @@ -385,6 +387,18 @@ The following table lists the Standard TypeBox types. These types are fully comp │ ) │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const U = Type.Union([ │ type U = 'open' | 'close' │ const T = { │ +│ Type.Literal('open'), │ │ type: 'string', │ +│ Type.Literal('close') │ type T = `on${U}` │ pattern: '^on(open|close)$' │ +│ ]) │ │ } │ +│ │ │ │ +│ const T = Type │ │ │ +│ .TemplateLiteral([ │ │ │ +│ Type.Literal('on'), │ │ │ +│ U │ │ │ +│ ]) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Record( │ type T = Record< │ const T = { │ │ Type.String(), │ string, │ type: 'object', │ │ Type.Number() │ number, │ patternProperties: { │ @@ -424,23 +438,23 @@ The following table lists the Standard TypeBox types. These types are fully comp ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │ │ Type.Object({ │ x: number, │ type: 'object', │ -│ x: Type.Number(), │ y: number │ properties: { │ -│ y: Type.Number() | }, 'x'> │ x: { │ -│ }), ['x'] │ │ type: 'number' │ -│ ) │ │ } │ -│ │ │ }, │ -│ │ │ required: ['x'] │ +│ x: Type.Number(), │ y: number │ required: ['x'], │ +│ y: Type.Number() │ }, 'x'> │ properties: { │ +│ }), ['x'] | │ x: { │ +│ ) │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Omit( │ type T = Omit<{ │ const T = { │ │ Type.Object({ │ x: number, │ type: 'object', │ -│ x: Type.Number(), │ y: number │ properties: { │ -│ y: Type.Number() | }, 'x'> │ y: { │ -│ }), ['x'] │ │ type: 'number' │ -│ ) │ │ } │ -│ │ │ }, │ -│ │ │ required: ['y'] │ +│ x: Type.Number(), │ y: number │ required: ['y'], │ +│ y: Type.Number() │ }, 'x'> │ properties: { │ +│ }), ['x'] | │ y: { │ +│ ) │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -687,7 +701,7 @@ type T = Static // type T = string | null ### Reference Types -Reference types are supported with `Type.Ref(...)`. The target type must specify a valid `$id`. +Reference types are supported with `Type.Ref`. The target type must specify a valid `$id`. ```typescript const T = Type.String({ $id: 'T' }) // const T = { @@ -704,7 +718,7 @@ const R = Type.Ref(T) // const R = { ### Recursive Types -Recursive types are supported with `Type.Recursive(...)`. +Recursive types are supported with `Type.Recursive` ```typescript const Node = Type.Recursive(Node => Type.Object({ // const Node = { @@ -741,33 +755,85 @@ function test(node: Node) { ### Conditional Types -Conditional types are supported with `Extends`, `Exclude` and `Extract`. - -**TypeScript** +Conditional types are supported with `Type.Extends`, `Type.Exclude` and `Type.Extract` ```typescript -type T0 = string extends number ? true : false -// ^ false -type T1 = Extract -// ^ number -type T2 = Exclude -// ^ string +// TypeScript + +type T0 = string extends number ? true : false // type T0 = false + +type T1 = Extract // type T1 = number + +type T2 = Exclude // type T2 = string + +// TypeBox + +const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) + +const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number()) + +const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number()) + + +type T0 = Static // type T0 = false + +type T1 = Static // type T1 = number + +type T2 = Static // type T2 = string ``` -**TypeBox** + + + +### Template Literal Types + +Template Literal types are supported with `Type.TemplateLiteral` + ```typescript -const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) -// ^ TLiteral -const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number()) -// ^ TNumber -const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number()) -// ^ TString +// TypeScript + +type T = `option${'A'|'B'}` // type T = 'optionA' | 'optionB' + +type R = Record // type R = { + // optionA: string + // optionB: string + // } + +// TypeBox + +const T = Type.TemplateLiteral([ // const T = { + Type.Literal('option'), // pattern: '^option(A|B)$', + Type.Union([ // type: 'string' + Type.Literal('A'), // } + Type.Literal('B') + ]) +]) + +const R = Type.Record(T, Type.String()) // const R = { + // type: 'object', + // required: ['optionA', 'optionB'], + // properties: { + // optionA: { + // type: 'string' + // }, + // optionB: { + // type: 'string' + // } + // } + // } + +type T = Static // type T = 'optionA' | 'optionB' + +type R = Static // type R = { + // optionA: string + // optionB: string + // } ``` ### Unsafe -Use `Type.Unsafe(...)` to create custom schematics with user defined inference rules. +Use `Type.Unsafe` to create custom schematics with user defined inference rules. ```typescript const T = Type.Unsafe({ type: 'number' }) // const T = { @@ -777,7 +843,7 @@ const T = Type.Unsafe({ type: 'number' }) // const T = { type T = Static // type T = string ``` -The `Type.Unsafe(...)` type can be useful to express specific OpenAPI schema representations. +The `Type.Unsafe` type can be useful to express specific OpenAPI schema representations. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' @@ -829,7 +895,7 @@ if(TypeGuard.TString(T)) { ### Strict -TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are used for type composition and reflection. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary. +TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are used for type composition and reflection. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict` function that will omit these properties if necessary. ```typescript const T = Type.Object({ // const T = { @@ -905,7 +971,7 @@ const R = Value.Check(T, { x: 1 }) // const R = true Use the Convert function to convert a value into its target type if a reasonable conversion is possible. ```typescript -const T = Type.Object({ x: Type.Number(), y: Type.Number() }) +const T = Type.Object({ x: Type.Number() }) const R1 = Value.Convert(T, { x: '3.14' }) // const R1 = { x: 3.14 } @@ -1181,16 +1247,16 @@ const R = Value.Check(T, { x: 1, y: 2 }) // const R = true ### Formats -Use the `Format(...)` function to create a custom string format. The following creates a custom string format that checks for lowercase strings. +Use the `Format(...)` function to create a custom string format. The following creates a format that checks for lowercase strings. ```typescript TypeSystem.Format('lowercase', value => value === value.toLowerCase()) // format should be lowercase const T = Type.String({ format: 'lowercase' }) -const A = Value.Check(T, 'action') // const A = true +const A = Value.Check(T, 'Hello') // const A = false -const B = Value.Check(T, 'ACTION') // const B = false +const B = Value.Check(T, 'hello') // const B = true ``` @@ -1217,7 +1283,7 @@ TypeSystem.AllowNaN = true ## Benchmark -This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.11.2. +This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.12.0. For additional comparative benchmarks, please refer to [typescript-runtime-type-benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/). @@ -1231,33 +1297,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 260 ms' │ ' 8 ms' │ ' 32.50 x' │ -│ Literal_Number │ 1000 │ ' 198 ms' │ ' 4 ms' │ ' 49.50 x' │ -│ Literal_Boolean │ 1000 │ ' 185 ms' │ ' 5 ms' │ ' 37.00 x' │ -│ Primitive_Number │ 1000 │ ' 176 ms' │ ' 9 ms' │ ' 19.56 x' │ -│ Primitive_String │ 1000 │ ' 161 ms' │ ' 9 ms' │ ' 17.89 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 215 ms' │ ' 12 ms' │ ' 17.92 x' │ -│ Primitive_Boolean │ 1000 │ ' 133 ms' │ ' 5 ms' │ ' 26.60 x' │ -│ Primitive_Null │ 1000 │ ' 143 ms' │ ' 8 ms' │ ' 17.88 x' │ -│ Object_Unconstrained │ 1000 │ ' 1181 ms' │ ' 38 ms' │ ' 31.08 x' │ -│ Object_Constrained │ 1000 │ ' 1168 ms' │ ' 32 ms' │ ' 36.50 x' │ -│ Tuple_Primitive │ 1000 │ ' 557 ms' │ ' 16 ms' │ ' 34.81 x' │ -│ Tuple_Object │ 1000 │ ' 1119 ms' │ ' 17 ms' │ ' 65.82 x' │ -│ Composite_Intersect │ 1000 │ ' 569 ms' │ ' 22 ms' │ ' 25.86 x' │ -│ Composite_Union │ 1000 │ ' 513 ms' │ ' 23 ms' │ ' 22.30 x' │ -│ Math_Vector4 │ 1000 │ ' 802 ms' │ ' 10 ms' │ ' 80.20 x' │ -│ Math_Matrix4 │ 1000 │ ' 395 ms' │ ' 12 ms' │ ' 32.92 x' │ -│ Array_Primitive_Number │ 1000 │ ' 282 ms' │ ' 8 ms' │ ' 35.25 x' │ -│ Array_Primitive_String │ 1000 │ ' 321 ms' │ ' 5 ms' │ ' 64.20 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 364 ms' │ ' 5 ms' │ ' 72.80 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1573 ms' │ ' 18 ms' │ ' 87.39 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1270 ms' │ ' 20 ms' │ ' 63.50 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 973 ms' │ ' 18 ms' │ ' 54.06 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1253 ms' │ ' 16 ms' │ ' 78.31 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 927 ms' │ ' 20 ms' │ ' 46.35 x' │ -│ Array_Composite_Union │ 1000 │ ' 1123 ms' │ ' 16 ms' │ ' 70.19 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1068 ms' │ ' 10 ms' │ ' 106.80 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 488 ms' │ ' 7 ms' │ ' 69.71 x' │ +│ Literal_String │ 1000 │ ' 257 ms' │ ' 8 ms' │ ' 32.13 x' │ +│ Literal_Number │ 1000 │ ' 203 ms' │ ' 4 ms' │ ' 50.75 x' │ +│ Literal_Boolean │ 1000 │ ' 183 ms' │ ' 4 ms' │ ' 45.75 x' │ +│ Primitive_Number │ 1000 │ ' 174 ms' │ ' 8 ms' │ ' 21.75 x' │ +│ Primitive_String │ 1000 │ ' 158 ms' │ ' 9 ms' │ ' 17.56 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 213 ms' │ ' 13 ms' │ ' 16.38 x' │ +│ Primitive_Boolean │ 1000 │ ' 136 ms' │ ' 6 ms' │ ' 22.67 x' │ +│ Primitive_Null │ 1000 │ ' 144 ms' │ ' 6 ms' │ ' 24.00 x' │ +│ Object_Unconstrained │ 1000 │ ' 1176 ms' │ ' 38 ms' │ ' 30.95 x' │ +│ Object_Constrained │ 1000 │ ' 1181 ms' │ ' 31 ms' │ ' 38.10 x' │ +│ Object_Vector3 │ 1000 │ ' 387 ms' │ ' 8 ms' │ ' 48.38 x' │ +│ Object_Box3D │ 1000 │ ' 1693 ms' │ ' 25 ms' │ ' 67.72 x' │ +│ Tuple_Primitive │ 1000 │ ' 470 ms' │ ' 15 ms' │ ' 31.33 x' │ +│ Tuple_Object │ 1000 │ ' 1206 ms' │ ' 17 ms' │ ' 70.94 x' │ +│ Composite_Intersect │ 1000 │ ' 567 ms' │ ' 20 ms' │ ' 28.35 x' │ +│ Composite_Union │ 1000 │ ' 515 ms' │ ' 21 ms' │ ' 24.52 x' │ +│ Math_Vector4 │ 1000 │ ' 787 ms' │ ' 10 ms' │ ' 78.70 x' │ +│ Math_Matrix4 │ 1000 │ ' 386 ms' │ ' 8 ms' │ ' 48.25 x' │ +│ Array_Primitive_Number │ 1000 │ ' 349 ms' │ ' 7 ms' │ ' 49.86 x' │ +│ Array_Primitive_String │ 1000 │ ' 336 ms' │ ' 4 ms' │ ' 84.00 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 284 ms' │ ' 3 ms' │ ' 94.67 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1704 ms' │ ' 19 ms' │ ' 89.68 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1456 ms' │ ' 18 ms' │ ' 80.89 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 792 ms' │ ' 15 ms' │ ' 52.80 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1552 ms' │ ' 17 ms' │ ' 91.29 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 744 ms' │ ' 18 ms' │ ' 41.33 x' │ +│ Array_Composite_Union │ 1000 │ ' 783 ms' │ ' 15 ms' │ ' 52.20 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1093 ms' │ ' 14 ms' │ ' 78.07 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 684 ms' │ ' 6 ms' │ ' 114.00 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1271,35 +1339,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 26 ms' │ ' 6 ms' │ ' 6 ms' │ ' 1.00 x' │ -│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ -│ Literal_Boolean │ 1000000 │ ' 19 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ -│ Primitive_Number │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ -│ Primitive_String │ 1000000 │ ' 26 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 159 ms' │ ' 45 ms' │ ' 37 ms' │ ' 1.22 x' │ -│ Primitive_Boolean │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ -│ Primitive_Null │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ -│ Object_Unconstrained │ 1000000 │ ' 809 ms' │ ' 35 ms' │ ' 30 ms' │ ' 1.17 x' │ -│ Object_Constrained │ 1000000 │ ' 1060 ms' │ ' 56 ms' │ ' 45 ms' │ ' 1.24 x' │ -│ Object_Recursive │ 1000000 │ ' 4965 ms' │ ' 397 ms' │ ' 100 ms' │ ' 3.97 x' │ -│ Tuple_Primitive │ 1000000 │ ' 159 ms' │ ' 22 ms' │ ' 16 ms' │ ' 1.38 x' │ -│ Tuple_Object │ 1000000 │ ' 658 ms' │ ' 31 ms' │ ' 27 ms' │ ' 1.15 x' │ -│ Composite_Intersect │ 1000000 │ ' 695 ms' │ ' 26 ms' │ ' 22 ms' │ ' 1.18 x' │ -│ Composite_Union │ 1000000 │ ' 503 ms' │ ' 24 ms' │ ' 19 ms' │ ' 1.26 x' │ -│ Math_Vector4 │ 1000000 │ ' 259 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1007 ms' │ ' 40 ms' │ ' 29 ms' │ ' 1.38 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 262 ms' │ ' 23 ms' │ ' 17 ms' │ ' 1.35 x' │ -│ Array_Primitive_String │ 1000000 │ ' 241 ms' │ ' 27 ms' │ ' 24 ms' │ ' 1.13 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 141 ms' │ ' 23 ms' │ ' 20 ms' │ ' 1.15 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 4976 ms' │ ' 70 ms' │ ' 67 ms' │ ' 1.04 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5234 ms' │ ' 143 ms' │ ' 120 ms' │ ' 1.19 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 19605 ms' │ ' 1909 ms' │ ' 350 ms' │ ' 5.45 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 706 ms' │ ' 39 ms' │ ' 32 ms' │ ' 1.22 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 2951 ms' │ ' 67 ms' │ ' 63 ms' │ ' 1.06 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 2969 ms' │ ' 49 ms' │ ' 44 ms' │ ' 1.11 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2191 ms' │ ' 77 ms' │ ' 41 ms' │ ' 1.88 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1164 ms' │ ' 41 ms' │ ' 25 ms' │ ' 1.64 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4903 ms' │ ' 115 ms' │ ' 99 ms' │ ' 1.16 x' │ +│ Literal_String │ 1000000 │ ' 27 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ +│ Literal_Number │ 1000000 │ ' 23 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Literal_Boolean │ 1000000 │ ' 21 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Primitive_Number │ 1000000 │ ' 26 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ +│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 155 ms' │ ' 49 ms' │ ' 43 ms' │ ' 1.14 x' │ +│ Primitive_Boolean │ 1000000 │ ' 23 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_Null │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Object_Unconstrained │ 1000000 │ ' 804 ms' │ ' 35 ms' │ ' 28 ms' │ ' 1.25 x' │ +│ Object_Constrained │ 1000000 │ ' 1041 ms' │ ' 55 ms' │ ' 41 ms' │ ' 1.34 x' │ +│ Object_Vector3 │ 1000000 │ ' 380 ms' │ ' 26 ms' │ ' 20 ms' │ ' 1.30 x' │ +│ Object_Box3D │ 1000000 │ ' 1785 ms' │ ' 65 ms' │ ' 52 ms' │ ' 1.25 x' │ +│ Object_Recursive │ 1000000 │ ' 4984 ms' │ ' 396 ms' │ ' 114 ms' │ ' 3.47 x' │ +│ Tuple_Primitive │ 1000000 │ ' 168 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ +│ Tuple_Object │ 1000000 │ ' 673 ms' │ ' 30 ms' │ ' 26 ms' │ ' 1.15 x' │ +│ Composite_Intersect │ 1000000 │ ' 751 ms' │ ' 28 ms' │ ' 20 ms' │ ' 1.40 x' │ +│ Composite_Union │ 1000000 │ ' 489 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ +│ Math_Vector4 │ 1000000 │ ' 259 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1002 ms' │ ' 40 ms' │ ' 30 ms' │ ' 1.33 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 252 ms' │ ' 22 ms' │ ' 15 ms' │ ' 1.47 x' │ +│ Array_Primitive_String │ 1000000 │ ' 227 ms' │ ' 22 ms' │ ' 18 ms' │ ' 1.22 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 150 ms' │ ' 23 ms' │ ' 22 ms' │ ' 1.05 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 4754 ms' │ ' 71 ms' │ ' 64 ms' │ ' 1.11 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 4787 ms' │ ' 142 ms' │ ' 123 ms' │ ' 1.15 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 19088 ms' │ ' 1735 ms' │ ' 314 ms' │ ' 5.53 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 650 ms' │ ' 41 ms' │ ' 31 ms' │ ' 1.32 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 2770 ms' │ ' 67 ms' │ ' 55 ms' │ ' 1.22 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 2693 ms' │ ' 50 ms' │ ' 39 ms' │ ' 1.28 x' │ +│ Array_Composite_Union │ 1000000 │ ' 1982 ms' │ ' 72 ms' │ ' 33 ms' │ ' 2.18 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1068 ms' │ ' 40 ms' │ ' 26 ms' │ ' 1.54 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4609 ms' │ ' 115 ms' │ ' 88 ms' │ ' 1.31 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1313,11 +1383,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '108.8 kb' │ ' 48.9 kb' │ '2.23 x' │ -│ typebox/errors │ ' 93.2 kb' │ ' 41.5 kb' │ '2.24 x' │ -│ typebox/system │ ' 60.0 kb' │ ' 24.6 kb' │ '2.43 x' │ -│ typebox/value │ '153.5 kb' │ ' 66.7 kb' │ '2.30 x' │ -│ typebox │ ' 58.7 kb' │ ' 24.1 kb' │ '2.43 x' │ +│ typebox/compiler │ '124.3 kb' │ ' 55.7 kb' │ '2.23 x' │ +│ typebox/errors │ '107.8 kb' │ ' 47.9 kb' │ '2.25 x' │ +│ typebox/system │ ' 73.3 kb' │ ' 30.2 kb' │ '2.43 x' │ +│ typebox/value │ '170.7 kb' │ ' 74.2 kb' │ '2.30 x' │ +│ typebox │ ' 72.0 kb' │ ' 29.7 kb' │ '2.43 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 1d269f98a..03d27ffeb 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -307,10 +307,6 @@ export namespace TypeCompiler { if (state_local_function_names.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } - function* Self(schema: Types.TSelf, references: Types.TSchema[], value: string): IterableIterator { - const func = CreateFunctionName(schema.$ref) - yield `${func}(${value})` - } function* String(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` @@ -326,6 +322,15 @@ export namespace TypeCompiler { function* Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'symbol')` } + function* TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof ${value} === 'string')` + const local = PushLocal(`${new RegExp(schema.pattern)};`) + yield `${local}.test(${value})` + } + function* This(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { + const func = CreateFunctionName(schema.$ref) + yield `${func}(${value})` + } function* Tuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { yield `(Array.isArray(${value}))` if (schema.items === undefined) return yield `${value}.length === 0` @@ -409,12 +414,14 @@ export namespace TypeCompiler { return yield* Record(schema_, references_, value) case 'Ref': return yield* Ref(schema_, references_, value) - case 'Self': - return yield* Self(schema_, references_, value) case 'String': return yield* String(schema_, references_, value) case 'Symbol': return yield* Symbol(schema_, references_, value) + case 'TemplateLiteral': + return yield* TemplateLiteral(schema_, references_, value) + case 'This': + return yield* This(schema_, references_, value) case 'Tuple': return yield* Tuple(schema_, references_, value) case 'Undefined': diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 521343938..1d8d7615e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -126,7 +126,7 @@ export class ValueErrorsUnknownTypeError extends Error { } } export class ValueErrorsDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueErrors: Unable to dereference schema with $id '${schema.$ref}'`) } } @@ -399,7 +399,7 @@ export namespace ValueErrors { const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(keyPattern) if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { - const numeric = keyPattern === '^(0|[1-9][0-9]*)$' + const numeric = keyPattern === Types.PatternNumberExact const type = numeric ? ValueErrorType.RecordKeyNumeric : ValueErrorType.RecordKeyString const message = numeric ? 'Expected all object property keys to be numeric' : 'Expected all object property keys to be strings' return yield { type, schema, path, value, message } @@ -414,12 +414,6 @@ export namespace ValueErrors { const target = references[index] yield* Visit(target, references, path, value) } - function* Self(schema: Types.TSelf, references: Types.TSchema[], path: string, value: any): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueErrorsDereferenceError(schema) - const target = references[index] - yield* Visit(target, references, path, value) - } function* String(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!IsString(value)) { return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } @@ -452,6 +446,21 @@ export namespace ValueErrors { return yield { type: ValueErrorType.Symbol, schema, path, value, message: 'Expected symbol' } } } + function* TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsString(value)) { + return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } + } + const regex = new RegExp(schema.pattern) + if (!regex.test(value)) { + yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } + } + } + function* This(schema: Types.TThis, references: Types.TSchema[], path: string, value: any): IterableIterator { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueErrorsDereferenceError(schema) + const target = references[index] + yield* Visit(target, references, path, value) + } function* Tuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!globalThis.Array.isArray(value)) { return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } @@ -551,12 +560,14 @@ export namespace ValueErrors { return yield* Record(schema_, references_, path, value) case 'Ref': return yield* Ref(schema_, references_, path, value) - case 'Self': - return yield* Self(schema_, references_, path, value) case 'String': return yield* String(schema_, references_, path, value) case 'Symbol': return yield* Symbol(schema_, references_, path, value) + case 'TemplateLiteral': + return yield* TemplateLiteral(schema_, references_, path, value) + case 'This': + return yield* This(schema_, references_, path, value) case 'Tuple': return yield* Tuple(schema_, references_, path, value) case 'Undefined': diff --git a/src/typebox.ts b/src/typebox.ts index 9887da23e..a25d5b0d2 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -27,12 +27,21 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ // -------------------------------------------------------------------------- -// Compositing Symbols +// Symbols // -------------------------------------------------------------------------- export const Modifier = Symbol.for('TypeBox.Modifier') export const Hint = Symbol.for('TypeBox.Hint') export const Kind = Symbol.for('TypeBox.Kind') // -------------------------------------------------------------------------- +// Patterns +// -------------------------------------------------------------------------- +export const PatternBoolean = '(true|false)' +export const PatternNumber = '(0|[1-9][0-9]*)' +export const PatternString = '.*' +export const PatternBooleanExact = `^${PatternBoolean}$` +export const PatternNumberExact = `^${PatternNumber}$` +export const PatternStringExact = `^${PatternString}$` +// -------------------------------------------------------------------------- // Helpers // -------------------------------------------------------------------------- export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never @@ -99,9 +108,10 @@ export type TAnySchema = | TPromise | TRecord | TRef - | TSelf | TString | TSymbol + | TTemplateLiteral + | TThis | TTuple | TUndefined | TUnion @@ -210,10 +220,9 @@ export interface TEnumOption { type: 'number' | 'string' const: T } -export type TEnumStatic> = T[keyof T] export interface TEnum = Record> extends TSchema { [Kind]: 'Union' - static: TEnumStatic + static: T[keyof T] anyOf: TLiteral[] } // -------------------------------------------------------------------------- @@ -227,19 +236,31 @@ export type TExtends = TUnionResult }[T]>, TSchema[]>> +export type TExcludeTemplateLiteral = Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never // prettier-ignore export type TExcludeArray = Assert> extends Static ? never : T[K] }[number]>, TSchema[]> extends infer R ? TUnionResult> : never -export type TExclude = T extends TUnion ? TExcludeArray : T extends U ? TNever : T +// prettier-ignore +export type TExclude = + T extends TTemplateLiteral ? TExcludeTemplateLiteral : + T extends TUnion ? TExcludeArray : + T extends U ? TNever : T // -------------------------------------------------------------------------- // TExtract // -------------------------------------------------------------------------- +export type TExtractTemplateLiteralResult = TUnionResult }[T]>, TSchema[]>> +export type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never // prettier-ignore export type TExtractArray = Assert> extends Static ? T[K] : never }[number]>, TSchema[]> extends infer R ? TUnionResult> : never -export type TExtract = T extends TUnion ? TExtractArray : T extends U ? T : TNever +// prettier-ignore +export type TExtract = + T extends TTemplateLiteral ? TExtractTemplateLiteral : + T extends TUnion ? TExtractArray : + T extends U ? T : T // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- @@ -267,12 +288,10 @@ export type TUnevaluatedProperties = undefined | TSchema | boolean export interface IntersectOptions extends SchemaOptions { unevaluatedProperties?: TUnevaluatedProperties } -export type TIntersectStatic = TupleToIntersect<{ [K in keyof T]: Static, P> }> - export interface TIntersect extends TSchema, IntersectOptions { [Kind]: 'Intersect' + static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> type?: 'object' - static: TIntersectStatic allOf: [...T] } // -------------------------------------------------------------------------- @@ -286,10 +305,11 @@ export type TKeyOfTuple = { : never // prettier-ignore export type TKeyOf = ( - T extends TComposite ? TKeyOfTuple : - T extends TIntersect ? TKeyOfTuple : - T extends TUnion ? TKeyOfTuple : - T extends TObject ? TKeyOfTuple : + T extends TComposite ? TKeyOfTuple : + T extends TIntersect ? TKeyOfTuple : + T extends TUnion ? TKeyOfTuple : + T extends TObject ? TKeyOfTuple : + T extends TRecord ? [K] : [] ) extends infer R ? TUnionResult> : never // -------------------------------------------------------------------------- @@ -312,10 +332,9 @@ export interface TNever extends TSchema { // -------------------------------------------------------------------------- // TNot // -------------------------------------------------------------------------- -export type TNotStatic<_ extends TSchema = TSchema, T extends TSchema = TSchema> = Static export interface TNot extends TSchema { [Kind]: 'Not' - static: TNotStatic + static: Static allOf: [{ not: Not }, T] } // -------------------------------------------------------------------------- @@ -426,10 +445,9 @@ export type TPick = // -------------------------------------------------------------------------- // TPromise // -------------------------------------------------------------------------- -export type TPromiseStatic = Promise> export interface TPromise extends TSchema { [Kind]: 'Promise' - static: TPromiseStatic + static: Promise> type: 'object' instanceOf: 'Promise' item: TSchema @@ -437,13 +455,16 @@ export interface TPromise extends TSchema { // -------------------------------------------------------------------------- // TRecord // -------------------------------------------------------------------------- -export type TRecordKey = TString | TNumeric | TUnion[]> -export type TRecordPropertiesFromUnionLiteral[]>, T extends TSchema> = Static extends string ? { [X in Static]: T } : never -export type TRecordPropertiesFromLiteral, T extends TSchema> = Evaluate<{ [K2 in K['const']]: T }> -export type TRecordStatic = Record, Static> -export interface TRecord extends TSchema { +export type RecordTemplateLiteralObjectType = Ensure]: T }>>> +export type RecordTemplateLiteralType = IsTemplateLiteralFinite extends true ? RecordTemplateLiteralObjectType : TRecord +export type RecordUnionLiteralType[]>, T extends TSchema> = Static extends string ? Ensure]: T }>> : never +export type RecordLiteralType, T extends TSchema> = Ensure> +export type RecordNumberType = Ensure> +export type RecordStringType = Ensure> +export type RecordKey = TUnion[]> | TLiteral | TTemplateLiteral | TInteger | TNumber | TString +export interface TRecord extends TSchema { [Kind]: 'Record' - static: TRecordStatic + static: Record, Static> type: 'object' patternProperties: { [pattern: string]: T } additionalProperties: false @@ -451,8 +472,8 @@ export interface TRecord extends TSchema { // -------------------------------------------------------------------------- // TRef // -------------------------------------------------------------------------- -export type TRefStatic = Static export interface TRef extends TSchema { [Kind]: 'Ref' - static: TRefStatic + static: Static $ref: string } // -------------------------------------------------------------------------- @@ -538,15 +558,54 @@ export interface TSymbol extends TSchema, SchemaOptions { typeOf: 'Symbol' } // -------------------------------------------------------------------------- +// TTemplateLiteral +// -------------------------------------------------------------------------- +// prettier-ignore +export type IsTemplateLiteralFiniteCheck = + T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray> : + T extends TUnion ? IsTemplateLiteralFiniteArray> : + T extends TString ? false : + T extends TBoolean ? false : + T extends TNumber ? false : + T extends TInteger ? false : + T extends TBigInt ? false : + T extends TLiteral ? true : + false +// prettier-ignore +export type IsTemplateLiteralFiniteArray = + T extends [infer L, ...infer R] ? IsTemplateLiteralFiniteCheck extends false ? false : IsTemplateLiteralFiniteArray> : + T extends [infer L] ? IsTemplateLiteralFiniteCheck extends false ? false : true : + true +export type IsTemplateLiteralFinite = T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray : false +export type TTemplateLiteralKind = TUnion | TLiteral | TInteger | TTemplateLiteral | TNumber | TBigInt | TString | TBoolean | TNever +// prettier-ignore +export type TTemplateLiteralConst = + T extends TUnion ? { [K in keyof U]: TTemplateLiteralUnion, Acc> }[number] : + T extends TTemplateLiteral ? `${Static}` : + T extends TLiteral ? `${U}` : + T extends TString ? `${string}` : + T extends TNumber ? `${number}` : + T extends TBigInt ? `${bigint}` : + T extends TBoolean ? `${boolean}` : + never +// prettier-ignore +export type TTemplateLiteralUnion = + T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : + T extends [infer L] ? `${TTemplateLiteralConst}${Acc}` : + Acc +export interface TTemplateLiteral extends TSchema { + [Kind]: 'TemplateLiteral' + static: TTemplateLiteralUnion + type: 'string' + pattern: string // todo: it may be possible to infer this pattern +} +// -------------------------------------------------------------------------- // TTuple // -------------------------------------------------------------------------- export type TTupleIntoArray> = T extends TTuple ? Assert : never -export type TTupleStatic = { - [K in keyof T]: T[K] extends TSchema ? Static : T[K] -} export interface TTuple extends TSchema { [Kind]: 'Tuple' - static: TTupleStatic + static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } type: 'array' items?: T additionalItems?: false @@ -574,6 +633,8 @@ export type TUnionResult = T extends [] ? TNever : T extend // -------------------------------------------------------------------------- // TUnion // -------------------------------------------------------------------------- +// prettier-ignore +export type TUnionTemplateLiteral> = Ensure}[S]>,TLiteral[]>>> export interface TUnion extends TSchema { [Kind]: 'Union' static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] @@ -764,7 +825,7 @@ export namespace TypeGuard { IsOptionalBoolean(schema.uniqueItems) ) } - /** Returns true if the given schema is TSymbol */ + /** Returns true if the given schema is TBigInt */ export function TBigInt(schema: unknown): schema is TBigInt { // prettier-ignore return ( @@ -996,16 +1057,6 @@ export namespace TypeGuard { } return true } - /** Returns true if the given schema is TSelf */ - export function TSelf(schema: unknown): schema is TSelf { - // prettier-ignore - return ( - TKind(schema) && - schema[Kind] === 'Self' && - IsOptionalString(schema.$id) && - IsString(schema.$ref) - ) - } /** Returns true if the given schema is TRef */ export function TRef(schema: unknown): schema is TRef { // prettier-ignore @@ -1029,7 +1080,6 @@ export namespace TypeGuard { IsOptionalFormat(schema.format) ) } - /** Returns true if the given schema is TSymbol */ export function TSymbol(schema: unknown): schema is TSymbol { // prettier-ignore @@ -1041,7 +1091,28 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - + /** Returns true if the given schema is TTemplateLiteral */ + export function TTemplateLiteral(schema: unknown): schema is TTemplateLiteral { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'TemplateLiteral' && + schema.type === 'string' && + IsString(schema.pattern) && + schema.pattern[0] === '^' && + schema.pattern[schema.pattern.length - 1] === '$' + ) + } + /** Returns true if the given schema is TThis */ + export function TThis(schema: unknown): schema is TThis { + // prettier-ignore + return ( + TKind(schema) && + schema[Kind] === 'This' && + IsOptionalString(schema.$id) && + IsString(schema.$ref) + ) + } /** Returns true if the given schema is TTuple */ export function TTuple(schema: unknown): schema is TTuple { // prettier-ignore @@ -1098,7 +1169,6 @@ export namespace TypeGuard { export function TUnionLiteral(schema: unknown): schema is TUnion[]> { return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema) && typeof schema.const === 'string') } - /** Returns true if the given schema is TUint8Array */ export function TUint8Array(schema: unknown): schema is TUint8Array { return TKind(schema) && schema[Kind] === 'Uint8Array' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.instanceOf === 'Uint8Array' && IsOptionalNumber(schema.minByteLength) && IsOptionalNumber(schema.maxByteLength) @@ -1164,10 +1234,11 @@ export namespace TypeGuard { TObject(schema) || TPromise(schema) || TRecord(schema) || - TSelf(schema) || TRef(schema) || TString(schema) || TSymbol(schema) || + TTemplateLiteral(schema) || + TThis(schema) || TTuple(schema) || TUndefined(schema) || TUnion(schema) || @@ -1523,13 +1594,13 @@ export namespace TypeExtends { // Record // -------------------------------------------------------------------------- function RecordKey(schema: TRecord) { - if ('^(0|[1-9][0-9]*)$' in schema.patternProperties) return Type.Number() - if ('^.*$' in schema.patternProperties) return Type.String() + if (PatternNumberExact in schema.patternProperties) return Type.Number() + if (PatternStringExact in schema.patternProperties) return Type.String() throw Error('TypeExtends: Cannot get record key') } function RecordValue(schema: TRecord) { - if ('^(0|[1-9][0-9]*)$' in schema.patternProperties) return schema.patternProperties['^(0|[1-9][0-9]*)$'] - if ('^.*$' in schema.patternProperties) return schema.patternProperties['^.*$'] + if (PatternNumberExact in schema.patternProperties) return schema.patternProperties[PatternNumberExact] + if (PatternStringExact in schema.patternProperties) return schema.patternProperties[PatternStringExact] throw Error('TypeExtends: Cannot get record value') } function RecordRight(left: TSchema, right: TRecord) { @@ -1645,7 +1716,7 @@ export namespace TypeExtends { function UnionRight(left: TSchema, right: TUnion): TypeExtendsResult { return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Union(left: TUnion, right: TSchema) { + function Union(left: TUnion, right: TSchema): TypeExtendsResult { return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- @@ -1683,6 +1754,10 @@ export namespace TypeExtends { return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { + // template union remap + if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) + if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) + // standard extends if (TypeGuard.TAny(left)) return Any(left, right) if (TypeGuard.TArray(left)) return Array(left, right) if (TypeGuard.TBigInt(left)) return BigInt(left, right) @@ -1696,10 +1771,10 @@ export namespace TypeExtends { if (TypeGuard.TNever(left)) return Never(left, right) if (TypeGuard.TNull(left)) return Null(left, right) if (TypeGuard.TNumber(left)) return Number(left, right) + if (TypeGuard.TObject(left)) return Object(left, right) if (TypeGuard.TRecord(left)) return Record(left, right) if (TypeGuard.TString(left)) return String(left, right) if (TypeGuard.TSymbol(left)) return Symbol(left, right) - if (TypeGuard.TObject(left)) return Object(left, right) if (TypeGuard.TTuple(left)) return Tuple(left, right) if (TypeGuard.TPromise(left)) return Promise(left, right) if (TypeGuard.TUint8Array(left)) return Uint8Array(left, right) @@ -1803,6 +1878,224 @@ export namespace KeyResolver { } } // -------------------------------------------------------------------------- +// TemplateLiteralPattern +// -------------------------------------------------------------------------- +export namespace TemplateLiteralPattern { + function Escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + } + function Visit(schema: TSchema, acc: string): string { + if (TypeGuard.TTemplateLiteral(schema)) { + const pattern = schema.pattern.slice(1, schema.pattern.length - 1) + return pattern + } else if (TypeGuard.TUnion(schema)) { + const tokens = schema.anyOf.map((schema) => Visit(schema, acc)).join('|') + return `(${tokens})` + } else if (TypeGuard.TNumber(schema)) { + return `${acc}${PatternNumber}` + } else if (TypeGuard.TInteger(schema)) { + return `${acc}${PatternNumber}` + } else if (TypeGuard.TBigInt(schema)) { + return `${acc}${PatternNumber}` + } else if (TypeGuard.TString(schema)) { + return `${acc}${PatternString}` + } else if (TypeGuard.TLiteral(schema)) { + return `${acc}${Escape(schema.const.toString())}` + } else if (TypeGuard.TBoolean(schema)) { + return `${acc}${PatternBoolean}` + } else if (TypeGuard.TNever(schema)) { + throw Error('TemplateLiteralPattern: TemplateLiteral cannot operate on types of TNever') + } else { + throw Error(`TemplateLiteralPattern: Unexpected Kind '${schema[Kind]}'`) + } + } + export function Create(kinds: TTemplateLiteralKind[]): string { + return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` + } +} +// -------------------------------------------------------------------------------------- +// TemplateLiteralResolver +// -------------------------------------------------------------------------------------- +export namespace TemplateLiteralResolver { + export function Resolve(template: TTemplateLiteral): TString | TUnion | TLiteral { + const expression = TemplateLiteralParser.ParseExact(template.pattern) + if (!TemplateLiteralFinite.Check(expression)) return Type.String() + const literals = [...TemplateLiteralGenerator.Generate(expression)].map((value) => Type.Literal(value)) + return Type.Union(literals) + } +} +// -------------------------------------------------------------------------------------- +// TemplateLiteralParser +// -------------------------------------------------------------------------------------- +export class TemplateLiteralParserError extends Error { + constructor(message: string) { + super(message) + } +} +export namespace TemplateLiteralParser { + export type Expression = And | Or | Const + export type Const = { type: 'const'; const: string } + export type And = { type: 'and'; expr: Expression[] } + export type Or = { type: 'or'; expr: Expression[] } + function Unescape(value: string) { + return value.replace(/\\/g, '') + } + function IsNonEscaped(pattern: string, index: number, char: string) { + return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 + } + function IsOpenParen(pattern: string, index: number) { + return IsNonEscaped(pattern, index, '(') + } + function IsCloseParen(pattern: string, index: number) { + return IsNonEscaped(pattern, index, ')') + } + function IsSeparator(pattern: string, index: number) { + return IsNonEscaped(pattern, index, '|') + } + function IsGroup(pattern: string) { + if (!(IsOpenParen(pattern, 0) && IsCloseParen(pattern, pattern.length - 1))) return false + let count = 0 + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (count === 0 && index !== pattern.length - 1) return false + } + return true + } + function InGroup(pattern: string) { + return pattern.slice(1, pattern.length - 1) + } + function IsPrecedenceOr(pattern: string) { + let count = 0 + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (IsSeparator(pattern, index) && count === 0) return true + } + return false + } + function IsPrecedenceAnd(pattern: string) { + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) return true + } + return false + } + function Or(pattern: string): Expression { + let [count, start] = [0, 0] + const expressions: Expression[] = [] + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (IsSeparator(pattern, index) && count === 0) { + const range = pattern.slice(start, index) + if (range.length > 0) expressions.push(Parse(range)) + start = index + 1 + } + } + const range = pattern.slice(start) + if (range.length > 0) expressions.push(Parse(range)) + if (expressions.length === 0) return { type: 'const', const: '' } + if (expressions.length === 1) return expressions[0] + return { type: 'or', expr: expressions } + } + function And(pattern: string): Expression { + function Group(value: string, index: number): [number, number] { + if (!IsOpenParen(value, index)) throw new TemplateLiteralParserError(`TemplateLiteralParser: Index must point to open parens`) + let count = 0 + for (let scan = index; scan < value.length; scan++) { + if (IsOpenParen(value, scan)) count += 1 + if (IsCloseParen(value, scan)) count -= 1 + if (count === 0) return [index, scan] + } + throw new TemplateLiteralParserError(`TemplateLiteralParser: Unclosed group parens in expression`) + } + function Range(pattern: string, index: number): [number, number] { + for (let scan = index; scan < pattern.length; scan++) { + if (IsOpenParen(pattern, scan)) return [index, scan] + } + return [index, pattern.length] + } + const expressions: Expression[] = [] + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) { + const [start, end] = Group(pattern, index) + const range = pattern.slice(start, end + 1) + expressions.push(Parse(range)) + index = end + } else { + const [start, end] = Range(pattern, index) + const range = pattern.slice(start, end) + if (range.length > 0) expressions.push(Parse(range)) + index = end - 1 + } + } + if (expressions.length === 0) return { type: 'const', const: '' } + if (expressions.length === 1) return expressions[0] + return { type: 'and', expr: expressions } + } + /** Parses a pattern and returns an expression tree */ + export function Parse(pattern: string): Expression { + if (IsGroup(pattern)) return Parse(InGroup(pattern)) + if (IsPrecedenceOr(pattern)) return Or(pattern) + if (IsPrecedenceAnd(pattern)) return And(pattern) + return { type: 'const', const: Unescape(pattern) } + } + /** Parses a pattern and strips forward and trailing ^ and $ */ + export function ParseExact(pattern: string): Expression { + return Parse(pattern.slice(1, pattern.length - 1)) + } +} +// -------------------------------------------------------------------------------------- +// TemplateLiteralFinite +// -------------------------------------------------------------------------------------- +export namespace TemplateLiteralFinite { + function IsNumber(expression: TemplateLiteralParser.Expression): boolean { + return expression.type === 'or' && expression.expr.length === 2 && expression.expr[0].type === 'const' && expression.expr[0].const === '0' && expression.expr[1].type === 'const' && expression.expr[1].const === '[1-9][0-9]*' + } + function IsBoolean(expression: TemplateLiteralParser.Expression): boolean { + return expression.type === 'or' && expression.expr.length === 2 && expression.expr[0].type === 'const' && expression.expr[0].const === 'true' && expression.expr[1].type === 'const' && expression.expr[1].const === 'false' + } + function IsString(expression: TemplateLiteralParser.Expression) { + return expression.type === 'const' && expression.const === '.*' + } + export function Check(expression: TemplateLiteralParser.Expression): boolean { + if (IsBoolean(expression)) return true + if (IsNumber(expression) || IsString(expression)) return false + if (expression.type === 'and') return expression.expr.every((expr) => Check(expr)) + if (expression.type === 'or') return expression.expr.every((expr) => Check(expr)) + if (expression.type === 'const') return true + throw Error(`TemplateLiteralFinite: Unknown expression type`) + } +} +// -------------------------------------------------------------------------------------- +// TemplateLiteralGenerator +// -------------------------------------------------------------------------------------- +export namespace TemplateLiteralGenerator { + function* Reduce(buffer: string[][]): IterableIterator { + if (buffer.length === 1) return yield* buffer[0] + for (const left of buffer[0]) { + for (const right of Reduce(buffer.slice(1))) { + yield `${left}${right}` + } + } + } + function* And(expression: TemplateLiteralParser.And): IterableIterator { + return yield* Reduce(expression.expr.map((expr) => [...Generate(expr)])) + } + function* Or(expression: TemplateLiteralParser.Or): IterableIterator { + for (const expr of expression.expr) yield* Generate(expr) + } + function* Const(expression: TemplateLiteralParser.Const): IterableIterator { + return yield expression.const + } + export function* Generate(expression: TemplateLiteralParser.Expression): IterableIterator { + if (expression.type === 'and') return yield* And(expression) + if (expression.type === 'or') return yield* Or(expression) + if (expression.type === 'const') return yield* Const(expression) + throw Error('TemplateLiteralGenerator: Unknown expression') + } +} +// -------------------------------------------------------------------------- // TypeOrdinal: Used for auto $id generation // -------------------------------------------------------------------------- let TypeOrdinal = 0 @@ -1909,6 +2202,8 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Excludes from the left type any type that is not assignable to the right */ public Exclude(left: L, right: R, options: SchemaOptions = {}): TExclude { + if (TypeGuard.TTemplateLiteral(left)) return this.Exclude(TemplateLiteralResolver.Resolve(left), right, options) as TExclude + if (TypeGuard.TTemplateLiteral(right)) return this.Exclude(left, TemplateLiteralResolver.Resolve(right), options) as any as TExclude if (TypeGuard.TUnion(left)) { const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) === TypeExtendsResult.False) return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExclude @@ -1918,6 +2213,8 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Extracts from the left type any type that is assignable to the right */ public Extract(left: L, right: R, options: SchemaOptions = {}): TExtract { + if (TypeGuard.TTemplateLiteral(left)) return this.Extract(TemplateLiteralResolver.Resolve(left), right, options) as TExtract + if (TypeGuard.TTemplateLiteral(right)) return this.Extract(left, TemplateLiteralResolver.Resolve(right), options) as any as TExtract if (TypeGuard.TUnion(left)) { const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) !== TypeExtendsResult.False) return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExtract @@ -1949,10 +2246,17 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Creates a KeyOf type */ public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { - const keys = KeyResolver.Resolve(schema) - // prettier-ignore - const keyof = keys.length === 0 ? this.Never(options) : this.Union(keys.map((key) => this.Literal(key)), options) - return keyof as TKeyOf + if (TypeGuard.TRecord(schema)) { + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + if (pattern === PatternNumberExact) return this.Number(options) as TKeyOf + if (pattern === PatternStringExact) return this.String(options) as TKeyOf + throw Error('StandardTypeBuilder: Unable to resolve key type from Record key pattern') + } else { + const resolved = KeyResolver.Resolve(schema) + if (resolved.length === 0) return this.Never(options) as TKeyOf + const literals = resolved.map((key) => this.Literal(key)) + return this.Union(literals, options) as TKeyOf + } } /** `[Standard]` Creates a Literal type */ public Literal(value: T, options: SchemaOptions = {}): TLiteral { @@ -2060,39 +2364,49 @@ export class StandardTypeBuilder extends TypeBuilder { return this.Create(schema) }, options) } - /** `[Standard]` Creates an Object type from the given Literal Union */ - public Record[]>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> - /** `[Standard]` Creates an Object type from the given Literal Union */ - public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): TObject> /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): TRecord - public Record(key: any, schema: TSchema, options: ObjectOptions = {}) { - if (TypeGuard.TLiteral(key)) { - if (typeof key.const === 'string' || typeof key.const === 'number') { - return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options) - } else throw Error('TypeBuilder: Record key can only be derived from literals of number or string') - } - if (TypeGuard.TUnion(key)) { + public Record[]>, T extends TSchema>(key: K, schema: T): RecordUnionLiteralType + /** `[Standard]` Creates a Record type */ + public Record, T extends TSchema>(key: K, schema: T): RecordLiteralType + /** `[Standard]` Creates a Record type */ + public Record(key: K, schema: T): RecordTemplateLiteralType + /** `[Standard]` Creates a Record type */ + public Record(key: K, schema: T): RecordNumberType + /** `[Standard]` Creates a Record type */ + public Record(key: K, schema: T): RecordStringType + /** `[Standard]` Creates a Record type */ + public Record(key: RecordKey, schema: TSchema, options: ObjectOptions = {}) { + if (TypeGuard.TTemplateLiteral(key)) { + const expression = TemplateLiteralParser.ParseExact(key.pattern) + // prettier-ignore + return TemplateLiteralFinite.Check(expression) + ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema, {}) }), {} as TProperties), options)) + : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) + } else if (TypeGuard.TUnionLiteral(key)) { if (key.anyOf.every((schema) => TypeGuard.TLiteral(schema) && (typeof schema.const === 'string' || typeof schema.const === 'number'))) { const properties = key.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) return this.Object(properties, { ...options, [Hint]: 'Record' }) } else throw Error('TypeBuilder: Record key can only be derived from union literal of number or string') + } else if (TypeGuard.TLiteral(key)) { + if (typeof key.const === 'string' || typeof key.const === 'number') { + return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options) + } else throw Error('TypeBuilder: Record key can only be derived from literals of number or string') + } else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) { + const pattern = PatternNumberExact + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) + } else if (TypeGuard.TString(key)) { + const pattern = key.pattern === undefined ? PatternStringExact : key.pattern + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) + } else { + throw Error(`StandardTypeBuilder: Invalid Record Key`) } - const pattern = ['Integer', 'Number'].includes(key[Kind]) ? '^(0|[1-9][0-9]*)$' : key[Kind] === 'String' && key.pattern ? key.pattern : '^.*$' - return this.Create({ - ...options, - [Kind]: 'Record', - type: 'object', - patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, - additionalProperties: false, - }) } /** `[Standard]` Creates a Recursive type */ - public Recursive(callback: (self: TSelf) => T, options: SchemaOptions = {}): TRecursive { + public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { if (options.$id === undefined) (options as any).$id = `T${TypeOrdinal++}` - const self = callback({ [Kind]: 'Self', $ref: `${options.$id}` } as any) - self.$id = options.$id - return this.Create({ ...options, ...self } as any) + const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) + thisType.$id = options.$id + return this.Create({ ...options, ...thisType } as any) } /** `[Standard]` Creates a Ref type. The referenced type must contain a $id */ public Ref(schema: T, options: SchemaOptions = {}): TRef { @@ -2121,6 +2435,11 @@ export class StandardTypeBuilder extends TypeBuilder { public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } + /** `[Standard]` Creates a template literal type */ + public TemplateLiteral(kinds: [...T], options: SchemaOptions = {}): TTemplateLiteral { + const pattern = TemplateLiteralPattern.Create(kinds) + return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) + } /** `[Standard]` Creates a Tuple type */ public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { const [additionalItems, minItems, maxItems] = [false, items.length, items.length] @@ -2137,11 +2456,18 @@ export class StandardTypeBuilder extends TypeBuilder { public Union(anyOf: [...T], options?: SchemaOptions): T[0] /** `[Standard]` Creates a Union type */ public Union(anyOf: [...T], options?: SchemaOptions): TUnion - public Union(anyOf: TSchema[], options: SchemaOptions = {}) { - if (anyOf.length === 0) return this.Never(options) - if (anyOf.length === 1) return this.Create(TypeClone.Clone(anyOf[0], options)) - const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema, {})) - return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) + /** `[Experimental]` Remaps a TemplateLiteral into a Union representation. This function is known to cause TS compiler crashes for finite templates with large generation counts. Use with caution. */ + public Union(template: T): TUnionTemplateLiteral + public Union(union: TSchema[] | TTemplateLiteral, options: SchemaOptions = {}) { + if (TypeGuard.TTemplateLiteral(union)) { + return TemplateLiteralResolver.Resolve(union) + } else { + const anyOf = union + if (anyOf.length === 0) return this.Never(options) + if (anyOf.length === 1) return this.Create(TypeClone.Clone(anyOf[0], options)) + const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema, {})) + return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) + } } /** `[Standard]` Creates an Unknown type */ public Unknown(options: SchemaOptions = {}): TUnknown { @@ -2153,7 +2479,7 @@ export class StandardTypeBuilder extends TypeBuilder { } } // -------------------------------------------------------------------------- -// TypeBuilder +// ExtendedTypeBuilder // -------------------------------------------------------------------------- export class ExtendedTypeBuilder extends StandardTypeBuilder { /** `[Extended]` Creates a BigInt type */ diff --git a/src/value/cast.ts b/src/value/cast.ts index 67e936a29..66bb2abce 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -35,7 +35,7 @@ import { ValueClone } from './clone' // Errors // ---------------------------------------------------------------------------------------------- export class ValueCastReferenceTypeError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) } } @@ -60,7 +60,7 @@ export class ValueCastUnknownTypeError extends Error { } } export class ValueCastDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueCast: Unable to dereference schema with $id '${schema.$ref}'`) } } @@ -225,18 +225,21 @@ export namespace ValueCast { const target = references[index] return Visit(target, references, value) } - function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): any { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCastDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } function String(schema: Types.TString, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) } + function TemplateLiteral(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + } + function This(schema: Types.TThis, references: Types.TSchema[], value: any): any { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCastDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) + } function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) if (!globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) @@ -301,12 +304,14 @@ export namespace ValueCast { return Record(schema_, references_, value) case 'Ref': return Ref(schema_, references_, value) - case 'Self': - return Self(schema_, references_, value) case 'String': return String(schema_, references_, value) case 'Symbol': return Symbol(schema_, references_, value) + case 'TemplateLiteral': + return TemplateLiteral(schema_, references_, value) + case 'This': + return This(schema_, references_, value) case 'Tuple': return Tuple(schema_, references_, value) case 'Undefined': diff --git a/src/value/check.ts b/src/value/check.ts index bf3f42909..d745b2979 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -39,7 +39,7 @@ export class ValueCheckUnknownTypeError extends Error { } } export class ValueCheckDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueCheck: Unable to dereference schema with $id '${schema.$ref}'`) } } @@ -292,12 +292,6 @@ export namespace ValueCheck { const target = references[index] return Visit(target, references, value) } - function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): boolean { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCheckDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } function String(schema: Types.TString, references: Types.TSchema[], value: any): boolean { if (!IsString(value)) { return false @@ -325,6 +319,18 @@ export namespace ValueCheck { } return true } + function TemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { + if (!IsString(value)) { + return false + } + return new RegExp(schema.pattern).test(value) + } + function This(schema: Types.TThis, references: Types.TSchema[], value: any): boolean { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCheckDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) + } function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { if (!globalThis.Array.isArray(value)) { return false @@ -412,12 +418,14 @@ export namespace ValueCheck { return Record(schema_, references_, value) case 'Ref': return Ref(schema_, references_, value) - case 'Self': - return Self(schema_, references_, value) case 'String': return String(schema_, references_, value) case 'Symbol': return Symbol(schema_, references_, value) + case 'TemplateLiteral': + return TemplateLiteral(schema_, references_, value) + case 'This': + return This(schema_, references_, value) case 'Tuple': return Tuple(schema_, references_, value) case 'Undefined': diff --git a/src/value/convert.ts b/src/value/convert.ts index b40ab967b..00f397b9b 100644 --- a/src/value/convert.ts +++ b/src/value/convert.ts @@ -39,7 +39,7 @@ export class ValueConvertUnknownTypeError extends Error { } } export class ValueConvertDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueConvert: Unable to dereference schema with $id '${schema.$ref}'`) } } @@ -243,18 +243,21 @@ export namespace ValueConvert { const target = references[index] return Visit(target, references, value) } - function Self(schema: Types.TSelf, references: Types.TSchema[], value: any): unknown { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueConvertDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } function String(schema: Types.TString, references: Types.TSchema[], value: any): unknown { return TryConvertString(value) } function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { return value } + function TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any) { + return value + } + function This(schema: Types.TThis, references: Types.TSchema[], value: any): unknown { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueConvertDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) + } function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { if (IsArray(value) && schema.items !== undefined) { return value.map((value, index) => { @@ -325,12 +328,14 @@ export namespace ValueConvert { return Record(schema_, references_, value) case 'Ref': return Ref(schema_, references_, value) - case 'Self': - return Self(schema_, references_, value) case 'String': return String(schema_, references_, value) case 'Symbol': return Symbol(schema_, references_, value) + case 'TemplateLiteral': + return TemplateLiteral(schema_, references_, value) + case 'This': + return This(schema_, references_, value) case 'Tuple': return Tuple(schema_, references_, value) case 'Undefined': diff --git a/src/value/create.ts b/src/value/create.ts index a2a0e1392..f6f63b68b 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -47,8 +47,13 @@ export class ValueCreateIntersectTypeError extends Error { super('ValueCreate: Can only create values for intersected objects and non-varying primitive types. Consider using a default value.') } } +export class ValueCreateTempateLiteralTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCreate: Can only create template literal values from patterns that produce finite sequences. Consider using a default value.') + } +} export class ValueCreateDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TSelf) { + constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueCreate: Unable to dereference schema with $id '${schema.$ref}'`) } } @@ -209,7 +214,7 @@ export namespace ValueCreate { const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] if ('default' in schema) { return schema.default - } else if (!(keyPattern === '^.*$' || keyPattern === '^(0|[1-9][0-9]*)$')) { + } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') return propertyKeys.reduce((acc, key) => { return { ...acc, [key]: Create(valueSchema, references) } @@ -228,16 +233,6 @@ export namespace ValueCreate { return Visit(target, references) } } - function Self(schema: Types.TSelf, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$id) - if (index === -1) throw new ValueCreateDereferenceError(schema) - const target = references[index] - return Visit(target, references) - } - } function String(schema: Types.TString, references: Types.TSchema[]): any { if (schema.pattern !== undefined) { if (!('default' in schema)) { @@ -272,6 +267,25 @@ export namespace ValueCreate { return globalThis.Symbol() } } + function TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { + if ('default' in schema) { + return schema.default + } + const expression = Types.TemplateLiteralParser.ParseExact(schema.pattern) + if (!Types.TemplateLiteralFinite.Check(expression)) throw new ValueCreateTempateLiteralTypeError(schema) + const sequence = Types.TemplateLiteralGenerator.Generate(expression) + return sequence.next().value + } + function This(schema: Types.TThis, references: Types.TSchema[]): any { + if ('default' in schema) { + return schema.default + } else { + const index = references.findIndex((foreign) => foreign.$id === schema.$id) + if (index === -1) throw new ValueCreateDereferenceError(schema) + const target = references[index] + return Visit(target, references) + } + } function Tuple(schema: Types.TTuple, references: Types.TSchema[]): any { if ('default' in schema) { return schema.default @@ -369,12 +383,14 @@ export namespace ValueCreate { return Record(schema_, references_) case 'Ref': return Ref(schema_, references_) - case 'Self': - return Self(schema_, references_) case 'String': return String(schema_, references_) case 'Symbol': return Symbol(schema_, references_) + case 'TemplateLiteral': + return TemplateLiteral(schema_, references_) + case 'This': + return This(schema_, references_) case 'Tuple': return Tuple(schema_, references_) case 'Undefined': diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index d04e227f4..1e2fa7629 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -30,6 +30,7 @@ import './regex' import './required' import './string' import './symbol' +import './template-literal' import './tuple' import './uint8array' import './union' diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts index 3cc78be1a..7ffb2b2ae 100644 --- a/test/runtime/compiler/recursive.ts +++ b/test/runtime/compiler/recursive.ts @@ -26,10 +26,10 @@ describe('type/compiler/Recursive', () => { }) it('Should validate recursive node type', () => { - const Node = Type.Recursive((Self) => + const Node = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) Ok(Node, { @@ -43,10 +43,10 @@ describe('type/compiler/Recursive', () => { it('Should validate wrapped recursive node type', () => { const Node = Type.Tuple([ - Type.Recursive((Self) => + Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ), ]) @@ -63,10 +63,10 @@ describe('type/compiler/Recursive', () => { it('Should not validate wrapped recursive node type with invalid id', () => { const Node = Type.Tuple([ - Type.Recursive((Self) => + Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ), ]) diff --git a/test/runtime/compiler/template-literal.ts b/test/runtime/compiler/template-literal.ts new file mode 100644 index 000000000..fb2ebdadb --- /dev/null +++ b/test/runtime/compiler/template-literal.ts @@ -0,0 +1,186 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/TemplateLiteral', () => { + // -------------------------------------------------------- + // Finite + // -------------------------------------------------------- + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([]) + Ok(T, '') + Fail(T, 'X') + }) + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([Type.Boolean()]) + Ok(T, 'true') + Ok(T, 'false') + Fail(T, 'X') + }) + it('Should validate finite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A') + ]) + Ok(T, 'A') + Fail(T, 'X') + }) + it('Should validate finite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Literal('B') + ]) + Ok(T, 'AB') + Fail(T, 'X') + }) + it('Should validate finite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + ]) + Ok(T, 'AB') + Ok(T, 'AC') + Fail(T, 'X') + }) + it('Should validate finite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + Type.Literal('D'), + ]) + Ok(T, 'ABD') + Ok(T, 'ACD') + Fail(T, 'X') + }) + it('Should validate finite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + ]) + Ok(T, '00') + Ok(T, '01') + Ok(T, '10') + Ok(T, '11') + Fail(T, 'X') + }) + // -------------------------------------------------------- + // Infinite + // -------------------------------------------------------- + it('Should validate infinite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Number() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Integer() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.BigInt() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.String() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Ok(T, 'a') + Ok(T, 'bb') + Ok(T, 'ccc') + Ok(T, 'dddd') + }) + + it('Should validate infinite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Number() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Integer() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 7', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.BigInt() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 8', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.String() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Ok(T, 'Aa') + Ok(T, 'Abb') + Ok(T, 'Accc') + Ok(T, 'Adddd') + Fail(T, 'X') + }) +}) diff --git a/test/runtime/schema/index.ts b/test/runtime/schema/index.ts index 705b1d169..6f60c4ce7 100644 --- a/test/runtime/schema/index.ts +++ b/test/runtime/schema/index.ts @@ -26,6 +26,7 @@ import './ref' import './regex' import './required' import './string' +import './template-literal' import './tuple' import './uint8array' import './union' diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts index f5e9a352f..a003f7f42 100644 --- a/test/runtime/schema/recursive.ts +++ b/test/runtime/schema/recursive.ts @@ -26,10 +26,10 @@ describe('type/schema/Recursive', () => { }) it('Should validate recursive node type', () => { - const Node = Type.Recursive((Self) => + const Node = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) Ok(Node, { @@ -43,10 +43,10 @@ describe('type/schema/Recursive', () => { it('Should validate wrapped recursive node type', () => { const Node = Type.Tuple([ - Type.Recursive((Self) => + Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ), ]) @@ -63,10 +63,10 @@ describe('type/schema/Recursive', () => { it('Should not validate wrapped recursive node type with invalid id', () => { const Node = Type.Tuple([ - Type.Recursive((Self) => + Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ), ]) diff --git a/test/runtime/schema/template-literal.ts b/test/runtime/schema/template-literal.ts new file mode 100644 index 000000000..4b3b7985f --- /dev/null +++ b/test/runtime/schema/template-literal.ts @@ -0,0 +1,186 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/schema/TemplateLiteral', () => { + // -------------------------------------------------------- + // Finite + // -------------------------------------------------------- + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([]) + Ok(T, '') + Fail(T, 'X') + }) + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([Type.Boolean()]) + Ok(T, 'true') + Ok(T, 'false') + Fail(T, 'X') + }) + it('Should validate finite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A') + ]) + Ok(T, 'A') + Fail(T, 'X') + }) + it('Should validate finite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Literal('B') + ]) + Ok(T, 'AB') + Fail(T, 'X') + }) + it('Should validate finite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + ]) + Ok(T, 'AB') + Ok(T, 'AC') + Fail(T, 'X') + }) + it('Should validate finite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + Type.Literal('D'), + ]) + Ok(T, 'ABD') + Ok(T, 'ACD') + Fail(T, 'X') + }) + it('Should validate finite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + ]) + Ok(T, '00') + Ok(T, '01') + Ok(T, '10') + Ok(T, '11') + Fail(T, 'X') + }) + // -------------------------------------------------------- + // Infinite + // -------------------------------------------------------- + it('Should validate infinite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Number() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Integer() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.BigInt() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.String() + ]) + Ok(T, '1') + Ok(T, '22') + Ok(T, '333') + Ok(T, '4444') + Ok(T, 'a') + Ok(T, 'bb') + Ok(T, 'ccc') + Ok(T, 'dddd') + }) + + it('Should validate infinite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Number() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Integer() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 7', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.BigInt() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Fail(T, 'X') + }) + it('Should validate infinite pattern 8', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.String() + ]) + Ok(T, 'A1') + Ok(T, 'A22') + Ok(T, 'A333') + Ok(T, 'A4444') + Ok(T, 'Aa') + Ok(T, 'Abb') + Ok(T, 'Accc') + Ok(T, 'Adddd') + Fail(T, 'X') + }) +}) diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts index f67300b2e..ad28839f3 100644 --- a/test/runtime/type/extends/any.ts +++ b/test/runtime/type/extends/any.ts @@ -13,7 +13,6 @@ describe('type/extends/Any', () => { const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = any extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.String()) @@ -89,7 +88,6 @@ describe('type/extends/Any', () => { const R = TypeExtends.Extends(Type.Any(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.Union) }) - it('Should extend Date', () => { type T = any extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Date()) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts index 973c7457f..f6a349d33 100644 --- a/test/runtime/type/extends/array.ts +++ b/test/runtime/type/extends/array.ts @@ -6,31 +6,26 @@ describe('type/extends/Array', () => { // ---------------------------------------------- // Generic Varying // ---------------------------------------------- - it('Should extend Array Varying 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array Varying 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array Varying 3', () => { type T = Array extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array Varying 4', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ---------------------------------------------- // Any // ---------------------------------------------- @@ -39,275 +34,229 @@ describe('type/extends/Array', () => { const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ---------------------------------------------- // Constrained // ---------------------------------------------- - it('Should extend constrained Any', () => { type T = Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Void', () => { type T = Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts index 290eee289..c627b3a42 100644 --- a/test/runtime/type/extends/boolean.ts +++ b/test/runtime/type/extends/boolean.ts @@ -8,97 +8,81 @@ describe('type/extends/Boolean', () => { const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = boolean extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = boolean extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Number', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = boolean extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = boolean extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = boolean extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = boolean extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = boolean extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = boolean extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = boolean extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = boolean extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = boolean extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = boolean extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = boolean extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = boolean extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts index 5746dbab4..6d5b16ede 100644 --- a/test/runtime/type/extends/constructor.ts +++ b/test/runtime/type/extends/constructor.ts @@ -8,67 +8,56 @@ describe('type/extends/Constructor', () => { const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 2', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 3', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 4', () => { type T = (new (a: number) => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Constructor 5', () => { type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 6', () => { type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Constructor 7', () => { type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Constructor 8', () => { type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 9', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 9', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 10', () => { type T = (new () => Array) extends new () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) @@ -79,169 +68,141 @@ describe('type/extends/Constructor', () => { const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 12', () => { type T = (new () => object) extends new () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Constructor 13', () => { type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 14', () => { type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 15', () => { type T = (new () => any) extends new () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Constructor 16', () => { type T = (new () => unknown) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Any', () => { type T = (new () => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = (new () => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = (new () => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 2', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 3', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = (new () => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = (new () => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = (new () => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 3', () => { type T = (new () => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 4', () => { type T = (new () => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = (new () => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = (new () => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 4', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 5', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = (new () => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = (new () => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = (new () => number) extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = (new () => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts index 53e7bfc55..21b6653ac 100644 --- a/test/runtime/type/extends/date.ts +++ b/test/runtime/type/extends/date.ts @@ -8,37 +8,31 @@ describe('type/extends/Date', () => { const R = TypeExtends.Extends(Type.Date(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = Date extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = Date extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = Date extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = Date extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) @@ -49,61 +43,51 @@ describe('type/extends/Date', () => { const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = Date extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = Date extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = Date extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = Date extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = Date extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = Date extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null', () => { type T = Date extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = Date extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = Date extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = Date extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Date()) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts index 93abeb0d4..8e36b03ed 100644 --- a/test/runtime/type/extends/function.ts +++ b/test/runtime/type/extends/function.ts @@ -8,241 +8,201 @@ describe('type/extends/Function', () => { const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Function 1', () => { type T = (() => number) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 2', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 3', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 4', () => { type T = ((a: number) => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Function 5', () => { type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 6', () => { type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Function 7', () => { type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Function 8', () => { type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 9', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 9', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 10', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 11', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 12', () => { type T = (() => object) extends () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Function 13', () => { type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 14', () => { type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 15', () => { type T = (() => any) extends () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Function 16', () => { type T = (() => unknown) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Any', () => { type T = (() => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = (() => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = (() => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 2', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 3', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = (() => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = (() => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = (() => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 3', () => { type T = (() => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 4', () => { type T = (() => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 5', () => { type T = (() => number) extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 1', () => { type T = (() => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = (() => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 4', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 5', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = (() => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = (() => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = (() => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) diff --git a/test/runtime/type/extends/index.ts b/test/runtime/type/extends/index.ts index ae680a5a0..1bd1dc959 100644 --- a/test/runtime/type/extends/index.ts +++ b/test/runtime/type/extends/index.ts @@ -14,6 +14,7 @@ import './promise' import './record' import './string' import './symbol' +import './template-literal' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts index 844e1ffdb..d3c5e6016 100644 --- a/test/runtime/type/extends/integer.ts +++ b/test/runtime/type/extends/integer.ts @@ -8,103 +8,86 @@ describe('type/extends/Integer', () => { const R = TypeExtends.Extends(Type.Integer(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts index 71377e5e8..f11545ae2 100644 --- a/test/runtime/type/extends/literal.ts +++ b/test/runtime/type/extends/literal.ts @@ -11,37 +11,31 @@ describe('type/extends/Literal', () => { const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown (String)', () => { type T = 'hello' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String (String)', () => { type T = 'hello' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Boolean (String)', () => { type T = 'hello' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array (String)', () => { type T = 'hello' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) @@ -57,83 +51,69 @@ describe('type/extends/Literal', () => { const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2 (String)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1 (String)', () => { type T = 'hello' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2 (String)', () => { type T = 'hello' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3 (String)', () => { type T = 'hello' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null (String)', () => { type T = 'hello' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined (String)', () => { type T = 'hello' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ------------------------------------------------------------------- // Number Literal // ------------------------------------------------------------------- - it('Should extend Any (Number)', () => { type T = 10 extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown (Number)', () => { type T = 10 extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String (Number)', () => { type T = 10 extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean (Number)', () => { type T = 10 extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Integer (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array (Number)', () => { type T = 10 extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) @@ -149,161 +129,134 @@ describe('type/extends/Literal', () => { const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2 (Number)', () => { type T = 10 extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1 (Number)', () => { type T = 10 extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2 (Number)', () => { type T = 10 extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3 (Number)', () => { type T = 10 extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null (Number)', () => { type T = 10 extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined (Number)', () => { type T = 10 extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ------------------------------------------------------------------- // Boolean Literal // ------------------------------------------------------------------- - it('Should extend Any (Boolean)', () => { type T = true extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown (Boolean)', () => { type T = true extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String (Boolean)', () => { type T = true extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean (Boolean)', () => { type T = true extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Number (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array (Boolean)', () => { type T = true extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple (Boolean)', () => { type T = true extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 1', () => { type T = 'hello' extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 2', () => { type T = 10 extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 3', () => { type T = true extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1 (Boolean)', () => { type T = true extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2 (Boolean)', () => { type T = true extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1 (Boolean)', () => { type T = true extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2 (Boolean)', () => { type T = true extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3 (Boolean)', () => { type T = true extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null (Boolean)', () => { type T = true extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined (Boolean)', () => { type T = true extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = true extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = true extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts index 82931c96d..f2d793ab9 100644 --- a/test/runtime/type/extends/null.ts +++ b/test/runtime/type/extends/null.ts @@ -8,109 +8,91 @@ describe('type/extends/Null', () => { const R = TypeExtends.Extends(Type.Null(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = null extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = null extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = null extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = null extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = null extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = null extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = null extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 2', () => { type T = null extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 3', () => { type T = null extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = null extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = null extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = null extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null', () => { type T = null extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Undefined', () => { type T = null extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = null extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = null extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Date()) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts index 06e893f9d..3c61fbe38 100644 --- a/test/runtime/type/extends/number.ts +++ b/test/runtime/type/extends/number.ts @@ -8,103 +8,86 @@ describe('type/extends/Number', () => { const R = TypeExtends.Extends(Type.Number(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts index b8c6a24db..44833d85b 100644 --- a/test/runtime/type/extends/promise.ts +++ b/test/runtime/type/extends/promise.ts @@ -6,243 +6,202 @@ describe('type/extends/Promise', () => { // ---------------------------------------------- // Generic Varying // ---------------------------------------------- - it('Should extend Promise Varying 1', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Promise Varying 2', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Promise Varying 3', () => { type T = Promise extends Promise ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Promise Varying 4', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ---------------------------------------------- // Any // ---------------------------------------------- - it('Should extend Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = Promise extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - // ---------------------------------------------- // Constrained // ---------------------------------------------- - it('Should extend constrained Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend constrained Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend constrained Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = Promise extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = Promise extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) diff --git a/test/runtime/type/extends/record.ts b/test/runtime/type/extends/record.ts index 74a7920a8..a3dfa77ee 100644 --- a/test/runtime/type/extends/record.ts +++ b/test/runtime/type/extends/record.ts @@ -10,7 +10,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) @@ -18,7 +17,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -26,7 +24,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) @@ -34,7 +31,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) @@ -42,7 +38,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) @@ -50,7 +45,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) @@ -58,7 +52,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) @@ -66,7 +59,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -74,7 +66,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -82,7 +73,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) @@ -90,9 +80,7 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - // ----- - it('Should extend Record 12', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) @@ -100,7 +88,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) @@ -108,7 +95,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) @@ -116,7 +102,6 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) @@ -124,107 +109,89 @@ describe('type/extends/Record', () => { const R = TypeExtends.Extends(A, B) Assert.deepEqual(R, TypeExtendsResult.True) }) - // ------------------------------------------------------------------- // Standard // ------------------------------------------------------------------- - it('Should extend Any', () => { type T = Record extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = Record extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = Record extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = Record extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts index 1ba4aa486..1746be6e6 100644 --- a/test/runtime/type/extends/string.ts +++ b/test/runtime/type/extends/string.ts @@ -8,121 +8,101 @@ describe('type/extends/String', () => { const R = TypeExtends.Extends(Type.String(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = string extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = string extends string ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Boolean', () => { type T = string extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = string extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = string extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record 1', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 2', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 3', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Record 4', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = string extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = string extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) diff --git a/test/runtime/type/extends/template-literal.ts b/test/runtime/type/extends/template-literal.ts new file mode 100644 index 000000000..9abf5f547 --- /dev/null +++ b/test/runtime/type/extends/template-literal.ts @@ -0,0 +1,162 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/TemplateLiteral', () => { + // ------------------------------------------------------------------- + // String Literal 'hello' + // ------------------------------------------------------------------- + it('Should extend Any (hello)', () => { + type T = 'hello' extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown (hello)', () => { + type T = 'hello' extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String (hello)', () => { + type T = 'hello' extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Boolean (hello)', () => { + type T = 'hello' extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number (hello)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer (hello)', () => { + type T = 'hello' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array (hello)', () => { + type T = 'hello' extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple (hello)', () => { + type T = 'hello' extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1 (hello)', () => { + type T = 'hello' extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2 (hello)', () => { + type T = 'hello' extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1 (hello)', () => { + type T = 'hello' extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 2 (hello)', () => { + type T = 'hello' extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3 (hello)', () => { + type T = 'hello' extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Null (hello)', () => { + type T = 'hello' extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined (hello)', () => { + type T = 'hello' extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + // ------------------------------------------------------------------- + // String Literal 'hello' | 'world' + // ------------------------------------------------------------------- + it('Should extend Any (hello | world)', () => { + type T = 'hello' | 'world' extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown (hello | world)', () => { + type T = 'hello' | 'world' extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend String (hello | world)', () => { + type T = 'hello' | 'world' extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Boolean (hello | world)', () => { + type T = 'hello' | 'world' extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number (hello | world)', () => { + type T = 'hello' | 'world' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer (hello | world)', () => { + type T = 'hello' | 'world' extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array (hello | world)', () => { + type T = 'hello' | 'world' extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple (hello | world)', () => { + type T = 'hello' | 'world' extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1 (hello | world)', () => { + type T = 'hello' | 'world' extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Object 2 (hello | world)', () => { + type T = 'hello' | 'world' extends { a: 10 } ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1 (hello | world)', () => { + type T = 'hello' | 'world' extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 2 (hello | world)', () => { + type T = 'hello' | 'world' extends any | number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3 (hello | world)', () => { + type T = 'hello' | 'world' extends boolean | number ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Null (hello | world)', () => { + type T = 'hello' | 'world' extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined (hello | world)', () => { + type T = 'hello' | 'world' extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) + Assert.deepEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts index 756a93d53..2ffea0806 100644 --- a/test/runtime/type/extends/uint8array.ts +++ b/test/runtime/type/extends/uint8array.ts @@ -8,103 +8,86 @@ describe('type/extends/Uint8Array', () => { const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = Uint8Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = Uint8Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = Uint8Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = Uint8Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Record', () => { type T = Uint8Array extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 1', () => { type T = Uint8Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Object 2', () => { type T = Uint8Array extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = Uint8Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = Uint8Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = Uint8Array extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null', () => { type T = Uint8Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = Uint8Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = Uint8Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = Uint8Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts index 46a4061a5..f6b39edcb 100644 --- a/test/runtime/type/extends/undefined.ts +++ b/test/runtime/type/extends/undefined.ts @@ -8,97 +8,81 @@ describe('type/extends/Undefined', () => { const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = undefined extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = undefined extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array', () => { type T = undefined extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = undefined extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = undefined extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 2', () => { type T = undefined extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 3', () => { type T = undefined extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = undefined extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = undefined extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 2', () => { type T = undefined extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Null', () => { type T = undefined extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = undefined extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Date', () => { type T = undefined extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts index c37f4c65f..152cd71bd 100644 --- a/test/runtime/type/extends/unknown.ts +++ b/test/runtime/type/extends/unknown.ts @@ -8,109 +8,91 @@ describe('type/extends/Unknown', () => { const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = unknown extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts index d800fb08d..171994949 100644 --- a/test/runtime/type/extends/void.ts +++ b/test/runtime/type/extends/void.ts @@ -8,115 +8,96 @@ describe('type/extends/Void', () => { const R = TypeExtends.Extends(Type.Void(), Type.Any()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Unknown', () => { type T = void extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend String', () => { type T = void extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.String()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Boolean', () => { type T = void extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Number', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Number()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Integer', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Integer()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 1', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Array 2', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Tuple', () => { type T = void extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 1', () => { type T = void extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 2', () => { type T = void extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Object 3', () => { type T = void extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 1', () => { type T = void extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Union 2', () => { type T = void extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 3', () => { type T = void extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Union 4', () => { type T = void extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Null', () => { type T = void extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Null()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Undefined', () => { type T = void extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) Assert.deepEqual(R, TypeExtendsResult.False) }) - it('Should extend Void', () => { type T = void extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Void()) Assert.deepEqual(R, TypeExtendsResult.True) }) - it('Should extend Date', () => { type T = void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Date()) diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts index be3e62261..f85a18fad 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/exclude.ts @@ -21,4 +21,70 @@ describe('type/guard/TExclude', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) Assert.deepEqual(TypeGuard.TNumber(T), true) }) + // ------------------------------------------------------------------------ + // TemplateLiteral | TemplateLiteral + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(TypeGuard.TNever(T), true) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C'].includes(T.const), true) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | Union 1 + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Exclude(A, B) + Assert.deepEqual(TypeGuard.TNever(T), true) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C'].includes(T.const), true) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + }) + // ------------------------------------------------------------------------ + // Union | TemplateLiteral 1 + // ------------------------------------------------------------------------ + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(TypeGuard.TNever(T), true) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C'].includes(T.const), true) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + }) }) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts index f1caa3737..66914e7e7 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/extract.ts @@ -21,4 +21,76 @@ describe('type/guard/TExtract', () => { Assert.deepEqual(TypeGuard.TString(T.anyOf[0]), true) Assert.deepEqual(TypeGuard.TBoolean(T.anyOf[1]), true) }) + // ------------------------------------------------------------------------ + // TemplateLiteral | TemplateLiteral + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A'].includes(T.const), true) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | Union 1 + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A'].includes(T.const), true) + }) + // ------------------------------------------------------------------------ + // Union | TemplateLiteral 1 + // ------------------------------------------------------------------------ + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Assert.deepEqual(['A'].includes(T.const), true) + }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 28cc9f5c8..d9d8086c4 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -18,9 +18,10 @@ import './object' import './promise' import './record' import './ref' -import './self' import './string' import './symbol' +import './template-literal' +import './this' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/type/guard/self.ts b/test/runtime/type/guard/self.ts deleted file mode 100644 index d9059c1ba..000000000 --- a/test/runtime/type/guard/self.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('type/guard/TSelf', () => { - it('Should guard for TSelf', () => { - Type.Recursive((Node) => { - const R = TypeGuard.TSelf(Node) - Assert.equal(R, true) - return Type.Object({ nodes: Type.Array(Node) }) - }) - }) - it('Should guard for TSelf with invalid $ref', () => { - Type.Recursive((Node) => { - // @ts-ignore - Node.$ref = 1 - const R = TypeGuard.TSelf(Node) - Assert.equal(R, false) - return Type.Object({ nodes: Type.Array(Node) }) - }) - }) -}) diff --git a/test/runtime/type/guard/template-literal.ts b/test/runtime/type/guard/template-literal.ts new file mode 100644 index 000000000..4d988829d --- /dev/null +++ b/test/runtime/type/guard/template-literal.ts @@ -0,0 +1,47 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TTemplateLiteral', () => { + it('Should guard for empty TemplateLiteral', () => { + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([])) + Assert.equal(R, true) + }) + it('Should guard for TSchema', () => { + const R = TypeGuard.TSchema(Type.TemplateLiteral([])) + Assert.equal(R, true) + }) + it('Should guard for TemplateLiteral (TTemplateLiteral)', () => { + const T = Type.TemplateLiteral([Type.Literal('hello')]) + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) + Assert.equal(R, true) + }) + it('Should guard for TemplateLiteral (TLiteral)', () => { + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) + Assert.equal(R, true) + }) + it('Should guard for TemplateLiteral (TString)', () => { + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.String()])) + Assert.equal(R, true) + }) + it('Should guard for TemplateLiteral (TNumber)', () => { + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Number()])) + Assert.equal(R, true) + }) + it('Should guard for TemplateLiteral (TBoolean)', () => { + const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) + Assert.equal(R, true) + }) + it('Should not guard for missing ^ expression prefix', () => { + const T = Type.TemplateLiteral([Type.Literal('hello')]) + // @ts-ignore + T.pattern = T.pattern.slice(1) + Assert.equal(TypeGuard.TTemplateLiteral(T), false) + }) + it('Should not guard for missing $ expression postfix', () => { + const T = Type.TemplateLiteral([Type.Literal('hello')]) + // @ts-ignore + T.pattern = T.pattern.slice(0, T.pattern.length - 1) + Assert.equal(TypeGuard.TTemplateLiteral(T), false) + }) +}) diff --git a/test/runtime/type/guard/this.ts b/test/runtime/type/guard/this.ts new file mode 100644 index 000000000..2da48ed13 --- /dev/null +++ b/test/runtime/type/guard/this.ts @@ -0,0 +1,22 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TThis', () => { + it('Should guard for TThis', () => { + Type.Recursive((This) => { + const R = TypeGuard.TThis(This) + Assert.equal(R, true) + return Type.Object({ nodes: Type.Array(This) }) + }) + }) + it('Should guard for TThis with invalid $ref', () => { + Type.Recursive((This) => { + // @ts-ignore + This.$ref = 1 + const R = TypeGuard.TThis(This) + Assert.equal(R, false) + return Type.Object({ nodes: Type.Array(This) }) + }) + }) +}) diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index 2ec254a35..0d61b85fd 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -1,5 +1,6 @@ import './clone/index' import './extends/index' import './guard/index' +import './normalize/index' import './registry/index' -import './normal/index' +import './template/index' diff --git a/test/runtime/type/normal/exclude.ts b/test/runtime/type/normalize/exclude.ts similarity index 100% rename from test/runtime/type/normal/exclude.ts rename to test/runtime/type/normalize/exclude.ts diff --git a/test/runtime/type/normal/extract.ts b/test/runtime/type/normalize/extract.ts similarity index 100% rename from test/runtime/type/normal/extract.ts rename to test/runtime/type/normalize/extract.ts diff --git a/test/runtime/type/normal/index.ts b/test/runtime/type/normalize/index.ts similarity index 100% rename from test/runtime/type/normal/index.ts rename to test/runtime/type/normalize/index.ts diff --git a/test/runtime/type/normal/intersect.ts b/test/runtime/type/normalize/intersect.ts similarity index 100% rename from test/runtime/type/normal/intersect.ts rename to test/runtime/type/normalize/intersect.ts diff --git a/test/runtime/type/normal/record.ts b/test/runtime/type/normalize/record.ts similarity index 100% rename from test/runtime/type/normal/record.ts rename to test/runtime/type/normalize/record.ts diff --git a/test/runtime/type/normal/union.ts b/test/runtime/type/normalize/union.ts similarity index 100% rename from test/runtime/type/normal/union.ts rename to test/runtime/type/normalize/union.ts diff --git a/test/runtime/type/registry/format.ts b/test/runtime/type/registry/format.ts index 7f04d15d3..494160aec 100644 --- a/test/runtime/type/registry/format.ts +++ b/test/runtime/type/registry/format.ts @@ -1,22 +1,19 @@ import { FormatRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('format/Format', () => { +describe('type/FormatRegistry', () => { it('Should set format', () => { FormatRegistry.Set('test#format1', () => true) }) - it('Should get format', () => { FormatRegistry.Set('test#format2', () => true) const format = FormatRegistry.Get('test#format2') Assert.equal(typeof format, 'function') }) - it('Should return true if exists', () => { FormatRegistry.Set('test#format3', () => true) Assert.equal(FormatRegistry.Has('test#format3'), true) }) - it('Should clear formats', () => { FormatRegistry.Set('test#format4', () => true) FormatRegistry.Clear() diff --git a/test/runtime/type/registry/index.ts b/test/runtime/type/registry/index.ts index 78c71b23b..1afccae94 100644 --- a/test/runtime/type/registry/index.ts +++ b/test/runtime/type/registry/index.ts @@ -1 +1,2 @@ import './format' +import './type' diff --git a/test/runtime/type/registry/type.ts b/test/runtime/type/registry/type.ts new file mode 100644 index 000000000..5289d3d6d --- /dev/null +++ b/test/runtime/type/registry/type.ts @@ -0,0 +1,22 @@ +import { TypeRegistry } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/TypeRegistry', () => { + it('Should set type', () => { + TypeRegistry.Set('test#type1', () => true) + }) + it('Should get type', () => { + TypeRegistry.Set('test#type2', () => true) + const format = TypeRegistry.Get('test#type2') + Assert.equal(typeof format, 'function') + }) + it('Should return true if exists', () => { + TypeRegistry.Set('test#type3', () => true) + Assert.equal(TypeRegistry.Has('test#type3'), true) + }) + it('Should clear types', () => { + TypeRegistry.Set('test#type4', () => true) + TypeRegistry.Clear() + Assert.equal(TypeRegistry.Has('test#type4'), false) + }) +}) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts new file mode 100644 index 000000000..e44c8164f --- /dev/null +++ b/test/runtime/type/template/finite.ts @@ -0,0 +1,61 @@ +import { PatternString, PatternBoolean, PatternNumber, TemplateLiteralParser, TemplateLiteralFinite } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/TemplateLiteralFinite', () => { + // --------------------------------------------------------------- + // Finite + // --------------------------------------------------------------- + it('Finite 1', () => { + const E = TemplateLiteralParser.Parse(`A`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, true) + }) + it('Finite 2', () => { + const E = TemplateLiteralParser.Parse(`A|B`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, true) + }) + it('Finite 3', () => { + const E = TemplateLiteralParser.Parse(`A(B|C)`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, true) + }) + it('Finite 4', () => { + const E = TemplateLiteralParser.Parse(`${PatternBoolean}`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, true) + }) + // --------------------------------------------------------------- + // Infinite + // --------------------------------------------------------------- + it('Infinite 1', () => { + const E = TemplateLiteralParser.Parse(`${PatternString}`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) + it('Infinite 2', () => { + const E = TemplateLiteralParser.Parse(`${PatternNumber}`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) + it('Infinite 3', () => { + const E = TemplateLiteralParser.Parse(`A|${PatternString}`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) + it('Infinite 4', () => { + const E = TemplateLiteralParser.Parse(`A|${PatternNumber}`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) + it('Infinite 5', () => { + const E = TemplateLiteralParser.Parse(`A(${PatternString})`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) + it('Infinite 6', () => { + const E = TemplateLiteralParser.Parse(`A(${PatternNumber})`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) +}) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts new file mode 100644 index 000000000..587e6cb9c --- /dev/null +++ b/test/runtime/type/template/generate.ts @@ -0,0 +1,199 @@ +import { TemplateLiteralParser, TemplateLiteralGenerator } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/TemplateLiteralGenerator', () => { + // --------------------------------------------------------------- + // Exact (No Default Unwrap) + // --------------------------------------------------------------- + it('Exact 1', () => { + const E = TemplateLiteralParser.Parse('^$') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['^$']) + }) + it('Exact 2', () => { + const E = TemplateLiteralParser.Parse('^A$') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['^A$']) + }) + // --------------------------------------------------------------- + // Patterns + // --------------------------------------------------------------- + it('Pattern 1', () => { + const E = TemplateLiteralParser.Parse('(true|false)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['true', 'false']) + }) + it('Pattern 2', () => { + const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['0', '[1-9][0-9]*']) + }) + it('Pattern 3', () => { + const E = TemplateLiteralParser.Parse('.*') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['.*']) + }) + // --------------------------------------------------------------- + // Expression + // --------------------------------------------------------------- + it('Expression 1', () => { + const E = TemplateLiteralParser.Parse(')') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, [')']) + }) + it('Expression 2', () => { + const E = TemplateLiteralParser.Parse('\\)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, [')']) + }) + it('Expression 3', () => { + const E = TemplateLiteralParser.Parse('\\(') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['(']) + }) + it('Expression 4', () => { + const E = TemplateLiteralParser.Parse('') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['']) + }) + it('Expression 5', () => { + const E = TemplateLiteralParser.Parse('\\') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['']) + }) + it('Expression 6', () => { + const E = TemplateLiteralParser.Parse('()') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['']) + }) + it('Expression 7', () => { + const E = TemplateLiteralParser.Parse('(a)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['a']) + }) + it('Expression 8', () => { + const E = TemplateLiteralParser.Parse('()))') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['))']) + }) + it('Expression 9', () => { + const E = TemplateLiteralParser.Parse('())') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, [')']) + }) + it('Expression 10', () => { + const E = TemplateLiteralParser.Parse('A|B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', 'B']) + }) + it('Expression 11', () => { + const E = TemplateLiteralParser.Parse('A|(B)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', 'B']) + }) + it('Expression 12', () => { + const E = TemplateLiteralParser.Parse('A(B)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['AB']) + }) + it('Expression 13', () => { + const E = TemplateLiteralParser.Parse('(A)B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['AB']) + }) + it('Expression 14', () => { + const E = TemplateLiteralParser.Parse('(A)|B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', 'B']) + }) + it('Expression 15', () => { + const E = TemplateLiteralParser.Parse('|') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['']) + }) + it('Expression 16', () => { + const E = TemplateLiteralParser.Parse('||') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['']) + }) + it('Expression 17', () => { + const E = TemplateLiteralParser.Parse('||A') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A']) + }) + it('Expression 18', () => { + const E = TemplateLiteralParser.Parse('A||') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A']) + }) + it('Expression 19', () => { + const E = TemplateLiteralParser.Parse('A||B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', 'B']) + }) + it('Expression 20', () => { + const E = TemplateLiteralParser.Parse('A|()|B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', '', 'B']) + }) + it('Expression 21', () => { + const E = TemplateLiteralParser.Parse('A|(|)|B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', '', 'B']) + }) + it('Expression 22', () => { + const E = TemplateLiteralParser.Parse('A|(||)|B') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', '', 'B']) + }) + it('Expression 23', () => { + const E = TemplateLiteralParser.Parse('|A(||)B|') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['AB']) + }) + it('Expression 24', () => { + const E = TemplateLiteralParser.Parse('A(B)(C)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['ABC']) + }) + it('Expression 25', () => { + const E = TemplateLiteralParser.Parse('A(B)|(C)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['AB', 'C']) + }) + it('Expression 26', () => { + const E = TemplateLiteralParser.Parse('A(B|C)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['AB', 'AC']) + }) + it('Expression 27', () => { + const E = TemplateLiteralParser.Parse('A|(B|C)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['A', 'B', 'C']) + }) + it('Expression 28', () => { + const E = TemplateLiteralParser.Parse('((A)B)C') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['ABC']) + }) + it('Expression 29', () => { + const E = TemplateLiteralParser.Parse('(0|1)(0|1)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['00', '01', '10', '11']) + }) + it('Expression 30', () => { + const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['0', '1', '0', '1']) + }) + it('Expression 31', () => { + const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['0', '1', '0', '1']) + }) + it('Expression 32', () => { + const E = TemplateLiteralParser.Parse('(0(1|0)1)') + const R = [...TemplateLiteralGenerator.Generate(E)] + Assert.deepEqual(R, ['011', '001']) + }) +}) diff --git a/test/runtime/type/template/index.ts b/test/runtime/type/template/index.ts new file mode 100644 index 000000000..032be5db6 --- /dev/null +++ b/test/runtime/type/template/index.ts @@ -0,0 +1,4 @@ +import './finite' +import './generate' +import './parser' +import './pattern' diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts new file mode 100644 index 000000000..682a5b912 --- /dev/null +++ b/test/runtime/type/template/parser.ts @@ -0,0 +1,607 @@ +import { TemplateLiteralParser } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/TemplateLiteralParser', () => { + // --------------------------------------------------------------- + // Throws + // --------------------------------------------------------------- + it('Throw 1', () => { + Assert.throws(() => TemplateLiteralParser.Parse('(')) + }) + it('Throw 2', () => { + Assert.throws(() => TemplateLiteralParser.Parse('(')) + }) + // --------------------------------------------------------------- + // Exact (No Default Unwrap) + // --------------------------------------------------------------- + it('Exact 1', () => { + const E = TemplateLiteralParser.Parse('^$') + Assert.deepEqual(E, { + type: 'const', + const: '^$', + }) + }) + it('Exact 2', () => { + const E = TemplateLiteralParser.Parse('^A$') + Assert.deepEqual(E, { + type: 'const', + const: '^A$', + }) + }) + // --------------------------------------------------------------- + // Patterns + // --------------------------------------------------------------- + it('Pattern 1', () => { + const E = TemplateLiteralParser.Parse('(true|false)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'true', + }, + { + type: 'const', + const: 'false', + }, + ], + }) + }) + it('Pattern 2', () => { + const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'const', + const: '[1-9][0-9]*', + }, + ], + }) + }) + it('Pattern 3', () => { + const E = TemplateLiteralParser.Parse('.*') + Assert.deepEqual(E, { + type: 'const', + const: '.*', + }) + }) + // --------------------------------------------------------------- + // Expression + // --------------------------------------------------------------- + it('Expression 1', () => { + const E = TemplateLiteralParser.Parse(')') + Assert.deepEqual(E, { + type: 'const', + const: ')', + }) + }) + it('Expression 2', () => { + const E = TemplateLiteralParser.Parse('\\)') + Assert.deepEqual(E, { + type: 'const', + const: ')', + }) + }) + it('Expression 3', () => { + const E = TemplateLiteralParser.Parse('\\(') + Assert.deepEqual(E, { + type: 'const', + const: '(', + }) + }) + it('Expression 4', () => { + const E = TemplateLiteralParser.Parse('') + Assert.deepEqual(E, { + type: 'const', + const: '', + }) + }) + it('Expression 5', () => { + const E = TemplateLiteralParser.Parse('\\') + Assert.deepEqual(E, { + type: 'const', + const: '', + }) + }) + it('Expression 6', () => { + const E = TemplateLiteralParser.Parse('()') + Assert.deepEqual(E, { + type: 'const', + const: '', + }) + }) + it('Expression 7', () => { + const E = TemplateLiteralParser.Parse('(a)') + Assert.deepEqual(E, { + type: 'const', + const: 'a', + }) + }) + it('Expression 8', () => { + const E = TemplateLiteralParser.Parse('()))') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: '', + }, + { + type: 'const', + const: '))', + }, + ], + }) + }) + it('Expression 9', () => { + const E = TemplateLiteralParser.Parse('())') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: '', + }, + { + type: 'const', + const: ')', + }, + ], + }) + }) + it('Expression 10', () => { + const E = TemplateLiteralParser.Parse('A|B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 11', () => { + const E = TemplateLiteralParser.Parse('A|(B)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 12', () => { + const E = TemplateLiteralParser.Parse('A(B)') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 13', () => { + const E = TemplateLiteralParser.Parse('(A)B') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 14', () => { + const E = TemplateLiteralParser.Parse('(A)|B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 15', () => { + const E = TemplateLiteralParser.Parse('|') + Assert.deepEqual(E, { + type: 'const', + const: '', + }) + }) + it('Expression 16', () => { + const E = TemplateLiteralParser.Parse('||') + Assert.deepEqual(E, { + type: 'const', + const: '', + }) + }) + it('Expression 17', () => { + const E = TemplateLiteralParser.Parse('||A') + Assert.deepEqual(E, { + type: 'const', + const: 'A', + }) + }) + it('Expression 18', () => { + const E = TemplateLiteralParser.Parse('A||') + Assert.deepEqual(E, { + type: 'const', + const: 'A', + }) + }) + it('Expression 19', () => { + const E = TemplateLiteralParser.Parse('A||B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 20', () => { + const E = TemplateLiteralParser.Parse('A|()|B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: '', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 21', () => { + const E = TemplateLiteralParser.Parse('A|(|)|B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: '', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 22', () => { + const E = TemplateLiteralParser.Parse('A|(||)|B') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: '', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 23', () => { + const E = TemplateLiteralParser.Parse('|A(||)B|') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: '', + }, + { + type: 'const', + const: 'B', + }, + ], + }) + }) + it('Expression 24', () => { + const E = TemplateLiteralParser.Parse('A(B)(C)') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + { + type: 'const', + const: 'C', + }, + ], + }) + }) + it('Expression 25', () => { + const E = TemplateLiteralParser.Parse('A(B)|(C)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }, + { + type: 'const', + const: 'C', + }, + ], + }) + }) + it('Expression 26', () => { + const E = TemplateLiteralParser.Parse('A(B|C)') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: 'B', + }, + { + type: 'const', + const: 'C', + }, + ], + }, + ], + }) + }) + it('Expression 27', () => { + const E = TemplateLiteralParser.Parse('A|(B|C)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: 'B', + }, + { + type: 'const', + const: 'C', + }, + ], + }, + ], + }) + }) + it('Expression 28', () => { + const E = TemplateLiteralParser.Parse('((A)B)C') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'and', + expr: [ + { + type: 'const', + const: 'A', + }, + { + type: 'const', + const: 'B', + }, + ], + }, + { + type: 'const', + const: 'C', + }, + ], + }) + }) + it('Expression 29', () => { + const E = TemplateLiteralParser.Parse('(0|1)(0|1)') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'const', + const: '1', + }, + ], + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'const', + const: '1', + }, + ], + }, + ], + }) + }) + it('Expression 30', () => { + const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'const', + const: '1', + }, + ], + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'const', + const: '1', + }, + ], + }, + ], + }) + }) + it('Expression 31', () => { + const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') + Assert.deepEqual(E, { + type: 'or', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: '1', + }, + { + type: 'const', + const: '0', + }, + ], + }, + { + type: 'const', + const: '1', + }, + ], + }) + }) + it('Expression 32', () => { + const E = TemplateLiteralParser.Parse('(0(1|0)1)') + Assert.deepEqual(E, { + type: 'and', + expr: [ + { + type: 'const', + const: '0', + }, + { + type: 'or', + expr: [ + { + type: 'const', + const: '1', + }, + { + type: 'const', + const: '0', + }, + ], + }, + { + type: 'const', + const: '1', + }, + ], + }) + }) +}) diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template/pattern.ts new file mode 100644 index 000000000..1936c8211 --- /dev/null +++ b/test/runtime/type/template/pattern.ts @@ -0,0 +1,145 @@ +import { Type, TTemplateLiteral, PatternNumber, PatternString, PatternBoolean } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/TemplateLiteralPattern', () => { + const Equal = (template: TTemplateLiteral, expect: string) => { + const pattern = template.pattern.slice(1, template.pattern.length - 1) + Assert.equal(pattern, expect) + } + // --------------------------------------------------------------- + // Pattern + // --------------------------------------------------------------- + it('Pattern 1', () => { + const T = Type.TemplateLiteral([Type.Number()]) + Equal(T, `${PatternNumber}`) + }) + it('Pattern 2', () => { + const T = Type.TemplateLiteral([Type.String()]) + Equal(T, `${PatternString}`) + }) + it('Pattern 3', () => { + const T = Type.TemplateLiteral([Type.Boolean()]) + Equal(T, `${PatternBoolean}`) + }) + it('Pattern 4', () => { + const T = Type.TemplateLiteral([Type.Literal('A'), Type.Number()]) + Equal(T, `A${PatternNumber}`) + }) + it('Pattern 5', () => { + const T = Type.TemplateLiteral([Type.Literal('A'), Type.String()]) + Equal(T, `A${PatternString}`) + }) + it('Pattern 6', () => { + const T = Type.TemplateLiteral([Type.Literal('A'), Type.Boolean()]) + Equal(T, `A${PatternBoolean}`) + }) + it('Pattern 7', () => { + const T = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Number()])]) + Equal(T, `(A|${PatternNumber})`) + }) + it('Pattern 8', () => { + const T = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.String()])]) + Equal(T, `(A|${PatternString})`) + }) + it('Pattern 9', () => { + const T = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Boolean()])]) + Equal(T, `(A|${PatternBoolean})`) + }) + // --------------------------------------------------------------- + // Template + // --------------------------------------------------------------- + it('Template 1', () => { + const T = Type.TemplateLiteral([]) + Equal(T, '') + }) + it('Template 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A') + ]) + Equal(T, 'A') + }) + it('Template 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Literal('B'), + ]) + Equal(T, 'AB') + }) + it('Template 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + ]) + ]) + Equal(T, 'AB') + }) + it('Template 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C'), + ]) + ]) + Equal(T, 'A(B|C)') + }) + it('Template 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C'), + ]), + Type.Union([ + Type.Literal('D'), + Type.Literal('E'), + ]) + ]) + Equal(T, 'A(B|C)(D|E)') + }) + it('Template 7', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.TemplateLiteral([Type.Literal('A')]), + Type.TemplateLiteral([Type.Literal('B')]) + ]) + Equal(T, 'AB') + }) + it('Template 8', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.TemplateLiteral([ + Type.Literal('B'), + Type.Literal('C'), + ]), + Type.Literal('D') + ]), + ]) + Equal(T, 'A(BC|D)') + }) + it('Template 9', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.TemplateLiteral([ + Type.Literal('B'), + Type.Literal('C'), + ]), + Type.Union([ + Type.Literal('D'), + Type.Literal('E') + ]) + ]), + ]) + Equal(T, 'A(BC|(D|E))') + }) +}) diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts index 815a8e208..2c8d49477 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/custom.ts @@ -3,7 +3,12 @@ import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Custom', () => { - TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') + before(() => { + TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') + }) + after(() => { + TypeRegistry.Clear() + }) const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' }) const E = 'hello' it('Should upcast from string', () => { diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 238aa9688..8db2c8711 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -20,6 +20,7 @@ import './record' import './regex' import './string' import './symbol' +import './template-literal' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/cast/recursive.ts b/test/runtime/value/cast/recursive.ts index 39a0a9f68..78cc5c220 100644 --- a/test/runtime/value/cast/recursive.ts +++ b/test/runtime/value/cast/recursive.ts @@ -3,10 +3,10 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Recursive', () => { - const T = Type.Recursive((Self) => + const T = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) const E = { id: '', nodes: [] } @@ -63,17 +63,17 @@ describe('value/cast/Recursive', () => { Assert.deepEqual(result, value) }) it('Should upcast from varying types', () => { - const TypeA = Type.Recursive((Self) => + const TypeA = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) - const TypeB = Type.Recursive((Self) => + const TypeB = Type.Recursive((This) => Type.Object({ id: Type.String(), name: Type.String({ default: 'test' }), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) const ValueA = { diff --git a/test/runtime/value/cast/template-literal.ts b/test/runtime/value/cast/template-literal.ts new file mode 100644 index 000000000..b2255bdfe --- /dev/null +++ b/test/runtime/value/cast/template-literal.ts @@ -0,0 +1,53 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/TemplateLiteral', () => { + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Literal('world')]) + const E = 'helloworld' + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from boolean', () => { + const value = true + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.deepEqual(result, E) + }) + it('Should preseve', () => { + const value = 'helloworld' + const result = Value.Cast(T, value) + Assert.deepEqual(result, 'helloworld') + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 1fc8801be..f5cc52770 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -21,6 +21,7 @@ import './record' import './regex' import './string' import './symbol' +import './template-literal' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/check/recursive.ts b/test/runtime/value/check/recursive.ts index 4659b7f8c..79f56d1cd 100644 --- a/test/runtime/value/check/recursive.ts +++ b/test/runtime/value/check/recursive.ts @@ -3,10 +3,10 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/check/Recursive', () => { - const T = Type.Recursive((Self) => + const T = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) diff --git a/test/runtime/value/check/template-literal.ts b/test/runtime/value/check/template-literal.ts new file mode 100644 index 000000000..7208578c7 --- /dev/null +++ b/test/runtime/value/check/template-literal.ts @@ -0,0 +1,187 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/TemplateLiteral', () => { + // -------------------------------------------------------- + // Finite + // -------------------------------------------------------- + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([]) + Assert.equal(Value.Check(T, ''), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([Type.Boolean()]) + Assert.equal(Value.Check(T, 'true'), true) + Assert.equal(Value.Check(T, 'false'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A') + ]) + Assert.equal(Value.Check(T, 'A'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Literal('B') + ]) + Assert.equal(Value.Check(T, 'AB'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + ]) + Assert.equal(Value.Check(T, 'AB'), true) + Assert.equal(Value.Check(T, 'AC'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Union([ + Type.Literal('B'), + Type.Literal('C') + ]), + Type.Literal('D'), + ]) + Assert.equal(Value.Check(T, 'ABD'), true) + Assert.equal(Value.Check(T, 'ACD'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate finite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + Type.Union([ + Type.Literal('0'), + Type.Literal('1') + ]), + ]) + Assert.equal(Value.Check(T, '00'), true) + Assert.equal(Value.Check(T, '01'), true) + Assert.equal(Value.Check(T, '10'), true) + Assert.equal(Value.Check(T, '11'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + // -------------------------------------------------------- + // Infinite + // -------------------------------------------------------- + it('Should validate infinite pattern 1', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Number() + ]) + Assert.equal(Value.Check(T, '1'), true) + Assert.equal(Value.Check(T, '22'), true) + Assert.equal(Value.Check(T, '333'), true) + Assert.equal(Value.Check(T, '4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 2', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Integer() + ]) + Assert.equal(Value.Check(T, '1'), true) + Assert.equal(Value.Check(T, '22'), true) + Assert.equal(Value.Check(T, '333'), true) + Assert.equal(Value.Check(T, '4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 3', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.BigInt() + ]) + Assert.equal(Value.Check(T, '1'), true) + Assert.equal(Value.Check(T, '22'), true) + Assert.equal(Value.Check(T, '333'), true) + Assert.equal(Value.Check(T, '4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 4', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.String() + ]) + Assert.equal(Value.Check(T, '1'), true) + Assert.equal(Value.Check(T, '22'), true) + Assert.equal(Value.Check(T, '333'), true) + Assert.equal(Value.Check(T, '4444'), true) + Assert.equal(Value.Check(T, 'a'), true) + Assert.equal(Value.Check(T, 'bb'), true) + Assert.equal(Value.Check(T, 'ccc'), true) + Assert.equal(Value.Check(T, 'dddd'), true) + }) + + it('Should validate infinite pattern 5', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Number() + ]) + Assert.equal(Value.Check(T, 'A1'), true) + Assert.equal(Value.Check(T, 'A22'), true) + Assert.equal(Value.Check(T, 'A333'), true) + Assert.equal(Value.Check(T, 'A4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 6', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.Integer() + ]) + Assert.equal(Value.Check(T, 'A1'), true) + Assert.equal(Value.Check(T, 'A22'), true) + Assert.equal(Value.Check(T, 'A333'), true) + Assert.equal(Value.Check(T, 'A4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 7', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.BigInt() + ]) + Assert.equal(Value.Check(T, 'A1'), true) + Assert.equal(Value.Check(T, 'A22'), true) + Assert.equal(Value.Check(T, 'A333'), true) + Assert.equal(Value.Check(T, 'A4444'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) + it('Should validate infinite pattern 8', () => { + // prettier-ignore + const T = Type.TemplateLiteral([ + Type.Literal('A'), + Type.String() + ]) + Assert.equal(Value.Check(T, 'A1'), true) + Assert.equal(Value.Check(T, 'A22'), true) + Assert.equal(Value.Check(T, 'A333'), true) + Assert.equal(Value.Check(T, 'A4444'), true) + Assert.equal(Value.Check(T, 'Aa'), true) + Assert.equal(Value.Check(T, 'Abb'), true) + Assert.equal(Value.Check(T, 'Accc'), true) + Assert.equal(Value.Check(T, 'Adddd'), true) + Assert.equal(Value.Check(T, 'X'), false) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index fb947093e..26caee56d 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -22,6 +22,7 @@ import './record' import './regex' import './string' import './symbol' +import './template-literal' import './tuple' import './uint8array' import './undefined' diff --git a/test/runtime/value/create/recursive.ts b/test/runtime/value/create/recursive.ts index 070169062..7f69c6b56 100644 --- a/test/runtime/value/create/recursive.ts +++ b/test/runtime/value/create/recursive.ts @@ -4,10 +4,10 @@ import { Assert } from '../../assert/index' describe('value/create/Recursive', () => { it('Should create value', () => { - const T = Type.Recursive((Self) => + const T = Type.Recursive((This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), ) Assert.deepEqual(Value.Create(T), { @@ -18,10 +18,10 @@ describe('value/create/Recursive', () => { it('Should create default', () => { const T = Type.Recursive( - (Self) => + (This) => Type.Object({ id: Type.String(), - nodes: Type.Array(Self), + nodes: Type.Array(This), }), { default: 7 }, ) diff --git a/test/runtime/value/create/template-literal.ts b/test/runtime/value/create/template-literal.ts new file mode 100644 index 000000000..4659b77f8 --- /dev/null +++ b/test/runtime/value/create/template-literal.ts @@ -0,0 +1,35 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/TemplateLiteral', () => { + it('Should create pattern 1', () => { + const T = Type.TemplateLiteral([Type.Literal('A')]) + const V = Value.Create(T) + Assert.deepEqual(V, 'A') + }) + it('Should create pattern 2', () => { + const T = Type.TemplateLiteral([Type.Literal('A'), Type.Literal('B')]) + const V = Value.Create(T) + Assert.deepEqual(V, 'AB') + }) + it('Should create pattern 3 (first only)', () => { + const T = Type.TemplateLiteral([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) + const V = Value.Create(T) + Assert.deepEqual(V, 'AB') + }) + it('Should create pattern 4 (first only)', () => { + const T = Type.TemplateLiteral([Type.Boolean()]) + const V = Value.Create(T) + Assert.deepEqual(V, 'true') + }) + it('Should throw on infinite pattern', () => { + const T = Type.TemplateLiteral([Type.Number()]) + Assert.throws(() => Value.Create(T)) + }) + it('Should create on infinite pattern with default', () => { + const T = Type.TemplateLiteral([Type.Number()], { default: 42 }) + const V = Value.Create(T) + Assert.deepEqual(V, 42) + }) +}) diff --git a/test/static/exclude.ts b/test/static/exclude.ts index b0a76354c..526e7bc60 100644 --- a/test/static/exclude.ts +++ b/test/static/exclude.ts @@ -1,4 +1,4 @@ -import { Type } from '@sinclair/typebox' +import { Type, TLiteral, TUnion } from '@sinclair/typebox' import { Expect } from './assert' { @@ -13,3 +13,72 @@ import { Expect } from './assert' const T = Type.Exclude(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) Expect(T).ToBe() } +// ------------------------------------------------------------------------ +// TemplateLiteral | TemplateLiteral +// ------------------------------------------------------------------------ +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C' | 'B'>() +} +// ------------------------------------------------------------------------ +// TemplateLiteral | Union +// ------------------------------------------------------------------------ +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C' | 'B'>() +} +// ------------------------------------------------------------------------ +// Union | TemplateLiteral +// ------------------------------------------------------------------------ +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe() +} +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C'>() +} +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Expect(T).ToBe<'C' | 'B'>() +} diff --git a/test/static/extract.ts b/test/static/extract.ts index 7cc603709..9e4073836 100644 --- a/test/static/extract.ts +++ b/test/static/extract.ts @@ -1,4 +1,4 @@ -import { Type } from '@sinclair/typebox' +import { Type, TLiteral, TUnion } from '@sinclair/typebox' import { Expect } from './assert' { @@ -7,9 +7,77 @@ import { Expect } from './assert' } { const T = Type.Extract(Type.String(), Type.Number()) - Expect(T).ToBe() + Expect(T).ToBe() } { const T = Type.Extract(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) Expect(T).ToBe() } +// ------------------------------------------------------------------------ +// TemplateLiteral | TemplateLiteral +// ------------------------------------------------------------------------ +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B' | 'C'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Expect(T).ToBe<'A'>() +} +// ------------------------------------------------------------------------ +// TemplateLiteral | Union +// ------------------------------------------------------------------------ +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B' | 'C'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B'>() +} +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Extract(A, B) + Expect(T).ToBe<'A'>() +} +// ------------------------------------------------------------------------ +// Union | TemplateLiteral +// ------------------------------------------------------------------------ +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B' | 'C'>() +} +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + + const T = Type.Extract(A, B) + Expect(T).ToBe<'A' | 'B'>() +} +{ + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Expect(T).ToBe<'A'>() +} diff --git a/test/static/index.ts b/test/static/index.ts index 8666ca223..70f003457 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -37,6 +37,7 @@ import './return-type' import './strict' import './string' import './symbol' +import './template-literal' import './tuple' import './union' import './unknown' diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts new file mode 100644 index 000000000..c46d94af3 --- /dev/null +++ b/test/static/template-literal.ts @@ -0,0 +1,44 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +{ + // Empty + const T = Type.TemplateLiteral([]) + Expect(T).ToInfer<''>() +} +{ + // Literal + const T = Type.TemplateLiteral([Type.Literal('hello')]) + Expect(T).ToInfer<'hello'>() +} +{ + // And Sequence + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Literal('world')]) + Expect(T).ToInfer<'helloworld'>() +} +{ + // And / Or Sequence + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('1'), Type.Literal('2')])]) + Expect(T).ToInfer<'hello1' | 'hello2'>() +} +{ + // Auxiliary Template + const A = Type.TemplateLiteral([Type.Union([Type.Literal('1'), Type.Literal('2')])]) + const T = Type.TemplateLiteral([Type.Literal('hello'), A]) + Expect(T).ToInfer<'hello1' | 'hello2'>() +} +{ + // String + const T = Type.TemplateLiteral([Type.String()]) + Expect(T).ToInfer<`${string}`>() +} +{ + // Number + const T = Type.TemplateLiteral([Type.Number()]) + Expect(T).ToInfer<`${number}`>() +} +{ + // Boolean + const T = Type.TemplateLiteral([Type.Boolean()]) + Expect(T).ToInfer<`${boolean}`>() +} From cffaac6395120044d9839f093e5c6abe7faaec55 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 9 Apr 2023 01:50:21 +0900 Subject: [PATCH 096/369] Workbench Compiler --- example/codegen/typescript-to-typebox.ts | 18 ++++++++++-------- example/index.ts | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/example/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts index 953f8a2be..7f69b2451 100644 --- a/example/codegen/typescript-to-typebox.ts +++ b/example/codegen/typescript-to-typebox.ts @@ -94,22 +94,24 @@ export namespace TypeScriptToTypeBox { // ------------------------------------------------------------------------- // TemplateLiteral // ------------------------------------------------------------------------- + // prettier-ignore function* TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { - const collect = node.getChildren().map(node => Collect(node)) - yield `Type.TemplateLiteral([${collect.join('')}])` + const collect = node.getChildren().map(node => Collect(node)).join('') + yield `Type.TemplateLiteral([${collect.slice(0, collect.length - 2)}])` // can't remove trailing here } + // prettier-ignore function* TemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { - const collect = node.getChildren().map(node => Collect(node)) - yield collect.join(', ') + const collect = node.getChildren().map(node => Collect(node)).join(', ') + if(collect.length > 0) yield `${collect}` } function* TemplateHead(node: ts.TemplateHead) { - yield `Type.Literal('${node.text}'), ` + if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` } function* TemplateMiddle(node: ts.TemplateMiddle) { - yield `Type.Literal('${node.text}'), ` + if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` } function* TemplateTail(node: ts.TemplateTail) { - yield `Type.Literal('${node.text}')` + if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` } function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { const types = node.types.map((type) => Collect(type)).join(',\n') @@ -378,4 +380,4 @@ export namespace TypeScriptToTypeBox { const types = Formatter.Format(typeDeclarations) return [imports, '', types].join('\n') } -} \ No newline at end of file +} diff --git a/example/index.ts b/example/index.ts index c5ad5a144..ed52156bd 100644 --- a/example/index.ts +++ b/example/index.ts @@ -37,4 +37,4 @@ console.log(C.Code()) // Check: Value // ----------------------------------------------------------- -console.log(C.Check(V)) \ No newline at end of file +console.log(C.Check(V)) From 5454181f142483227b0046c845688951aeb2fb98 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 10 Apr 2023 20:19:30 +0900 Subject: [PATCH 097/369] Revision 0.27.1 (#372) --- changelog/0.27.1.md | 5 ++ package-lock.json | 4 +- package.json | 2 +- readme.md | 34 ++++++++- src/value/index.ts | 1 + src/value/mutate.ts | 110 ++++++++++++++++++++++++++++ src/value/value.ts | 5 ++ test/runtime/assert/assert.ts | 10 +-- test/runtime/value/index.ts | 1 + test/runtime/value/mutate/index.ts | 1 + test/runtime/value/mutate/mutate.ts | 88 ++++++++++++++++++++++ 11 files changed, 245 insertions(+), 16 deletions(-) create mode 100644 changelog/0.27.1.md create mode 100644 src/value/mutate.ts create mode 100644 test/runtime/value/mutate/index.ts create mode 100644 test/runtime/value/mutate/mutate.ts diff --git a/changelog/0.27.1.md b/changelog/0.27.1.md new file mode 100644 index 000000000..016f05ced --- /dev/null +++ b/changelog/0.27.1.md @@ -0,0 +1,5 @@ +## [0.27.1](https://www.npmjs.com/package/@sinclair/typebox/v/0.27.1) + +## Updates + +- Adds a `Value.Mutate(current, next): void` function. This function performs a deep mutable assignment on a value by internally remapping the `next(right)` values on the `current(left)`. Values omitted on the right will also be deleted on the left. This function can be useful instances where mutation of data is required without modification to existing references. An example of which might be React which tracks reference values to signal redraws. This function is implemented by way of `ValuePointer`. diff --git a/package-lock.json b/package-lock.json index 64dbe8148..23096db77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.0", + "version": "0.27.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.0", + "version": "0.27.1", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 6b792d3e1..546a220e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.0", + "version": "0.27.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index b5cb73687..a5a76d424 100644 --- a/readme.md +++ b/readme.md @@ -96,6 +96,7 @@ License MIT - [Diff](#values-diff) - [Patch](#values-patch) - [Errors](#values-errors) + - [Mutate](#values-mutate) - [Pointer](#values-pointer) - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) @@ -1053,7 +1054,6 @@ const E = Value.Diff(A, B) // const E = [ const C = Value.Patch(A, E) // const C = { x: 3 } ``` - ### Errors @@ -1076,6 +1076,29 @@ const R = [...Value.Errors(T, { x: '42' })] // const R = [{ // }] ``` + + +### Mutate + +Use the Mutate function to perform a deep mutable value assignment while retaining internal references. + +```typescript +const Y = { z: 1 } // const Y = { z: 1 } + +const X = { y: Y } // const X = { y: { z: 1 } } + +const A = { x: X } // const A = { x: { y: { z: 1 } } } + + +Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } + +const R0 = A.x.y.z === 2 // const R0 = 2 + +const R1 = A.x.y === Y // const R1 = true + +const R2 = A.x === X // const R2 = true +``` + ### Pointer @@ -1087,10 +1110,13 @@ import { ValuePointer } from '@sinclair/typebox/value' const A = { x: 0, y: 0, z: 0 } -ValuePointer.Set(A, '/x', 1) // const A = { x: 1, y: 0, z: 0 } -ValuePointer.Set(A, '/y', 1) // const A = { x: 1, y: 1, z: 0 } -ValuePointer.Set(A, '/z', 1) // const A = { x: 1, y: 1, z: 1 } +ValuePointer.Set(A, '/x', 1) // const A' = { x: 1, y: 0, z: 0 } + +ValuePointer.Set(A, '/y', 1) // const A' = { x: 1, y: 1, z: 0 } + +ValuePointer.Set(A, '/z', 1) // const A' = { x: 1, y: 1, z: 1 } ``` + ## TypeCheck diff --git a/src/value/index.ts b/src/value/index.ts index 5598e30ab..a2235b01f 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -29,5 +29,6 @@ THE SOFTWARE. export { ValueError, ValueErrorIterator, ValueErrorType } from '../errors/index' export { ValueHash } from './hash' export { Edit, Insert, Update, Delete } from './delta' +export { Mutable } from './mutate' export * from './pointer' export * from './value' diff --git a/src/value/mutate.ts b/src/value/mutate.ts new file mode 100644 index 000000000..44d11a6e7 --- /dev/null +++ b/src/value/mutate.ts @@ -0,0 +1,110 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Is, TypedArrayType } from './is' +import { ValuePointer } from './pointer' +import { ValueClone } from './clone' + +export class ValueMutateTypeMismatchError extends Error { + constructor() { + super('ValueMutate: Cannot assign due type mismatch of assignable values') + } +} +export class ValueMutateInvalidRootMutationError extends Error { + constructor() { + super('ValueMutate: Only object and array types can be mutated at the root level') + } +} +export type Mutable = { [key: string]: unknown } | unknown[] +export namespace ValueMutate { + function Object(root: Mutable, path: string, current: unknown, next: Record) { + if (!Is.Object(current)) { + ValuePointer.Set(root, path, ValueClone.Clone(next)) + } else { + const currentKeys = globalThis.Object.keys(current) + const nextKeys = globalThis.Object.keys(next) + for (const currentKey of currentKeys) { + if (!nextKeys.includes(currentKey)) { + delete current[currentKey] + } + } + for (const nextKey of nextKeys) { + if (!currentKeys.includes(nextKey)) { + current[nextKey] = null + } + } + for (const nextKey of nextKeys) { + Visit(root, `${path}/${nextKey}`, current[nextKey], next[nextKey]) + } + } + } + function Array(root: Mutable, path: string, current: unknown, next: unknown[]) { + if (!Is.Array(current)) { + ValuePointer.Set(root, path, ValueClone.Clone(next)) + } else { + for (let index = 0; index < next.length; index++) { + Visit(root, `${path}/${index}`, current[index], next[index]) + } + current.splice(next.length) + } + } + function TypedArray(root: Mutable, path: string, current: unknown, next: TypedArrayType) { + if (Is.TypedArray(current) && current.length === next.length) { + for (let i = 0; i < current.length; i++) { + current[i] = next[i] + } + } else { + ValuePointer.Set(root, path, ValueClone.Clone(next)) + } + } + function Value(root: Mutable, path: string, current: unknown, next: unknown) { + if (current === next) return + ValuePointer.Set(root, path, next) + } + function Visit(root: Mutable, path: string, current: unknown, next: unknown) { + if (Is.Array(next)) { + return Array(root, path, current, next) + } else if (Is.TypedArray(next)) { + return TypedArray(root, path, current, next) + } else if (Is.Object(next)) { + return Object(root, path, current, next) + } else if (Is.Value(next)) { + return Value(root, path, current, next) + } + } + /** Performs a deep mutable value assignment while retaining internal references. */ + export function Mutate(current: Mutable, next: Mutable): void { + if (Is.TypedArray(current) || Is.Value(current) || Is.TypedArray(next) || Is.Value(next)) { + throw new ValueMutateInvalidRootMutationError() + } + if ((Is.Object(current) && Is.Array(next)) || (Is.Array(current) && Is.Object(next))) { + throw new ValueMutateTypeMismatchError() + } + Visit(current, '', current, next) + } +} diff --git a/src/value/value.ts b/src/value/value.ts index b76700865..ed9335d28 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import * as Types from '../typebox' import { ValueErrors, ValueErrorIterator, ValueError } from '../errors/index' +import { ValueMutate, Mutable } from './mutate' import { ValueHash } from './hash' import { ValueEqual } from './equal' import { ValueCast } from './cast' @@ -99,4 +100,8 @@ export namespace Value { export function Patch(current: unknown, edits: Edit[]): T { return ValueDelta.Patch(current, edits) as T } + /** Performs a deep mutable value assignment while retaining internal references. */ + export function Mutate(current: Mutable, next: Mutable): void { + ValueMutate.Mutate(current, next) + } } diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index 19543636d..d632824b4 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -7,15 +7,12 @@ export namespace Assert { const next = port++ return next } - export function equal(actual: unknown, expect: unknown) { - return assert.equal(actual, expect) + return assert.strictEqual(actual, expect) } - export function notEqual(actual: unknown, expect: unknown) { return assert.notEqual(actual, expect) } - export function deepEqual(actual: unknown, expect: unknown) { if (actual instanceof Uint8Array && expect instanceof Uint8Array) { assert.equal(actual.length, expect.length) @@ -23,12 +20,10 @@ export namespace Assert { } return assert.deepEqual(actual, expect) } - let nextIdOrdinal = 0 export function nextId() { return `nextID${nextIdOrdinal++}` } - export function throws(callback: Function) { try { callback() @@ -37,7 +32,6 @@ export namespace Assert { } throw Error('Expected throw') } - export async function throwsAsync(callback: Function) { try { await callback() @@ -46,12 +40,10 @@ export namespace Assert { } throw Error('Expected throw') } - export function isTypeOf(value: any, type: any) { if (typeof value === type) return throw Error(`Value is not typeof ${type}`) } - export function isInstanceOf(value: any, constructor: any) { if (value instanceof constructor) return throw Error(`Value is not instance of ${constructor}`) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index ad07f09f0..57403d7b8 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -6,4 +6,5 @@ import './create' import './delta' import './equal' import './hash' +import './mutate' import './pointer' diff --git a/test/runtime/value/mutate/index.ts b/test/runtime/value/mutate/index.ts new file mode 100644 index 000000000..d37705ce3 --- /dev/null +++ b/test/runtime/value/mutate/index.ts @@ -0,0 +1 @@ +import './mutate' \ No newline at end of file diff --git a/test/runtime/value/mutate/mutate.ts b/test/runtime/value/mutate/mutate.ts new file mode 100644 index 000000000..bffdf8298 --- /dev/null +++ b/test/runtime/value/mutate/mutate.ts @@ -0,0 +1,88 @@ +import { Value } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +describe('value/mutate/Mutate', () => { + // -------------------------------------------- + // Throw + // -------------------------------------------- + it('should throw 1', () => { + // @ts-ignore + Assert.throws(() => Value.Mutate(1, 1)) + }) + it('should throw 2', () => { + // @ts-ignore + Assert.throws(() => Value.Mutate({}, 1)) + }) + it('should throw 3', () => { + // @ts-ignore + Assert.throws(() => Value.Mutate([], 1)) + }) + it('should throw 4', () => { + // @ts-ignore + Assert.throws(() => Value.Mutate({}, [])) + }) + it('should throw 5', () => { + // @ts-ignore + Assert.throws(() => Value.Mutate([], {})) + }) + // -------------------------------------------- + // Mutate + // -------------------------------------------- + it('Should mutate 0', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, {}) + Assert.deepEqual(A, {}) + }) + it('Should mutate 1', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: { y: { z: 2 } } }) + Assert.equal(A.x.y.z, 2) + Assert.equal(A.x.y, Y) + Assert.equal(A.x, X) + }) + it('Should mutate 2', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: { y: { z: [1, 2, 3] } } }) + Assert.deepEqual(A.x.y.z, [1, 2, 3]) + Assert.equal(A.x.y, Y) + Assert.equal(A.x, X) + }) + it('Should mutate 3', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: {} }) + Assert.equal(A.x.y, undefined) + Assert.equal(A.x, X) + }) + it('Should mutate 4', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: { y: 1 } }) + Assert.equal(A.x.y, 1) + Assert.equal(A.x, X) + }) + it('Should mutate 5', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: { y: [1, 2, 3] } }) + Assert.deepEqual(A.x.y, [1, 2, 3]) + Assert.equal(A.x, X) + }) + it('Should mutate 6', () => { + const Y = { z: 1 } + const X = { y: Y } + const A = { x: X } + Value.Mutate(A, { x: [1, 2, 3] }) + Assert.notEqual(A.x, X) + Assert.deepEqual(A.x, [1, 2, 3]) + }) +}) From 691c24b845a749b46734724a44e79c8b251e37e1 Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 10 Apr 2023 20:27:53 +0900 Subject: [PATCH 098/369] Revision 0.27.1 --- changelog/0.27.1.md | 2 +- test/runtime/value/mutate/index.ts | 2 +- test/runtime/value/mutate/mutate.ts | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/changelog/0.27.1.md b/changelog/0.27.1.md index 016f05ced..eba9126bd 100644 --- a/changelog/0.27.1.md +++ b/changelog/0.27.1.md @@ -2,4 +2,4 @@ ## Updates -- Adds a `Value.Mutate(current, next): void` function. This function performs a deep mutable assignment on a value by internally remapping the `next(right)` values on the `current(left)`. Values omitted on the right will also be deleted on the left. This function can be useful instances where mutation of data is required without modification to existing references. An example of which might be React which tracks reference values to signal redraws. This function is implemented by way of `ValuePointer`. +- Adds a `Value.Mutate(left, right)` function. This function performs a deep mutable assignment on a value by internally remapping the `right` values on the `left`. Values omitted on the right will also be deleted on the left. This function can be useful scenarios where mutation of data is required without replacing existing reference values. An example of which might be React which tracks reference values to indicate redraw. This function is implemented by way of `ValuePointer`. diff --git a/test/runtime/value/mutate/index.ts b/test/runtime/value/mutate/index.ts index d37705ce3..28e30125c 100644 --- a/test/runtime/value/mutate/index.ts +++ b/test/runtime/value/mutate/index.ts @@ -1 +1 @@ -import './mutate' \ No newline at end of file +import './mutate' diff --git a/test/runtime/value/mutate/mutate.ts b/test/runtime/value/mutate/mutate.ts index bffdf8298..1c4d81ef1 100644 --- a/test/runtime/value/mutate/mutate.ts +++ b/test/runtime/value/mutate/mutate.ts @@ -29,58 +29,58 @@ describe('value/mutate/Mutate', () => { // Mutate // -------------------------------------------- it('Should mutate 0', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, {}) Assert.deepEqual(A, {}) }) it('Should mutate 1', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: { y: { z: 2 } } }) Assert.equal(A.x.y.z, 2) Assert.equal(A.x.y, Y) Assert.equal(A.x, X) }) it('Should mutate 2', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: { y: { z: [1, 2, 3] } } }) Assert.deepEqual(A.x.y.z, [1, 2, 3]) Assert.equal(A.x.y, Y) Assert.equal(A.x, X) }) it('Should mutate 3', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: {} }) Assert.equal(A.x.y, undefined) Assert.equal(A.x, X) }) it('Should mutate 4', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: { y: 1 } }) Assert.equal(A.x.y, 1) Assert.equal(A.x, X) }) it('Should mutate 5', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: { y: [1, 2, 3] } }) Assert.deepEqual(A.x.y, [1, 2, 3]) Assert.equal(A.x, X) }) it('Should mutate 6', () => { - const Y = { z: 1 } + const Y = { z: 1 } const X = { y: Y } - const A = { x: X } + const A = { x: X } Value.Mutate(A, { x: [1, 2, 3] }) Assert.notEqual(A.x, X) Assert.deepEqual(A.x, [1, 2, 3]) From 39fccd596ebff1e75500f91eba60e1b16942ce2a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 11 Apr 2023 06:36:05 +0900 Subject: [PATCH 099/369] Union Error Emit Order (#375) --- package-lock.json | 4 ++-- package.json | 2 +- src/errors/errors.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23096db77..3729af7b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.1", + "version": "0.27.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.1", + "version": "0.27.2", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 546a220e9..d1183832e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.1", + "version": "0.27.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 1d8d7615e..be83707c9 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -490,12 +490,12 @@ export namespace ValueErrors { if (variantErrors.length === 0) return errors.push(...variantErrors) } - for (const error of errors) { - yield error - } if (errors.length > 0) { yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } } + for (const error of errors) { + yield error + } } function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value instanceof globalThis.Uint8Array)) { From e1b923cdbafe5d408bb738fec0ce7f9749c9f034 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 11 Apr 2023 14:41:51 +0900 Subject: [PATCH 100/369] Record Option Overloads (#377) --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 10 +++--- test/runtime/type/guard/record.ts | 57 ++++++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3729af7b3..74cb8e749 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.2", + "version": "0.27.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.2", + "version": "0.27.3", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index d1183832e..3d670d213 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.2", + "version": "0.27.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index a25d5b0d2..997128ace 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2365,15 +2365,15 @@ export class StandardTypeBuilder extends TypeBuilder { }, options) } /** `[Standard]` Creates a Record type */ - public Record[]>, T extends TSchema>(key: K, schema: T): RecordUnionLiteralType + public Record[]>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType /** `[Standard]` Creates a Record type */ - public Record, T extends TSchema>(key: K, schema: T): RecordLiteralType + public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordLiteralType /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T): RecordTemplateLiteralType + public Record(key: K, schema: T, options?: ObjectOptions): RecordTemplateLiteralType /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T): RecordNumberType + public Record(key: K, schema: T, options?: ObjectOptions): RecordNumberType /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T): RecordStringType + public Record(key: K, schema: T, options?: ObjectOptions): RecordStringType /** `[Standard]` Creates a Record type */ public Record(key: RecordKey, schema: TSchema, options: ObjectOptions = {}) { if (TypeGuard.TTemplateLiteral(key)) { diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 2418c0e3d..45ebcb41e 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -1,8 +1,63 @@ -import { TypeGuard } from '@sinclair/typebox' +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternNumber } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TRecord', () => { + // ------------------------------------------------------------- + // Overloads + // ------------------------------------------------------------- + it('Should guard overload 1', () => { + const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TObject(T), true) + Assert.equal(TypeGuard.TString(T.properties.A), true) + Assert.equal(TypeGuard.TString(T.properties.B), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 2', () => { + const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal + Assert.equal(TypeGuard.TObject(T), true) + Assert.equal(TypeGuard.TString(T.properties.A), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 3', () => { + // @ts-ignore + Assert.throws(() => Type.Record(Type.Union([]), Type.String(), { extra: 1 })) + }) + it('Should guard overload 4', () => { + const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TObject(T), true) + Assert.equal(TypeGuard.TString(T.properties.A), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 5', () => { + const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Record(L, Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TObject(T), true) + Assert.equal(TypeGuard.TString(T.properties.helloA), true) + Assert.equal(TypeGuard.TString(T.properties.helloB), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 6', () => { + const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TRecord(T), true) + Assert.equal(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 7', () => { + const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TRecord(T), true) + Assert.equal(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.equal(T.extra, 1) + }) + it('Should guard overload 8', () => { + const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TRecord(T), true) + Assert.equal(TypeGuard.TString(T.patternProperties[PatternStringExact]), true) + Assert.equal(T.extra, 1) + }) + // ------------------------------------------------------------- + // Variants + // ------------------------------------------------------------- it('Should guard for TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) Assert.equal(R, true) From d8effff195311fde88f972d6e1abfc71640f8e36 Mon Sep 17 00:00:00 2001 From: Mauro <35332423+m-ronchi@users.noreply.github.com> Date: Wed, 12 Apr 2023 14:48:17 +0200 Subject: [PATCH 101/369] Compiler Character Check Fix (#378) --- src/compiler/compiler.ts | 2 +- test/runtime/compiler/object.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 03d27ffeb..5d80435ff 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -64,7 +64,7 @@ namespace Character { return code === 95 } export function IsAlpha(code: number) { - return (code >= 64 && code <= 90) || (code >= 97 && code <= 122) + return (code >= 65 && code <= 90) || (code >= 97 && code <= 122) } export function IsNumeric(code: number) { return code >= 48 && code <= 57 diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index b45f9a8b6..94897ec63 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -136,6 +136,7 @@ describe('type/compiler/Object', () => { 'node-mirror:release:1': Type.Optional(Type.Literal(6)), // issue: 356 'node-mirror:release:2': Type.Union([Type.Literal(7), Type.Undefined()]), // key known "a'a": Type.Literal(8), + "@onlyAtSymbol": Type.Literal(9) }) Ok(T, { 'with-hyphen': 1, @@ -146,6 +147,7 @@ describe('type/compiler/Object', () => { 'node-mirror:release:1': 6, 'node-mirror:release:2': 7, "a'a": 8, + "@onlyAtSymbol": 9 }) }) it('Should validate schema additional properties of string', () => { From d4e6760eb301d22c236826235221a5ea49af8b54 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 12 Apr 2023 21:50:15 +0900 Subject: [PATCH 102/369] Revision 0.27.4 --- package-lock.json | 4 ++-- package.json | 2 +- test/runtime/compiler/object.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 74cb8e749..40c670b34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.3", + "version": "0.27.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.3", + "version": "0.27.4", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 3d670d213..cf0937f20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.3", + "version": "0.27.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 94897ec63..187207b3c 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -136,7 +136,7 @@ describe('type/compiler/Object', () => { 'node-mirror:release:1': Type.Optional(Type.Literal(6)), // issue: 356 'node-mirror:release:2': Type.Union([Type.Literal(7), Type.Undefined()]), // key known "a'a": Type.Literal(8), - "@onlyAtSymbol": Type.Literal(9) + '@onlyAtSymbol': Type.Literal(9), }) Ok(T, { 'with-hyphen': 1, @@ -147,7 +147,7 @@ describe('type/compiler/Object', () => { 'node-mirror:release:1': 6, 'node-mirror:release:2': 7, "a'a": 8, - "@onlyAtSymbol": 9 + '@onlyAtSymbol': 9, }) }) it('Should validate schema additional properties of string', () => { From 7462636ade246ab3c18e9b31ece90bb2e024e634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Vod=C3=A1=C4=8Dek?= Date: Thu, 13 Apr 2023 15:17:39 +0200 Subject: [PATCH 103/369] Documentation (#379) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a5a76d424..6c6e443cd 100644 --- a/readme.md +++ b/readme.md @@ -101,7 +101,7 @@ License MIT - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) -- [TypeSystem](#typecheck) +- [TypeSystem](#typesystem) - [Types](#typesystem-types) - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) From ae503c5549a11ab81b9b5168cff65ac0f6d3116a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 14 Apr 2023 10:56:47 +0900 Subject: [PATCH 104/369] Template Literal Finite Checks (#381) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 29 +++++++++++++++++++------- test/runtime/type/guard/record.ts | 14 +++++++------ test/runtime/type/template/finite.ts | 10 +++++++++ test/runtime/type/template/generate.ts | 6 +++--- test/runtime/type/template/parser.ts | 6 +++--- test/runtime/type/template/pattern.ts | 19 +++++++++++++++++ 8 files changed, 67 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40c670b34..7c2f3e507 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.4", + "version": "0.27.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.4", + "version": "0.27.5", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index cf0937f20..ca02865f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.4", + "version": "0.27.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 997128ace..564399508 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -37,7 +37,7 @@ export const Kind = Symbol.for('TypeBox.Kind') // -------------------------------------------------------------------------- export const PatternBoolean = '(true|false)' export const PatternNumber = '(0|[1-9][0-9]*)' -export const PatternString = '.*' +export const PatternString = '(.*)' export const PatternBooleanExact = `^${PatternBoolean}$` export const PatternNumberExact = `^${PatternNumber}$` export const PatternStringExact = `^${PatternString}$` @@ -456,7 +456,7 @@ export interface TPromise extends TSchema { // TRecord // -------------------------------------------------------------------------- export type RecordTemplateLiteralObjectType = Ensure]: T }>>> -export type RecordTemplateLiteralType = IsTemplateLiteralFinite extends true ? RecordTemplateLiteralObjectType : TRecord +export type RecordTemplateLiteralType = IsTemplateLiteralFinite extends true ? RecordTemplateLiteralObjectType : TRecord export type RecordUnionLiteralType[]>, T extends TSchema> = Static extends string ? Ensure]: T }>> : never export type RecordLiteralType, T extends TSchema> = Ensure> export type RecordNumberType = Ensure> @@ -1937,9 +1937,6 @@ export namespace TemplateLiteralParser { export type Const = { type: 'const'; const: string } export type And = { type: 'and'; expr: Expression[] } export type Or = { type: 'or'; expr: Expression[] } - function Unescape(value: string) { - return value.replace(/\\/g, '') - } function IsNonEscaped(pattern: string, index: number, char: string) { return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 } @@ -2038,7 +2035,7 @@ export namespace TemplateLiteralParser { if (IsGroup(pattern)) return Parse(InGroup(pattern)) if (IsPrecedenceOr(pattern)) return Or(pattern) if (IsPrecedenceAnd(pattern)) return And(pattern) - return { type: 'const', const: Unescape(pattern) } + return { type: 'const', const: pattern } } /** Parses a pattern and strips forward and trailing ^ and $ */ export function ParseExact(pattern: string): Expression { @@ -2050,10 +2047,26 @@ export namespace TemplateLiteralParser { // -------------------------------------------------------------------------------------- export namespace TemplateLiteralFinite { function IsNumber(expression: TemplateLiteralParser.Expression): boolean { - return expression.type === 'or' && expression.expr.length === 2 && expression.expr[0].type === 'const' && expression.expr[0].const === '0' && expression.expr[1].type === 'const' && expression.expr[1].const === '[1-9][0-9]*' + // prettier-ignore + return ( + expression.type === 'or' && + expression.expr.length === 2 && + expression.expr[0].type === 'const' && + expression.expr[0].const === '0' && + expression.expr[1].type === 'const' && + expression.expr[1].const === '[1-9][0-9]*' + ) } function IsBoolean(expression: TemplateLiteralParser.Expression): boolean { - return expression.type === 'or' && expression.expr.length === 2 && expression.expr[0].type === 'const' && expression.expr[0].const === 'true' && expression.expr[1].type === 'const' && expression.expr[1].const === 'false' + // prettier-ignore + return ( + expression.type === 'or' && + expression.expr.length === 2 && + expression.expr[0].type === 'const' && + expression.expr[0].const === 'true' && + expression.expr[1].type === 'const' && + expression.expr[1].const === 'false' + ) } function IsString(expression: TemplateLiteralParser.Expression) { return expression.type === 'const' && expression.const === '.*' diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 45ebcb41e..f43a70a1a 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -1,4 +1,4 @@ -import { TypeGuard, PatternNumberExact, PatternStringExact, PatternNumber } from '@sinclair/typebox' +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' @@ -55,6 +55,13 @@ describe('type/guard/TRecord', () => { Assert.equal(TypeGuard.TString(T.patternProperties[PatternStringExact]), true) Assert.equal(T.extra, 1) }) + it('Should guard overload 9', () => { + const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) + const T = Type.Record(L, Type.String(), { extra: 1 }) + Assert.equal(TypeGuard.TRecord(T), true) + Assert.equal(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`]), true) + Assert.equal(T.extra, 1) + }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- @@ -62,7 +69,6 @@ describe('type/guard/TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) Assert.equal(R, true) }) - it('Should guard for TRecord with TObject value', () => { const R = TypeGuard.TRecord( Type.Record( @@ -75,18 +81,15 @@ describe('type/guard/TRecord', () => { ) Assert.equal(R, true) }) - it('Should not guard for TRecord', () => { const R = TypeGuard.TRecord(null) Assert.equal(R, false) }) - it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) Assert.equal(R, false) }) - it('Should not guard for TRecord with TObject value with invalid Property', () => { const R = TypeGuard.TRecord( Type.Record( @@ -99,7 +102,6 @@ describe('type/guard/TRecord', () => { ) Assert.equal(R, false) }) - it('Transform: Should should transform to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts index e44c8164f..c843229ed 100644 --- a/test/runtime/type/template/finite.ts +++ b/test/runtime/type/template/finite.ts @@ -25,6 +25,11 @@ describe('type/TemplateLiteralFinite', () => { const R = TemplateLiteralFinite.Check(E) Assert.deepEqual(R, true) }) + it('Finite 5', () => { + const E = TemplateLiteralParser.Parse(`\\.\\*`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, true) + }) // --------------------------------------------------------------- // Infinite // --------------------------------------------------------------- @@ -58,4 +63,9 @@ describe('type/TemplateLiteralFinite', () => { const R = TemplateLiteralFinite.Check(E) Assert.deepEqual(R, false) }) + it('Infinite 7', () => { + const E = TemplateLiteralParser.Parse(`${PatternString}_foo`) + const R = TemplateLiteralFinite.Check(E) + Assert.deepEqual(R, false) + }) }) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts index 587e6cb9c..df00189db 100644 --- a/test/runtime/type/template/generate.ts +++ b/test/runtime/type/template/generate.ts @@ -44,12 +44,12 @@ describe('type/TemplateLiteralGenerator', () => { it('Expression 2', () => { const E = TemplateLiteralParser.Parse('\\)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, [')']) + Assert.deepEqual(R, ['\\)']) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['(']) + Assert.deepEqual(R, ['\\(']) }) it('Expression 4', () => { const E = TemplateLiteralParser.Parse('') @@ -59,7 +59,7 @@ describe('type/TemplateLiteralGenerator', () => { it('Expression 5', () => { const E = TemplateLiteralParser.Parse('\\') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['']) + Assert.deepEqual(R, ['\\']) }) it('Expression 6', () => { const E = TemplateLiteralParser.Parse('()') diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts index 682a5b912..9cf2607f1 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template/parser.ts @@ -84,14 +84,14 @@ describe('type/TemplateLiteralParser', () => { const E = TemplateLiteralParser.Parse('\\)') Assert.deepEqual(E, { type: 'const', - const: ')', + const: '\\)', }) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') Assert.deepEqual(E, { type: 'const', - const: '(', + const: '\\(', }) }) it('Expression 4', () => { @@ -105,7 +105,7 @@ describe('type/TemplateLiteralParser', () => { const E = TemplateLiteralParser.Parse('\\') Assert.deepEqual(E, { type: 'const', - const: '', + const: '\\', }) }) it('Expression 6', () => { diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template/pattern.ts index 1936c8211..d43148951 100644 --- a/test/runtime/type/template/pattern.ts +++ b/test/runtime/type/template/pattern.ts @@ -7,6 +7,25 @@ describe('type/TemplateLiteralPattern', () => { Assert.equal(pattern, expect) } // --------------------------------------------------------------- + // Escape + // --------------------------------------------------------------- + it('Escape 1', () => { + const T = Type.TemplateLiteral([Type.Literal('.*')]) + Assert.equal(T.pattern, '^\\.\\*$') + }) + it('Escape 2', () => { + const T = Type.TemplateLiteral([Type.Literal('(')]) + Assert.equal(T.pattern, '^\\($') + }) + it('Escape 3', () => { + const T = Type.TemplateLiteral([Type.Literal(')')]) + Assert.equal(T.pattern, '^\\)$') + }) + it('Escape 4', () => { + const T = Type.TemplateLiteral([Type.Literal('|')]) + Assert.equal(T.pattern, '^\\|$') + }) + // --------------------------------------------------------------- // Pattern // --------------------------------------------------------------- it('Pattern 1', () => { From 5f641c13aefe4697f282d6ee8f2727738629416b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 14 Apr 2023 13:11:35 +0900 Subject: [PATCH 105/369] Recursive KeyOf (#382) --- package-lock.json | 4 +- package.json | 2 +- readme.md | 2 +- src/typebox.ts | 15 ++++--- src/value/create.ts | 10 ++++- test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/keyof.ts | 59 ++++++++++++++++++++++++++ test/runtime/value/create/intersect.ts | 22 ++++++++++ test/static/keyof.ts | 12 ++++++ 9 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 test/runtime/type/guard/keyof.ts diff --git a/package-lock.json b/package-lock.json index 7c2f3e507..83f85c198 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.5", + "version": "0.27.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.5", + "version": "0.27.6", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index ca02865f9..04a929976 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.5", + "version": "0.27.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 6c6e443cd..c5120a9d5 100644 --- a/readme.md +++ b/readme.md @@ -402,7 +402,7 @@ The following table lists the Standard TypeBox types. These types are fully comp ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Record( │ type T = Record< │ const T = { │ │ Type.String(), │ string, │ type: 'object', │ -│ Type.Number() │ number, │ patternProperties: { │ +│ Type.Number() │ number │ patternProperties: { │ │ ) │ > │ '^.*$': { │ │ │ │ type: 'number' │ │ │ │ } │ diff --git a/src/typebox.ts b/src/typebox.ts index 564399508..21ad70980 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -305,11 +305,12 @@ export type TKeyOfTuple = { : never // prettier-ignore export type TKeyOf = ( - T extends TComposite ? TKeyOfTuple : - T extends TIntersect ? TKeyOfTuple : - T extends TUnion ? TKeyOfTuple : - T extends TObject ? TKeyOfTuple : - T extends TRecord ? [K] : + T extends TComposite ? TKeyOfTuple : + T extends TIntersect ? TKeyOfTuple : + T extends TUnion ? TKeyOfTuple : + T extends TObject ? TKeyOfTuple : + T extends TRecursive ? TKeyOfTuple : + T extends TRecord ? [K] : [] ) extends infer R ? TUnionResult> : never // -------------------------------------------------------------------------- @@ -394,7 +395,7 @@ export interface TObject extends TSchema, O export type TOmitArray = Assert<{ [K2 in keyof T]: TOmit, K> }, TSchema[]> export type TOmitProperties = Evaluate, TProperties>> // prettier-ignore -export type TOmit = +export type TOmit = T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -436,7 +437,7 @@ export type TPickProperties = [K in keyof R]: Assert extends TSchema ? R[K] : never }): never // prettier-ignore -export type TPick = +export type TPick = T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : diff --git a/src/value/create.ts b/src/value/create.ts index f6f63b68b..dace6a6a2 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -44,7 +44,7 @@ export class ValueCreateNeverTypeError extends Error { } export class ValueCreateIntersectTypeError extends Error { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Can only create values for intersected objects and non-varying primitive types. Consider using a default value.') + super('ValueCreate: Intersect produced invalid value. Consider using a default value.') } } export class ValueCreateTempateLiteralTypeError extends Error { @@ -152,7 +152,13 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - const value = schema.type === 'object' ? schema.allOf.reduce((acc, schema) => ({ ...acc, ...(Visit(schema, references) as any) }), {}) : schema.allOf.reduce((_, schema) => Visit(schema, references), undefined as any) + // Note: The best we can do here is attempt to instance each sub type and apply through object assign. For non-object + // sub types, we just escape the assignment and just return the value. In the latter case, this is typically going to + // be a consequence of an illogical intersection. + const value = schema.allOf.reduce((acc, schema) => { + const next = Visit(schema, references) as any + return typeof next === 'object' ? { ...acc, ...next } : next + }, {}) if (!ValueCheck.Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) return value } diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index d9d8086c4..d40906195 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -11,6 +11,7 @@ import './function' import './integer' import './literal' import './intersect' +import './keyof' import './not' import './null' import './number' diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/keyof.ts new file mode 100644 index 000000000..d1f6e9ecb --- /dev/null +++ b/test/runtime/type/guard/keyof.ts @@ -0,0 +1,59 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TKeyOf', () => { + it('Should guard for keyof TObject', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TUnion(K), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + }) + it('Should guard for keyof TRecursive', () => { + const T = Type.Recursive((Self) => + Type.Object({ + x: Type.Number(), + y: Type.Array(Self), + }), + ) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TUnion(K), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + }) + it('Should guard for keyof TIntersect', () => { + const T = Type.Intersect([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TUnion(K), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + }) + it('Should guard for keyof TUnion', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TNever(K), true) + }) + it('Should guard for keyof TNull', () => { + const T = Type.Null() + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TNever(K), true) + }) +}) diff --git a/test/runtime/value/create/intersect.ts b/test/runtime/value/create/intersect.ts index 344ad5b90..f007480ae 100644 --- a/test/runtime/value/create/intersect.ts +++ b/test/runtime/value/create/intersect.ts @@ -37,4 +37,26 @@ describe('value/create/Intersect', () => { const R = Value.Create(T) Assert.deepEqual(R, 'hello') }) + it('Should create from nested intersection', () => { + const T = Type.Intersect([ + Type.Object({ + x: Type.Number({ default: 1 }), + }), + Type.Intersect([ + Type.Object({ + y: Type.Number({ default: 2 }), + }), + Type.Object({ + z: Type.Number({ default: 3 }), + }), + ]), + ]) + const R = Value.Create(T) + Assert.deepEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should create non varying primitive', () => { + const T = Type.Intersect([Type.Number(), Type.Number(), Type.Number()]) + const R = Value.Create(T) + Assert.deepEqual(R, 0) + }) }) diff --git a/test/static/keyof.ts b/test/static/keyof.ts index ff353b7fa..bda75e87f 100644 --- a/test/static/keyof.ts +++ b/test/static/keyof.ts @@ -79,3 +79,15 @@ import { Type } from '@sinclair/typebox' Expect(K2).ToInfer<'y' | 'z'>() } } +{ + const T = Type.Recursive((Self) => + Type.Object({ + a: Type.String(), + b: Type.String(), + c: Type.String(), + d: Type.Array(Self), + }), + ) + const K = Type.KeyOf(T) + Expect(K).ToInfer<'a' | 'b' | 'c' | 'd'>() +} From fc616031db973c78c7a61f803a396244f896649d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 14 Apr 2023 17:07:59 +0900 Subject: [PATCH 106/369] Recursive Utility Types (#383) --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 15 +++++--- test/static/recursive.ts | 75 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 83f85c198..841d11cd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.6", + "version": "0.27.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.6", + "version": "0.27.7", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 04a929976..4f0e07a85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.6", + "version": "0.27.7", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 21ad70980..6bd693dd6 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -305,11 +305,11 @@ export type TKeyOfTuple = { : never // prettier-ignore export type TKeyOf = ( + T extends TRecursive ? TKeyOfTuple : T extends TComposite ? TKeyOfTuple : T extends TIntersect ? TKeyOfTuple : T extends TUnion ? TKeyOfTuple : T extends TObject ? TKeyOfTuple : - T extends TRecursive ? TKeyOfTuple : T extends TRecord ? [K] : [] ) extends infer R ? TUnionResult> : never @@ -396,6 +396,7 @@ export type TOmitArray = Assert<{ [K2 export type TOmitProperties = Evaluate, TProperties>> // prettier-ignore export type TOmit = + T extends TRecursive ? TRecursive> : T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -420,6 +421,7 @@ export type TPartialProperties = Evaluate> // prettier-ignore export type TPartial = + T extends TRecursive ? TRecursive> : T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -438,6 +440,7 @@ export type TPickProperties = }): never // prettier-ignore export type TPick = + T extends TRecursive ? TRecursive> : T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -480,6 +483,7 @@ export interface TThis extends TSchema { } export type TRecursiveReduce = Static]> export interface TRecursive extends TSchema { + [Hint]: 'Recursive' static: TRecursiveReduce } // -------------------------------------------------------------------------- @@ -508,6 +512,7 @@ export type TRequiredProperties = Evaluate> // prettier-ignore export type TRequired = + T extends TRecursive ? TRecursive> : T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -584,9 +589,9 @@ export type TTemplateLiteralConst = T extends TUnion ? { [K in keyof U]: TTemplateLiteralUnion, Acc> }[number] : T extends TTemplateLiteral ? `${Static}` : T extends TLiteral ? `${U}` : - T extends TString ? `${string}` : - T extends TNumber ? `${number}` : - T extends TBigInt ? `${bigint}` : + T extends TString ? `${string}` : + T extends TNumber ? `${number}` : + T extends TBigInt ? `${bigint}` : T extends TBoolean ? `${boolean}` : never // prettier-ignore @@ -2420,7 +2425,7 @@ export class StandardTypeBuilder extends TypeBuilder { if (options.$id === undefined) (options as any).$id = `T${TypeOrdinal++}` const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) thisType.$id = options.$id - return this.Create({ ...options, ...thisType } as any) + return this.Create({ ...options, [Hint]: 'Recursive', ...thisType } as any) } /** `[Standard]` Creates a Ref type. The referenced type must contain a $id */ public Ref(schema: T, options: SchemaOptions = {}): TRef { diff --git a/test/static/recursive.ts b/test/static/recursive.ts index 777195109..06ca5d025 100644 --- a/test/static/recursive.ts +++ b/test/static/recursive.ts @@ -2,14 +2,79 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' { - const T = Type.Recursive((Node) => + // identity + const R = Type.Recursive((Node) => Type.Object({ id: Type.String(), nodes: Type.Array(Node), }), ) - - type T = Static - - Expect(T).ToInfer() // ? how to test.... + type T = Static + Expect(R).ToInfer<{ id: string; nodes: T[] }>() +} +{ + // keyof + const R = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const T = Type.KeyOf(R) + Expect(T).ToInfer<'id' | 'nodes'>() +} +{ + // partial + const R = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const T = Type.Partial(R) + Expect(T).ToInfer<{ + id: string | undefined + nodes: Static[] | undefined + }>() +} +{ + // required + const R = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const P = Type.Partial(R) + const T = Type.Required(P) + Expect(T).ToInfer<{ + id: string + nodes: Static[] + }>() +} +{ + // pick + const R = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const T = Type.Pick(R, ['id']) + Expect(T).ToInfer<{ + id: string + }>() +} +{ + // omit + const R = Type.Recursive((Node) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(Node), + }), + ) + const T = Type.Omit(R, ['id']) + Expect(T).ToInfer<{ + nodes: Static[] + }>() } From e348464766597309af3ecb21af57199524267a20 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 14 Apr 2023 19:53:20 +0900 Subject: [PATCH 107/369] TypeMap Example --- example/typemap/readme.md | 7 + example/typemap/typemap.ts | 496 +++++++++++++++++++++++++++++++++++++ 2 files changed, 503 insertions(+) create mode 100644 example/typemap/readme.md create mode 100644 example/typemap/typemap.ts diff --git a/example/typemap/readme.md b/example/typemap/readme.md new file mode 100644 index 000000000..fdadd3ab2 --- /dev/null +++ b/example/typemap/readme.md @@ -0,0 +1,7 @@ +# TypeMap + +TypeMap is a fluent type builder abstraction built on top of TypeBox. It is modelled after the Yup and Zod and libraries, but internally uses TypeBox for type composition, inference and runtime type assertion. It supports the same advanced compositional types as TypeBox (including generics, conditional, recursive and template literal types), but offers this functionality as a set of chainable lowercase types. + +Like TypeBox, TypeMap internally uses JSON schema for its type representation. It provides access to the TypeBox compiler infrastructure with the `.compile()` and `.code()` methods which are available on all types, and access to the internal schema via `.schema()`. Types also implement `.check()` and `.cast()` for convenience. + +TypeMap is implemented as an example for reference purposes only. \ No newline at end of file diff --git a/example/typemap/typemap.ts b/example/typemap/typemap.ts new file mode 100644 index 000000000..9352ff370 --- /dev/null +++ b/example/typemap/typemap.ts @@ -0,0 +1,496 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/typemap + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeCompiler, ValueError, TypeCheck } from '@sinclair/typebox/compiler' +import { Value, ValueErrorIterator } from '@sinclair/typebox/value' +import { TypeSystem } from '@sinclair/typebox/system' +import * as T from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------------------- +// Formats +// ------------------------------------------------------------------------------------------------- +// prettier-ignore +TypeSystem.Format('email', (value) => /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(value)) +// prettier-ignore +TypeSystem.Format('uuid', (value) => /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(value)) +// prettier-ignore +TypeSystem.Format('url', (value) => /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu.test(value)) +// prettier-ignore +TypeSystem.Format('ipv6', (value) => /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i.test(value)) +// prettier-ignore +TypeSystem.Format('ipv4', (value) => /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/.test(value)) +// ------------------------------------------------------------------------------------------------- +// Type Mapping +// ------------------------------------------------------------------------------------------------- +export type TypeToType = T extends Type ? S : never +export type TypeToTuple = { + [K in keyof T]: T[K] extends Type ? S : never +} +export type TypeToProperties = T.Assert< + { + [K in keyof T]: T[K] extends Type ? S : never + }, + T.TProperties +> +export type PropertiesType = Record +// prettier-ignore +export type TemplateLiteralType = + | Type + | Type + | Type + | Type + | Type + | Type + | Type + | Type + | Type; +// ------------------------------------------------------------------------------------------------- +// Error +// ------------------------------------------------------------------------------------------------- +export class TypeValueError extends Error { + constructor(public readonly errors: ValueError[]) { + super('TypeValueError: Invalid Value') + } +} +// ------------------------------------------------------------------------------------------------- +// Assert +// ------------------------------------------------------------------------------------------------- +export interface TypeAssert { + check(value: unknown): value is T.Static + errors(value: unknown): ValueErrorIterator + code(): string +} +export class TypeAssertDynamic implements TypeAssert { + readonly #schema: T + constructor(schema: T) { + this.#schema = schema + } + public check(value: unknown): value is T.Static { + return Value.Check(this.#schema, [], value) + } + public errors(value: unknown): ValueErrorIterator { + return Value.Errors(this.#schema, [], value) + } + public code(): string { + return TypeCompiler.Code(this.#schema) + } +} +export class TypeAssertCompiled implements TypeAssert { + readonly #typecheck: TypeCheck + constructor(schema: T) { + this.#typecheck = TypeCompiler.Compile(schema) + } + public check(value: unknown): value is T.Static { + return this.#typecheck.Check(value) + } + public errors(value: unknown): ValueErrorIterator { + return this.#typecheck.Errors(value) + } + public code(): string { + return this.#typecheck.Code() + } +} +// ----------------------------------------------------------------------------------------- +// Type +// ----------------------------------------------------------------------------------------- +export class Type { + #assert: TypeAssert + #schema: T + constructor(schema: T) { + this.#assert = new TypeAssertDynamic(schema) + this.#schema = schema + } + /** Augments this type with the given options */ + public options(options: T.SchemaOptions) { + return new Type({ ...this.schema, ...options } as T) + } + /** Maps a property as readonly and optional */ + public readonlyOptional(): Type> { + return new Type(T.Type.ReadonlyOptional(this.#schema)) + } + /** Maps a property as optional */ + public optional(): Type> { + return new Type(T.Type.Optional(this.#schema)) + } + /** Maps a property as readonly */ + public readonly(): Type> { + return new Type(T.Type.Readonly(this.#schema)) + } + /** Composes this type as a intersect with the given type */ + public and(type: U): Type]>> { + return new Type(T.Type.Intersect([this.#schema, type.schema()])) as any + } + /** Composes this type as a union with the given type */ + public or(type: U): Type]>> { + return new Type(T.Type.Union([this.#schema, type.schema()])) as any + } + /** Picks the given properties from this type */ + public pick)[]>(keys: readonly [...K]): Type> + /** Picks the given properties from this type */ + public pick[]>>(keys: K): Type>> + /** Picks the given properties from this type */ + public pick>(key: K): Type> + /** Picks the given properties from this type */ + public pick(key: K): Type> { + return new Type(T.Type.Pick(this.#schema, key)) + } + /** Omits the given properties from this type */ + public omit)[]>(keys: readonly [...K]): Type> + /** Omits the given properties from this type */ + public omit[]>>(keys: K): Type>> + /** Omits the given properties from this type */ + public omit>(key: K): Type> + /** Omits the given properties from this type */ + public omit(key: K): Type> { + return new Type(T.Type.Omit(this.#schema, key)) + } + /** Applies partial to this type */ + public partial(): Type> { + return new Type(T.Type.Partial(this.#schema)) + } + /** Applies required to this type */ + public required(): Type> { + return new Type(T.Type.Required(this.schema())) + } + /** Returns the keys of this type */ + public keyof(): Type> { + return new Type(T.Type.KeyOf(this.schema())) + } + /** Checks this value and throws if invalid */ + public assert(value: unknown): void { + if (this.#assert.check(value)) return + throw new TypeValueError([...this.#assert.errors(value)]) + } + /** Casts this value into this type */ + public cast(value: unknown): T.Static { + return Value.Cast(this.#schema, [], value) + } + /** Returns true if this value is valid for this type */ + public check(value: unknown): value is T.Static { + return this.#assert.check(value) + } + /** Returns the assertion code for this type */ + public code(): string { + return this.#assert.code() + } + /** Creates a default value for this type */ + public create(): T.Static { + return Value.Create(this.#schema, []) + } + /** Sets a default value for this type */ + public default(value: T.Static): this { + return new Type({ ...this.#schema, default: value }) as this + } + /** Parses the given value and returns the valid if valid. Otherwise throw. */ + public parse(value: unknown): T.Static { + this.assert(value) + return value + } + /** Compiles this type */ + public compile(): this { + const compiled = new Type(this.#schema) + compiled.#assert = new TypeAssertCompiled(this.#schema) + return compiled as this + } + /** Returns the schema associated with this type */ + public schema(): T { + return Value.Clone(this.#schema) + } +} +// ----------------------------------------------------------------------------------------- +// Array +// ----------------------------------------------------------------------------------------- +export class ArrayType> extends Type { + public maxItems(n: number) { + return this.options({ maxItems: n }) + } + public minItems(n: number) { + return this.options({ minItems: n }) + } + public length(n: number) { + return this.options({ minItems: n, maxItems: n }) + } + public uniqueItems() { + return this.options({ uniqueItems: true }) + } +} +// ----------------------------------------------------------------------------------------- +// Object +// ----------------------------------------------------------------------------------------- +export class ObjectType extends Type { + public strict() { + return this.options({ additionalProperties: false }) + } +} +// ----------------------------------------------------------------------------------------- +// String +// ----------------------------------------------------------------------------------------- +export class StringType extends Type { + public minLength(n: number) { + return this.options({ minLength: n }) + } + public maxLength(n: number) { + return this.options({ maxLength: n }) + } + public length(n: number) { + return this.options({ maxLength: n, minLength: n }) + } + public email() { + return this.options({ format: 'email' }) + } + public uuid() { + return this.options({ format: 'uuid' }) + } + public url() { + return this.options({ format: 'url' }) + } + public ipv6() { + return this.options({ format: 'ipv6' }) + } + public ipv4() { + return this.options({ format: 'ipv4' }) + } +} +// ----------------------------------------------------------------------------------------- +// Number +// ----------------------------------------------------------------------------------------- +export class NumberType extends Type { + public exclusiveMinimum(n: number) { + return this.options({ exclusiveMinimum: n }) + } + public minimum(n: number) { + return this.options({ minimum: n }) + } + public exclusiveMaximum(n: number) { + return this.options({ exclusiveMaximum: n }) + } + public maximum(n: number) { + return this.options({ maximum: n }) + } + public multipleOf(n: number) { + return this.options({ multipleOf: n }) + } + public positive() { + return this.options({ minimum: 0 }) + } + public negative() { + return this.options({ maximum: 0 }) + } +} +// ----------------------------------------------------------------------------------------- +// Integer +// ----------------------------------------------------------------------------------------- +export class IntegerType extends Type { + public exclusiveMinimum(n: number) { + return this.options({ exclusiveMinimum: n }) + } + public minimum(n: number) { + return this.options({ minimum: n }) + } + public exclusiveMaximum(n: number) { + return this.options({ exclusiveMaximum: n }) + } + public maximum(n: number) { + return this.options({ maximum: n }) + } + public multipleOf(n: number) { + return this.options({ multipleOf: n }) + } + public positive() { + return this.options({ minimum: 0 }) + } + public negative() { + return this.options({ maximum: 0 }) + } +} +// ----------------------------------------------------------------------------------------- +// BigInt +// ----------------------------------------------------------------------------------------- +export class BigIntType extends Type { + public exclusiveMinimum(n: bigint) { + return this.options({ exclusiveMinimum: n }) + } + public minimum(n: bigint) { + return this.options({ minimum: n }) + } + public exclusiveMaximum(n: bigint) { + return this.options({ exclusiveMaximum: n }) + } + public maximum(n: bigint) { + return this.options({ maximum: n }) + } + public multipleOf(n: bigint) { + return this.options({ multipleOf: n }) + } + public positive() { + return this.options({ minimum: BigInt(0) }) + } + public negative() { + return this.options({ maximum: BigInt(0) }) + } +} +// ----------------------------------------------------------------------------------------- +// Uint8Array +// ----------------------------------------------------------------------------------------- +export class ModelUint8Array extends Type { + public minByteLength(n: number) { + return this.options({ minByteLength: n }) + } + public maxByteLength(n: number) { + return this.options({ maxByteLength: n }) + } +} +// ----------------------------------------------------------------------------------------- +// Record +// ----------------------------------------------------------------------------------------- +export class RecordType extends Type> {} +// ----------------------------------------------------------------------------------------- +// Recursive +// ----------------------------------------------------------------------------------------- +export class ThisType extends Type {} +export class RecursiveType extends Type {} +// ----------------------------------------------------------------------------------------- +// ModelBuilder +// ----------------------------------------------------------------------------------------- +export class ModelBuilder { + /** Creates an any type */ + public any() { + return new Type(T.Type.Any()) + } + /** Creates an array type */ + public array(type: T) { + return new ArrayType(T.Type.Array(type.schema())) + } + /** Creates boolean type */ + public boolean() { + return new Type(T.Type.Boolean()) + } + /** Creates a bigint type */ + public bigint() { + return new BigIntType(T.Type.BigInt()) + } + /** Creates a date type */ + public date() { + return new Type(T.Type.Date()) + } + /** Creates a integer type */ + public integer() { + return new IntegerType(T.Type.Integer()) + } + /** Creates a number type */ + public number() { + return new NumberType(T.Type.Number()) + } + /** Creates a intersect type */ + public intersect(types: [...T]): Type>> { + const internal = types.map((type) => type.schema()) + return new Type(T.Type.Intersect(internal)) as any + } + /** Creates an keyof type */ + public keyof(type: T): Type>> { + return new Type(T.Type.KeyOf(type.schema())) as any + } + /** Creates a literal type */ + public literal(value: T) { + return new Type(T.Type.Literal(value)) + } + /** Creates a never type */ + public never() { + return new Type(T.Type.Never()) + } + /** Creates a null type */ + public null() { + return new Type(T.Type.Null()) + } + /** Creates a object type */ + public object(properties: T): Type>> { + const mapped = Object.keys(properties).reduce((acc, key) => ({ ...acc, [key]: properties[key].schema() }), {} as T.TProperties) + return new ObjectType(T.Type.Object(mapped)) as any + } + /** Creates a partial type */ + public partial(type: T): Type> { + return new Type(T.Type.Partial(type.schema())) as any + } + /** Creates a promise type */ + public promise(type: T): Type> { + return new Type(T.Type.Promise(type.schema())) as any + } + /** Creates an unknown type */ + public unknown() { + return new Type(T.Type.Unknown()) + } + /** Creates a record type */ + public record(type: T): RecordType> { + return new Type(T.Type.Record(T.Type.String(), type.schema())) as any + } + /** Creates a recursive type */ + public recursive(callback: (This: ThisType) => Type) { + // prettier-ignore + return new Type(T.Type.Recursive((This) => callback(new ThisType(This)).schema())) + } + /** Creates a required type */ + public required(type: T) { + return new Type(T.Type.Required(type.schema())) + } + /** Creates a string type */ + public string() { + return new StringType(T.Type.String()) + } + /** Creates a symbol type */ + public symbol() { + return new Type(T.Type.Symbol()) + } + /** Creates a template literal type */ + public templateLiteral(types: [...T]): Type>> { + const mapped = types.map((type) => type.schema()) + return new Type(T.Type.TemplateLiteral(mapped as any)) + } + /** Creates a tuple type */ + public tuple(types: [...T]): Type>> { + return new Type(T.Type.Tuple(types.map((type) => type.schema()))) as any + } + /** Creates a uint8array type */ + public uint8array() { + return new Type(T.Type.Uint8Array()) + } + /** Creates an undefined type */ + public undefined() { + return new Type(T.Type.Undefined()) + } + /** Creates a union type */ + public union(union: [...T]): Type>> { + const mapped = union.map((type) => type.schema()) + return new Type(T.Type.Union(mapped)) as any + } +} + +export type Infer = T extends Type ? T.Static : unknown + +export const TypeBuilder = new ModelBuilder() + +export default TypeBuilder From 6cfcdc02cc813af2f1be57407c771fc4fadfc34a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 15 Apr 2023 15:07:51 +0900 Subject: [PATCH 108/369] Support Unregistered Kinds in ObjectMap (#385) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 22 +++++++++++----------- test/runtime/compiler/omit.ts | 4 ++-- test/runtime/compiler/partial.ts | 16 ---------------- test/runtime/type/guard/index.ts | 3 +++ test/runtime/type/guard/omit.ts | 17 +++++++++++++++++ test/runtime/type/guard/partial.ts | 23 +++++++++++++++++++++++ test/runtime/type/guard/pick.ts | 17 +++++++++++++++++ 9 files changed, 76 insertions(+), 32 deletions(-) create mode 100644 test/runtime/type/guard/omit.ts create mode 100644 test/runtime/type/guard/partial.ts create mode 100644 test/runtime/type/guard/pick.ts diff --git a/package-lock.json b/package-lock.json index 841d11cd1..9fa990515 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.7", + "version": "0.27.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.7", + "version": "0.27.8", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 4f0e07a85..eeff01d95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.7", + "version": "0.27.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 6bd693dd6..687c02be0 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1832,24 +1832,24 @@ export namespace TypeClone { // -------------------------------------------------------------------------- export namespace ObjectMap { function Intersect(schema: TIntersect, callback: (object: TObject) => TObject) { - return Type.Intersect( - schema.allOf.map((inner) => Visit(inner, callback)), - { ...schema }, - ) + // prettier-ignore + return Type.Intersect(schema.allOf.map((inner) => Visit(inner, callback)), { ...schema }) } function Union(schema: TUnion, callback: (object: TObject) => TObject) { - return Type.Union( - schema.anyOf.map((inner) => Visit(inner, callback)), - { ...schema }, - ) + // prettier-ignore + return Type.Union(schema.anyOf.map((inner) => Visit(inner, callback)), { ...schema }) } function Object(schema: TObject, callback: (object: TObject) => TObject) { return callback(schema) } function Visit(schema: TSchema, callback: (object: TObject) => TObject): TSchema { - if (TypeGuard.TIntersect(schema)) return Intersect(schema, callback) - if (TypeGuard.TUnion(schema)) return Union(schema, callback) - if (TypeGuard.TObject(schema)) return Object(schema, callback) + // There are cases where users need to map objects with unregistered kinds. Using a TypeGuard here would + // prevent sub schema mapping as unregistered kinds will not pass TSchema checks. This is notable in the + // case of TObject where unregistered property kinds cause the TObject check to fail. As mapping is only + // used for composition, we use explicit checks instead. + if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, callback) + if (schema[Kind] === 'Union') return Union(schema as TUnion, callback) + if (schema[Kind] === 'Object') return Object(schema as TObject, callback) return schema } export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts index 9d4ed2723..9c38ff7c3 100644 --- a/test/runtime/compiler/omit.ts +++ b/test/runtime/compiler/omit.ts @@ -1,6 +1,6 @@ -import { Type } from '@sinclair/typebox' +import { Type, Kind } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' +import { deepEqual, strictEqual } from 'assert' describe('type/compiler/Omit', () => { it('Should omit properties on the source schema', () => { diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index e295aa6b9..684282403 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -50,20 +50,4 @@ describe('type/compiler/Partial', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) - it('Should support partial properties of raw TUnsafe', () => { - // https://github.com/sinclairzx81/typebox/issues/364 - const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) - strictEqual(T.required, undefined) - }) - it('Should not support partial properties of unknown TUnsafe', () => { - // https://github.com/sinclairzx81/typebox/issues/364 - const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) - strictEqual(T.required![0], 'x') - }) - it('Should support partial properties of custom TUnsafe', () => { - // https://github.com/sinclairzx81/typebox/issues/364 - const U = TypeSystem.Type('CustomPartialType', () => true) - const T = Type.Partial(Type.Object({ x: U() })) - strictEqual(T.required, undefined) - }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index d40906195..d88632516 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -16,6 +16,9 @@ import './not' import './null' import './number' import './object' +import './omit' +import './partial' +import './pick' import './promise' import './record' import './ref' diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts new file mode 100644 index 000000000..7a6055a27 --- /dev/null +++ b/test/runtime/type/guard/omit.ts @@ -0,0 +1,17 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TOmit', () => { + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/384 + // ------------------------------------------------------------------------- + it('Should support TUnsafe omit properties with no Kind', () => { + const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) + Assert.deepEqual(T.required, ['y']) + }) + it('Should support TUnsafe omit properties with unregistered Kind', () => { + const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownOmitType' }), y: Type.Number() }), ['x']) + Assert.deepEqual(T.required, ['y']) + }) +}) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts new file mode 100644 index 000000000..5eb292ca9 --- /dev/null +++ b/test/runtime/type/guard/partial.ts @@ -0,0 +1,23 @@ +import { TypeSystem } from '@sinclair/typebox/system' +import { TypeGuard } from '@sinclair/typebox' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TPartial', () => { + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/364 + // ------------------------------------------------------------------------- + it('Should support TUnsafe partial properties with no Kind', () => { + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) + Assert.deepEqual(T.required, undefined) + }) + it('Should support TUnsafe partial properties with unregistered Kind', () => { + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) + Assert.deepEqual(T.required, undefined) + }) + it('Should support TUnsafe partial properties with registered Kind', () => { + const U = TypeSystem.Type('CustomPartialType', () => true) + const T = Type.Partial(Type.Object({ x: U() })) + Assert.deepEqual(T.required, undefined) + }) +}) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts new file mode 100644 index 000000000..65767c95d --- /dev/null +++ b/test/runtime/type/guard/pick.ts @@ -0,0 +1,17 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TPick', () => { + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/384 + // ------------------------------------------------------------------------- + it('Should support TUnsafe omit properties with no Kind', () => { + const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) + Assert.deepEqual(T.required, ['x']) + }) + it('Should support TUnsafe omit properties with unregistered Kind', () => { + const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownPickType' }), y: Type.Number() }), ['x']) + Assert.deepEqual(T.required, ['x']) + }) +}) From 8f3755792635217dcb6bc028dfb11c279b7665aa Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 21 Apr 2023 06:42:32 +0900 Subject: [PATCH 109/369] Revision 0.28.0 (#395) --- .github/workflows/ci.yml | 2 +- changelog/0.28.0.md | 199 ++++++++++++ package-lock.json | 4 +- package.json | 2 +- readme.md | 161 ++++++---- src/compiler/compiler.ts | 38 +-- src/errors/errors.ts | 49 +-- src/system/system.ts | 13 - src/typebox.ts | 441 ++++++++++++++++++-------- src/value/check.ts | 42 +-- test/runtime/compiler/intersect.ts | 136 ++++++++ test/runtime/compiler/record.ts | 57 +++- test/runtime/schema/intersect.ts | 136 ++++++++ test/runtime/schema/record.ts | 39 ++- test/runtime/system/system.ts | 8 +- test/runtime/type/guard/composite.ts | 10 +- test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/indexed.ts | 98 ++++++ test/runtime/type/guard/keyof.ts | 27 +- test/runtime/type/guard/omit.ts | 89 ++++++ test/runtime/type/guard/pick.ts | 93 +++++- test/runtime/value/check/intersect.ts | 135 ++++++++ test/runtime/value/check/record.ts | 32 +- 23 files changed, 1491 insertions(+), 321 deletions(-) create mode 100644 changelog/0.28.0.md create mode 100644 test/runtime/type/guard/indexed.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a07597518..b1192b393 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - node: [14.x, 16.x, 18.x] + node: [16.x, 18.x, 20.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - uses: actions/checkout@v2 diff --git a/changelog/0.28.0.md b/changelog/0.28.0.md new file mode 100644 index 000000000..3ed1851fe --- /dev/null +++ b/changelog/0.28.0.md @@ -0,0 +1,199 @@ +## [0.28.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.28.0) + +## Overview + +Revision 0.28.0 adds support for Indexed Access Types. This update also includes moderate breaking changes to Record and Composite types and does require a minor semver revision tick. + +## Contents + +- Enhancements + - [Indexed Access Types](#Indexed-Access-Types) + - [KeyOf Tuple and Array](#KeyOf-Tuple-and-Array) +- Breaking Changes + - [Record Types Allow Additional Properties By Default](#Record-Types-Allow-Additional-Properties-By-Default) + - [Composite Returns Intersect for Overlapping Properties](#Composite-Returns-Intersect-for-Overlapping-Properties) + + + +## Indexed Access Types + +Revision 0.28.0 adds [Indexed Access Type](https://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html) support with a new `Type.Index()` mapping type. These types allow for deep property lookups without needing to prop dive through JSON Schema properties. This type is based on the TypeScript implementation of Indexed Access Types and allows for generalized selection of properties for complex types irrespective of if that type is a Object, Union, Intersection, Array or Tuple. + +```typescript +// ---------------------------------------------------------- +// The following types A and B are structurally equivalent, +// but have varying JSON Schema representations. +// ---------------------------------------------------------- +const A = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean(), +}) + +const B = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.String() }), + Type.Object({ z: Type.Boolean() }) +]) + +// ---------------------------------------------------------- +// TypeBox 0.27.0 - Non Uniform +// ---------------------------------------------------------- +const A_X = A.properties.x // TNumber +const A_Y = A.properties.y // TString +const A_Z = A.properties.z // TBoolean + +const B_X = B.allOf[0].properties.x // TNumber +const B_Y = B.allOf[1].properties.y // TString +const B_Z = B.allOf[2].properties.z // TBoolean + +// ---------------------------------------------------------- +// TypeBox 0.28.0 - Uniform via Type.Index +// ---------------------------------------------------------- +const A_X = Type.Index(A, ['x']) // TNumber +const A_Y = Type.Index(A, ['y']) // TString +const A_Z = Type.Index(A, ['z']) // TBoolean + +const B_X = Type.Index(B, ['x']) // TNumber +const B_Y = Type.Index(B, ['y']) // TString +const B_Z = Type.Index(B, ['z']) // TBoolean +``` +Indexed Access Types support has also been extended to Tuple and Array types. +```typescript +// ----------------------------------------------------------- +// Array +// ----------------------------------------------------------- +type T = string[] + +type I = T[number] // type T = string + +const T = Type.Array(Type.String()) + +const I = Type.Index(T, Type.Number()) // const I = TString + +// ----------------------------------------------------------- +// Tuple +// ----------------------------------------------------------- +type T = ['A', 'B', 'C'] + +type I = T[0 | 1] // type I = 'A' | 'B' + +const T = Type.Array(Type.String()) + +const I = Type.Index(T, Type.Union([ // const I = TUnion<[ + Type.Literal(0), // TLiteral<'A'>, + Type.Literal(1), // TLiteral<'B'> +])) // ]> +``` + + + +## KeyOf Tuple and Array + +Revision 0.28.0 includes additional `Type.KeyOf` support for Array and Tuple types. Keys of Array will always return `TNumber`, whereas keys of Tuple will return a LiteralUnion for each index of that tuple. + +```typescript +// ----------------------------------------------------------- +// KeyOf: Tuple +// ----------------------------------------------------------- +const T = Type.Tuple([Type.Number(), Type.Number(), Type.Number()]) + +const K = Type.KeyOf(T) // const K = TUnion<[ + // TLiteral<'0'>, + // TLiteral<'1'>, + // TLiteral<'2'>, + // ]> + +// ----------------------------------------------------------- +// KeyOf: Array +// ----------------------------------------------------------- +const T = Type.Array(Type.String()) + +const K = Type.KeyOf(T) // const K = TNumber +``` +It is possible to combine KeyOf with Index types to extract properties from array and object constructs. +```typescript +// ----------------------------------------------------------- +// KeyOf + Index: Object +// ----------------------------------------------------------- +const T = Type.Object({ x: Type.Number(), y: Type.String(), z: Type.Boolean() }) + +const K = Type.Index(T, Type.KeyOf(T)) // const K = TUnion<[ + // TNumber, + // TString, + // TBoolean, + // ]> + +// ----------------------------------------------------------- +// KeyOf + Index: Tuple +// ----------------------------------------------------------- +const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) + +const K = Type.Index(T, Type.KeyOf(T)) // const K = TUnion<[ + // TNumber, + // TString, + // TBoolean, + // ]> +``` +## Breaking Changes + +The following are breaking changes in Revision 0.28.0 + + + +## Record Types Allow Additional Properties By Default + +Revision 0.28.0 no longer applies an automatic `additionalProperties: false` constraint to types of `TRecord`. Previously this constraint was set to prevent records with numeric keys from allowing unevaluated additional properties with non-numeric keys. This constraint worked in revisions up to 0.26.0, but since the move to use `allOf` intersect schema representations, this meant that types of Record could no longer be composed with intersections. This is due to the JSON Schema rules around extending closed schemas. Information on these rules can be found at the link below. + +https://json-schema.org/understanding-json-schema/reference/object.html#extending-closed-schemas + +For the most part, the omission of this constraint shouldn't impact existing record types with string keys, however numeric keys may cause problems. Consider the following where the validation unexpectedly succeeds for the following numeric keyed record. + +```typescript +const T = Type.Record(Type.Number(), Type.String()) + +const R = Value.Check(T, { a: null }) // true - Because `a` is non-numeric and thus is treated as an + // additional unevaluated property. +``` +Moving forward, Records with numeric keys "should" be constrained explicitly with `additionalProperties: false` via options if that record does not require composition through intersection. This is largely inline with the existing constraints one might apply to types of Object. + +```typescript +const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: false +}) + +const R = Value.Check(T, { a: null }) // false - Because `a` is non-numeric additional property +``` + + + +## Composite Returns Intersect for Overlapping Properties + +This is a minor breaking change with respect to the schema returned for Composite objects with overlapping varying property types. Previously TypeBox would evaluate `TNever` by performing an internal `extends` check against each overlapping property type. However problems emerged using this implementation for users who needed to use Composite with types of `TUnsafe`. This is due to unsafe types being incompatible with TypeBox's internal extends logic. + +The solution implemented in 0.28.0 is to return the full intersection of all overlapping properties. The reasoning here is that if the overlapping properties of varying types result in an illogical intersection, this is semantically the same as resolving `never` for that property. This approach avoids the need to internally check if all overlapping properties extend or narrow one another. + +```typescript +const T = Type.Composite([ + Type.Object({ x: Type.Number() }), // overlapping property 'x' of varying type + Type.Object({ x: Type.String() }) +]) + +// ----------------------------------------------------------- +// Revision 0.27.0 +// ----------------------------------------------------------- +const R = Type.Object({ + x: Type.Never() // Never evaluated through extends checks. +}) + +// ----------------------------------------------------------- +// Revision 0.28.0 +// ----------------------------------------------------------- +const R = Type.Object({ + x: Type.Intersect([ // Illogical intersections are semantically the same as never + Type.Number(), + Type.String() + ]) +}) +``` +This implementation should make it more clear what the internal mechanics are for object compositing. Future revisions of TypeBox may however provide a utility function to test illogical intersections for Never for known types. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9fa990515..deb9afe98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.27.8", + "version": "0.28.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.27.8", + "version": "0.28.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index eeff01d95..24257708f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.27.8", + "version": "0.28.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index c5120a9d5..5155d455c 100644 --- a/readme.md +++ b/readme.md @@ -81,7 +81,8 @@ License MIT - [References](#types-references) - [Recursive](#types-recursive) - [Conditional](#types-conditional) - - [Template Literal](#types-template-literal) + - [Template](#types-template-literal) + - [Indexed](#types-indexed) - [Guards](#types-guards) - [Unsafe](#types-unsafe) - [Strict](#types-strict) @@ -276,6 +277,14 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Index( │ type T = { │ const T = { │ +│ Type.Object({ │ x: number, │ type: number │ +│ x: Type.Number(), │ y: string │ } │ +│ y: Type.String() │ }['x'] │ │ +│ }), ['x'] │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ enum Foo { │ enum Foo { │ const T = { │ │ A, │ A, │ anyOf: [{ │ │ B │ B │ type: 'number', │ @@ -830,6 +839,24 @@ type R = Static // type R = { // } ``` + + +### Indexed Access Types + +Indexed Access types are supported with `Type.Index` + +```typescript +const T = Type.Object({ // type T = { + x: Type.Number(), // x: number + y: Type.String(), // y: string + z: Type.Boolean() // z: boolean +}) // } + +const A = Type.Index(T, ['x']) // type A = T['x'] + +const B = Type.Index(T, Type.KeyOf(T)) // type B = T[keyof T] +``` + ### Unsafe @@ -1309,7 +1336,7 @@ TypeSystem.AllowNaN = true ## Benchmark -This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.12.0. +This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.12.0 running on Node 20.0.0. For additional comparative benchmarks, please refer to [typescript-runtime-type-benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/). @@ -1323,35 +1350,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 257 ms' │ ' 8 ms' │ ' 32.13 x' │ -│ Literal_Number │ 1000 │ ' 203 ms' │ ' 4 ms' │ ' 50.75 x' │ -│ Literal_Boolean │ 1000 │ ' 183 ms' │ ' 4 ms' │ ' 45.75 x' │ -│ Primitive_Number │ 1000 │ ' 174 ms' │ ' 8 ms' │ ' 21.75 x' │ -│ Primitive_String │ 1000 │ ' 158 ms' │ ' 9 ms' │ ' 17.56 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 213 ms' │ ' 13 ms' │ ' 16.38 x' │ -│ Primitive_Boolean │ 1000 │ ' 136 ms' │ ' 6 ms' │ ' 22.67 x' │ -│ Primitive_Null │ 1000 │ ' 144 ms' │ ' 6 ms' │ ' 24.00 x' │ -│ Object_Unconstrained │ 1000 │ ' 1176 ms' │ ' 38 ms' │ ' 30.95 x' │ -│ Object_Constrained │ 1000 │ ' 1181 ms' │ ' 31 ms' │ ' 38.10 x' │ -│ Object_Vector3 │ 1000 │ ' 387 ms' │ ' 8 ms' │ ' 48.38 x' │ -│ Object_Box3D │ 1000 │ ' 1693 ms' │ ' 25 ms' │ ' 67.72 x' │ -│ Tuple_Primitive │ 1000 │ ' 470 ms' │ ' 15 ms' │ ' 31.33 x' │ -│ Tuple_Object │ 1000 │ ' 1206 ms' │ ' 17 ms' │ ' 70.94 x' │ -│ Composite_Intersect │ 1000 │ ' 567 ms' │ ' 20 ms' │ ' 28.35 x' │ -│ Composite_Union │ 1000 │ ' 515 ms' │ ' 21 ms' │ ' 24.52 x' │ -│ Math_Vector4 │ 1000 │ ' 787 ms' │ ' 10 ms' │ ' 78.70 x' │ -│ Math_Matrix4 │ 1000 │ ' 386 ms' │ ' 8 ms' │ ' 48.25 x' │ -│ Array_Primitive_Number │ 1000 │ ' 349 ms' │ ' 7 ms' │ ' 49.86 x' │ -│ Array_Primitive_String │ 1000 │ ' 336 ms' │ ' 4 ms' │ ' 84.00 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 284 ms' │ ' 3 ms' │ ' 94.67 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1704 ms' │ ' 19 ms' │ ' 89.68 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1456 ms' │ ' 18 ms' │ ' 80.89 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 792 ms' │ ' 15 ms' │ ' 52.80 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1552 ms' │ ' 17 ms' │ ' 91.29 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 744 ms' │ ' 18 ms' │ ' 41.33 x' │ -│ Array_Composite_Union │ 1000 │ ' 783 ms' │ ' 15 ms' │ ' 52.20 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1093 ms' │ ' 14 ms' │ ' 78.07 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 684 ms' │ ' 6 ms' │ ' 114.00 x' │ +│ Literal_String │ 1000 │ ' 243 ms' │ ' 8 ms' │ ' 30.38 x' │ +│ Literal_Number │ 1000 │ ' 195 ms' │ ' 5 ms' │ ' 39.00 x' │ +│ Literal_Boolean │ 1000 │ ' 162 ms' │ ' 4 ms' │ ' 40.50 x' │ +│ Primitive_Number │ 1000 │ ' 168 ms' │ ' 6 ms' │ ' 28.00 x' │ +│ Primitive_String │ 1000 │ ' 164 ms' │ ' 5 ms' │ ' 32.80 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 214 ms' │ ' 9 ms' │ ' 23.78 x' │ +│ Primitive_Boolean │ 1000 │ ' 132 ms' │ ' 4 ms' │ ' 33.00 x' │ +│ Primitive_Null │ 1000 │ ' 148 ms' │ ' 4 ms' │ ' 37.00 x' │ +│ Object_Unconstrained │ 1000 │ ' 1158 ms' │ ' 30 ms' │ ' 38.60 x' │ +│ Object_Constrained │ 1000 │ ' 1263 ms' │ ' 25 ms' │ ' 50.52 x' │ +│ Object_Vector3 │ 1000 │ ' 384 ms' │ ' 7 ms' │ ' 54.86 x' │ +│ Object_Box3D │ 1000 │ ' 1932 ms' │ ' 27 ms' │ ' 71.56 x' │ +│ Tuple_Primitive │ 1000 │ ' 478 ms' │ ' 14 ms' │ ' 34.14 x' │ +│ Tuple_Object │ 1000 │ ' 1232 ms' │ ' 14 ms' │ ' 88.00 x' │ +│ Composite_Intersect │ 1000 │ ' 671 ms' │ ' 17 ms' │ ' 39.47 x' │ +│ Composite_Union │ 1000 │ ' 537 ms' │ ' 18 ms' │ ' 29.83 x' │ +│ Math_Vector4 │ 1000 │ ' 816 ms' │ ' 14 ms' │ ' 58.29 x' │ +│ Math_Matrix4 │ 1000 │ ' 417 ms' │ ' 6 ms' │ ' 69.50 x' │ +│ Array_Primitive_Number │ 1000 │ ' 378 ms' │ ' 5 ms' │ ' 75.60 x' │ +│ Array_Primitive_String │ 1000 │ ' 353 ms' │ ' 6 ms' │ ' 58.83 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 279 ms' │ ' 5 ms' │ ' 55.80 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1794 ms' │ ' 20 ms' │ ' 89.70 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1586 ms' │ ' 19 ms' │ ' 83.47 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 791 ms' │ ' 13 ms' │ ' 60.85 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1638 ms' │ ' 17 ms' │ ' 96.35 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 796 ms' │ ' 17 ms' │ ' 46.82 x' │ +│ Array_Composite_Union │ 1000 │ ' 798 ms' │ ' 15 ms' │ ' 53.20 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1127 ms' │ ' 14 ms' │ ' 80.50 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 677 ms' │ ' 9 ms' │ ' 75.22 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1365,37 +1392,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 27 ms' │ ' 6 ms' │ ' 5 ms' │ ' 1.20 x' │ -│ Literal_Number │ 1000000 │ ' 23 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Literal_Boolean │ 1000000 │ ' 21 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Primitive_Number │ 1000000 │ ' 26 ms' │ ' 19 ms' │ ' 11 ms' │ ' 1.73 x' │ -│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 155 ms' │ ' 49 ms' │ ' 43 ms' │ ' 1.14 x' │ -│ Primitive_Boolean │ 1000000 │ ' 23 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_Null │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Object_Unconstrained │ 1000000 │ ' 804 ms' │ ' 35 ms' │ ' 28 ms' │ ' 1.25 x' │ -│ Object_Constrained │ 1000000 │ ' 1041 ms' │ ' 55 ms' │ ' 41 ms' │ ' 1.34 x' │ -│ Object_Vector3 │ 1000000 │ ' 380 ms' │ ' 26 ms' │ ' 20 ms' │ ' 1.30 x' │ -│ Object_Box3D │ 1000000 │ ' 1785 ms' │ ' 65 ms' │ ' 52 ms' │ ' 1.25 x' │ -│ Object_Recursive │ 1000000 │ ' 4984 ms' │ ' 396 ms' │ ' 114 ms' │ ' 3.47 x' │ -│ Tuple_Primitive │ 1000000 │ ' 168 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ -│ Tuple_Object │ 1000000 │ ' 673 ms' │ ' 30 ms' │ ' 26 ms' │ ' 1.15 x' │ -│ Composite_Intersect │ 1000000 │ ' 751 ms' │ ' 28 ms' │ ' 20 ms' │ ' 1.40 x' │ -│ Composite_Union │ 1000000 │ ' 489 ms' │ ' 24 ms' │ ' 16 ms' │ ' 1.50 x' │ -│ Math_Vector4 │ 1000000 │ ' 259 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1002 ms' │ ' 40 ms' │ ' 30 ms' │ ' 1.33 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 252 ms' │ ' 22 ms' │ ' 15 ms' │ ' 1.47 x' │ -│ Array_Primitive_String │ 1000000 │ ' 227 ms' │ ' 22 ms' │ ' 18 ms' │ ' 1.22 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 150 ms' │ ' 23 ms' │ ' 22 ms' │ ' 1.05 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 4754 ms' │ ' 71 ms' │ ' 64 ms' │ ' 1.11 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 4787 ms' │ ' 142 ms' │ ' 123 ms' │ ' 1.15 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 19088 ms' │ ' 1735 ms' │ ' 314 ms' │ ' 5.53 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 650 ms' │ ' 41 ms' │ ' 31 ms' │ ' 1.32 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 2770 ms' │ ' 67 ms' │ ' 55 ms' │ ' 1.22 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 2693 ms' │ ' 50 ms' │ ' 39 ms' │ ' 1.28 x' │ -│ Array_Composite_Union │ 1000000 │ ' 1982 ms' │ ' 72 ms' │ ' 33 ms' │ ' 2.18 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1068 ms' │ ' 40 ms' │ ' 26 ms' │ ' 1.54 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4609 ms' │ ' 115 ms' │ ' 88 ms' │ ' 1.31 x' │ +│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ +│ Literal_Boolean │ 1000000 │ ' 19 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_Number │ 1000000 │ ' 24 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Primitive_String │ 1000000 │ ' 26 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 159 ms' │ ' 45 ms' │ ' 36 ms' │ ' 1.25 x' │ +│ Primitive_Boolean │ 1000000 │ ' 22 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ +│ Primitive_Null │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Object_Unconstrained │ 1000000 │ ' 914 ms' │ ' 34 ms' │ ' 26 ms' │ ' 1.31 x' │ +│ Object_Constrained │ 1000000 │ ' 1095 ms' │ ' 50 ms' │ ' 38 ms' │ ' 1.32 x' │ +│ Object_Vector3 │ 1000000 │ ' 414 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Object_Box3D │ 1000000 │ ' 1933 ms' │ ' 55 ms' │ ' 53 ms' │ ' 1.04 x' │ +│ Object_Recursive │ 1000000 │ ' 4995 ms' │ ' 378 ms' │ ' 177 ms' │ ' 2.14 x' │ +│ Tuple_Primitive │ 1000000 │ ' 168 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ +│ Tuple_Object │ 1000000 │ ' 681 ms' │ ' 31 ms' │ ' 19 ms' │ ' 1.63 x' │ +│ Composite_Intersect │ 1000000 │ ' 718 ms' │ ' 25 ms' │ ' 15 ms' │ ' 1.67 x' │ +│ Composite_Union │ 1000000 │ ' 511 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ +│ Math_Vector4 │ 1000000 │ ' 285 ms' │ ' 23 ms' │ ' 12 ms' │ ' 1.92 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1197 ms' │ ' 39 ms' │ ' 28 ms' │ ' 1.39 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 294 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ +│ Array_Primitive_String │ 1000000 │ ' 251 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 131 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 5249 ms' │ ' 69 ms' │ ' 56 ms' │ ' 1.23 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5299 ms' │ ' 127 ms' │ ' 123 ms' │ ' 1.03 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 19609 ms' │ ' 1711 ms' │ ' 608 ms' │ ' 2.81 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 734 ms' │ ' 38 ms' │ ' 30 ms' │ ' 1.27 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 2843 ms' │ ' 63 ms' │ ' 51 ms' │ ' 1.24 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 2794 ms' │ ' 43 ms' │ ' 36 ms' │ ' 1.19 x' │ +│ Array_Composite_Union │ 1000000 │ ' 1892 ms' │ ' 66 ms' │ ' 33 ms' │ ' 2.00 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1177 ms' │ ' 37 ms' │ ' 23 ms' │ ' 1.61 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 5115 ms' │ ' 110 ms' │ ' 85 ms' │ ' 1.29 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1409,11 +1436,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '124.3 kb' │ ' 55.7 kb' │ '2.23 x' │ -│ typebox/errors │ '107.8 kb' │ ' 47.9 kb' │ '2.25 x' │ -│ typebox/system │ ' 73.3 kb' │ ' 30.2 kb' │ '2.43 x' │ -│ typebox/value │ '170.7 kb' │ ' 74.2 kb' │ '2.30 x' │ -│ typebox │ ' 72.0 kb' │ ' 29.7 kb' │ '2.43 x' │ +│ typebox/compiler │ '127.2 kb' │ ' 56.9 kb' │ '2.23 x' │ +│ typebox/errors │ '110.9 kb' │ ' 49.2 kb' │ '2.25 x' │ +│ typebox/system │ ' 76.4 kb' │ ' 31.5 kb' │ '2.42 x' │ +│ typebox/value │ '176.9 kb' │ ' 76.8 kb' │ '2.30 x' │ +│ typebox │ ' 75.3 kb' │ ' 31.1 kb' │ '2.42 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 5d80435ff..02591e1eb 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -209,22 +209,17 @@ export namespace TypeCompiler { if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` } function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { - if (schema.unevaluatedProperties === undefined) { - const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) - yield `${expressions.join(' && ')}` - } else if (schema.unevaluatedProperties === false) { - // prettier-ignore - const schemaKeys = Types.KeyResolver.Resolve(schema).map((key) => `'${key}'`).join(', ') - const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) - const expression1 = `Object.getOwnPropertyNames(${value}).every(key => [${schemaKeys}].includes(key))` - yield `${expressions.join(' && ')} && ${expression1}` - } else if (typeof schema.unevaluatedProperties === 'object') { - // prettier-ignore - const schemaKeys = Types.KeyResolver.Resolve(schema).map((key) => `'${key}'`).join(', ') - const expressions = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) - const expression1 = CreateExpression(schema.unevaluatedProperties, references, 'value[key]') - const expression2 = `Object.getOwnPropertyNames(${value}).every(key => [${schemaKeys}].includes(key) || ${expression1})` - yield `${expressions.join(' && ')} && ${expression2}` + const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ') + if (schema.unevaluatedProperties === false) { + const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))` + yield `${check1} && ${check2}` + } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { + const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, 'value[key]')})` + yield `${check1} && ${check2}` + } else { + yield `${check1}` } } function* Literal(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { @@ -291,11 +286,12 @@ export namespace TypeCompiler { yield IsRecordCheck(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` - const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const local = PushLocal(`new RegExp(/${keyPattern}/)`) - yield `(Object.getOwnPropertyNames(${value}).every(key => ${local}.test(key)))` - const expression = CreateExpression(valueSchema, references, 'value') - yield `Object.values(${value}).every(value => ${expression})` + const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const local = PushLocal(`new RegExp(/${patternKey}/)`) + const check1 = CreateExpression(patternSchema, references, value) + const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' + const expression = `(${local}.test(key) ? ${check1} : ${check2})` + yield `(Object.entries(${value}).every(([key, value]) => ${expression}))` } function* Ref(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { const index = references.findIndex((foreign) => foreign.$id === schema.$ref) diff --git a/src/errors/errors.ts b/src/errors/errors.ts index be83707c9..6b637d347 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -219,7 +219,7 @@ export namespace ValueErrors { function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { yield* Visit(schema.returns, references, path, value.prototype) } - function* Date(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + function* Date(schema: Types.TDate, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value instanceof globalThis.Date)) { return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } } @@ -244,7 +244,7 @@ export namespace ValueErrors { return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } } } - function* Integer(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + function* Integer(schema: Types.TInteger, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!IsInteger(value)) { return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } } @@ -265,8 +265,8 @@ export namespace ValueErrors { } } function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { - for (const subschema of schema.allOf) { - const next = Visit(subschema, references, path, value).next() + for (const inner of schema.allOf) { + const next = Visit(inner, references, path, value).next() if (!next.done) { yield next.value yield { type: ValueErrorType.Intersect, schema, path, value, message: `Expected all sub schemas to be valid` } @@ -274,19 +274,17 @@ export namespace ValueErrors { } } if (schema.unevaluatedProperties === false) { - const schemaKeys = Types.KeyResolver.Resolve(schema) - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - for (const valueKey of valueKeys) { - if (!schemaKeys.includes(valueKey)) { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + for (const valueKey of globalThis.Object.getOwnPropertyNames(value)) { + if (!keyCheck.test(valueKey)) { yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Unexpected property` } } } } if (typeof schema.unevaluatedProperties === 'object') { - const schemaKeys = Types.KeyResolver.Resolve(schema) - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - for (const valueKey of valueKeys) { - if (!schemaKeys.includes(valueKey)) { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + for (const valueKey of globalThis.Object.getOwnPropertyNames(value)) { + if (!keyCheck.test(valueKey)) { const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() if (!next.done) { yield next.value @@ -317,7 +315,7 @@ export namespace ValueErrors { return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } } } - function* Number(schema: Types.TNumeric, references: Types.TSchema[], path: string, value: any): IterableIterator { + function* Number(schema: Types.TNumber, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!IsNumber(value)) { return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } @@ -396,16 +394,21 @@ export namespace ValueErrors { if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } } - const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const regex = new RegExp(keyPattern) - if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { - const numeric = keyPattern === Types.PatternNumberExact - const type = numeric ? ValueErrorType.RecordKeyNumeric : ValueErrorType.RecordKeyString - const message = numeric ? 'Expected all object property keys to be numeric' : 'Expected all object property keys to be strings' - return yield { type, schema, path, value, message } - } - for (const [propKey, propValue] of globalThis.Object.entries(value)) { - yield* Visit(valueSchema, references, `${path}/${propKey}`, propValue) + const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const regex = new RegExp(patternKey) + for (const [propertyKey, propertyValue] of globalThis.Object.entries(value)) { + if (regex.test(propertyKey)) { + yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) + continue + } + if (typeof schema.additionalProperties === 'object') { + yield* Visit(schema.additionalProperties, references, `${path}/${propertyKey}`, propertyValue) + } + if (schema.additionalProperties === false) { + const propertyPath = `${path}/${propertyKey}` + const message = `Unexpected property '${propertyPath}'` + return yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: propertyPath, value: propertyValue, message } + } } } function* Ref(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { diff --git a/src/system/system.ts b/src/system/system.ts index a7dda5804..880f800e4 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -52,7 +52,6 @@ export namespace TypeSystem { export let AllowNaN: boolean = false /** Sets whether `null` should validate for void types. The default is `false` */ export let AllowVoidNull: boolean = false - // ------------------------------------------------------------------------ // String Formats and Types // ------------------------------------------------------------------------ @@ -68,16 +67,4 @@ export namespace TypeSystem { Types.FormatRegistry.Set(format, check) return format } - - // ------------------------------------------------------------------------ - // Deprecated - // ------------------------------------------------------------------------ - /** @deprecated Use `TypeSystem.Type()` instead. */ - export function CreateType(kind: string, check: (options: Options, value: unknown) => boolean) { - return Type(kind, check) - } - /** @deprecated Use `TypeSystem.Format()` instead. */ - export function CreateFormat(format: F, check: (value: string) => boolean): F { - return Format(format, check) - } } diff --git a/src/typebox.ts b/src/typebox.ts index 687c02be0..a76c35e3f 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -53,6 +53,22 @@ export type Assert = T extends E ? T : never export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never export type Ensure = T extends infer U ? U : never // -------------------------------------------------------------------------- +// Type Assertions +// -------------------------------------------------------------------------- +export type AssertProperties = T extends TProperties ? T : TProperties +export type AssertSchemas = T extends E ? T : [] +export type AssertSchema = T extends E ? T : TNever +export type AssertObjects = Assert +export type AssertObject = Assert +export type AssertKeys = Assert +export type AssertKey = Assert +export type Key = keyof any +// -------------------------------------------------------------------------- +// Type Normalization +// -------------------------------------------------------------------------- +export type IntersectType = T extends [] ? TNever : T extends [TSchema] ? AssertSchema : TIntersect +export type UnionType = T extends [] ? TNever : T extends [TSchema] ? AssertSchema : TUnion +// -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- export type TModifier = TReadonlyOptional | TOptional | TReadonly @@ -121,7 +137,6 @@ export type TAnySchema = // -------------------------------------------------------------------------- // TNumeric // -------------------------------------------------------------------------- -export type TNumeric = TInteger | TNumber export interface NumericOptions extends SchemaOptions { exclusiveMaximum?: N exclusiveMinimum?: N @@ -178,18 +193,38 @@ export type TInstanceType> = T['retur // -------------------------------------------------------------------------- // TComposite // -------------------------------------------------------------------------- -export type TCompositeEvaluateArray = { [K in keyof T]: T[K] extends TSchema ? Static : never } -export type TCompositeArray = { [K in keyof T]: T[K] extends TObject ? P : {} } -export type TCompositeProperties = Evaluate : I extends object ? I : {}> -export interface TComposite extends TObject { - [Hint]: 'Composite' // Hint is required to differentiate between object | intersection on pick, omit, required, partial and keyof - static: Evaluate>> - properties: TCompositeProperties> -} +export type TCompositeIsOptional = T extends TOptional | TReadonlyOptional ? true : false +// prettier-ignore +export type TCompositeOptional = T extends [infer L, ...infer R] + ? TCompositeIsOptional> extends false ? false + : TCompositeOptional> : true +export type TCompositeKeyOfUnion1 = keyof T['properties'] +// prettier-ignore +export type TCompositeKeyOfUnion2 = T extends [infer L, ...infer R] + ? TCompositeKeyOfUnion1> | TCompositeKeyOfUnion2> + : never +export type TCompositeKeyOf = UnionToTuple> +export type TCompositePropertiesWithKey1 = K extends keyof T['properties'] ? [T['properties'][K]] : [] +// prettier-ignore +export type TCompositePropertiesWithKey2 = T extends [infer L, ...infer R] + ? [...TCompositePropertiesWithKey1, K>, ...TCompositePropertiesWithKey2, K>] + : [] +// prettier-ignore +export type TCompositeObjectProperty = TCompositePropertiesWithKey2 extends infer S ? + TCompositeOptional> extends true + ? { [_ in K]: TOptional>> } + : { [_ in K]: IntersectType> } + : {} +// prettier-ignore +export type TCompositeObjectsWithKeys = K extends [infer L, ...infer R] ? L extends Key + ? TCompositeObjectProperty & TCompositeObjectsWithKeys> + : {} + : {} +export type TComposite = Ensure>>>>> // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- -export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] +export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] export interface TConstructor extends TSchema { [Kind]: 'Constructor' static: new (...param: TConstructorParameterArray) => Static @@ -231,17 +266,17 @@ export interface TEnum = Record = (Static extends Static ? T : U) extends infer O ? - UnionToTuple extends [infer X, infer Y] ? TUnion<[Assert, Assert]> : Assert + UnionToTuple extends [infer X, infer Y] ? TUnion<[AssertSchema, AssertSchema]> : AssertSchema : never // -------------------------------------------------------------------------- // TExclude // -------------------------------------------------------------------------- -export type TExcludeTemplateLiteralResult = TUnionResult }[T]>, TSchema[]>> +export type TExcludeTemplateLiteralResult = UnionType }[T]>>> export type TExcludeTemplateLiteral = Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never // prettier-ignore -export type TExcludeArray = Assert> extends Static ? never : T[K] -}[number]>, TSchema[]> extends infer R ? TUnionResult> : never +export type TExcludeArray = AssertSchemas> extends Static ? never : T[K] +}[number]>> extends infer R ? UnionType> : never // prettier-ignore export type TExclude = T extends TTemplateLiteral ? TExcludeTemplateLiteral : @@ -250,12 +285,12 @@ export type TExclude = // -------------------------------------------------------------------------- // TExtract // -------------------------------------------------------------------------- -export type TExtractTemplateLiteralResult = TUnionResult }[T]>, TSchema[]>> +export type TExtractTemplateLiteralResult = UnionType }[T]>>> export type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never // prettier-ignore -export type TExtractArray = Assert> extends Static ? T[K] : never -}[number]>, TSchema[]> extends infer R ? TUnionResult> : never +export type TExtractArray = AssertSchemas> extends Static ? T[K] : never +}[number]>> extends infer R ? UnionType> : never // prettier-ignore export type TExtract = T extends TTemplateLiteral ? TExtractTemplateLiteral : @@ -264,7 +299,7 @@ export type TExtract = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] export interface TFunction extends TSchema { [Kind]: 'Function' static: (...param: TFunctionParameters) => Static @@ -274,6 +309,46 @@ export interface TFunction = + K extends keyof T ? [T[K]] : + [] +// prettier-ignore +export type TIndexTuple = + K extends keyof T ? [T[K]] : + [] +// prettier-ignore +export type TIndexComposite = + T extends [infer L, ...infer R] ? [...TIndexKey, K>, ...TIndexComposite, K>] : + [] +// prettier-ignore +export type TIndexKey = + T extends TRecursive ? TIndexKey : + T extends TIntersect ? TIndexComposite : + T extends TUnion ? TIndexComposite : + T extends TObject ? TIndexProperty : + T extends TTuple ? TIndexTuple : + T extends TArray ? S : + [] +// prettier-ignore +export type TIndexKeys = + K extends [infer L, ...infer R] ? [...TIndexKey>, ...TIndexKeys>] : + [] +// prettier-ignore +export type TIndex = + TIndexKeys extends infer R ? + T extends TRecursive ? TIndex : + T extends TTuple ? UnionType> : + T extends TIntersect ? UnionType> : + T extends TUnion ? UnionType> : + T extends TObject ? UnionType> : + T extends TArray ? UnionType> : + TNever : + TNever +// -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- export interface TInteger extends TSchema, NumericOptions { @@ -290,29 +365,37 @@ export interface IntersectOptions extends SchemaOptions { } export interface TIntersect extends TSchema, IntersectOptions { [Kind]: 'Intersect' - static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> + static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> type?: 'object' allOf: [...T] } // -------------------------------------------------------------------------- // TKeyOf // -------------------------------------------------------------------------- +export type TKeyOfProperties = Static extends infer S + ? UnionToTuple< + { + [K in keyof S]: TLiteral<`${Assert}`> + }[keyof S] + > + : [] // prettier-ignore -export type TKeyOfTuple = { - [K in keyof Static]: TLiteral> -} extends infer U - ? UnionToTuple> // optional yields undefined keys - : never +export type TKeyOfIndicesArray = UnionToTuple +// prettier-ignore +export type TKeyOfIndices = AssertSchemas extends infer R ? { + [K in keyof R] : TLiteral> +}: []> // prettier-ignore export type TKeyOf = ( - T extends TRecursive ? TKeyOfTuple : - T extends TComposite ? TKeyOfTuple : - T extends TIntersect ? TKeyOfTuple : - T extends TUnion ? TKeyOfTuple : - T extends TObject ? TKeyOfTuple : + T extends TRecursive ? TKeyOfProperties : + T extends TIntersect ? TKeyOfProperties : + T extends TUnion ? TKeyOfProperties : + T extends TObject ? TKeyOfProperties : + T extends TTuple ? TKeyOfIndices : + T extends TArray ? [TNumber] : T extends TRecord ? [K] : [] -) extends infer R ? TUnionResult> : never +) extends infer R ? UnionType> : never // -------------------------------------------------------------------------- // TLiteral // -------------------------------------------------------------------------- @@ -392,12 +475,11 @@ export interface TObject extends TSchema, O // -------------------------------------------------------------------------- // TOmit // -------------------------------------------------------------------------- -export type TOmitArray = Assert<{ [K2 in keyof T]: TOmit, K> }, TSchema[]> -export type TOmitProperties = Evaluate, TProperties>> +export type TOmitArray = AssertSchemas<{ [K2 in keyof T]: TOmit, K> }> +export type TOmitProperties = Evaluate>> // prettier-ignore export type TOmit = T extends TRecursive ? TRecursive> : - T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -405,24 +487,23 @@ export type TOmit // -------------------------------------------------------------------------- // TParameters // -------------------------------------------------------------------------- -export type TParameters = TTuple +export type TParameters = Ensure> // -------------------------------------------------------------------------- // TPartial // -------------------------------------------------------------------------- -export type TPartialObjectArray = Assert<{ [K in keyof T]: TPartial> }, TObject[]> -export type TPartialArray = Assert<{ [K in keyof T]: TPartial> }, TSchema[]> +export type TPartialObjectArray = AssertSchemas<{ [K in keyof T]: TPartial> }, TObject[]> +export type TPartialArray = AssertSchemas<{ [K in keyof T]: TPartial> }> // prettier-ignore -export type TPartialProperties = Evaluate = Evaluate ? TReadonlyOptional : T[K] extends TReadonly ? TReadonlyOptional : T[K] extends TOptional ? TOptional : TOptional -}, TProperties>> +}>> // prettier-ignore export type TPartial = T extends TRecursive ? TRecursive> : - T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -430,18 +511,17 @@ export type TPartial = // -------------------------------------------------------------------------- // TPick // -------------------------------------------------------------------------- -export type TPickArray = { [K2 in keyof T]: TPick, K> } +export type TPickArray = { [K2 in keyof T]: TPick, K> } // Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, // we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. // prettier-ignore export type TPickProperties = Pick, keyof T>> extends infer R ? ({ - [K in keyof R]: Assert extends TSchema ? R[K] : never + [K in keyof R]: AssertSchema extends TSchema ? R[K] : never }): never // prettier-ignore export type TPick = T extends TRecursive ? TRecursive> : - T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -471,7 +551,7 @@ export interface TRecord, Static> type: 'object' patternProperties: { [pattern: string]: T } - additionalProperties: false + additionalProperties: TAdditionalProperties } // -------------------------------------------------------------------------- // TRecursive @@ -501,19 +581,18 @@ export type TReturnType = T['returns'] // -------------------------------------------------------------------------- // TRequired // -------------------------------------------------------------------------- -export type TRequiredArray = Assert<{ [K in keyof T]: TRequired> }, TSchema[]> +export type TRequiredArray = AssertSchemas<{ [K in keyof T]: TRequired> }> // prettier-ignore -export type TRequiredProperties = Evaluate = Evaluate ? TReadonly : T[K] extends TReadonly ? TReadonly : T[K] extends TOptional ? U : T[K] -}, TProperties>> +}>> // prettier-ignore export type TRequired = T extends TRecursive ? TRecursive> : - T extends TComposite ? TComposite> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : @@ -540,15 +619,15 @@ export type StringFormatOption = | 'json-pointer' | 'relative-json-pointer' | 'regex' -export interface StringOptions extends SchemaOptions { +export interface StringOptions extends SchemaOptions { minLength?: number maxLength?: number pattern?: string - format?: Format + format?: string contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64' contentMediaType?: string } -export interface TString extends TSchema, StringOptions { +export interface TString extends TSchema, StringOptions { [Kind]: 'String' static: string type: 'string' @@ -570,17 +649,16 @@ export interface TSymbol extends TSchema, SchemaOptions { export type IsTemplateLiteralFiniteCheck = T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray> : T extends TUnion ? IsTemplateLiteralFiniteArray> : - T extends TString ? false : + T extends TString ? false : T extends TBoolean ? false : - T extends TNumber ? false : + T extends TNumber ? false : T extends TInteger ? false : - T extends TBigInt ? false : + T extends TBigInt ? false : T extends TLiteral ? true : false // prettier-ignore export type IsTemplateLiteralFiniteArray = T extends [infer L, ...infer R] ? IsTemplateLiteralFiniteCheck extends false ? false : IsTemplateLiteralFiniteArray> : - T extends [infer L] ? IsTemplateLiteralFiniteCheck extends false ? false : true : true export type IsTemplateLiteralFinite = T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray : false export type TTemplateLiteralKind = TUnion | TLiteral | TInteger | TTemplateLiteral | TNumber | TBigInt | TString | TBoolean | TNever @@ -589,16 +667,16 @@ export type TTemplateLiteralConst = T extends TUnion ? { [K in keyof U]: TTemplateLiteralUnion, Acc> }[number] : T extends TTemplateLiteral ? `${Static}` : T extends TLiteral ? `${U}` : - T extends TString ? `${string}` : - T extends TNumber ? `${number}` : - T extends TBigInt ? `${bigint}` : + T extends TString ? `${string}` : + T extends TNumber ? `${number}` : + T extends TBigInt ? `${bigint}` : T extends TBoolean ? `${boolean}` : never // prettier-ignore export type TTemplateLiteralUnion = - T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : - T extends [infer L] ? `${TTemplateLiteralConst}${Acc}` : + T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc +export type TTemplateLiteralKeyArray = AssertKeys>> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' static: TTemplateLiteralUnion @@ -608,7 +686,7 @@ export interface TTemplateLiteral> = T extends TTuple ? Assert : never +export type TTupleIntoArray> = T extends TTuple ? AssertSchemas : never export interface TTuple extends TSchema { [Kind]: 'Tuple' static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } @@ -630,17 +708,18 @@ export interface TUndefined extends TSchema { // -------------------------------------------------------------------------- // TUnionOfLiteral // -------------------------------------------------------------------------- -export type TUnionOfLiteralArray[]> = { [K in keyof T]: Assert['const'] }[number] -export type TUnionOfLiteral[]>> = TUnionOfLiteralArray -// -------------------------------------------------------------------------- -// TUnionResult - Used by Extract, Exclude and KeyOf for normalized union unwrap -// -------------------------------------------------------------------------- -export type TUnionResult = T extends [] ? TNever : T extends [infer S] ? S : TUnion +// prettier-ignore +export type TLiteralUnionReduce[]> = + T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : + [] +// prettier-ignore +export type TLiteralUnion[]>> = + T extends TUnion ? TLiteralUnionReduce[]>> : [] // -------------------------------------------------------------------------- // TUnion // -------------------------------------------------------------------------- // prettier-ignore -export type TUnionTemplateLiteral> = Ensure}[S]>,TLiteral[]>>> +export type TUnionTemplateLiteral> = Ensure}[S]>,TLiteral[]>>> export interface TUnion extends TSchema { [Kind]: 'Union' static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] @@ -781,6 +860,9 @@ export namespace TypeGuard { } return true } + function IsAdditionalProperties(value: unknown): value is TAdditionalProperties { + return IsOptionalBoolean(value) || TSchema(value) + } function IsBigInt(value: unknown): value is bigint { return typeof value === 'bigint' } @@ -948,20 +1030,25 @@ export namespace TypeGuard { export function TKind(schema: unknown): schema is Record { return IsObject(schema) && Kind in schema && typeof (schema as any)[Kind] === 'string' // TS 4.1.5: any required for symbol indexer } + /** Returns true if the given schema is TLiteral */ + export function TLiteralString(schema: unknown): schema is TLiteral { + return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'string' + } + /** Returns true if the given schema is TLiteral */ + export function TLiteralNumber(schema: unknown): schema is TLiteral { + return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'number' + } + /** Returns true if the given schema is TLiteral */ + export function TLiteralBoolean(schema: unknown): schema is TLiteral { + return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'boolean' + } + /** Returns true if the given schema is TUnion[]> */ + export function TLiteralUnion(schema: unknown): schema is TUnion[]> { + return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema)) + } /** Returns true if the given schema is TLiteral */ export function TLiteral(schema: unknown): schema is TLiteral { - // prettier-ignore - return ( - TKind(schema) && - schema[Kind] === 'Literal' && - IsOptionalString(schema.$id) && - ( - IsString(schema.const) || - IsNumber(schema.const) || - IsBoolean(schema.const) || - IsBigInt(schema.const) - ) - ) + return TLiteralString(schema) || TLiteralNumber(schema) || TLiteralBoolean(schema) } /** Returns true if the given schema is TNever */ export function TNever(schema: unknown): schema is TNever { @@ -1013,7 +1100,7 @@ export namespace TypeGuard { schema.type === 'object' && IsOptionalString(schema.$id) && IsObject(schema.properties) && - (IsOptionalBoolean(schema.additionalProperties) || IsOptionalSchema(schema.additionalProperties)) && + IsAdditionalProperties(schema.additionalProperties) && IsOptionalNumber(schema.minProperties) && IsOptionalNumber(schema.maxProperties) ) @@ -1046,7 +1133,7 @@ export namespace TypeGuard { schema[Kind] === 'Record' && schema.type === 'object' && IsOptionalString(schema.$id) && - schema.additionalProperties === false && + IsAdditionalProperties(schema.additionalProperties) && IsObject(schema.patternProperties)) ) { return false @@ -1171,10 +1258,6 @@ export namespace TypeGuard { } return true } - /** Returns true if the given schema is TUnion[]> */ - export function TUnionLiteral(schema: unknown): schema is TUnion[]> { - return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema) && typeof schema.const === 'string') - } /** Returns true if the given schema is TUint8Array */ export function TUint8Array(schema: unknown): schema is TUint8Array { return TKind(schema) && schema[Kind] === 'Uint8Array' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.instanceOf === 'Uint8Array' && IsOptionalNumber(schema.minByteLength) && IsOptionalNumber(schema.maxByteLength) @@ -1760,10 +1843,10 @@ export namespace TypeExtends { return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { - // template union remap + // Template Literal Union Unwrap if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) - // standard extends + // Standard Extends if (TypeGuard.TAny(left)) return Any(left, right) if (TypeGuard.TArray(left)) return Array(left, right) if (TypeGuard.TBigInt(left)) return BigInt(left, right) @@ -1828,6 +1911,35 @@ export namespace TypeClone { } } // -------------------------------------------------------------------------- +// IndexedAccessor +// -------------------------------------------------------------------------- +export namespace IndexedAccessor { + function Intersect(schema: TIntersect, key: string): TSchema[] { + return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[]) + } + function Union(schema: TUnion, key: string): TSchema[] { + return schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[]) + } + function Object(schema: TObject, key: string): TSchema[] { + const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((k) => k === key) + return keys.map((key) => schema.properties[key]) + } + function Tuple(schema: TTuple, key: string): TSchema[] { + const items = schema.items === undefined ? [] : (schema.items as TSchema[]) + return items.filter((_, index) => index.toString() === key.toString()) + } + function Visit(schema: TSchema, key: string): TSchema[] { + if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, key) + if (schema[Kind] === 'Union') return Union(schema as TUnion, key) + if (schema[Kind] === 'Object') return Object(schema as TObject, key) + if (schema[Kind] === 'Tuple') return Tuple(schema as TTuple, key) + return [] + } + export function Resolve(schema: TSchema, keys: string[]): TSchema[] { + return keys.reduce((acc, key) => [...acc, ...Visit(schema, key)], [] as TSchema[]) + } +} +// -------------------------------------------------------------------------- // ObjectMap // -------------------------------------------------------------------------- export namespace ObjectMap { @@ -1859,28 +1971,59 @@ export namespace ObjectMap { // -------------------------------------------------------------------------- // KeyResolver // -------------------------------------------------------------------------- +export interface KeyResolverOptions { + includePatterns: boolean +} export namespace KeyResolver { - function IsKeyable(schema: TSchema) { - return TypeGuard.TIntersect(schema) || TypeGuard.TUnion(schema) || (TypeGuard.TObject(schema) && globalThis.Object.getOwnPropertyNames(schema.properties).length > 0) + function UnwrapPattern(key: string) { + return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key } - function Intersect(schema: TIntersect) { - return [...schema.allOf.filter((schema) => IsKeyable(schema)).reduce((set, schema) => Visit(schema).map((key) => set.add(key))[0], new Set())] + function Intersect(schema: TIntersect, options: KeyResolverOptions): string[] { + return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) } - function Union(schema: TUnion) { - const sets = schema.anyOf.filter((schema) => IsKeyable(schema)).map((inner) => Visit(inner)) + function Union(schema: TUnion, options: KeyResolverOptions): string[] { + const sets = schema.anyOf.map((inner) => Visit(inner, options)) return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] } - function Object(schema: TObject) { + function Object(schema: TObject, options: KeyResolverOptions): string[] { return globalThis.Object.keys(schema.properties) } - function Visit(schema: TSchema): string[] { - if (TypeGuard.TIntersect(schema)) return Intersect(schema) - if (TypeGuard.TUnion(schema)) return Union(schema) - if (TypeGuard.TObject(schema)) return Object(schema) + function Record(schema: TRecord, options: KeyResolverOptions): string[] { + return options.includePatterns ? globalThis.Object.keys(schema.patternProperties) : [] + } + function Visit(schema: TSchema, options: KeyResolverOptions): string[] { + if (TypeGuard.TIntersect(schema)) return Intersect(schema, options) + if (TypeGuard.TUnion(schema)) return Union(schema, options) + if (TypeGuard.TObject(schema)) return Object(schema, options) + if (TypeGuard.TRecord(schema)) return Record(schema, options) return [] } - export function Resolve(schema: T) { - return Visit(schema) + /** Resolves an array of keys in this schema */ + export function ResolveKeys(schema: TSchema, options: KeyResolverOptions): string[] { + return [...new Set(Visit(schema, options))] + } + /** Resolves a regular expression pattern matching all keys in this schema */ + export function ResolvePattern(schema: TSchema): string { + const keys = ResolveKeys(schema, { includePatterns: true }) + const pattern = keys.map((key) => `(${UnwrapPattern(key)})`) + return `^(${pattern.join('|')})$` + } +} +// -------------------------------------------------------------------------- +// KeyArrayResolver +// -------------------------------------------------------------------------- +export namespace KeyArrayResolver { + /** Resolves an array of string[] keys from the given schema or array type. */ + export function Resolve(schema: TSchema | string[]): string[] { + if (globalThis.Array.isArray(schema)) return schema + if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const) + if (TypeGuard.TLiteral(schema)) return [schema.const as string] + if (TypeGuard.TTemplateLiteral(schema)) { + const expression = TemplateLiteralParser.ParseExact(schema.pattern) + if (!TemplateLiteralFinite.Check(expression)) throw Error('KeyArrayResolver: Cannot resolve keys from infinite template expression') + return [...TemplateLiteralGenerator.Generate(expression)] + } + return [] } } // -------------------------------------------------------------------------- @@ -1923,6 +2066,7 @@ export namespace TemplateLiteralPattern { // TemplateLiteralResolver // -------------------------------------------------------------------------------------- export namespace TemplateLiteralResolver { + /** Resolves a template literal as a TUnion */ export function Resolve(template: TTemplateLiteral): TString | TUnion | TLiteral { const expression = TemplateLiteralParser.ParseExact(template.pattern) if (!TemplateLiteralFinite.Check(expression)) return Type.String() @@ -2186,19 +2330,19 @@ export class StandardTypeBuilder extends TypeBuilder { const property = TypeClone.Clone(schema, {}) if (!optional.has(key)) delete property[Modifier] if (key in properties) { - const left = TypeExtends.Extends(properties[key], property) !== TypeExtendsResult.False - const right = TypeExtends.Extends(property, properties[key]) !== TypeExtendsResult.False - if (!left && !right) properties[key] = Type.Never() - if (!left && right) properties[key] = property + properties[key] = TypeGuard.TIntersect(properties[key]) ? this.Intersect([...properties[key].allOf, property]) : this.Intersect([properties[key], property]) } else { properties[key] = property } } } + for (const key of globalThis.Object.getOwnPropertyNames(properties)) { + properties[key] = optional.has(key) ? this.Optional(properties[key]) : properties[key] + } if (required.size > 0) { - return this.Create({ ...options, [Kind]: 'Object', [Hint]: 'Composite', type: 'object', properties, required: [...required] }) + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] }) } else { - return this.Create({ ...options, [Kind]: 'Object', [Hint]: 'Composite', type: 'object', properties }) + return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) } } /** `[Standard]` Creates a Enum type */ @@ -2214,9 +2358,9 @@ export class StandardTypeBuilder extends TypeBuilder { case TypeExtendsResult.Union: return this.Union([TypeClone.Clone(trueType, options), TypeClone.Clone(falseType, options)]) as any as TExtends case TypeExtendsResult.True: - return TypeClone.Clone(trueType, options) as TExtends + return TypeClone.Clone(trueType, options) as unknown as TExtends case TypeExtendsResult.False: - return TypeClone.Clone(falseType, options) as TExtends + return TypeClone.Clone(falseType, options) as unknown as TExtends } } /** `[Standard]` Excludes from the left type any type that is not assignable to the right */ @@ -2241,6 +2385,29 @@ export class StandardTypeBuilder extends TypeBuilder { return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? TypeClone.Clone(left, options) : this.Never(options)) as any } } + /** `[Standard]` Returns indexed property types for the given keys */ + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex + /** `[Standard]` Returns indexed property types for the given keys */ + public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index>(schema: T, key: K, options?: SchemaOptions): TIndex + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, key: K, options?: SchemaOptions): TIndex> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, key: K, options?: SchemaOptions): T['items'] + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, key: K, options?: SchemaOptions): TIndex + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + const keys = KeyArrayResolver.Resolve(unresolved) + + if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { + return TypeClone.Clone(schema.items, options) + } + const resolved = IndexedAccessor.Resolve(schema, keys as any) + const cloned = resolved.map((schema) => TypeClone.Clone(schema, {})) + return this.Union(cloned, options) + } /** `[Standard]` Creates an Integer type */ public Integer(options: NumericOptions = {}): TInteger { return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) @@ -2267,14 +2434,20 @@ export class StandardTypeBuilder extends TypeBuilder { public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { if (TypeGuard.TRecord(schema)) { const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - if (pattern === PatternNumberExact) return this.Number(options) as TKeyOf - if (pattern === PatternStringExact) return this.String(options) as TKeyOf + if (pattern === PatternNumberExact) return this.Number(options) as unknown as TKeyOf + if (pattern === PatternStringExact) return this.String(options) as unknown as TKeyOf throw Error('StandardTypeBuilder: Unable to resolve key type from Record key pattern') + } else if (TypeGuard.TTuple(schema)) { + const items = schema.items === undefined ? [] : schema.items + const literals = items.map((_, index) => Type.Literal(index)) + return this.Union(literals, options) as unknown as TKeyOf + } else if (TypeGuard.TArray(schema)) { + return this.Number(options) as unknown as TKeyOf } else { - const resolved = KeyResolver.Resolve(schema) - if (resolved.length === 0) return this.Never(options) as TKeyOf - const literals = resolved.map((key) => this.Literal(key)) - return this.Union(literals, options) as TKeyOf + const keys = KeyResolver.ResolveKeys(schema, { includePatterns: false }) + if (keys.length === 0) return this.Never(options) as TKeyOf + const literals = keys.map((key) => this.Literal(key)) + return this.Union(literals, options) as unknown as TKeyOf } } /** `[Standard]` Creates a Literal type */ @@ -2313,18 +2486,15 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ - public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit> + public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> + /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit - public Omit(schema: TSchema, unresolved: unknown, options: SchemaOptions = {}): any { - // prettier-ignore - const keys = - TypeGuard.TUnionLiteral(unresolved) ? unresolved.anyOf.map((schema) => schema.const) : - TypeGuard.TLiteral(unresolved) ? [unresolved.const] : - TypeGuard.TNever(unresolved) ? [] : - (unresolved as string[]) + public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { if (schema.required) { @@ -2359,18 +2529,15 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ - public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick> + public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick>(schema: T, key: K, options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> + /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick - public Pick(schema: TSchema, unresolved: unknown, options: SchemaOptions = {}): any { - // prettier-ignore - const keys = - TypeGuard.TUnionLiteral(unresolved) ? unresolved.anyOf.map((schema) => schema.const) : - TypeGuard.TLiteral(unresolved) ? [unresolved.const] : - TypeGuard.TNever(unresolved) ? [] : - (unresolved as string[]) + public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { if (schema.required) { @@ -2400,8 +2567,8 @@ export class StandardTypeBuilder extends TypeBuilder { // prettier-ignore return TemplateLiteralFinite.Check(expression) ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema, {}) }), {} as TProperties), options)) - : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) - } else if (TypeGuard.TUnionLiteral(key)) { + : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }}) + } else if (TypeGuard.TLiteralUnion(key)) { if (key.anyOf.every((schema) => TypeGuard.TLiteral(schema) && (typeof schema.const === 'string' || typeof schema.const === 'number'))) { const properties = key.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) return this.Object(properties, { ...options, [Hint]: 'Record' }) @@ -2412,10 +2579,10 @@ export class StandardTypeBuilder extends TypeBuilder { } else throw Error('TypeBuilder: Record key can only be derived from literals of number or string') } else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) { const pattern = PatternNumberExact - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) } else if (TypeGuard.TString(key)) { const pattern = key.pattern === undefined ? PatternStringExact : key.pattern - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) }, additionalProperties: false }) + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) } else { throw Error(`StandardTypeBuilder: Invalid Record Key`) } @@ -2451,7 +2618,7 @@ export class StandardTypeBuilder extends TypeBuilder { }, options) } /** `[Standard]` Creates a String type */ - public String(options: StringOptions = {}): TString { + public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } /** `[Standard]` Creates a template literal type */ diff --git a/src/value/check.ts b/src/value/check.ts index d745b2979..9b62e00fe 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -175,18 +175,17 @@ export namespace ValueCheck { return true } function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): boolean { - if (!schema.allOf.every((schema) => Visit(schema, references, value))) { - return false - } else if (schema.unevaluatedProperties === false) { - const schemaKeys = Types.KeyResolver.Resolve(schema) - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - return valueKeys.every((key) => schemaKeys.includes(key)) + const check1 = schema.allOf.every((schema) => Visit(schema, references, value)) + if (schema.unevaluatedProperties === false) { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const check2 = globalThis.Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key)) + return check1 && check2 } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const schemaKeys = Types.KeyResolver.Resolve(schema) - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - return valueKeys.every((key) => schemaKeys.includes(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const check2 = globalThis.Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) + return check1 && check2 } else { - return true + return check1 } } function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { @@ -276,15 +275,20 @@ export namespace ValueCheck { if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { return false } - const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const regex = new RegExp(keyPattern) - if (!globalThis.Object.getOwnPropertyNames(value).every((key) => regex.test(key))) { - return false - } - for (const propValue of globalThis.Object.values(value)) { - if (!Visit(valueSchema, references, propValue)) return false - } - return true + const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] + const regex = new RegExp(patternKey) + return globalThis.Object.entries(value).every(([key, value]) => { + if (regex.test(key)) { + return Visit(patternSchema, references, value) + } + if (typeof schema.additionalProperties === 'object') { + return Visit(schema.additionalProperties, references, value) + } + if (schema.additionalProperties === false) { + return false + } + return true + }) } function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { const index = references.findIndex((foreign) => foreign.$id === schema.$ref) diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index 610dd7f30..0f9d5272c 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -80,4 +80,140 @@ describe('type/compiler/Intersect', () => { const T = Type.Intersect([A, B]) Fail(T, { x: 1, y: 1 }) }) + + it('unevaluatedProperties with Record 1', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Ok(T, { x: 1, y: 2 }) + }) + it('unevaluatedProperties with Record 2', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Ok(T, { x: 1, y: 2, 0: 'hello' }) + }) + it('unevaluatedProperties with Record 3', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Fail(T, { x: 1, y: 2, 0: 1 }) + }) + it('unevaluatedProperties with Record 4', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2 }) + }) + it('unevaluatedProperties with Record 5', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, z: true }) + }) + it('unevaluatedProperties with Record 6', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Fail(T, { x: 1, y: 2, z: 1 }) + }) + it('unevaluatedProperties with Record 7', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, 0: '' }) + }) + it('unevaluatedProperties with Record 8', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, 0: '', z: true }) + }) + it('unevaluatedProperties with Record 9', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Fail(T, { x: 1, y: 2, 0: '', z: 1 }) + }) }) diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 428c35f92..2f39b5684 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -2,6 +2,20 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Record', () => { + // ------------------------------------------------------------- + // TypeBox Only: Date and Record + // ------------------------------------------------------------- + it('Should fail record with Date', () => { + const T = Type.Record(Type.String(), Type.String()) + Fail(T, new Date()) + }) + it('Should fail record with Uint8Array', () => { + const T = Type.Record(Type.String(), Type.String()) + Fail(T, new Uint8Array()) + }) + // ------------------------------------------------------------- + // Standard Assertions + // ------------------------------------------------------------- it('Should validate when all property values are numbers', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3 }) @@ -65,7 +79,7 @@ describe('type/compiler/Record', () => { }) it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) - const T = Type.Record(K, Type.Number()) + const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { op_a: 1, op_b: 2, @@ -80,11 +94,11 @@ describe('type/compiler/Record', () => { Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are integers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for integers keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '00': 1, '01': 2, @@ -93,7 +107,7 @@ describe('type/compiler/Record', () => { }) }) it('Should not validate if passing a signed integers keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '-0': 1, '-1': 2, @@ -109,11 +123,11 @@ describe('type/compiler/Record', () => { Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '00': 1, '01': 2, @@ -122,7 +136,7 @@ describe('type/compiler/Record', () => { }) }) it('Should not validate if passing a signed numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '-0': 1, '-1': 2, @@ -131,15 +145,30 @@ describe('type/compiler/Record', () => { }) }) it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) - it('Should fail record with Date', () => { - const T = Type.Record(Type.String(), Type.String()) - Fail(T, new Date()) + // ------------------------------------------------------------ + // AdditionalProperties + // ------------------------------------------------------------ + it('AdditionalProperties 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true }) + Ok(T, { 1: '', 2: '', x: 1, y: 2, z: 3 }) }) - it('Should fail record with Uint8Array', () => { - const T = Type.Record(Type.String(), Type.String()) - Fail(T, new Uint8Array()) + it('AdditionalProperties 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + Ok(T, { 1: '', 2: '', 3: '' }) + }) + it('AdditionalProperties 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + Fail(T, { 1: '', 2: '', x: '' }) + }) + it('AdditionalProperties 4', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + Fail(T, { 1: '', 2: '', x: '' }) + }) + it('AdditionalProperties 5', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + Ok(T, { 1: '', 2: '', x: true }) }) }) diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts index d3de6c365..55f4f0659 100644 --- a/test/runtime/schema/intersect.ts +++ b/test/runtime/schema/intersect.ts @@ -80,4 +80,140 @@ describe('type/schema/Intersect', () => { const T = Type.Intersect([A, B]) Fail(T, { x: 1, y: 1 }) }) + + it('unevaluatedProperties with Record 1', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Ok(T, { x: 1, y: 2 }) + }) + it('unevaluatedProperties with Record 2', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Ok(T, { x: 1, y: 2, 0: 'hello' }) + }) + it('unevaluatedProperties with Record 3', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Fail(T, { x: 1, y: 2, 0: 1 }) + }) + it('unevaluatedProperties with Record 4', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2 }) + }) + it('unevaluatedProperties with Record 5', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, z: true }) + }) + it('unevaluatedProperties with Record 6', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Fail(T, { x: 1, y: 2, z: 1 }) + }) + it('unevaluatedProperties with Record 7', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, 0: '' }) + }) + it('unevaluatedProperties with Record 8', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Ok(T, { x: 1, y: 2, 0: '', z: true }) + }) + it('unevaluatedProperties with Record 9', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Fail(T, { x: 1, y: 2, 0: '', z: 1 }) + }) }) diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index bacc80f42..d772d8817 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -65,7 +65,7 @@ describe('type/schema/Record', () => { }) it('Should should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegEx(/^op_.*$/) - const T = Type.Record(K, Type.Number()) + const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { op_a: 1, op_b: 2, @@ -80,11 +80,11 @@ describe('type/schema/Record', () => { Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are integers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for integers keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '00': 1, '01': 2, @@ -93,7 +93,7 @@ describe('type/schema/Record', () => { }) }) it('Should not validate if passing a signed integers keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) + const T = Type.Record(Type.Integer(), Type.Number(), { additionalProperties: false }) Fail(T, { '-0': 1, '-1': 2, @@ -109,11 +109,11 @@ describe('type/schema/Record', () => { Ok(T, { '0': 1, '1': 2, '2': 3, '3': 4 }) }) it('Should validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) it('Should not validate if passing a leading zeros for numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '00': 1, '01': 2, @@ -122,7 +122,7 @@ describe('type/schema/Record', () => { }) }) it('Should not validate if passing a signed numeric keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '-0': 1, '-1': 2, @@ -131,7 +131,30 @@ describe('type/schema/Record', () => { }) }) it('Should not validate when all property keys are numbers, but one property is a string with varying type', () => { - const T = Type.Record(Type.Number(), Type.Number()) + const T = Type.Record(Type.Number(), Type.Number(), { additionalProperties: false }) Fail(T, { '0': 1, '1': 2, '2': 3, '3': 4, a: 'hello' }) }) + // ------------------------------------------------------------ + // AdditionalProperties + // ------------------------------------------------------------ + it('AdditionalProperties 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true }) + Ok(T, { 1: '', 2: '', x: 1, y: 2, z: 3 }) + }) + it('AdditionalProperties 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + Ok(T, { 1: '', 2: '', 3: '' }) + }) + it('AdditionalProperties 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + Fail(T, { 1: '', 2: '', x: '' }) + }) + it('AdditionalProperties 4', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + Fail(T, { 1: '', 2: '', x: '' }) + }) + it('AdditionalProperties 5', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + Ok(T, { 1: '', 2: '', x: true }) + }) }) diff --git a/test/runtime/system/system.ts b/test/runtime/system/system.ts index dc7c51df5..33e3373db 100644 --- a/test/runtime/system/system.ts +++ b/test/runtime/system/system.ts @@ -135,9 +135,13 @@ describe('system/TypeSystem/AllowArrayObjects', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, [0, 1, 2]) }) - it('Should not validate arrays as Records with Number Keys', () => { + it('Should validate arrays as Records with Number Keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays as Records with Integer Keys', () => { const T = Type.Record(Type.Integer(), Type.Number()) - Fail(T, [0, 1, 2]) + Ok(T, [0, 1, 2]) }) it('Should not validate arrays as Records with Object Values', () => { const T = Type.Record( diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index 2ace75790..c9d7d611e 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -10,11 +10,11 @@ describe('type/guard/TComposite', () => { }) it('Should guard for overlapping properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) - Assert.equal(TypeGuard.TNumber(T.properties.x), true) - }) - it('Should guard for illogical intersection and yield never', () => { - const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) - Assert.equal(TypeGuard.TNever(T.properties.x), true) + Assert.equal(TypeGuard.TIntersect(T.properties.x), true) + // @ts-ignore + Assert.equal(TypeGuard.TNumber(T.properties.x.allOf[0]), true) + // @ts-ignore + Assert.equal(TypeGuard.TNumber(T.properties.x.allOf[1]), true) }) it('Should not produce optional property if all properties are not optional', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index d88632516..907c82e4a 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -8,6 +8,7 @@ import './date' import './exclude' import './extract' import './function' +import './indexed' import './integer' import './literal' import './intersect' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts new file mode 100644 index 000000000..0ddd02fe9 --- /dev/null +++ b/test/runtime/type/guard/indexed.ts @@ -0,0 +1,98 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TIndex', () => { + it('Should Index 1', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, ['x']) + Assert.deepEqual(TypeGuard.TNumber(I), true) + }) + it('Should Index 2', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, ['x', 'y']) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Should Index 3', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, Type.KeyOf(T)) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Should Index 4', () => { + const T = Type.Object({ + ab: Type.Number(), + ac: Type.String(), + }) + const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Should Index 5', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) + const I = Type.Index(T, ['x', 'y']) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Should Index 6', () => { + const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Should Index 7', () => { + const T = Type.Array(Type.Null()) + const I = Type.Index(T, Type.Number()) + Assert.deepEqual(TypeGuard.TNull(I), true) + }) + it('Should Index 6', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0]) + Assert.deepEqual(TypeGuard.TLiteralString(I), true) + Assert.deepEqual(I.const, 'hello') + }) + it('Should Index 8', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [1]) + Assert.deepEqual(TypeGuard.TLiteralString(I), true) + Assert.deepEqual(I.const, 'world') + }) + it('Should Index 9', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0, 1]) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(I.anyOf[0].const, 'hello') + Assert.deepEqual(I.anyOf[1].const, 'world') + }) + it('Should Index 10', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [1, 0]) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(I.anyOf[0].const, 'world') + Assert.deepEqual(I.anyOf[1].const, 'hello') + }) + it('Should Index 11', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0, 0, 0, 1]) + Assert.deepEqual(TypeGuard.TUnion(I), true) + Assert.deepEqual(I.anyOf[0].const, 'hello') + Assert.deepEqual(I.anyOf[1].const, 'hello') + Assert.deepEqual(I.anyOf[2].const, 'hello') + Assert.deepEqual(I.anyOf[3].const, 'world') + }) +}) diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/keyof.ts index d1f6e9ecb..59c3067bd 100644 --- a/test/runtime/type/guard/keyof.ts +++ b/test/runtime/type/guard/keyof.ts @@ -3,7 +3,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TKeyOf', () => { - it('Should guard for keyof TObject', () => { + it('Should KeyOf 1', () => { const T = Type.Object({ x: Type.Number(), y: Type.Number(), @@ -13,7 +13,7 @@ describe('type/guard/TKeyOf', () => { Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) - it('Should guard for keyof TRecursive', () => { + it('Should KeyOf 2', () => { const T = Type.Recursive((Self) => Type.Object({ x: Type.Number(), @@ -25,7 +25,7 @@ describe('type/guard/TKeyOf', () => { Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) - it('Should guard for keyof TIntersect', () => { + it('Should KeyOf 3', () => { const T = Type.Intersect([ Type.Object({ x: Type.Number(), @@ -39,7 +39,7 @@ describe('type/guard/TKeyOf', () => { Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) - it('Should guard for keyof TUnion', () => { + it('Should KeyOf 4', () => { const T = Type.Union([ Type.Object({ x: Type.Number(), @@ -51,9 +51,26 @@ describe('type/guard/TKeyOf', () => { const K = Type.KeyOf(T) Assert.deepEqual(TypeGuard.TNever(K), true) }) - it('Should guard for keyof TNull', () => { + it('Should KeyOf 5', () => { const T = Type.Null() const K = Type.KeyOf(T) Assert.deepEqual(TypeGuard.TNever(K), true) }) + it('Should KeyOf 6', () => { + const T = Type.Array(Type.Number()) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TNumber(K), true) + }) + it('Should KeyOf 7', () => { + const T = Type.Tuple([]) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TNever(K), true) + }) + it('Should KeyOf 8', () => { + const T = Type.Tuple([Type.Number(), Type.Null()]) + const K = Type.KeyOf(T) + Assert.deepEqual(TypeGuard.TUnion(K), true) + Assert.deepEqual(K.anyOf[0].const, '0') + Assert.deepEqual(K.anyOf[1].const, '1') + }) }) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 7a6055a27..debf380db 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -14,4 +14,93 @@ describe('type/guard/TOmit', () => { const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownOmitType' }), y: Type.Number() }), ['x']) Assert.deepEqual(T.required, ['y']) }) + // ------------------------------------------------------------------------- + // Standard Tests + // ------------------------------------------------------------------------- + it('Should Omit 1', () => { + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ['x'], + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.deepEqual(T.required, ['y']) + }) + it('Should Omit 2', () => { + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Optional(Type.Number()), + }), + ['x'], + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.deepEqual(T.required, undefined) + }) + it('Should Omit 3', () => { + const L = Type.Literal('x') + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.deepEqual(T.required, ['y']) + }) + it('Should Omit 4', () => { + const L = Type.Literal('x') + const T = Type.Omit(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) + Assert.deepEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) + // @ts-ignore + Assert.deepEqual(T.allOf[1].properties.x, undefined) + }) + it('Should Omit 5', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + // @ts-ignore + Assert.deepEqual(T.properties.x, undefined) + // @ts-ignore + Assert.deepEqual(T.properties.y, undefined) + // @ts-ignore + Assert.deepEqual(T.required, undefined) + }) + it('Should Omit 6', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + // @ts-ignore + Assert.deepEqual(T.properties.x, undefined) + // @ts-ignore + Assert.deepEqual(T.properties.y, undefined) + // @ts-ignore + Assert.deepEqual(T.required, undefined) + }) + it('Should Omit 7', () => { + const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) + const T = Type.Omit( + Type.Object({ + ab: Type.Number(), + ac: Type.Number(), + ad: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.ad), true) + Assert.deepEqual(T.required, ['ad']) + }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 65767c95d..2ca44cc15 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -7,11 +7,102 @@ describe('type/guard/TPick', () => { // case: https://github.com/sinclairzx81/typebox/issues/384 // ------------------------------------------------------------------------- it('Should support TUnsafe omit properties with no Kind', () => { - const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) + const T = Type.Pick( + Type.Object({ + x: Type.Unsafe({ x: 1 }), + y: Type.Number(), + }), + ['x'], + ) Assert.deepEqual(T.required, ['x']) }) it('Should support TUnsafe omit properties with unregistered Kind', () => { const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownPickType' }), y: Type.Number() }), ['x']) Assert.deepEqual(T.required, ['x']) }) + // ------------------------------------------------------------------------- + // Standard Tests + // ------------------------------------------------------------------------- + it('Should Pick 1', () => { + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ['x'], + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.deepEqual(T.required, ['x']) + }) + it('Should Pick 2', () => { + const T = Type.Pick( + Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number(), + }), + ['x'], + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.deepEqual(T.required, undefined) + }) + it('Should Pick 3', () => { + const L = Type.Literal('x') + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.deepEqual(T.required, ['x']) + }) + it('Should Pick 4', () => { + const L = Type.Literal('x') + const T = Type.Pick(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) + + Assert.deepEqual(TypeGuard.TNumber(T.allOf[0].properties.x), true) + // @ts-ignore + Assert.deepEqual(T.allOf[1].properties.y, undefined) + }) + it('Should Pick 5', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.deepEqual(T.required, ['x', 'y']) + }) + it('Should Pick 6', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.deepEqual(T.required, ['x', 'y']) + }) + it('Should Pick 7', () => { + const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) + const T = Type.Pick( + Type.Object({ + ab: Type.Number(), + ac: Type.Number(), + ad: Type.Number(), + }), + L, + ) + Assert.deepEqual(TypeGuard.TNumber(T.properties.ab), true) + Assert.deepEqual(TypeGuard.TNumber(T.properties.ac), true) + Assert.deepEqual(T.required, ['ab', 'ac']) + }) }) diff --git a/test/runtime/value/check/intersect.ts b/test/runtime/value/check/intersect.ts index 5c9941220..a7574a19e 100644 --- a/test/runtime/value/check/intersect.ts +++ b/test/runtime/value/check/intersect.ts @@ -81,4 +81,139 @@ describe('value/check/Intersect', () => { const T = Type.Intersect([A, B]) Assert.equal(Value.Check(T, { x: 1, y: 1 }), false) }) + it('unevaluatedProperties with Record 1', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2 }), true) + }) + it('unevaluatedProperties with Record 2', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, 0: 'hello' }), true) + }) + it('unevaluatedProperties with Record 3', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: false, + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, 0: 1 }), false) + }) + it('unevaluatedProperties with Record 4', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2 }), true) + }) + it('unevaluatedProperties with Record 5', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, z: true }), true) + }) + it('unevaluatedProperties with Record 6', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, z: 1 }), false) + }) + it('unevaluatedProperties with Record 7', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '' }), true) + }) + it('unevaluatedProperties with Record 8', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '', z: true }), true) + }) + it('unevaluatedProperties with Record 9', () => { + const T = Type.Intersect( + [ + Type.Record(Type.Number(), Type.String()), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ], + { + unevaluatedProperties: Type.Boolean(), + }, + ) + Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '', z: 1 }), false) + }) }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 2df12220d..3fe8b5d08 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -136,7 +136,7 @@ describe('value/check/Record', () => { }) it('Should not pass record with invalid number key', () => { - const T = Type.Record(Type.Number(), Type.String()) + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) const value = { a: 'a', 1: 'a', @@ -159,7 +159,7 @@ describe('value/check/Record', () => { Assert.equal(result, true) }) it('Should not pass record with invalid integer key', () => { - const T = Type.Record(Type.Integer(), Type.String()) + const T = Type.Record(Type.Integer(), Type.String(), { additionalProperties: false }) const value = { a: 'a', 1: 'a', @@ -168,4 +168,32 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.equal(result, false) }) + // ------------------------------------------------------------ + // AdditionalProperties + // ------------------------------------------------------------ + it('AdditionalProperties 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true }) + const R = Value.Check(T, { 1: '', 2: '', x: 1, y: 2, z: 3 }) + Assert.equal(R, true) + }) + it('AdditionalProperties 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + const R = Value.Check(T, { 1: '', 2: '', 3: '' }) + Assert.equal(R, true) + }) + it('AdditionalProperties 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) + const R = Value.Check(T, { 1: '', 2: '', x: '' }) + Assert.equal(R, false) + }) + it('AdditionalProperties 4', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + const R = Value.Check(T, { 1: '', 2: '', x: '' }) + Assert.equal(R, false) + }) + it('AdditionalProperties 5', () => { + const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) + const R = Value.Check(T, { 1: '', 2: '', x: true }) + Assert.equal(R, true) + }) }) From a8f4f180bc6f02659dbdddf057e11d4a808d4ef0 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 21 Apr 2023 08:20:07 +0900 Subject: [PATCH 110/369] Revision 0.28.1 (#397) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 2 +- test/static/index.ts | 1 + test/static/indexed.ts | 29 +++++++++++++++++++++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 test/static/indexed.ts diff --git a/package-lock.json b/package-lock.json index deb9afe98..91b7e01ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.0", + "version": "0.28.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.0", + "version": "0.28.1", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 24257708f..e8ff85441 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.0", + "version": "0.28.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index a76c35e3f..098346ec3 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -710,7 +710,7 @@ export interface TUndefined extends TSchema { // -------------------------------------------------------------------------- // prettier-ignore export type TLiteralUnionReduce[]> = - T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : + T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : [] // prettier-ignore export type TLiteralUnion[]>> = diff --git a/test/static/index.ts b/test/static/index.ts index 70f003457..2d551c739 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -10,6 +10,7 @@ import './emum' import './extract' import './exclude' import './function' +import './indexed' import './instance-type' import './intersect' import './keyof' diff --git a/test/static/indexed.ts b/test/static/indexed.ts new file mode 100644 index 000000000..7e12c017c --- /dev/null +++ b/test/static/indexed.ts @@ -0,0 +1,29 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, ['x', 'y']) + Expect(I).ToInfer() +} +{ + const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) + const I = Type.Index(T, Type.Union([Type.Literal('0'), Type.Literal('1')])) + Expect(I).ToInfer() +} +{ + const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) + Expect(I).ToInfer() +} +{ + const T = Type.Object({ + ab: Type.Number(), + ac: Type.String(), + }) + const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) + Expect(I).ToInfer() +} From 9e71365ed6cde9c8e1656976c9d4fb4e27767746 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 21 Apr 2023 11:48:07 +0900 Subject: [PATCH 111/369] Template Literal DSL --- example/template-dsl/index.ts | 29 ++++++++ example/template-dsl/readme.md | 31 ++++++++ example/template-dsl/template-dsl.ts | 105 +++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 example/template-dsl/index.ts create mode 100644 example/template-dsl/readme.md create mode 100644 example/template-dsl/template-dsl.ts diff --git a/example/template-dsl/index.ts b/example/template-dsl/index.ts new file mode 100644 index 000000000..44f129157 --- /dev/null +++ b/example/template-dsl/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/template-dsl + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './template-dsl' \ No newline at end of file diff --git a/example/template-dsl/readme.md b/example/template-dsl/readme.md new file mode 100644 index 000000000..e55dba237 --- /dev/null +++ b/example/template-dsl/readme.md @@ -0,0 +1,31 @@ +# TemplateLiteral DSL + +This example implements a small string based DSL for dynamically parsing strings into `TTemplateLiteral` types at runtime. The `template-dsl.ts` script provided with this example contains the full implementation. It can be copied and pasted into projects with TypeBox 0.28.0 or higher installed. + +The example is a candiate for possible inclusion in TypeBox under a `Type.TemplateLiteral(template_dsl_string)` overloaded type function. + +## Example + +The DSL supports a similar syntax to TypeScript template literal type syntax. The following shows general usage. + +```typescript +import { Static } from '@sinclair/typebox' +import { TemplateLiteral } from './template-dsl' + +// ---------------------------------------------------------------- +// Path +// ---------------------------------------------------------------- + +const Path = TemplateLiteral('/users/${number}/posts/${string}') + +type Path = Static // type Path = '/users/${number}/posts/${string}' + +// ---------------------------------------------------------------- +// Bytes +// ---------------------------------------------------------------- + +const Byte = TemplateLiteral('${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}') + +type Byte = Static // type Byte = '00000000' | '00000001' | '00000010' ... | '11111111' +``` + diff --git a/example/template-dsl/template-dsl.ts b/example/template-dsl/template-dsl.ts new file mode 100644 index 000000000..1db5777e3 --- /dev/null +++ b/example/template-dsl/template-dsl.ts @@ -0,0 +1,105 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/template-dsl + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Type, TTemplateLiteral, TLiteral, TTemplateLiteralKind, TNumber, TString, TBoolean, TBigInt, Assert, Ensure, UnionType } from '@sinclair/typebox' + +// ------------------------------------------------------------------------- +// Helpers +// ------------------------------------------------------------------------- +export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T +// ------------------------------------------------------------------------- +// TTemplateLiteralParser +// ------------------------------------------------------------------------- +// prettier-ignore +export type TTemplateLiteralParserUnionLiteral = + T extends `${infer L}|${infer R}` ? [TLiteral>, ...TTemplateLiteralParserUnionLiteral] : + T extends `${infer L}` ? [TLiteral>] : + [] +export type TTemplateLiteralParserUnion = UnionType> +// prettier-ignore +export type TTemplateLiteralParserTerminal = + T extends 'boolean' ? TBoolean : + T extends 'bigint' ? TBigInt : + T extends 'number' ? TNumber : + T extends 'string' ? TString : + TTemplateLiteralParserUnion +// prettier-ignore +export type TTemplateLiteralParserTemplate = + T extends `{${infer L}}${infer R}` ? [TTemplateLiteralParserTerminal, ...TTemplateLiteralParserTemplate] : + T extends `${infer L}$${infer R}` ? [TLiteral, ...TTemplateLiteralParserTemplate] : + T extends `${infer L}` ? [TLiteral] : + [] +export type TTemplateLiteralParser = Ensure, TTemplateLiteralKind[]>>> +// --------------------------------------------------------------------- +// TemplateLiteralParser +// --------------------------------------------------------------------- +namespace TemplateLiteralParser { + export function * ParseUnion(template: string): IterableIterator { + const trim = template.trim() + if(trim === 'boolean') return yield Type.Boolean() + if(trim === 'number') return yield Type.Number() + if(trim === 'bigint') return yield Type.BigInt() + if(trim === 'string') return yield Type.String() + const literals = trim.split('|').map(literal => Type.Literal(literal.trim())) + return yield literals.length === 0 ? Type.Never() : literals.length === 1 ? literals[0] : Type.Union(literals) + } + export function * ParseTerminal(template: string): IterableIterator { + if(template[1] !== '{') { + const L = Type.Literal('$') + const R = ParseLiteral(template.slice(1)) + return yield * [L, ...R] + } + for(let i = 2; i < template.length; i++) { + if(template[i] === '}') { + const L = ParseUnion(template.slice(2, i)) + const R = ParseLiteral(template.slice(i+1)) + return yield * [...L, ...R] + } + } + yield Type.Literal(template) + } + export function * ParseLiteral(template: string): IterableIterator { + for(let i = 0; i < template.length; i++) { + if(template[i] === '$') { + const L = Type.Literal(template.slice(0, i)) + const R = ParseTerminal(template.slice(i)) + return yield * [L, ...R] + } + } + yield Type.Literal(template) + } + export function Parse(template: string): TTemplateLiteral { + return Type.TemplateLiteral([...ParseLiteral(template)]) + } +} +// ------------------------------------------------------------------------------------------ +// TemplateLiteral DSL +// ------------------------------------------------------------------------------------------ +export function TemplateLiteral(template: T): TTemplateLiteralParser { + return TemplateLiteralParser.Parse(template) +} \ No newline at end of file From f8e50286d779c171982dae65c4a9565b777edcf7 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 21 Apr 2023 21:43:21 +0900 Subject: [PATCH 112/369] Expression Variable Fix for Compiled Records (#403) --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 3 +-- test/runtime/compiler/record.ts | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 91b7e01ed..3da05bcf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.1", + "version": "0.28.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.1", + "version": "0.28.2", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index e8ff85441..2ca7f90dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.1", + "version": "0.28.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 02591e1eb..bf0d67a91 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -248,7 +248,6 @@ export namespace TypeCompiler { if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` } - function* Object(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { yield IsObjectCheck(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` @@ -288,7 +287,7 @@ export namespace TypeCompiler { if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] const local = PushLocal(`new RegExp(/${patternKey}/)`) - const check1 = CreateExpression(patternSchema, references, value) + const check1 = CreateExpression(patternSchema, references, 'value') const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' const expression = `(${local}.test(key) ? ${check1} : ${check2})` yield `(Object.entries(${value}).every(([key, value]) => ${expression}))` diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 2f39b5684..8bee7fa76 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -2,6 +2,23 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Record', () => { + // ------------------------------------------------------------- + // Issues + // ------------------------------------------------------------- + it('Issue: https://github.com/sinclairzx81/typebox/issues/402', () => { + const T = Type.Object({ + foo: Type.Object({ + bar: Type.Record(Type.String(), Type.Number()), + }), + }) + Ok(T, { foo: { bar: { x: 42 } } }) + Ok(T, { foo: { bar: {} } }) + Fail(T, { foo: { bar: { x: '42' } } }) + Fail(T, { foo: { bar: [] } }) + Fail(T, { foo: {} }) + Fail(T, { foo: [] }) + Fail(T, {}) + }) // ------------------------------------------------------------- // TypeBox Only: Date and Record // ------------------------------------------------------------- From 749b4536f69d1c2cc291deaafbc051781fd77dc2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 22 Apr 2023 06:28:50 +0900 Subject: [PATCH 113/369] Revision 0.28.3 (#406) --- changelog/0.28.3.md | 57 ++++++ example/template-dsl/template-dsl.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- readme.md | 72 +++++-- src/typebox.ts | 145 +++++++------- test/runtime/assert/assert.ts | 13 +- test/runtime/compiler/recursive.ts | 4 +- test/runtime/errors/iterator.ts | 8 +- test/runtime/schema/composite.ts | 2 +- test/runtime/schema/recursive.ts | 4 +- test/runtime/type/extends/any.ts | 36 ++-- test/runtime/type/extends/array.ts | 100 +++++----- test/runtime/type/extends/bigint.ts | 38 ++-- test/runtime/type/extends/boolean.ts | 34 ++-- test/runtime/type/extends/constructor.ts | 82 ++++---- test/runtime/type/extends/date.ts | 36 ++-- test/runtime/type/extends/function.ts | 82 ++++---- test/runtime/type/extends/integer.ts | 36 ++-- test/runtime/type/extends/literal.ts | 100 +++++----- test/runtime/type/extends/null.ts | 38 ++-- test/runtime/type/extends/number.ts | 36 ++-- test/runtime/type/extends/object.ts | 56 +++--- test/runtime/type/extends/promise.ts | 78 ++++---- test/runtime/type/extends/record.ts | 64 +++--- test/runtime/type/extends/string.ts | 42 ++-- test/runtime/type/extends/symbol.ts | 40 ++-- test/runtime/type/extends/template-literal.ts | 60 +++--- test/runtime/type/extends/tuple.ts | 62 +++--- test/runtime/type/extends/uint8array.ts | 36 ++-- test/runtime/type/extends/undefined.ts | 34 ++-- test/runtime/type/extends/union.ts | 52 ++--- test/runtime/type/extends/unknown.ts | 38 ++-- test/runtime/type/extends/void.ts | 40 ++-- test/runtime/type/guard/any.ts | 6 +- test/runtime/type/guard/array.ts | 16 +- test/runtime/type/guard/bigint.ts | 6 +- test/runtime/type/guard/boolean.ts | 6 +- test/runtime/type/guard/composite.ts | 14 +- test/runtime/type/guard/constructor.ts | 22 +-- test/runtime/type/guard/date.ts | 14 +- test/runtime/type/guard/exclude.ts | 36 ++-- test/runtime/type/guard/extract.ts | 48 ++--- test/runtime/type/guard/function.ts | 22 +-- test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/indexed.ts | 81 +++++--- test/runtime/type/guard/integer.ts | 16 +- test/runtime/type/guard/intersect.ts | 4 +- test/runtime/type/guard/keyof.ts | 32 +-- test/runtime/type/guard/literal.ts | 12 +- test/runtime/type/guard/not.ts | 8 +- test/runtime/type/guard/null.ts | 6 +- test/runtime/type/guard/number.ts | 16 +- test/runtime/type/guard/object.ts | 20 +- test/runtime/type/guard/omit.ts | 36 ++-- test/runtime/type/guard/partial.ts | 6 +- test/runtime/type/guard/pick.ts | 38 ++-- test/runtime/type/guard/promise.ts | 10 +- test/runtime/type/guard/record.ts | 66 +++---- test/runtime/type/guard/ref.ts | 6 +- test/runtime/type/guard/rest.ts | 22 +++ test/runtime/type/guard/string.ts | 12 +- test/runtime/type/guard/symbol.ts | 6 +- test/runtime/type/guard/template-literal.ts | 18 +- test/runtime/type/guard/this.ts | 4 +- test/runtime/type/guard/tuple.ts | 8 +- test/runtime/type/guard/uint8array.ts | 10 +- test/runtime/type/guard/undefined.ts | 6 +- test/runtime/type/guard/union.ts | 20 +- test/runtime/type/guard/unknown.ts | 6 +- test/runtime/type/guard/unsafe.ts | 10 +- test/runtime/type/guard/void.ts | 6 +- test/runtime/type/normalize/exclude.ts | 6 +- test/runtime/type/normalize/extract.ts | 10 +- test/runtime/type/normalize/intersect.ts | 6 +- test/runtime/type/normalize/record.ts | 2 +- test/runtime/type/normalize/union.ts | 6 +- test/runtime/type/registry/format.ts | 6 +- test/runtime/type/registry/type.ts | 6 +- test/runtime/type/template/finite.ts | 24 +-- test/runtime/type/template/generate.ts | 74 +++---- test/runtime/type/template/parser.ts | 74 +++---- test/runtime/type/template/pattern.ts | 10 +- test/runtime/value/cast/any.ts | 18 +- test/runtime/value/cast/array.ts | 38 ++-- test/runtime/value/cast/bigint.ts | 18 +- test/runtime/value/cast/boolean.ts | 18 +- test/runtime/value/cast/composite.ts | 20 +- test/runtime/value/cast/custom.ts | 16 +- test/runtime/value/cast/date.ts | 12 +- test/runtime/value/cast/enum.ts | 20 +- test/runtime/value/cast/integer.ts | 14 +- test/runtime/value/cast/intersect.ts | 12 +- test/runtime/value/cast/keyof.ts | 18 +- test/runtime/value/cast/literal.ts | 18 +- test/runtime/value/cast/not.ts | 20 +- test/runtime/value/cast/null.ts | 18 +- test/runtime/value/cast/number.ts | 16 +- test/runtime/value/cast/object.ts | 28 +-- test/runtime/value/cast/record.ts | 20 +- test/runtime/value/cast/recursive.ts | 20 +- test/runtime/value/cast/regex.ts | 18 +- test/runtime/value/cast/string.ts | 14 +- test/runtime/value/cast/symbol.ts | 2 +- test/runtime/value/cast/template-literal.ts | 18 +- test/runtime/value/cast/tuple.ts | 26 +-- test/runtime/value/cast/uint8array.ts | 18 +- test/runtime/value/cast/undefined.ts | 18 +- test/runtime/value/cast/union.ts | 38 ++-- test/runtime/value/cast/unknown.ts | 18 +- test/runtime/value/cast/void.ts | 18 +- test/runtime/value/check/any.ts | 16 +- test/runtime/value/check/array.ts | 18 +- test/runtime/value/check/bigint.ts | 14 +- test/runtime/value/check/boolean.ts | 16 +- test/runtime/value/check/composite.ts | 10 +- test/runtime/value/check/custom.ts | 8 +- test/runtime/value/check/date.ts | 18 +- test/runtime/value/check/enum.ts | 8 +- test/runtime/value/check/integer.ts | 12 +- test/runtime/value/check/intersect.ts | 54 ++--- test/runtime/value/check/keyof.ts | 8 +- test/runtime/value/check/literal.ts | 8 +- test/runtime/value/check/never.ts | 14 +- test/runtime/value/check/not.ts | 22 +-- test/runtime/value/check/null.ts | 16 +- test/runtime/value/check/number.ts | 24 +-- test/runtime/value/check/object.ts | 58 +++--- test/runtime/value/check/record.ts | 46 ++--- test/runtime/value/check/recursive.ts | 10 +- test/runtime/value/check/ref.ts | 16 +- test/runtime/value/check/regex.ts | 4 +- test/runtime/value/check/string.ts | 16 +- test/runtime/value/check/symbol.ts | 18 +- test/runtime/value/check/template-literal.ts | 134 ++++++------- test/runtime/value/check/tuple.ts | 10 +- test/runtime/value/check/uint8array.ts | 8 +- test/runtime/value/check/undefined.ts | 16 +- test/runtime/value/check/union.ts | 12 +- test/runtime/value/check/unknown.ts | 16 +- test/runtime/value/check/void.ts | 16 +- test/runtime/value/clone/clone.ts | 48 ++--- test/runtime/value/convert/any.ts | 14 +- test/runtime/value/convert/array.ts | 20 +- test/runtime/value/convert/bigint.ts | 16 +- test/runtime/value/convert/boolean.ts | 58 +++--- test/runtime/value/convert/composite.ts | 2 +- test/runtime/value/convert/custom.ts | 6 +- test/runtime/value/convert/date.ts | 18 +- test/runtime/value/convert/integer.ts | 28 +-- test/runtime/value/convert/literal.ts | 30 +-- test/runtime/value/convert/never.ts | 6 +- test/runtime/value/convert/null.ts | 6 +- test/runtime/value/convert/number.ts | 28 +-- test/runtime/value/convert/object.ts | 2 +- test/runtime/value/convert/record.ts | 2 +- test/runtime/value/convert/string.ts | 26 +-- test/runtime/value/convert/symbol.ts | 4 +- test/runtime/value/convert/tuple.ts | 6 +- test/runtime/value/convert/undefined.ts | 4 +- test/runtime/value/convert/union.ts | 8 +- test/runtime/value/convert/unknown.ts | 14 +- test/runtime/value/create/any.ts | 4 +- test/runtime/value/create/array.ts | 6 +- test/runtime/value/create/bigint.ts | 4 +- test/runtime/value/create/boolean.ts | 4 +- test/runtime/value/create/composite.ts | 6 +- test/runtime/value/create/constructor.ts | 4 +- test/runtime/value/create/custom.ts | 2 +- test/runtime/value/create/enum.ts | 4 +- test/runtime/value/create/function.ts | 4 +- test/runtime/value/create/integer.ts | 4 +- test/runtime/value/create/intersect.ts | 14 +- test/runtime/value/create/keyof.ts | 4 +- test/runtime/value/create/literal.ts | 8 +- test/runtime/value/create/not.ts | 6 +- test/runtime/value/create/null.ts | 4 +- test/runtime/value/create/number.ts | 4 +- test/runtime/value/create/object.ts | 10 +- test/runtime/value/create/record.ts | 4 +- test/runtime/value/create/recursive.ts | 4 +- test/runtime/value/create/ref.ts | 2 +- test/runtime/value/create/regex.ts | 2 +- test/runtime/value/create/string.ts | 4 +- test/runtime/value/create/symbol.ts | 4 +- test/runtime/value/create/template-literal.ts | 10 +- test/runtime/value/create/tuple.ts | 8 +- test/runtime/value/create/uint8array.ts | 10 +- test/runtime/value/create/undefined.ts | 4 +- test/runtime/value/create/union.ts | 6 +- test/runtime/value/create/unknown.ts | 4 +- test/runtime/value/create/void.ts | 4 +- test/runtime/value/delta/diff.ts | 96 ++++----- test/runtime/value/delta/patch.ts | 98 ++++----- test/runtime/value/equal/equal.ts | 42 ++-- test/runtime/value/hash/hash.ts | 34 ++-- test/runtime/value/mutate/mutate.ts | 28 +-- test/runtime/value/pointer/pointer.ts | 186 +++++++++--------- test/static/index.ts | 1 + test/static/indexed.ts | 21 ++ test/static/rest.ts | 36 ++++ 201 files changed, 2426 insertions(+), 2231 deletions(-) create mode 100644 changelog/0.28.3.md create mode 100644 test/runtime/type/guard/rest.ts create mode 100644 test/static/rest.ts diff --git a/changelog/0.28.3.md b/changelog/0.28.3.md new file mode 100644 index 000000000..876b7e3fb --- /dev/null +++ b/changelog/0.28.3.md @@ -0,0 +1,57 @@ +## [0.28.3](https://www.npmjs.com/package/@sinclair/typebox/v/0.28.3) + +## Overview + +Revision 0.28.3 adds a new Rest type to support variadic type composition. + +## Contents + +- Enhancements + - [Variadic Types](#Variadic-Types) + + + +## Variadic Types + +Revision 0.28.3 adds a new type named `Type.Rest`. This type is used to extract a tuple array of type of `[...TSchema]`. The return value of this type is not strictly JSON Schema, however the tuple array can be used as a parameter to other types that accept tuples as their arguments. + +### Tuple Concatenation + +```typescript +// TypeScript + +type A = [1, 2] + +type B = [3, 4] + +type C = [...A, ...B] + +// TypeBox + +const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + +const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + +const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) +``` + +### Tuple To Parameter + +```typescript +// TypeScript + +type P = [number, number] + +type F1 = (param: [...P]) => void + +type F2 = (param: [...P, number]) => void + +// TypeBox + +const P = Type.Tuple([Type.Number(), Type.Number()]) + +const F1 = Type.Function(Type.Rest(P), Type.Void()) + +const F2 = Type.Function([...Type.Rest(P), Type.Number()], Type.Void()) +``` + diff --git a/example/template-dsl/template-dsl.ts b/example/template-dsl/template-dsl.ts index 1db5777e3..ad110da3a 100644 --- a/example/template-dsl/template-dsl.ts +++ b/example/template-dsl/template-dsl.ts @@ -60,7 +60,7 @@ export type TTemplateLiteralParser = Ensure { - const trim = template.trim() + const trim = template.trim().replace(/"|'/g, '') if(trim === 'boolean') return yield Type.Boolean() if(trim === 'number') return yield Type.Number() if(trim === 'bigint') return yield Type.BigInt() diff --git a/package-lock.json b/package-lock.json index 3da05bcf9..7429ae097 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.2", + "version": "0.28.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.2", + "version": "0.28.3", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 2ca7f90dc..89382d651 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.2", + "version": "0.28.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 5155d455c..7c0fc9e57 100644 --- a/readme.md +++ b/readme.md @@ -83,6 +83,7 @@ License MIT - [Conditional](#types-conditional) - [Template](#types-template-literal) - [Indexed](#types-indexed) + - [Variadic](#types-variadic) - [Guards](#types-guards) - [Unsafe](#types-unsafe) - [Strict](#types-strict) @@ -256,7 +257,8 @@ The following table lists the Standard TypeBox types. These types are fully comp │ }) │ } │ properties: { │ │ │ │ x: { │ │ │ │ type: 'number' │ -│ │ │ }, { │ +│ │ │ }, │ +│ │ │ y: { │ │ │ │ type: 'number' │ │ │ │ } │ │ │ │ } │ @@ -277,14 +279,6 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Index( │ type T = { │ const T = { │ -│ Type.Object({ │ x: number, │ type: number │ -│ x: Type.Number(), │ y: string │ } │ -│ y: Type.String() │ }['x'] │ │ -│ }), ['x'] │ │ │ -│ ) │ │ │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ enum Foo { │ enum Foo { │ const T = { │ │ A, │ A, │ anyOf: [{ │ │ B │ B │ type: 'number', │ @@ -468,6 +462,28 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Index( │ type T = { │ const T = { │ +│ Type.Object({ │ x: number, │ type: 'number' │ +│ x: Type.Number(), │ y: string │ } │ +│ y: Type.String() │ }['x'] │ │ +│ }), ['x'] │ │ │ +│ ) │ │ │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const A = Type.Tuple([ │ type A = [0, 1] │ const T = { │ +│ Type.Literal(0), │ type B = [2, 3] │ type: 'array', │ +│ Type.Literal(1) │ type T = [...A, ...B] │ items: [ │ +│ ]) │ │ { const: 0 }, │ +│ const B = Type.Tuple([ │ │ { const: 1 }, │ +| Type.Literal(2), │ │ { const: 2 }, │ +| Type.Literal(3) │ │ { const: 3 } │ +│ ]) │ │ ], │ +│ const T = Type.Tuple([ │ │ additionalItems: false, │ +| ...Type.Rest(A), │ │ minItems: 4, │ +| ...Type.Test(B) │ │ maxItems: 4 │ +│ ]) │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const R = { │ │ x: Type.Number(), │ x: number, │ $ref: 'T' │ │ y: Type.Number() │ y: number │ } │ @@ -856,10 +872,34 @@ const A = Type.Index(T, ['x']) // type A = T['x'] const B = Type.Index(T, Type.KeyOf(T)) // type B = T[keyof T] ``` + + +### Variadic Types + +Variadic types are supported with `Type.Rest`. This type will extract interior types from a tuple and return them as a flat array. This array can then be passed to other types that accept arrays as arguments. + +The following creates variadic functions using the Rest type. + +```typescript +// TypeScript +type P = [number, number] + +type F1 = (param: [...P]) => void + +type F2 = (param: [...P, number]) => void + +// TypeBox + +const P = Type.Tuple([Type.Number(), Type.Number()]) + +const F1 = Type.Function(Type.Rest(P), Type.Void()) + +const F2 = Type.Function([...Type.Rest(P), Type.Number()], Type.Void()) +``` -### Unsafe +### Unsafe Types Use `Type.Unsafe` to create custom schematics with user defined inference rules. @@ -904,7 +944,7 @@ type T = Static // type T = 'A' | 'B' | 'C' -### Guards +### Type Gaurds TypeBox provides a `TypeGuard` module that can be used for reflection and asserting values as types. @@ -1436,11 +1476,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '127.2 kb' │ ' 56.9 kb' │ '2.23 x' │ -│ typebox/errors │ '110.9 kb' │ ' 49.2 kb' │ '2.25 x' │ -│ typebox/system │ ' 76.4 kb' │ ' 31.5 kb' │ '2.42 x' │ -│ typebox/value │ '176.9 kb' │ ' 76.8 kb' │ '2.30 x' │ -│ typebox │ ' 75.3 kb' │ ' 31.1 kb' │ '2.42 x' │ +│ typebox/compiler │ '126.7 kb' │ ' 56.6 kb' │ '2.24 x' │ +│ typebox/errors │ '110.4 kb' │ ' 48.8 kb' │ '2.26 x' │ +│ typebox/system │ ' 75.9 kb' │ ' 31.1 kb' │ '2.44 x' │ +│ typebox/value │ '176.4 kb' │ ' 76.4 kb' │ '2.31 x' │ +│ typebox │ ' 74.8 kb' │ ' 30.7 kb' │ '2.44 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/typebox.ts b/src/typebox.ts index 098346ec3..e95c3cdc2 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -56,18 +56,13 @@ export type Ensure = T extends infer U ? U : never // Type Assertions // -------------------------------------------------------------------------- export type AssertProperties = T extends TProperties ? T : TProperties -export type AssertSchemas = T extends E ? T : [] -export type AssertSchema = T extends E ? T : TNever -export type AssertObjects = Assert -export type AssertObject = Assert -export type AssertKeys = Assert -export type AssertKey = Assert -export type Key = keyof any +export type AssertRest = T extends E ? T : [] +export type AssertType = T extends E ? T : TNever // -------------------------------------------------------------------------- // Type Normalization // -------------------------------------------------------------------------- -export type IntersectType = T extends [] ? TNever : T extends [TSchema] ? AssertSchema : TIntersect -export type UnionType = T extends [] ? TNever : T extends [TSchema] ? AssertSchema : TUnion +export type IntersectType = T extends [] ? TNever : T extends [TSchema] ? AssertType : TIntersect +export type UnionType = T extends [] ? TNever : T extends [TSchema] ? AssertType : TUnion // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- @@ -76,6 +71,10 @@ export type TReadonly = T & { [Modifier]: 'Readonly' } export type TOptional = T & { [Modifier]: 'Optional' } export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOptional' } // -------------------------------------------------------------------------- +// Key +// -------------------------------------------------------------------------- +export type Key = keyof any +// -------------------------------------------------------------------------- // TSchema // -------------------------------------------------------------------------- export interface SchemaOptions { @@ -196,8 +195,8 @@ export type TInstanceType> = T['retur export type TCompositeIsOptional = T extends TOptional | TReadonlyOptional ? true : false // prettier-ignore export type TCompositeOptional = T extends [infer L, ...infer R] - ? TCompositeIsOptional> extends false ? false - : TCompositeOptional> : true + ? TCompositeIsOptional> extends false ? false + : TCompositeOptional> : true export type TCompositeKeyOfUnion1 = keyof T['properties'] // prettier-ignore export type TCompositeKeyOfUnion2 = T extends [infer L, ...infer R] @@ -207,24 +206,24 @@ export type TCompositeKeyOf = UnionToTuple = K extends keyof T['properties'] ? [T['properties'][K]] : [] // prettier-ignore export type TCompositePropertiesWithKey2 = T extends [infer L, ...infer R] - ? [...TCompositePropertiesWithKey1, K>, ...TCompositePropertiesWithKey2, K>] + ? [...TCompositePropertiesWithKey1, K>, ...TCompositePropertiesWithKey2, K>] : [] // prettier-ignore export type TCompositeObjectProperty = TCompositePropertiesWithKey2 extends infer S ? - TCompositeOptional> extends true - ? { [_ in K]: TOptional>> } - : { [_ in K]: IntersectType> } + TCompositeOptional> extends true + ? { [_ in K]: TOptional>> } + : { [_ in K]: IntersectType> } : {} // prettier-ignore export type TCompositeObjectsWithKeys = K extends [infer L, ...infer R] ? L extends Key - ? TCompositeObjectProperty & TCompositeObjectsWithKeys> + ? TCompositeObjectProperty & TCompositeObjectsWithKeys> : {} : {} -export type TComposite = Ensure>>>>> +export type TComposite = Ensure, Key[]>>>>> // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- -export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] +export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] export interface TConstructor extends TSchema { [Kind]: 'Constructor' static: new (...param: TConstructorParameterArray) => Static @@ -266,17 +265,17 @@ export interface TEnum = Record = (Static extends Static ? T : U) extends infer O ? - UnionToTuple extends [infer X, infer Y] ? TUnion<[AssertSchema, AssertSchema]> : AssertSchema + UnionToTuple extends [infer X, infer Y] ? TUnion<[AssertType, AssertType]> : AssertType : never // -------------------------------------------------------------------------- // TExclude // -------------------------------------------------------------------------- -export type TExcludeTemplateLiteralResult = UnionType }[T]>>> +export type TExcludeTemplateLiteralResult = UnionType }[T]>>> export type TExcludeTemplateLiteral = Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never // prettier-ignore -export type TExcludeArray = AssertSchemas> extends Static ? never : T[K] -}[number]>> extends infer R ? UnionType> : never +export type TExcludeArray = AssertRest> extends Static ? never : T[K] +}[number]>> extends infer R ? UnionType> : never // prettier-ignore export type TExclude = T extends TTemplateLiteral ? TExcludeTemplateLiteral : @@ -285,12 +284,12 @@ export type TExclude = // -------------------------------------------------------------------------- // TExtract // -------------------------------------------------------------------------- -export type TExtractTemplateLiteralResult = UnionType }[T]>>> +export type TExtractTemplateLiteralResult = UnionType }[T]>>> export type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never // prettier-ignore -export type TExtractArray = AssertSchemas> extends Static ? T[K] : never -}[number]>> extends infer R ? UnionType> : never +export type TExtractArray = AssertRest> extends Static ? T[K] : never +}[number]>> extends infer R ? UnionType> : never // prettier-ignore export type TExtract = T extends TTemplateLiteral ? TExtractTemplateLiteral : @@ -299,7 +298,7 @@ export type TExtract = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] export interface TFunction extends TSchema { [Kind]: 'Function' static: (...param: TFunctionParameters) => Static @@ -322,7 +321,7 @@ export type TIndexTuple = [] // prettier-ignore export type TIndexComposite = - T extends [infer L, ...infer R] ? [...TIndexKey, K>, ...TIndexComposite, K>] : + T extends [infer L, ...infer R] ? [...TIndexKey, K>, ...TIndexComposite, K>] : [] // prettier-ignore export type TIndexKey = @@ -341,11 +340,11 @@ export type TIndexKeys = export type TIndex = TIndexKeys extends infer R ? T extends TRecursive ? TIndex : - T extends TTuple ? UnionType> : - T extends TIntersect ? UnionType> : - T extends TUnion ? UnionType> : - T extends TObject ? UnionType> : - T extends TArray ? UnionType> : + T extends TTuple ? UnionType> : + T extends TIntersect ? UnionType> : + T extends TUnion ? UnionType> : + T extends TObject ? UnionType> : + T extends TArray ? UnionType> : TNever : TNever // -------------------------------------------------------------------------- @@ -365,7 +364,7 @@ export interface IntersectOptions extends SchemaOptions { } export interface TIntersect extends TSchema, IntersectOptions { [Kind]: 'Intersect' - static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> + static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> type?: 'object' allOf: [...T] } @@ -382,7 +381,7 @@ export type TKeyOfProperties = Static extends infer S // prettier-ignore export type TKeyOfIndicesArray = UnionToTuple // prettier-ignore -export type TKeyOfIndices = AssertSchemas extends infer R ? { +export type TKeyOfIndices = AssertRest extends infer R ? { [K in keyof R] : TLiteral> }: []> // prettier-ignore @@ -395,7 +394,7 @@ export type TKeyOf = ( T extends TArray ? [TNumber] : T extends TRecord ? [K] : [] -) extends infer R ? UnionType> : never +) extends infer R ? UnionType> : never // -------------------------------------------------------------------------- // TLiteral // -------------------------------------------------------------------------- @@ -475,7 +474,7 @@ export interface TObject extends TSchema, O // -------------------------------------------------------------------------- // TOmit // -------------------------------------------------------------------------- -export type TOmitArray = AssertSchemas<{ [K2 in keyof T]: TOmit, K> }> +export type TOmitArray = AssertRest<{ [K2 in keyof T]: TOmit, K> }> export type TOmitProperties = Evaluate>> // prettier-ignore export type TOmit = @@ -491,8 +490,8 @@ export type TParameters = Ensure> // -------------------------------------------------------------------------- // TPartial // -------------------------------------------------------------------------- -export type TPartialObjectArray = AssertSchemas<{ [K in keyof T]: TPartial> }, TObject[]> -export type TPartialArray = AssertSchemas<{ [K in keyof T]: TPartial> }> +export type TPartialObjectArray = AssertRest<{ [K in keyof T]: TPartial> }, TObject[]> +export type TPartialArray = AssertRest<{ [K in keyof T]: TPartial> }> // prettier-ignore export type TPartialProperties = Evaluate = // -------------------------------------------------------------------------- // TPick // -------------------------------------------------------------------------- -export type TPickArray = { [K2 in keyof T]: TPick, K> } +export type TPickArray = { [K2 in keyof T]: TPick, K> } // Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, // we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. // prettier-ignore export type TPickProperties = Pick, keyof T>> extends infer R ? ({ - [K in keyof R]: AssertSchema extends TSchema ? R[K] : never + [K in keyof R]: AssertType extends TSchema ? R[K] : never }): never // prettier-ignore export type TPick = @@ -575,13 +574,17 @@ export interface TRef extends TSchema { $ref: string } // -------------------------------------------------------------------------- +// TRest +// -------------------------------------------------------------------------- +export type TRest = T extends TTuple ? Assert : Assert<[T], TSchema[]> +// -------------------------------------------------------------------------- // TReturnType // -------------------------------------------------------------------------- export type TReturnType = T['returns'] // -------------------------------------------------------------------------- // TRequired // -------------------------------------------------------------------------- -export type TRequiredArray = AssertSchemas<{ [K in keyof T]: TRequired> }> +export type TRequiredArray = AssertRest<{ [K in keyof T]: TRequired> }> // prettier-ignore export type TRequiredProperties = Evaluate = export type TTemplateLiteralUnion = T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc -export type TTemplateLiteralKeyArray = AssertKeys>> +export type TTemplateLiteralKeyArray = Assert>, Key[]> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' static: TTemplateLiteralUnion @@ -686,7 +689,7 @@ export interface TTemplateLiteral> = T extends TTuple ? AssertSchemas : never +export type TTupleIntoArray> = T extends TTuple ? AssertRest : never export interface TTuple extends TSchema { [Kind]: 'Tuple' static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } @@ -2394,16 +2397,22 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TIndex> /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): T['items'] + public Index(schema: T, key: K, options?: SchemaOptions): UnionType> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, key: K, options?: SchemaOptions): AssertType /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TIndex /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) - if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { return TypeClone.Clone(schema.items, options) } + if (TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved)) { + const items = schema.items === undefined ? [] : schema.items + const cloned = items.map((schema) => TypeClone.Clone(schema, {})) + return this.Union(cloned, options) + } const resolved = IndexedAccessor.Resolve(schema, keys as any) const cloned = resolved.map((schema) => TypeClone.Clone(schema, {})) return this.Union(cloned, options) @@ -2507,7 +2516,6 @@ export class StandardTypeBuilder extends TypeBuilder { return this.Create(schema) }, options) } - /** `[Standard]` Creates a mapped type where all properties are Optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { function Apply(schema: TSchema) { @@ -2617,6 +2625,15 @@ export class StandardTypeBuilder extends TypeBuilder { return schema }, options) } + /** `[Standard]` Returns a schema array which allows types to compose with the JavaScript spread operator */ + public Rest(schema: T): TRest { + if (TypeGuard.TTuple(schema)) { + if (schema.items === undefined) return [] as TSchema[] as TRest + return schema.items.map((schema) => TypeClone.Clone(schema, {})) as TRest + } else { + return [TypeClone.Clone(schema, {})] as TRest + } + } /** `[Standard]` Creates a String type */ public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) @@ -2677,40 +2694,20 @@ export class ExtendedTypeBuilder extends StandardTypeBuilder { return this.Tuple([...schema.parameters], { ...options }) } /** `[Extended]` Creates a Constructor type */ - public Constructor, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor, U> - /** `[Extended]` Creates a Constructor type */ - public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor - public Constructor(parameters: any, returns: any, options: SchemaOptions = {}) { + public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { const clonedReturns = TypeClone.Clone(returns, {}) - if (TypeGuard.TTuple(parameters)) { - const clonedParameters = parameters.items === undefined ? [] : parameters.items.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) - } else if (globalThis.Array.isArray(parameters)) { - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) - } else { - throw new Error('ExtendedTypeBuilder.Constructor: Invalid parameters') - } + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) } /** `[Extended]` Creates a Date type */ public Date(options: DateOptions = {}): TDate { return this.Create({ ...options, [Kind]: 'Date', type: 'object', instanceOf: 'Date' }) } /** `[Extended]` Creates a Function type */ - public Function, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction, U> - /** `[Extended]` Creates a Function type */ - public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction - public Function(parameters: any, returns: any, options: SchemaOptions = {}) { + public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { const clonedReturns = TypeClone.Clone(returns, {}) - if (TypeGuard.TTuple(parameters)) { - const clonedParameters = parameters.items === undefined ? [] : parameters.items.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) - } else if (globalThis.Array.isArray(parameters)) { - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) - } else { - throw new Error('ExtendedTypeBuilder.Function: Invalid parameters') - } + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) + return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) } /** `[Extended]` Extracts the InstanceType from the given Constructor */ public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index d632824b4..83ab9a5d5 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -7,19 +7,22 @@ export namespace Assert { const next = port++ return next } - export function equal(actual: unknown, expect: unknown) { - return assert.strictEqual(actual, expect) + export function isTrue(value: boolean) { + return assert.strictEqual(value, true) } - export function notEqual(actual: unknown, expect: unknown) { - return assert.notEqual(actual, expect) + export function isFalse(value: boolean) { + return assert.strictEqual(value, false) } - export function deepEqual(actual: unknown, expect: unknown) { + export function isEqual(actual: unknown, expect: unknown) { if (actual instanceof Uint8Array && expect instanceof Uint8Array) { assert.equal(actual.length, expect.length) for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i]) } return assert.deepEqual(actual, expect) } + export function notEqual(actual: unknown, expect: unknown) { + return assert.notEqual(actual, expect) + } let nextIdOrdinal = 0 export function nextId() { return `nextID${nextIdOrdinal++}` diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts index 7ffb2b2ae..b5b1c37ac 100644 --- a/test/runtime/compiler/recursive.ts +++ b/test/runtime/compiler/recursive.ts @@ -10,7 +10,7 @@ describe('type/compiler/Recursive', () => { nodes: Type.Array(Node), }), ) - Assert.equal(Node.$id === undefined, false) + Assert.isEqual(Node.$id === undefined, false) }) it('Should override default ordinal $id if specified', () => { @@ -22,7 +22,7 @@ describe('type/compiler/Recursive', () => { }), { $id: 'Node' }, ) - Assert.equal(Node.$id === 'Node', true) + Assert.isEqual(Node.$id === 'Node', true) }) it('Should validate recursive node type', () => { diff --git a/test/runtime/errors/iterator.ts b/test/runtime/errors/iterator.ts index 3a61c3ad9..e0baf4404 100644 --- a/test/runtime/errors/iterator.ts +++ b/test/runtime/errors/iterator.ts @@ -5,7 +5,7 @@ import { Assert } from '../assert' describe('errors/ValueErrorIterator', () => { it('Should return undefined for non error', () => { const R = ValueErrors.Errors(Type.Number(), [], 1).First() - Assert.equal(R, undefined) + Assert.isEqual(R, undefined) }) it('Should return a value error when error', () => { const { type, path, message } = ValueErrors.Errors(Type.Number(), [], '').First()! @@ -15,11 +15,11 @@ describe('errors/ValueErrorIterator', () => { }) it('Should yield empty array for non error', () => { const R = [...ValueErrors.Errors(Type.Number(), [], 1)] - Assert.equal(R.length, 0) + Assert.isEqual(R.length, 0) }) it('Should yield array with 1 error when error', () => { const R = [...ValueErrors.Errors(Type.Number(), [], 'foo')] - Assert.equal(R.length, 1) + Assert.isEqual(R.length, 1) }) it('Should yield array with N errors when error', () => { // prettier-ignore @@ -27,6 +27,6 @@ describe('errors/ValueErrorIterator', () => { x: Type.Number(), y: Type.Number() }), [], {})] // require object to invoke internal check - Assert.equal(R.length > 1, true) + Assert.isEqual(R.length > 1, true) }) }) diff --git a/test/runtime/schema/composite.ts b/test/runtime/schema/composite.ts index 9e19cb26c..93061fca4 100644 --- a/test/runtime/schema/composite.ts +++ b/test/runtime/schema/composite.ts @@ -89,6 +89,6 @@ describe('type/schema/Composite', () => { }) const A = Type.Composite([T]) const B = Type.Composite([T]) - Assert.deepEqual(A, B) + Assert.isEqual(A, B) }) }) diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts index a003f7f42..8ec132c0c 100644 --- a/test/runtime/schema/recursive.ts +++ b/test/runtime/schema/recursive.ts @@ -10,7 +10,7 @@ describe('type/schema/Recursive', () => { nodes: Type.Array(Node), }), ) - Assert.equal(Node.$id === undefined, false) + Assert.isEqual(Node.$id === undefined, false) }) it('Should override default ordinal $id if specified', () => { @@ -22,7 +22,7 @@ describe('type/schema/Recursive', () => { }), { $id: 'Node' }, ) - Assert.equal(Node.$id === 'Node', true) + Assert.isEqual(Node.$id === 'Node', true) }) it('Should validate recursive node type', () => { diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts index ad28839f3..ae1ae1597 100644 --- a/test/runtime/type/extends/any.ts +++ b/test/runtime/type/extends/any.ts @@ -6,91 +6,91 @@ describe('type/extends/Any', () => { it('Should extend Any', () => { type T = any extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = any extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = any extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Boolean', () => { type T = any extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Number', () => { type T = any extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Integer', () => { type T = any extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Array 1', () => { type T = any extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Array 2', () => { type T = any extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Tuple', () => { type T = any extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 1', () => { type T = any extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 2', () => { type T = any extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 3', () => { type T = any extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 1', () => { type T = any extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 2', () => { type T = any extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = any extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Undefined', () => { type T = any extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Void', () => { type T = any extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Date', () => { type T = any extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) }) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts index f6a349d33..ac7b04e45 100644 --- a/test/runtime/type/extends/array.ts +++ b/test/runtime/type/extends/array.ts @@ -9,22 +9,22 @@ describe('type/extends/Array', () => { it('Should extend Array Varying 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 3', () => { type T = Array extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 4', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Any @@ -32,112 +32,112 @@ describe('type/extends/Array', () => { it('Should extend Any', () => { type T = Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Constrained @@ -145,121 +145,121 @@ describe('type/extends/Array', () => { it('Should extend constrained Any', () => { type T = Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Void', () => { type T = Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/bigint.ts b/test/runtime/type/extends/bigint.ts index 779cb8424..454e22cbe 100644 --- a/test/runtime/type/extends/bigint.ts +++ b/test/runtime/type/extends/bigint.ts @@ -6,96 +6,96 @@ describe('type/extends/BigInt', () => { it('Should extend Any', () => { type T = bigint extends any ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = bigint extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = bigint extends string ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = bigint extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = bigint extends number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = bigint extends number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = bigint extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = bigint extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = bigint extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = bigint extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = bigint extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = bigint extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = bigint extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = bigint extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = bigint extends boolean | bigint ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = bigint extends null ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = bigint extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = bigint extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = bigint extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts index c627b3a42..8bb9b460f 100644 --- a/test/runtime/type/extends/boolean.ts +++ b/test/runtime/type/extends/boolean.ts @@ -6,86 +6,86 @@ describe('type/extends/Boolean', () => { it('Should extend Any', () => { type T = boolean extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = boolean extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = boolean extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Number', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = boolean extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = boolean extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = boolean extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = boolean extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = boolean extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = boolean extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = boolean extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = boolean extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = boolean extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = boolean extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = boolean extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = boolean extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts index 6d5b16ede..82211cbe1 100644 --- a/test/runtime/type/extends/constructor.ts +++ b/test/runtime/type/extends/constructor.ts @@ -6,206 +6,206 @@ describe('type/extends/Constructor', () => { it('Should extend Function', () => { type T = (new () => number) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 2', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 3', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 4', () => { type T = (new (a: number) => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 5', () => { type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 6', () => { type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 7', () => { type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 8', () => { type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 10', () => { type T = (new () => Array) extends new () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 11', () => { type T = (new () => Array) extends new () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 12', () => { type T = (new () => object) extends new () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 13', () => { type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 14', () => { type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 15', () => { type T = (new () => any) extends new () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 16', () => { type T = (new () => unknown) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Any', () => { type T = (new () => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = (new () => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = (new () => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = (new () => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = (new () => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = (new () => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = (new () => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = (new () => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = (new () => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = (new () => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = (new () => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = (new () => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = (new () => number) extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = (new () => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts index 21b6653ac..7aba817c8 100644 --- a/test/runtime/type/extends/date.ts +++ b/test/runtime/type/extends/date.ts @@ -6,91 +6,91 @@ describe('type/extends/Date', () => { it('Should extend Any', () => { type T = Date extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Date extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Date extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Date extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Date extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Date extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Date extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Date extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Date extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Date extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Date extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Date extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Date extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Date extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Date extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Date extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts index 8e36b03ed..7823aa382 100644 --- a/test/runtime/type/extends/function.ts +++ b/test/runtime/type/extends/function.ts @@ -6,206 +6,206 @@ describe('type/extends/Function', () => { it('Should extend Constructor 1', () => { type T = (() => number) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Function 1', () => { type T = (() => number) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 2', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 3', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 4', () => { type T = ((a: number) => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Function 5', () => { type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 6', () => { type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Function 7', () => { type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Function 8', () => { type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 10', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 11', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 12', () => { type T = (() => object) extends () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Function 13', () => { type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 14', () => { type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 15', () => { type T = (() => any) extends () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Function 16', () => { type T = (() => unknown) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Any', () => { type T = (() => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = (() => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = (() => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = (() => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = (() => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = (() => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = (() => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = (() => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = (() => number) extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = (() => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = (() => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = (() => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = (() => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = (() => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts index d3c5e6016..d0113e0f2 100644 --- a/test/runtime/type/extends/integer.ts +++ b/test/runtime/type/extends/integer.ts @@ -6,91 +6,91 @@ describe('type/extends/Integer', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts index f11545ae2..a99e26a95 100644 --- a/test/runtime/type/extends/literal.ts +++ b/test/runtime/type/extends/literal.ts @@ -9,77 +9,77 @@ describe('type/extends/Literal', () => { it('Should extend Any (String)', () => { type T = 'hello' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (String)', () => { type T = 'hello' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String (String)', () => { type T = 'hello' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (String)', () => { type T = 'hello' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array (String)', () => { type T = 'hello' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (String)', () => { type T = 'hello' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (String)', () => { type T = 'hello' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (String)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (String)', () => { type T = 'hello' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (String)', () => { type T = 'hello' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (String)', () => { type T = 'hello' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null (String)', () => { type T = 'hello' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (String)', () => { type T = 'hello' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // Number Literal @@ -87,77 +87,77 @@ describe('type/extends/Literal', () => { it('Should extend Any (Number)', () => { type T = 10 extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (Number)', () => { type T = 10 extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String (Number)', () => { type T = 10 extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean (Number)', () => { type T = 10 extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Integer (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array (Number)', () => { type T = 10 extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (Number)', () => { type T = 10 extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (Number)', () => { type T = 10 extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (Number)', () => { type T = 10 extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (Number)', () => { type T = 10 extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (Number)', () => { type T = 10 extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (Number)', () => { type T = 10 extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null (Number)', () => { type T = 10 extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (Number)', () => { type T = 10 extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // Boolean Literal @@ -165,101 +165,101 @@ describe('type/extends/Literal', () => { it('Should extend Any (Boolean)', () => { type T = true extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (Boolean)', () => { type T = true extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String (Boolean)', () => { type T = true extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean (Boolean)', () => { type T = true extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Number (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array (Boolean)', () => { type T = true extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (Boolean)', () => { type T = true extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = 'hello' extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = 10 extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 3', () => { type T = true extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (Boolean)', () => { type T = true extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (Boolean)', () => { type T = true extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (Boolean)', () => { type T = true extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2 (Boolean)', () => { type T = true extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (Boolean)', () => { type T = true extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null (Boolean)', () => { type T = true extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (Boolean)', () => { type T = true extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = true extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = true extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts index f2d793ab9..dc9077b94 100644 --- a/test/runtime/type/extends/null.ts +++ b/test/runtime/type/extends/null.ts @@ -6,96 +6,96 @@ describe('type/extends/Null', () => { it('Should extend Any', () => { type T = null extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = null extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = null extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = null extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = null extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = null extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = null extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = null extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = null extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = null extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = null extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = null extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = null extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = null extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Undefined', () => { type T = null extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = null extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = null extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts index 3c61fbe38..c34c88573 100644 --- a/test/runtime/type/extends/number.ts +++ b/test/runtime/type/extends/number.ts @@ -6,91 +6,91 @@ describe('type/extends/Number', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts index b5a2973ed..0ab311516 100644 --- a/test/runtime/type/extends/object.ts +++ b/test/runtime/type/extends/object.ts @@ -11,42 +11,42 @@ describe('type/extends/Object', () => { const A = Type.Object({ x: Type.Number(), y: Type.Number() }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = { x: number } extends { x: number; y: number } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = { x: number; y: string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.String() }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 4', () => { type T = { x: number } extends { x: number; y: string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.String() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = { x: number | string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 6', () => { type T = { x: number } extends { x: number | string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) // ---------------------------------------------------------- // Record @@ -56,35 +56,35 @@ describe('type/extends/Object', () => { const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 5', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 6', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) // ---------------------------------------------------------- // Standard @@ -92,86 +92,86 @@ describe('type/extends/Object', () => { it('Should extend Any', () => { type T = { a: number } extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = { a: number } extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = { a: number } extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = { a: number } extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = { a: number } extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = { a: number } extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = { a: number } extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = { a: number } extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = { a: number } extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = { a: number } extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = { a: number } extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = { a: number } extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = { a: number } extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = { a: number } extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = { a: number } extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = { a: number } extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = { a: number } extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts index 44833d85b..7ebd71de5 100644 --- a/test/runtime/type/extends/promise.ts +++ b/test/runtime/type/extends/promise.ts @@ -9,22 +9,22 @@ describe('type/extends/Promise', () => { it('Should extend Promise Varying 1', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 2', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 3', () => { type T = Promise extends Promise ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 4', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Any @@ -32,87 +32,87 @@ describe('type/extends/Promise', () => { it('Should extend Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Promise extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Constrained @@ -120,91 +120,91 @@ describe('type/extends/Promise', () => { it('Should extend constrained Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Promise extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Promise extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/record.ts b/test/runtime/type/extends/record.ts index a3dfa77ee..369e6050a 100644 --- a/test/runtime/type/extends/record.ts +++ b/test/runtime/type/extends/record.ts @@ -8,77 +8,77 @@ describe('type/extends/Record', () => { const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) // ----- it('Should extend Record 12', () => { @@ -86,28 +86,28 @@ describe('type/extends/Record', () => { const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) // ------------------------------------------------------------------- // Standard @@ -115,86 +115,86 @@ describe('type/extends/Record', () => { it('Should extend Any', () => { type T = Record extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Record extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = Record extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Record extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts index 1746be6e6..dc8954666 100644 --- a/test/runtime/type/extends/string.ts +++ b/test/runtime/type/extends/string.ts @@ -6,106 +6,106 @@ describe('type/extends/String', () => { it('Should extend Any', () => { type T = string extends any ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = string extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = string extends string ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean', () => { type T = string extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = string extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = string extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = string extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = string extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/symbol.ts b/test/runtime/type/extends/symbol.ts index 402de3d61..e80838409 100644 --- a/test/runtime/type/extends/symbol.ts +++ b/test/runtime/type/extends/symbol.ts @@ -6,101 +6,101 @@ describe('type/extends/Symbol', () => { it('Should extend Any', () => { type T = symbol extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = symbol extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = symbol extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = symbol extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = symbol extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = symbol extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = symbol extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = symbol extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = symbol extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = symbol extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = symbol extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = symbol extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = symbol extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = symbol extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = symbol extends boolean | symbol ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = symbol extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = symbol extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = symbol extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = symbol extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol extends symbol ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Symbol()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/template-literal.ts b/test/runtime/type/extends/template-literal.ts index 9abf5f547..c8a98aa19 100644 --- a/test/runtime/type/extends/template-literal.ts +++ b/test/runtime/type/extends/template-literal.ts @@ -9,77 +9,77 @@ describe('type/extends/TemplateLiteral', () => { it('Should extend Any (hello)', () => { type T = 'hello' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (hello)', () => { type T = 'hello' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String (hello)', () => { type T = 'hello' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (hello)', () => { type T = 'hello' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number (hello)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (hello)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array (hello)', () => { type T = 'hello' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (hello)', () => { type T = 'hello' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (hello)', () => { type T = 'hello' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (hello)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (hello)', () => { type T = 'hello' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (hello)', () => { type T = 'hello' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (hello)', () => { type T = 'hello' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null (hello)', () => { type T = 'hello' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (hello)', () => { type T = 'hello' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // String Literal 'hello' | 'world' @@ -87,76 +87,76 @@ describe('type/extends/TemplateLiteral', () => { it('Should extend Any (hello | world)', () => { type T = 'hello' | 'world' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (hello | world)', () => { type T = 'hello' | 'world' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String (hello | world)', () => { type T = 'hello' | 'world' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (hello | world)', () => { type T = 'hello' | 'world' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array (hello | world)', () => { type T = 'hello' | 'world' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (hello | world)', () => { type T = 'hello' | 'world' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (hello | world)', () => { type T = 'hello' | 'world' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (hello | world)', () => { type T = 'hello' | 'world' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (hello | world)', () => { type T = 'hello' | 'world' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (hello | world)', () => { type T = 'hello' | 'world' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (hello | world)', () => { type T = 'hello' | 'world' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null (hello | world)', () => { type T = 'hello' | 'world' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (hello | world)', () => { type T = 'hello' | 'world' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/tuple.ts b/test/runtime/type/extends/tuple.ts index 15486a6a6..f763fb884 100644 --- a/test/runtime/type/extends/tuple.ts +++ b/test/runtime/type/extends/tuple.ts @@ -6,156 +6,156 @@ describe('type/extends/Tuple', () => { it('Should extend Any', () => { type T = [string, number] extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = [string, number] extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = [string, number] extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = [string, number] extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = [string, number] extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array 2', () => { type T = [string, number] extends Array ? 1 : 2 // 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Array 4', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 1', () => { type T = [string, number] extends [string, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 2', () => { type T = [string, number] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 3', () => { type T = [string, any] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 4', () => { type T = [string, number] extends [string, any] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 5', () => { type T = [string, unknown] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 6', () => { type T = [string, number] extends [string, unknown] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 7', () => { type T = [] extends [string, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 8', () => { type T = [string, number] extends [] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = [string, number] extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = [string, number] extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = [string, number] extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = [string, number] extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = [string, number] extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = [string, number] extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = [string, number] extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = [string, number] extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = [string, number] extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = [string, number] extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts index 2ffea0806..286e00d9e 100644 --- a/test/runtime/type/extends/uint8array.ts +++ b/test/runtime/type/extends/uint8array.ts @@ -6,91 +6,91 @@ describe('type/extends/Uint8Array', () => { it('Should extend Any', () => { type T = Uint8Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Uint8Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Uint8Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Uint8Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Uint8Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Uint8Array extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 1', () => { type T = Uint8Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Uint8Array extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Uint8Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Uint8Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Uint8Array extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Uint8Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Uint8Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Uint8Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Uint8Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts index f6b39edcb..78b8f4a2b 100644 --- a/test/runtime/type/extends/undefined.ts +++ b/test/runtime/type/extends/undefined.ts @@ -6,86 +6,86 @@ describe('type/extends/Undefined', () => { it('Should extend Any', () => { type T = undefined extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = undefined extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = undefined extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = undefined extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = undefined extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = undefined extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = undefined extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = undefined extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = undefined extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = undefined extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = undefined extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = undefined extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = undefined extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Date', () => { type T = undefined extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/union.ts b/test/runtime/type/extends/union.ts index ab749f76b..35b00def8 100644 --- a/test/runtime/type/extends/union.ts +++ b/test/runtime/type/extends/union.ts @@ -6,131 +6,131 @@ describe('type/extends/Union', () => { it('Should extend Any', () => { type T = number | string extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number | string extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number | string extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number | string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = number | string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = number | string extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number | string extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number | string extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number | string extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number | string extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number | string extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = number | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = any | boolean extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 5', () => { type T = any | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 6', () => { type T = any | {} extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 7', () => { type T = any extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.Union) + Assert.isEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 8', () => { type T = unknown | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 9', () => { type T = unknown extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = number | string extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number | string extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number | string extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void 2', () => { type T = number | string | void extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date 2', () => { type T = Date | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend BigInt', () => { type T = bigint | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts index 152cd71bd..4a5adba9d 100644 --- a/test/runtime/type/extends/unknown.ts +++ b/test/runtime/type/extends/unknown.ts @@ -6,96 +6,96 @@ describe('type/extends/Unknown', () => { it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts index 171994949..8cf53aee2 100644 --- a/test/runtime/type/extends/void.ts +++ b/test/runtime/type/extends/void.ts @@ -6,101 +6,101 @@ describe('type/extends/Void', () => { it('Should extend Any', () => { type T = void extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Any()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = void extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = void extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.String()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = void extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Number()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Integer()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = void extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = void extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = void extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = void extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = void extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = void extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = void extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = void extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = void extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Null()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = void extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = void extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Void()) - Assert.deepEqual(R, TypeExtendsResult.True) + Assert.isEqual(R, TypeExtendsResult.True) }) it('Should extend Date', () => { type T = void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Date()) - Assert.deepEqual(R, TypeExtendsResult.False) + Assert.isEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/guard/any.ts b/test/runtime/type/guard/any.ts index 8f8e4d86c..ba3eaed09 100644 --- a/test/runtime/type/guard/any.ts +++ b/test/runtime/type/guard/any.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TAny', () => { it('Should guard for TAny', () => { const R = TypeGuard.TAny(Type.Any()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TAny', () => { const R = TypeGuard.TAny(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TAny with invalid $id', () => { // @ts-ignore const R = TypeGuard.TAny(Type.Any({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/array.ts b/test/runtime/type/guard/array.ts index a6bec8803..80a185979 100644 --- a/test/runtime/type/guard/array.ts +++ b/test/runtime/type/guard/array.ts @@ -5,12 +5,12 @@ import { Assert } from '../../assert/index' describe('type/guard/TArray', () => { it('Should guard for TArray', () => { const R = TypeGuard.TArray(Type.Array(Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TArray', () => { const R = TypeGuard.TArray(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should guard for nested object TArray', () => { @@ -22,7 +22,7 @@ describe('type/guard/TArray', () => { }), ), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for nested object TArray', () => { @@ -34,30 +34,30 @@ describe('type/guard/TArray', () => { }), ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TArray with invalid $id', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.Number(), { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TArray with invalid minItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { minItems: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TArray with invalid maxItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { maxItems: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TArray with invalid uniqueItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { uniqueItems: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/bigint.ts b/test/runtime/type/guard/bigint.ts index 12be1911b..200294d8b 100644 --- a/test/runtime/type/guard/bigint.ts +++ b/test/runtime/type/guard/bigint.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TBigInt', () => { it('Should guard for TBigInt', () => { const R = TypeGuard.TBigInt(Type.BigInt()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TBigInt', () => { const R = TypeGuard.TBigInt(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for BigInt with invalid $id', () => { // @ts-ignore const R = TypeGuard.TBigInt(Type.BigInt({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/boolean.ts b/test/runtime/type/guard/boolean.ts index 5368dedb2..ec9d8b3cd 100644 --- a/test/runtime/type/guard/boolean.ts +++ b/test/runtime/type/guard/boolean.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TBoolean', () => { it('Should guard for TBoolean', () => { const R = TypeGuard.TBoolean(Type.Boolean()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TBoolean', () => { const R = TypeGuard.TBoolean(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TBoolean with invalid $id', () => { // @ts-ignore const R = TypeGuard.TBoolean(Type.Boolean({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index c9d7d611e..23405772a 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -5,23 +5,23 @@ import { Assert } from '../../assert/index' describe('type/guard/TComposite', () => { it('Should guard for distinct properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - Assert.equal(TypeGuard.TNumber(T.properties.x), true) - Assert.equal(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) }) it('Should guard for overlapping properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) - Assert.equal(TypeGuard.TIntersect(T.properties.x), true) + Assert.isEqual(TypeGuard.TIntersect(T.properties.x), true) // @ts-ignore - Assert.equal(TypeGuard.TNumber(T.properties.x.allOf[0]), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.x.allOf[0]), true) // @ts-ignore - Assert.equal(TypeGuard.TNumber(T.properties.x.allOf[1]), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.x.allOf[1]), true) }) it('Should not produce optional property if all properties are not optional', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) - Assert.equal(TypeGuard.TOptional(T.properties.x), false) + Assert.isEqual(TypeGuard.TOptional(T.properties.x), false) }) it('Should produce optional property if all properties are optional', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) })]) - Assert.equal(TypeGuard.TOptional(T.properties.x), true) + Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) }) }) diff --git a/test/runtime/type/guard/constructor.ts b/test/runtime/type/guard/constructor.ts index 813594c0c..b60155859 100644 --- a/test/runtime/type/guard/constructor.ts +++ b/test/runtime/type/guard/constructor.ts @@ -5,31 +5,31 @@ import { Assert } from '../../assert/index' describe('type/guard/TConstructor', () => { it('Should guard for TConstructor', () => { const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TConstructor', () => { const R = TypeGuard.TConstructor(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TConstructor with invalid $id', () => { // @ts-ignore const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TConstructor with invalid Params', () => { const R = TypeGuard.TConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TConstructor with invalid Return', () => { const R = TypeGuard.TConstructor(Type.Constructor([], {} as any)) - Assert.equal(R, false) + Assert.isEqual(R, false) }) - it('Should guard for TConstructor with empty TTuple', () => { - const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([]), Type.Number())) - Assert.equal(R, true) + it('Should guard for TConstructor with empty Rest Tuple', () => { + const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([])), Type.Number())) + Assert.isEqual(R, true) }) - it('Should guard for TConstructor with array TTuple', () => { - const R = TypeGuard.TConstructor(Type.Constructor(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) - Assert.equal(R, true) + it('Should guard for TConstructor with Rest Tuple', () => { + const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/guard/date.ts b/test/runtime/type/guard/date.ts index b14cd4600..98fb8db2f 100644 --- a/test/runtime/type/guard/date.ts +++ b/test/runtime/type/guard/date.ts @@ -5,41 +5,41 @@ import { Assert } from '../../assert/index' describe('type/guard/TDate', () => { it('Should guard for TDate', () => { const R = TypeGuard.TDate(Type.Date()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TDate', () => { const R = TypeGuard.TDate(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TDate with invalid $id', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TDate with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ minimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TDate with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ maximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TDate with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ exclusiveMinimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TDate with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ exclusiveMaximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts index f85a18fad..4f6d3a29f 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/exclude.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExclude', () => { it('Should extract string from number', () => { const T = Type.Exclude(Type.String(), Type.Number()) - Assert.deepEqual(TypeGuard.TString(T), true) + Assert.isEqual(TypeGuard.TString(T), true) }) it('Should extract string from string', () => { const T = Type.Exclude(Type.String(), Type.String()) - Assert.deepEqual(TypeGuard.TNever(T), true) + Assert.isEqual(TypeGuard.TNever(T), true) }) it('Should extract string | number | boolean from string', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.deepEqual(TypeGuard.TUnion(T), true) - Assert.deepEqual(TypeGuard.TNumber(T.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(T), true) + Assert.isEqual(TypeGuard.TNumber(T.anyOf[0]), true) + Assert.isEqual(TypeGuard.TBoolean(T.anyOf[1]), true) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.deepEqual(TypeGuard.TNumber(T), true) + Assert.isEqual(TypeGuard.TNumber(T), true) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -28,20 +28,20 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(TypeGuard.TNever(T), true) + Assert.isEqual(TypeGuard.TNever(T), true) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C'].includes(T.const), true) + Assert.isEqual(['C'].includes(T.const), true) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) }) // ------------------------------------------------------------------------ // TemplateLiteral | Union 1 @@ -50,20 +50,20 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) - Assert.deepEqual(TypeGuard.TNever(T), true) + Assert.isEqual(TypeGuard.TNever(T), true) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C'].includes(T.const), true) + Assert.isEqual(['C'].includes(T.const), true) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) }) // ------------------------------------------------------------------------ // Union | TemplateLiteral 1 @@ -72,19 +72,19 @@ describe('type/guard/TExclude', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(TypeGuard.TNever(T), true) + Assert.isEqual(TypeGuard.TNever(T), true) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C'].includes(T.const), true) + Assert.isEqual(['C'].includes(T.const), true) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) }) }) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts index 66914e7e7..13d59bd0c 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/extract.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExtract', () => { it('Should extract string from number', () => { const T = Type.Extract(Type.String(), Type.Number()) - Assert.deepEqual(TypeGuard.TNever(T), true) + Assert.isEqual(TypeGuard.TNever(T), true) }) it('Should extract string from string', () => { const T = Type.Extract(Type.String(), Type.String()) - Assert.deepEqual(TypeGuard.TString(T), true) + Assert.isEqual(TypeGuard.TString(T), true) }) it('Should extract string | number | boolean from string', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.deepEqual(TypeGuard.TString(T), true) + Assert.isEqual(TypeGuard.TString(T), true) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.deepEqual(TypeGuard.TUnion(T), true) - Assert.deepEqual(TypeGuard.TString(T.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(T), true) + Assert.isEqual(TypeGuard.TString(T.anyOf[0]), true) + Assert.isEqual(TypeGuard.TBoolean(T.anyOf[1]), true) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -28,22 +28,22 @@ describe('type/guard/TExtract', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A'].includes(T.const), true) + Assert.isEqual(['A'].includes(T.const), true) }) // ------------------------------------------------------------------------ // TemplateLiteral | Union 1 @@ -52,22 +52,22 @@ describe('type/guard/TExtract', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Extract(A, B) - Assert.deepEqual(['A'].includes(T.const), true) + Assert.isEqual(['A'].includes(T.const), true) }) // ------------------------------------------------------------------------ // Union | TemplateLiteral 1 @@ -76,21 +76,21 @@ describe('type/guard/TExtract', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.deepEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.deepEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) + Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Assert.deepEqual(['A'].includes(T.const), true) + Assert.isEqual(['A'].includes(T.const), true) }) }) diff --git a/test/runtime/type/guard/function.ts b/test/runtime/type/guard/function.ts index e70b67d57..2608266fd 100644 --- a/test/runtime/type/guard/function.ts +++ b/test/runtime/type/guard/function.ts @@ -5,31 +5,31 @@ import { Assert } from '../../assert/index' describe('type/guard/TFunction', () => { it('Should guard for TFunction', () => { const R = TypeGuard.TFunction(Type.Function([], Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TFunction', () => { const R = TypeGuard.TFunction(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TFunction with invalid $id', () => { // @ts-ignore const R = TypeGuard.TFunction(Type.Function([], Type.Number(), { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TFunction with invalid Params', () => { const R = TypeGuard.TFunction(Type.Function([{} as any, {} as any], Type.Number())) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TFunction with invalid Return', () => { const R = TypeGuard.TFunction(Type.Function([], {} as any)) - Assert.equal(R, false) + Assert.isEqual(R, false) }) - it('Should guard for TFunction with empty TTuple', () => { - const R = TypeGuard.TFunction(Type.Function(Type.Tuple([]), Type.Number())) - Assert.equal(R, true) + it('Should guard for TFunction with empty Rest Tuple', () => { + const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([])), Type.Number())) + Assert.isEqual(R, true) }) - it('Should guard for TFunction with array TTuple', () => { - const R = TypeGuard.TFunction(Type.Function(Type.Tuple([Type.Number(), Type.String()]), Type.Number())) - Assert.equal(R, true) + it('Should guard for TFunction with Rest Tuple', () => { + const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 907c82e4a..969112f91 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -23,6 +23,7 @@ import './pick' import './promise' import './record' import './ref' +import './rest' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 0ddd02fe9..15eeaa08b 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -9,7 +9,7 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x']) - Assert.deepEqual(TypeGuard.TNumber(I), true) + Assert.isEqual(TypeGuard.TNumber(I), true) }) it('Should Index 2', () => { const T = Type.Object({ @@ -17,9 +17,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x', 'y']) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) }) it('Should Index 3', () => { const T = Type.Object({ @@ -27,9 +27,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, Type.KeyOf(T)) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) }) it('Should Index 4', () => { const T = Type.Object({ @@ -37,62 +37,79 @@ describe('type/guard/TIndex', () => { ac: Type.String(), }) const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) }) it('Should Index 5', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) }) it('Should Index 6', () => { const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) }) it('Should Index 7', () => { const T = Type.Array(Type.Null()) const I = Type.Index(T, Type.Number()) - Assert.deepEqual(TypeGuard.TNull(I), true) + Assert.isEqual(TypeGuard.TNull(I), true) }) it('Should Index 6', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0]) - Assert.deepEqual(TypeGuard.TLiteralString(I), true) - Assert.deepEqual(I.const, 'hello') + Assert.isEqual(TypeGuard.TLiteralString(I), true) + Assert.isEqual(I.const, 'hello') }) it('Should Index 8', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1]) - Assert.deepEqual(TypeGuard.TLiteralString(I), true) - Assert.deepEqual(I.const, 'world') + Assert.isEqual(TypeGuard.TLiteralString(I), true) + Assert.isEqual(I.const, 'world') }) it('Should Index 9', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 1]) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(I.anyOf[0].const, 'hello') - Assert.deepEqual(I.anyOf[1].const, 'world') + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(I.anyOf[0].const, 'hello') + Assert.isEqual(I.anyOf[1].const, 'world') }) it('Should Index 10', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1, 0]) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(I.anyOf[0].const, 'world') - Assert.deepEqual(I.anyOf[1].const, 'hello') + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(I.anyOf[0].const, 'world') + Assert.isEqual(I.anyOf[1].const, 'hello') }) it('Should Index 11', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 0, 0, 1]) - Assert.deepEqual(TypeGuard.TUnion(I), true) - Assert.deepEqual(I.anyOf[0].const, 'hello') - Assert.deepEqual(I.anyOf[1].const, 'hello') - Assert.deepEqual(I.anyOf[2].const, 'hello') - Assert.deepEqual(I.anyOf[3].const, 'world') + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(I.anyOf[0].const, 'hello') + Assert.isEqual(I.anyOf[1].const, 'hello') + Assert.isEqual(I.anyOf[2].const, 'hello') + Assert.isEqual(I.anyOf[3].const, 'world') + }) + it('Should Index 12', () => { + const T = Type.Tuple([Type.String(), Type.Boolean()]) + const I = Type.Index(T, Type.Number()) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TBoolean(I.anyOf[1]), true) + }) + it('Should Index 13', () => { + const T = Type.Tuple([Type.String()]) + const I = Type.Index(T, Type.Number()) + Assert.isEqual(TypeGuard.TString(I), true) + }) + it('Should Index 14', () => { + const T = Type.Tuple([]) + const I = Type.Index(T, Type.Number()) + Assert.isEqual(TypeGuard.TNever(I), true) }) }) diff --git a/test/runtime/type/guard/integer.ts b/test/runtime/type/guard/integer.ts index 28976d20e..1938d4d23 100644 --- a/test/runtime/type/guard/integer.ts +++ b/test/runtime/type/guard/integer.ts @@ -5,40 +5,40 @@ import { Assert } from '../../assert/index' describe('type/guard/TInteger', () => { it('Should guard for TInteger', () => { const R = TypeGuard.TInteger(Type.Integer()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TInteger', () => { const R = TypeGuard.TInteger(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid $id', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ multipleOf: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ minimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ maximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMinimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TInteger with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMaximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/intersect.ts index 907b4a72d..9ed33e6b2 100644 --- a/test/runtime/type/guard/intersect.ts +++ b/test/runtime/type/guard/intersect.ts @@ -14,7 +14,7 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TIntersect', () => { const R = TypeGuard.TIntersect( @@ -27,6 +27,6 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/keyof.ts index 59c3067bd..cca0b0dae 100644 --- a/test/runtime/type/guard/keyof.ts +++ b/test/runtime/type/guard/keyof.ts @@ -9,9 +9,9 @@ describe('type/guard/TKeyOf', () => { y: Type.Number(), }) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TUnion(K), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(K), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) it('Should KeyOf 2', () => { const T = Type.Recursive((Self) => @@ -21,9 +21,9 @@ describe('type/guard/TKeyOf', () => { }), ) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TUnion(K), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(K), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) it('Should KeyOf 3', () => { const T = Type.Intersect([ @@ -35,9 +35,9 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TUnion(K), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.deepEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.isEqual(TypeGuard.TUnion(K), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) + Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) }) it('Should KeyOf 4', () => { const T = Type.Union([ @@ -49,28 +49,28 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TNever(K), true) + Assert.isEqual(TypeGuard.TNever(K), true) }) it('Should KeyOf 5', () => { const T = Type.Null() const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TNever(K), true) + Assert.isEqual(TypeGuard.TNever(K), true) }) it('Should KeyOf 6', () => { const T = Type.Array(Type.Number()) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TNumber(K), true) + Assert.isEqual(TypeGuard.TNumber(K), true) }) it('Should KeyOf 7', () => { const T = Type.Tuple([]) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TNever(K), true) + Assert.isEqual(TypeGuard.TNever(K), true) }) it('Should KeyOf 8', () => { const T = Type.Tuple([Type.Number(), Type.Null()]) const K = Type.KeyOf(T) - Assert.deepEqual(TypeGuard.TUnion(K), true) - Assert.deepEqual(K.anyOf[0].const, '0') - Assert.deepEqual(K.anyOf[1].const, '1') + Assert.isEqual(TypeGuard.TUnion(K), true) + Assert.isEqual(K.anyOf[0].const, '0') + Assert.isEqual(K.anyOf[1].const, '1') }) }) diff --git a/test/runtime/type/guard/literal.ts b/test/runtime/type/guard/literal.ts index 674c9dd40..70d568f39 100644 --- a/test/runtime/type/guard/literal.ts +++ b/test/runtime/type/guard/literal.ts @@ -5,28 +5,28 @@ import { Assert } from '../../assert/index' describe('type/guard/TLiteral', () => { it('Should guard for TLiteral of String', () => { const R = TypeGuard.TLiteral(Type.Literal('hello')) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TLiteral of Number', () => { const R = TypeGuard.TLiteral(Type.Literal(42)) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TLiteral of Boolean', () => { const R = TypeGuard.TLiteral(Type.Literal(true)) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TLiteral of Null', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(null)) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TLiteral', () => { const R = TypeGuard.TLiteral(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TLiteral with invalid $id', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(42, { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/not.ts index 0bbc9d5ec..b5a8bf07a 100644 --- a/test/runtime/type/guard/not.ts +++ b/test/runtime/type/guard/not.ts @@ -5,19 +5,19 @@ import { Assert } from '../../assert/index' describe('type/guard/TNot', () => { it('Should guard for TNot', () => { const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TNot 1', () => { const R = TypeGuard.TNot(Type.Not(null as any, Type.String())) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNot 2', () => { const R = TypeGuard.TNot(Type.Not(Type.String(), null as any)) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNot with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String()), { $id: 1 }) - Assert.equal(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/guard/null.ts b/test/runtime/type/guard/null.ts index 11656ab0c..7fc7f1931 100644 --- a/test/runtime/type/guard/null.ts +++ b/test/runtime/type/guard/null.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TNull', () => { it('Should guard for TNull', () => { const R = TypeGuard.TNull(Type.Null()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TNull', () => { const R = TypeGuard.TNull(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNull with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNull(Type.Null({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/number.ts b/test/runtime/type/guard/number.ts index c325b27b4..de8782045 100644 --- a/test/runtime/type/guard/number.ts +++ b/test/runtime/type/guard/number.ts @@ -5,40 +5,40 @@ import { Assert } from '../../assert/index' describe('type/guard/TNumber', () => { it('Should guard for TNumber', () => { const R = TypeGuard.TNumber(Type.Number()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TNumber', () => { const R = TypeGuard.TNumber(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ multipleOf: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ minimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ maximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMinimum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TNumber with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMaximum: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/object.ts b/test/runtime/type/guard/object.ts index 2cdb5044c..4811794bd 100644 --- a/test/runtime/type/guard/object.ts +++ b/test/runtime/type/guard/object.ts @@ -10,12 +10,12 @@ describe('type/guard/TObject', () => { y: Type.Number(), }), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TObject', () => { const R = TypeGuard.TObject(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with escape characters in property key', () => { @@ -24,7 +24,7 @@ describe('type/guard/TObject', () => { 'hello\nworld': Type.Number(), }), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with invalid property values', () => { @@ -34,7 +34,7 @@ describe('type/guard/TObject', () => { y: {} as any, }), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with invalid additionalProperties', () => { @@ -49,7 +49,7 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with invalid $id', () => { @@ -64,7 +64,7 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with invalid minProperties', () => { @@ -79,7 +79,7 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with invalid maxProperties', () => { @@ -94,7 +94,7 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should guard for TObject with invalid additional properties', () => { @@ -110,7 +110,7 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TObject with valid additional properties schema', () => { @@ -125,6 +125,6 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index debf380db..0c2ccaf1a 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -8,11 +8,11 @@ describe('type/guard/TOmit', () => { // ------------------------------------------------------------------------- it('Should support TUnsafe omit properties with no Kind', () => { const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) - Assert.deepEqual(T.required, ['y']) + Assert.isEqual(T.required, ['y']) }) it('Should support TUnsafe omit properties with unregistered Kind', () => { const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownOmitType' }), y: Type.Number() }), ['x']) - Assert.deepEqual(T.required, ['y']) + Assert.isEqual(T.required, ['y']) }) // ------------------------------------------------------------------------- // Standard Tests @@ -25,8 +25,8 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.deepEqual(T.required, ['y']) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(T.required, ['y']) }) it('Should Omit 2', () => { const T = Type.Omit( @@ -36,8 +36,8 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.deepEqual(T.required, undefined) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(T.required, undefined) }) it('Should Omit 3', () => { const L = Type.Literal('x') @@ -48,15 +48,15 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.deepEqual(T.required, ['y']) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(T.required, ['y']) }) it('Should Omit 4', () => { const L = Type.Literal('x') const T = Type.Omit(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.deepEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) + Assert.isEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) // @ts-ignore - Assert.deepEqual(T.allOf[1].properties.x, undefined) + Assert.isEqual(T.allOf[1].properties.x, undefined) }) it('Should Omit 5', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) @@ -68,11 +68,11 @@ describe('type/guard/TOmit', () => { L, ) // @ts-ignore - Assert.deepEqual(T.properties.x, undefined) + Assert.isEqual(T.properties.x, undefined) // @ts-ignore - Assert.deepEqual(T.properties.y, undefined) + Assert.isEqual(T.properties.y, undefined) // @ts-ignore - Assert.deepEqual(T.required, undefined) + Assert.isEqual(T.required, undefined) }) it('Should Omit 6', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) @@ -84,11 +84,11 @@ describe('type/guard/TOmit', () => { L, ) // @ts-ignore - Assert.deepEqual(T.properties.x, undefined) + Assert.isEqual(T.properties.x, undefined) // @ts-ignore - Assert.deepEqual(T.properties.y, undefined) + Assert.isEqual(T.properties.y, undefined) // @ts-ignore - Assert.deepEqual(T.required, undefined) + Assert.isEqual(T.required, undefined) }) it('Should Omit 7', () => { const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) @@ -100,7 +100,7 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.ad), true) - Assert.deepEqual(T.required, ['ad']) + Assert.isEqual(TypeGuard.TNumber(T.properties.ad), true) + Assert.isEqual(T.required, ['ad']) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index 5eb292ca9..523f2acc3 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -9,15 +9,15 @@ describe('type/guard/TPartial', () => { // ------------------------------------------------------------------------- it('Should support TUnsafe partial properties with no Kind', () => { const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) - Assert.deepEqual(T.required, undefined) + Assert.isEqual(T.required, undefined) }) it('Should support TUnsafe partial properties with unregistered Kind', () => { const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) - Assert.deepEqual(T.required, undefined) + Assert.isEqual(T.required, undefined) }) it('Should support TUnsafe partial properties with registered Kind', () => { const U = TypeSystem.Type('CustomPartialType', () => true) const T = Type.Partial(Type.Object({ x: U() })) - Assert.deepEqual(T.required, undefined) + Assert.isEqual(T.required, undefined) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 2ca44cc15..43a22ce80 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -14,11 +14,11 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.deepEqual(T.required, ['x']) + Assert.isEqual(T.required, ['x']) }) it('Should support TUnsafe omit properties with unregistered Kind', () => { const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownPickType' }), y: Type.Number() }), ['x']) - Assert.deepEqual(T.required, ['x']) + Assert.isEqual(T.required, ['x']) }) // ------------------------------------------------------------------------- // Standard Tests @@ -31,8 +31,8 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.deepEqual(T.required, ['x']) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(T.required, ['x']) }) it('Should Pick 2', () => { const T = Type.Pick( @@ -42,8 +42,8 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.deepEqual(T.required, undefined) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(T.required, undefined) }) it('Should Pick 3', () => { const L = Type.Literal('x') @@ -54,16 +54,16 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.deepEqual(T.required, ['x']) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(T.required, ['x']) }) it('Should Pick 4', () => { const L = Type.Literal('x') const T = Type.Pick(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.deepEqual(TypeGuard.TNumber(T.allOf[0].properties.x), true) + Assert.isEqual(TypeGuard.TNumber(T.allOf[0].properties.x), true) // @ts-ignore - Assert.deepEqual(T.allOf[1].properties.y, undefined) + Assert.isEqual(T.allOf[1].properties.y, undefined) }) it('Should Pick 5', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) @@ -74,9 +74,9 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.deepEqual(T.required, ['x', 'y']) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(T.required, ['x', 'y']) }) it('Should Pick 6', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) @@ -87,9 +87,9 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.deepEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.deepEqual(T.required, ['x', 'y']) + Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.isEqual(T.required, ['x', 'y']) }) it('Should Pick 7', () => { const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) @@ -101,8 +101,8 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.deepEqual(TypeGuard.TNumber(T.properties.ab), true) - Assert.deepEqual(TypeGuard.TNumber(T.properties.ac), true) - Assert.deepEqual(T.required, ['ab', 'ac']) + Assert.isEqual(TypeGuard.TNumber(T.properties.ab), true) + Assert.isEqual(TypeGuard.TNumber(T.properties.ac), true) + Assert.isEqual(T.required, ['ab', 'ac']) }) }) diff --git a/test/runtime/type/guard/promise.ts b/test/runtime/type/guard/promise.ts index eeedaf2f0..96e49b0bd 100644 --- a/test/runtime/type/guard/promise.ts +++ b/test/runtime/type/guard/promise.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('type/guard/TPromise', () => { it('Should guard for TPromise', () => { const R = TypeGuard.TPromise(Type.Promise(Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TPromise', () => { const R = TypeGuard.TPromise(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TPromise with invalid $id', () => { // @ts-ignore const R = TypeGuard.TPromise(Type.Promise(Type.Number(), { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should guard for TPromise with nested TObject', () => { @@ -28,7 +28,7 @@ describe('type/guard/TPromise', () => { }), ), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TPromise with nested TObject', () => { @@ -40,6 +40,6 @@ describe('type/guard/TPromise', () => { }), ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index f43a70a1a..350f9b3fc 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -8,16 +8,16 @@ describe('type/guard/TRecord', () => { // ------------------------------------------------------------- it('Should guard overload 1', () => { const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TObject(T), true) - Assert.equal(TypeGuard.TString(T.properties.A), true) - Assert.equal(TypeGuard.TString(T.properties.B), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TObject(T), true) + Assert.isEqual(TypeGuard.TString(T.properties.A), true) + Assert.isEqual(TypeGuard.TString(T.properties.B), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 2', () => { const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal - Assert.equal(TypeGuard.TObject(T), true) - Assert.equal(TypeGuard.TString(T.properties.A), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TObject(T), true) + Assert.isEqual(TypeGuard.TString(T.properties.A), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 3', () => { // @ts-ignore @@ -25,49 +25,49 @@ describe('type/guard/TRecord', () => { }) it('Should guard overload 4', () => { const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TObject(T), true) - Assert.equal(TypeGuard.TString(T.properties.A), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TObject(T), true) + Assert.isEqual(TypeGuard.TString(T.properties.A), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 5', () => { const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TObject(T), true) - Assert.equal(TypeGuard.TString(T.properties.helloA), true) - Assert.equal(TypeGuard.TString(T.properties.helloB), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TObject(T), true) + Assert.isEqual(TypeGuard.TString(T.properties.helloA), true) + Assert.isEqual(TypeGuard.TString(T.properties.helloB), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 6', () => { const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TRecord(T), true) - Assert.equal(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TRecord(T), true) + Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 7', () => { const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TRecord(T), true) - Assert.equal(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TRecord(T), true) + Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 8', () => { const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TRecord(T), true) - Assert.equal(TypeGuard.TString(T.patternProperties[PatternStringExact]), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TRecord(T), true) + Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternStringExact]), true) + Assert.isEqual(T.extra, 1) }) it('Should guard overload 9', () => { const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.equal(TypeGuard.TRecord(T), true) - Assert.equal(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`]), true) - Assert.equal(T.extra, 1) + Assert.isEqual(TypeGuard.TRecord(T), true) + Assert.isEqual(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`]), true) + Assert.isEqual(T.extra, 1) }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- it('Should guard for TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TRecord with TObject value', () => { const R = TypeGuard.TRecord( @@ -79,16 +79,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TRecord', () => { const R = TypeGuard.TRecord(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TRecord with TObject value with invalid Property', () => { const R = TypeGuard.TRecord( @@ -100,16 +100,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Transform: Should should transform to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Transform: Should should transform to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.equal(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/guard/ref.ts b/test/runtime/type/guard/ref.ts index e76680cf7..53b381954 100644 --- a/test/runtime/type/guard/ref.ts +++ b/test/runtime/type/guard/ref.ts @@ -6,12 +6,12 @@ describe('type/guard/TRef', () => { it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = TypeGuard.TRef(Type.Ref(T)) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TRef', () => { const R = TypeGuard.TRef(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TRef with invalid $ref', () => { @@ -20,6 +20,6 @@ describe('type/guard/TRef', () => { // @ts-ignore S.$ref = 1 const R = TypeGuard.TRef(S) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/rest.ts b/test/runtime/type/guard/rest.ts new file mode 100644 index 000000000..1e9e831ee --- /dev/null +++ b/test/runtime/type/guard/rest.ts @@ -0,0 +1,22 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TRest', () => { + it('Should guard Rest 1', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const R = Type.Rest(T) + Assert.isTrue(TypeGuard.TString(R[0])) + Assert.isTrue(TypeGuard.TNumber(R[1])) + }) + it('Should guard Rest 2', () => { + const T = Type.Tuple([]) + const R = Type.Rest(T) + Assert.isEqual(R.length, 0) + }) + it('Should guard Rest 3', () => { + const T = Type.String() + const R = Type.Rest(T) + Assert.isTrue(TypeGuard.TString(R[0])) + }) +}) diff --git a/test/runtime/type/guard/string.ts b/test/runtime/type/guard/string.ts index 24b33dd1e..308da7e62 100644 --- a/test/runtime/type/guard/string.ts +++ b/test/runtime/type/guard/string.ts @@ -5,35 +5,35 @@ import { Assert } from '../../assert/index' describe('type/guard/TString', () => { it('Should guard for TString', () => { const R = TypeGuard.TString(Type.String()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TString', () => { const R = TypeGuard.TString(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TString with invalid $id', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TString with invalid minLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ minLength: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TString with invalid maxLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ maxLength: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TString with invalid pattern', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ pattern: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/symbol.ts b/test/runtime/type/guard/symbol.ts index 7683f099e..a22a53fd7 100644 --- a/test/runtime/type/guard/symbol.ts +++ b/test/runtime/type/guard/symbol.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TSymbol', () => { it('Should guard for TSymbol', () => { const R = TypeGuard.TSymbol(Type.Symbol()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TSymbol', () => { const R = TypeGuard.TSymbol(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TSymbol with invalid $id', () => { // @ts-ignore const R = TypeGuard.TSymbol(Type.Symbol({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/template-literal.ts b/test/runtime/type/guard/template-literal.ts index 4d988829d..79112c294 100644 --- a/test/runtime/type/guard/template-literal.ts +++ b/test/runtime/type/guard/template-literal.ts @@ -5,43 +5,43 @@ import { Assert } from '../../assert/index' describe('type/guard/TTemplateLiteral', () => { it('Should guard for empty TemplateLiteral', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TSchema', () => { const R = TypeGuard.TSchema(Type.TemplateLiteral([])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TemplateLiteral (TTemplateLiteral)', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TemplateLiteral (TLiteral)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TemplateLiteral (TString)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.String()])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TemplateLiteral (TNumber)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Number()])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard for TemplateLiteral (TBoolean)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for missing ^ expression prefix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(1) - Assert.equal(TypeGuard.TTemplateLiteral(T), false) + Assert.isEqual(TypeGuard.TTemplateLiteral(T), false) }) it('Should not guard for missing $ expression postfix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(0, T.pattern.length - 1) - Assert.equal(TypeGuard.TTemplateLiteral(T), false) + Assert.isEqual(TypeGuard.TTemplateLiteral(T), false) }) }) diff --git a/test/runtime/type/guard/this.ts b/test/runtime/type/guard/this.ts index 2da48ed13..1668c93ad 100644 --- a/test/runtime/type/guard/this.ts +++ b/test/runtime/type/guard/this.ts @@ -6,7 +6,7 @@ describe('type/guard/TThis', () => { it('Should guard for TThis', () => { Type.Recursive((This) => { const R = TypeGuard.TThis(This) - Assert.equal(R, true) + Assert.isEqual(R, true) return Type.Object({ nodes: Type.Array(This) }) }) }) @@ -15,7 +15,7 @@ describe('type/guard/TThis', () => { // @ts-ignore This.$ref = 1 const R = TypeGuard.TThis(This) - Assert.equal(R, false) + Assert.isEqual(R, false) return Type.Object({ nodes: Type.Array(This) }) }) }) diff --git a/test/runtime/type/guard/tuple.ts b/test/runtime/type/guard/tuple.ts index 8e549aa57..d438753b6 100644 --- a/test/runtime/type/guard/tuple.ts +++ b/test/runtime/type/guard/tuple.ts @@ -5,19 +5,19 @@ import { Assert } from '../../assert/index' describe('type/guard/TTuple', () => { it('Should guard for TTuple', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()])) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TTuple', () => { const R = TypeGuard.TTuple(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TTuple with invalid $id', () => { // @ts-ignore const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TTuple with invalid Items', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), {} as any])) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/uint8array.ts b/test/runtime/type/guard/uint8array.ts index cd4c741e7..c2657f6af 100644 --- a/test/runtime/type/guard/uint8array.ts +++ b/test/runtime/type/guard/uint8array.ts @@ -5,29 +5,29 @@ import { Assert } from '../../assert/index' describe('type/guard/TUint8Array', () => { it('Should guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(Type.Uint8Array()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUint8Array with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUint8Array with invalid minByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ minByteLength: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUint8Array with invalid maxByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ maxByteLength: '1' })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/undefined.ts b/test/runtime/type/guard/undefined.ts index cc8bbeea3..7f6d4a593 100644 --- a/test/runtime/type/guard/undefined.ts +++ b/test/runtime/type/guard/undefined.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TUndefined', () => { it('Should guard for TUndefined', () => { const R = TypeGuard.TUndefined(Type.Undefined()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TUndefined', () => { const R = TypeGuard.TUndefined(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUndefined with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUndefined(Type.Undefined({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/union.ts b/test/runtime/type/guard/union.ts index 16fc75d41..b28b0052c 100644 --- a/test/runtime/type/guard/union.ts +++ b/test/runtime/type/guard/union.ts @@ -14,11 +14,11 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TUnion', () => { const R = TypeGuard.TUnion(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should guard for TUnion with invalid $id', () => { const R = TypeGuard.TUnion( @@ -38,7 +38,7 @@ describe('type/guard/TUnion', () => { }, ), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUnion with invalid variant', () => { const R = TypeGuard.TUnion( @@ -49,7 +49,7 @@ describe('type/guard/TUnion', () => { {} as any, ]), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUnion with invalid object variant', () => { const R = TypeGuard.TUnion( @@ -62,25 +62,25 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Transform: Should transform to never for zero length union', () => { const T = Type.Union([]) const R = TypeGuard.TNever(T) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Transform: Should unwrap union type for array of length === 1', () => { const T = Type.Union([Type.String()]) const R = TypeGuard.TString(T) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Transform: Should retain union if array length > 1', () => { const T = Type.Union([Type.String(), Type.Number()]) const R1 = TypeGuard.TUnion(T) const R2 = TypeGuard.TString(T.anyOf[0]) const R3 = TypeGuard.TNumber(T.anyOf[1]) - Assert.equal(R1, true) - Assert.equal(R2, true) - Assert.equal(R3, true) + Assert.isEqual(R1, true) + Assert.isEqual(R2, true) + Assert.isEqual(R3, true) }) }) diff --git a/test/runtime/type/guard/unknown.ts b/test/runtime/type/guard/unknown.ts index 4d3e5980c..2c2dce22e 100644 --- a/test/runtime/type/guard/unknown.ts +++ b/test/runtime/type/guard/unknown.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TUnknown', () => { it('Should guard for TUnknown', () => { const R = TypeGuard.TUnknown(Type.Unknown()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TUnknown', () => { const R = TypeGuard.TUnknown(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TUnknown with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUnknown(Type.Unknown({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/unsafe.ts b/test/runtime/type/guard/unsafe.ts index 8cb296441..b54e1f16b 100644 --- a/test/runtime/type/guard/unsafe.ts +++ b/test/runtime/type/guard/unsafe.ts @@ -6,27 +6,27 @@ describe('type/guard/TUnsafe', () => { it('Should guard raw TUnsafe', () => { const T = Type.Unsafe({ x: 1 }) const R = TypeGuard.TUnsafe(T) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard raw TUnsafe as TSchema', () => { const T = Type.Unsafe({ x: 1 }) const R = TypeGuard.TSchema(T) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should guard override TUnsafe as TSchema when registered', () => { TypeRegistry.Set('type/guard/TUnsafe/Type1', () => true) const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type1' }) const R = TypeGuard.TSchema(T) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard TUnsafe with unregistered kind', () => { const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type2' }) const R = TypeGuard.TUnsafe(T) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TString', () => { const T = Type.String() const R = TypeGuard.TUnsafe(T) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/guard/void.ts b/test/runtime/type/guard/void.ts index 839719a2f..976639621 100644 --- a/test/runtime/type/guard/void.ts +++ b/test/runtime/type/guard/void.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TVoid', () => { it('Should guard for TVoid', () => { const R = TypeGuard.TVoid(Type.Void()) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('Should not guard for TVoid', () => { const R = TypeGuard.TVoid(null) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('Should not guard for TVoid with invalid $id', () => { // @ts-ignore const R = TypeGuard.TVoid(Type.Void({ $id: 1 })) - Assert.equal(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/normalize/exclude.ts b/test/runtime/type/normalize/exclude.ts index fd0234242..16a91ede6 100644 --- a/test/runtime/type/normalize/exclude.ts +++ b/test/runtime/type/normalize/exclude.ts @@ -6,16 +6,16 @@ describe('type/normal/Exclude', () => { it('Normalize 1', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) const R = TypeGuard.TUnion(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 2', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.String()) const R = TypeGuard.TNumber(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 3', () => { const T = Type.Exclude(Type.Union([Type.String()]), Type.String()) const R = TypeGuard.TNever(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/normalize/extract.ts b/test/runtime/type/normalize/extract.ts index bb3a3d09d..f4b87a649 100644 --- a/test/runtime/type/normalize/extract.ts +++ b/test/runtime/type/normalize/extract.ts @@ -6,26 +6,26 @@ describe('type/normal/Extract', () => { it('Normalize 1', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Number()])) const R = TypeGuard.TUnion(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 2', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) const R = TypeGuard.TString(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 3', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.String()) const R = TypeGuard.TString(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 4', () => { const T = Type.Extract(Type.Union([Type.String()]), Type.String()) const R = TypeGuard.TString(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 5', () => { const T = Type.Extract(Type.Union([]), Type.String()) const R = TypeGuard.TNever(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/normalize/intersect.ts b/test/runtime/type/normalize/intersect.ts index 95accfd2d..3b712b5ea 100644 --- a/test/runtime/type/normalize/intersect.ts +++ b/test/runtime/type/normalize/intersect.ts @@ -6,16 +6,16 @@ describe('type/normal/Intersect', () => { it('Normalize 1', () => { const T = Type.Intersect([Type.Number(), Type.String()]) const R = TypeGuard.TIntersect(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 2', () => { const T = Type.Intersect([Type.Number()]) const R = TypeGuard.TNumber(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 3', () => { const T = Type.Intersect([]) const R = TypeGuard.TNever(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/normalize/record.ts b/test/runtime/type/normalize/record.ts index 53337902f..99b03383a 100644 --- a/test/runtime/type/normalize/record.ts +++ b/test/runtime/type/normalize/record.ts @@ -7,6 +7,6 @@ describe('type/normal/Record', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Record(K, Type.String()) const R = TypeGuard.TObject(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/normalize/union.ts b/test/runtime/type/normalize/union.ts index 44e5a7ece..aff9483fc 100644 --- a/test/runtime/type/normalize/union.ts +++ b/test/runtime/type/normalize/union.ts @@ -6,16 +6,16 @@ describe('type/normal/Union', () => { it('Normalize 1', () => { const T = Type.Union([Type.Number(), Type.String()]) const R = TypeGuard.TUnion(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 2', () => { const T = Type.Union([Type.Number()]) const R = TypeGuard.TNumber(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Normalize 3', () => { const T = Type.Union([]) const R = TypeGuard.TNever(T) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/type/registry/format.ts b/test/runtime/type/registry/format.ts index 494160aec..df2568182 100644 --- a/test/runtime/type/registry/format.ts +++ b/test/runtime/type/registry/format.ts @@ -8,15 +8,15 @@ describe('type/FormatRegistry', () => { it('Should get format', () => { FormatRegistry.Set('test#format2', () => true) const format = FormatRegistry.Get('test#format2') - Assert.equal(typeof format, 'function') + Assert.isEqual(typeof format, 'function') }) it('Should return true if exists', () => { FormatRegistry.Set('test#format3', () => true) - Assert.equal(FormatRegistry.Has('test#format3'), true) + Assert.isEqual(FormatRegistry.Has('test#format3'), true) }) it('Should clear formats', () => { FormatRegistry.Set('test#format4', () => true) FormatRegistry.Clear() - Assert.equal(FormatRegistry.Has('test#format4'), false) + Assert.isEqual(FormatRegistry.Has('test#format4'), false) }) }) diff --git a/test/runtime/type/registry/type.ts b/test/runtime/type/registry/type.ts index 5289d3d6d..0ad9a6613 100644 --- a/test/runtime/type/registry/type.ts +++ b/test/runtime/type/registry/type.ts @@ -8,15 +8,15 @@ describe('type/TypeRegistry', () => { it('Should get type', () => { TypeRegistry.Set('test#type2', () => true) const format = TypeRegistry.Get('test#type2') - Assert.equal(typeof format, 'function') + Assert.isEqual(typeof format, 'function') }) it('Should return true if exists', () => { TypeRegistry.Set('test#type3', () => true) - Assert.equal(TypeRegistry.Has('test#type3'), true) + Assert.isEqual(TypeRegistry.Has('test#type3'), true) }) it('Should clear types', () => { TypeRegistry.Set('test#type4', () => true) TypeRegistry.Clear() - Assert.equal(TypeRegistry.Has('test#type4'), false) + Assert.isEqual(TypeRegistry.Has('test#type4'), false) }) }) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts index c843229ed..0941a39c2 100644 --- a/test/runtime/type/template/finite.ts +++ b/test/runtime/type/template/finite.ts @@ -8,27 +8,27 @@ describe('type/TemplateLiteralFinite', () => { it('Finite 1', () => { const E = TemplateLiteralParser.Parse(`A`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Finite 2', () => { const E = TemplateLiteralParser.Parse(`A|B`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Finite 3', () => { const E = TemplateLiteralParser.Parse(`A(B|C)`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Finite 4', () => { const E = TemplateLiteralParser.Parse(`${PatternBoolean}`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Finite 5', () => { const E = TemplateLiteralParser.Parse(`\\.\\*`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) // --------------------------------------------------------------- // Infinite @@ -36,36 +36,36 @@ describe('type/TemplateLiteralFinite', () => { it('Infinite 1', () => { const E = TemplateLiteralParser.Parse(`${PatternString}`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 2', () => { const E = TemplateLiteralParser.Parse(`${PatternNumber}`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 3', () => { const E = TemplateLiteralParser.Parse(`A|${PatternString}`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 4', () => { const E = TemplateLiteralParser.Parse(`A|${PatternNumber}`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 5', () => { const E = TemplateLiteralParser.Parse(`A(${PatternString})`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 6', () => { const E = TemplateLiteralParser.Parse(`A(${PatternNumber})`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Infinite 7', () => { const E = TemplateLiteralParser.Parse(`${PatternString}_foo`) const R = TemplateLiteralFinite.Check(E) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts index df00189db..24ee76f25 100644 --- a/test/runtime/type/template/generate.ts +++ b/test/runtime/type/template/generate.ts @@ -8,12 +8,12 @@ describe('type/TemplateLiteralGenerator', () => { it('Exact 1', () => { const E = TemplateLiteralParser.Parse('^$') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['^$']) + Assert.isEqual(R, ['^$']) }) it('Exact 2', () => { const E = TemplateLiteralParser.Parse('^A$') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['^A$']) + Assert.isEqual(R, ['^A$']) }) // --------------------------------------------------------------- // Patterns @@ -21,17 +21,17 @@ describe('type/TemplateLiteralGenerator', () => { it('Pattern 1', () => { const E = TemplateLiteralParser.Parse('(true|false)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['true', 'false']) + Assert.isEqual(R, ['true', 'false']) }) it('Pattern 2', () => { const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['0', '[1-9][0-9]*']) + Assert.isEqual(R, ['0', '[1-9][0-9]*']) }) it('Pattern 3', () => { const E = TemplateLiteralParser.Parse('.*') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['.*']) + Assert.isEqual(R, ['.*']) }) // --------------------------------------------------------------- // Expression @@ -39,161 +39,161 @@ describe('type/TemplateLiteralGenerator', () => { it('Expression 1', () => { const E = TemplateLiteralParser.Parse(')') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, [')']) + Assert.isEqual(R, [')']) }) it('Expression 2', () => { const E = TemplateLiteralParser.Parse('\\)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['\\)']) + Assert.isEqual(R, ['\\)']) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['\\(']) + Assert.isEqual(R, ['\\(']) }) it('Expression 4', () => { const E = TemplateLiteralParser.Parse('') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['']) + Assert.isEqual(R, ['']) }) it('Expression 5', () => { const E = TemplateLiteralParser.Parse('\\') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['\\']) + Assert.isEqual(R, ['\\']) }) it('Expression 6', () => { const E = TemplateLiteralParser.Parse('()') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['']) + Assert.isEqual(R, ['']) }) it('Expression 7', () => { const E = TemplateLiteralParser.Parse('(a)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['a']) + Assert.isEqual(R, ['a']) }) it('Expression 8', () => { const E = TemplateLiteralParser.Parse('()))') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['))']) + Assert.isEqual(R, ['))']) }) it('Expression 9', () => { const E = TemplateLiteralParser.Parse('())') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, [')']) + Assert.isEqual(R, [')']) }) it('Expression 10', () => { const E = TemplateLiteralParser.Parse('A|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', 'B']) + Assert.isEqual(R, ['A', 'B']) }) it('Expression 11', () => { const E = TemplateLiteralParser.Parse('A|(B)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', 'B']) + Assert.isEqual(R, ['A', 'B']) }) it('Expression 12', () => { const E = TemplateLiteralParser.Parse('A(B)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['AB']) + Assert.isEqual(R, ['AB']) }) it('Expression 13', () => { const E = TemplateLiteralParser.Parse('(A)B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['AB']) + Assert.isEqual(R, ['AB']) }) it('Expression 14', () => { const E = TemplateLiteralParser.Parse('(A)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', 'B']) + Assert.isEqual(R, ['A', 'B']) }) it('Expression 15', () => { const E = TemplateLiteralParser.Parse('|') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['']) + Assert.isEqual(R, ['']) }) it('Expression 16', () => { const E = TemplateLiteralParser.Parse('||') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['']) + Assert.isEqual(R, ['']) }) it('Expression 17', () => { const E = TemplateLiteralParser.Parse('||A') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A']) + Assert.isEqual(R, ['A']) }) it('Expression 18', () => { const E = TemplateLiteralParser.Parse('A||') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A']) + Assert.isEqual(R, ['A']) }) it('Expression 19', () => { const E = TemplateLiteralParser.Parse('A||B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', 'B']) + Assert.isEqual(R, ['A', 'B']) }) it('Expression 20', () => { const E = TemplateLiteralParser.Parse('A|()|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', '', 'B']) + Assert.isEqual(R, ['A', '', 'B']) }) it('Expression 21', () => { const E = TemplateLiteralParser.Parse('A|(|)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', '', 'B']) + Assert.isEqual(R, ['A', '', 'B']) }) it('Expression 22', () => { const E = TemplateLiteralParser.Parse('A|(||)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', '', 'B']) + Assert.isEqual(R, ['A', '', 'B']) }) it('Expression 23', () => { const E = TemplateLiteralParser.Parse('|A(||)B|') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['AB']) + Assert.isEqual(R, ['AB']) }) it('Expression 24', () => { const E = TemplateLiteralParser.Parse('A(B)(C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['ABC']) + Assert.isEqual(R, ['ABC']) }) it('Expression 25', () => { const E = TemplateLiteralParser.Parse('A(B)|(C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['AB', 'C']) + Assert.isEqual(R, ['AB', 'C']) }) it('Expression 26', () => { const E = TemplateLiteralParser.Parse('A(B|C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['AB', 'AC']) + Assert.isEqual(R, ['AB', 'AC']) }) it('Expression 27', () => { const E = TemplateLiteralParser.Parse('A|(B|C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['A', 'B', 'C']) + Assert.isEqual(R, ['A', 'B', 'C']) }) it('Expression 28', () => { const E = TemplateLiteralParser.Parse('((A)B)C') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['ABC']) + Assert.isEqual(R, ['ABC']) }) it('Expression 29', () => { const E = TemplateLiteralParser.Parse('(0|1)(0|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['00', '01', '10', '11']) + Assert.isEqual(R, ['00', '01', '10', '11']) }) it('Expression 30', () => { const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['0', '1', '0', '1']) + Assert.isEqual(R, ['0', '1', '0', '1']) }) it('Expression 31', () => { const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['0', '1', '0', '1']) + Assert.isEqual(R, ['0', '1', '0', '1']) }) it('Expression 32', () => { const E = TemplateLiteralParser.Parse('(0(1|0)1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.deepEqual(R, ['011', '001']) + Assert.isEqual(R, ['011', '001']) }) }) diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts index 9cf2607f1..5c9654b78 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template/parser.ts @@ -16,14 +16,14 @@ describe('type/TemplateLiteralParser', () => { // --------------------------------------------------------------- it('Exact 1', () => { const E = TemplateLiteralParser.Parse('^$') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '^$', }) }) it('Exact 2', () => { const E = TemplateLiteralParser.Parse('^A$') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '^A$', }) @@ -33,7 +33,7 @@ describe('type/TemplateLiteralParser', () => { // --------------------------------------------------------------- it('Pattern 1', () => { const E = TemplateLiteralParser.Parse('(true|false)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -49,7 +49,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Pattern 2', () => { const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -65,7 +65,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Pattern 3', () => { const E = TemplateLiteralParser.Parse('.*') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '.*', }) @@ -75,56 +75,56 @@ describe('type/TemplateLiteralParser', () => { // --------------------------------------------------------------- it('Expression 1', () => { const E = TemplateLiteralParser.Parse(')') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: ')', }) }) it('Expression 2', () => { const E = TemplateLiteralParser.Parse('\\)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '\\)', }) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '\\(', }) }) it('Expression 4', () => { const E = TemplateLiteralParser.Parse('') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '', }) }) it('Expression 5', () => { const E = TemplateLiteralParser.Parse('\\') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '\\', }) }) it('Expression 6', () => { const E = TemplateLiteralParser.Parse('()') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '', }) }) it('Expression 7', () => { const E = TemplateLiteralParser.Parse('(a)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: 'a', }) }) it('Expression 8', () => { const E = TemplateLiteralParser.Parse('()))') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -140,7 +140,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 9', () => { const E = TemplateLiteralParser.Parse('())') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -156,7 +156,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 10', () => { const E = TemplateLiteralParser.Parse('A|B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -172,7 +172,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 11', () => { const E = TemplateLiteralParser.Parse('A|(B)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -188,7 +188,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 12', () => { const E = TemplateLiteralParser.Parse('A(B)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -204,7 +204,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 13', () => { const E = TemplateLiteralParser.Parse('(A)B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -220,7 +220,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 14', () => { const E = TemplateLiteralParser.Parse('(A)|B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -236,35 +236,35 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 15', () => { const E = TemplateLiteralParser.Parse('|') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '', }) }) it('Expression 16', () => { const E = TemplateLiteralParser.Parse('||') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: '', }) }) it('Expression 17', () => { const E = TemplateLiteralParser.Parse('||A') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: 'A', }) }) it('Expression 18', () => { const E = TemplateLiteralParser.Parse('A||') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'const', const: 'A', }) }) it('Expression 19', () => { const E = TemplateLiteralParser.Parse('A||B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -280,7 +280,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 20', () => { const E = TemplateLiteralParser.Parse('A|()|B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -300,7 +300,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 21', () => { const E = TemplateLiteralParser.Parse('A|(|)|B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -320,7 +320,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 22', () => { const E = TemplateLiteralParser.Parse('A|(||)|B') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -340,7 +340,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 23', () => { const E = TemplateLiteralParser.Parse('|A(||)B|') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -360,7 +360,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 24', () => { const E = TemplateLiteralParser.Parse('A(B)(C)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -380,7 +380,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 25', () => { const E = TemplateLiteralParser.Parse('A(B)|(C)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -405,7 +405,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 26', () => { const E = TemplateLiteralParser.Parse('A(B|C)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -430,7 +430,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 27', () => { const E = TemplateLiteralParser.Parse('A|(B|C)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -455,7 +455,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 28', () => { const E = TemplateLiteralParser.Parse('((A)B)C') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -480,7 +480,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 29', () => { const E = TemplateLiteralParser.Parse('(0|1)(0|1)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { @@ -514,7 +514,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 30', () => { const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -548,7 +548,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 31', () => { const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'or', expr: [ { @@ -577,7 +577,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 32', () => { const E = TemplateLiteralParser.Parse('(0(1|0)1)') - Assert.deepEqual(E, { + Assert.isEqual(E, { type: 'and', expr: [ { diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template/pattern.ts index d43148951..b87143069 100644 --- a/test/runtime/type/template/pattern.ts +++ b/test/runtime/type/template/pattern.ts @@ -4,26 +4,26 @@ import { Assert } from '../../assert/index' describe('type/TemplateLiteralPattern', () => { const Equal = (template: TTemplateLiteral, expect: string) => { const pattern = template.pattern.slice(1, template.pattern.length - 1) - Assert.equal(pattern, expect) + Assert.isEqual(pattern, expect) } // --------------------------------------------------------------- // Escape // --------------------------------------------------------------- it('Escape 1', () => { const T = Type.TemplateLiteral([Type.Literal('.*')]) - Assert.equal(T.pattern, '^\\.\\*$') + Assert.isEqual(T.pattern, '^\\.\\*$') }) it('Escape 2', () => { const T = Type.TemplateLiteral([Type.Literal('(')]) - Assert.equal(T.pattern, '^\\($') + Assert.isEqual(T.pattern, '^\\($') }) it('Escape 3', () => { const T = Type.TemplateLiteral([Type.Literal(')')]) - Assert.equal(T.pattern, '^\\)$') + Assert.isEqual(T.pattern, '^\\)$') }) it('Escape 4', () => { const T = Type.TemplateLiteral([Type.Literal('|')]) - Assert.equal(T.pattern, '^\\|$') + Assert.isEqual(T.pattern, '^\\|$') }) // --------------------------------------------------------------- // Pattern diff --git a/test/runtime/value/cast/any.ts b/test/runtime/value/cast/any.ts index a294da43f..b5beef61a 100644 --- a/test/runtime/value/cast/any.ts +++ b/test/runtime/value/cast/any.ts @@ -7,46 +7,46 @@ describe('value/cast/Any', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result.getTime(100), 100) + Assert.isEqual(result.getTime(100), 100) }) it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { a: 1, b: 2 }) + Assert.isEqual(result, { a: 1, b: 2 }) }) }) diff --git a/test/runtime/value/cast/array.ts b/test/runtime/value/cast/array.ts index 820c3f699..904c51811 100644 --- a/test/runtime/value/cast/array.ts +++ b/test/runtime/value/cast/array.ts @@ -8,82 +8,82 @@ describe('value/cast/Array', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, [1]) + Assert.isEqual(result, [1]) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = [6, 7, 8] const result = Value.Cast(T, value) - Assert.deepEqual(result, [6, 7, 8]) + Assert.isEqual(result, [6, 7, 8]) }) it('Should preserve with invalid element set to default', () => { const value = [6, 7, 8, 'hello', 9] const result = Value.Cast(T, value) - Assert.deepEqual(result, [6, 7, 8, 0, 9]) + Assert.isEqual(result, [6, 7, 8, 0, 9]) }) // ----------------------------------------------------------------- // Constraints: Ranges // ----------------------------------------------------------------- it('Should cast array and truncate to maxItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3 }), [0, 1, 2, 4, 5, 6]) - Assert.deepEqual(result, [0, 1, 2]) + Assert.isEqual(result, [0, 1, 2]) }) it('Should cast arrays and append array to minItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { minItems: 6 }), [0, 1, 2]) - Assert.deepEqual(result, [0, 1, 2, 0, 0, 0]) + Assert.isEqual(result, [0, 1, 2, 0, 0, 0]) }) it('Should cast array and truncate to maxItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3, default: [0, 1, 2, 4, 5, 6] }), null) - Assert.deepEqual(result, [0, 1, 2]) + Assert.isEqual(result, [0, 1, 2]) }) it('Should cast arrays and append array to minItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number({ default: 1 }), { minItems: 6, default: [0, 1, 2] }), null) - Assert.deepEqual(result, [0, 1, 2, 1, 1, 1]) + Assert.isEqual(result, [0, 1, 2, 1, 1, 1]) }) // ----------------------------------------------------------------- // Constraints: Unique // ----------------------------------------------------------------- it('Should cast arrays with uniqueItems with unique default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true, default: [0, 1, 2] }), null) - Assert.deepEqual(result, [0, 1, 2]) + Assert.isEqual(result, [0, 1, 2]) }) it('Should cast arrays with uniqueItems with unique value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), [0, 1, 2]) - Assert.deepEqual(result, [0, 1, 2]) + Assert.isEqual(result, [0, 1, 2]) }) it('Should throw when casting arrays with uniqueItems and no value or default value', () => { Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), null)) @@ -101,18 +101,18 @@ describe('value/cast/Array', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const value = [1, 1, 2, 2] const result = Value.Cast(T, value) - Assert.deepEqual(result, [1, 2]) + Assert.isEqual(result, [1, 2]) }) it('Should should fill up with defaults to minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) const value = [1, 2] const result = Value.Cast(T, value) - Assert.deepEqual(result, [1, 2, 0]) + Assert.isEqual(result, [1, 2, 0]) }) it('Should should truncate to maxItems', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) const value = [1, 2, 3, 4] const result = Value.Cast(T, value) - Assert.deepEqual(result, [1, 2, 3]) + Assert.isEqual(result, [1, 2, 3]) }) }) diff --git a/test/runtime/value/cast/bigint.ts b/test/runtime/value/cast/bigint.ts index 8e69e3756..57589c93e 100644 --- a/test/runtime/value/cast/bigint.ts +++ b/test/runtime/value/cast/bigint.ts @@ -8,46 +8,46 @@ describe('value/cast/BigInt', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = BigInt(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, BigInt(100)) + Assert.isEqual(result, BigInt(100)) }) }) diff --git a/test/runtime/value/cast/boolean.ts b/test/runtime/value/cast/boolean.ts index a790df65b..6c14b5603 100644 --- a/test/runtime/value/cast/boolean.ts +++ b/test/runtime/value/cast/boolean.ts @@ -8,46 +8,46 @@ describe('value/cast/Boolean', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/cast/composite.ts b/test/runtime/value/cast/composite.ts index 781cf2448..8be304b8e 100644 --- a/test/runtime/value/cast/composite.ts +++ b/test/runtime/value/cast/composite.ts @@ -25,47 +25,47 @@ describe('value/cast/Composite', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast and preserve object', () => { const value = { x: 7, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 7, y: 8, z: 9, @@ -77,7 +77,7 @@ describe('value/cast/Composite', () => { it('Should upcast and preserve from incorrect properties', () => { const value = { x: {}, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 0, y: 8, z: 9, diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts index 2c8d49477..e29b21a7d 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/custom.ts @@ -14,37 +14,37 @@ describe('value/cast/Custom', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = { a: 'hello', b: 'world' } @@ -55,6 +55,6 @@ describe('value/cast/Custom', () => { }), value, ) - Assert.deepEqual(result, { a: 'hello', b: 'world' }) + Assert.isEqual(result, { a: 'hello', b: 'world' }) }) }) diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts index b094c8c79..ec681d908 100644 --- a/test/runtime/value/cast/date.ts +++ b/test/runtime/value/cast/date.ts @@ -8,31 +8,31 @@ describe('value/cast/Date', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result.getTime(), 100) + Assert.isEqual(result.getTime(), 100) }) }) diff --git a/test/runtime/value/cast/enum.ts b/test/runtime/value/cast/enum.ts index 86e6d2119..1b0d1d12b 100644 --- a/test/runtime/value/cast/enum.ts +++ b/test/runtime/value/cast/enum.ts @@ -12,51 +12,51 @@ describe('value/cast/Boolean', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 123 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from enum A', () => { const value = Foo.A const result = Value.Cast(T, value) - Assert.deepEqual(result, Foo.A) + Assert.isEqual(result, Foo.A) }) it('Should upcast from enum B', () => { const value = Foo.B const result = Value.Cast(T, value) - Assert.deepEqual(result, Foo.B) + Assert.isEqual(result, Foo.B) }) }) diff --git a/test/runtime/value/cast/integer.ts b/test/runtime/value/cast/integer.ts index 1bf6228c9..534913a83 100644 --- a/test/runtime/value/cast/integer.ts +++ b/test/runtime/value/cast/integer.ts @@ -8,36 +8,36 @@ describe('value/cast/Integer', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, 1) + Assert.isEqual(result, 1) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) }) diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index 8a681fddb..596ecb2da 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -10,7 +10,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number() }) ]) const V = Value.Cast(T, 1) - Assert.deepEqual(V, { x: 0, y: 0 }) + Assert.isEqual(V, { x: 0, y: 0 }) }) it('Should cast from an partial object and preserve', () => { // prettier-ignore @@ -19,7 +19,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number() }) ]) const V = Value.Cast(T, { x: 1 }) - Assert.deepEqual(V, { x: 1, y: 0 }) + Assert.isEqual(V, { x: 1, y: 0 }) }) it('Should cast and use default values', () => { // prettier-ignore @@ -28,7 +28,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number({ default: 42 }) }) ]) const V = Value.Cast(T, { x: 1 }) - Assert.deepEqual(V, { x: 1, y: 42 }) + Assert.isEqual(V, { x: 1, y: 42 }) }) it('Should throw with an illogical intersect', () => { // prettier-ignore @@ -53,7 +53,7 @@ describe('value/cast/Intersect', () => { Type.Object({ x: Type.Number({ default: 1000 }) }) ]) const V = Value.Cast(T, null) - Assert.deepEqual(V, { x: 1000 }) + Assert.isEqual(V, { x: 1000 }) }) it('Should use last intersected default for equivalent sub schemas (primitives)', () => { // prettier-ignore @@ -62,7 +62,7 @@ describe('value/cast/Intersect', () => { Type.Number({ default: 1000 }) ]) const V = Value.Cast(T, null) - Assert.deepEqual(V, 1000) + Assert.isEqual(V, 1000) }) it('Should preserve if default is specified', () => { // prettier-ignore @@ -71,6 +71,6 @@ describe('value/cast/Intersect', () => { Type.Number({ default: 1000 }) ]) const V = Value.Cast(T, 2000) - Assert.deepEqual(V, 2000) + Assert.isEqual(V, 2000) }) }) diff --git a/test/runtime/value/cast/keyof.ts b/test/runtime/value/cast/keyof.ts index 7ee9795fe..81cd828d3 100644 --- a/test/runtime/value/cast/keyof.ts +++ b/test/runtime/value/cast/keyof.ts @@ -14,46 +14,46 @@ describe('value/cast/KeyOf', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = 'y' const result = Value.Cast(T, value) - Assert.deepEqual(result, 'y') + Assert.isEqual(result, 'y') }) }) diff --git a/test/runtime/value/cast/literal.ts b/test/runtime/value/cast/literal.ts index 557abdf81..71ca7dff8 100644 --- a/test/runtime/value/cast/literal.ts +++ b/test/runtime/value/cast/literal.ts @@ -8,46 +8,46 @@ describe('value/cast/Literal', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) }) diff --git a/test/runtime/value/cast/not.ts b/test/runtime/value/cast/not.ts index ac783efcf..0fc2d1ed4 100644 --- a/test/runtime/value/cast/not.ts +++ b/test/runtime/value/cast/not.ts @@ -8,52 +8,52 @@ describe('value/cast/Not', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = 100 const result = Value.Cast(T, value) - Assert.deepEqual(result, 100) + Assert.isEqual(result, 100) }) it('Should not preserve when schema is illogical', () => { const T = Type.Not(Type.Number(), Type.Number()) const value = 100 const result = Value.Cast(T, value) - Assert.deepEqual(result, 0) + Assert.isEqual(result, 0) }) }) diff --git a/test/runtime/value/cast/null.ts b/test/runtime/value/cast/null.ts index 467068b5c..b4d019bd4 100644 --- a/test/runtime/value/cast/null.ts +++ b/test/runtime/value/cast/null.ts @@ -8,46 +8,46 @@ describe('value/cast/Null', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, null) + Assert.isEqual(result, null) }) }) diff --git a/test/runtime/value/cast/number.ts b/test/runtime/value/cast/number.ts index e3e43b537..66d999c91 100644 --- a/test/runtime/value/cast/number.ts +++ b/test/runtime/value/cast/number.ts @@ -8,41 +8,41 @@ describe('value/cast/Number', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, 1) + Assert.isEqual(result, 1) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = 123 const result = Value.Cast(T, value) - Assert.deepEqual(result, 123) + Assert.isEqual(result, 123) }) }) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts index 0c631b556..3fa460100 100644 --- a/test/runtime/value/cast/object.ts +++ b/test/runtime/value/cast/object.ts @@ -22,47 +22,47 @@ describe('value/cast/Object', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = { x: 7, y: 8, z: 9, a: 10, b: 11, c: 12 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 7, y: 8, z: 9, @@ -74,7 +74,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object', () => { const value = { x: 7, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 7, y: 8, z: 9, @@ -86,7 +86,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object with incorrect properties', () => { const value = { x: {}, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 0, y: 8, z: 9, @@ -98,7 +98,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object and omit unknown properties', () => { const value = { x: 7, y: 8, z: 9, unknown: 'foo' } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 7, y: 8, z: 9, @@ -127,7 +127,7 @@ describe('value/cast/Object', () => { z: true, }, ) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 1, y: 2, z: { a: 0, b: 0 }, @@ -153,7 +153,7 @@ describe('value/cast/Object', () => { z: { b: 1 }, }, ) - Assert.deepEqual(result, { + Assert.isEqual(result, { x: 1, y: 2, z: { a: 0, b: 1 }, diff --git a/test/runtime/value/cast/record.ts b/test/runtime/value/cast/record.ts index e62515ae2..1ef6828af 100644 --- a/test/runtime/value/cast/record.ts +++ b/test/runtime/value/cast/record.ts @@ -16,42 +16,42 @@ describe('value/cast/Record', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = { @@ -59,7 +59,7 @@ describe('value/cast/Record', () => { b: { x: 4, y: 5, z: 6 }, } const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should preserve and patch invalid records', () => { const value = { @@ -70,7 +70,7 @@ describe('value/cast/Record', () => { e: { x: 1, y: 2, w: 9000 }, } const result = Value.Cast(T, value) - Assert.deepEqual(result, { + Assert.isEqual(result, { a: { x: 1, y: 2, z: 3 }, b: { x: 4, y: 5, z: 0 }, c: { x: 0, y: 0, z: 0 }, diff --git a/test/runtime/value/cast/recursive.ts b/test/runtime/value/cast/recursive.ts index 78cc5c220..cf4316ba4 100644 --- a/test/runtime/value/cast/recursive.ts +++ b/test/runtime/value/cast/recursive.ts @@ -13,42 +13,42 @@ describe('value/cast/Recursive', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = { @@ -60,7 +60,7 @@ describe('value/cast/Recursive', () => { ], } const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from varying types', () => { const TypeA = Type.Recursive((This) => @@ -85,7 +85,7 @@ describe('value/cast/Recursive', () => { ], } const ValueB = Value.Cast(TypeB, ValueA) - // Assert.deepEqual(ValueB, { + // Assert.isEqual(ValueB, { // id: 'A', // name: 'test', // nodes: [ diff --git a/test/runtime/value/cast/regex.ts b/test/runtime/value/cast/regex.ts index ab34f4264..d23bef49c 100644 --- a/test/runtime/value/cast/regex.ts +++ b/test/runtime/value/cast/regex.ts @@ -8,46 +8,46 @@ describe('value/cast/RegEx', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, 'foo') + Assert.isEqual(result, 'foo') }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) }) diff --git a/test/runtime/value/cast/string.ts b/test/runtime/value/cast/string.ts index 0ed878b4d..ff8f5c11d 100644 --- a/test/runtime/value/cast/string.ts +++ b/test/runtime/value/cast/string.ts @@ -8,36 +8,36 @@ describe('value/cast/String', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) }) diff --git a/test/runtime/value/cast/symbol.ts b/test/runtime/value/cast/symbol.ts index 15af14c42..05fd88049 100644 --- a/test/runtime/value/cast/symbol.ts +++ b/test/runtime/value/cast/symbol.ts @@ -47,6 +47,6 @@ describe('value/cast/Symbol', () => { it('Should preserve', () => { const value = Symbol('hello') const result = Value.Cast(T, value) - Assert.deepEqual(result.description, value.description) + Assert.isEqual(result.description, value.description) }) }) diff --git a/test/runtime/value/cast/template-literal.ts b/test/runtime/value/cast/template-literal.ts index b2255bdfe..322928ba5 100644 --- a/test/runtime/value/cast/template-literal.ts +++ b/test/runtime/value/cast/template-literal.ts @@ -8,46 +8,46 @@ describe('value/cast/TemplateLiteral', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = 'helloworld' const result = Value.Cast(T, value) - Assert.deepEqual(result, 'helloworld') + Assert.isEqual(result, 'helloworld') }) }) diff --git a/test/runtime/value/cast/tuple.ts b/test/runtime/value/cast/tuple.ts index 8ba7835c5..cb805cf6e 100644 --- a/test/runtime/value/cast/tuple.ts +++ b/test/runtime/value/cast/tuple.ts @@ -8,66 +8,66 @@ describe('value/cast/Tuple', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, [1, '']) + Assert.isEqual(result, [1, '']) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = [42, 'world'] const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast with empty', () => { const value = [] as any[] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should append with less than tuple length', () => { const value = [42] const result = Value.Cast(T, value) - Assert.deepEqual(result, [42, '']) + Assert.isEqual(result, [42, '']) }) it('Should truncate with greater than tuple length', () => { const value = [42, '', true] const result = Value.Cast(T, value) - Assert.deepEqual(result, [42, '']) + Assert.isEqual(result, [42, '']) }) it('Should preserve and patch invalid element', () => { const value = [{}, 'hello'] const result = Value.Cast(T, value) - Assert.deepEqual(result, [0, 'hello']) + Assert.isEqual(result, [0, 'hello']) }) }) diff --git a/test/runtime/value/cast/uint8array.ts b/test/runtime/value/cast/uint8array.ts index 638eb9c57..4adf0eb44 100644 --- a/test/runtime/value/cast/uint8array.ts +++ b/test/runtime/value/cast/uint8array.ts @@ -8,46 +8,46 @@ describe('value/cast/Uint8Array', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = new Uint8Array([6, 7, 8, 9, 10]) const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) }) diff --git a/test/runtime/value/cast/undefined.ts b/test/runtime/value/cast/undefined.ts index d8a089ce6..9821ee947 100644 --- a/test/runtime/value/cast/undefined.ts +++ b/test/runtime/value/cast/undefined.ts @@ -8,46 +8,46 @@ describe('value/cast/Undefined', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preseve', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, undefined) + Assert.isEqual(result, undefined) }) }) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 5850866b8..4ca73b41e 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -32,87 +32,87 @@ describe('value/cast/Union', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve A', () => { const value = { type: 'A', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should preserve B', () => { const value = { type: 'B', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should infer through heuristics #1', () => { const value = { type: 'A', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + Assert.isEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) it('Should infer through heuristics #2', () => { const value = { type: 'B', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #3', () => { const value = { type: 'A', a: 'a', b: 'b', c: null } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + Assert.isEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) it('Should infer through heuristics #4', () => { const value = { type: 'B', x: 1, y: 2, z: {} } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #5', () => { const value = { type: 'B', x: 1, y: 2, z: null } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #6', () => { const value = { x: 1 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) + Assert.isEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) }) it('Should infer through heuristics #7', () => { const value = { a: null } // property existing should contribute const result = Value.Cast(T, value) - Assert.deepEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should cast with default value (create)', () => { const result = Value.Cast( @@ -125,7 +125,7 @@ describe('value/cast/Union', () => { value: 'D', }, ) - Assert.deepEqual(result, { + Assert.isEqual(result, { id: 42, value: 'C', }) @@ -141,7 +141,7 @@ describe('value/cast/Union', () => { value: 'B', }, ) - Assert.deepEqual(result, { + Assert.isEqual(result, { id: 42, value: 'B', }) diff --git a/test/runtime/value/cast/unknown.ts b/test/runtime/value/cast/unknown.ts index 806b37257..f6193fb7c 100644 --- a/test/runtime/value/cast/unknown.ts +++ b/test/runtime/value/cast/unknown.ts @@ -7,46 +7,46 @@ describe('value/cast/Unknown', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, value) + Assert.isEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result: any = Value.Cast(T, value) - Assert.deepEqual(result.getTime(), 100) + Assert.isEqual(result.getTime(), 100) }) it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) - Assert.deepEqual(result, { a: 1, b: 2 }) + Assert.isEqual(result, { a: 1, b: 2 }) }) }) diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts index 787c0bb85..6996f3bf0 100644 --- a/test/runtime/value/cast/void.ts +++ b/test/runtime/value/cast/void.ts @@ -8,46 +8,46 @@ describe('value/cast/Void', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.deepEqual(result, E) + Assert.isEqual(result, E) }) it('Should preserve', () => { const value = null const result = Value.Cast(T, value) - Assert.deepEqual(result, null) + Assert.isEqual(result, null) }) }) diff --git a/test/runtime/value/check/any.ts b/test/runtime/value/check/any.ts index a9fab67d4..1f46bb09a 100644 --- a/test/runtime/value/check/any.ts +++ b/test/runtime/value/check/any.ts @@ -7,41 +7,41 @@ describe('value/check/Any', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index 64f3f3806..c0ef023b8 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -7,46 +7,46 @@ describe('value/check/Array', () => { const T = Type.Array(Type.Number()) const value = [1, 2, 3] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail number array', () => { const T = Type.Array(Type.Number()) const value = ['a', 'b', 'c'] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass object array', () => { const T = Type.Array(Type.Object({ x: Type.Number() })) const value = [{ x: 1 }, { x: 1 }, { x: 1 }] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail object array', () => { const T = Type.Array(Type.Object({ x: Type.Number() })) const value = [{ x: 1 }, { x: 1 }, 1] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(Type.Array(Type.Any()), value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should validate array with unique primitive items', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const result = Value.Check(T, [0, 1, 2]) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should not validate array with non-unique primitive items', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const result = Value.Check(T, [0, 0, 2]) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should validate array with unique object items', () => { @@ -56,7 +56,7 @@ describe('value/check/Array', () => { { x: 2, y: 2 }, { x: 3, y: 3 }, ]) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should not validate array with non-unique object items', () => { @@ -66,6 +66,6 @@ describe('value/check/Array', () => { { x: 1, y: 1 }, { x: 3, y: 3 }, ]) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/bigint.ts b/test/runtime/value/check/bigint.ts index 74bf4c90d..2f663c384 100644 --- a/test/runtime/value/check/bigint.ts +++ b/test/runtime/value/check/bigint.ts @@ -7,38 +7,38 @@ describe('value/check/BigInt', () => { it('Should not validate NaN', () => { const T = Type.BigInt() const result = Value.Check(T, NaN) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.BigInt() const result = Value.Check(T, Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.BigInt() const result = Value.Check(T, -Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail integer', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail integer', () => { const value = 3.14 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass bigint', () => { const result = Value.Check(T, BigInt(0)) - Assert.equal(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/check/boolean.ts b/test/runtime/value/check/boolean.ts index 887f3f020..d15d8b7d4 100644 --- a/test/runtime/value/check/boolean.ts +++ b/test/runtime/value/check/boolean.ts @@ -7,41 +7,41 @@ describe('value/check/Boolean', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/composite.ts b/test/runtime/value/check/composite.ts index c9b4db2b6..fe4afa39f 100644 --- a/test/runtime/value/check/composite.ts +++ b/test/runtime/value/check/composite.ts @@ -25,7 +25,7 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail intersect with invalid property', () => { @@ -38,7 +38,7 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail intersect with missing property', () => { @@ -50,13 +50,13 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail intersect with primitive value', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass intersect with optional properties', () => { @@ -77,6 +77,6 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts index 796bc6f23..45e3dbba1 100644 --- a/test/runtime/value/check/custom.ts +++ b/test/runtime/value/check/custom.ts @@ -7,25 +7,25 @@ describe('type/check/Custom', () => { it('Should validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Assert.deepEqual(Value.Check(T, 1n), true) + Assert.isEqual(Value.Check(T, 1n), true) }) it('Should not validate bigint', () => { const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Assert.deepEqual(Value.Check(T, 1), false) + Assert.isEqual(Value.Check(T, 1), false) }) it('Should validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), }) - Assert.deepEqual(Value.Check(T, { x: 1n }), true) + Assert.isEqual(Value.Check(T, { x: 1n }), true) }) it('Should not validate bigint nested', () => { const T = Type.Object({ x: Type.Unsafe({ [Kind]: 'BigInt' }), }) - Assert.deepEqual(Value.Check(T, { x: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1 }), false) }) }) diff --git a/test/runtime/value/check/date.ts b/test/runtime/value/check/date.ts index 39d40e00d..2d4e74f8c 100644 --- a/test/runtime/value/check/date.ts +++ b/test/runtime/value/check/date.ts @@ -7,46 +7,46 @@ describe('value/check/Date', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should not validate Date if is invalid', () => { const value = new Date('not-a-valid-date') const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/enum.ts b/test/runtime/value/check/enum.ts index 5eb8c08e6..ec1395cdc 100644 --- a/test/runtime/value/check/enum.ts +++ b/test/runtime/value/check/enum.ts @@ -12,23 +12,23 @@ describe('value/check/Enum', () => { it('Should pass enum option A', () => { const value = Foo.A const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass enum option B', () => { const value = Foo.A const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail unknown value', () => { const value = 'unknown' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts index ae46303f7..2db4fe7a7 100644 --- a/test/runtime/value/check/integer.ts +++ b/test/runtime/value/check/integer.ts @@ -8,34 +8,34 @@ describe('value/check/Integer', () => { it('Should not validate NaN', () => { const T = Type.Integer() const result = Value.Check(T, NaN) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.Integer() const result = Value.Check(T, Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.Integer() const result = Value.Check(T, -Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass integer', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail integer', () => { const value = 3.14 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/intersect.ts b/test/runtime/value/check/intersect.ts index a7574a19e..e41a9d0ad 100644 --- a/test/runtime/value/check/intersect.ts +++ b/test/runtime/value/check/intersect.ts @@ -7,79 +7,79 @@ describe('value/check/Intersect', () => { const A = Type.Number() const B = Type.Number() const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, 1), true) + Assert.isEqual(Value.Check(T, 1), true) }) it('Should not intersect string and number', () => { const A = Type.String() const B = Type.Number() const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, 1), false) - Assert.equal(Value.Check(T, '1'), false) + Assert.isEqual(Value.Check(T, 1), false) + Assert.isEqual(Value.Check(T, '1'), false) }) it('Should intersect two objects', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) }) it('Should not intersect two objects with internal additionalProperties false', () => { const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, { x: 1, y: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), false) }) it('Should intersect two objects and mandate required properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) - Assert.equal(Value.Check(T, { x: 1 }), false) - Assert.equal(Value.Check(T, { x: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) + Assert.isEqual(Value.Check(T, { x: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1 }), false) }) it('Should intersect two objects with unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.equal(Value.Check(T, { x: 1, y: 1, z: 1 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), true) }) it('Should intersect two objects and restrict unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], { unevaluatedProperties: false }) - Assert.equal(Value.Check(T, { x: 1, y: 1, z: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), false) }) it('Should intersect two objects and allow unevaluated properties of number', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) - Assert.equal(Value.Check(T, { x: 1, y: 2, z: 3 }), true) - Assert.equal(Value.Check(T, { x: 1, y: 2, z: '1' }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: 3 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: '1' }), false) }) it('Should intersect two union objects with overlapping properties of the same type', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ x: Type.Number() })]) const T = Type.Intersect([A, B]) - Assert.equal(Value.Check(T, { x: 1 }), true) - Assert.equal(Value.Check(T, { x: '1' }), false) + Assert.isEqual(Value.Check(T, { x: 1 }), true) + Assert.isEqual(Value.Check(T, { x: '1' }), false) }) it('Should not intersect two union objects with overlapping properties of varying types', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ x: Type.String() })]) const T = Type.Intersect([A, B]) - Assert.equal(Value.Check(T, { x: 1 }), false) - Assert.equal(Value.Check(T, { x: '1' }), false) + Assert.isEqual(Value.Check(T, { x: 1 }), false) + Assert.isEqual(Value.Check(T, { x: '1' }), false) }) it('Should intersect two union objects with non-overlapping properties', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ y: Type.Number() })]) const T = Type.Intersect([A, B]) - Assert.equal(Value.Check(T, { x: 1, y: 1 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) }) it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => { const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })]) const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })]) const T = Type.Intersect([A, B]) - Assert.equal(Value.Check(T, { x: 1, y: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), false) }) it('unevaluatedProperties with Record 1', () => { const T = Type.Intersect( @@ -94,7 +94,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2 }), true) }) it('unevaluatedProperties with Record 2', () => { const T = Type.Intersect( @@ -109,7 +109,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, 0: 'hello' }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: 'hello' }), true) }) it('unevaluatedProperties with Record 3', () => { const T = Type.Intersect( @@ -124,7 +124,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, 0: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: 1 }), false) }) it('unevaluatedProperties with Record 4', () => { const T = Type.Intersect( @@ -139,7 +139,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2 }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2 }), true) }) it('unevaluatedProperties with Record 5', () => { const T = Type.Intersect( @@ -154,7 +154,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, z: true }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: true }), true) }) it('unevaluatedProperties with Record 6', () => { const T = Type.Intersect( @@ -169,7 +169,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, z: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: 1 }), false) }) it('unevaluatedProperties with Record 7', () => { const T = Type.Intersect( @@ -184,7 +184,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '' }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '' }), true) }) it('unevaluatedProperties with Record 8', () => { const T = Type.Intersect( @@ -199,7 +199,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '', z: true }), true) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: true }), true) }) it('unevaluatedProperties with Record 9', () => { const T = Type.Intersect( @@ -214,6 +214,6 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.equal(Value.Check(T, { x: 1, y: 2, 0: '', z: 1 }), false) + Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: 1 }), false) }) }) diff --git a/test/runtime/value/check/keyof.ts b/test/runtime/value/check/keyof.ts index 132446d60..ff9234f27 100644 --- a/test/runtime/value/check/keyof.ts +++ b/test/runtime/value/check/keyof.ts @@ -14,24 +14,24 @@ describe('value/check/KeyOf', () => { it('Should pass keyof', () => { const value = 'x' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail keyof', () => { const value = 'w' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail keyof with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail keyof with null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/literal.ts b/test/runtime/value/check/literal.ts index 84667b1e0..ae1e28dcb 100644 --- a/test/runtime/value/check/literal.ts +++ b/test/runtime/value/check/literal.ts @@ -7,21 +7,21 @@ describe('value/check/Literal', () => { it('Should pass literal', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail literal', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail literal with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail literal with null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/never.ts b/test/runtime/value/check/never.ts index 4f4948d6a..979e74474 100644 --- a/test/runtime/value/check/never.ts +++ b/test/runtime/value/check/never.ts @@ -7,36 +7,36 @@ describe('value/check/Never', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/not.ts b/test/runtime/value/check/not.ts index b6a0b08f6..abfc89597 100644 --- a/test/runtime/value/check/not.ts +++ b/test/runtime/value/check/not.ts @@ -5,8 +5,8 @@ import { Assert } from '../../assert/index' describe('value/check/Not', () => { it('Should validate with not number', () => { const T = Type.Not(Type.Number(), Type.String()) - Assert.equal(Value.Check(T, 1), false) - Assert.equal(Value.Check(T, 'A'), true) + Assert.isEqual(Value.Check(T, 1), false) + Assert.isEqual(Value.Check(T, 'A'), true) }) it('Should validate with union left', () => { // prettier-ignore @@ -15,10 +15,10 @@ describe('value/check/Not', () => { Type.Literal('B'), Type.Literal('C') ]), Type.String()) - Assert.equal(Value.Check(T, 'A'), false) - Assert.equal(Value.Check(T, 'B'), false) - Assert.equal(Value.Check(T, 'C'), false) - Assert.equal(Value.Check(T, 'D'), true) + Assert.isEqual(Value.Check(T, 'A'), false) + Assert.isEqual(Value.Check(T, 'B'), false) + Assert.isEqual(Value.Check(T, 'C'), false) + Assert.isEqual(Value.Check(T, 'D'), true) }) it('Should validate with union right', () => { // prettier-ignore @@ -26,14 +26,14 @@ describe('value/check/Not', () => { Type.String(), Type.Boolean() ])) - Assert.equal(Value.Check(T, 1), false) - Assert.equal(Value.Check(T, 'A'), true) - Assert.equal(Value.Check(T, true), true) + Assert.isEqual(Value.Check(T, 1), false) + Assert.isEqual(Value.Check(T, 'A'), true) + Assert.isEqual(Value.Check(T, true), true) }) it('Should not validate with symmetric left right', () => { // prettier-ignore const T = Type.Not(Type.Number(), Type.Number()) - Assert.equal(Value.Check(T, 1), false) - Assert.equal(Value.Check(T, true), false) + Assert.isEqual(Value.Check(T, 1), false) + Assert.isEqual(Value.Check(T, true), false) }) }) diff --git a/test/runtime/value/check/null.ts b/test/runtime/value/check/null.ts index 15b9f786b..fea54f9ec 100644 --- a/test/runtime/value/check/null.ts +++ b/test/runtime/value/check/null.ts @@ -7,41 +7,41 @@ describe('value/check/Null', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts index e2d0c63b8..3b60578ed 100644 --- a/test/runtime/value/check/number.ts +++ b/test/runtime/value/check/number.ts @@ -7,61 +7,61 @@ describe('value/check/Number', () => { it('Should not validate NaN', () => { const T = Type.Number() const result = Value.Check(T, NaN) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.Number() const result = Value.Check(T, Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.Number() const result = Value.Check(T, -Infinity) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail NaN', () => { const result = Value.Check(Type.Number(), NaN) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index 6a06962de..65485881b 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -22,7 +22,7 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail object with additional properties', () => { @@ -41,7 +41,7 @@ describe('value/check/Object', () => { a: 1, } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object with invalid property', () => { @@ -54,7 +54,7 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object with missing property', () => { @@ -66,7 +66,7 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass object with optional properties', () => { @@ -84,19 +84,19 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail object with null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should validate schema additional properties of string', () => { @@ -110,7 +110,7 @@ describe('value/check/Object', () => { }, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -119,7 +119,7 @@ describe('value/check/Object', () => { true, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -140,7 +140,7 @@ describe('value/check/Object', () => { }, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -149,7 +149,7 @@ describe('value/check/Object', () => { true, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -172,7 +172,7 @@ describe('value/check/Object', () => { }, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -181,7 +181,7 @@ describe('value/check/Object', () => { true, ) - Assert.equal( + Assert.isEqual( Value.Check(T, { x: 1, y: 2, @@ -192,36 +192,36 @@ describe('value/check/Object', () => { }) it('Should check for property key if property type is undefined', () => { const T = Type.Object({ x: Type.Undefined() }) - Assert.equal(Value.Check(T, { x: undefined }), true) - Assert.equal(Value.Check(T, {}), false) + Assert.isEqual(Value.Check(T, { x: undefined }), true) + Assert.isEqual(Value.Check(T, {}), false) }) it('Should check for property key if property type extends undefined', () => { const T = Type.Object({ x: Type.Union([Type.Number(), Type.Undefined()]) }) - Assert.equal(Value.Check(T, { x: 1 }), true) - Assert.equal(Value.Check(T, { x: undefined }), true) - Assert.equal(Value.Check(T, {}), false) + Assert.isEqual(Value.Check(T, { x: 1 }), true) + Assert.isEqual(Value.Check(T, { x: undefined }), true) + Assert.isEqual(Value.Check(T, {}), false) }) it('Should not check for property key if property type is undefined and optional', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) - Assert.equal(Value.Check(T, { x: undefined }), true) - Assert.equal(Value.Check(T, {}), true) + Assert.isEqual(Value.Check(T, { x: undefined }), true) + Assert.isEqual(Value.Check(T, {}), true) }) it('Should not check for property key if property type extends undefined and optional', () => { const T = Type.Object({ x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])) }) - Assert.equal(Value.Check(T, { x: 1 }), true) - Assert.equal(Value.Check(T, { x: undefined }), true) - Assert.equal(Value.Check(T, {}), true) + Assert.isEqual(Value.Check(T, { x: 1 }), true) + Assert.isEqual(Value.Check(T, { x: undefined }), true) + Assert.isEqual(Value.Check(T, {}), true) }) it('Should check undefined for optional property of number', () => { const T = Type.Object({ x: Type.Optional(Type.Number()) }) - Assert.equal(Value.Check(T, { x: 1 }), true) - Assert.equal(Value.Check(T, { x: undefined }), true) // allowed by default - Assert.equal(Value.Check(T, {}), true) + Assert.isEqual(Value.Check(T, { x: 1 }), true) + Assert.isEqual(Value.Check(T, { x: undefined }), true) // allowed by default + Assert.isEqual(Value.Check(T, {}), true) }) it('Should check undefined for optional property of undefined', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) - Assert.equal(Value.Check(T, { x: 1 }), false) - Assert.equal(Value.Check(T, {}), true) - Assert.equal(Value.Check(T, { x: undefined }), true) + Assert.isEqual(Value.Check(T, { x: 1 }), false) + Assert.isEqual(Value.Check(T, {}), true) + Assert.isEqual(Value.Check(T, { x: undefined }), true) }) }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 3fe8b5d08..1e36e37b1 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -20,33 +20,33 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail when below minProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 }) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) }) it('Should fail when above maxProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 }) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) }) it('Should fail with illogical minProperties | maxProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 }) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3 }), false) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), false) - Assert.equal(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), false) + Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) }) it('Should fail record with Date', () => { const T = Type.Record(Type.String(), Type.String()) const result = Value.Check(T, new Date()) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail record with Uint8Array', () => { const T = Type.Record(Type.String(), Type.String()) const result = Value.Check(T, new Uint8Array()) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail record with missing property', () => { const T = Type.Record( @@ -64,7 +64,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail record with invalid property', () => { const T = Type.Record( @@ -83,7 +83,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass record with optional property', () => { const T = Type.Record( @@ -101,7 +101,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass record with optional property', () => { const T = Type.Record( @@ -119,7 +119,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) // ------------------------------------------------- // Number Key @@ -132,7 +132,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should not pass record with invalid number key', () => { @@ -143,7 +143,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) // ------------------------------------------------- // Integer Key @@ -156,7 +156,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should not pass record with invalid integer key', () => { const T = Type.Record(Type.Integer(), Type.String(), { additionalProperties: false }) @@ -166,7 +166,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) // ------------------------------------------------------------ // AdditionalProperties @@ -174,26 +174,26 @@ describe('value/check/Record', () => { it('AdditionalProperties 1', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true }) const R = Value.Check(T, { 1: '', 2: '', x: 1, y: 2, z: 3 }) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('AdditionalProperties 2', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) const R = Value.Check(T, { 1: '', 2: '', 3: '' }) - Assert.equal(R, true) + Assert.isEqual(R, true) }) it('AdditionalProperties 3', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) const R = Value.Check(T, { 1: '', 2: '', x: '' }) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('AdditionalProperties 4', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) const R = Value.Check(T, { 1: '', 2: '', x: '' }) - Assert.equal(R, false) + Assert.isEqual(R, false) }) it('AdditionalProperties 5', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) const R = Value.Check(T, { 1: '', 2: '', x: true }) - Assert.equal(R, true) + Assert.isEqual(R, true) }) }) diff --git a/test/runtime/value/check/recursive.ts b/test/runtime/value/check/recursive.ts index 79f56d1cd..f6c7b94be 100644 --- a/test/runtime/value/check/recursive.ts +++ b/test/runtime/value/check/recursive.ts @@ -20,7 +20,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail recursive with invalid id', () => { @@ -33,7 +33,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail rec with invalid nodes', () => { @@ -46,7 +46,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail recursive with missing id', () => { @@ -55,7 +55,7 @@ describe('value/check/Recursive', () => { nodes: [{ nodes: [] }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail rec with missing nodes', () => { @@ -64,6 +64,6 @@ describe('value/check/Recursive', () => { nodes: [{ id: 'B' }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/ref.ts b/test/runtime/value/check/ref.ts index be4c2980b..e838a383a 100644 --- a/test/runtime/value/check/ref.ts +++ b/test/runtime/value/check/ref.ts @@ -13,7 +13,7 @@ describe('value/check/Ref', () => { { $id: Assert.nextId() }, ) const R = Type.Ref(T) - Assert.deepEqual( + Assert.isEqual( Value.Check(R, [T], { x: 1, y: 2, @@ -33,7 +33,7 @@ describe('value/check/Ref', () => { { $id: Assert.nextId() }, ) const R = Type.Ref(T) - Assert.deepEqual( + Assert.isEqual( Value.Check(R, [T], { x: 1, y: 2, @@ -59,10 +59,10 @@ describe('value/check/Ref', () => { }, { $id: 'T' }, ) - Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3 }), true) - Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 'hello' } }), true) - Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 1 } }), false) - Assert.deepEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: {} }), false) + Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3 }), true) + Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 'hello' } }), true) + Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 1 } }), false) + Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: {} }), false) // Ok(R, { x: 1, y: 2, z: 3 }, [T]) // Ok(R, , [T]) // Fail(R, , [T]) @@ -77,7 +77,7 @@ describe('value/check/Ref', () => { }), ) const R = Type.Ref(T) - Assert.deepEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) - Assert.deepEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) + Assert.isEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) + Assert.isEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) }) }) diff --git a/test/runtime/value/check/regex.ts b/test/runtime/value/check/regex.ts index 343e10980..b56076ded 100644 --- a/test/runtime/value/check/regex.ts +++ b/test/runtime/value/check/regex.ts @@ -7,13 +7,13 @@ describe('value/check/RegEx', () => { const T = Type.RegEx(/foo/) const value = 'foo' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail regex', () => { const T = Type.RegEx(/foo/) const value = 'bar' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/string.ts b/test/runtime/value/check/string.ts index 7db4daca3..1dbc26690 100644 --- a/test/runtime/value/check/string.ts +++ b/test/runtime/value/check/string.ts @@ -7,41 +7,41 @@ describe('value/check/String', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/symbol.ts b/test/runtime/value/check/symbol.ts index f2df521eb..12b343d15 100644 --- a/test/runtime/value/check/symbol.ts +++ b/test/runtime/value/check/symbol.ts @@ -7,46 +7,46 @@ describe('value/check/Symbol', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass symbol', () => { const value = Symbol(1) const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/check/template-literal.ts b/test/runtime/value/check/template-literal.ts index 7208578c7..44fbbb66b 100644 --- a/test/runtime/value/check/template-literal.ts +++ b/test/runtime/value/check/template-literal.ts @@ -9,23 +9,23 @@ describe('value/check/TemplateLiteral', () => { it('Should validate finite pattern 1', () => { // prettier-ignore const T = Type.TemplateLiteral([]) - Assert.equal(Value.Check(T, ''), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, ''), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 1', () => { // prettier-ignore const T = Type.TemplateLiteral([Type.Boolean()]) - Assert.equal(Value.Check(T, 'true'), true) - Assert.equal(Value.Check(T, 'false'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'true'), true) + Assert.isEqual(Value.Check(T, 'false'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 2', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.Literal('A') ]) - Assert.equal(Value.Check(T, 'A'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'A'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 3', () => { // prettier-ignore @@ -33,8 +33,8 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Literal('B') ]) - Assert.equal(Value.Check(T, 'AB'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'AB'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 4', () => { // prettier-ignore @@ -45,9 +45,9 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('C') ]), ]) - Assert.equal(Value.Check(T, 'AB'), true) - Assert.equal(Value.Check(T, 'AC'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'AB'), true) + Assert.isEqual(Value.Check(T, 'AC'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 5', () => { // prettier-ignore @@ -59,9 +59,9 @@ describe('value/check/TemplateLiteral', () => { ]), Type.Literal('D'), ]) - Assert.equal(Value.Check(T, 'ABD'), true) - Assert.equal(Value.Check(T, 'ACD'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'ABD'), true) + Assert.isEqual(Value.Check(T, 'ACD'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 6', () => { // prettier-ignore @@ -75,11 +75,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('1') ]), ]) - Assert.equal(Value.Check(T, '00'), true) - Assert.equal(Value.Check(T, '01'), true) - Assert.equal(Value.Check(T, '10'), true) - Assert.equal(Value.Check(T, '11'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, '00'), true) + Assert.isEqual(Value.Check(T, '01'), true) + Assert.isEqual(Value.Check(T, '10'), true) + Assert.isEqual(Value.Check(T, '11'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) // -------------------------------------------------------- // Infinite @@ -89,47 +89,47 @@ describe('value/check/TemplateLiteral', () => { const T = Type.TemplateLiteral([ Type.Number() ]) - Assert.equal(Value.Check(T, '1'), true) - Assert.equal(Value.Check(T, '22'), true) - Assert.equal(Value.Check(T, '333'), true) - Assert.equal(Value.Check(T, '4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, '1'), true) + Assert.isEqual(Value.Check(T, '22'), true) + Assert.isEqual(Value.Check(T, '333'), true) + Assert.isEqual(Value.Check(T, '4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 2', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.Integer() ]) - Assert.equal(Value.Check(T, '1'), true) - Assert.equal(Value.Check(T, '22'), true) - Assert.equal(Value.Check(T, '333'), true) - Assert.equal(Value.Check(T, '4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, '1'), true) + Assert.isEqual(Value.Check(T, '22'), true) + Assert.isEqual(Value.Check(T, '333'), true) + Assert.isEqual(Value.Check(T, '4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 3', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.BigInt() ]) - Assert.equal(Value.Check(T, '1'), true) - Assert.equal(Value.Check(T, '22'), true) - Assert.equal(Value.Check(T, '333'), true) - Assert.equal(Value.Check(T, '4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, '1'), true) + Assert.isEqual(Value.Check(T, '22'), true) + Assert.isEqual(Value.Check(T, '333'), true) + Assert.isEqual(Value.Check(T, '4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 4', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.String() ]) - Assert.equal(Value.Check(T, '1'), true) - Assert.equal(Value.Check(T, '22'), true) - Assert.equal(Value.Check(T, '333'), true) - Assert.equal(Value.Check(T, '4444'), true) - Assert.equal(Value.Check(T, 'a'), true) - Assert.equal(Value.Check(T, 'bb'), true) - Assert.equal(Value.Check(T, 'ccc'), true) - Assert.equal(Value.Check(T, 'dddd'), true) + Assert.isEqual(Value.Check(T, '1'), true) + Assert.isEqual(Value.Check(T, '22'), true) + Assert.isEqual(Value.Check(T, '333'), true) + Assert.isEqual(Value.Check(T, '4444'), true) + Assert.isEqual(Value.Check(T, 'a'), true) + Assert.isEqual(Value.Check(T, 'bb'), true) + Assert.isEqual(Value.Check(T, 'ccc'), true) + Assert.isEqual(Value.Check(T, 'dddd'), true) }) it('Should validate infinite pattern 5', () => { @@ -138,11 +138,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Number() ]) - Assert.equal(Value.Check(T, 'A1'), true) - Assert.equal(Value.Check(T, 'A22'), true) - Assert.equal(Value.Check(T, 'A333'), true) - Assert.equal(Value.Check(T, 'A4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'A1'), true) + Assert.isEqual(Value.Check(T, 'A22'), true) + Assert.isEqual(Value.Check(T, 'A333'), true) + Assert.isEqual(Value.Check(T, 'A4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 6', () => { // prettier-ignore @@ -150,11 +150,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Integer() ]) - Assert.equal(Value.Check(T, 'A1'), true) - Assert.equal(Value.Check(T, 'A22'), true) - Assert.equal(Value.Check(T, 'A333'), true) - Assert.equal(Value.Check(T, 'A4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'A1'), true) + Assert.isEqual(Value.Check(T, 'A22'), true) + Assert.isEqual(Value.Check(T, 'A333'), true) + Assert.isEqual(Value.Check(T, 'A4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 7', () => { // prettier-ignore @@ -162,11 +162,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.BigInt() ]) - Assert.equal(Value.Check(T, 'A1'), true) - Assert.equal(Value.Check(T, 'A22'), true) - Assert.equal(Value.Check(T, 'A333'), true) - Assert.equal(Value.Check(T, 'A4444'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'A1'), true) + Assert.isEqual(Value.Check(T, 'A22'), true) + Assert.isEqual(Value.Check(T, 'A333'), true) + Assert.isEqual(Value.Check(T, 'A4444'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 8', () => { // prettier-ignore @@ -174,14 +174,14 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.String() ]) - Assert.equal(Value.Check(T, 'A1'), true) - Assert.equal(Value.Check(T, 'A22'), true) - Assert.equal(Value.Check(T, 'A333'), true) - Assert.equal(Value.Check(T, 'A4444'), true) - Assert.equal(Value.Check(T, 'Aa'), true) - Assert.equal(Value.Check(T, 'Abb'), true) - Assert.equal(Value.Check(T, 'Accc'), true) - Assert.equal(Value.Check(T, 'Adddd'), true) - Assert.equal(Value.Check(T, 'X'), false) + Assert.isEqual(Value.Check(T, 'A1'), true) + Assert.isEqual(Value.Check(T, 'A22'), true) + Assert.isEqual(Value.Check(T, 'A333'), true) + Assert.isEqual(Value.Check(T, 'A4444'), true) + Assert.isEqual(Value.Check(T, 'Aa'), true) + Assert.isEqual(Value.Check(T, 'Abb'), true) + Assert.isEqual(Value.Check(T, 'Accc'), true) + Assert.isEqual(Value.Check(T, 'Adddd'), true) + Assert.isEqual(Value.Check(T, 'X'), false) }) }) diff --git a/test/runtime/value/check/tuple.ts b/test/runtime/value/check/tuple.ts index 2d010d9aa..2fc512f98 100644 --- a/test/runtime/value/check/tuple.ts +++ b/test/runtime/value/check/tuple.ts @@ -7,34 +7,34 @@ describe('value/check/Tuple', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail when tuple is less than length', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail when tuple is greater than length', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1, 1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass empty tuple', () => { const T = Type.Tuple([]) const value = [] as any[] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail empty tuple', () => { const T = Type.Tuple([]) const value = [1] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/uint8array.ts b/test/runtime/value/check/uint8array.ts index 9940e7c4a..21f526a79 100644 --- a/test/runtime/value/check/uint8array.ts +++ b/test/runtime/value/check/uint8array.ts @@ -7,27 +7,27 @@ describe('value/check/Uint8Array', () => { const T = Type.Uint8Array() const value = new Uint8Array(4) const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail Uint8Array', () => { const T = Type.Uint8Array() const value = [0, 1, 2, 3] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Uint8Array with undefined', () => { const T = Type.Uint8Array() const value = undefined const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Uint8Array with null', () => { const T = Type.Uint8Array() const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/undefined.ts b/test/runtime/value/check/undefined.ts index 83b6f3383..d25ec8303 100644 --- a/test/runtime/value/check/undefined.ts +++ b/test/runtime/value/check/undefined.ts @@ -7,41 +7,41 @@ describe('value/check/Undefined', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/union.ts b/test/runtime/value/check/union.ts index ff822c895..bbd29951e 100644 --- a/test/runtime/value/check/union.ts +++ b/test/runtime/value/check/union.ts @@ -20,25 +20,25 @@ describe('value/check/Union', () => { it('Should pass union A', () => { const value = { type: 'A', x: 1, y: 1 } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass union B', () => { const value = { type: 'B', x: true, y: false } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail union A', () => { const value = { type: 'A', x: true, y: false } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail union B', () => { const value = { type: 'B', x: 1, y: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass union A with optional properties', () => { @@ -55,7 +55,7 @@ describe('value/check/Union', () => { const T = Type.Union([A, B]) const value = { type: 'A' } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail union A with invalid optional properties', () => { @@ -72,6 +72,6 @@ describe('value/check/Union', () => { const T = Type.Union([A, B]) const value = { type: 'A', x: true, y: false } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/check/unknown.ts b/test/runtime/value/check/unknown.ts index 19e305188..bb9943933 100644 --- a/test/runtime/value/check/unknown.ts +++ b/test/runtime/value/check/unknown.ts @@ -7,41 +7,41 @@ describe('value/check/Unknown', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) }) diff --git a/test/runtime/value/check/void.ts b/test/runtime/value/check/void.ts index 5930cb44f..0c1b26eb9 100644 --- a/test/runtime/value/check/void.ts +++ b/test/runtime/value/check/void.ts @@ -7,41 +7,41 @@ describe('value/check/Void', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.equal(result, true) + Assert.isEqual(result, true) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.equal(result, false) + Assert.isEqual(result, false) }) }) diff --git a/test/runtime/value/clone/clone.ts b/test/runtime/value/clone/clone.ts index 4fdcf9e04..8655dd1ba 100644 --- a/test/runtime/value/clone/clone.ts +++ b/test/runtime/value/clone/clone.ts @@ -7,38 +7,38 @@ describe('value/clone/Clone', () => { // -------------------------------------------- it('Should clone null', () => { const R = Value.Clone(null) - Assert.deepEqual(R, null) + Assert.isEqual(R, null) }) it('Should clone undefined', () => { const R = Value.Clone(undefined) - Assert.deepEqual(R, undefined) + Assert.isEqual(R, undefined) }) it('Should clone number', () => { const R = Value.Clone(1) - Assert.deepEqual(R, 1) + Assert.isEqual(R, 1) }) it('Should clone bigint', () => { const R = Value.Clone(1n) - Assert.deepEqual(R, 1n) + Assert.isEqual(R, 1n) }) it('Should clone boolean', () => { const R = Value.Clone(true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should clone string', () => { const R = Value.Clone('hello') - Assert.deepEqual(R, 'hello') + Assert.isEqual(R, 'hello') }) it('Should clone symbol', () => { const S = Symbol('hello') const R = Value.Clone(S) - Assert.deepEqual(R, S) + Assert.isEqual(R, S) }) // -------------------------------------------- @@ -52,7 +52,7 @@ describe('value/clone/Clone', () => { z: 3, } const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone object #2', () => { @@ -67,7 +67,7 @@ describe('value/clone/Clone', () => { }, } const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone object #3', () => { @@ -78,7 +78,7 @@ describe('value/clone/Clone', () => { w: [0, 1, 2, 3, 4], } const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) // -------------------------------------------- // ArrayType @@ -86,7 +86,7 @@ describe('value/clone/Clone', () => { it('Should clone array #1', () => { const V = [1, 2, 3, 4] const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone array #2', () => { const V = [ @@ -96,7 +96,7 @@ describe('value/clone/Clone', () => { [1, 2, 3], ] const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone array #3', () => { const V = [ @@ -106,72 +106,72 @@ describe('value/clone/Clone', () => { { x: 1, y: 2, z: 3 }, ] const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Int8Array', () => { const V = new Int8Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Uint8Array', () => { const V = new Uint8Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Uint8ClampedArray', () => { const V = new Uint8ClampedArray([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Int16Array', () => { const V = new Int16Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Uint16Array', () => { const V = new Uint16Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Int32Array', () => { const V = new Int32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Uint32Array', () => { const V = new Int32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Float32Array', () => { const V = new Float32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone Float64Array', () => { const V = new Float64Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone BigInt64Array', () => { const V = new BigInt64Array([1n, 2n, 3n, 4n]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should clone BigUint64Array', () => { const V = new BigUint64Array([1n, 2n, 3n, 4n]) const R = Value.Clone(V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) }) diff --git a/test/runtime/value/convert/any.ts b/test/runtime/value/convert/any.ts index cb776c87c..81f57de58 100644 --- a/test/runtime/value/convert/any.ts +++ b/test/runtime/value/convert/any.ts @@ -7,36 +7,36 @@ describe('value/convert/Any', () => { it('Should convert null', () => { const V = null const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert undefined', () => { const V = undefined const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert string', () => { const V = 'hello' const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert number', () => { const V = 42 const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert boolean', () => { const V = true const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert object', () => { const V = { x: 1 } const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert array', () => { const V = [1, 2, 3] const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) }) diff --git a/test/runtime/value/convert/array.ts b/test/runtime/value/convert/array.ts index 18e41d496..be8ed6af6 100644 --- a/test/runtime/value/convert/array.ts +++ b/test/runtime/value/convert/array.ts @@ -6,27 +6,27 @@ describe('value/convert/Array', () => { it('Should convert array of number', () => { const T = Type.Array(Type.Number()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.deepEqual(R, [1, 3.14, 1, 3.14, 1, 0, 1, 0, 'hello']) + Assert.isEqual(R, [1, 3.14, 1, 3.14, 1, 0, 1, 0, 'hello']) }) it('Should convert array of boolean', () => { const T = Type.Array(Type.Boolean()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.deepEqual(R, [true, 3.14, true, '3.14', true, false, true, false, 'hello']) + Assert.isEqual(R, [true, 3.14, true, '3.14', true, false, true, false, 'hello']) }) it('Should convert array of string', () => { const T = Type.Array(Type.String()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.deepEqual(R, ['1', '3.14', '1', '3.14', 'true', 'false', 'true', 'false', 'hello']) + Assert.isEqual(R, ['1', '3.14', '1', '3.14', 'true', 'false', 'true', 'false', 'hello']) }) it('Should convert array of date', () => { const T = Type.Array(Type.Date()) const R = Value.Convert(T, [1, '1', true, false, 'true', 'false', 'hello']) as any[] - Assert.deepEqual(R[0].getTime(), 1) - Assert.deepEqual(R[1].getTime(), 1) - Assert.deepEqual(R[2].getTime(), 1) - Assert.deepEqual(R[3].getTime(), 0) - Assert.deepEqual(R[4].getTime(), 1) - Assert.deepEqual(R[5].getTime(), 0) - Assert.deepEqual(R[6], 'hello') + Assert.isEqual(R[0].getTime(), 1) + Assert.isEqual(R[1].getTime(), 1) + Assert.isEqual(R[2].getTime(), 1) + Assert.isEqual(R[3].getTime(), 0) + Assert.isEqual(R[4].getTime(), 1) + Assert.isEqual(R[5].getTime(), 0) + Assert.isEqual(R[6], 'hello') }) }) diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts index 69a23791e..83a069632 100644 --- a/test/runtime/value/convert/bigint.ts +++ b/test/runtime/value/convert/bigint.ts @@ -6,41 +6,41 @@ describe('value/convert/BigInt', () => { it('Should convert bitint from string 1', () => { const T = Type.BigInt() const R = Value.Convert(T, '1') - Assert.deepEqual(R, BigInt(1)) + Assert.isEqual(R, BigInt(1)) }) it('Should convert bigint from string 2', () => { const T = Type.BigInt() const R = Value.Convert(T, '3.14') - Assert.deepEqual(R, BigInt(3)) + Assert.isEqual(R, BigInt(3)) }) it('Should convert bitint from string 3', () => { const T = Type.BigInt() const R = Value.Convert(T, 'true') - Assert.deepEqual(R, BigInt(1)) + Assert.isEqual(R, BigInt(1)) }) it('Should convert bigint from string 4', () => { const T = Type.BigInt() const R = Value.Convert(T, 'false') - Assert.deepEqual(R, BigInt(0)) + Assert.isEqual(R, BigInt(0)) }) it('Should convert bitint from number 1', () => { const T = Type.BigInt() const R = Value.Convert(T, 1) - Assert.deepEqual(R, BigInt(1)) + Assert.isEqual(R, BigInt(1)) }) it('Should convert bigint from number 2', () => { const T = Type.BigInt() const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, BigInt(3)) + Assert.isEqual(R, BigInt(3)) }) it('Should convert bitint from boolean 1', () => { const T = Type.BigInt() const R = Value.Convert(T, true) - Assert.deepEqual(R, BigInt(1)) + Assert.isEqual(R, BigInt(1)) }) it('Should convert bigint from boolean 2', () => { const T = Type.BigInt() const R = Value.Convert(T, false) - Assert.deepEqual(R, BigInt(0)) + Assert.isEqual(R, BigInt(0)) }) }) diff --git a/test/runtime/value/convert/boolean.ts b/test/runtime/value/convert/boolean.ts index 02f3422af..2a72c08ee 100644 --- a/test/runtime/value/convert/boolean.ts +++ b/test/runtime/value/convert/boolean.ts @@ -6,47 +6,47 @@ describe('value/convert/Boolean', () => { it('Should convert from string 1', () => { const T = Type.Boolean() const R = Value.Convert(T, '1') - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from string 2', () => { const T = Type.Boolean() const R = Value.Convert(T, '3.14') - Assert.deepEqual(R, '3.14') + Assert.isEqual(R, '3.14') }) it('Should convert from string 3', () => { const T = Type.Boolean() const R = Value.Convert(T, 'true') - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from string 4', () => { const T = Type.Boolean() const R = Value.Convert(T, 'false') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should convert from number 1', () => { const T = Type.Boolean() const R = Value.Convert(T, 1) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from number 2', () => { const T = Type.Boolean() const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, 3.14) + Assert.isEqual(R, 3.14) }) it('Should convert from number 3', () => { const T = Type.Boolean() const R = Value.Convert(T, 1.1) - Assert.deepEqual(R, 1.1) + Assert.isEqual(R, 1.1) }) it('Should convert from boolean 1', () => { const T = Type.Boolean() const R = Value.Convert(T, true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from boolean 2', () => { const T = Type.Boolean() const R = Value.Convert(T, false) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) // ---------------------------------------------------------- // Casts @@ -54,101 +54,101 @@ describe('value/convert/Boolean', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) it('Should convert string #2', () => { const value = 'true' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert string #3', () => { const value = 'TRUE' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert string #4', () => { const value = 'false' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert string #5', () => { const value = '0' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert string #6', () => { const value = '1' const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert string #7', () => { const value = '0' const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert string #8', () => { const value = '1' const result = Value.Convert(Type.Boolean({ default: false }), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert string #8', () => { const value = '2' const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, '2') + Assert.isEqual(result, '2') }) it('Should convert number #1', () => { const value = 0 const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert number #2', () => { const value = 1n const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert number #3', () => { const value = 1 const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert number #4', () => { const value = 2 const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, 2) + Assert.isEqual(result, 2) }) it('Should convert number #5', () => { const value = 0 const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert number #6', () => { const value = 1 const result = Value.Convert(Type.Boolean({ default: false }), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert number #7', () => { const value = 2 const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.deepEqual(result, 2) + Assert.isEqual(result, 2) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, true) + Assert.isEqual(result, true) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, false) + Assert.isEqual(result, false) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, {}) + Assert.isEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.Boolean(), value) - Assert.deepEqual(result, []) + Assert.isEqual(result, []) }) }) diff --git a/test/runtime/value/convert/composite.ts b/test/runtime/value/convert/composite.ts index a8944d449..5c7678984 100644 --- a/test/runtime/value/convert/composite.ts +++ b/test/runtime/value/convert/composite.ts @@ -11,6 +11,6 @@ describe('value/convert/Composite', () => { Type.Object({ z: Type.Boolean() }) ]) const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) - Assert.deepEqual(R, { x: 42, y: true, z: 'hello' }) + Assert.isEqual(R, { x: 42, y: true, z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/custom.ts b/test/runtime/value/convert/custom.ts index 82887cefd..5bc20d59c 100644 --- a/test/runtime/value/convert/custom.ts +++ b/test/runtime/value/convert/custom.ts @@ -7,18 +7,18 @@ describe('value/convert/Custom', () => { const Custom = TypeSystem.Type('type/convert/Custom/1', () => true) const T = Custom() const R = Value.Convert(T, true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not convert 2', () => { const Custom = TypeSystem.Type('type/convert/Custom/2', () => true) const T = Custom() const R = Value.Convert(T, 42) - Assert.deepEqual(R, 42) + Assert.isEqual(R, 42) }) it('Should not convert 3', () => { const Custom = TypeSystem.Type('type/convert/Custom/3', () => true) const T = Custom() const R = Value.Convert(T, 'hello') - Assert.deepEqual(R, 'hello') + Assert.isEqual(R, 'hello') }) }) diff --git a/test/runtime/value/convert/date.ts b/test/runtime/value/convert/date.ts index 27ea49c24..171b5fb4c 100644 --- a/test/runtime/value/convert/date.ts +++ b/test/runtime/value/convert/date.ts @@ -5,38 +5,38 @@ import { Assert } from '../../assert/index' describe('value/convert/Date', () => { it('Should convert from number', () => { const result = Value.Convert(Type.Date(), 123) as Date - Assert.deepEqual(result.getTime(), 123) + Assert.isEqual(result.getTime(), 123) }) it('Should convert from numeric string', () => { const result = Value.Convert(Type.Date(), '123') as Date - Assert.deepEqual(result.getTime(), 123) + Assert.isEqual(result.getTime(), 123) }) it('Should convert from boolean true (interpretted as numeric 1)', () => { const result = Value.Convert(Type.Date(), true) as Date - Assert.deepEqual(result.getTime(), 1) + Assert.isEqual(result.getTime(), 1) }) it('Should convert from datetime string', () => { const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03.000Z') as Date - Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + Assert.isEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') }) it('Should convert from datetime string without timezone', () => { const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03') as Date - Assert.deepEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + Assert.isEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') }) it('Should convert from time with timezone', () => { const result = Value.Convert(Type.Date(), '01:02:03.000Z') as Date - Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + Assert.isEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') }) it('Should convert from time without timezone', () => { const result = Value.Convert(Type.Date(), '01:02:03') as Date - Assert.deepEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + Assert.isEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') }) it('Should convert from date string', () => { const result = Value.Convert(Type.Date(), '1980-02-03') as Date - Assert.deepEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') + Assert.isEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') }) it('Should convert invalid strings to unix epoch 0', () => { const result = Value.Convert(Type.Date(), 'invalid-date') as Date - Assert.deepEqual(result, 'invalid-date') + Assert.isEqual(result, 'invalid-date') }) }) diff --git a/test/runtime/value/convert/integer.ts b/test/runtime/value/convert/integer.ts index e2f03c32a..727bddf0c 100644 --- a/test/runtime/value/convert/integer.ts +++ b/test/runtime/value/convert/integer.ts @@ -6,27 +6,27 @@ describe('value/convert/Integer', () => { it('Should convert from string 1', () => { const T = Type.Integer() const R = Value.Convert(T, '3.14') - Assert.deepEqual(R, 3) + Assert.isEqual(R, 3) }) it('Should convert from string 2', () => { const T = Type.Integer() const R = Value.Convert(T, '42') - Assert.deepEqual(R, 42) + Assert.isEqual(R, 42) }) it('Should convert from boolean 1', () => { const T = Type.Integer() const R = Value.Convert(T, true) - Assert.deepEqual(R, 1) + Assert.isEqual(R, 1) }) it('Should convert from boolean 2', () => { const T = Type.Integer() const R = Value.Convert(T, false) - Assert.deepEqual(R, 0) + Assert.isEqual(R, 0) }) it('Should convert from number 1', () => { const T = Type.Integer() const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, 3) + Assert.isEqual(R, 3) }) // ---------------------------------------------------------- // Casts @@ -34,46 +34,46 @@ describe('value/convert/Integer', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) it('Should convert string #2', () => { const value = '3.14' const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 3) + Assert.isEqual(result, 3) }) it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 0) + Assert.isEqual(result, 0) }) it('Should convert string #4', () => { const value = '-100' const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, -100) + Assert.isEqual(result, -100) }) it('Should convert number', () => { const value = 42 const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 42) + Assert.isEqual(result, 42) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 1) + Assert.isEqual(result, 1) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, 0) + Assert.isEqual(result, 0) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, {}) + Assert.isEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.Integer(), value) - Assert.deepEqual(result, []) + Assert.isEqual(result, []) }) }) diff --git a/test/runtime/value/convert/literal.ts b/test/runtime/value/convert/literal.ts index 6943c3637..c46ba3dc6 100644 --- a/test/runtime/value/convert/literal.ts +++ b/test/runtime/value/convert/literal.ts @@ -6,80 +6,80 @@ describe('value/convert/Literal:String', () => { it('Should convert from number 1', () => { const T = Type.Literal('1') const R = Value.Convert(T, 1) - Assert.deepEqual(R, '1') + Assert.isEqual(R, '1') }) it('Should convert from number 2', () => { const T = Type.Literal('1') const R = Value.Convert(T, 2) - Assert.deepEqual(R, 2) + Assert.isEqual(R, 2) }) it('Should convert from boolean', () => { const T = Type.Literal('true') const R = Value.Convert(T, true) - Assert.deepEqual(R, 'true') + Assert.isEqual(R, 'true') }) }) describe('value/convert/Literal:Number', () => { it('Should convert from number 1', () => { const T = Type.Literal(3.14) const R = Value.Convert(T, '3.14') - Assert.deepEqual(R, 3.14) + Assert.isEqual(R, 3.14) }) it('Should convert from number 2', () => { const T = Type.Literal(3.14) const R = Value.Convert(T, '3.15') - Assert.deepEqual(R, '3.15') + Assert.isEqual(R, '3.15') }) it('Should convert from boolean 1', () => { const T = Type.Literal(1) const R = Value.Convert(T, true) - Assert.deepEqual(R, 1) + Assert.isEqual(R, 1) }) it('Should convert from boolean 2', () => { const T = Type.Literal(0) const R = Value.Convert(T, false) - Assert.deepEqual(R, 0) + Assert.isEqual(R, 0) }) it('Should convert from boolean 3', () => { const T = Type.Literal(2) const R = Value.Convert(T, true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) }) describe('value/convert/Literal:Boolean', () => { it('Should convert from number 1', () => { const T = Type.Literal(true) const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, 3.14) + Assert.isEqual(R, 3.14) }) it('Should convert from number 2', () => { const T = Type.Literal(true) const R = Value.Convert(T, 1) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from string 1', () => { const T = Type.Literal(true) const R = Value.Convert(T, 'true') - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from string 2', () => { const T = Type.Literal(false) const R = Value.Convert(T, 'false') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should convert from string 3', () => { const T = Type.Literal(true) const R = Value.Convert(T, '1') - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should convert from string 4', () => { const T = Type.Literal(false) const R = Value.Convert(T, '0') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should convert from string 5', () => { const T = Type.Literal(false) const R = Value.Convert(T, '2') - Assert.deepEqual(R, '2') + Assert.isEqual(R, '2') }) }) diff --git a/test/runtime/value/convert/never.ts b/test/runtime/value/convert/never.ts index eb481d593..5f6d0c4a6 100644 --- a/test/runtime/value/convert/never.ts +++ b/test/runtime/value/convert/never.ts @@ -6,16 +6,16 @@ describe('value/convert/Never', () => { it('Should not convert 1', () => { const T = Type.Never() const R = Value.Convert(T, true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not convert 2', () => { const T = Type.Never() const R = Value.Convert(T, 42) - Assert.deepEqual(R, 42) + Assert.isEqual(R, 42) }) it('Should not convert 3', () => { const T = Type.Never() const R = Value.Convert(T, 'true') - Assert.deepEqual(R, 'true') + Assert.isEqual(R, 'true') }) }) diff --git a/test/runtime/value/convert/null.ts b/test/runtime/value/convert/null.ts index bda1930a8..12d5ab1ee 100644 --- a/test/runtime/value/convert/null.ts +++ b/test/runtime/value/convert/null.ts @@ -6,14 +6,14 @@ describe('value/convert/Null', () => { const T = Type.Null() it('Should convert from string 1', () => { const R = Value.Convert(T, 'null') - Assert.deepEqual(R, null) + Assert.isEqual(R, null) }) it('Should convert from string 2', () => { const R = Value.Convert(T, 'NULL') - Assert.deepEqual(R, null) + Assert.isEqual(R, null) }) it('Should convert from string 3', () => { const R = Value.Convert(T, 'nil') - Assert.deepEqual(R, 'nil') + Assert.isEqual(R, 'nil') }) }) diff --git a/test/runtime/value/convert/number.ts b/test/runtime/value/convert/number.ts index 3b5029c8c..05636653c 100644 --- a/test/runtime/value/convert/number.ts +++ b/test/runtime/value/convert/number.ts @@ -6,23 +6,23 @@ describe('value/convert/Number', () => { const T = Type.Number() it('Should convert from string 1', () => { const R = Value.Convert(T, '3.14') - Assert.deepEqual(R, 3.14) + Assert.isEqual(R, 3.14) }) it('Should convert from string 2', () => { const R = Value.Convert(T, '42') - Assert.deepEqual(R, 42) + Assert.isEqual(R, 42) }) it('Should convert from boolean 1', () => { const R = Value.Convert(T, true) - Assert.deepEqual(R, 1) + Assert.isEqual(R, 1) }) it('Should convert from boolean 2', () => { const R = Value.Convert(T, false) - Assert.deepEqual(R, 0) + Assert.isEqual(R, 0) }) it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, 3.14) + Assert.isEqual(R, 3.14) }) // ---------------------------------------------------------- @@ -31,46 +31,46 @@ describe('value/convert/Number', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) it('Should convert string #2', () => { const value = '3.14' const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 3.14) + Assert.isEqual(result, 3.14) }) it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 0) + Assert.isEqual(result, 0) }) it('Should convert string #4', () => { const value = '-100' const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, -100) + Assert.isEqual(result, -100) }) it('Should convert number', () => { const value = 42 const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 42) + Assert.isEqual(result, 42) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 1) + Assert.isEqual(result, 1) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Number(), value) - Assert.deepEqual(result, 0) + Assert.isEqual(result, 0) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, {}) + Assert.isEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, []) + Assert.isEqual(result, []) }) }) diff --git a/test/runtime/value/convert/object.ts b/test/runtime/value/convert/object.ts index 2c85d60f2..27dd6e824 100644 --- a/test/runtime/value/convert/object.ts +++ b/test/runtime/value/convert/object.ts @@ -11,6 +11,6 @@ describe('value/convert/Object', () => { z: Type.Boolean() }) const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) - Assert.deepEqual(R, { x: 42, y: true, z: 'hello' }) + Assert.isEqual(R, { x: 42, y: true, z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts index 641476f17..e773e8f25 100644 --- a/test/runtime/value/convert/record.ts +++ b/test/runtime/value/convert/record.ts @@ -6,6 +6,6 @@ describe('value/convert/Record', () => { it('Should convert record value to numeric', () => { const T = Type.Record(Type.String(), Type.Number()) const V = Value.Convert(T, { x: '42', y: '24', z: 'hello' }) - Assert.deepEqual(V, { x: 42, y: '24', z: 'hello' }) + Assert.isEqual(V, { x: 42, y: '24', z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/string.ts b/test/runtime/value/convert/string.ts index b20c3ccf7..3580bbda7 100644 --- a/test/runtime/value/convert/string.ts +++ b/test/runtime/value/convert/string.ts @@ -6,27 +6,27 @@ describe('value/convert/String', () => { const T = Type.String() it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, '3.14') + Assert.isEqual(R, '3.14') }) it('Should convert from number 2', () => { const R = Value.Convert(T, 3) - Assert.deepEqual(R, '3') + Assert.isEqual(R, '3') }) it('Should convert from boolean 1', () => { const R = Value.Convert(T, true) - Assert.deepEqual(R, 'true') + Assert.isEqual(R, 'true') }) it('Should convert from boolean 2', () => { const R = Value.Convert(T, false) - Assert.deepEqual(R, 'false') + Assert.isEqual(R, 'false') }) it('Should convert from bigint', () => { const R = Value.Convert(T, BigInt(12345)) - Assert.deepEqual(R, '12345') + Assert.isEqual(R, '12345') }) it('Should convert from symbol', () => { const R = Value.Convert(T, Symbol(12345)) - Assert.deepEqual(R, '12345') + Assert.isEqual(R, '12345') }) // ---------------------------------------------------------- // Casts @@ -34,36 +34,36 @@ describe('value/convert/String', () => { it('Should convert string', () => { const value = 'hello' const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, 'hello') + Assert.isEqual(result, 'hello') }) it('Should convert number #1', () => { const value = 42 const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, '42') + Assert.isEqual(result, '42') }) it('Should convert number #2', () => { const value = 42n const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, '42') + Assert.isEqual(result, '42') }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, 'true') + Assert.isEqual(result, 'true') }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, 'false') + Assert.isEqual(result, 'false') }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, {}) + Assert.isEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.String(), value) - Assert.deepEqual(result, []) + Assert.isEqual(result, []) }) }) diff --git a/test/runtime/value/convert/symbol.ts b/test/runtime/value/convert/symbol.ts index fe2a158e3..096b9c3cd 100644 --- a/test/runtime/value/convert/symbol.ts +++ b/test/runtime/value/convert/symbol.ts @@ -6,10 +6,10 @@ describe('value/convert/Symbol', () => { const T = Type.Symbol() it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.deepEqual(R, '3.14') + Assert.isEqual(R, '3.14') }) it('Should convert from number 2', () => { const R = Value.Convert(T, 3) - Assert.deepEqual(R, '3') + Assert.isEqual(R, '3') }) }) diff --git a/test/runtime/value/convert/tuple.ts b/test/runtime/value/convert/tuple.ts index eafc01c42..6047f3fc1 100644 --- a/test/runtime/value/convert/tuple.ts +++ b/test/runtime/value/convert/tuple.ts @@ -6,16 +6,16 @@ describe('value/convert/Tuple', () => { it('Should convert from Array 1', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1', 'true']) - Assert.deepEqual(R, [1, true]) + Assert.isEqual(R, [1, true]) }) it('Should convert from Array 2', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1']) - Assert.deepEqual(R, [1]) + Assert.isEqual(R, [1]) }) it('Should convert from Array 3', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1', '2', '3']) - Assert.deepEqual(R, [1, 2, '3']) + Assert.isEqual(R, [1, 2, '3']) }) }) diff --git a/test/runtime/value/convert/undefined.ts b/test/runtime/value/convert/undefined.ts index 130f4177e..90e9201a1 100644 --- a/test/runtime/value/convert/undefined.ts +++ b/test/runtime/value/convert/undefined.ts @@ -6,10 +6,10 @@ describe('value/convert/Undefined', () => { const T = Type.Undefined() it('Should convert from string 1', () => { const R = Value.Convert(T, 'undefined') - Assert.deepEqual(R, undefined) + Assert.isEqual(R, undefined) }) it('Should convert from string 2', () => { const R = Value.Convert(T, 'hello') - Assert.deepEqual(R, 'hello') + Assert.isEqual(R, 'hello') }) }) diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index 7fd75a17a..96c79b4dd 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -10,15 +10,15 @@ describe('value/convert/Union', () => { const V1 = Value.Convert(T, { x: '42' }) const V2 = Value.Convert(T, { x: 'null' }) const V3 = Value.Convert(T, { x: 'hello' }) - Assert.deepEqual(V1, { x: 42 }) - Assert.deepEqual(V2, { x: null }) - Assert.deepEqual(V3, { x: 'hello' }) + Assert.isEqual(V1, { x: 42 }) + Assert.isEqual(V2, { x: null }) + Assert.isEqual(V3, { x: 'hello' }) }) it('Should convert first variant in ambiguous conversion', () => { const T = Type.Object({ x: Type.Union([Type.Boolean(), Type.Number()]), }) const V1 = Value.Convert(T, { x: '1' }) - Assert.deepEqual(V1, { x: true }) + Assert.isEqual(V1, { x: true }) }) }) diff --git a/test/runtime/value/convert/unknown.ts b/test/runtime/value/convert/unknown.ts index 0b8cea1f9..7f43ce7f5 100644 --- a/test/runtime/value/convert/unknown.ts +++ b/test/runtime/value/convert/unknown.ts @@ -7,36 +7,36 @@ describe('value/convert/Unknown', () => { it('Should convert null', () => { const V = null const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert undefined', () => { const V = undefined const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert string', () => { const V = 'hello' const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert number', () => { const V = 42 const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert boolean', () => { const V = true const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert object', () => { const V = { x: 1 } const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) it('Should convert array', () => { const V = [1, 2, 3] const R = Value.Convert(T, V) - Assert.deepEqual(R, V) + Assert.isEqual(R, V) }) }) diff --git a/test/runtime/value/create/any.ts b/test/runtime/value/create/any.ts index bbd59d47d..3dc08a4af 100644 --- a/test/runtime/value/create/any.ts +++ b/test/runtime/value/create/any.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Any', () => { it('Should create value', () => { const T = Type.Any() - Assert.deepEqual(Value.Create(T), {}) + Assert.isEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Any({ default: 1 }) - Assert.deepEqual(Value.Create(T), 1) + Assert.isEqual(Value.Create(T), 1) }) }) diff --git a/test/runtime/value/create/array.ts b/test/runtime/value/create/array.ts index 1d97755a1..e61a1a4f3 100644 --- a/test/runtime/value/create/array.ts +++ b/test/runtime/value/create/array.ts @@ -5,14 +5,14 @@ import { Assert } from '../../assert/index' describe('value/create/Any', () => { it('Should create value', () => { const T = Type.Array(Type.String()) - Assert.deepEqual(Value.Create(T), []) + Assert.isEqual(Value.Create(T), []) }) it('Should create default', () => { const T = Type.Array(Type.String(), { default: ['1'] }) - Assert.deepEqual(Value.Create(T), ['1']) + Assert.isEqual(Value.Create(T), ['1']) }) it('Should create with minItems', () => { const T = Type.Array(Type.String(), { minItems: 4 }) - Assert.deepEqual(Value.Create(T), ['', '', '', '']) + Assert.isEqual(Value.Create(T), ['', '', '', '']) }) }) diff --git a/test/runtime/value/create/bigint.ts b/test/runtime/value/create/bigint.ts index 616058107..2409d73ec 100644 --- a/test/runtime/value/create/bigint.ts +++ b/test/runtime/value/create/bigint.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/BigInt', () => { it('Should create value', () => { const T = Type.BigInt() - Assert.deepEqual(Value.Create(T), BigInt(0)) + Assert.isEqual(Value.Create(T), BigInt(0)) }) it('Should create default', () => { const T = Type.BigInt({ default: true }) - Assert.deepEqual(Value.Create(T), true) + Assert.isEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/boolean.ts b/test/runtime/value/create/boolean.ts index 9315f20b6..853dd9ac4 100644 --- a/test/runtime/value/create/boolean.ts +++ b/test/runtime/value/create/boolean.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Boolean', () => { it('Should create value', () => { const T = Type.Boolean() - Assert.deepEqual(Value.Create(T), false) + Assert.isEqual(Value.Create(T), false) }) it('Should create default', () => { const T = Type.Boolean({ default: true }) - Assert.deepEqual(Value.Create(T), true) + Assert.isEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/composite.ts b/test/runtime/value/create/composite.ts index 96995304a..40421aa6e 100644 --- a/test/runtime/value/create/composite.ts +++ b/test/runtime/value/create/composite.ts @@ -15,7 +15,7 @@ describe('value/create/Composite', () => { c: Type.Number(), }) const T = Type.Composite([A, B]) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 0, y: 0, z: 0, @@ -36,7 +36,7 @@ describe('value/create/Composite', () => { c: Type.Number({ default: 6 }), }) const T = Type.Composite([A, B]) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 1, y: 2, z: 3, @@ -57,7 +57,7 @@ describe('value/create/Composite', () => { c: Type.Optional(Type.Number()), }) const T = Type.Composite([A, B]) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 0, y: 0, z: 0, diff --git a/test/runtime/value/create/constructor.ts b/test/runtime/value/create/constructor.ts index b4c9b7701..7bf4d7777 100644 --- a/test/runtime/value/create/constructor.ts +++ b/test/runtime/value/create/constructor.ts @@ -13,7 +13,7 @@ describe('value/create/Constructor', () => { const C = Value.Create(T) const I = new C() const R = I.test() - Assert.deepEqual(R, 123) + Assert.isEqual(R, 123) }) it('Should create default', () => { @@ -33,6 +33,6 @@ describe('value/create/Constructor', () => { const C = Value.Create(T) const I = new C() const R = I.test() - Assert.deepEqual(R, 321) + Assert.isEqual(R, 321) }) }) diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts index 1a3ca5424..3c03cb110 100644 --- a/test/runtime/value/create/custom.ts +++ b/test/runtime/value/create/custom.ts @@ -6,7 +6,7 @@ describe('value/create/Custom', () => { it('Should create custom value with default', () => { TypeRegistry.Set('CustomCreate1', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) it('Should throw when no default value is specified', () => { diff --git a/test/runtime/value/create/enum.ts b/test/runtime/value/create/enum.ts index b6dcb55ca..6a2cf1aa6 100644 --- a/test/runtime/value/create/enum.ts +++ b/test/runtime/value/create/enum.ts @@ -9,7 +9,7 @@ describe('value/create/Boolean', () => { B, } const T = Type.Enum(Foo) - Assert.deepEqual(Value.Create(T), Foo.A) + Assert.isEqual(Value.Create(T), Foo.A) }) it('Should create default', () => { enum Foo { @@ -17,6 +17,6 @@ describe('value/create/Boolean', () => { B, } const T = Type.Enum(Foo, { default: Foo.B }) - Assert.deepEqual(Value.Create(T), Foo.B) + Assert.isEqual(Value.Create(T), Foo.B) }) }) diff --git a/test/runtime/value/create/function.ts b/test/runtime/value/create/function.ts index 6315bd203..e69514260 100644 --- a/test/runtime/value/create/function.ts +++ b/test/runtime/value/create/function.ts @@ -7,13 +7,13 @@ describe('value/create/Function', () => { const T = Type.Function([], Type.Number({ default: 123 })) const F = Value.Create(T) const R = F() - Assert.deepEqual(R, 123) + Assert.isEqual(R, 123) }) it('Should create default', () => { const T = Type.Function([], Type.Number({ default: 123 }), { default: () => 321 }) const F = Value.Create(T) const R = F() - Assert.deepEqual(R, 321) + Assert.isEqual(R, 321) }) }) diff --git a/test/runtime/value/create/integer.ts b/test/runtime/value/create/integer.ts index 3643a1f54..8354172a0 100644 --- a/test/runtime/value/create/integer.ts +++ b/test/runtime/value/create/integer.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Integer', () => { it('Should create value', () => { const T = Type.Integer() - Assert.deepEqual(Value.Create(T), 0) + Assert.isEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.Integer({ default: 7 }) - Assert.deepEqual(Value.Create(T), 7) + Assert.isEqual(Value.Create(T), 7) }) }) diff --git a/test/runtime/value/create/intersect.ts b/test/runtime/value/create/intersect.ts index f007480ae..195f2364d 100644 --- a/test/runtime/value/create/intersect.ts +++ b/test/runtime/value/create/intersect.ts @@ -7,22 +7,22 @@ describe('value/create/Intersect', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) const R = Value.Create(T) - Assert.deepEqual(R, { x: 0, y: 0 }) + Assert.isEqual(R, { x: 0, y: 0 }) }) it('Should create value with default', () => { const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 100 }) }), Type.Object({ y: Type.Number({ default: 200 }) })]) const R = Value.Create(T) - Assert.deepEqual(R, { x: 100, y: 200 }) + Assert.isEqual(R, { x: 100, y: 200 }) }) it('Should create for overlapping intersection', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number(), y: Type.Number() })]) const R = Value.Create(T) - Assert.deepEqual(R, { x: 0, y: 0 }) + Assert.isEqual(R, { x: 0, y: 0 }) }) it('Should create with last intersected overlapping default', () => { const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 1 }) }), Type.Object({ x: Type.Number({ default: 2 }), y: Type.Number() })]) const R = Value.Create(T) - Assert.deepEqual(R, { x: 2, y: 0 }) + Assert.isEqual(R, { x: 2, y: 0 }) }) it('Should throw for non-constructable intersection 1', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) @@ -35,7 +35,7 @@ describe('value/create/Intersect', () => { it('Should not throw for non-constructable intersection with default', () => { const T = Type.Intersect([Type.String(), Type.Number()], { default: 'hello' }) const R = Value.Create(T) - Assert.deepEqual(R, 'hello') + Assert.isEqual(R, 'hello') }) it('Should create from nested intersection', () => { const T = Type.Intersect([ @@ -52,11 +52,11 @@ describe('value/create/Intersect', () => { ]), ]) const R = Value.Create(T) - Assert.deepEqual(R, { x: 1, y: 2, z: 3 }) + Assert.isEqual(R, { x: 1, y: 2, z: 3 }) }) it('Should create non varying primitive', () => { const T = Type.Intersect([Type.Number(), Type.Number(), Type.Number()]) const R = Value.Create(T) - Assert.deepEqual(R, 0) + Assert.isEqual(R, 0) }) }) diff --git a/test/runtime/value/create/keyof.ts b/test/runtime/value/create/keyof.ts index 6b498bb15..4f8aca015 100644 --- a/test/runtime/value/create/keyof.ts +++ b/test/runtime/value/create/keyof.ts @@ -10,7 +10,7 @@ describe('value/create/KeyOf', () => { y: Type.Number(), }), ) - Assert.deepEqual(Value.Create(T), 'x') + Assert.isEqual(Value.Create(T), 'x') }) it('Should create default', () => { const T = Type.KeyOf( @@ -20,6 +20,6 @@ describe('value/create/KeyOf', () => { }), { default: 'y' }, ) - Assert.deepEqual(Value.Create(T), 'y') + Assert.isEqual(Value.Create(T), 'y') }) }) diff --git a/test/runtime/value/create/literal.ts b/test/runtime/value/create/literal.ts index d2913ab7f..8bdb7db2c 100644 --- a/test/runtime/value/create/literal.ts +++ b/test/runtime/value/create/literal.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('value/create/Literal', () => { it('Should create literal string', () => { const T = Type.Literal('hello') - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) it('Should create literal number', () => { const T = Type.Literal(1) - Assert.deepEqual(Value.Create(T), 1) + Assert.isEqual(Value.Create(T), 1) }) it('Should create literal boolean', () => { const T = Type.Literal(true) - Assert.deepEqual(Value.Create(T), true) + Assert.isEqual(Value.Create(T), true) }) it('Should create literal from default value', () => { const T = Type.Literal(true, { default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/not.ts b/test/runtime/value/create/not.ts index 132d4eb9e..433e8552f 100644 --- a/test/runtime/value/create/not.ts +++ b/test/runtime/value/create/not.ts @@ -6,16 +6,16 @@ describe('value/create/Not', () => { it('Should create value', () => { const T = Type.Not(Type.String(), Type.Number()) const R = Value.Create(T) - Assert.equal(R, 0) + Assert.isEqual(R, 0) }) it('Should create value with default inner', () => { const T = Type.Not(Type.String(), Type.Number({ default: 100 })) const R = Value.Create(T) - Assert.equal(R, 100) + Assert.isEqual(R, 100) }) it('Should create value with default outer', () => { const T = Type.Not(Type.String(), Type.Number(), { default: 100 }) const R = Value.Create(T) - Assert.equal(R, 100) + Assert.isEqual(R, 100) }) }) diff --git a/test/runtime/value/create/null.ts b/test/runtime/value/create/null.ts index 45c42fc86..3fc129a50 100644 --- a/test/runtime/value/create/null.ts +++ b/test/runtime/value/create/null.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Null', () => { it('Should create value', () => { const T = Type.Null() - Assert.deepEqual(Value.Create(T), null) + Assert.isEqual(Value.Create(T), null) }) it('Should create null from default value', () => { const T = Type.Null({ default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/number.ts b/test/runtime/value/create/number.ts index 7be03f37d..db68e95a5 100644 --- a/test/runtime/value/create/number.ts +++ b/test/runtime/value/create/number.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Number', () => { it('Should create value', () => { const T = Type.Number() - Assert.deepEqual(Value.Create(T), 0) + Assert.isEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.Number({ default: 7 }) - Assert.deepEqual(Value.Create(T), 7) + Assert.isEqual(Value.Create(T), 7) }) }) diff --git a/test/runtime/value/create/object.ts b/test/runtime/value/create/object.ts index 50b41ee3b..8f0ff10b4 100644 --- a/test/runtime/value/create/object.ts +++ b/test/runtime/value/create/object.ts @@ -9,7 +9,7 @@ describe('value/create/Object', () => { y: Type.Number(), z: Type.Number(), }) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 0, y: 0, z: 0, @@ -22,7 +22,7 @@ describe('value/create/Object', () => { y: Type.Optional(Type.Number()), z: Type.Optional(Type.Number()), }) - Assert.deepEqual(Value.Create(T), {}) + Assert.isEqual(Value.Create(T), {}) }) it('Should create default with default properties', () => { @@ -31,7 +31,7 @@ describe('value/create/Object', () => { y: Type.Number({ default: 2 }), z: Type.Number({ default: 3 }), }) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 1, y: 2, z: 3, @@ -49,7 +49,7 @@ describe('value/create/Object', () => { z: Type.Number(), }), }) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 0, y: 0, z: 0, @@ -66,7 +66,7 @@ describe('value/create/Object', () => { }, { default: { x: 1, y: 2, z: 3 } }, ) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { x: 1, y: 2, z: 3, diff --git a/test/runtime/value/create/record.ts b/test/runtime/value/create/record.ts index c19d8555f..05ebfd482 100644 --- a/test/runtime/value/create/record.ts +++ b/test/runtime/value/create/record.ts @@ -5,7 +5,7 @@ import { Assert } from '../../assert/index' describe('value/create/Record', () => { it('Should create value', () => { const T = Type.Record(Type.String(), Type.Object({})) - Assert.deepEqual(Value.Create(T), {}) + Assert.isEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Record(Type.String(), Type.Object({}), { @@ -13,6 +13,6 @@ describe('value/create/Record', () => { x: {}, }, }) - Assert.deepEqual(Value.Create(T), { x: {} }) + Assert.isEqual(Value.Create(T), { x: {} }) }) }) diff --git a/test/runtime/value/create/recursive.ts b/test/runtime/value/create/recursive.ts index 7f69c6b56..b12dd0b0c 100644 --- a/test/runtime/value/create/recursive.ts +++ b/test/runtime/value/create/recursive.ts @@ -10,7 +10,7 @@ describe('value/create/Recursive', () => { nodes: Type.Array(This), }), ) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { id: '', nodes: [], }) @@ -25,6 +25,6 @@ describe('value/create/Recursive', () => { }), { default: 7 }, ) - Assert.deepEqual(Value.Create(T), 7) + Assert.isEqual(Value.Create(T), 7) }) }) diff --git a/test/runtime/value/create/ref.ts b/test/runtime/value/create/ref.ts index 621236c98..16c7e4abd 100644 --- a/test/runtime/value/create/ref.ts +++ b/test/runtime/value/create/ref.ts @@ -25,6 +25,6 @@ describe('value/create/Ref', () => { { $id: 'T', default: 'target' }, ) const R = Type.Ref(T, { default: 'override' }) - Assert.deepEqual(Value.Create(R), 'override') // terminated at R default value + Assert.isEqual(Value.Create(R), 'override') // terminated at R default value }) }) diff --git a/test/runtime/value/create/regex.ts b/test/runtime/value/create/regex.ts index 78d535a2d..8afa9fb8e 100644 --- a/test/runtime/value/create/regex.ts +++ b/test/runtime/value/create/regex.ts @@ -11,6 +11,6 @@ describe('value/create/RegEx', () => { }) it('Should create default', () => { const T = Type.RegEx(/foo/, { default: 'foo' }) - Assert.deepEqual(Value.Create(T), 'foo') + Assert.isEqual(Value.Create(T), 'foo') }) }) diff --git a/test/runtime/value/create/string.ts b/test/runtime/value/create/string.ts index d5a7bcd77..41a0035ef 100644 --- a/test/runtime/value/create/string.ts +++ b/test/runtime/value/create/string.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/String', () => { it('Should create value', () => { const T = Type.String() - Assert.deepEqual(Value.Create(T), 0) + Assert.isEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.String({ default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/symbol.ts b/test/runtime/value/create/symbol.ts index da3ba1c3a..2c78809c2 100644 --- a/test/runtime/value/create/symbol.ts +++ b/test/runtime/value/create/symbol.ts @@ -6,10 +6,10 @@ describe('value/create/Symbol', () => { it('Should create value', () => { const T = Type.Symbol() const V = Value.Create(T) - Assert.deepEqual(typeof V, 'symbol') + Assert.isEqual(typeof V, 'symbol') }) it('Should create default', () => { const T = Type.Symbol({ default: true }) - Assert.deepEqual(Value.Create(T), true) + Assert.isEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/template-literal.ts b/test/runtime/value/create/template-literal.ts index 4659b77f8..7103f1272 100644 --- a/test/runtime/value/create/template-literal.ts +++ b/test/runtime/value/create/template-literal.ts @@ -6,22 +6,22 @@ describe('value/create/TemplateLiteral', () => { it('Should create pattern 1', () => { const T = Type.TemplateLiteral([Type.Literal('A')]) const V = Value.Create(T) - Assert.deepEqual(V, 'A') + Assert.isEqual(V, 'A') }) it('Should create pattern 2', () => { const T = Type.TemplateLiteral([Type.Literal('A'), Type.Literal('B')]) const V = Value.Create(T) - Assert.deepEqual(V, 'AB') + Assert.isEqual(V, 'AB') }) it('Should create pattern 3 (first only)', () => { const T = Type.TemplateLiteral([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) const V = Value.Create(T) - Assert.deepEqual(V, 'AB') + Assert.isEqual(V, 'AB') }) it('Should create pattern 4 (first only)', () => { const T = Type.TemplateLiteral([Type.Boolean()]) const V = Value.Create(T) - Assert.deepEqual(V, 'true') + Assert.isEqual(V, 'true') }) it('Should throw on infinite pattern', () => { const T = Type.TemplateLiteral([Type.Number()]) @@ -30,6 +30,6 @@ describe('value/create/TemplateLiteral', () => { it('Should create on infinite pattern with default', () => { const T = Type.TemplateLiteral([Type.Number()], { default: 42 }) const V = Value.Create(T) - Assert.deepEqual(V, 42) + Assert.isEqual(V, 42) }) }) diff --git a/test/runtime/value/create/tuple.ts b/test/runtime/value/create/tuple.ts index f2c91ccdb..2edb53bd6 100644 --- a/test/runtime/value/create/tuple.ts +++ b/test/runtime/value/create/tuple.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('value/create/Tuple', () => { it('Should create value', () => { const T = Type.Tuple([Type.Number(), Type.String()]) - Assert.deepEqual(Value.Create(T), [0, '']) + Assert.isEqual(Value.Create(T), [0, '']) }) it('Should create default', () => { const T = Type.Tuple([Type.Number(), Type.String()], { default: [7, 'hello'] }) - Assert.deepEqual(Value.Create(T), [7, 'hello']) + Assert.isEqual(Value.Create(T), [7, 'hello']) }) it('Should create default elements', () => { const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })]) - Assert.deepEqual(Value.Create(T), [7, 'hello']) + Assert.isEqual(Value.Create(T), [7, 'hello']) }) it('Should create default by overriding elements', () => { const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })], { default: [32, 'world'] }) - Assert.deepEqual(Value.Create(T), [32, 'world']) + Assert.isEqual(Value.Create(T), [32, 'world']) }) }) diff --git a/test/runtime/value/create/uint8array.ts b/test/runtime/value/create/uint8array.ts index 6effa6d4c..1284f29f7 100644 --- a/test/runtime/value/create/uint8array.ts +++ b/test/runtime/value/create/uint8array.ts @@ -7,22 +7,22 @@ describe('value/create/Uint8Array', () => { const T = Type.Uint8Array() const value = Value.Create(T) Assert.isInstanceOf(value, Uint8Array) - Assert.equal(value.length, 0) + Assert.isEqual(value.length, 0) }) it('Should create default', () => { const T = Type.Uint8Array({ default: new Uint8Array([0, 1, 2, 3]) }) const value = Value.Create(T) Assert.isInstanceOf(value, Uint8Array) - Assert.equal(value.length, 4) - Assert.deepEqual([value[0], value[1], value[2], value[3]], [0, 1, 2, 3]) + Assert.isEqual(value.length, 4) + Assert.isEqual([value[0], value[1], value[2], value[3]], [0, 1, 2, 3]) }) it('Should create with minByteLength', () => { const T = Type.Uint8Array({ minByteLength: 4 }) const value = Value.Create(T) Assert.isInstanceOf(value, Uint8Array) - Assert.equal(value.length, 4) - Assert.deepEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) + Assert.isEqual(value.length, 4) + Assert.isEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) }) }) diff --git a/test/runtime/value/create/undefined.ts b/test/runtime/value/create/undefined.ts index b684204c6..d9e3ed509 100644 --- a/test/runtime/value/create/undefined.ts +++ b/test/runtime/value/create/undefined.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Undefined', () => { it('Should create value', () => { const T = Type.Undefined() - Assert.deepEqual(Value.Create(T), undefined) + Assert.isEqual(Value.Create(T), undefined) }) it('Should create value from default value', () => { const T = Type.Undefined({ default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/union.ts b/test/runtime/value/create/union.ts index 63e14202f..1d3c79677 100644 --- a/test/runtime/value/create/union.ts +++ b/test/runtime/value/create/union.ts @@ -17,7 +17,7 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.deepEqual(Value.Create(T), { + Assert.isEqual(Value.Create(T), { type: 'A', x: 0, y: 0, @@ -34,7 +34,7 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.deepEqual(Value.Create(T), null) + Assert.isEqual(Value.Create(T), null) }) it('Should create union Array', () => { @@ -46,6 +46,6 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.deepEqual(Value.Create(T), []) + Assert.isEqual(Value.Create(T), []) }) }) diff --git a/test/runtime/value/create/unknown.ts b/test/runtime/value/create/unknown.ts index d86946c6c..d75a062c8 100644 --- a/test/runtime/value/create/unknown.ts +++ b/test/runtime/value/create/unknown.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Unknown', () => { it('Should create value', () => { const T = Type.Unknown() - Assert.deepEqual(Value.Create(T), {}) + Assert.isEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Unknown({ default: 1 }) - Assert.deepEqual(Value.Create(T), 1) + Assert.isEqual(Value.Create(T), 1) }) }) diff --git a/test/runtime/value/create/void.ts b/test/runtime/value/create/void.ts index b74886656..56c8a9a66 100644 --- a/test/runtime/value/create/void.ts +++ b/test/runtime/value/create/void.ts @@ -5,11 +5,11 @@ import { Assert } from '../../assert/index' describe('value/create/Void', () => { it('Should create value', () => { const T = Type.Void() - Assert.deepEqual(Value.Create(T), null) + Assert.isEqual(Value.Create(T), null) }) it('Should create value from default value', () => { const T = Type.Void({ default: 'hello' }) - Assert.deepEqual(Value.Create(T), 'hello') + Assert.isEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/delta/diff.ts b/test/runtime/value/delta/diff.ts index 5a86fccb4..bda3d0a1d 100644 --- a/test/runtime/value/delta/diff.ts +++ b/test/runtime/value/delta/diff.ts @@ -26,35 +26,35 @@ describe('value/delta/Diff', () => { const B = null const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL undefined to undefined', () => { const A = undefined const B = undefined const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL string to string', () => { const A = 'hello' const B = 'hello' const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL number to number', () => { const A = 1 const B = 1 const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL boolean to boolean', () => { const A = true const B = true const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL symbol to symbol', () => { const S = Symbol('A') @@ -62,21 +62,21 @@ describe('value/delta/Diff', () => { const B = S const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL object to object', () => { const A = { x: 1, y: 2, z: 3 } const B = { x: 1, y: 2, z: 3 } const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NULL array to array', () => { const A = [1, 2, 3] const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- @@ -88,7 +88,7 @@ describe('value/delta/Diff', () => { const B = null const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to undefined', () => { @@ -96,49 +96,49 @@ describe('value/delta/Diff', () => { const B = undefined const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to number', () => { const A = null const B = 1 const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to boolean', () => { const A = null const B = true const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to string', () => { const A = null const B = 'hello' const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to symbol', () => { const A = null const B = Symbol('A') const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to object', () => { const A = null const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff TYPE change null to array', () => { const A = null const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- // Value Change Root @@ -149,7 +149,7 @@ describe('value/delta/Diff', () => { const B = 2 const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff VALUE change boolean', () => { @@ -157,7 +157,7 @@ describe('value/delta/Diff', () => { const B = true const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff VALUE change string', () => { @@ -165,7 +165,7 @@ describe('value/delta/Diff', () => { const B = 'world' const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff VALUE change symbol', () => { @@ -173,7 +173,7 @@ describe('value/delta/Diff', () => { const B = Symbol('B') const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- @@ -185,7 +185,7 @@ describe('value/delta/Diff', () => { const B = [1, 2, 3, 9] const D = Value.Diff(A, B) const E = [Update('/3', 9)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT push', () => { @@ -193,7 +193,7 @@ describe('value/delta/Diff', () => { const B = [1, 2, 3, 4, 5] const D = Value.Diff(A, B) const E = [Insert('/4', 5)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT push twice', () => { @@ -201,7 +201,7 @@ describe('value/delta/Diff', () => { const B = [1, 2, 3, 4, 5, 6] const D = Value.Diff(A, B) const E = [Insert('/4', 5), Insert('/5', 6)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT pop', () => { @@ -209,7 +209,7 @@ describe('value/delta/Diff', () => { const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [Delete('/3')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT pop twice', () => { @@ -217,7 +217,7 @@ describe('value/delta/Diff', () => { const B = [1, 2] const D = Value.Diff(A, B) const E = [Delete('/3'), Delete('/2')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT unshift', () => { @@ -225,7 +225,7 @@ describe('value/delta/Diff', () => { const B = [2, 3, 4] const D = Value.Diff(A, B) const E = [Update('/0', 2), Update('/1', 3), Update('/2', 4), Delete('/3')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff ELEMENT unshift twice', () => { @@ -233,7 +233,7 @@ describe('value/delta/Diff', () => { const B = [3, 4] const D = Value.Diff(A, B) const E = [Update('/0', 3), Update('/1', 4), Delete('/3'), Delete('/2')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- @@ -245,35 +245,35 @@ describe('value/delta/Diff', () => { const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const E = [Insert('/z', 1)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff PROPERTY delete', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1 } const D = Value.Diff(A, B) const E = [Delete('/z')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff PROPERTY update', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1, z: 2 } const D = Value.Diff(A, B) const E = [Update('/z', 2)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff PROPERTY all values', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 2, y: 2, z: 2 } const D = Value.Diff(A, B) const E = [Update('/x', 2), Update('/y', 2), Update('/z', 2)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff PROPERTY all delete, all insert', () => { const A = { x: 1, y: 1, z: 1 } const B = { a: 2, b: 2, c: 2 } const D = Value.Diff(A, B) const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff PROPERTY update, insert and delete order preserved', () => { @@ -281,7 +281,7 @@ describe('value/delta/Diff', () => { const B = { a: 2, b: 2, c: 2, w: 2 } const D = Value.Diff(A, B) const E = [Update('/w', 2), Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- @@ -293,7 +293,7 @@ describe('value/delta/Diff', () => { const B = { v: { x: 1, y: 1, z: 1 } } const D = Value.Diff(A, B) const E = [Update('/v', B.v)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED OBJECT diff value change update', () => { @@ -301,7 +301,7 @@ describe('value/delta/Diff', () => { const B = { v: 2 } const D = Value.Diff(A, B) const E = [Update('/v', B.v)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED OBJECT diff partial property update', () => { @@ -309,7 +309,7 @@ describe('value/delta/Diff', () => { const B = { v: { x: 2, y: 2, z: 2 } } const D = Value.Diff(A, B) const E = [Update('/v/x', B.v.x), Update('/v/y', B.v.y), Update('/v/z', B.v.z)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED OBJECT diff partial property insert', () => { @@ -317,7 +317,7 @@ describe('value/delta/Diff', () => { const B = { v: { x: 1, y: 1, z: 1, w: 1 } } const D = Value.Diff(A, B) const E = [Insert('/v/w', B.v.w)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED OBJECT diff partial property delete', () => { @@ -325,7 +325,7 @@ describe('value/delta/Diff', () => { const B = { v: { x: 1, y: 1 } } const D = Value.Diff(A, B) const E = [Delete('/v/z')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED OBJECT ordered diff - update, insert and delete', () => { @@ -333,7 +333,7 @@ describe('value/delta/Diff', () => { const B = { v: { x: 2, w: 2 } } const D = Value.Diff(A, B) const E = [Update('/v/x', B.v.x), Insert('/v/w', B.v.w), Delete('/v/y')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) // ---------------------------------------------------- @@ -345,7 +345,7 @@ describe('value/delta/Diff', () => { const B = [{ v: { x: 1, y: 1, z: 1 } }] const D = Value.Diff(A, B) const E = [Update('/0/v', B[0].v)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED ARRAY object diff value change update', () => { @@ -353,21 +353,21 @@ describe('value/delta/Diff', () => { const B = [{ v: 2 }] const D = Value.Diff(A, B) const E = [Update('/0/v', B[0].v)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED ARRAY object diff partial property update', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 2, y: 2, z: 2 } }] const D = Value.Diff(A, B) const E = [Update('/0/v/x', B[0].v.x), Update('/0/v/y', B[0].v.y), Update('/0/v/z', B[0].v.z)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED ARRAY object diff partial property insert', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] const D = Value.Diff(A, B) const E = [Insert('/0/v/w', B[0].v.w)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED ARRAY object diff partial property delete', () => { @@ -375,7 +375,7 @@ describe('value/delta/Diff', () => { const B = [{ v: { x: 1, y: 1 } }] const D = Value.Diff(A, B) const E = [Delete('/0/v/z')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff NESTED ARRAY update, insert and delete order preserved', () => { @@ -383,7 +383,7 @@ describe('value/delta/Diff', () => { const B = [{ v: { x: 2, w: 2 } }] const D = Value.Diff(A, B) const E = [Update('/0/v/x', B[0].v.x), Insert('/0/v/w', B[0].v.w), Delete('/0/v/y')] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should throw if attempting to diff a current value with symbol key', () => { @@ -403,7 +403,7 @@ describe('value/delta/Diff', () => { const B = new Uint8Array([0, 9, 2, 9]) const D = Value.Diff(A, B) const E = [Update('/1', 9), Update('/3', 9)] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff a Uint8Array (less than requires full update)', () => { @@ -411,7 +411,7 @@ describe('value/delta/Diff', () => { const B = new Uint8Array([0, 9, 2]) const D = Value.Diff(A, B) const E = [Update('', new Uint8Array([0, 9, 2]))] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) it('Should diff a Uint8Array (greater than requires full update)', () => { @@ -419,6 +419,6 @@ describe('value/delta/Diff', () => { const B = new Uint8Array([0, 9, 2, 3, 4]) const D = Value.Diff(A, B) const E = [Update('', new Uint8Array([0, 9, 2, 3, 4]))] - Assert.deepEqual(D, E) + Assert.isEqual(D, E) }) }) diff --git a/test/runtime/value/delta/patch.ts b/test/runtime/value/delta/patch.ts index 62a0c50e1..cb9ac37f3 100644 --- a/test/runtime/value/delta/patch.ts +++ b/test/runtime/value/delta/patch.ts @@ -10,35 +10,35 @@ describe('value/delta/Patch', () => { const B = null const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL undefined to undefined', () => { const A = undefined const B = undefined const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL string to string', () => { const A = 'hello' const B = 'hello' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL number to number', () => { const A = 1 const B = 1 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL boolean to boolean', () => { const A = true const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL symbol to symbol', () => { const S = Symbol('A') @@ -46,7 +46,7 @@ describe('value/delta/Patch', () => { const B = S const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL object to object', () => { @@ -54,7 +54,7 @@ describe('value/delta/Patch', () => { const B = { x: 1, y: 2, z: 3 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NULL array to array', () => { @@ -62,7 +62,7 @@ describe('value/delta/Patch', () => { const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- @@ -74,7 +74,7 @@ describe('value/delta/Patch', () => { const B = null const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to undefined', () => { @@ -82,56 +82,56 @@ describe('value/delta/Patch', () => { const B = undefined const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to number', () => { const A = null const B = 1 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to boolean', () => { const A = null const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to string', () => { const A = null const B = 'hello' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to symbol', () => { const A = null const B = Symbol('A') const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to object', () => { const A = null const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change null to array', () => { const A = null const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change object to array', () => { const A = { x: 1, y: 2 } const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch TYPE change array to object', () => { @@ -139,7 +139,7 @@ describe('value/delta/Patch', () => { const B = { x: 1, y: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- @@ -151,7 +151,7 @@ describe('value/delta/Patch', () => { const B = 2 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch VALUE change boolean', () => { @@ -159,7 +159,7 @@ describe('value/delta/Patch', () => { const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch VALUE change string', () => { @@ -167,7 +167,7 @@ describe('value/delta/Patch', () => { const B = 'world' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch VALUE change symbol', () => { @@ -175,7 +175,7 @@ describe('value/delta/Patch', () => { const B = Symbol('B') const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- @@ -187,7 +187,7 @@ describe('value/delta/Patch', () => { const B = [1, 2, 3, 9] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT push', () => { @@ -195,7 +195,7 @@ describe('value/delta/Patch', () => { const B = [1, 2, 3, 4, 5] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT push twice', () => { @@ -203,7 +203,7 @@ describe('value/delta/Patch', () => { const B = [1, 2, 3, 4, 5, 6] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT pop', () => { @@ -211,7 +211,7 @@ describe('value/delta/Patch', () => { const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT pop twice', () => { @@ -219,7 +219,7 @@ describe('value/delta/Patch', () => { const B = [1, 2] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT unshift', () => { @@ -227,7 +227,7 @@ describe('value/delta/Patch', () => { const B = [2, 3, 4] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch ELEMENT unshift twice', () => { @@ -235,7 +235,7 @@ describe('value/delta/Patch', () => { const B = [3, 4] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- @@ -247,35 +247,35 @@ describe('value/delta/Patch', () => { const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch PROPERTY delete', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch PROPERTY update', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1, z: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch PROPERTY all values', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 2, y: 2, z: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch PROPERTY all delete, all insert', () => { const A = { x: 1, y: 1, z: 1 } const B = { a: 2, b: 2, c: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch PROPERTY update, insert and delete order preserved', () => { @@ -283,7 +283,7 @@ describe('value/delta/Patch', () => { const B = { a: 2, b: 2, c: 2, w: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- // Object Nested @@ -294,21 +294,21 @@ describe('value/delta/Patch', () => { const B = { v: { x: 1, y: 1, z: 1 } } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED OBJECT diff value change update', () => { const A = { v: 1 } const B = { v: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED OBJECT diff partial property update', () => { const A = { v: { x: 1, y: 1, z: 1 } } const B = { v: { x: 2, y: 2, z: 2 } } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED OBJECT update, insert and delete order preserved', () => { @@ -316,7 +316,7 @@ describe('value/delta/Patch', () => { const B = { v: { x: 2, w: 2 } } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- @@ -328,7 +328,7 @@ describe('value/delta/Patch', () => { const B = [{ v: { x: 1, y: 1, z: 1 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED ARRAY object diff value change update', () => { @@ -336,21 +336,21 @@ describe('value/delta/Patch', () => { const B = [{ v: 2 }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED ARRAY object diff partial property update', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 2, y: 2, z: 2 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED ARRAY object diff partial property insert', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED ARRAY object diff partial property delete', () => { @@ -358,7 +358,7 @@ describe('value/delta/Patch', () => { const B = [{ v: { x: 1, y: 1 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch NESTED ARRAY object ordered diff - update, insert and delete', () => { @@ -366,7 +366,7 @@ describe('value/delta/Patch', () => { const B = [{ v: { x: 2, w: 2 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch Uint8Array (same size)', () => { @@ -374,7 +374,7 @@ describe('value/delta/Patch', () => { const B = [{ v: new Uint8Array([0, 1, 2]) }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch Uint8Array (less than size)', () => { @@ -382,7 +382,7 @@ describe('value/delta/Patch', () => { const B = [{ v: new Uint8Array([0, 1]) }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) it('Should patch Uint8Array (greater than size)', () => { @@ -390,7 +390,7 @@ describe('value/delta/Patch', () => { const B = [{ v: new Uint8Array([0, 1, 2, 4]) }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) // ---------------------------------------------------- // Mega Values @@ -442,6 +442,6 @@ describe('value/delta/Patch', () => { ] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.deepEqual(B, P) + Assert.isEqual(B, P) }) }) diff --git a/test/runtime/value/equal/equal.ts b/test/runtime/value/equal/equal.ts index b02583e6e..dda3c6b18 100644 --- a/test/runtime/value/equal/equal.ts +++ b/test/runtime/value/equal/equal.ts @@ -4,108 +4,108 @@ import { Assert } from '../../assert/index' describe('value/equal/Equal', () => { it('Should equal null value', () => { const R = Value.Equal(null, null) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal null value', () => { const R = Value.Equal(null, 'null') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal undefined value', () => { const R = Value.Equal(undefined, undefined) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal undefined value', () => { const R = Value.Equal(undefined, 'undefined') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal symbol value', () => { const S1 = Symbol.for('test') const S2 = Symbol.for('test') const R = Value.Equal(S1, S2) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal symbol value', () => { const S1 = Symbol('test') const S2 = Symbol('test') const R = Value.Equal(S1, S2) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal string value', () => { const R = Value.Equal('hello', 'hello') - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal string value', () => { const R = Value.Equal('hello', 'world') - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal number value', () => { const R = Value.Equal(1, 1) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal number value', () => { const R = Value.Equal(1, 2) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal boolean value', () => { const R = Value.Equal(true, true) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal boolean value', () => { const R = Value.Equal(true, false) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal array value', () => { const R = Value.Equal([0, 1, 2], [0, 1, 2]) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal array value', () => { const R = Value.Equal([0, 1, 2], [0, 1, 3]) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should not equal array value with additional elements', () => { const R = Value.Equal([0, 1, 2], [0, 1, 2, 3]) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal object value', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3 }) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal object value', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 4 }) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should not equal object value with additional properties', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3, w: 1 }) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should equal typed array value', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 2])) - Assert.deepEqual(R, true) + Assert.isEqual(R, true) }) it('Should not equal typed array value', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 3])) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) it('Should not equal typed array value with varying type', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Int8Array([0, 1, 2])) - Assert.deepEqual(R, false) + Assert.isEqual(R, false) }) }) diff --git a/test/runtime/value/hash/hash.ts b/test/runtime/value/hash/hash.ts index 45b255342..eac4765e8 100644 --- a/test/runtime/value/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -3,39 +3,39 @@ import { Assert } from '../../assert/index' describe('value/hash/Hash', () => { it('Should hash number', () => { - Assert.equal('bigint', typeof ValueHash.Create(1)) + Assert.isEqual('bigint', typeof ValueHash.Create(1)) const A = ValueHash.Create(1) const B = ValueHash.Create(2) Assert.notEqual(A, B) }) it('Should hash string', () => { - Assert.equal('bigint', typeof ValueHash.Create('hello')) + Assert.isEqual('bigint', typeof ValueHash.Create('hello')) const A = ValueHash.Create('hello') const B = ValueHash.Create('world') Assert.notEqual(A, B) }) it('Should hash boolean', () => { - Assert.equal('bigint', typeof ValueHash.Create(true)) - Assert.equal('bigint', typeof ValueHash.Create(false)) + Assert.isEqual('bigint', typeof ValueHash.Create(true)) + Assert.isEqual('bigint', typeof ValueHash.Create(false)) const A = ValueHash.Create(true) const B = ValueHash.Create(false) Assert.notEqual(A, B) }) it('Should hash null', () => { - Assert.equal('bigint', typeof ValueHash.Create(null)) + Assert.isEqual('bigint', typeof ValueHash.Create(null)) const A = ValueHash.Create(null) const B = ValueHash.Create(undefined) Assert.notEqual(A, B) }) it('Should hash array', () => { - Assert.equal('bigint', typeof ValueHash.Create([0, 1, 2, 3])) + Assert.isEqual('bigint', typeof ValueHash.Create([0, 1, 2, 3])) const A = ValueHash.Create([0, 1, 2, 3]) const B = ValueHash.Create([0, 2, 2, 3]) Assert.notEqual(A, B) }) it('Should hash object 1', () => { // prettier-ignore - Assert.equal('bigint', typeof ValueHash.Create({ x: 1, y: 2 })) + Assert.isEqual('bigint', typeof ValueHash.Create({ x: 1, y: 2 })) const A = ValueHash.Create({ x: 1, y: 2 }) const B = ValueHash.Create({ x: 2, y: 2 }) Assert.notEqual(A, B) @@ -61,51 +61,51 @@ describe('value/hash/Hash', () => { Assert.notEqual(A, B) }) it('Should hash Date', () => { - Assert.equal('bigint', typeof ValueHash.Create(new Date())) + Assert.isEqual('bigint', typeof ValueHash.Create(new Date())) const A = ValueHash.Create(new Date(1)) const B = ValueHash.Create(new Date(2)) Assert.notEqual(A, B) }) it('Should hash Uint8Array', () => { - Assert.equal('bigint', typeof ValueHash.Create(new Uint8Array([0, 1, 2, 3]))) + Assert.isEqual('bigint', typeof ValueHash.Create(new Uint8Array([0, 1, 2, 3]))) const A = ValueHash.Create(new Uint8Array([0, 1, 2, 3])) const B = ValueHash.Create(new Uint8Array([0, 2, 2, 3])) Assert.notEqual(A, B) }) it('Should hash undefined', () => { - Assert.equal('bigint', typeof ValueHash.Create(undefined)) + Assert.isEqual('bigint', typeof ValueHash.Create(undefined)) const A = ValueHash.Create(undefined) const B = ValueHash.Create(null) Assert.notEqual(A, B) }) it('Should hash symbol 1', () => { - Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) const A = ValueHash.Create(Symbol(1)) const B = ValueHash.Create(Symbol()) Assert.notEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) const A = ValueHash.Create(Symbol(1)) const B = ValueHash.Create(Symbol(2)) Assert.notEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.equal('bigint', typeof ValueHash.Create(Symbol())) + Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) const A = ValueHash.Create(Symbol(1)) const B = ValueHash.Create(Symbol(1)) - Assert.equal(A, B) + Assert.isEqual(A, B) }) it('Should hash bigint 1', () => { - Assert.equal('bigint', typeof ValueHash.Create(BigInt(1))) + Assert.isEqual('bigint', typeof ValueHash.Create(BigInt(1))) const A = ValueHash.Create(BigInt(1)) const B = ValueHash.Create(BigInt(2)) Assert.notEqual(A, B) }) it('Should hash bigint 2', () => { - Assert.equal('bigint', typeof ValueHash.Create(BigInt(1))) + Assert.isEqual('bigint', typeof ValueHash.Create(BigInt(1))) const A = ValueHash.Create(BigInt(1)) const B = ValueHash.Create(BigInt(1)) - Assert.equal(A, B) + Assert.isEqual(A, B) }) }) diff --git a/test/runtime/value/mutate/mutate.ts b/test/runtime/value/mutate/mutate.ts index 1c4d81ef1..fe5b5d053 100644 --- a/test/runtime/value/mutate/mutate.ts +++ b/test/runtime/value/mutate/mutate.ts @@ -33,49 +33,49 @@ describe('value/mutate/Mutate', () => { const X = { y: Y } const A = { x: X } Value.Mutate(A, {}) - Assert.deepEqual(A, {}) + Assert.isEqual(A, {}) }) it('Should mutate 1', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: { z: 2 } } }) - Assert.equal(A.x.y.z, 2) - Assert.equal(A.x.y, Y) - Assert.equal(A.x, X) + Assert.isEqual(A.x.y.z, 2) + Assert.isEqual(A.x.y, Y) + Assert.isEqual(A.x, X) }) it('Should mutate 2', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: { z: [1, 2, 3] } } }) - Assert.deepEqual(A.x.y.z, [1, 2, 3]) - Assert.equal(A.x.y, Y) - Assert.equal(A.x, X) + Assert.isEqual(A.x.y.z, [1, 2, 3]) + Assert.isEqual(A.x.y, Y) + Assert.isEqual(A.x, X) }) it('Should mutate 3', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: {} }) - Assert.equal(A.x.y, undefined) - Assert.equal(A.x, X) + Assert.isEqual(A.x.y, undefined) + Assert.isEqual(A.x, X) }) it('Should mutate 4', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: 1 } }) - Assert.equal(A.x.y, 1) - Assert.equal(A.x, X) + Assert.isEqual(A.x.y, 1) + Assert.isEqual(A.x, X) }) it('Should mutate 5', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: [1, 2, 3] } }) - Assert.deepEqual(A.x.y, [1, 2, 3]) - Assert.equal(A.x, X) + Assert.isEqual(A.x.y, [1, 2, 3]) + Assert.isEqual(A.x, X) }) it('Should mutate 6', () => { const Y = { z: 1 } @@ -83,6 +83,6 @@ describe('value/mutate/Mutate', () => { const A = { x: X } Value.Mutate(A, { x: [1, 2, 3] }) Assert.notEqual(A.x, X) - Assert.deepEqual(A.x, [1, 2, 3]) + Assert.isEqual(A.x, [1, 2, 3]) }) }) diff --git a/test/runtime/value/pointer/pointer.ts b/test/runtime/value/pointer/pointer.ts index a3854baa6..6c1ee1652 100644 --- a/test/runtime/value/pointer/pointer.ts +++ b/test/runtime/value/pointer/pointer.ts @@ -8,102 +8,102 @@ describe('value/pointer/Pointer', () => { it('Should produce correct format #1', () => { const R = [...ValuePointer.Format('')] - Assert.deepEqual(R, []) + Assert.isEqual(R, []) }) it('Should produce correct format #2', () => { const R = [...ValuePointer.Format('a')] - Assert.deepEqual(R, ['a']) + Assert.isEqual(R, ['a']) }) it('Should produce correct format #3', () => { const R = [...ValuePointer.Format('/')] - Assert.deepEqual(R, ['']) + Assert.isEqual(R, ['']) }) it('Should produce correct format #4', () => { const R = [...ValuePointer.Format('/x')] - Assert.deepEqual(R, ['x']) + Assert.isEqual(R, ['x']) }) it('Should produce correct format #5', () => { const R = [...ValuePointer.Format('/x/')] - Assert.deepEqual(R, ['x', '']) + Assert.isEqual(R, ['x', '']) }) it('Should produce correct format #6', () => { const R = [...ValuePointer.Format('/x//')] - Assert.deepEqual(R, ['x', '', '']) + Assert.isEqual(R, ['x', '', '']) }) it('Should produce correct format #7', () => { const R = [...ValuePointer.Format('/x//y')] - Assert.deepEqual(R, ['x', '', 'y']) + Assert.isEqual(R, ['x', '', 'y']) }) it('Should produce correct format #8', () => { const R = [...ValuePointer.Format('/x//y/')] - Assert.deepEqual(R, ['x', '', 'y', '']) + Assert.isEqual(R, ['x', '', 'y', '']) }) it('Should produce correct format #9', () => { const R = [...ValuePointer.Format('/x/~0')] - Assert.deepEqual(R, ['x', '~']) + Assert.isEqual(R, ['x', '~']) }) it('Should produce correct format #10', () => { const R = [...ValuePointer.Format('/x/~1')] - Assert.deepEqual(R, ['x', '/']) + Assert.isEqual(R, ['x', '/']) }) it('Should produce correct format #11', () => { const R = [...ValuePointer.Format('/x/~0/')] - Assert.deepEqual(R, ['x', '~', '']) + Assert.isEqual(R, ['x', '~', '']) }) it('Should produce correct format #12', () => { const R = [...ValuePointer.Format('/x/~1/')] - Assert.deepEqual(R, ['x', '/', '']) + Assert.isEqual(R, ['x', '/', '']) }) it('Should produce correct format #13', () => { const R = [...ValuePointer.Format('/x/a~0b')] - Assert.deepEqual(R, ['x', 'a~b']) + Assert.isEqual(R, ['x', 'a~b']) }) it('Should produce correct format #14', () => { const R = [...ValuePointer.Format('/x/a~1b')] - Assert.deepEqual(R, ['x', 'a/b']) + Assert.isEqual(R, ['x', 'a/b']) }) it('Should produce correct format #15', () => { const R = [...ValuePointer.Format('/x/a~0b/')] - Assert.deepEqual(R, ['x', 'a~b', '']) + Assert.isEqual(R, ['x', 'a~b', '']) }) it('Should produce correct format #16', () => { const R = [...ValuePointer.Format('/x/a~1b/')] - Assert.deepEqual(R, ['x', 'a/b', '']) + Assert.isEqual(R, ['x', 'a/b', '']) }) it('Should produce correct format #17', () => { const R = [...ValuePointer.Format('/x/a~0b///y')] - Assert.deepEqual(R, ['x', 'a~b', '', '', 'y']) + Assert.isEqual(R, ['x', 'a~b', '', '', 'y']) }) it('Should produce correct format #18', () => { const R = [...ValuePointer.Format('/x/a~1b///y')] - Assert.deepEqual(R, ['x', 'a/b', '', '', 'y']) + Assert.isEqual(R, ['x', 'a/b', '', '', 'y']) }) it('Should produce correct format #19', () => { const R = [...ValuePointer.Format('/x/a~0b///')] - Assert.deepEqual(R, ['x', 'a~b', '', '', '']) + Assert.isEqual(R, ['x', 'a~b', '', '', '']) }) it('Should produce correct format #20', () => { const R = [...ValuePointer.Format('/x/a~1b///')] - Assert.deepEqual(R, ['x', 'a/b', '', '', '']) + Assert.isEqual(R, ['x', 'a/b', '', '', '']) }) //----------------------------------------------- @@ -112,59 +112,59 @@ describe('value/pointer/Pointer', () => { it('Should get array #1', () => { const V = [0, 1, 2, 3] - Assert.deepEqual(ValuePointer.Get(V, ''), [0, 1, 2, 3]) - Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) - Assert.deepEqual(ValuePointer.Get(V, '/0'), 0) - Assert.deepEqual(ValuePointer.Get(V, '/1'), 1) - Assert.deepEqual(ValuePointer.Get(V, '/2'), 2) - Assert.deepEqual(ValuePointer.Get(V, '/3'), 3) + Assert.isEqual(ValuePointer.Get(V, ''), [0, 1, 2, 3]) + Assert.isEqual(ValuePointer.Get(V, '/'), undefined) + Assert.isEqual(ValuePointer.Get(V, '/0'), 0) + Assert.isEqual(ValuePointer.Get(V, '/1'), 1) + Assert.isEqual(ValuePointer.Get(V, '/2'), 2) + Assert.isEqual(ValuePointer.Get(V, '/3'), 3) }) it('Should get array #2', () => { const V = [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }] - Assert.deepEqual(ValuePointer.Get(V, ''), [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]) - Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) - Assert.deepEqual(ValuePointer.Get(V, '/0'), { x: 0 }) - Assert.deepEqual(ValuePointer.Get(V, '/1'), { x: 1 }) - Assert.deepEqual(ValuePointer.Get(V, '/2'), { x: 2 }) - Assert.deepEqual(ValuePointer.Get(V, '/3'), { x: 3 }) - Assert.deepEqual(ValuePointer.Get(V, '/0/x'), 0) - Assert.deepEqual(ValuePointer.Get(V, '/1/x'), 1) - Assert.deepEqual(ValuePointer.Get(V, '/2/x'), 2) - Assert.deepEqual(ValuePointer.Get(V, '/3/x'), 3) + Assert.isEqual(ValuePointer.Get(V, ''), [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]) + Assert.isEqual(ValuePointer.Get(V, '/'), undefined) + Assert.isEqual(ValuePointer.Get(V, '/0'), { x: 0 }) + Assert.isEqual(ValuePointer.Get(V, '/1'), { x: 1 }) + Assert.isEqual(ValuePointer.Get(V, '/2'), { x: 2 }) + Assert.isEqual(ValuePointer.Get(V, '/3'), { x: 3 }) + Assert.isEqual(ValuePointer.Get(V, '/0/x'), 0) + Assert.isEqual(ValuePointer.Get(V, '/1/x'), 1) + Assert.isEqual(ValuePointer.Get(V, '/2/x'), 2) + Assert.isEqual(ValuePointer.Get(V, '/3/x'), 3) }) it('Should get object #1', () => { const V = { x: 0, y: 1, z: 2 } - Assert.deepEqual(ValuePointer.Get(V, ''), { x: 0, y: 1, z: 2 }) - Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) - Assert.deepEqual(ValuePointer.Get(V, '/x'), 0) - Assert.deepEqual(ValuePointer.Get(V, '/y'), 1) - Assert.deepEqual(ValuePointer.Get(V, '/z'), 2) + Assert.isEqual(ValuePointer.Get(V, ''), { x: 0, y: 1, z: 2 }) + Assert.isEqual(ValuePointer.Get(V, '/'), undefined) + Assert.isEqual(ValuePointer.Get(V, '/x'), 0) + Assert.isEqual(ValuePointer.Get(V, '/y'), 1) + Assert.isEqual(ValuePointer.Get(V, '/z'), 2) }) it('Should get object #2', () => { const V = { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } } - Assert.deepEqual(ValuePointer.Get(V, ''), { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } }) - Assert.deepEqual(ValuePointer.Get(V, '/'), undefined) - Assert.deepEqual(ValuePointer.Get(V, '/x'), { x: 0 }) - Assert.deepEqual(ValuePointer.Get(V, '/y'), { x: 1 }) - Assert.deepEqual(ValuePointer.Get(V, '/z'), { x: 2 }) + Assert.isEqual(ValuePointer.Get(V, ''), { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } }) + Assert.isEqual(ValuePointer.Get(V, '/'), undefined) + Assert.isEqual(ValuePointer.Get(V, '/x'), { x: 0 }) + Assert.isEqual(ValuePointer.Get(V, '/y'), { x: 1 }) + Assert.isEqual(ValuePointer.Get(V, '/z'), { x: 2 }) }) it('Should get object #3', () => { const V = { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } } - Assert.deepEqual(ValuePointer.Get(V, ''), { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } }) - Assert.deepEqual(ValuePointer.Get(V, '/'), { x: -1 }) - Assert.deepEqual(ValuePointer.Get(V, '/x'), { '': { x: 1 } }) - Assert.deepEqual(ValuePointer.Get(V, '/y'), { '': { x: 2 } }) - Assert.deepEqual(ValuePointer.Get(V, '/z'), { '': { x: 3 } }) - Assert.deepEqual(ValuePointer.Get(V, '/x/'), { x: 1 }) - Assert.deepEqual(ValuePointer.Get(V, '/y/'), { x: 2 }) - Assert.deepEqual(ValuePointer.Get(V, '/z/'), { x: 3 }) - Assert.deepEqual(ValuePointer.Get(V, '/x//x'), 1) - Assert.deepEqual(ValuePointer.Get(V, '/y//x'), 2) - Assert.deepEqual(ValuePointer.Get(V, '/z//x'), 3) + Assert.isEqual(ValuePointer.Get(V, ''), { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } }) + Assert.isEqual(ValuePointer.Get(V, '/'), { x: -1 }) + Assert.isEqual(ValuePointer.Get(V, '/x'), { '': { x: 1 } }) + Assert.isEqual(ValuePointer.Get(V, '/y'), { '': { x: 2 } }) + Assert.isEqual(ValuePointer.Get(V, '/z'), { '': { x: 3 } }) + Assert.isEqual(ValuePointer.Get(V, '/x/'), { x: 1 }) + Assert.isEqual(ValuePointer.Get(V, '/y/'), { x: 2 }) + Assert.isEqual(ValuePointer.Get(V, '/z/'), { x: 3 }) + Assert.isEqual(ValuePointer.Get(V, '/x//x'), 1) + Assert.isEqual(ValuePointer.Get(V, '/y//x'), 2) + Assert.isEqual(ValuePointer.Get(V, '/z//x'), 3) }) //----------------------------------------------- @@ -173,37 +173,37 @@ describe('value/pointer/Pointer', () => { it('Should return has true for undefined', () => { const V = undefined - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for null', () => { const V = null - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for object', () => { const V = {} - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for array', () => { const V: any[] = [] - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for string', () => { const V = 'hello' - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for number', () => { const V = 42 - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for boolean', () => { const V = false - Assert.deepEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) }) it('Should return has true for deeply nested', () => { @@ -216,20 +216,20 @@ describe('value/pointer/Pointer', () => { n: null, } // exists - Assert.deepEqual(ValuePointer.Has(V, ''), true) - Assert.deepEqual(ValuePointer.Has(V, '/'), true) - Assert.deepEqual(ValuePointer.Has(V, '//x'), true) - Assert.deepEqual(ValuePointer.Has(V, '//x/y'), true) - Assert.deepEqual(ValuePointer.Has(V, '//x/y/z'), true) - Assert.deepEqual(ValuePointer.Has(V, '/x'), true) - Assert.deepEqual(ValuePointer.Has(V, '/y/x'), true) - Assert.deepEqual(ValuePointer.Has(V, '/z'), true) - Assert.deepEqual(ValuePointer.Has(V, '/z/0'), true) - Assert.deepEqual(ValuePointer.Has(V, '/z/0/x'), true) - Assert.deepEqual(ValuePointer.Has(V, '/z/1'), true) - Assert.deepEqual(ValuePointer.Has(V, '/z/1/y'), true) - Assert.deepEqual(ValuePointer.Has(V, '/x'), true) - Assert.deepEqual(ValuePointer.Has(V, '/n'), true) + Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.isEqual(ValuePointer.Has(V, '/'), true) + Assert.isEqual(ValuePointer.Has(V, '//x'), true) + Assert.isEqual(ValuePointer.Has(V, '//x/y'), true) + Assert.isEqual(ValuePointer.Has(V, '//x/y/z'), true) + Assert.isEqual(ValuePointer.Has(V, '/x'), true) + Assert.isEqual(ValuePointer.Has(V, '/y/x'), true) + Assert.isEqual(ValuePointer.Has(V, '/z'), true) + Assert.isEqual(ValuePointer.Has(V, '/z/0'), true) + Assert.isEqual(ValuePointer.Has(V, '/z/0/x'), true) + Assert.isEqual(ValuePointer.Has(V, '/z/1'), true) + Assert.isEqual(ValuePointer.Has(V, '/z/1/y'), true) + Assert.isEqual(ValuePointer.Has(V, '/x'), true) + Assert.isEqual(ValuePointer.Has(V, '/n'), true) }) //----------------------------------------------- @@ -246,7 +246,7 @@ describe('value/pointer/Pointer', () => { ValuePointer.Set(V, '/0', 3) ValuePointer.Set(V, '/1', 4) ValuePointer.Set(V, '/2', 5) - Assert.deepEqual(V, [3, 4, 5]) + Assert.isEqual(V, [3, 4, 5]) }) it('Should set object values', () => { @@ -254,20 +254,20 @@ describe('value/pointer/Pointer', () => { ValuePointer.Set(V, '/x', 3) ValuePointer.Set(V, '/y', 4) ValuePointer.Set(V, '/z', 5) - Assert.deepEqual(V, { x: 3, y: 4, z: 5 }) + Assert.isEqual(V, { x: 3, y: 4, z: 5 }) }) it('Should set object values recursively #1', () => { const V = {} ValuePointer.Set(V, '/x/y/z', 1) - Assert.deepEqual(V, { x: { y: { z: 1 } } }) + Assert.isEqual(V, { x: { y: { z: 1 } } }) }) it('Should set object values recursively #2', () => { const V = {} ValuePointer.Set(V, '/x/0/y/z/', 1) ValuePointer.Set(V, '/x/1/y/z/', 2) - Assert.deepEqual(V, { + Assert.isEqual(V, { x: { 0: { y: { @@ -303,7 +303,7 @@ describe('value/pointer/Pointer', () => { } ValuePointer.Delete(V, '/x/y') ValuePointer.Delete(V, '/y') - Assert.deepEqual(V, { x: { x: 1, z: 3 } }) + Assert.isEqual(V, { x: { x: 1, z: 3 } }) }) it('Should be a noop if property does not exist', () => { @@ -313,7 +313,7 @@ describe('value/pointer/Pointer', () => { } ValuePointer.Delete(V, '/x/w') ValuePointer.Delete(V, '/w') - Assert.deepEqual(V, { + Assert.isEqual(V, { x: { x: 1, y: 2, z: 3 }, y: { x: 3, y: 4, z: 5 }, }) @@ -322,19 +322,19 @@ describe('value/pointer/Pointer', () => { it('Should not delete owner', () => { const V = { x: { y: { z: 1 } } } ValuePointer.Delete(V, '/x/y/z') - Assert.deepEqual(V, { x: { y: {} } }) + Assert.isEqual(V, { x: { y: {} } }) }) it('Should delete owner', () => { const V = { x: { y: { z: 1 } } } ValuePointer.Delete(V, '/x/y') - Assert.deepEqual(V, { x: {} }) + Assert.isEqual(V, { x: {} }) }) it('Should not throw if deleting null property', () => { const V = { x: { y: null } } ValuePointer.Delete(V, '/x/y/z') - Assert.deepEqual(V, { x: { y: null } }) + Assert.isEqual(V, { x: { y: null } }) }) //----------------------------------------------- @@ -345,14 +345,14 @@ describe('value/pointer/Pointer', () => { const V = { x: { '~': { x: 1 } }, } - Assert.deepEqual(ValuePointer.Get(V, '/x/~0'), { x: 1 }) + Assert.isEqual(ValuePointer.Get(V, '/x/~0'), { x: 1 }) }) it('Should support get ~1 pointer escape', () => { const V = { x: { '/': { x: 1 } }, } - Assert.deepEqual(ValuePointer.Get(V, '/x/~1'), { x: 1 }) + Assert.isEqual(ValuePointer.Get(V, '/x/~1'), { x: 1 }) }) it('Should support set ~0 pointer escape', () => { @@ -360,7 +360,7 @@ describe('value/pointer/Pointer', () => { x: { '~': { x: 1 } }, } ValuePointer.Set(V, '/x/~0', { x: 2 }) - Assert.deepEqual(V, { + Assert.isEqual(V, { x: { '~': { x: 2 } }, }) }) @@ -370,7 +370,7 @@ describe('value/pointer/Pointer', () => { x: { '/': { x: 1 } }, } ValuePointer.Set(V, '/x/~1', { x: 2 }) - Assert.deepEqual(V, { + Assert.isEqual(V, { x: { '/': { x: 2 } }, }) }) @@ -380,7 +380,7 @@ describe('value/pointer/Pointer', () => { x: { '~': { x: 1 } }, } ValuePointer.Delete(V, '/x/~0') - Assert.deepEqual(V, { + Assert.isEqual(V, { x: {}, }) }) @@ -390,7 +390,7 @@ describe('value/pointer/Pointer', () => { x: { '/': { x: 1 } }, } ValuePointer.Delete(V, '/x/~1') - Assert.deepEqual(V, { + Assert.isEqual(V, { x: {}, }) }) diff --git a/test/static/index.ts b/test/static/index.ts index 2d551c739..7cb57c759 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -34,6 +34,7 @@ import './record' import './ref' import './regex' import './required' +import './rest' import './return-type' import './strict' import './string' diff --git a/test/static/indexed.ts b/test/static/indexed.ts index 7e12c017c..01ee8758e 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -27,3 +27,24 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) Expect(I).ToInfer() } +{ + const A = Type.Tuple([Type.String(), Type.Boolean()]) + + const R = Type.Index(A, Type.Number()) + + Expect(R).ToInfer() +} +{ + const A = Type.Tuple([Type.String()]) + + const R = Type.Index(A, Type.Number()) + + Expect(R).ToInfer() +} +{ + const A = Type.Tuple([]) + + const R = Type.Index(A, Type.Number()) + + Expect(R).ToInfer() +} diff --git a/test/static/rest.ts b/test/static/rest.ts new file mode 100644 index 000000000..129e22aa4 --- /dev/null +++ b/test/static/rest.ts @@ -0,0 +1,36 @@ +import { Expect } from './assert' +import { Type, Static } from '@sinclair/typebox' + +{ + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Expect(C).ToInfer<[1, 2, 3, 4]>() +} +{ + const A = Type.String() + const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Expect(C).ToInfer<[string, 3, 4]>() +} +{ + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.String() + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Expect(C).ToInfer<[1, 2, string]>() +} +{ + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.Union(Type.Rest(A)) + Expect(B).ToInfer<1 | 2>() +} +{ + const A = Type.Tuple([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const B = Type.Intersect(Type.Rest(A)) + Expect(B).ToInfer<{ x: number } & { y: number }>() +} +{ + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.Function(Type.Rest(A), Type.Null()) + Expect(B).ToInfer<(a: 1, b: 2) => null>() +} From 11b09a1e0550c67ba5fa4f07483a82e0685cf884 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 22 Apr 2023 08:49:28 +0900 Subject: [PATCH 114/369] Revision 0.28.4 (#407) --- package-lock.json | 4 +- package.json | 2 +- readme.md | 10 ++-- src/typebox.ts | 77 +++++++++++++++---------- test/runtime/type/guard/indexed.ts | 93 +++++++++++++++++++++--------- test/runtime/type/guard/record.ts | 58 +++++++++++-------- 6 files changed, 155 insertions(+), 89 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7429ae097..4213c74c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.3", + "version": "0.28.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.3", + "version": "0.28.4", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 89382d651..ad4c5c940 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.3", + "version": "0.28.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 7c0fc9e57..9392ee2e9 100644 --- a/readme.md +++ b/readme.md @@ -1476,11 +1476,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '126.7 kb' │ ' 56.6 kb' │ '2.24 x' │ -│ typebox/errors │ '110.4 kb' │ ' 48.8 kb' │ '2.26 x' │ -│ typebox/system │ ' 75.9 kb' │ ' 31.1 kb' │ '2.44 x' │ -│ typebox/value │ '176.4 kb' │ ' 76.4 kb' │ '2.31 x' │ -│ typebox │ ' 74.8 kb' │ ' 30.7 kb' │ '2.44 x' │ +│ typebox/compiler │ '127.1 kb' │ ' 56.7 kb' │ '2.24 x' │ +│ typebox/errors │ '110.9 kb' │ ' 48.9 kb' │ '2.27 x' │ +│ typebox/system │ ' 76.3 kb' │ ' 31.2 kb' │ '2.44 x' │ +│ typebox/value │ '176.8 kb' │ ' 76.5 kb' │ '2.31 x' │ +│ typebox │ ' 75.2 kb' │ ' 30.8 kb' │ '2.44 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/typebox.ts b/src/typebox.ts index e95c3cdc2..d96f99d71 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -73,7 +73,7 @@ export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOp // -------------------------------------------------------------------------- // Key // -------------------------------------------------------------------------- -export type Key = keyof any +export type Key = string | number // -------------------------------------------------------------------------- // TSchema // -------------------------------------------------------------------------- @@ -310,21 +310,20 @@ export interface TFunction = - K extends keyof T ? [T[K]] : +export type TIndexProperty = + T[K] extends infer R ? [R] : [] // prettier-ignore -export type TIndexTuple = +export type TIndexTuple = K extends keyof T ? [T[K]] : [] // prettier-ignore -export type TIndexComposite = +export type TIndexComposite = T extends [infer L, ...infer R] ? [...TIndexKey, K>, ...TIndexComposite, K>] : [] // prettier-ignore -export type TIndexKey = +export type TIndexKey = T extends TRecursive ? TIndexKey : T extends TIntersect ? TIndexComposite : T extends TUnion ? TIndexComposite : @@ -333,11 +332,12 @@ export type TIndexKey = T extends TArray ? S : [] // prettier-ignore -export type TIndexKeys = - K extends [infer L, ...infer R] ? [...TIndexKey>, ...TIndexKeys>] : +export type TIndexKeys = + K extends [infer L, ...infer R] ? + [...TIndexKey>, ...TIndexKeys>] : [] // prettier-ignore -export type TIndex = +export type TIndex = TIndexKeys extends infer R ? T extends TRecursive ? TIndex : T extends TTuple ? UnionType> : @@ -540,7 +540,7 @@ export interface TPromise extends TSchema { // -------------------------------------------------------------------------- export type RecordTemplateLiteralObjectType = Ensure]: T }>>> export type RecordTemplateLiteralType = IsTemplateLiteralFinite extends true ? RecordTemplateLiteralObjectType : TRecord -export type RecordUnionLiteralType[]>, T extends TSchema> = Static extends string ? Ensure]: T }>> : never +export type RecordUnionLiteralType = Static extends string ? Ensure]: T }>> : never export type RecordLiteralType, T extends TSchema> = Ensure> export type RecordNumberType = Ensure> export type RecordStringType = Ensure> @@ -1045,9 +1045,9 @@ export namespace TypeGuard { export function TLiteralBoolean(schema: unknown): schema is TLiteral { return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'boolean' } - /** Returns true if the given schema is TUnion[]> */ - export function TLiteralUnion(schema: unknown): schema is TUnion[]> { - return TUnion(schema) && schema.anyOf.every((schema) => TLiteral(schema)) + /** Returns true if the given schema is TUnion[]> */ + export function TLiteralUnion(schema: unknown): schema is TUnion { + return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) } /** Returns true if the given schema is TLiteral */ export function TLiteral(schema: unknown): schema is TLiteral { @@ -1924,12 +1924,12 @@ export namespace IndexedAccessor { return schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[]) } function Object(schema: TObject, key: string): TSchema[] { - const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((k) => k === key) + const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((key_) => key_ === key) return keys.map((key) => schema.properties[key]) } function Tuple(schema: TTuple, key: string): TSchema[] { const items = schema.items === undefined ? [] : (schema.items as TSchema[]) - return items.filter((_, index) => index.toString() === key.toString()) + return items.filter((_, index) => index.toString() === key) } function Visit(schema: TSchema, key: string): TSchema[] { if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, key) @@ -1938,8 +1938,8 @@ export namespace IndexedAccessor { if (schema[Kind] === 'Tuple') return Tuple(schema as TTuple, key) return [] } - export function Resolve(schema: TSchema, keys: string[]): TSchema[] { - return keys.reduce((acc, key) => [...acc, ...Visit(schema, key)], [] as TSchema[]) + export function Resolve(schema: TSchema, keys: (string | number)[]): TSchema[] { + return keys.reduce((acc, key) => [...acc, ...Visit(schema, key.toString())], [] as TSchema[]) } } // -------------------------------------------------------------------------- @@ -2019,7 +2019,7 @@ export namespace KeyArrayResolver { /** Resolves an array of string[] keys from the given schema or array type. */ export function Resolve(schema: TSchema | string[]): string[] { if (globalThis.Array.isArray(schema)) return schema - if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const) + if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const.toString()) if (TypeGuard.TLiteral(schema)) return [schema.const as string] if (TypeGuard.TTemplateLiteral(schema)) { const expression = TemplateLiteralParser.ParseExact(schema.pattern) @@ -2030,6 +2030,24 @@ export namespace KeyArrayResolver { } } // -------------------------------------------------------------------------- +// UnionResolver +// -------------------------------------------------------------------------- +export namespace UnionResolver { + function* Union(union: TUnion): IterableIterator { + for (const schema of union.anyOf) { + if (schema[Kind] === 'Union') { + yield* Union(schema as TUnion) + } else { + yield schema + } + } + } + /** Returns a resolved union with interior unions flattened */ + export function Resolve(union: TUnion): TUnion { + return Type.Union([...Union(union)], { ...union }) + } +} +// -------------------------------------------------------------------------- // TemplateLiteralPattern // -------------------------------------------------------------------------- export namespace TemplateLiteralPattern { @@ -2389,11 +2407,11 @@ export class StandardTypeBuilder extends TypeBuilder { } } /** `[Standard]` Returns indexed property types for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> /** `[Standard]` Returns indexed property types for the given keys */ - public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> + public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> /** `[Standard]` Returns indexed property types for the given keys */ - public Index>(schema: T, key: K, options?: SchemaOptions): TIndex + public Index>(schema: T, key: K, options?: SchemaOptions): TIndex /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TIndex> /** `[Standard]` Returns indexed property types for the given keys */ @@ -2559,7 +2577,7 @@ export class StandardTypeBuilder extends TypeBuilder { }, options) } /** `[Standard]` Creates a Record type */ - public Record[]>, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType + public Record(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType /** `[Standard]` Creates a Record type */ public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordLiteralType /** `[Standard]` Creates a Record type */ @@ -2576,15 +2594,16 @@ export class StandardTypeBuilder extends TypeBuilder { return TemplateLiteralFinite.Check(expression) ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema, {}) }), {} as TProperties), options)) : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }}) - } else if (TypeGuard.TLiteralUnion(key)) { - if (key.anyOf.every((schema) => TypeGuard.TLiteral(schema) && (typeof schema.const === 'string' || typeof schema.const === 'number'))) { - const properties = key.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) + } else if (TypeGuard.TUnion(key)) { + const union = UnionResolver.Resolve(key) + if (TypeGuard.TLiteralUnion(union)) { + const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) return this.Object(properties, { ...options, [Hint]: 'Record' }) - } else throw Error('TypeBuilder: Record key can only be derived from union literal of number or string') + } else throw Error('TypeBuilder: Record key of type union contains non-literal types') } else if (TypeGuard.TLiteral(key)) { if (typeof key.const === 'string' || typeof key.const === 'number') { return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options) - } else throw Error('TypeBuilder: Record key can only be derived from literals of number or string') + } else throw Error('TypeBuilder: Record key of type literal is not of type string or number') } else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) { const pattern = PatternNumberExact return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) @@ -2592,7 +2611,7 @@ export class StandardTypeBuilder extends TypeBuilder { const pattern = key.pattern === undefined ? PatternStringExact : key.pattern return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) } else { - throw Error(`StandardTypeBuilder: Invalid Record Key`) + throw Error(`StandardTypeBuilder: Record key is an invalid type`) } } /** `[Standard]` Creates a Recursive type */ diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 15eeaa08b..f648ba708 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -9,7 +9,7 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TNumber(I), true) + Assert.isTrue(TypeGuard.TNumber(I)) }) it('Should Index 2', () => { const T = Type.Object({ @@ -17,9 +17,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 3', () => { const T = Type.Object({ @@ -27,9 +27,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, Type.KeyOf(T)) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 4', () => { const T = Type.Object({ @@ -37,59 +37,59 @@ describe('type/guard/TIndex', () => { ac: Type.String(), }) const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 5', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 6', () => { const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 7', () => { const T = Type.Array(Type.Null()) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TNull(I), true) + Assert.isTrue(TypeGuard.TNull(I)) }) it('Should Index 6', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0]) - Assert.isEqual(TypeGuard.TLiteralString(I), true) + Assert.isTrue(TypeGuard.TLiteralString(I)) Assert.isEqual(I.const, 'hello') }) it('Should Index 8', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1]) - Assert.isEqual(TypeGuard.TLiteralString(I), true) + Assert.isTrue(TypeGuard.TLiteralString(I)) Assert.isEqual(I.const, 'world') }) it('Should Index 9', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 1]) - Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isTrue(TypeGuard.TUnion(I)) Assert.isEqual(I.anyOf[0].const, 'hello') Assert.isEqual(I.anyOf[1].const, 'world') }) it('Should Index 10', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1, 0]) - Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isTrue(TypeGuard.TUnion(I)) Assert.isEqual(I.anyOf[0].const, 'world') Assert.isEqual(I.anyOf[1].const, 'hello') }) it('Should Index 11', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 0, 0, 1]) - Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isTrue(TypeGuard.TUnion(I)) Assert.isEqual(I.anyOf[0].const, 'hello') Assert.isEqual(I.anyOf[1].const, 'hello') Assert.isEqual(I.anyOf[2].const, 'hello') @@ -98,18 +98,57 @@ describe('type/guard/TIndex', () => { it('Should Index 12', () => { const T = Type.Tuple([Type.String(), Type.Boolean()]) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TBoolean(I.anyOf[1]), true) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TBoolean(I.anyOf[1])) }) it('Should Index 13', () => { const T = Type.Tuple([Type.String()]) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TString(I), true) + Assert.isTrue(TypeGuard.TString(I)) }) it('Should Index 14', () => { const T = Type.Tuple([]) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TNever(I), true) + Assert.isTrue(TypeGuard.TNever(I)) + }) + it('Should Index 15', () => { + const T = Type.Object({ + 0: Type.Number(), + }) + const I = Type.Index(T, Type.Literal(0)) + Assert.isTrue(TypeGuard.TNumber(I)) + }) + it('Should Index 16', () => { + const T = Type.Object({ + 0: Type.Number(), + }) + const I = Type.Index(T, Type.Literal('0')) + Assert.isTrue(TypeGuard.TNumber(I)) + }) + it('Should Index 17', () => { + const T = Type.Object({ + '0': Type.Number(), + }) + const I = Type.Index(T, Type.Literal(0)) + Assert.isTrue(TypeGuard.TNumber(I)) + }) + it('Should Index 18', () => { + const T = Type.Object({ + '0': Type.Number(), + }) + const I = Type.Index(T, Type.Literal('0')) + Assert.isTrue(TypeGuard.TNumber(I)) + }) + it('Should Index 19', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)])) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.isTrue(TypeGuard.TBoolean(I.anyOf[1])) }) }) diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 350f9b3fc..94bea05ec 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -8,15 +8,15 @@ describe('type/guard/TRecord', () => { // ------------------------------------------------------------- it('Should guard overload 1', () => { const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TObject(T), true) - Assert.isEqual(TypeGuard.TString(T.properties.A), true) - Assert.isEqual(TypeGuard.TString(T.properties.B), true) + Assert.isTrue(TypeGuard.TObject(T)) + Assert.isTrue(TypeGuard.TString(T.properties.A)) + Assert.isTrue(TypeGuard.TString(T.properties.B)) Assert.isEqual(T.extra, 1) }) it('Should guard overload 2', () => { const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal - Assert.isEqual(TypeGuard.TObject(T), true) - Assert.isEqual(TypeGuard.TString(T.properties.A), true) + Assert.isTrue(TypeGuard.TObject(T)) + Assert.isTrue(TypeGuard.TString(T.properties.A)) Assert.isEqual(T.extra, 1) }) it('Should guard overload 3', () => { @@ -25,49 +25,57 @@ describe('type/guard/TRecord', () => { }) it('Should guard overload 4', () => { const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TObject(T), true) - Assert.isEqual(TypeGuard.TString(T.properties.A), true) + Assert.isTrue(TypeGuard.TObject(T)) + Assert.isTrue(TypeGuard.TString(T.properties.A)) Assert.isEqual(T.extra, 1) }) it('Should guard overload 5', () => { const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TObject(T), true) - Assert.isEqual(TypeGuard.TString(T.properties.helloA), true) - Assert.isEqual(TypeGuard.TString(T.properties.helloB), true) + Assert.isTrue(TypeGuard.TObject(T)) + Assert.isTrue(TypeGuard.TString(T.properties.helloA)) + Assert.isTrue(TypeGuard.TString(T.properties.helloB)) Assert.isEqual(T.extra, 1) }) it('Should guard overload 6', () => { const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TRecord(T), true) - Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.isTrue(TypeGuard.TRecord(T)) + Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) Assert.isEqual(T.extra, 1) }) it('Should guard overload 7', () => { const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TRecord(T), true) - Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternNumberExact]), true) + Assert.isTrue(TypeGuard.TRecord(T)) + Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) Assert.isEqual(T.extra, 1) }) it('Should guard overload 8', () => { const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TRecord(T), true) - Assert.isEqual(TypeGuard.TString(T.patternProperties[PatternStringExact]), true) + Assert.isTrue(TypeGuard.TRecord(T)) + Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternStringExact])) Assert.isEqual(T.extra, 1) }) it('Should guard overload 9', () => { const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.isEqual(TypeGuard.TRecord(T), true) - Assert.isEqual(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`]), true) + Assert.isTrue(TypeGuard.TRecord(T)) + Assert.isTrue(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`])) Assert.isEqual(T.extra, 1) }) + it('Should guard overload 10', () => { + const L = Type.Union([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Record(L, Type.String()) + Assert.isTrue(TypeGuard.TObject(T)) + Assert.isTrue(TypeGuard.TString(T.properties.A)) + Assert.isTrue(TypeGuard.TString(T.properties.B)) + Assert.isTrue(TypeGuard.TString(T.properties.C)) + }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- it('Should guard for TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) - Assert.isEqual(R, true) + Assert.isTrue(R) }) it('Should guard for TRecord with TObject value', () => { const R = TypeGuard.TRecord( @@ -79,16 +87,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.isEqual(R, true) + Assert.isTrue(R) }) it('Should not guard for TRecord', () => { const R = TypeGuard.TRecord(null) - Assert.isEqual(R, false) + Assert.isFalse(R) }) it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) - Assert.isEqual(R, false) + Assert.isFalse(R) }) it('Should not guard for TRecord with TObject value with invalid Property', () => { const R = TypeGuard.TRecord( @@ -100,16 +108,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.isEqual(R, false) + Assert.isFalse(R) }) it('Transform: Should should transform to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.isEqual(R, true) + Assert.isTrue(R) }) it('Transform: Should should transform to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.isEqual(R, true) + Assert.isTrue(R) }) }) From 376e482737bd915460f122c95c66482b77fa9416 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 22 Apr 2023 15:09:32 +0900 Subject: [PATCH 115/369] Revision 0.28.4 --- readme.md | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/readme.md b/readme.md index 9392ee2e9..a36722e49 100644 --- a/readme.md +++ b/readme.md @@ -83,7 +83,7 @@ License MIT - [Conditional](#types-conditional) - [Template](#types-template-literal) - [Indexed](#types-indexed) - - [Variadic](#types-variadic) + - [Rest](#types-rest) - [Guards](#types-guards) - [Unsafe](#types-unsafe) - [Strict](#types-strict) @@ -872,30 +872,43 @@ const A = Type.Index(T, ['x']) // type A = T['x'] const B = Type.Index(T, Type.KeyOf(T)) // type B = T[keyof T] ``` - + -### Variadic Types +### Rest Types -Variadic types are supported with `Type.Rest`. This type will extract interior types from a tuple and return them as a flat array. This array can then be passed to other types that accept arrays as arguments. - -The following creates variadic functions using the Rest type. +Rest parameters are supported with `Type.Rest`. This function is used to extract interior arrays from tuples to allow them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatination and variadic function composition. ```typescript // TypeScript -type P = [number, number] +type T = [number, number] // type T = [number, number] -type F1 = (param: [...P]) => void +type C = [...T, number] // type C = [number, number, number] -type F2 = (param: [...P, number]) => void +type F = (...param: C) => void // type F = ( + // param0: number, + // param1: number, + // param2: number, + // ) => void // TypeBox -const P = Type.Tuple([Type.Number(), Type.Number()]) - -const F1 = Type.Function(Type.Rest(P), Type.Void()) - -const F2 = Type.Function([...Type.Rest(P), Type.Number()], Type.Void()) +const T = Type.Tuple([ // const T: TTuple<[ + Type.Number(), // TNumber, + Type.Number() // TNumber, +]) // ]> + +const C = Type.Tuple([ // const C: TTuple<[ + ...Type.Rest(T), // TNumber, + Type.Number() // TNumber, +]) // TNumber + // ]> + +const F = Type.Function(Type.Rest(C), Type.Void()) // const F: TFunction<[ + // TNumber, + // TNumber, + // TNumber + // ], TVoid> ``` From 35b1bb9ec931d71a71ff12ae32eebcdbad4812b6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 23 Apr 2023 13:48:01 +0900 Subject: [PATCH 116/369] Revision 0.28.5 (#409) --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 60 +++++++++++++++--------------- test/runtime/type/guard/indexed.ts | 18 +++++++++ test/static/indexed.ts | 14 +++++++ 5 files changed, 66 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4213c74c8..1ccf314c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.4", + "version": "0.28.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.4", + "version": "0.28.5", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index ad4c5c940..a34bb2f43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.4", + "version": "0.28.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index d96f99d71..21df62331 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -337,9 +337,9 @@ export type TIndexKeys = [...TIndexKey>, ...TIndexKeys>] : [] // prettier-ignore -export type TIndex = +export type TIndexFromKeyTuple = TIndexKeys extends infer R ? - T extends TRecursive ? TIndex : + T extends TRecursive ? TIndexFromKeyTuple : T extends TTuple ? UnionType> : T extends TIntersect ? UnionType> : T extends TUnion ? UnionType> : @@ -347,6 +347,14 @@ export type TIndex = T extends TArray ? UnionType> : TNever : TNever +// prettier-ignore +export type TIndex = + [T, K] extends [TTuple, TNumber] ? UnionType> : + [T, K] extends [TArray, TNumber] ? AssertType : + K extends TTemplateLiteral ? TIndexFromKeyTuple> : + K extends TUnion[]> ? TIndexFromKeyTuple> : + K extends TLiteral ? TIndexFromKeyTuple : + TNever // -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- @@ -679,7 +687,7 @@ export type TTemplateLiteralConst = export type TTemplateLiteralUnion = T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc -export type TTemplateLiteralKeyArray = Assert>, Key[]> +export type TTemplateLiteralKeyTuple = Assert>, Key[]> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' static: TTemplateLiteralUnion @@ -690,9 +698,12 @@ export interface TTemplateLiteral> = T extends TTuple ? AssertRest : never + +export type TTupleInfer = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleInfer, P>] : [] + export interface TTuple extends TSchema { [Kind]: 'Tuple' - static: { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } + static: TTupleInfer // { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } type: 'array' items?: T additionalItems?: false @@ -709,15 +720,16 @@ export interface TUndefined extends TSchema { typeOf: 'Undefined' } // -------------------------------------------------------------------------- -// TUnionOfLiteral +// TUnionLiteral // -------------------------------------------------------------------------- // prettier-ignore export type TLiteralUnionReduce[]> = T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : [] // prettier-ignore -export type TLiteralUnion[]>> = - T extends TUnion ? TLiteralUnionReduce[]>> : [] +export type TUnionLiteral[]>> = + T extends TUnion ? TLiteralUnionReduce[]>> : + [] // -------------------------------------------------------------------------- // TUnion // -------------------------------------------------------------------------- @@ -1045,10 +1057,6 @@ export namespace TypeGuard { export function TLiteralBoolean(schema: unknown): schema is TLiteral { return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'boolean' } - /** Returns true if the given schema is TUnion[]> */ - export function TLiteralUnion(schema: unknown): schema is TUnion { - return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) - } /** Returns true if the given schema is TLiteral */ export function TLiteral(schema: unknown): schema is TLiteral { return TLiteralString(schema) || TLiteralNumber(schema) || TLiteralBoolean(schema) @@ -1245,6 +1253,10 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } + /** Returns true if the given schema is TUnion[]> */ + export function TUnionLiteral(schema: unknown): schema is TUnion { + return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) + } /** Returns true if the given schema is TUnion */ export function TUnion(schema: unknown): schema is TUnion { // prettier-ignore @@ -2019,7 +2031,7 @@ export namespace KeyArrayResolver { /** Resolves an array of string[] keys from the given schema or array type. */ export function Resolve(schema: TSchema | string[]): string[] { if (globalThis.Array.isArray(schema)) return schema - if (TypeGuard.TLiteralUnion(schema)) return schema.anyOf.map((schema) => schema.const.toString()) + if (TypeGuard.TUnionLiteral(schema)) return schema.anyOf.map((schema) => schema.const.toString()) if (TypeGuard.TLiteral(schema)) return [schema.const as string] if (TypeGuard.TTemplateLiteral(schema)) { const expression = TemplateLiteralParser.ParseExact(schema.pattern) @@ -2407,19 +2419,9 @@ export class StandardTypeBuilder extends TypeBuilder { } } /** `[Standard]` Returns indexed property types for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index>(schema: T, key: K, options?: SchemaOptions): TIndex - /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): UnionType> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): AssertType + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndexFromKeyTuple> /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): TIndex + public Index(schema: T, key: K, options?: SchemaOptions): TIndex /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) @@ -2513,11 +2515,11 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ - public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> + public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> + public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { @@ -2555,11 +2557,11 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ - public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> + public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick>(schema: T, key: K, options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> + public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { @@ -2596,7 +2598,7 @@ export class StandardTypeBuilder extends TypeBuilder { : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }}) } else if (TypeGuard.TUnion(key)) { const union = UnionResolver.Resolve(key) - if (TypeGuard.TLiteralUnion(union)) { + if (TypeGuard.TUnionLiteral(union)) { const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) return this.Object(properties, { ...options, [Hint]: 'Record' }) } else throw Error('TypeBuilder: Record key of type union contains non-literal types') diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index f648ba708..84683b5d8 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -151,4 +151,22 @@ describe('type/guard/TIndex', () => { Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) Assert.isTrue(TypeGuard.TBoolean(I.anyOf[1])) }) + it('Should Index 20', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.BigInt()) + Assert.isTrue(TypeGuard.TNever(I)) + }) + it('Should Index 21', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.Object({})) + Assert.isTrue(TypeGuard.TNever(I)) + }) }) diff --git a/test/static/indexed.ts b/test/static/indexed.ts index 01ee8758e..629582bf6 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -48,3 +48,17 @@ import { Type, Static } from '@sinclair/typebox' Expect(R).ToInfer() } +{ + const A = Type.Object({}) + + const R = Type.Index(A, Type.BigInt()) // Support Overload + + Expect(R).ToInfer() +} +{ + const A = Type.Array(Type.Number()) + + const R = Type.Index(A, Type.BigInt()) // Support Overload + + Expect(R).ToInfer() +} From 1abe0bf417cd811225437ebba1e7f440c956a133 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 23 Apr 2023 14:03:25 +0900 Subject: [PATCH 117/369] License and Install Size Badges (#410) --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index a36722e49..dfd9c4629 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,8 @@ [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) +[![Install Size](https://packagephobia.com/badge?p=@sinclair/typebox)](https://packagephobia.com/result?p=@sinclair/typebox) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) From 0661aca2abf18b4e94913af0555ea6528135f64b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 25 Apr 2023 18:45:15 +0900 Subject: [PATCH 118/369] Enhanced Indexed Access Types (#413) --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 137 ++++++++++++------------ test/runtime/type/guard/indexed.ts | 64 ++++++++++++ test/static/indexed.ts | 161 +++++++++++++++++++++++++++++ 5 files changed, 292 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ccf314c6..43f8a0abe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.5", + "version": "0.28.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.5", + "version": "0.28.6", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index a34bb2f43..4b0198722 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.5", + "version": "0.28.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 21df62331..65feb5990 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -49,6 +49,8 @@ export type TupleToUnion = { [K in keyof T]: T[K] }[number] export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] +export type Discard = T extends [infer L, ...infer R] ? (L extends D ? Discard : [L, ...Discard]) : [] +export type Flat = T extends [] ? [] : T extends [infer L] ? [...Flat] : T extends [infer L, ...infer R] ? [...Flat, ...Flat] : [T] export type Assert = T extends E ? T : never export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never export type Ensure = T extends infer U ? U : never @@ -310,50 +312,36 @@ export interface TFunction = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] +export type TIndexProperty = K extends keyof T ? [T[K]] : [] +export type TIndexTuple = K extends keyof T ? [T[K]] : [] // prettier-ignore -export type TIndexProperty = - T[K] extends infer R ? [R] : +export type TIndexType = + T extends TRecursive ? TIndexType : + T extends TIntersect ? IntersectType>, TNever>>> : + T extends TUnion ? UnionType>>> : + T extends TObject ? UnionType>>> : + T extends TTuple ? UnionType>>> : [] // prettier-ignore -export type TIndexTuple = - K extends keyof T ? [T[K]] : - [] -// prettier-ignore -export type TIndexComposite = - T extends [infer L, ...infer R] ? [...TIndexKey, K>, ...TIndexComposite, K>] : - [] -// prettier-ignore -export type TIndexKey = - T extends TRecursive ? TIndexKey : - T extends TIntersect ? TIndexComposite : - T extends TUnion ? TIndexComposite : - T extends TObject ? TIndexProperty : - T extends TTuple ? TIndexTuple : - T extends TArray ? S : - [] +export type TIndexRestMany = + K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : + [] // prettier-ignore -export type TIndexKeys = - K extends [infer L, ...infer R] ? - [...TIndexKey>, ...TIndexKeys>] : - [] -// prettier-ignore -export type TIndexFromKeyTuple = - TIndexKeys extends infer R ? - T extends TRecursive ? TIndexFromKeyTuple : - T extends TTuple ? UnionType> : - T extends TIntersect ? UnionType> : - T extends TUnion ? UnionType> : - T extends TObject ? UnionType> : - T extends TArray ? UnionType> : - TNever : +export type TIndexReduce = + T extends TRecursive ? TIndexReduce : + T extends TIntersect ? UnionType>> : + T extends TUnion ? UnionType>> : + T extends TObject ? UnionType>> : + T extends TTuple ? UnionType>> : TNever // prettier-ignore export type TIndex = [T, K] extends [TTuple, TNumber] ? UnionType> : [T, K] extends [TArray, TNumber] ? AssertType : - K extends TTemplateLiteral ? TIndexFromKeyTuple> : - K extends TUnion[]> ? TIndexFromKeyTuple> : - K extends TLiteral ? TIndexFromKeyTuple : + K extends TTemplateLiteral ? TIndexReduce> : + K extends TUnion[]> ? TIndexReduce> : + K extends TLiteral ? TIndexReduce : TNever // -------------------------------------------------------------------------- // TInteger @@ -379,12 +367,9 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // TKeyOf // -------------------------------------------------------------------------- +// prettier-ignore export type TKeyOfProperties = Static extends infer S - ? UnionToTuple< - { - [K in keyof S]: TLiteral<`${Assert}`> - }[keyof S] - > + ? UnionToTuple<{[K in keyof S]: TLiteral<`${Assert}`>}[keyof S]> : [] // prettier-ignore export type TKeyOfIndicesArray = UnionToTuple @@ -687,7 +672,7 @@ export type TTemplateLiteralConst = export type TTemplateLiteralUnion = T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc -export type TTemplateLiteralKeyTuple = Assert>, Key[]> +export type TTemplateLiteralKeyRest = Assert>, Key[]> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' static: TTemplateLiteralUnion @@ -727,7 +712,7 @@ export type TLiteralUnionReduce[]> = T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : [] // prettier-ignore -export type TUnionLiteral[]>> = +export type TUnionLiteralKeyRest[]>> = T extends TUnion ? TLiteralUnionReduce[]>> : [] // -------------------------------------------------------------------------- @@ -782,9 +767,8 @@ export interface TVoid extends TSchema { // -------------------------------------------------------------------------- // Static // -------------------------------------------------------------------------- -/** Creates a TypeScript static type from a TypeBox type */ +/** Infers a static type from a TypeBox type */ export type Static = (T & { params: P })['static'] - // -------------------------------------------------------------------------- // TypeRegistry // -------------------------------------------------------------------------- @@ -1929,29 +1913,37 @@ export namespace TypeClone { // IndexedAccessor // -------------------------------------------------------------------------- export namespace IndexedAccessor { - function Intersect(schema: TIntersect, key: string): TSchema[] { - return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[]) - } - function Union(schema: TUnion, key: string): TSchema[] { - return schema.anyOf.reduce((acc, schema) => [...acc, ...Visit(schema, key)], [] as TSchema[]) - } - function Object(schema: TObject, key: string): TSchema[] { - const keys = globalThis.Object.getOwnPropertyNames(schema.properties).filter((key_) => key_ === key) - return keys.map((key) => schema.properties[key]) - } - function Tuple(schema: TTuple, key: string): TSchema[] { - const items = schema.items === undefined ? [] : (schema.items as TSchema[]) - return items.filter((_, index) => index.toString() === key) - } - function Visit(schema: TSchema, key: string): TSchema[] { + function Intersect(schema: TIntersect, key: string): TSchema { + const schemas = schema.allOf.reduce((acc, schema) => { + const indexed = Visit(schema, key) + return indexed[Kind] === 'Never' ? acc : [...acc, indexed] + }, [] as TSchema[]) + return Type.Intersect(schemas) + } + function Union(schema: TUnion, key: string): TSchema { + const schemas = schema.anyOf.map((schema) => Visit(schema, key)) + return Type.Union(schemas) + } + function Object(schema: TObject, key: string): TSchema { + const property = schema.properties[key] + return property === undefined ? Type.Never() : Type.Union([property]) + } + function Tuple(schema: TTuple, key: string): TSchema { + const items = schema.items + if (items === undefined) return Type.Never() + const element = items[key as any as number] // + if (element === undefined) return Type.Never() + return element + } + function Visit(schema: TSchema, key: string): TSchema { if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, key) if (schema[Kind] === 'Union') return Union(schema as TUnion, key) if (schema[Kind] === 'Object') return Object(schema as TObject, key) if (schema[Kind] === 'Tuple') return Tuple(schema as TTuple, key) - return [] + return Type.Never() } - export function Resolve(schema: TSchema, keys: (string | number)[]): TSchema[] { - return keys.reduce((acc, key) => [...acc, ...Visit(schema, key.toString())], [] as TSchema[]) + export function Resolve(schema: TSchema, keys: Key[], options: SchemaOptions = {}): TSchema { + return Type.Union(keys.map((key) => Visit(schema, key.toString()))) } } // -------------------------------------------------------------------------- @@ -2419,23 +2411,22 @@ export class StandardTypeBuilder extends TypeBuilder { } } /** `[Standard]` Returns indexed property types for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndexFromKeyTuple> + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndexReduce> /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TIndex /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - const keys = KeyArrayResolver.Resolve(unresolved) if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { return TypeClone.Clone(schema.items, options) - } - if (TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved)) { + } else if (TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved)) { const items = schema.items === undefined ? [] : schema.items const cloned = items.map((schema) => TypeClone.Clone(schema, {})) return this.Union(cloned, options) + } else { + const keys = KeyArrayResolver.Resolve(unresolved) + const clone = TypeClone.Clone(schema, {}) + return IndexedAccessor.Resolve(clone, keys, options) } - const resolved = IndexedAccessor.Resolve(schema, keys as any) - const cloned = resolved.map((schema) => TypeClone.Clone(schema, {})) - return this.Union(cloned, options) } /** `[Standard]` Creates an Integer type */ public Integer(options: NumericOptions = {}): TInteger { @@ -2515,11 +2506,11 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ - public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> + public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> + public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { @@ -2557,11 +2548,11 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ - public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> + public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick>(schema: T, key: K, options?: SchemaOptions): TPick /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> + public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 84683b5d8..bc47ba647 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -169,4 +169,68 @@ describe('type/guard/TIndex', () => { const I = Type.Index(T, Type.Object({})) Assert.isTrue(TypeGuard.TNever(I)) }) + it('Should Index 22', () => { + const A = Type.Object({ x: Type.Literal('A') }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x']) + Assert.isTrue(TypeGuard.TIntersect(I)) + Assert.isTrue(TypeGuard.TLiteral(I.allOf[0])) + Assert.isTrue(TypeGuard.TLiteral(I.allOf[1])) + Assert.isTrue(TypeGuard.TLiteral(I.allOf[2])) + Assert.isTrue(TypeGuard.TLiteral(I.allOf[3])) + }) + it('Should Index 23', () => { + const A = Type.Object({ x: Type.Literal('A') }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Union([A, B, C, D]) + const I = Type.Index(T, ['x']) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TLiteral(I.anyOf[0])) + Assert.isTrue(TypeGuard.TLiteral(I.anyOf[1])) + Assert.isTrue(TypeGuard.TLiteral(I.anyOf[2])) + Assert.isTrue(TypeGuard.TLiteral(I.anyOf[3])) + }) + it('Should Index 24', () => { + const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x', 'y']) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TIntersect(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + }) + it('Should Index 25', () => { + const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) + const B = Type.Object({ x: Type.Literal('B'), y: Type.String() }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x', 'y']) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TIntersect(I.anyOf[0])) + Assert.isTrue(TypeGuard.TIntersect(I.anyOf[1])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1].allOf[0])) + Assert.isTrue(TypeGuard.TString(I.anyOf[1].allOf[1])) + }) + it('Should Index 26', () => { + const T = Type.Recursive((This) => + Type.Object({ + x: Type.String(), + y: Type.Number(), + z: This, + }), + ) + const I = Type.Index(T, ['x', 'y', 'z']) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.isTrue(TypeGuard.TThis(I.anyOf[2])) + }) }) diff --git a/test/static/indexed.ts b/test/static/indexed.ts index 629582bf6..aa27690d3 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -62,3 +62,164 @@ import { Type, Static } from '@sinclair/typebox' Expect(R).ToInfer() } +// ------------------------------------------------------------------ +// Intersections +// ------------------------------------------------------------------ +{ + type A = { x: string; y: 1 } + type B = { x: string; y: number } + type C = A & B + type R = C['y'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.String(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['y']) + Expect(R).ToBe<1>() +} +{ + type A = { x: string; y: 1 } + type B = { x: string; y: number } + type C = A & B + type R = C['x'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.String(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['x']) + Expect(R).ToBe() +} +{ + type A = { x: string; y: 1 } + type B = { x: string; y: number } + type C = A & B + type R = C['x' | 'y'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.String(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['x', 'y']) + Expect(R).ToBe() +} +{ + type A = { x: string; y: number } + type B = { x: number; y: number } + type C = A & B + type R = C['x'] + + const A = Type.Object({ x: Type.String(), y: Type.Number() }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['x']) + Expect(R).ToBe() +} +{ + type A = { x: string; y: number } + type B = { x: number; y: number } + type C = A & B + type R = C['y'] + + const A = Type.Object({ x: Type.String(), y: Type.Number() }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['y']) + Expect(R).ToBe() +} +{ + type A = { x: string; y: number } + type B = { x: number; y: number } + type C = A & B + type R = C['x' | 'y'] + + const A = Type.Object({ x: Type.String(), y: Type.Number() }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const C = Type.Intersect([A, B]) + const R = Type.Index(C, ['x', 'y']) + Expect(R).ToBe() +} +{ + type A = { x: string; y: 1 } + type B = { x: string; y: number } + type C = { x: string; y: number } + type D = { x: string } + type I = (A & B) & (C & D) + type R = I['x' | 'y'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.String(), y: Type.Number() }) + const C = Type.Object({ x: Type.String(), y: Type.Number() }) + const D = Type.Object({ x: Type.String() }) + const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) + const R = Type.Index(I, ['x', 'y']) + type O = Static + Expect(R).ToBe() +} +{ + type A = { x: string; y: 1 } + type B = { x: number; y: number } + type C = { x: string; y: number } + type D = { x: string } + type I = (A & B) & (C & D) + type R = I['x' | 'y'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const C = Type.Object({ x: Type.String(), y: Type.Number() }) + const D = Type.Object({ x: Type.String() }) + const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) + const R = Type.Index(I, ['x', 'y']) + type O = Static + Expect(R).ToBe<1>() +} +{ + type A = { x: string; y: 1 } + type B = { x: number; y: number } + type C = { x: string; y: number } + type D = { x: string } + type I = (A | B) & (C & D) + type R = I['x' | 'y'] + + const A = Type.Object({ x: Type.String(), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.Number(), y: Type.Number() }) + const C = Type.Object({ x: Type.String(), y: Type.Number() }) + const D = Type.Object({ x: Type.String() }) + const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])]) + const R = Type.Index(I, ['x', 'y']) + type O = Static + Expect(R).ToBe() +} +{ + type A = { x: 'A'; y: 1 } + type B = { x: 'B'; y: number } + type C = { x: 'C'; y: number } + type D = { x: 'D' } + type I = A | B | C | D + type R = I['x'] + + const A = Type.Object({ x: Type.Literal('A'), y: Type.Literal(1) }) + const B = Type.Object({ x: Type.Literal('B'), y: Type.Number() }) + const C = Type.Object({ x: Type.Literal('C'), y: Type.Number() }) + const D = Type.Object({ x: Type.Literal('D') }) + const I = Type.Union([A, B, C, D]) + const R = Type.Index(I, Type.Union([Type.Literal('x')])) + type O = Static + Expect(R).ToBe<'A' | 'B' | 'C' | 'D'>() +} +{ + type I = { + x: string + y: number + z: I + } + type R = I['x' | 'y' | 'z'] + const I = Type.Recursive((This) => + Type.Object({ + x: Type.String(), + y: Type.Number(), + z: This, + }), + ) + const R = Type.Index(I, ['x', 'y', 'z']) // z unresolvable + type O = Static + Expect(R).ToBe() +} From 4955935c31b149e3b839be532da7395124ccd3ed Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 25 Apr 2023 19:14:50 +0900 Subject: [PATCH 119/369] Exact Optional Property Types (#414) --- readme.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index dfd9c4629..323c3efd9 100644 --- a/readme.md +++ b/readme.md @@ -1371,9 +1371,15 @@ const B = Value.Check(T, 'hello') // const B = true ### Policies -TypeBox validates using JSON Schema assertion policies by default. It is possible to override these policies and have TypeBox assert using TypeScript policies. The following overrides are available. +TypeBox validates using standard JSON Schema assertion policies by default. It is possible to override some of these policies to have TypeBox assert inline with TypeScript static assertion rules. The following policy overrides are available. ```typescript +// Disallow undefined values for optional properties (default is false) +// +// const A: { x?: number } = { x: undefined } - disallowed when enabled + +TypeSystem.ExactOptionalPropertyTypes = true + // Allow arrays to validate as object types (default is false) // // const A: {} = [] - allowed in TS From ec5d319fde571aee171c44456bb1f4304e9cf10f Mon Sep 17 00:00:00 2001 From: ulucs Date: Wed, 26 Apr 2023 14:19:02 +0300 Subject: [PATCH 120/369] Documentation (#415) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 323c3efd9..beeaa3547 100644 --- a/readme.md +++ b/readme.md @@ -959,7 +959,7 @@ type T = Static // type T = 'A' | 'B' | 'C' -### Type Gaurds +### Type Guards TypeBox provides a `TypeGuard` module that can be used for reflection and asserting values as types. From 11b5e1cbf9bdfe0e59d27d2d6d37cf56f173a71d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 27 Apr 2023 15:39:52 +0900 Subject: [PATCH 121/369] Numeric Indexed Keys (#416) --- example/trpc/readme.md | 20 +++++----- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 5 ++- test/runtime/type/guard/indexed.ts | 44 +++++++++++++++++++++ test/static/indexed.ts | 63 +++++++++++++++++++++--------- 6 files changed, 105 insertions(+), 33 deletions(-) diff --git a/example/trpc/readme.md b/example/trpc/readme.md index 5a2b36158..adcb43aa7 100644 --- a/example/trpc/readme.md +++ b/example/trpc/readme.md @@ -44,21 +44,21 @@ export const appRouter = t.router({ ## RpcType -Unlike Zod, Yup, Superstruct, TypeBox types are pure JSON Schema and do not implement built a in `parse` or `validate` function. Because of this, you will need to wrap the Type in a TRPC compatible function which implements `parse` or `validate` on behalf of TRPC. As JSON Schema is a formal specification, there are a number of ways you can choose to implement this function, the following shows recommended implementations. +Unlike Zod, Yup, Superstruct, TypeBox types are pure JSON Schema and do not implement a built in `parse()` or `validate()` function. Because of this, you will need to wrap the Type in a TRPC compatible function which implements `parse()` on behalf of TRPC. As JSON Schema is a formal specification, there are a number of ways you may choose to implement this function. The following are some recommended implementations. -### With [TypeCompiler](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAKgMr4AW8VMlABfUJGyNUoAOTgq0RPljJo2YCTIAjRgA8ZdBs1YdOpeAGEpuOPGyjxk6XIVKVajeZ27g+a7ewDeiYWdi4AJQAFCwBRbEl7MQkpWXBCPHxgKjsANzsDGnhdENZIAFclQmhGRFBwgjMyAB5OUCLCJAATKi5eASEAPgAKKn5BZCxOABoceEg7JHx4Kkm+8YBtAF1QAF5QLYBKdhpQUD9EKlYx-ABrXa5zKwYA8iebBBGxoRnsOYWlZYHE6zQhlbC1IY5ZCwMqYUAVG6IRgAd0QRx2A2Op1O0EgQ2uN1eAlukOhsIOR1+oPBoChMPgwNO50uYVwyEIfBm6CoVGQAHN4A49utyKKCeQ4gkqKT6QdNusAAybRmgDmSZGgRDwDWcKKxeLMIYcbm8gVYAAGABI2Cb+fAksxQNa2RyROaZn5OnCZAAhACCABEAPrhGIARQAqjFuJwZKIgacRDQkzQQBAYAgUOgsJIyh1sJRaMFjGFFNBCLrog5ks40hksrl8oYSmFGoKkk5Uq5lKp1Jp4N4gkZQhx6vg29XOzJyMAMv2CszWMhOp0AJKIXB5+5jttDNvkADyWgAVvAiEbgRMHmRyAA5MqoLR2IYHKbArSTcx3h9P7Av5MUjQNCLqAy6dAeeabqwew7uYe5fvej7PoBwE1Cy0GgGWFZ6uQ+C-Oy8D-oUxQliByC4Lg4SMHmdj3IQ5C5vmF6nGBWD0bgkhLJ0YIMtioDkIoUFDGB65QQm2LkNRhBCWBEHSXm4mnOQACOsLYMQQxGphG5biI6KYoJebkMIADU2lQeQWhHGm-YALS8vMAFAA) +### With [TypeCompiler](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgFQJ5gKYGELmAGwyjgF84AzKXOAcgAEBnYAOwGN8BDYKAehnQwAjCAA8erXGAJEaAWABQoSLEQoAyqwAWGEB1JwDhytXpM2nbnwHCRcxeGjwkyAEoAFLAFEoVYmSNUILR0MFBgrDwMRABuMgoKGCLK8OQArmwwwBDMcC7haJgAPMhwiTAYzAAmDOpaOhwAfAAUDHW6AFwoADRwUBjkRBWsGAydyBraugDaALpwALxwswCUiAoGEswM8HWsANYLKAI4eIRQAHQnUoQtbRw9fQN9bCPL670YMKlQOU3RHPhUhhOuk9swIAB3ZireYNNbyQxwYDkOBNXZ7S7afZ-AFA5arPpfH5wf6AjDvDbZbaqMAcGCaHogEYMDgAcww+kW6PO3l8DBxZOW5wAYtxtk1lgBCClwelUCFwZgYBWuDy86BNJBMhgs9mdAAGABIENrdRgyORoHBjbT6SR9T0JJVgbQAEIAQQAIgB9FyeACKAFVPGpkDRSG8EaQFCQgA) The following uses the TypeCompiler to perform JIT optimized type checking for TRPC. ```typescript -import { TSchema } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' +import { TSchema } from '@sinclair/typebox' import { TRPCError } from '@trpc/server' -export function RpcType(schema: T) { - const check = TypeCompiler.Compile(schema) +export function RpcType(schema: T, references: TSchema[] = []) { + const check = TypeCompiler.Compile(schema, references) return (value: unknown) => { if (check.Check(value)) return value const { path, message } = check.Errors(value).First()! @@ -69,18 +69,18 @@ export function RpcType(schema: T) { -### With [Value](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAasrAK7ygAvqEjZGqUAHJwVaInyxk0bMBJkARowAewAG49+kug2asOAFQDK+ABbxUyISLETps+YuWrS8TVqP0TCzsoOYASgAKAMIAothi2E6i4lLghHj4wFTw2Lo5RjTwWkGskLzyhNCMiKBhBOY+ADzmoEWESAAmVKHWdg4AfAAUVLb2yFjmADQ48JA5SPjwVBO9YwDaALqgALygmwCU7DSgM4S82DWD+nyYoOUA1oiMAO6Ih9v9Rycn0JCD3DdyFE7Ph7sNRg5ptd+PtDth4GcLqBofBjt98NUqGZQLhkIQbNN0FQqMgAOYCYS7NbkGkA-jkOIJKjgvrIabwubw+RLKEGeD7DZrAAMGzRJ3xYmeoEQ8Cl4WijOYgw4RJJ5KwAAMACRsVVk+DCSDMUA63H4wQa6YYjq3SQAIQAggARAD6YRiAEUAKoxSzmSRCfZowQ0EM0EAQGAIFDoLBiXjtbCUWiBUwhOTQQjyqJJFypdIELI5PLYAImYIWHy5lJuOQKJQqNS+bRlkohOr4BpkauucjADJNgoYxBY0DIDodACSiFwCZ2tXqPkGXYoAHl1AAreBEZVo8ahHzkAByvFQ6hyg32kzR6gmh5PZ4vQcEsJoNGHo-HHVXCdnrF2HYrsu96nue2CXkG76Yqw-6gBmWaRFE5D4PCeLwJeb5FG2H6sMguC4GEjAJjk86EOQ8aJruJxflgZG4GIiwdOcqLfKA5ByH+gxftOf5Bqx5BEYQnFfj+QkJnx3zkAAjvw2DEIMypwTOc4vjsnwcQm5COAA1Epf7kOohwRk2AC0JJzKG+xAA) +### With [Value](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgNQIYBsCuBTOBfOQwgMyghDgHIABAZ2ADsBjdVYKAehgE8xsAjCAA8OANww5KAWABQoSLERwAKgGUmAC2whU+IqXJU6jFm048+godLnho8JMoBKABQDCAUShkoegxRoYKDAmDlpsKFEIm1lsIQV4YkxmGGAIBjgnEOVebAAeZTg4mGwGABNaFXUtHQA+AApaTW1UAC4VABo4KGxiCNKmbFp2tWadAG0AXTgAXjgpgEpEWUIemEwoDPrxLGx25IBrBggAdwYlmdrlmSI4YGI4erRdgDo3LSYDxrHULp6+nrMIZdHY4BZLNYbDKg7ArIhMdK0BxwMCoGAaLogIa0VAAc1wBDmzxwLy8Plo3xqvzgMIWLwAYuwkfUFgBCOGEdFkE5wBjYHnOdxk6D1JBY2g4-HtAAGABIEOLJdgCMRoHB5aj0XhpV0EWU9lQAEIAQQAIgB9JweACKAFUPKplJR8As4XhZHggA) -The following performs dynamic type checking without code evaluation. +The following uses dynamic type checking without code evaluation. This is slower to validate, but may be required in restricted code evaluation environments. ```typescript import { Value } from '@sinclair/typebox/value' import { TSchema } from '@sinclair/typebox' import { TRPCError } from '@trpc/server' -export function RpcType(schema: T) { +export function RpcType(schema: T, references: TSchema[] = []) { return (value: unknown) => { - if (Value.Check(schema, value)) return value + if (Value.Check(schema, references, value)) return value const { path, message } = Value.Errors(schema, value).First()! throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) } @@ -89,7 +89,7 @@ export function RpcType(schema: T) { -### With [Ajv](https://www.typescriptlang.org/dev/bug-workbench/?target=99&lib=true&ts=4.7.4#code/PTAEAEDMEsBsFMB2BDAtvAXKATgBwMYAuAnrvAHSEDOAUNKrgPbaGgDeoAKgMr4AW8VMgA0oboWSFo+UAF9QkbI1SgA5OCrRE+WMmjZgJMgCNGAD1V0GzVh04AlAAoBhAKLYl2OQqUr1hPHxgKnhsADdQy3omFlAAQQArMJ9lNWQk4AATaCpCYAAmAAYiyxp4MxjWSABXbSlGRFB7Ak5SeAAeTlBywiRMqi5eASEAPgAKKn5BZCxOUWx4SFCkfHgqWaHpgG0AXVAAXlBdgEp2GlBQfAbc0HTkw8R4AHd4pLHj85xF5e018khmK5kPwxmMFksFr9TvsRrckuRkJlMpshGDvpDVscPhcrogblN8ABrA5wsLkK4MODwCZTITYr6EarYRpjMLIWDVTCgWqExCMJ6IY5YcSSaSdWEws4XC7QSBjAmE1nszlYhlMxpsjnwT44662UDoKhUZAAc3goi0uWQv0ckj43kOCvIoU8VAAhFtCjsdaBCHwlC9Hi8HC53J4xhxDcazVgAAYAEjYUdN8HkAK8ictEhtdtksdEV0yXNUACE4gARAD69lcAEUAKqubicVRyemyGgdmggCAwBAodBYJTVXrYSi0aI2digLTQQgh5zeRSpfyBYKhCLYKLWWJ2NpL3xqDRaHR6AxGeCmCxWSrT5r4VpkA8r8jAQIX0q4m6IzIASUQuAjiS96PtSoHkAA8sYCTwEQEafDMXBtOQABy1SoMYoTvMInzGLMyFoRhWEfLIWI0DQX6sD+EEjoBrCHCBbRjOBhGYdg7wfBReq+iSs7zk4zjkgskjUpx5S3pRty4Lg9iMCOoQkoQ5DDqO8EXD+WBKbgSirJkTLatKoDkFodFjD+-50fSFzkHJhCmdRtEjlZRkAI6ctgxCghwJlAaRBywj5SnIKAADUM4ASO5DGKcPYXgAtMaSydscQA) +### With [Ajv](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgFQMoGMAWBTEBDAGjlRjxmHTgF84AzKCEOAcgAEBnYAO3QBs9gUAPQwAnmGwAjCAA9mAWABQoSLEQoASgAUAwgFEoDKNTimzdBkzYwoYdEPbYoANycLl4aPACCAK2cWjCx4-kIAJsDsMEIATAAM8e5K2DKq8LQArjzkEFxwGnbI4tgAPMhwKTDYXGHsKBg4+AB8ABTsWLh4AFwoRFDYtE7V6NjsPWgd+ADaALpwALxwswCUiEqm6LlRcCEBi1zYAO5wfs4ty+tw-YP9PKMAdLTQenhYLS3XQ3er8007-vc8GEwg1Oh8Bl8RssLooNlt4B10ABrBb-Zz3TbgYC8bBtSZ4GGmfowDJQPItZx4XgZbA9LJIrgQQ5cZY9EhkChlP6-NawszAWhwFqIpEUqk06FXbAkslwSnU7CXOFcbZIECjdh4ADm2CI3CieDuWjImBMixF9ycRnYAEIpnEZkq4DBMAxjgdjshtPpDNAWmqNdraXAAAYAEgQ6vYmp1NCexgj+tIRpNVBDRE2YWDzAAQt4ACIAfQ0egAigBVPSoZDMaiE6hKKhAA) The following uses Ajv to perform more generalized JSON Schema checks across the complete JSON Schema specification. diff --git a/package-lock.json b/package-lock.json index 43f8a0abe..e330584a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.6", + "version": "0.28.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.6", + "version": "0.28.7", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 4b0198722..8883728d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.6", + "version": "0.28.7", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 65feb5990..3a4a7bd67 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -369,7 +369,7 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // prettier-ignore export type TKeyOfProperties = Static extends infer S - ? UnionToTuple<{[K in keyof S]: TLiteral<`${Assert}`>}[keyof S]> + ? UnionToTuple<{[K in keyof S]: TLiteral>}[keyof S]> : [] // prettier-ignore export type TKeyOfIndicesArray = UnionToTuple @@ -1943,7 +1943,8 @@ export namespace IndexedAccessor { return Type.Never() } export function Resolve(schema: TSchema, keys: Key[], options: SchemaOptions = {}): TSchema { - return Type.Union(keys.map((key) => Visit(schema, key.toString()))) + // prettier-ignore + return Type.Union(keys.map((key) => Visit(schema, key.toString())), options) } } // -------------------------------------------------------------------------- diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index bc47ba647..822267285 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -233,4 +233,48 @@ describe('type/guard/TIndex', () => { Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) Assert.isTrue(TypeGuard.TThis(I.anyOf[2])) }) + it('Should Index 27', () => { + const T = Type.Object({ + 0: Type.String(), + 1: Type.Number(), + }) + const I = Type.Index(T, [0, 1]) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + }) + it('Should Index 28', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, [0, '1']) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + }) + it('Should Index 29', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal('1')])) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + }) + it('Should Index 30', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + // Note: Expect TNever for anyOf[1] but permit for TNumber due to IndexedAccess + // Resolve() which currently cannot differentiate between string and numeric keys + // on the object. This may be resolvable in later revisions, but test for this + // fall-through to ensure case is documented. For review. + }) }) diff --git a/test/static/indexed.ts b/test/static/indexed.ts index aa27690d3..c2cf5f9b6 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -6,60 +6,60 @@ import { Type, Static } from '@sinclair/typebox' x: Type.Number(), y: Type.String(), }) - const I = Type.Index(T, ['x', 'y']) - Expect(I).ToInfer() + const R = Type.Index(T, ['x', 'y']) + type O = Static + Expect(R).ToInfer() } { const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) - const I = Type.Index(T, Type.Union([Type.Literal('0'), Type.Literal('1')])) - Expect(I).ToInfer() + const R = Type.Index(T, Type.Union([Type.Literal('0'), Type.Literal('1')])) + type O = Static + Expect(R).ToInfer() } { const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) - const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) - Expect(I).ToInfer() + const R = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) + type O = Static + Expect(R).ToInfer() } { const T = Type.Object({ ab: Type.Number(), ac: Type.String(), }) - const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) - Expect(I).ToInfer() + + const R = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) + type O = Static + Expect(R).ToInfer() } { const A = Type.Tuple([Type.String(), Type.Boolean()]) - const R = Type.Index(A, Type.Number()) - + type O = Static Expect(R).ToInfer() } { const A = Type.Tuple([Type.String()]) - const R = Type.Index(A, Type.Number()) - + type O = Static Expect(R).ToInfer() } { const A = Type.Tuple([]) - const R = Type.Index(A, Type.Number()) - + type O = Static Expect(R).ToInfer() } { const A = Type.Object({}) - const R = Type.Index(A, Type.BigInt()) // Support Overload - + type O = Static Expect(R).ToInfer() } { const A = Type.Array(Type.Number()) - const R = Type.Index(A, Type.BigInt()) // Support Overload - + type O = Static Expect(R).ToInfer() } // ------------------------------------------------------------------ @@ -75,6 +75,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.String(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['y']) + type O = Static Expect(R).ToBe<1>() } { @@ -87,6 +88,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.String(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x']) + type O = Static Expect(R).ToBe() } { @@ -99,6 +101,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.String(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x', 'y']) + type O = Static Expect(R).ToBe() } { @@ -111,6 +114,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.Number(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x']) + type O = Static Expect(R).ToBe() } { @@ -123,6 +127,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.Number(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['y']) + type O = Static Expect(R).ToBe() } { @@ -135,6 +140,7 @@ import { Type, Static } from '@sinclair/typebox' const B = Type.Object({ x: Type.Number(), y: Type.Number() }) const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x', 'y']) + type O = Static Expect(R).ToBe() } { @@ -223,3 +229,24 @@ import { Type, Static } from '@sinclair/typebox' type O = Static Expect(R).ToBe() } +// ------------------------------------------------ +// Numeric | String Variants +// ------------------------------------------------ +{ + const T = Type.Object({ + 0: Type.Number(), + '1': Type.String(), + }) + const R = Type.Index(T, [0, '1']) + type O = Static + Expect(R).ToBe() +} +{ + const T = Type.Object({ + 0: Type.Number(), + '1': Type.String(), + }) + const R = Type.Index(T, Type.KeyOf(T)) + type O = Static + Expect(R).ToBe() +} From a4f412b63c0c8811f03362959725569ddaa43300 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 28 Apr 2023 18:17:18 +0900 Subject: [PATCH 122/369] Construct Composite Using Indexed Access Type (#420) --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 69 +++++----------------------- test/runtime/type/guard/composite.ts | 15 ++++-- test/static/composite.ts | 29 +++++++----- 5 files changed, 43 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index e330584a8..5b4dbac6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.7", + "version": "0.28.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.7", + "version": "0.28.8", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 8883728d5..fd300dcf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.7", + "version": "0.28.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 3a4a7bd67..618e85a90 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -194,34 +194,18 @@ export type TInstanceType> = T['retur // -------------------------------------------------------------------------- // TComposite // -------------------------------------------------------------------------- -export type TCompositeIsOptional = T extends TOptional | TReadonlyOptional ? true : false // prettier-ignore -export type TCompositeOptional = T extends [infer L, ...infer R] - ? TCompositeIsOptional> extends false ? false - : TCompositeOptional> : true -export type TCompositeKeyOfUnion1 = keyof T['properties'] -// prettier-ignore -export type TCompositeKeyOfUnion2 = T extends [infer L, ...infer R] - ? TCompositeKeyOfUnion1> | TCompositeKeyOfUnion2> - : never -export type TCompositeKeyOf = UnionToTuple> -export type TCompositePropertiesWithKey1 = K extends keyof T['properties'] ? [T['properties'][K]] : [] -// prettier-ignore -export type TCompositePropertiesWithKey2 = T extends [infer L, ...infer R] - ? [...TCompositePropertiesWithKey1, K>, ...TCompositePropertiesWithKey2, K>] - : [] -// prettier-ignore -export type TCompositeObjectProperty = TCompositePropertiesWithKey2 extends infer S ? - TCompositeOptional> extends true - ? { [_ in K]: TOptional>> } - : { [_ in K]: IntersectType> } +export type TCompositeReduce, K extends string[]> = K extends [infer L, ...infer R] + ? { [_ in Assert]: TIndexType> } & TCompositeReduce> : {} // prettier-ignore -export type TCompositeObjectsWithKeys = K extends [infer L, ...infer R] ? L extends Key - ? TCompositeObjectProperty & TCompositeObjectsWithKeys> - : {} +export type TCompositeSelect> = UnionToTuple> extends infer K + ? Evaluate>> : {} -export type TComposite = Ensure, Key[]>>>>> +// prettier-ignore +export type TComposite = TIntersect extends infer R + ? TObject>>> + : TObject<{}> // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- @@ -2337,39 +2321,10 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Creates a Composite object type. */ public Composite(objects: [...T], options?: ObjectOptions): TComposite { - const isOptionalAll = (objects: TObject[], key: string) => objects.every((object) => !(key in object.properties) || IsOptional(object.properties[key])) - const IsOptional = (schema: TSchema) => TypeGuard.TOptional(schema) || TypeGuard.TReadonlyOptional(schema) - const [required, optional] = [new Set(), new Set()] - for (const object of objects) { - for (const key of globalThis.Object.getOwnPropertyNames(object.properties)) { - if (isOptionalAll(objects, key)) optional.add(key) - } - } - for (const object of objects) { - for (const key of globalThis.Object.getOwnPropertyNames(object.properties)) { - if (!optional.has(key)) required.add(key) - } - } - const properties = {} as Record - for (const object of objects) { - for (const [key, schema] of Object.entries(object.properties)) { - const property = TypeClone.Clone(schema, {}) - if (!optional.has(key)) delete property[Modifier] - if (key in properties) { - properties[key] = TypeGuard.TIntersect(properties[key]) ? this.Intersect([...properties[key].allOf, property]) : this.Intersect([properties[key], property]) - } else { - properties[key] = property - } - } - } - for (const key of globalThis.Object.getOwnPropertyNames(properties)) { - properties[key] = optional.has(key) ? this.Optional(properties[key]) : properties[key] - } - if (required.size > 0) { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] }) - } else { - return this.Create({ ...options, [Kind]: 'Object', type: 'object', properties }) - } + const intersect: any = Type.Intersect(objects, {}) + const keys = KeyResolver.ResolveKeys(intersect, { includePatterns: false }) + const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Type.Index(intersect, [key]) }), {} as TProperties) + return Type.Object(properties, options) as TComposite } /** `[Standard]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index 23405772a..86ced5e53 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -20,8 +20,15 @@ describe('type/guard/TComposite', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) Assert.isEqual(TypeGuard.TOptional(T.properties.x), false) }) - it('Should produce optional property if all properties are optional', () => { - const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) })]) - Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) - }) + // Note for: https://github.com/sinclairzx81/typebox/issues/419 + // Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access + // call. Currently, there isn't a trivial way to perform this check without running into possibly infinite instantiation issues. + // The optional check is only specific to overlapping properties. Singular properties will continue to work as expected. The + // rule is "if all composite properties for a key are optional, then the composite property is optional". Defer this test and + // document as minor breaking change. + // + // it('Should produce optional property if all properties are optional', () => { + // const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) })]) + // Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) + // }) }) diff --git a/test/static/composite.ts b/test/static/composite.ts index fcbc5c4a9..b4399e20f 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -43,19 +43,24 @@ import { Type, Static } from '@sinclair/typebox' A: number }>() } -// Overlapping All Optional +// Overlapping All Optional (Deferred) +// Note for: https://github.com/sinclairzx81/typebox/issues/419 +// Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access +// call. Currently, there isn't a trivial way to perform this check without running into possibly infinite instantiation issues. +// The optional check is only specific to overlapping properties. Singular properties will continue to work as expected. The +// rule is "if all composite properties for a key are optional, then the composite property is optional". Defer this test and +// document as minor breaking change. { - const A = Type.Object({ - A: Type.Optional(Type.Number()), - }) - const B = Type.Object({ - A: Type.Optional(Type.Number()), - }) - const T = Type.Composite([A, B]) - - Expect(T).ToInfer<{ - A: number | undefined - }>() + // const A = Type.Object({ + // A: Type.Optional(Type.Number()), + // }) + // const B = Type.Object({ + // A: Type.Optional(Type.Number()), + // }) + // const T = Type.Composite([A, B]) + // Expect(T).ToInfer<{ + // A: number | undefined + // }>() } // Distinct Properties { From d4d6ffccd6adc7d6ee61f69926f853349e95b3d2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 1 May 2023 23:23:45 +0900 Subject: [PATCH 123/369] Template Literal DSL (#423) --- example/template-dsl/index.ts | 29 -------- example/template-dsl/readme.md | 31 -------- example/template-dsl/template-dsl.ts | 105 --------------------------- package-lock.json | 4 +- package.json | 2 +- readme.md | 67 ++++++++++------- src/typebox.ts | 77 +++++++++++++++++++- 7 files changed, 120 insertions(+), 195 deletions(-) delete mode 100644 example/template-dsl/index.ts delete mode 100644 example/template-dsl/readme.md delete mode 100644 example/template-dsl/template-dsl.ts diff --git a/example/template-dsl/index.ts b/example/template-dsl/index.ts deleted file mode 100644 index 44f129157..000000000 --- a/example/template-dsl/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/template-dsl - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './template-dsl' \ No newline at end of file diff --git a/example/template-dsl/readme.md b/example/template-dsl/readme.md deleted file mode 100644 index e55dba237..000000000 --- a/example/template-dsl/readme.md +++ /dev/null @@ -1,31 +0,0 @@ -# TemplateLiteral DSL - -This example implements a small string based DSL for dynamically parsing strings into `TTemplateLiteral` types at runtime. The `template-dsl.ts` script provided with this example contains the full implementation. It can be copied and pasted into projects with TypeBox 0.28.0 or higher installed. - -The example is a candiate for possible inclusion in TypeBox under a `Type.TemplateLiteral(template_dsl_string)` overloaded type function. - -## Example - -The DSL supports a similar syntax to TypeScript template literal type syntax. The following shows general usage. - -```typescript -import { Static } from '@sinclair/typebox' -import { TemplateLiteral } from './template-dsl' - -// ---------------------------------------------------------------- -// Path -// ---------------------------------------------------------------- - -const Path = TemplateLiteral('/users/${number}/posts/${string}') - -type Path = Static // type Path = '/users/${number}/posts/${string}' - -// ---------------------------------------------------------------- -// Bytes -// ---------------------------------------------------------------- - -const Byte = TemplateLiteral('${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}${0|1}') - -type Byte = Static // type Byte = '00000000' | '00000001' | '00000010' ... | '11111111' -``` - diff --git a/example/template-dsl/template-dsl.ts b/example/template-dsl/template-dsl.ts deleted file mode 100644 index ad110da3a..000000000 --- a/example/template-dsl/template-dsl.ts +++ /dev/null @@ -1,105 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/template-dsl - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Type, TTemplateLiteral, TLiteral, TTemplateLiteralKind, TNumber, TString, TBoolean, TBigInt, Assert, Ensure, UnionType } from '@sinclair/typebox' - -// ------------------------------------------------------------------------- -// Helpers -// ------------------------------------------------------------------------- -export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T -// ------------------------------------------------------------------------- -// TTemplateLiteralParser -// ------------------------------------------------------------------------- -// prettier-ignore -export type TTemplateLiteralParserUnionLiteral = - T extends `${infer L}|${infer R}` ? [TLiteral>, ...TTemplateLiteralParserUnionLiteral] : - T extends `${infer L}` ? [TLiteral>] : - [] -export type TTemplateLiteralParserUnion = UnionType> -// prettier-ignore -export type TTemplateLiteralParserTerminal = - T extends 'boolean' ? TBoolean : - T extends 'bigint' ? TBigInt : - T extends 'number' ? TNumber : - T extends 'string' ? TString : - TTemplateLiteralParserUnion -// prettier-ignore -export type TTemplateLiteralParserTemplate = - T extends `{${infer L}}${infer R}` ? [TTemplateLiteralParserTerminal, ...TTemplateLiteralParserTemplate] : - T extends `${infer L}$${infer R}` ? [TLiteral, ...TTemplateLiteralParserTemplate] : - T extends `${infer L}` ? [TLiteral] : - [] -export type TTemplateLiteralParser = Ensure, TTemplateLiteralKind[]>>> -// --------------------------------------------------------------------- -// TemplateLiteralParser -// --------------------------------------------------------------------- -namespace TemplateLiteralParser { - export function * ParseUnion(template: string): IterableIterator { - const trim = template.trim().replace(/"|'/g, '') - if(trim === 'boolean') return yield Type.Boolean() - if(trim === 'number') return yield Type.Number() - if(trim === 'bigint') return yield Type.BigInt() - if(trim === 'string') return yield Type.String() - const literals = trim.split('|').map(literal => Type.Literal(literal.trim())) - return yield literals.length === 0 ? Type.Never() : literals.length === 1 ? literals[0] : Type.Union(literals) - } - export function * ParseTerminal(template: string): IterableIterator { - if(template[1] !== '{') { - const L = Type.Literal('$') - const R = ParseLiteral(template.slice(1)) - return yield * [L, ...R] - } - for(let i = 2; i < template.length; i++) { - if(template[i] === '}') { - const L = ParseUnion(template.slice(2, i)) - const R = ParseLiteral(template.slice(i+1)) - return yield * [...L, ...R] - } - } - yield Type.Literal(template) - } - export function * ParseLiteral(template: string): IterableIterator { - for(let i = 0; i < template.length; i++) { - if(template[i] === '$') { - const L = Type.Literal(template.slice(0, i)) - const R = ParseTerminal(template.slice(i)) - return yield * [L, ...R] - } - } - yield Type.Literal(template) - } - export function Parse(template: string): TTemplateLiteral { - return Type.TemplateLiteral([...ParseLiteral(template)]) - } -} -// ------------------------------------------------------------------------------------------ -// TemplateLiteral DSL -// ------------------------------------------------------------------------------------------ -export function TemplateLiteral(template: T): TTemplateLiteralParser { - return TemplateLiteralParser.Parse(template) -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5b4dbac6e..347be6872 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.8", + "version": "0.28.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.8", + "version": "0.28.9", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index fd300dcf6..1cb30d7a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.8", + "version": "0.28.9", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index beeaa3547..19148dea4 100644 --- a/readme.md +++ b/readme.md @@ -83,7 +83,7 @@ License MIT - [References](#types-references) - [Recursive](#types-recursive) - [Conditional](#types-conditional) - - [Template](#types-template-literal) + - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) - [Rest](#types-rest) - [Guards](#types-guards) @@ -814,11 +814,13 @@ type T2 = Static // type T2 = string ### Template Literal Types -Template Literal types are supported with `Type.TemplateLiteral` +TypeBox supports Template Literal types using `Type.TemplateLiteral`. These types can be created using a simple template DSL syntax, however more complex template literals can be created by passing an array of literal and union types. The examples below show the template DSL syntax. ```typescript // TypeScript +type P = `/post/${string}/user/${number}` // type P = `/post/${string}/user/${number}` + type T = `option${'A'|'B'}` // type T = 'optionA' | 'optionB' type R = Record // type R = { @@ -828,13 +830,17 @@ type R = Record // type R = { // TypeBox -const T = Type.TemplateLiteral([ // const T = { - Type.Literal('option'), // pattern: '^option(A|B)$', - Type.Union([ // type: 'string' - Type.Literal('A'), // } - Type.Literal('B') - ]) -]) +const P = Type.TemplateLiteral('/post/${string}/user/${number}') + + // const P = { + // type: 'string', + // pattern: '^/post/(.*)/user/(0|[1-9][0-9]*)$' + // } + +const T = Type.TemplateLiteral('option${A|B}') // const T = { + // pattern: '^option(A|B)$', + // type: 'string' + // } const R = Type.Record(T, Type.String()) // const R = { // type: 'object', @@ -848,37 +854,48 @@ const R = Type.Record(T, Type.String()) // const R = { // } // } // } - -type T = Static // type T = 'optionA' | 'optionB' - -type R = Static // type R = { - // optionA: string - // optionB: string - // } ``` ### Indexed Access Types -Indexed Access types are supported with `Type.Index` +TypeBox supports Indexed Access types using `Type.Index`. This feature provides a consistent way to access property types without having to extract them from the underlying schema representation. Indexed accessors are supported for object and tuples, as well as nested union and intersect types. ```typescript -const T = Type.Object({ // type T = { - x: Type.Number(), // x: number - y: Type.String(), // y: string - z: Type.Boolean() // z: boolean -}) // } +const T = Type.Object({ // const T = { + x: Type.Number(), // type: 'object', + y: Type.String(), // required: ['x', 'y', 'z'], + z: Type.Boolean() // properties: { +}) // x: { type: 'number' }, + // y: { type: 'string' }, + // z: { type: 'string' }, + // } + // } -const A = Type.Index(T, ['x']) // type A = T['x'] +const A = Type.Index(T, ['x']) // const A = { type: 'number' } -const B = Type.Index(T, Type.KeyOf(T)) // type B = T[keyof T] +const B = Type.Index(T, ['x', 'y']) // const B = { + // anyOf: [ + // { type: 'number' }, + // { type: 'string' } + // ] + // } + +const C = Type.Index(T, Type.KeyOf(T)) // const C = { + // anyOf: [ + // { type: 'number' }, + // { type: 'string' }, + // { type: 'boolean' } + // ] + // } ``` + ### Rest Types -Rest parameters are supported with `Type.Rest`. This function is used to extract interior arrays from tuples to allow them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatination and variadic function composition. +Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatination as well as for variadic functions. ```typescript // TypeScript diff --git a/src/typebox.ts b/src/typebox.ts index 618e85a90..6f2bc1b13 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -51,6 +51,7 @@ export type UnionLast = UnionToIntersect 0 : ne export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] export type Discard = T extends [infer L, ...infer R] ? (L extends D ? Discard : [L, ...Discard]) : [] export type Flat = T extends [] ? [] : T extends [infer L] ? [...Flat] : T extends [infer L, ...infer R] ? [...Flat, ...Flat] : [T] +export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T export type Assert = T extends E ? T : never export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never export type Ensure = T extends infer U ? U : never @@ -622,6 +623,29 @@ export interface TSymbol extends TSchema, SchemaOptions { type: 'null' typeOf: 'Symbol' } +// ------------------------------------------------------------------------- +// TTemplateLiteralParserDsl +// ------------------------------------------------------------------------- +// prettier-ignore +export type TTemplateLiteralDslParserUnionLiteral = + T extends `${infer L}|${infer R}` ? [TLiteral>, ...TTemplateLiteralDslParserUnionLiteral] : + T extends `${infer L}` ? [TLiteral>] : + [] +export type TTemplateLiteralDslParserUnion = UnionType> +// prettier-ignore +export type TTemplateLiteralDslParserTerminal = + T extends 'boolean' ? TBoolean : + T extends 'bigint' ? TBigInt : + T extends 'number' ? TNumber : + T extends 'string' ? TString : + TTemplateLiteralDslParserUnion +// prettier-ignore +export type TTemplateLiteralDslParserTemplate = + T extends `{${infer L}}${infer R}` ? [TTemplateLiteralDslParserTerminal, ...TTemplateLiteralDslParserTemplate] : + T extends `${infer L}$${infer R}` ? [TLiteral, ...TTemplateLiteralDslParserTemplate] : + T extends `${infer L}` ? [TLiteral] : + [] +export type TTemplateLiteralDslParser = Ensure, TTemplateLiteralKind[]>>> // -------------------------------------------------------------------------- // TTemplateLiteral // -------------------------------------------------------------------------- @@ -2268,6 +2292,48 @@ export namespace TemplateLiteralGenerator { throw Error('TemplateLiteralGenerator: Unknown expression') } } +// --------------------------------------------------------------------- +// TemplateLiteralDslParser +// --------------------------------------------------------------------- +export namespace TemplateLiteralDslParser { + function* ParseUnion(template: string): IterableIterator { + const trim = template.trim().replace(/"|'/g, '') + if (trim === 'boolean') return yield Type.Boolean() + if (trim === 'number') return yield Type.Number() + if (trim === 'bigint') return yield Type.BigInt() + if (trim === 'string') return yield Type.String() + const literals = trim.split('|').map((literal) => Type.Literal(literal.trim())) + return yield literals.length === 0 ? Type.Never() : literals.length === 1 ? literals[0] : Type.Union(literals) + } + function* ParseTerminal(template: string): IterableIterator { + if (template[1] !== '{') { + const L = Type.Literal('$') + const R = ParseLiteral(template.slice(1)) + return yield* [L, ...R] + } + for (let i = 2; i < template.length; i++) { + if (template[i] === '}') { + const L = ParseUnion(template.slice(2, i)) + const R = ParseLiteral(template.slice(i + 1)) + return yield* [...L, ...R] + } + } + yield Type.Literal(template) + } + function* ParseLiteral(template: string): IterableIterator { + for (let i = 0; i < template.length; i++) { + if (template[i] === '$') { + const L = Type.Literal(template.slice(0, i)) + const R = ParseTerminal(template.slice(i)) + return yield* [L, ...R] + } + } + yield Type.Literal(template) + } + export function Parse(template_dsl: string): TTemplateLiteralKind[] { + return [...ParseLiteral(template_dsl)] + } +} // -------------------------------------------------------------------------- // TypeOrdinal: Used for auto $id generation // -------------------------------------------------------------------------- @@ -2606,9 +2672,16 @@ export class StandardTypeBuilder extends TypeBuilder { public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } + /** `[Experimental]` Creates a template literal type from dsl string */ + public TemplateLiteral(dsl: T, options?: SchemaOptions): TTemplateLiteralDslParser + /** `[Standard]` Creates a template literal type */ + public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral /** `[Standard]` Creates a template literal type */ - public TemplateLiteral(kinds: [...T], options: SchemaOptions = {}): TTemplateLiteral { - const pattern = TemplateLiteralPattern.Create(kinds) + public TemplateLiteral(unresolved: unknown, options: SchemaOptions = {}) { + // prettier-ignore + const pattern = (typeof unresolved === 'string') + ? TemplateLiteralPattern.Create(TemplateLiteralDslParser.Parse(unresolved)) + : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) } /** `[Standard]` Creates a Tuple type */ From c5da476c491329707f555fcbf8fd032a829f0624 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 8 May 2023 02:26:17 +0900 Subject: [PATCH 124/369] Documentation --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index 19148dea4..fc40b0023 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,6 @@ [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) -[![Install Size](https://packagephobia.com/badge?p=@sinclair/typebox)](https://packagephobia.com/result?p=@sinclair/typebox) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) From f80f0ff350fa887419eaf3e7f11357061240966b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 May 2023 16:11:20 +0900 Subject: [PATCH 125/369] Workbench (#431) --- readme.md | 38 +++++++++++++++++++++++++++++--------- workbench.png | Bin 0 -> 43713 bytes 2 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 workbench.png diff --git a/readme.md b/readme.md index fc40b0023..3145e7d64 100644 --- a/readme.md +++ b/readme.md @@ -108,6 +108,7 @@ License MIT - [Types](#typesystem-types) - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) +- [Workbench](#workbench) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) - [Validate](#benchmark-validate) @@ -1345,26 +1346,31 @@ import { TypeSystem } from '@sinclair/typebox/system' ### Types -Use the `Type(...)` function to create a custom type. This function will return a type factory function that can be used to construct the type. The following creates a Point type. +Use the `Type(...)` function to create custom types. This function lets you specify custom value assertion logic and will return a type factory function which is used to instance the type. This function accepts two generic arguments, the first is the inference type, the second is options used to constrain the type. The following creates a Vector type. ```typescript -type PointOptions = { } // The Type Options +type VectorOptions = { abs: boolean } -type PointType = { x: number, y: number } // The Static Type +type Vector = { x: number, y: number } -const Point = TypeSystem.Type('Point', (options, value) => { +const Vector = TypeSystem.Type('Vector', (options, value) => { return ( typeof value === 'object' && value !== null && - typeof value.x === 'number' && - typeof value.y === 'number' + 'x' in value && typeof value.x === 'number' && + 'y' in value && typeof value.y === 'number' && + (options.abs ? (value.x === Math.abs(value.x) && value.y === Math.abs(value.y)) : true) ) }) -const T = Point() +const T = Vector({ abs: true }) -type T = Static // type T = { x: number, y: number } +type T = Static // type T = Vector -const R = Value.Check(T, { x: 1, y: 2 }) // const R = true +const R1 = Value.Check(T, { x: 1, y: 1 }) // const R1 = true + +const R2 = Value.Check(T, { x: 1, y: '1' }) // const R2 = false + +const R3 = Value.Check(T, { x: 1, y: -1 }) // const R3 = false ``` @@ -1409,6 +1415,20 @@ TypeSystem.AllowArrayObjects = true TypeSystem.AllowNaN = true ``` + + +## Workbench + +TypeBox offers a web based code generation tool that can be used to create TypeBox types from TypeScript type definitions. This tool is written to prototype new TypeBox features, but can be used to rapidly convert TypeScript type definitions in to TypeBox types as well as raw JSON Schema. + +[Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) + +

      + + + +
      + ## Benchmark diff --git a/workbench.png b/workbench.png new file mode 100644 index 0000000000000000000000000000000000000000..817b9d17445d7ab66329ade51e302536cda28385 GIT binary patch literal 43713 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 literal 0 HcmV?d00001 From 8ca61abeb5f66a19493b06847c7c9c48bf5a2397 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 May 2023 22:26:28 +0900 Subject: [PATCH 126/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3145e7d64..6ff5dbb9b 100644 --- a/readme.md +++ b/readme.md @@ -11,8 +11,8 @@ [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) From 9b33feb5fad14c3833e37113e13a313ba7f12bd5 Mon Sep 17 00:00:00 2001 From: Grzegorz Uriasz Date: Sat, 13 May 2023 09:00:12 -0700 Subject: [PATCH 127/369] Corrected Scope for Additional Properties (#434) --- src/compiler/compiler.ts | 4 +- test/runtime/compiler/intersect.ts | 7 +++ test/runtime/compiler/object.ts | 85 ++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index bf0d67a91..b2ffb9c76 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -216,7 +216,7 @@ export namespace TypeCompiler { yield `${check1} && ${check2}` } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) - const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, 'value[key]')})` + const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})` yield `${check1} && ${check2}` } else { yield `${check1}` @@ -273,7 +273,7 @@ export namespace TypeCompiler { } } if (typeof schema.additionalProperties === 'object') { - const expression = CreateExpression(schema.additionalProperties, references, 'value[key]') + const expression = CreateExpression(schema.additionalProperties, references, `${value}[key]`) const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]` yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index 0f9d5272c..ce7f934c7 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -54,6 +54,13 @@ describe('type/compiler/Intersect', () => { Ok(T, { x: 1, y: 2, z: 3 }) Fail(T, { x: 1, y: 2, z: '1' }) }) + it('Should intersect two nested objects and allow unevaluated properties of number', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const T = Type.Object({ nested: Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) }) + Ok(T, { nested: { x: 1, y: 2, z: 3 } }) + Fail(T, { nested: { x: 1, y: 2, z: '1' } }) + }) it('Should intersect two union objects with overlapping properties of the same type', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ x: Type.Number() })]) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 187207b3c..f1299f7e1 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -215,6 +215,91 @@ describe('type/compiler/Object', () => { z: 3, }) }) + + it('Should validate nested schema additional properties of string', () => { + const T = Type.Object({ + nested: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }) + Ok(T, { + nested: { + x: 1, + y: 2, + z: 'hello', + }, + }) + Fail(T, { + nested: { + x: 1, + y: 2, + z: 3, + }, + }) + }) + it('Should validate nested schema additional properties of array', () => { + const T = Type.Object({ + nested: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Array(Type.Number()), + }, + ), + }) + Ok(T, { + nested: { + x: 1, + y: 2, + z: [0, 1, 2], + }, + }) + Fail(T, { + nested: { + x: 1, + y: 2, + z: 3, + }, + }) + }) + it('Should validate nested schema additional properties of object', () => { + const T = Type.Object({ + nested: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.Object({ + z: Type.Number(), + }), + }, + ), + }) + Ok(T, { + nested: { + x: 1, + y: 2, + z: { z: 1 }, + }, + }) + Fail(T, { + nested: { + x: 1, + y: 2, + z: 3, + }, + }) + }) + it('Should check for property key if property type is undefined', () => { const T = Type.Object({ x: Type.Undefined() }) Ok(T, { x: undefined }) From f740515feb51d35826425caae4a880f6650ca7ae Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 14 May 2023 01:03:49 +0900 Subject: [PATCH 128/369] Revision 0.28.10 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 347be6872..640ff4d4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.9", + "version": "0.28.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.9", + "version": "0.28.10", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 1cb30d7a7..07cc9d478 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.9", + "version": "0.28.10", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From 41dae20a284f93567e03b4244eeb104d6b4508c3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 17 May 2023 12:08:41 +0900 Subject: [PATCH 129/369] Additional Fast Path Undefined Checks (#439) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 8 ++++++++ src/value/check.ts | 4 ++-- test/runtime/compiler/object.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 640ff4d4c..5e568c440 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 07cc9d478..f889ab997 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.10", + "version": "0.28.11", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 6f2bc1b13..a6c405a49 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1353,6 +1353,14 @@ export namespace TypeGuard { export namespace ExtendsUndefined { export function Check(schema: TSchema): boolean { if (schema[Kind] === 'Undefined') return true + if (schema[Kind] === 'Not') { + const not = schema as TNot + return Check(not.allOf[1]) + } + if (schema[Kind] === 'Intersect') { + const intersect = schema as TIntersect + return intersect.allOf.every((schema) => Check(schema)) + } if (schema[Kind] === 'Union') { const union = schema as TUnion return union.anyOf.some((schema) => Check(schema)) diff --git a/src/value/check.ts b/src/value/check.ts index 9b62e00fe..0700dbdd7 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -238,8 +238,8 @@ export namespace ValueCheck { if (!Visit(property, references, value[knownKey])) { return false } - if (Types.ExtendsUndefined.Check(property)) { - return knownKey in value + if (Types.ExtendsUndefined.Check(property) && !(knownKey in value)) { + return false } } else { if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index f1299f7e1..872b987b7 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -2,6 +2,33 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Object', () => { + // ----------------------------------------------------- + // TypeCompiler Only + // ----------------------------------------------------- + it('Should handle extends undefined check 1', () => { + const T = Type.Object({ + A: Type.Not(Type.Number(), Type.Undefined()), + B: Type.Union([Type.Number(), Type.Undefined()]), + C: Type.Intersect([Type.Undefined(), Type.Undefined()]), + }) + Ok(T, { + A: undefined, + B: undefined, + C: undefined, + }) + }) + // https://github.com/sinclairzx81/typebox/issues/437 + it('Should handle extends undefined check 2', () => { + const T = Type.Object({ + A: Type.Not(Type.Null(), Type.Undefined()), + }) + Ok(T, { A: undefined }) + Fail(T, { A: null }) + Fail(T, {}) + }) + // ----------------------------------------------------- + // Standard Checks + // ----------------------------------------------------- it('Should not validate a number', () => { const T = Type.Object({}) Fail(T, 42) From 81724d1a72ea18faa36560dac9df5dc402fa14bb Mon Sep 17 00:00:00 2001 From: Erfan Safari Date: Wed, 17 May 2023 12:25:04 +0330 Subject: [PATCH 130/369] Documentation (#436) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 6ff5dbb9b..27e2563bc 100644 --- a/readme.md +++ b/readme.md @@ -1277,7 +1277,7 @@ The TypeCompiler is provided as an optional import. import { TypeCompiler } from '@sinclair/typebox/compiler' ``` -Use the `Compile(...)` function to compile a type. +Use the `Compile(...)` function to compile a type. Note that compilation is an expensive operation that should typically be performed once per type during application start up. TypeBox does not cache previously compiled types, so applications are expected to hold references to each compiled type for the lifetime of the application. ```typescript const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck Date: Thu, 18 May 2023 14:38:53 +0900 Subject: [PATCH 131/369] Documentation --- example/typedef/typedef.ts | 12 +++++++++--- readme.md | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 544d1b893..131f7ed55 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -267,11 +267,17 @@ export namespace TypeDefCheck { return IsInt(value, 0, Number.MAX_SAFE_INTEGER) } function Union(schema: TUnion, value: unknown): boolean { - return IsObject(value) && + if (!( + IsObject(value) && schema.discriminator in value && IsString(value[schema.discriminator]) && - value[schema.discriminator] as any in schema.mapping && - Visit(schema.mapping[value[schema.discriminator] as any], value) + value[schema.discriminator] as any in schema.mapping + )) return false + // We shouldn't create objects just to omit the discriminator (optimize) + const inner = globalThis.Object.keys(value).reduce((acc, key) => { + return key === schema.discriminator ? acc : { [key]: value[key] } + }, {}) + return Visit(schema.mapping[value[schema.discriminator] as any], inner) } function Visit(schema: Types.TSchema, value: unknown): boolean { const anySchema = schema as any diff --git a/readme.md b/readme.md index 27e2563bc..e79f444df 100644 --- a/readme.md +++ b/readme.md @@ -1419,7 +1419,7 @@ TypeSystem.AllowNaN = true ## Workbench -TypeBox offers a web based code generation tool that can be used to create TypeBox types from TypeScript type definitions. This tool is written to prototype new TypeBox features, but can be used to rapidly convert TypeScript type definitions in to TypeBox types as well as raw JSON Schema. +TypeBox offers a small web based code generation tool that can be used to convert TypeScript types into TypeBox type definitions as well as a variety of other formats. [Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) From 00939c13465a052bba8b43cd3715f27f909ecd2b Mon Sep 17 00:00:00 2001 From: Pierre Dahmani Date: Tue, 23 May 2023 05:06:27 +0200 Subject: [PATCH 132/369] Ecosystem (#435) --- readme.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index e79f444df..a65a96b2d 100644 --- a/readme.md +++ b/readme.md @@ -109,6 +109,7 @@ License MIT - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) - [Workbench](#workbench) +- [Ecosystem](#ecosystem) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) - [Validate](#benchmark-validate) @@ -1424,11 +1425,22 @@ TypeBox offers a small web based code generation tool that can be used to conver [Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/)
      - +
      + + +## Ecosystem + +| Package Name | Short Description | +| ------------- | ------------- | +| [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | +| [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | +| [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | +| [ts2typebox](https://github.com/xddq/ts2typebox) | Cli tool to generate TypeBox JSON schemas based on your Typescript types | + ## Benchmark From 21b83967894e6a0736cd455a4f7a870022e46b09 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 23 May 2023 12:13:02 +0900 Subject: [PATCH 133/369] Ecosystem --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index a65a96b2d..8267839d8 100644 --- a/readme.md +++ b/readme.md @@ -1434,6 +1434,8 @@ TypeBox offers a small web based code generation tool that can be used to conver ## Ecosystem +The following is a list of community packages that provide additional framework integration and tooling support for TypeBox. + | Package Name | Short Description | | ------------- | ------------- | | [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | From 479ad6777f2b596122b89a1aa6337bcd9cc3c6c0 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 23 May 2023 12:13:22 +0900 Subject: [PATCH 134/369] Ecosystem --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8267839d8..f646c72a5 100644 --- a/readme.md +++ b/readme.md @@ -1436,7 +1436,7 @@ TypeBox offers a small web based code generation tool that can be used to conver The following is a list of community packages that provide additional framework integration and tooling support for TypeBox. -| Package Name | Short Description | +| Package | Description | | ------------- | ------------- | | [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | From 1903324127bf656343900ba2c8cd1b9406673c15 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 25 May 2023 14:49:16 +0900 Subject: [PATCH 135/369] Support Enum Inference In Template Literal (#445) --- package-lock.json | 18 +++++++-------- package.json | 4 ++-- readme.md | 2 +- src/typebox.ts | 2 +- test/runtime/compiler/template-literal.ts | 24 +++++++++++++++++++ test/static/enum.ts | 16 +++++++------ test/static/template-literal.ts | 28 +++++++++++++++++++++++ 7 files changed, 74 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e568c440..a5be00a23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.11", + "version": "0.28.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.11", + "version": "0.28.12", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", @@ -18,7 +18,7 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.0.2" + "typescript": "^5.0.4" } }, "node_modules/@esbuild/linux-loong64": { @@ -1410,9 +1410,9 @@ } }, "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2442,9 +2442,9 @@ "dev": true }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index f889ab997..a6df44510 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.11", + "version": "0.28.12", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -42,6 +42,6 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.0.2" + "typescript": "^5.0.4" } } diff --git a/readme.md b/readme.md index f646c72a5..282c4febd 100644 --- a/readme.md +++ b/readme.md @@ -1434,7 +1434,7 @@ TypeBox offers a small web based code generation tool that can be used to conver ## Ecosystem -The following is a list of community packages that provide additional framework integration and tooling support for TypeBox. +The following is a list of community packages that provide general tooling and framework support for TypeBox. | Package | Description | | ------------- | ------------- | diff --git a/src/typebox.ts b/src/typebox.ts index a6c405a49..de871f3b1 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -244,7 +244,7 @@ export interface TEnumOption { export interface TEnum = Record> extends TSchema { [Kind]: 'Union' static: T[keyof T] - anyOf: TLiteral[] + anyOf: TLiteral[] } // -------------------------------------------------------------------------- // TExtends diff --git a/test/runtime/compiler/template-literal.ts b/test/runtime/compiler/template-literal.ts index fb2ebdadb..d0357ae96 100644 --- a/test/runtime/compiler/template-literal.ts +++ b/test/runtime/compiler/template-literal.ts @@ -183,4 +183,28 @@ describe('type/compiler/TemplateLiteral', () => { Ok(T, 'Adddd') Fail(T, 'X') }) + it('Should validate enum (implicit)', () => { + enum E { + A, + B, + C, + } + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Enum(E)]) + Ok(T, 'hello0') + Ok(T, 'hello1') + Ok(T, 'hello2') + Fail(T, 'hello3') + }) + it('Should validate enum (explicit)', () => { + enum E { + A, + B = 'B', + C = 'C', + } + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Enum(E)]) + Ok(T, 'hello0') + Ok(T, 'helloB') + Ok(T, 'helloC') + Fail(T, 'helloD') + }) }) diff --git a/test/static/enum.ts b/test/static/enum.ts index aee807c9f..e44ae72aa 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -1,12 +1,14 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' -enum E { - A, - B = 'hello', - C = 42, -} +{ + enum E { + A, + B = 'hello', + C = 42, + } -const T = Type.Enum(E) + const T = Type.Enum(E) -Expect(T).ToBe>() // ? + Expect(T).ToBe>() // ? +} diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts index c46d94af3..1b8ebf3b1 100644 --- a/test/static/template-literal.ts +++ b/test/static/template-literal.ts @@ -42,3 +42,31 @@ import { Type } from '@sinclair/typebox' const T = Type.TemplateLiteral([Type.Boolean()]) Expect(T).ToInfer<`${boolean}`>() } +{ + // Enum Implicit + enum E { + A, + B, + C, + } + const A = Type.Enum(E) + const T = Type.TemplateLiteral([Type.Literal('hello'), A]) + Expect(T).ToInfer<'hello0' | 'hello1' | 'hello2'>() +} +{ + // Enum Explicit + enum E { + A, + B = 'B', + C = 'C', + } + const A = Type.Enum(E) + const T = Type.TemplateLiteral([Type.Literal('hello'), A]) + Expect(T).ToInfer<'hello0' | 'helloB' | 'helloC'>() +} +{ + // Enum Object Explicit + const A = Type.Enum(Object.freeze({ a: 'A', b: 'B' })) + const T = Type.TemplateLiteral([Type.Literal('hello'), A]) + Expect(T).ToInfer<'helloA' | 'helloB'>() +} From 963447cee6fae1c313927f22d69d50f46d750fff Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 26 May 2023 13:33:03 +0900 Subject: [PATCH 136/369] Assert Array Constraints Before Elements (#448) --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5be00a23..cf8638870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.12", + "version": "0.28.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.12", + "version": "0.28.13", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index a6df44510..98d695221 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.12", + "version": "0.28.13", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index b2ffb9c76..d737b58d6 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -171,10 +171,10 @@ export namespace TypeCompiler { } function* Array(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { const expression = CreateExpression(schema.items, references, 'value') - yield `Array.isArray(${value}) && ${value}.every(value => ${expression})` if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` + yield `Array.isArray(${value}) && ${value}.every(value => ${expression})` } function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` From 5a5160984dc94fff662f2a69f8252e2d88029363 Mon Sep 17 00:00:00 2001 From: Pierre Dahmani Date: Sun, 28 May 2023 04:12:12 +0200 Subject: [PATCH 137/369] Ecosystem (#450) --- readme.md | 119 +++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/readme.md b/readme.md index 282c4febd..5b9bcbbcb 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@

      TypeBox

      JSON Schema Type Builder with Static Type Resolution for TypeScript

      - +
      @@ -65,7 +65,7 @@ type T = Static // type T = { TypeBox is a runtime type builder 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 assertion rules of the TypeScript compiler. TypeBox enables one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. -This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. +This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. License MIT @@ -145,23 +145,23 @@ type T = { const T = Type.Object({ // const T = { id: Type.String(), // type: 'object', - name: Type.String(), // properties: { - timestamp: Type.Integer() // id: { -}) // type: 'string' + name: Type.String(), // properties: { + timestamp: Type.Integer() // id: { +}) // type: 'string' // }, - // name: { - // type: 'string' + // name: { + // type: 'string' // }, - // timestamp: { - // type: 'integer' + // timestamp: { + // type: 'integer' // } - // }, + // }, // required: [ // 'id', // 'name', // 'timestamp' // ] - // } + // } //-------------------------------------------------------------------------------------------- // @@ -186,7 +186,7 @@ import { Value } from '@sinclair/typebox/value' function receive(value: T) { // ... as a Static Type if(Value.Check(T, value)) { // ... as a JSON Schema - + // ok... } } @@ -196,7 +196,7 @@ function receive(value: T) { // ... as a Static Type ## Types -TypeBox types are JSON schema fragments that can be composed into more complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. +TypeBox types are JSON schema fragments that can be composed into more complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. @@ -645,22 +645,22 @@ You can pass JSON Schema options on the last argument of any type. Option hints ```typescript // String must be an email -const T = Type.String({ // const T = { +const T = Type.String({ // const T = { format: 'email' // type: 'string', -}) // format: 'email' +}) // format: 'email' // } // Mumber must be a multiple of 2 -const T = Type.Number({ // const T = { - multipleOf: 2 // type: 'number', -}) // multipleOf: 2 +const T = Type.Number({ // const T = { + multipleOf: 2 // type: 'number', +}) // multipleOf: 2 // } // Array must have at least 5 integer values -const T = Type.Array(Type.Integer(), { // const T = { +const T = Type.Array(Type.Integer(), { // const T = { minItems: 5 // type: 'array', -}) // minItems: 5, - // items: { +}) // minItems: 5, + // items: { // type: 'integer' // } // } @@ -737,7 +737,7 @@ const T = Type.String({ $id: 'T' }) // const T = { // $id: 'T', // type: 'string' // } - + const R = Type.Ref(T) // const R = { // $ref: 'T' // } @@ -808,7 +808,7 @@ type T0 = Static // type T0 = false type T1 = Static // type T1 = number -type T2 = Static // type T2 = string +type T2 = Static // type T2 = string ``` @@ -876,16 +876,16 @@ const T = Type.Object({ // const T = { const A = Type.Index(T, ['x']) // const A = { type: 'number' } -const B = Type.Index(T, ['x', 'y']) // const B = { +const B = Type.Index(T, ['x', 'y']) // const B = { // anyOf: [ - // { type: 'number' }, + // { type: 'number' }, // { type: 'string' } // ] // } -const C = Type.Index(T, Type.KeyOf(T)) // const C = { +const C = Type.Index(T, Type.KeyOf(T)) // const C = { // anyOf: [ - // { type: 'number' }, + // { type: 'number' }, // { type: 'string' }, // { type: 'boolean' } // ] @@ -987,7 +987,7 @@ import { Type, TypeGuard } from '@sinclair/typebox' const T = Type.String() if(TypeGuard.TString(T)) { - + // T is TString } ``` @@ -1012,12 +1012,12 @@ const T = Type.Object({ // const T = { // } const U = Type.Strict(T) // const U = { - // type: 'object', - // properties: { - // name: { - // type: 'string' - // } - // } + // type: 'object', + // properties: { + // name: { + // type: 'string' + // } + // } // } ``` @@ -1183,21 +1183,21 @@ const R = [...Value.Errors(T, { x: '42' })] // const R = [{ Use the Mutate function to perform a deep mutable value assignment while retaining internal references. ```typescript -const Y = { z: 1 } // const Y = { z: 1 } +const Y = { z: 1 } // const Y = { z: 1 } const X = { y: Y } // const X = { y: { z: 1 } } -const A = { x: X } // const A = { x: { y: { z: 1 } } } +const A = { x: X } // const A = { x: { y: { z: 1 } } } -Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } +Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } const R0 = A.x.y.z === 2 // const R0 = 2 const R1 = A.x.y === Y // const R1 = true const R2 = A.x === X // const R2 = true -``` +``` @@ -1241,29 +1241,29 @@ import addFormats from 'ajv-formats' import Ajv from 'ajv' const ajv = addFormats(new Ajv({}), [ - 'date-time', - 'time', - 'date', - 'email', - 'hostname', - 'ipv4', - 'ipv6', - 'uri', - 'uri-reference', + 'date-time', + 'time', + 'date', + 'email', + 'hostname', + 'ipv4', + 'ipv6', + 'uri', + 'uri-reference', 'uuid', - 'uri-template', - 'json-pointer', - 'relative-json-pointer', + 'uri-template', + 'json-pointer', + 'relative-json-pointer', 'regex' ]) -const C = ajv.compile(Type.Object({ +const C = ajv.compile(Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() })) -const R = C({ x: 1, y: 2, z: 3 }) // const R = true +const R = C({ x: 1, y: 2, z: 3 }) // const R = true ``` @@ -1350,15 +1350,15 @@ import { TypeSystem } from '@sinclair/typebox/system' Use the `Type(...)` function to create custom types. This function lets you specify custom value assertion logic and will return a type factory function which is used to instance the type. This function accepts two generic arguments, the first is the inference type, the second is options used to constrain the type. The following creates a Vector type. ```typescript -type VectorOptions = { abs: boolean } +type VectorOptions = { abs: boolean } type Vector = { x: number, y: number } const Vector = TypeSystem.Type('Vector', (options, value) => { return ( typeof value === 'object' && value !== null && - 'x' in value && typeof value.x === 'number' && - 'y' in value && typeof value.y === 'number' && + 'x' in value && typeof value.x === 'number' && + 'y' in value && typeof value.y === 'number' && (options.abs ? (value.x === Math.abs(value.x) && value.y === Math.abs(value.y)) : true) ) }) @@ -1383,7 +1383,7 @@ Use the `Format(...)` function to create a custom string format. The following c ```typescript TypeSystem.Format('lowercase', value => value === value.toLowerCase()) // format should be lowercase -const T = Type.String({ format: 'lowercase' }) +const T = Type.String({ format: 'lowercase' }) const A = Value.Check(T, 'Hello') // const A = false @@ -1407,13 +1407,13 @@ TypeSystem.ExactOptionalPropertyTypes = true // // const A: {} = [] - allowed in TS -TypeSystem.AllowArrayObjects = true +TypeSystem.AllowArrayObjects = true // Allow numeric values to be NaN or + or - Infinity (default is false) // // const A: number = NaN - allowed in TS -TypeSystem.AllowNaN = true +TypeSystem.AllowNaN = true ``` @@ -1441,7 +1441,8 @@ The following is a list of community packages that provide general tooling and f | [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | -| [ts2typebox](https://github.com/xddq/ts2typebox) | Cli tool to generate TypeBox JSON schemas based on your Typescript types | +| [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from JSON schemas | +| [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | From 1be023b6d0c7b5906d7c766ea9fcdb6aeacf1075 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 30 May 2023 04:18:11 +0900 Subject: [PATCH 138/369] Assert Key for Any and Unknown Property Types (#452) --- example/typedef/typedef.ts | 24 +++--- package-lock.json | 4 +- package.json | 2 +- readme.md | 130 ++++++++++++++++---------------- src/compiler/compiler.ts | 8 +- src/value/check.ts | 8 +- test/runtime/compiler/object.ts | 28 +++++++ 7 files changed, 122 insertions(+), 82 deletions(-) diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 131f7ed55..c89b52a56 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -175,18 +175,18 @@ export interface TUnion('TypeDef:Array', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:String', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:String', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => TypeDefCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => TypeDefCheck.Check(schema, value)) // -------------------------------------------------------------------------- // TypeDefCheck // -------------------------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index cf8638870..75f6630b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.13", + "version": "0.28.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.13", + "version": "0.28.14", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 98d695221..1a60169aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.13", + "version": "0.28.14", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 5b9bcbbcb..673870cb6 100644 --- a/readme.md +++ b/readme.md @@ -1462,35 +1462,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 243 ms' │ ' 8 ms' │ ' 30.38 x' │ -│ Literal_Number │ 1000 │ ' 195 ms' │ ' 5 ms' │ ' 39.00 x' │ -│ Literal_Boolean │ 1000 │ ' 162 ms' │ ' 4 ms' │ ' 40.50 x' │ -│ Primitive_Number │ 1000 │ ' 168 ms' │ ' 6 ms' │ ' 28.00 x' │ -│ Primitive_String │ 1000 │ ' 164 ms' │ ' 5 ms' │ ' 32.80 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 214 ms' │ ' 9 ms' │ ' 23.78 x' │ -│ Primitive_Boolean │ 1000 │ ' 132 ms' │ ' 4 ms' │ ' 33.00 x' │ -│ Primitive_Null │ 1000 │ ' 148 ms' │ ' 4 ms' │ ' 37.00 x' │ -│ Object_Unconstrained │ 1000 │ ' 1158 ms' │ ' 30 ms' │ ' 38.60 x' │ -│ Object_Constrained │ 1000 │ ' 1263 ms' │ ' 25 ms' │ ' 50.52 x' │ -│ Object_Vector3 │ 1000 │ ' 384 ms' │ ' 7 ms' │ ' 54.86 x' │ -│ Object_Box3D │ 1000 │ ' 1932 ms' │ ' 27 ms' │ ' 71.56 x' │ -│ Tuple_Primitive │ 1000 │ ' 478 ms' │ ' 14 ms' │ ' 34.14 x' │ -│ Tuple_Object │ 1000 │ ' 1232 ms' │ ' 14 ms' │ ' 88.00 x' │ -│ Composite_Intersect │ 1000 │ ' 671 ms' │ ' 17 ms' │ ' 39.47 x' │ -│ Composite_Union │ 1000 │ ' 537 ms' │ ' 18 ms' │ ' 29.83 x' │ -│ Math_Vector4 │ 1000 │ ' 816 ms' │ ' 14 ms' │ ' 58.29 x' │ -│ Math_Matrix4 │ 1000 │ ' 417 ms' │ ' 6 ms' │ ' 69.50 x' │ -│ Array_Primitive_Number │ 1000 │ ' 378 ms' │ ' 5 ms' │ ' 75.60 x' │ -│ Array_Primitive_String │ 1000 │ ' 353 ms' │ ' 6 ms' │ ' 58.83 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 279 ms' │ ' 5 ms' │ ' 55.80 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1794 ms' │ ' 20 ms' │ ' 89.70 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1586 ms' │ ' 19 ms' │ ' 83.47 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 791 ms' │ ' 13 ms' │ ' 60.85 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1638 ms' │ ' 17 ms' │ ' 96.35 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 796 ms' │ ' 17 ms' │ ' 46.82 x' │ -│ Array_Composite_Union │ 1000 │ ' 798 ms' │ ' 15 ms' │ ' 53.20 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1127 ms' │ ' 14 ms' │ ' 80.50 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 677 ms' │ ' 9 ms' │ ' 75.22 x' │ +│ Literal_String │ 1000 │ ' 230 ms' │ ' 7 ms' │ ' 32.86 x' │ +│ Literal_Number │ 1000 │ ' 171 ms' │ ' 4 ms' │ ' 42.75 x' │ +│ Literal_Boolean │ 1000 │ ' 148 ms' │ ' 4 ms' │ ' 37.00 x' │ +│ Primitive_Number │ 1000 │ ' 160 ms' │ ' 6 ms' │ ' 26.67 x' │ +│ Primitive_String │ 1000 │ ' 149 ms' │ ' 6 ms' │ ' 24.83 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 199 ms' │ ' 9 ms' │ ' 22.11 x' │ +│ Primitive_Boolean │ 1000 │ ' 126 ms' │ ' 3 ms' │ ' 42.00 x' │ +│ Primitive_Null │ 1000 │ ' 141 ms' │ ' 4 ms' │ ' 35.25 x' │ +│ Object_Unconstrained │ 1000 │ ' 1105 ms' │ ' 27 ms' │ ' 40.93 x' │ +│ Object_Constrained │ 1000 │ ' 1178 ms' │ ' 22 ms' │ ' 53.55 x' │ +│ Object_Vector3 │ 1000 │ ' 368 ms' │ ' 8 ms' │ ' 46.00 x' │ +│ Object_Box3D │ 1000 │ ' 1668 ms' │ ' 28 ms' │ ' 59.57 x' │ +│ Tuple_Primitive │ 1000 │ ' 446 ms' │ ' 12 ms' │ ' 37.17 x' │ +│ Tuple_Object │ 1000 │ ' 1146 ms' │ ' 15 ms' │ ' 76.40 x' │ +│ Composite_Intersect │ 1000 │ ' 661 ms' │ ' 18 ms' │ ' 36.72 x' │ +│ Composite_Union │ 1000 │ ' 513 ms' │ ' 17 ms' │ ' 30.18 x' │ +│ Math_Vector4 │ 1000 │ ' 767 ms' │ ' 10 ms' │ ' 76.70 x' │ +│ Math_Matrix4 │ 1000 │ ' 396 ms' │ ' 10 ms' │ ' 39.60 x' │ +│ Array_Primitive_Number │ 1000 │ ' 353 ms' │ ' 8 ms' │ ' 44.13 x' │ +│ Array_Primitive_String │ 1000 │ ' 358 ms' │ ' 5 ms' │ ' 71.60 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 265 ms' │ ' 3 ms' │ ' 88.33 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1476 ms' │ ' 19 ms' │ ' 77.68 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1698 ms' │ ' 20 ms' │ ' 84.90 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 654 ms' │ ' 9 ms' │ ' 72.67 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1492 ms' │ ' 15 ms' │ ' 99.47 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 1045 ms' │ ' 16 ms' │ ' 65.31 x' │ +│ Array_Composite_Union │ 1000 │ ' 702 ms' │ ' 14 ms' │ ' 50.14 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1016 ms' │ ' 12 ms' │ ' 84.67 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 621 ms' │ ' 5 ms' │ ' 124.20 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1504,37 +1504,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ -│ Literal_Boolean │ 1000000 │ ' 19 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_Number │ 1000000 │ ' 24 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ -│ Primitive_String │ 1000000 │ ' 26 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 159 ms' │ ' 45 ms' │ ' 36 ms' │ ' 1.25 x' │ -│ Primitive_Boolean │ 1000000 │ ' 22 ms' │ ' 17 ms' │ ' 10 ms' │ ' 1.70 x' │ -│ Primitive_Null │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Object_Unconstrained │ 1000000 │ ' 914 ms' │ ' 34 ms' │ ' 26 ms' │ ' 1.31 x' │ -│ Object_Constrained │ 1000000 │ ' 1095 ms' │ ' 50 ms' │ ' 38 ms' │ ' 1.32 x' │ -│ Object_Vector3 │ 1000000 │ ' 414 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Object_Box3D │ 1000000 │ ' 1933 ms' │ ' 55 ms' │ ' 53 ms' │ ' 1.04 x' │ -│ Object_Recursive │ 1000000 │ ' 4995 ms' │ ' 378 ms' │ ' 177 ms' │ ' 2.14 x' │ -│ Tuple_Primitive │ 1000000 │ ' 168 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ -│ Tuple_Object │ 1000000 │ ' 681 ms' │ ' 31 ms' │ ' 19 ms' │ ' 1.63 x' │ -│ Composite_Intersect │ 1000000 │ ' 718 ms' │ ' 25 ms' │ ' 15 ms' │ ' 1.67 x' │ -│ Composite_Union │ 1000000 │ ' 511 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ -│ Math_Vector4 │ 1000000 │ ' 285 ms' │ ' 23 ms' │ ' 12 ms' │ ' 1.92 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1197 ms' │ ' 39 ms' │ ' 28 ms' │ ' 1.39 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 294 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ -│ Array_Primitive_String │ 1000000 │ ' 251 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 131 ms' │ ' 22 ms' │ ' 14 ms' │ ' 1.57 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 5249 ms' │ ' 69 ms' │ ' 56 ms' │ ' 1.23 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5299 ms' │ ' 127 ms' │ ' 123 ms' │ ' 1.03 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 19609 ms' │ ' 1711 ms' │ ' 608 ms' │ ' 2.81 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 734 ms' │ ' 38 ms' │ ' 30 ms' │ ' 1.27 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 2843 ms' │ ' 63 ms' │ ' 51 ms' │ ' 1.24 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 2794 ms' │ ' 43 ms' │ ' 36 ms' │ ' 1.19 x' │ -│ Array_Composite_Union │ 1000000 │ ' 1892 ms' │ ' 66 ms' │ ' 33 ms' │ ' 2.00 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1177 ms' │ ' 37 ms' │ ' 23 ms' │ ' 1.61 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 5115 ms' │ ' 110 ms' │ ' 85 ms' │ ' 1.29 x' │ +│ Literal_String │ 1000000 │ ' 22 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 19 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Literal_Boolean │ 1000000 │ ' 18 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_Number │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_String │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 171 ms' │ ' 42 ms' │ ' 36 ms' │ ' 1.17 x' │ +│ Primitive_Boolean │ 1000000 │ ' 20 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_Null │ 1000000 │ ' 21 ms' │ ' 16 ms' │ ' 8 ms' │ ' 2.00 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1099 ms' │ ' 31 ms' │ ' 25 ms' │ ' 1.24 x' │ +│ Object_Constrained │ 1000000 │ ' 1224 ms' │ ' 51 ms' │ ' 36 ms' │ ' 1.42 x' │ +│ Object_Vector3 │ 1000000 │ ' 420 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Object_Box3D │ 1000000 │ ' 2012 ms' │ ' 53 ms' │ ' 43 ms' │ ' 1.23 x' │ +│ Object_Recursive │ 1000000 │ ' 5080 ms' │ ' 320 ms' │ ' 150 ms' │ ' 2.13 x' │ +│ Tuple_Primitive │ 1000000 │ ' 154 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Tuple_Object │ 1000000 │ ' 749 ms' │ ' 27 ms' │ ' 18 ms' │ ' 1.50 x' │ +│ Composite_Intersect │ 1000000 │ ' 775 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ +│ Composite_Union │ 1000000 │ ' 533 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Math_Vector4 │ 1000000 │ ' 275 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1136 ms' │ ' 37 ms' │ ' 26 ms' │ ' 1.42 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 316 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Array_Primitive_String │ 1000000 │ ' 239 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 151 ms' │ ' 20 ms' │ ' 15 ms' │ ' 1.33 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 5809 ms' │ ' 64 ms' │ ' 56 ms' │ ' 1.14 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 6210 ms' │ ' 123 ms' │ ' 101 ms' │ ' 1.22 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 21779 ms' │ ' 1466 ms' │ ' 548 ms' │ ' 2.68 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 730 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3259 ms' │ ' 63 ms' │ ' 49 ms' │ ' 1.29 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3276 ms' │ ' 44 ms' │ ' 34 ms' │ ' 1.29 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2279 ms' │ ' 64 ms' │ ' 31 ms' │ ' 2.06 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1139 ms' │ ' 36 ms' │ ' 23 ms' │ ' 1.57 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4924 ms' │ ' 110 ms' │ ' 92 ms' │ ' 1.20 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1548,11 +1548,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '127.1 kb' │ ' 56.7 kb' │ '2.24 x' │ -│ typebox/errors │ '110.9 kb' │ ' 48.9 kb' │ '2.27 x' │ -│ typebox/system │ ' 76.3 kb' │ ' 31.2 kb' │ '2.44 x' │ -│ typebox/value │ '176.8 kb' │ ' 76.5 kb' │ '2.31 x' │ -│ typebox │ ' 75.2 kb' │ ' 30.8 kb' │ '2.44 x' │ +│ typebox/compiler │ '128.0 kb' │ ' 56.9 kb' │ '2.25 x' │ +│ typebox/errors │ '111.6 kb' │ ' 49.1 kb' │ '2.27 x' │ +│ typebox/system │ ' 77.0 kb' │ ' 31.5 kb' │ '2.45 x' │ +│ typebox/value │ '177.7 kb' │ ' 76.8 kb' │ '2.31 x' │ +│ typebox │ ' 75.9 kb' │ ' 31.0 kb' │ '2.45 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index d737b58d6..424ed4c49 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -143,6 +143,12 @@ export namespace TypeCompiler { function IsString(value: unknown): value is string { return typeof value === 'string' } + // ---------------------------------------------------------------------- + // SchemaGuards + // ---------------------------------------------------------------------- + function IsAnyOrUnknown(schema: Types.TSchema) { + return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' + } // ------------------------------------------------------------------- // Polices // ------------------------------------------------------------------- @@ -258,7 +264,7 @@ export namespace TypeCompiler { const property = schema.properties[knownKey] if (schema.required && schema.required.includes(knownKey)) { yield* Visit(property, references, memberExpression) - if (Types.ExtendsUndefined.Check(property)) yield `('${knownKey}' in ${value})` + if (Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` } else { const expression = CreateExpression(property, references, memberExpression) yield IsExactOptionalProperty(value, knownKey, expression) diff --git a/src/value/check.ts b/src/value/check.ts index 0700dbdd7..40c2fd5ef 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -60,6 +60,12 @@ export namespace ValueCheck { return value !== undefined } // ---------------------------------------------------------------------- + // SchemaGuards + // ---------------------------------------------------------------------- + function IsAnyOrUnknown(schema: Types.TSchema) { + return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' + } + // ---------------------------------------------------------------------- // Policies // ---------------------------------------------------------------------- function IsExactOptionalProperty(value: Record, key: string) { @@ -238,7 +244,7 @@ export namespace ValueCheck { if (!Visit(property, references, value[knownKey])) { return false } - if (Types.ExtendsUndefined.Check(property) && !(knownKey in value)) { + if ((Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) { return false } } else { diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 872b987b7..47a59a685 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -361,4 +361,32 @@ describe('type/compiler/Object', () => { Ok(T, { x: undefined }) Ok(T, {}) }) + it('Should check for required property of any', () => { + const T = Type.Object({ x: Type.Any() }) + Fail(T, {}) + Ok(T, { x: undefined }) + Ok(T, { x: 1 }) + Ok(T, { x: true }) + }) + it('Should check for required property of unknown', () => { + const T = Type.Object({ x: Type.Unknown() }) + Fail(T, {}) + Ok(T, { x: undefined }) + Ok(T, { x: 1 }) + Ok(T, { x: true }) + }) + it('Should check for required property of any (when optional)', () => { + const T = Type.Object({ x: Type.Optional(Type.Any()) }) + Ok(T, {}) + Ok(T, { x: undefined }) + Ok(T, { x: 1 }) + Ok(T, { x: true }) + }) + it('Should check for required property of unknown (when optional)', () => { + const T = Type.Object({ x: Type.Optional(Type.Unknown()) }) + Ok(T, {}) + Ok(T, { x: undefined }) + Ok(T, { x: 1 }) + Ok(T, { x: true }) + }) }) From 935394faacaa5449fe6bc02846fc1e535163619e Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 30 May 2023 04:19:29 +0900 Subject: [PATCH 139/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 673870cb6..be7b63f5d 100644 --- a/readme.md +++ b/readme.md @@ -1192,7 +1192,7 @@ const A = { x: X } // const A = { x: { y: { z: Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } -const R0 = A.x.y.z === 2 // const R0 = 2 +const R0 = A.x.y.z === 2 // const R0 = true const R1 = A.x.y === Y // const R1 = true From 8878f5a9ae0073c0bb52e145d172739911dd1570 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 2 Jun 2023 11:23:15 +0900 Subject: [PATCH 140/369] Update TypeScript 5.1.3 --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 75f6630b2..8a8b8f60f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.0.4" + "typescript": "^5.1.3" } }, "node_modules/@esbuild/linux-loong64": { @@ -1410,16 +1410,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/uri-js": { @@ -2442,9 +2442,9 @@ "dev": true }, "typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 1a60169aa..7dad4c690 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,6 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.0.4" + "typescript": "^5.1.3" } } From 2b0d0f4d37080c9a2fb1a1b50923a0e5e43a03a0 Mon Sep 17 00:00:00 2001 From: melbourne2991 Date: Sat, 10 Jun 2023 17:12:22 +1000 Subject: [PATCH 141/369] Hoist Types with Identifiers (#456) Co-authored-by: Will Leach --- src/compiler/compiler.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 424ed4c49..9a6b6cbc5 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -364,19 +364,25 @@ export namespace TypeCompiler { state_remote_custom_types.set(schema_key, schema) yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})` } - function* Visit(schema: T, references: Types.TSchema[], value: string): IterableIterator { + function* Visit(schema: T, references: Types.TSchema[], value: string, root = false): IterableIterator { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any // Reference: Referenced schemas can originate from either additional schemas // or inline in the schema itself. Ideally the recursive path should align to // reference path. Consider for refactor. - if (IsString(schema.$id) && !state_local_function_names.has(schema.$id)) { - state_local_function_names.add(schema.$id) + if (IsString(schema.$id)) { const name = CreateFunctionName(schema.$id) - const body = CreateFunction(name, schema, references, 'value') - PushFunction(body) - yield `${name}(${value})` - return + + if (!state_local_function_names.has(schema.$id)) { + state_local_function_names.add(schema.$id) + const body = CreateFunction(name, schema, references, 'value') + PushFunction(body) + } + + if (!root) { + yield `${name}(${value})` + return + } } switch (schema_[Types.Kind]) { case 'Any': @@ -458,7 +464,7 @@ export namespace TypeCompiler { return `check_${Identifier.Encode($id)}` } function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string): string { - const expression = [...Visit(schema, references, value)].map((condition) => ` ${condition}`).join(' &&\n') + const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n') return `function ${name}(value) {\n return (\n${expression}\n )\n}` } function PushFunction(functionBody: string) { From 5823bf99f2ab13895a5af2b82990d30c685fdbbe Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 10 Jun 2023 17:04:26 +0900 Subject: [PATCH 142/369] Ensure Types With Identifiers are Hoisted (#458) --- package-lock.json | 4 +- package.json | 2 +- readme.md | 122 +++++++++++++++++++-------------------- src/compiler/compiler.ts | 21 ++++--- 4 files changed, 74 insertions(+), 75 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8a8b8f60f..92701e4ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.14", + "version": "0.28.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.14", + "version": "0.28.15", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7dad4c690..eb06253a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.14", + "version": "0.28.15", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index be7b63f5d..49f8b1c85 100644 --- a/readme.md +++ b/readme.md @@ -1462,35 +1462,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 230 ms' │ ' 7 ms' │ ' 32.86 x' │ -│ Literal_Number │ 1000 │ ' 171 ms' │ ' 4 ms' │ ' 42.75 x' │ -│ Literal_Boolean │ 1000 │ ' 148 ms' │ ' 4 ms' │ ' 37.00 x' │ -│ Primitive_Number │ 1000 │ ' 160 ms' │ ' 6 ms' │ ' 26.67 x' │ -│ Primitive_String │ 1000 │ ' 149 ms' │ ' 6 ms' │ ' 24.83 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 199 ms' │ ' 9 ms' │ ' 22.11 x' │ -│ Primitive_Boolean │ 1000 │ ' 126 ms' │ ' 3 ms' │ ' 42.00 x' │ -│ Primitive_Null │ 1000 │ ' 141 ms' │ ' 4 ms' │ ' 35.25 x' │ -│ Object_Unconstrained │ 1000 │ ' 1105 ms' │ ' 27 ms' │ ' 40.93 x' │ -│ Object_Constrained │ 1000 │ ' 1178 ms' │ ' 22 ms' │ ' 53.55 x' │ -│ Object_Vector3 │ 1000 │ ' 368 ms' │ ' 8 ms' │ ' 46.00 x' │ -│ Object_Box3D │ 1000 │ ' 1668 ms' │ ' 28 ms' │ ' 59.57 x' │ -│ Tuple_Primitive │ 1000 │ ' 446 ms' │ ' 12 ms' │ ' 37.17 x' │ -│ Tuple_Object │ 1000 │ ' 1146 ms' │ ' 15 ms' │ ' 76.40 x' │ -│ Composite_Intersect │ 1000 │ ' 661 ms' │ ' 18 ms' │ ' 36.72 x' │ -│ Composite_Union │ 1000 │ ' 513 ms' │ ' 17 ms' │ ' 30.18 x' │ -│ Math_Vector4 │ 1000 │ ' 767 ms' │ ' 10 ms' │ ' 76.70 x' │ -│ Math_Matrix4 │ 1000 │ ' 396 ms' │ ' 10 ms' │ ' 39.60 x' │ -│ Array_Primitive_Number │ 1000 │ ' 353 ms' │ ' 8 ms' │ ' 44.13 x' │ -│ Array_Primitive_String │ 1000 │ ' 358 ms' │ ' 5 ms' │ ' 71.60 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 265 ms' │ ' 3 ms' │ ' 88.33 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1476 ms' │ ' 19 ms' │ ' 77.68 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1698 ms' │ ' 20 ms' │ ' 84.90 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 654 ms' │ ' 9 ms' │ ' 72.67 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1492 ms' │ ' 15 ms' │ ' 99.47 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 1045 ms' │ ' 16 ms' │ ' 65.31 x' │ -│ Array_Composite_Union │ 1000 │ ' 702 ms' │ ' 14 ms' │ ' 50.14 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1016 ms' │ ' 12 ms' │ ' 84.67 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 621 ms' │ ' 5 ms' │ ' 124.20 x' │ +│ Literal_String │ 1000 │ ' 220 ms' │ ' 6 ms' │ ' 36.67 x' │ +│ Literal_Number │ 1000 │ ' 172 ms' │ ' 4 ms' │ ' 43.00 x' │ +│ Literal_Boolean │ 1000 │ ' 162 ms' │ ' 4 ms' │ ' 40.50 x' │ +│ Primitive_Number │ 1000 │ ' 161 ms' │ ' 6 ms' │ ' 26.83 x' │ +│ Primitive_String │ 1000 │ ' 154 ms' │ ' 4 ms' │ ' 38.50 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 204 ms' │ ' 10 ms' │ ' 20.40 x' │ +│ Primitive_Boolean │ 1000 │ ' 131 ms' │ ' 4 ms' │ ' 32.75 x' │ +│ Primitive_Null │ 1000 │ ' 142 ms' │ ' 5 ms' │ ' 28.40 x' │ +│ Object_Unconstrained │ 1000 │ ' 1263 ms' │ ' 29 ms' │ ' 43.55 x' │ +│ Object_Constrained │ 1000 │ ' 1267 ms' │ ' 24 ms' │ ' 52.79 x' │ +│ Object_Vector3 │ 1000 │ ' 382 ms' │ ' 7 ms' │ ' 54.57 x' │ +│ Object_Box3D │ 1000 │ ' 1723 ms' │ ' 28 ms' │ ' 61.54 x' │ +│ Tuple_Primitive │ 1000 │ ' 495 ms' │ ' 13 ms' │ ' 38.08 x' │ +│ Tuple_Object │ 1000 │ ' 1271 ms' │ ' 16 ms' │ ' 79.44 x' │ +│ Composite_Intersect │ 1000 │ ' 656 ms' │ ' 19 ms' │ ' 34.53 x' │ +│ Composite_Union │ 1000 │ ' 529 ms' │ ' 18 ms' │ ' 29.39 x' │ +│ Math_Vector4 │ 1000 │ ' 802 ms' │ ' 14 ms' │ ' 57.29 x' │ +│ Math_Matrix4 │ 1000 │ ' 411 ms' │ ' 6 ms' │ ' 68.50 x' │ +│ Array_Primitive_Number │ 1000 │ ' 369 ms' │ ' 6 ms' │ ' 61.50 x' │ +│ Array_Primitive_String │ 1000 │ ' 369 ms' │ ' 4 ms' │ ' 92.25 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 297 ms' │ ' 3 ms' │ ' 99.00 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1582 ms' │ ' 20 ms' │ ' 79.10 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1629 ms' │ ' 19 ms' │ ' 85.74 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 652 ms' │ ' 12 ms' │ ' 54.33 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1587 ms' │ ' 16 ms' │ ' 99.19 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 1051 ms' │ ' 15 ms' │ ' 70.07 x' │ +│ Array_Composite_Union │ 1000 │ ' 733 ms' │ ' 15 ms' │ ' 48.87 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1071 ms' │ ' 12 ms' │ ' 89.25 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 636 ms' │ ' 5 ms' │ ' 127.20 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1504,37 +1504,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 22 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 19 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Literal_Boolean │ 1000000 │ ' 18 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Number │ 1000000 │ ' 23 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_String │ 1000000 │ ' 23 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 171 ms' │ ' 42 ms' │ ' 36 ms' │ ' 1.17 x' │ -│ Primitive_Boolean │ 1000000 │ ' 20 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Null │ 1000000 │ ' 21 ms' │ ' 16 ms' │ ' 8 ms' │ ' 2.00 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1099 ms' │ ' 31 ms' │ ' 25 ms' │ ' 1.24 x' │ -│ Object_Constrained │ 1000000 │ ' 1224 ms' │ ' 51 ms' │ ' 36 ms' │ ' 1.42 x' │ -│ Object_Vector3 │ 1000000 │ ' 420 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Object_Box3D │ 1000000 │ ' 2012 ms' │ ' 53 ms' │ ' 43 ms' │ ' 1.23 x' │ -│ Object_Recursive │ 1000000 │ ' 5080 ms' │ ' 320 ms' │ ' 150 ms' │ ' 2.13 x' │ -│ Tuple_Primitive │ 1000000 │ ' 154 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Tuple_Object │ 1000000 │ ' 749 ms' │ ' 27 ms' │ ' 18 ms' │ ' 1.50 x' │ -│ Composite_Intersect │ 1000000 │ ' 775 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ -│ Composite_Union │ 1000000 │ ' 533 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Math_Vector4 │ 1000000 │ ' 275 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1136 ms' │ ' 37 ms' │ ' 26 ms' │ ' 1.42 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 316 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Array_Primitive_String │ 1000000 │ ' 239 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 151 ms' │ ' 20 ms' │ ' 15 ms' │ ' 1.33 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 5809 ms' │ ' 64 ms' │ ' 56 ms' │ ' 1.14 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 6210 ms' │ ' 123 ms' │ ' 101 ms' │ ' 1.22 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 21779 ms' │ ' 1466 ms' │ ' 548 ms' │ ' 2.68 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 730 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3259 ms' │ ' 63 ms' │ ' 49 ms' │ ' 1.29 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3276 ms' │ ' 44 ms' │ ' 34 ms' │ ' 1.29 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2279 ms' │ ' 64 ms' │ ' 31 ms' │ ' 2.06 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1139 ms' │ ' 36 ms' │ ' 23 ms' │ ' 1.57 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4924 ms' │ ' 110 ms' │ ' 92 ms' │ ' 1.20 x' │ +│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 5 ms' │ ' 1.00 x' │ +│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Literal_Boolean │ 1000000 │ ' 18 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_Number │ 1000000 │ ' 25 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 174 ms' │ ' 44 ms' │ ' 36 ms' │ ' 1.22 x' │ +│ Primitive_Boolean │ 1000000 │ ' 22 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_Null │ 1000000 │ ' 22 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1065 ms' │ ' 33 ms' │ ' 25 ms' │ ' 1.32 x' │ +│ Object_Constrained │ 1000000 │ ' 1192 ms' │ ' 53 ms' │ ' 38 ms' │ ' 1.39 x' │ +│ Object_Vector3 │ 1000000 │ ' 410 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Object_Box3D │ 1000000 │ ' 1939 ms' │ ' 54 ms' │ ' 50 ms' │ ' 1.08 x' │ +│ Object_Recursive │ 1000000 │ ' 5248 ms' │ ' 355 ms' │ ' 149 ms' │ ' 2.38 x' │ +│ Tuple_Primitive │ 1000000 │ ' 163 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Tuple_Object │ 1000000 │ ' 737 ms' │ ' 29 ms' │ ' 20 ms' │ ' 1.45 x' │ +│ Composite_Intersect │ 1000000 │ ' 761 ms' │ ' 24 ms' │ ' 15 ms' │ ' 1.60 x' │ +│ Composite_Union │ 1000000 │ ' 519 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Math_Vector4 │ 1000000 │ ' 247 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1045 ms' │ ' 39 ms' │ ' 27 ms' │ ' 1.44 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 256 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Array_Primitive_String │ 1000000 │ ' 222 ms' │ ' 21 ms' │ ' 14 ms' │ ' 1.50 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 149 ms' │ ' 22 ms' │ ' 16 ms' │ ' 1.38 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 5473 ms' │ ' 67 ms' │ ' 59 ms' │ ' 1.14 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5548 ms' │ ' 130 ms' │ ' 116 ms' │ ' 1.12 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 21047 ms' │ ' 1710 ms' │ ' 584 ms' │ ' 2.93 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 691 ms' │ ' 35 ms' │ ' 29 ms' │ ' 1.21 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3075 ms' │ ' 63 ms' │ ' 50 ms' │ ' 1.26 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3126 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2086 ms' │ ' 68 ms' │ ' 33 ms' │ ' 2.06 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1069 ms' │ ' 38 ms' │ ' 23 ms' │ ' 1.65 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4559 ms' │ ' 111 ms' │ ' 88 ms' │ ' 1.26 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1548,7 +1548,7 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '128.0 kb' │ ' 56.9 kb' │ '2.25 x' │ +│ typebox/compiler │ '128.0 kb' │ ' 57.0 kb' │ '2.25 x' │ │ typebox/errors │ '111.6 kb' │ ' 49.1 kb' │ '2.27 x' │ │ typebox/system │ ' 77.0 kb' │ ' 31.5 kb' │ '2.45 x' │ │ typebox/value │ '177.7 kb' │ ' 76.8 kb' │ '2.31 x' │ diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9a6b6cbc5..961f9fffb 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -367,22 +367,18 @@ export namespace TypeCompiler { function* Visit(schema: T, references: Types.TSchema[], value: string, root = false): IterableIterator { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - // Reference: Referenced schemas can originate from either additional schemas - // or inline in the schema itself. Ideally the recursive path should align to - // reference path. Consider for refactor. + // Rule: Types with identifiers are hoisted into their own functions. The following will generate a function for the schema + // and yield the call to that function. This call is only made if NOT the root type which allows the generated function to + // yield its expression. The root argument is only true when making calls via CreateFunction(). Note there is potential to + // omit the root argument and conditional by refactoring the logic below. Consider for review. if (IsString(schema.$id)) { const name = CreateFunctionName(schema.$id) - if (!state_local_function_names.has(schema.$id)) { state_local_function_names.add(schema.$id) const body = CreateFunction(name, schema, references, 'value') PushFunction(body) } - - if (!root) { - yield `${name}(${value})` - return - } + if (!root) return yield `${name}(${value})` } switch (schema_[Types.Kind]) { case 'Any': @@ -483,9 +479,12 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- function Build(schema: T, references: Types.TSchema[]): string { ResetCompiler() - const check = CreateFunction('check', schema, references, 'value') + const check = CreateFunction('check', schema, references, 'value') // interior visit const locals = GetLocals() - return `${locals.join('\n')}\nreturn ${check}` + // prettier-ignore + return IsString(schema.$id) // ensure top level schemas with $id's are hoisted + ? `${locals.join('\n')}\nreturn function check(value) {\n return ${CreateFunctionName(schema.$id)}(value)\n}` + : `${locals.join('\n')}\nreturn ${check}` } /** Returns the generated assertion code used to validate this type. */ export function Code(schema: T, references: Types.TSchema[] = []) { From d2c023ad4ed2ec578a92ab3f9d4661aaf7c61617 Mon Sep 17 00:00:00 2001 From: Alex Badmashkaev Date: Tue, 13 Jun 2023 13:07:14 +0300 Subject: [PATCH 143/369] Documentation (#463) --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 49f8b1c85..c362be839 100644 --- a/readme.md +++ b/readme.md @@ -483,7 +483,7 @@ The following table lists the Standard TypeBox types. These types are fully comp │ ]) │ │ ], │ │ const T = Type.Tuple([ │ │ additionalItems: false, │ | ...Type.Rest(A), │ │ minItems: 4, │ -| ...Type.Test(B) │ │ maxItems: 4 │ +| ...Type.Rest(B) │ │ maxItems: 4 │ │ ]) │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -896,7 +896,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { ### Rest Types -Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatination as well as for variadic functions. +Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatenation as well as for variadic functions. ```typescript // TypeScript @@ -1335,7 +1335,7 @@ console.log(C.Code()) // return function check(va ## TypeSystem -The TypeBox TypeSystem module provides functionality to define types above and beyond the Standard and Extended type sets as well as control various assertion polices. Configurations made to the TypeSystem module are observed by both `TypeCompiler` and `Value` modules. +The TypeBox TypeSystem module provides functionality to define types above and beyond the Standard and Extended type sets as well as control various assertion policies. Configurations made to the TypeSystem module are observed by both `TypeCompiler` and `Value` modules. The TypeSystem module is provided as an optional import. From fc188b2ef67634820610de833c54cdc45703f47e Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 15 Jun 2023 11:55:10 +0900 Subject: [PATCH 144/369] Documentation --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index c362be839..7d7b48faf 100644 --- a/readme.md +++ b/readme.md @@ -870,7 +870,7 @@ const T = Type.Object({ // const T = { z: Type.Boolean() // properties: { }) // x: { type: 'number' }, // y: { type: 'string' }, - // z: { type: 'string' }, + // z: { type: 'string' } // } // } @@ -908,14 +908,14 @@ type C = [...T, number] // type C = [number, number type F = (...param: C) => void // type F = ( // param0: number, // param1: number, - // param2: number, + // param2: number // ) => void // TypeBox const T = Type.Tuple([ // const T: TTuple<[ Type.Number(), // TNumber, - Type.Number() // TNumber, + Type.Number() // TNumber ]) // ]> const C = Type.Tuple([ // const C: TTuple<[ From 75fd14092153466a1f5a539eaa45ea317d1e8f76 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 20 Jun 2023 16:07:50 +0900 Subject: [PATCH 145/369] Array Assertion Order (#467) --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 24 ++++++++++++++---------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92701e4ed..43f63db8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.15", + "version": "0.28.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.15", + "version": "0.28.16", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index eb06253a7..30dc668ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.15", + "version": "0.28.16", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 961f9fffb..f7a71b074 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -176,11 +176,12 @@ export namespace TypeCompiler { yield 'true' } function* Array(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { - const expression = CreateExpression(schema.items, references, 'value') + yield `Array.isArray(${value})` if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` - yield `Array.isArray(${value}) && ${value}.every(value => ${expression})` + const expression = CreateExpression(schema.items, references, 'value') + yield `${value}.every(value => ${expression})` } function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` @@ -302,9 +303,9 @@ export namespace TypeCompiler { const index = references.findIndex((foreign) => foreign.$id === schema.$ref) if (index === -1) throw new TypeCompilerDereferenceError(schema) const target = references[index] - // Reference: If we have seen this reference before we can just yield and return - // the function call. If this isn't the case we defer to visit to generate and - // set the function for subsequent passes. Consider for refactor. + // Reference: If we have seen this reference before we can just yield and + // return the function call. If this isn't the case we defer to visit to + // generate and set the function for subsequent passes. if (state_local_function_names.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } @@ -333,7 +334,7 @@ export namespace TypeCompiler { yield `${func}(${value})` } function* Tuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { - yield `(Array.isArray(${value}))` + yield `Array.isArray(${value})` if (schema.items === undefined) return yield `${value}.length === 0` yield `(${value}.length === ${schema.maxItems})` for (let i = 0; i < schema.items.length; i++) { @@ -367,10 +368,13 @@ export namespace TypeCompiler { function* Visit(schema: T, references: Types.TSchema[], value: string, root = false): IterableIterator { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - // Rule: Types with identifiers are hoisted into their own functions. The following will generate a function for the schema - // and yield the call to that function. This call is only made if NOT the root type which allows the generated function to - // yield its expression. The root argument is only true when making calls via CreateFunction(). Note there is potential to - // omit the root argument and conditional by refactoring the logic below. Consider for review. + // Rule: Types with identifiers are hoisted into their own functions. + // The following will generate a function for the schema and yield the + // call to that function. This call is only made if NOT the root type + // which allows the generated function to yield its expression. The + // root argument is only true when making calls via CreateFunction(). + // Note there is potential to omit the root argument and conditional + // by refactoring the logic below. Consider for review. if (IsString(schema.$id)) { const name = CreateFunctionName(schema.$id) if (!state_local_function_names.has(schema.$id)) { From 772e566449b1db5b4136453d1661b0299b834f66 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 22 Jun 2023 01:52:36 +0900 Subject: [PATCH 146/369] TypeCompiler Multiple Language Support (#469) --- package-lock.json | 4 +- package.json | 2 +- src/compiler/compiler.ts | 80 +++++++++++++++++++++++----------------- 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 43f63db8b..84a84be8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.16", + "version": "0.28.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.16", + "version": "0.28.17", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 30dc668ed..ba31a48e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.16", + "version": "0.28.17", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index f7a71b074..fa5383ffd 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -129,6 +129,10 @@ export class TypeCompilerTypeGuardError extends Error { super('TypeCompiler: Preflight validation check failed to guard for the given schema') } } + +export interface TypeCompilerOptions { + language: 'typescript' | 'javascript' +} /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { // ------------------------------------------------------------------- @@ -181,7 +185,8 @@ export namespace TypeCompiler { if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` const expression = CreateExpression(schema.items, references, 'value') - yield `${value}.every(value => ${expression})` + const parameter = CreateParameter('value') + yield `${value}.every((${parameter}) => ${expression})` } function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` @@ -192,7 +197,7 @@ export namespace TypeCompiler { if (IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` } function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { - yield `typeof ${value} === 'boolean'` + yield `(typeof ${value} === 'boolean')` } function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { yield* Visit(schema.returns, references, `${value}.prototype`) @@ -205,7 +210,7 @@ export namespace TypeCompiler { if (IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` } function* Function(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { - yield `typeof ${value} === 'function'` + yield `(typeof ${value} === 'function')` } function* Integer(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` @@ -220,20 +225,20 @@ export namespace TypeCompiler { if (schema.unevaluatedProperties === false) { const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))` - yield `${check1} && ${check2}` + yield `(${check1} && ${check2})` } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})` - yield `${check1} && ${check2}` + yield `(${check1} && ${check2})` } else { - yield `${check1}` + yield `(${check1})` } } function* Literal(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { - yield `${value} === ${schema.const}` + yield `(${value} === ${schema.const})` } else { - yield `${value} === '${schema.const}'` + yield `(${value} === '${schema.const}')` } } function* Never(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { @@ -245,7 +250,7 @@ export namespace TypeCompiler { yield `!${left} && ${right}` } function* Null(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { - yield `${value} === null` + yield `(${value} === null)` } function* Number(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { yield IsNumberCheck(value) @@ -306,7 +311,7 @@ export namespace TypeCompiler { // Reference: If we have seen this reference before we can just yield and // return the function call. If this isn't the case we defer to visit to // generate and set the function for subsequent passes. - if (state_local_function_names.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` + if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } function* String(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { @@ -361,8 +366,8 @@ export namespace TypeCompiler { yield IsVoidCheck(value) } function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { - const schema_key = `schema_key_${state_remote_custom_types.size}` - state_remote_custom_types.set(schema_key, schema) + const schema_key = `schema_key_${state.customs.size}` + state.customs.set(schema_key, schema) yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})` } function* Visit(schema: T, references: Types.TSchema[], value: string, root = false): IterableIterator { @@ -377,8 +382,8 @@ export namespace TypeCompiler { // by refactoring the logic below. Consider for review. if (IsString(schema.$id)) { const name = CreateFunctionName(schema.$id) - if (!state_local_function_names.has(schema.$id)) { - state_local_function_names.add(schema.$id) + if (!state.functions.has(schema.$id)) { + state.functions.add(schema.$id) const body = CreateFunction(name, schema, references, 'value') PushFunction(body) } @@ -449,13 +454,12 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- // Compiler State // ------------------------------------------------------------------- - const state_local_variables = new Set() // local variables and functions - const state_local_function_names = new Set() // local function names used call ref validators - const state_remote_custom_types = new Map() // remote custom types used during compilation - function ResetCompiler() { - state_local_variables.clear() - state_local_function_names.clear() - state_remote_custom_types.clear() + // prettier-ignore + const state = { + language: 'javascript' as TypeCompilerOptions['language'], // target language + variables: new Set(), // local variables + functions: new Set(), // local functions + customs: new Map(), // custom type data } function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string): string { return `(${[...Visit(schema, references, value)].join(' && ')})` @@ -463,48 +467,58 @@ export namespace TypeCompiler { function CreateFunctionName($id: string) { return `check_${Identifier.Encode($id)}` } + function CreateParameter(name: string) { + const annotation = state.language === 'typescript' ? ': any' : '' + return `${name}${annotation}` + } function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string): string { const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n') - return `function ${name}(value) {\n return (\n${expression}\n )\n}` + const parameter = CreateParameter('value') + return `function ${name}(${parameter}) {\n return (\n${expression}\n )\n}` } function PushFunction(functionBody: string) { - state_local_variables.add(functionBody) + state.variables.add(functionBody) } function PushLocal(expression: string) { - const local = `local_${state_local_variables.size}` - state_local_variables.add(`const ${local} = ${expression}`) + const local = `local_${state.variables.size}` + state.variables.add(`const ${local} = ${expression}`) return local } function GetLocals() { - return [...state_local_variables.values()] + return [...state.variables.values()] } // ------------------------------------------------------------------- // Compile // ------------------------------------------------------------------- function Build(schema: T, references: Types.TSchema[]): string { - ResetCompiler() const check = CreateFunction('check', schema, references, 'value') // interior visit const locals = GetLocals() + const parameter = CreateParameter('value') // prettier-ignore return IsString(schema.$id) // ensure top level schemas with $id's are hoisted - ? `${locals.join('\n')}\nreturn function check(value) {\n return ${CreateFunctionName(schema.$id)}(value)\n}` + ? `${locals.join('\n')}\nreturn function check(${parameter}) {\n return ${CreateFunctionName(schema.$id)}(value)\n}` : `${locals.join('\n')}\nreturn ${check}` } /** Returns the generated assertion code used to validate this type. */ - export function Code(schema: T, references: Types.TSchema[] = []) { + export function Code(schema: T, references: Types.TSchema[] = [], options: TypeCompilerOptions = { language: 'javascript' }) { + // compiler-reset + state.language = options.language + state.variables.clear() + state.functions.clear() + state.customs.clear() if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) return Build(schema, references) } /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */ export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { - const code = Code(schema, references) - const custom_schemas = new Map(state_remote_custom_types) + const code = Code(schema, references, { language: 'javascript' }) + const customs = new Map(state.customs) const compiledFunction = globalThis.Function('custom', 'format', 'hash', code) const checkFunction = compiledFunction( (kind: string, schema_key: string, value: unknown) => { - if (!Types.TypeRegistry.Has(kind) || !custom_schemas.has(schema_key)) return false - const schema = custom_schemas.get(schema_key)! + if (!Types.TypeRegistry.Has(kind) || !customs.has(schema_key)) return false + const schema = customs.get(schema_key)! const func = Types.TypeRegistry.Get(kind)! return func(schema, value) }, From 28d0f332f26b1c7c5ba13de0501d6078d009d9c5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 22 Jun 2023 13:08:39 +0900 Subject: [PATCH 147/369] TypeCompiler Return Type Annotation (#470) --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 27 ++++++++++++++++----------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84a84be8d..675abfe05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.17", + "version": "0.28.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.17", + "version": "0.28.18", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index ba31a48e2..a2e701284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.17", + "version": "0.28.18", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index fa5383ffd..4ba3c8e3e 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -131,7 +131,7 @@ export class TypeCompilerTypeGuardError extends Error { } export interface TypeCompilerOptions { - language: 'typescript' | 'javascript' + language?: 'typescript' | 'javascript' } /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { @@ -185,7 +185,7 @@ export namespace TypeCompiler { if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` const expression = CreateExpression(schema.items, references, 'value') - const parameter = CreateParameter('value') + const parameter = CreateParameter('value', 'any') yield `${value}.every((${parameter}) => ${expression})` } function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { @@ -461,20 +461,24 @@ export namespace TypeCompiler { functions: new Set(), // local functions customs: new Map(), // custom type data } - function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string): string { - return `(${[...Visit(schema, references, value)].join(' && ')})` - } function CreateFunctionName($id: string) { return `check_${Identifier.Encode($id)}` } - function CreateParameter(name: string) { - const annotation = state.language === 'typescript' ? ': any' : '' + function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string): string { + return `(${[...Visit(schema, references, value)].join(' && ')})` + } + function CreateParameter(name: string, type: string) { + const annotation = state.language === 'typescript' ? `: ${type}` : '' return `${name}${annotation}` } + function CreateReturns(type: string) { + return state.language === 'typescript' ? `: ${type}` : '' + } function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string): string { const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n') - const parameter = CreateParameter('value') - return `function ${name}(${parameter}) {\n return (\n${expression}\n )\n}` + const parameter = CreateParameter('value', 'any') + const returns = CreateReturns('boolean') + return `function ${name}(${parameter})${returns} {\n return (\n${expression}\n )\n}` } function PushFunction(functionBody: string) { state.variables.add(functionBody) @@ -493,10 +497,11 @@ export namespace TypeCompiler { function Build(schema: T, references: Types.TSchema[]): string { const check = CreateFunction('check', schema, references, 'value') // interior visit const locals = GetLocals() - const parameter = CreateParameter('value') + const parameter = CreateParameter('value', 'any') + const returns = CreateReturns('boolean') // prettier-ignore return IsString(schema.$id) // ensure top level schemas with $id's are hoisted - ? `${locals.join('\n')}\nreturn function check(${parameter}) {\n return ${CreateFunctionName(schema.$id)}(value)\n}` + ? `${locals.join('\n')}\nreturn function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}` : `${locals.join('\n')}\nreturn ${check}` } /** Returns the generated assertion code used to validate this type. */ From 06f3c399a249edf7517a27f5e4bf07a0614e5cc6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 27 Jun 2023 04:24:56 +0900 Subject: [PATCH 148/369] Check for Non-Terminating Recursive Types (#476) --- package-lock.json | 4 +-- package.json | 2 +- src/value/create.ts | 34 ++++++++++++++++++-------- src/value/value.ts | 2 +- test/runtime/value/create/recursive.ts | 25 ++++++++++++++++++- test/runtime/value/create/ref.ts | 5 ++++ 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 675abfe05..84a231b3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.18", + "version": "0.28.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.18", + "version": "0.28.19", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index a2e701284..7404794dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.18", + "version": "0.28.19", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/create.ts b/src/value/create.ts index dace6a6a2..ae2e9b18d 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -57,6 +57,11 @@ export class ValueCreateDereferenceError extends Error { super(`ValueCreate: Unable to dereference schema with $id '${schema.$ref}'`) } } +export class ValueCreateRecursiveInstantiationError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly recursiveMaxDepth: number) { + super('ValueCreate: Value cannot be created as recursive type may produce value of infinite size. Consider using a default.') + } +} // -------------------------------------------------------------------------- // ValueCreate // -------------------------------------------------------------------------- @@ -84,7 +89,7 @@ export namespace ValueCreate { return schema.default } else if (schema.minItems !== undefined) { return globalThis.Array.from({ length: schema.minItems }).map((item) => { - return ValueCreate.Create(schema.items, references) + return Visit(schema.items, references) }) } else { return [] @@ -108,7 +113,7 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - const value = ValueCreate.Create(schema.returns, references) as any + const value = Visit(schema.returns, references) as any if (typeof value === 'object' && !globalThis.Array.isArray(value)) { return class { constructor() { @@ -136,7 +141,7 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - return () => ValueCreate.Create(schema.returns, references) + return () => Visit(schema.returns, references) } } function Integer(schema: Types.TInteger, references: Types.TSchema[]): any { @@ -204,7 +209,7 @@ export namespace ValueCreate { return ( schema.default || globalThis.Object.entries(schema.properties).reduce((acc, [key, schema]) => { - return required.has(key) ? { ...acc, [key]: ValueCreate.Create(schema, references) } : { ...acc } + return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc } }, {}) ) } @@ -213,7 +218,7 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - return globalThis.Promise.resolve(ValueCreate.Create(schema.item, references)) + return globalThis.Promise.resolve(Visit(schema.item, references)) } } function Record(schema: Types.TRecord, references: Types.TSchema[]): any { @@ -223,7 +228,7 @@ export namespace ValueCreate { } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') return propertyKeys.reduce((acc, key) => { - return { ...acc, [key]: Create(valueSchema, references) } + return { ...acc, [key]: Visit(valueSchema, references) } }, {}) } else { return {} @@ -233,7 +238,7 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$id) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) if (index === -1) throw new ValueCreateDereferenceError(schema) const target = references[index] return Visit(target, references) @@ -283,10 +288,11 @@ export namespace ValueCreate { return sequence.next().value } function This(schema: Types.TThis, references: Types.TSchema[]): any { + if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) if ('default' in schema) { return schema.default } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$id) + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) if (index === -1) throw new ValueCreateDereferenceError(schema) const target = references[index] return Visit(target, references) @@ -299,7 +305,7 @@ export namespace ValueCreate { if (schema.items === undefined) { return [] } else { - return globalThis.Array.from({ length: schema.minItems }).map((_, index) => ValueCreate.Create((schema.items as any[])[index], references)) + return globalThis.Array.from({ length: schema.minItems }).map((_, index) => Visit((schema.items as any[])[index], references)) } } function Undefined(schema: Types.TUndefined, references: Types.TSchema[]): any { @@ -315,7 +321,7 @@ export namespace ValueCreate { } else if (schema.anyOf.length === 0) { throw new Error('ValueCreate.Union: Cannot create Union with zero variants') } else { - return ValueCreate.Create(schema.anyOf[0], references) + return Visit(schema.anyOf[0], references) } } function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { @@ -414,7 +420,15 @@ export namespace ValueCreate { return UserDefined(schema_, references_) } } + // -------------------------------------------------------- + // State + // -------------------------------------------------------- + const recursiveMaxDepth = 512 + let recursiveDepth = 0 + + /** Creates a value from the given schema and references */ export function Create(schema: T, references: Types.TSchema[]): Types.Static { + recursiveDepth = 0 return Visit(schema, references) } } diff --git a/src/value/value.ts b/src/value/value.ts index ed9335d28..96bf2982f 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -27,7 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { ValueErrors, ValueErrorIterator, ValueError } from '../errors/index' +import { ValueErrors, ValueErrorIterator } from '../errors/index' import { ValueMutate, Mutable } from './mutate' import { ValueHash } from './hash' import { ValueEqual } from './equal' diff --git a/test/runtime/value/create/recursive.ts b/test/runtime/value/create/recursive.ts index b12dd0b0c..3f78233f2 100644 --- a/test/runtime/value/create/recursive.ts +++ b/test/runtime/value/create/recursive.ts @@ -15,7 +15,6 @@ describe('value/create/Recursive', () => { nodes: [], }) }) - it('Should create default', () => { const T = Type.Recursive( (This) => @@ -27,4 +26,28 @@ describe('value/create/Recursive', () => { ) Assert.isEqual(Value.Create(T), 7) }) + it('Should throw on infinite type', () => { + const T = Type.Recursive((This) => + Type.Object({ + x: This, + }), + ) + Assert.throws(() => Value.Create(T)) + }) + it('Should not throw on recursive type when terminating sub type proceeds self', () => { + const T = Type.Recursive((This) => + Type.Object({ + x: Type.Union([Type.Null(), This]), + }), + ) + Assert.isEqual(Value.Create(T), { x: null }) + }) + it('Should not throw on recursive type when self is optional', () => { + const T = Type.Recursive((This) => + Type.Object({ + x: Type.Optional(This), + }), + ) + Assert.isEqual(Value.Create(T), {}) + }) }) diff --git a/test/runtime/value/create/ref.ts b/test/runtime/value/create/ref.ts index 16c7e4abd..b296f0bb2 100644 --- a/test/runtime/value/create/ref.ts +++ b/test/runtime/value/create/ref.ts @@ -27,4 +27,9 @@ describe('value/create/Ref', () => { const R = Type.Ref(T, { default: 'override' }) Assert.isEqual(Value.Create(R), 'override') // terminated at R default value }) + it('Should dereference remote schema via $ref', () => { + const R = Type.Number({ $id: 'S' }) + const T = Type.Object({ x: Type.Ref(R) }) + Assert.isEqual(Value.Create(T, [R]), { x: 0 }) + }) }) From 3b6c9849bf962abfa6898bc06bff44fbcd1d3262 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 27 Jun 2023 17:14:42 +0900 Subject: [PATCH 149/369] Resolve Numeric Constraint Reporting Error (#479) --- package-lock.json | 4 ++-- package.json | 2 +- src/errors/errors.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84a231b3e..683b43e5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.19", + "version": "0.28.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.19", + "version": "0.28.20", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7404794dd..d6fb9bfdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.19", + "version": "0.28.20", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 6b637d347..6c7203e1d 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -66,7 +66,7 @@ export enum ValueErrorType { NumberMultipleOf, NumberExclusiveMinimum, NumberExclusiveMaximum, - NumberMinumum, + NumberMinimum, NumberMaximum, Object, ObjectMinProperties, @@ -329,10 +329,10 @@ export namespace ValueErrors { yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } + yield { type: ValueErrorType.NumberMinimum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } } if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.NumberMinumum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } + yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } } } function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { From ebef7cc1cad04ab1be459e1a673fcb31fd571ff1 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 2 Jul 2023 22:37:54 +0900 Subject: [PATCH 150/369] Revision 0.29.0 (#483) --- changelog/0.29.0.md | 108 ++++++++++++++++++++++++ package-lock.json | 18 ++-- package.json | 4 +- readme.md | 77 ++++++++++++----- src/compiler/compiler.ts | 5 +- src/errors/errors.ts | 3 +- src/typebox.ts | 62 ++++++++------ src/value/cast.ts | 2 +- src/value/check.ts | 2 +- src/value/create.ts | 7 +- test/runtime/compiler/not.ts | 47 ++++++----- test/runtime/compiler/object.ts | 4 +- test/runtime/schema/not.ts | 47 ++++++----- test/runtime/type/extends/index.ts | 1 + test/runtime/type/extends/not.ts | 130 +++++++++++++++++++++++++++++ test/runtime/type/guard/not.ts | 10 +-- test/runtime/value/cast/not.ts | 25 ++---- test/runtime/value/check/not.ts | 6 -- test/runtime/value/create/not.ts | 11 ++- test/static/not.ts | 8 +- typebox.png | Bin 946728 -> 872194 bytes workbench.png | Bin 43713 -> 0 bytes 22 files changed, 431 insertions(+), 146 deletions(-) create mode 100644 changelog/0.29.0.md create mode 100644 test/runtime/type/extends/not.ts delete mode 100644 workbench.png diff --git a/changelog/0.29.0.md b/changelog/0.29.0.md new file mode 100644 index 000000000..e4cca9f49 --- /dev/null +++ b/changelog/0.29.0.md @@ -0,0 +1,108 @@ +## [0.29.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.29.0) + +## Overview + +Revision 0.29.0 makes a minor interface and schema representation change to the `Type.Not` type. This revision also includes a fix for indexed access types on TypeScript 5.1.6. + +As this revision constitutes a breaking representation change for `Type.Not`, a minor semver revision is required. + +## Type.Not Representation Change + +The `Type.Not` was first introduced in Revision 0.26.0. This type accepted two arguments, the first is the `not` type, the second is the `allowed` type. In 0.26.0, TypeBox would treat the `allowed` type as the inferred type with the schema represented in the following form. + +### 0.26.0 + +```typescript +// allow all numbers except the number 42 +// +const T = Type.Not(Type.Literal(42), Type.Number()) +// ^ ^ +// not type allowed type + +// represented as +// +const T = { + allOf: [ + { not: { const: 42 } }, + { type: 'number' } + ] +} + +// inferred as +// +type T = Static // type T = number +``` +In 0.26.0. the rationale for the second `allowed` argument was provide a correct static type to infer, where one could describe what the type wasn't on the first and what it was on the second (with inference of operating on the second argument). This approach was to echo possible suggestions for negated type syntax in TypeScript. + +```typescript +type T = number & not 42 // not actual typescript syntax! +``` + +### 0.29.0 + +Revision 0.29.0 changes the `Type.Not` type to take a single `not` argument only. This type statically infers as `unknown` + +```typescript +// allow all types except the literal number 42 +// +const T = Type.Not(Type.Literal(42)) +// ^ +// not type + +// represented as +// +const T = { not: { const: 42 } } + +// inferred as +// +type T = Static // type T = unknown + +``` +### Upgrading to 0.29.0 + +In revision 0.29.0, you can express the 0.26.0 Not type via `Type.Intersect` which explicitly creates the `allOf` representation. The type inference works in this case as intersected `number & unknown` yields the most narrowed type (which is `number`) + +```typescript +// allow all numbers except the number 42 +// +const T = Type.Intersect([ Type.Not(Type.Literal(42)), Type.Number() ]) +// ^ ^ +// not type allowed type + +// represented as +// +const T = { + allOf: [ + { not: { const: 42 } }, + { type: 'number' } + ] +} +// inferred as +// +type T = Static // type T = number +``` +The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of the type intersection narrowing capabilities of TypeScript with respect to inference. + +### Invert Not + +In revision 0.29.0, it is possible to invert the `Not` type. TypeBox will track each inversion and statically infer appropriately. + +```typescript +// not not string +// +const T = Type.Not(Type.Not(Type.String())) + +// represented as +// +const T = { + not: { + not: { + type: "string" + } + } +} + +// inferred as +// +type T = Static // type T = string +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 683b43e5d..98d18eb2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.28.20", + "version": "0.29.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.28.20", + "version": "0.29.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", @@ -18,7 +18,7 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.1.3" + "typescript": "^5.1.6" } }, "node_modules/@esbuild/linux-loong64": { @@ -1410,9 +1410,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2442,9 +2442,9 @@ "dev": true }, "typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index d6fb9bfdd..6cc25a10f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.28.20", + "version": "0.29.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -42,6 +42,6 @@ "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.1.3" + "typescript": "^5.1.6" } } diff --git a/readme.md b/readme.md index 7d7b48faf..1ff66fddc 100644 --- a/readme.md +++ b/readme.md @@ -84,6 +84,7 @@ License MIT - [Conditional](#types-conditional) - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) + - [Not](#types-not) - [Rest](#types-rest) - [Guards](#types-guards) - [Unsafe](#types-unsafe) @@ -353,20 +354,11 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Not( | type T = string │ const T = { │ -| Type.Union([ │ │ allOf: [{ │ -│ Type.Literal('x'), │ │ not: { │ -│ Type.Literal('y'), │ │ anyOf: [ │ -│ Type.Literal('z') │ │ { const: 'x' }, │ -│ ]), │ │ { const: 'y' }, │ -│ Type.String() │ │ { const: 'z' } │ -│ ) │ │ ] │ -│ │ │ } │ -│ │ │ }, { │ -│ │ │ type: 'string' │ -│ │ │ }] │ +│ const T = Type.Not( | type T = unknown │ const T = { │ +│ Type.String() │ │ not: { │ +│ ) │ │ type: 'string' │ +│ │ │ } │ │ │ │ } │ -│ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Extends( │ type T = │ const T = { │ │ Type.String(), │ string extends number │ const: false, │ @@ -650,7 +642,7 @@ const T = Type.String({ // const T = { }) // format: 'email' // } -// Mumber must be a multiple of 2 +// Number must be a multiple of 2 const T = Type.Number({ // const T = { multipleOf: 2 // type: 'number', }) // multipleOf: 2 @@ -892,6 +884,49 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { // } ``` + + +### Not Types + +Not types are supported with `Type.Not`. This type represents the JSON Schema `not` keyword and will statically infer as `unknown`. Note that negated (or not) types are not supported in TypeScript, but can still be partially expressed by interpreting `not` as the broad type `unknown`. When used with intersect types, the Not type can create refined assertion rules for types by leveraging TypeScript's ability to narrow from `unknown` to an intended type through intersection. + +For example, consider a type which is `number` but not `1 | 2 | 3` and where the static type would still technically be a `number`. The following shows a pseudo TypeScript example using `not` followed by the TypeBox implementation. + +```typescript +// Pseudo TypeScript + +type T = number & not (1 | 2 | 3) // allow all numbers except 1, 2, 3 + +// TypeBox + +const T = Type.Intersect([ // const T = { + Type.Number(), // allOf: [ + Type.Not(Type.Union([ // { type: "number" }, + Type.Literal(1), // { + Type.Literal(2), // not: { + Type.Literal(3) // anyOf: [ + ])) // { const: 1, type: "number" }, +]) // { const: 2, type: "number" }, + // { const: 3, type: "number" } + // ] + // } + // } + // ] + // } + +type T = Static // evaluates as: + // + // type T = (number & (not (1 | 2 | 3))) + // type T = (number & (unknown)) + // type T = (number) +``` + +The Not type can be used with constraints to define schematics for types that would otherwise be difficult to express. +```typescript +const Even = Type.Number({ multipleOf: 2 }) + +const Odd = Type.Intersect([Type.Number(), Type.Not(Even)]) +``` ### Rest Types @@ -1420,29 +1455,25 @@ TypeSystem.AllowNaN = true ## Workbench -TypeBox offers a small web based code generation tool that can be used to convert TypeScript types into TypeBox type definitions as well as a variety of other formats. +TypeBox offers a web based code generation tool that can be used to convert TypeScript types into TypeBox types as well as a variety of other runtime type representations. [Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) -
      - - - -
      - ## Ecosystem -The following is a list of community packages that provide general tooling and framework support for TypeBox. +The following is a list of community packages that provide general tooling and framework integration support for TypeBox. | Package | Description | | ------------- | ------------- | | [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | +| [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from JSON schemas | -| [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | +| [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | +| [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 4ba3c8e3e..49e8ddb95 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -245,9 +245,8 @@ export namespace TypeCompiler { yield `false` } function* Not(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { - const left = CreateExpression(schema.allOf[0].not, references, value) - const right = CreateExpression(schema.allOf[1], references, value) - yield `!${left} && ${right}` + const expression = CreateExpression(schema.not, references, value) + yield `(!${expression})` } function* Null(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { yield `(${value} === null)` diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 6c7203e1d..aa5fe28be 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -305,10 +305,9 @@ export namespace ValueErrors { yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } } function* Not(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (Visit(schema.allOf[0].not, references, path, value).next().done === true) { + if (Visit(schema.not, references, path, value).next().done === true) { yield { type: ValueErrorType.Not, schema, path, value, message: `Value should not validate` } } - yield* Visit(schema.allOf[1], references, path, value) } function* Null(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { if (!(value === null)) { diff --git a/src/typebox.ts b/src/typebox.ts index de871f3b1..f5c04c6aa 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -313,21 +313,13 @@ export type TIndexRestMany = K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : [] // prettier-ignore -export type TIndexReduce = - T extends TRecursive ? TIndexReduce : +export type TIndex = + T extends TRecursive ? TIndex : T extends TIntersect ? UnionType>> : T extends TUnion ? UnionType>> : T extends TObject ? UnionType>> : T extends TTuple ? UnionType>> : TNever -// prettier-ignore -export type TIndex = - [T, K] extends [TTuple, TNumber] ? UnionType> : - [T, K] extends [TArray, TNumber] ? AssertType : - K extends TTemplateLiteral ? TIndexReduce> : - K extends TUnion[]> ? TIndexReduce> : - K extends TLiteral ? TIndexReduce : - TNever // -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- @@ -393,10 +385,10 @@ export interface TNever extends TSchema { // -------------------------------------------------------------------------- // TNot // -------------------------------------------------------------------------- -export interface TNot extends TSchema { +export interface TNot extends TSchema { [Kind]: 'Not' - static: Static - allOf: [{ not: Not }, T] + static: T extends TNot ? Static : unknown + not: T } // -------------------------------------------------------------------------- // TNull @@ -1063,11 +1055,7 @@ export namespace TypeGuard { return ( TKind(schema) && schema[Kind] === 'Not' && - IsArray(schema.allOf) && - schema.allOf.length === 2 && - IsObject(schema.allOf[0]) && - TSchema(schema.allOf[0].not) && - TSchema(schema.allOf[1]) + TSchema(schema.not) ) } /** Returns true if the given schema is TNull */ @@ -1354,8 +1342,7 @@ export namespace ExtendsUndefined { export function Check(schema: TSchema): boolean { if (schema[Kind] === 'Undefined') return true if (schema[Kind] === 'Not') { - const not = schema as TNot - return Check(not.allOf[1]) + return !Check(schema.not) } if (schema[Kind] === 'Intersect') { const intersect = schema as TIntersect @@ -1551,6 +1538,18 @@ export namespace TypeExtends { return TypeExtendsResult.True } // -------------------------------------------------------------------------- + // Not + // -------------------------------------------------------------------------- + function ResolveNot(schema: T): TUnknown | TNot['not'] { + let [current, depth]: [TSchema, number] = [schema, 0] + while (true) { + if (!TypeGuard.TNot(current)) break + current = current.not + depth += 1 + } + return depth % 2 === 0 ? current : Type.Unknown() + } + // -------------------------------------------------------------------------- // Null // -------------------------------------------------------------------------- function Null(left: TNull, right: TSchema) { @@ -1858,6 +1857,9 @@ export namespace TypeExtends { return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { + // Not Unwrap + if (TypeGuard.TNot(left)) return Visit(ResolveNot(left), right) + if (TypeGuard.TNot(right)) return Visit(left, ResolveNot(right)) // Template Literal Union Unwrap if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) @@ -2441,9 +2443,19 @@ export class StandardTypeBuilder extends TypeBuilder { } } /** `[Standard]` Returns indexed property types for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndexReduce> + public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, keys: K, options?: SchemaOptions): AssertType + /** `[Standard]` Returns indexed property types for the given keys */ + public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex + /** `[Standard]` Returns indexed property types for the given keys */ + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> + /** `[Standard]` Returns indexed property types for the given keys */ + public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> /** `[Standard]` Returns indexed property types for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): TIndex + public Index(schema: T, key: K, options?: SchemaOptions): TSchema /** `[Standard]` Returns indexed property types for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { @@ -2508,9 +2520,9 @@ export class StandardTypeBuilder extends TypeBuilder { public Never(options: SchemaOptions = {}): TNever { return this.Create({ ...options, [Kind]: 'Never', not: {} }) } - /** `[Standard]` Creates a Not type. The first argument is the disallowed type, the second is the allowed. */ - public Not(not: N, schema: T, options?: SchemaOptions): TNot { - return this.Create({ ...options, [Kind]: 'Not', allOf: [{ not: TypeClone.Clone(not, {}) }, TypeClone.Clone(schema, {})] }) + /** `[Standard]` Creates a Not type */ + public Not(not: T, options?: SchemaOptions): TNot { + return this.Create({ ...options, [Kind]: 'Not', not }) } /** `[Standard]` Creates a Null type */ public Null(options: SchemaOptions = {}): TNull { diff --git a/src/value/cast.ts b/src/value/cast.ts index 66bb2abce..1ff3db94c 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -178,7 +178,7 @@ export namespace ValueCast { throw new ValueCastNeverTypeError(schema) } function Not(schema: Types.TNot, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema.allOf[1], references) + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) } function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) diff --git a/src/value/check.ts b/src/value/check.ts index 40c2fd5ef..a0458cb8f 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -201,7 +201,7 @@ export namespace ValueCheck { return false } function Not(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { - return !Visit(schema.allOf[0].not, references, value) && Visit(schema.allOf[1], references, value) + return !Visit(schema.not, references, value) } function Null(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { return value === null diff --git a/src/value/create.ts b/src/value/create.ts index ae2e9b18d..66973e3d4 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -42,6 +42,11 @@ export class ValueCreateNeverTypeError extends Error { super('ValueCreate: Never types cannot be created') } } +export class ValueCreateNotTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('ValueCreate: Not types must have a default value') + } +} export class ValueCreateIntersectTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueCreate: Intersect produced invalid value. Consider using a default value.') @@ -182,7 +187,7 @@ export namespace ValueCreate { if ('default' in schema) { return schema.default } else { - return Visit(schema.allOf[1], references) + throw new ValueCreateNotTypeError(schema) } } function Null(schema: Types.TNull, references: Types.TSchema[]): any { diff --git a/test/runtime/compiler/not.ts b/test/runtime/compiler/not.ts index 27a07c5ba..144b31de4 100644 --- a/test/runtime/compiler/not.ts +++ b/test/runtime/compiler/not.ts @@ -2,37 +2,44 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/compiler/Not', () => { - it('Should validate with not number', () => { - const T = Type.Not(Type.Number(), Type.String()) + it('Should validate not number', () => { + const T = Type.Not(Type.Number()) Fail(T, 1) - Ok(T, 'A') + Ok(T, '1') }) - it('Should validate with union left', () => { + it('Should validate not not number', () => { + const T = Type.Not(Type.Not(Type.Number())) + Ok(T, 1) + Fail(T, '1') + }) + it('Should validate not union', () => { // prettier-ignore const T = Type.Not(Type.Union([ Type.Literal('A'), Type.Literal('B'), Type.Literal('C') - ]), Type.String()) + ])) Fail(T, 'A') Fail(T, 'B') Fail(T, 'C') Ok(T, 'D') }) - it('Should validate with union right', () => { - // prettier-ignore - const T = Type.Not(Type.Number(), Type.Union([ - Type.String(), - Type.Boolean() - ])) - Fail(T, 1) - Ok(T, 'A') - Ok(T, true) - }) - it('Should not validate with symmetric left right', () => { - // prettier-ignore - const T = Type.Not(Type.Number(), Type.Number()) - Fail(T, 1) - Fail(T, true) // not a number, but not a number either? + it('Should validate not object intersection', () => { + const T = Type.Intersect([ + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + Type.Object({ + x: Type.Not(Type.Literal(0)), + y: Type.Not(Type.Literal(0)), + z: Type.Not(Type.Literal(0)), + }), + ]) + Fail(T, { x: 0, y: 0, z: 0 }) + Fail(T, { x: 1, y: 0, z: 0 }) + Fail(T, { x: 1, y: 1, z: 0 }) + Ok(T, { x: 1, y: 1, z: 1 }) }) }) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 47a59a685..5b3079344 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -7,7 +7,7 @@ describe('type/compiler/Object', () => { // ----------------------------------------------------- it('Should handle extends undefined check 1', () => { const T = Type.Object({ - A: Type.Not(Type.Number(), Type.Undefined()), + A: Type.Not(Type.Number()), B: Type.Union([Type.Number(), Type.Undefined()]), C: Type.Intersect([Type.Undefined(), Type.Undefined()]), }) @@ -20,7 +20,7 @@ describe('type/compiler/Object', () => { // https://github.com/sinclairzx81/typebox/issues/437 it('Should handle extends undefined check 2', () => { const T = Type.Object({ - A: Type.Not(Type.Null(), Type.Undefined()), + A: Type.Not(Type.Null()), }) Ok(T, { A: undefined }) Fail(T, { A: null }) diff --git a/test/runtime/schema/not.ts b/test/runtime/schema/not.ts index c8ec36faf..491de05b4 100644 --- a/test/runtime/schema/not.ts +++ b/test/runtime/schema/not.ts @@ -2,37 +2,44 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('type/schema/Not', () => { - it('Should validate with not number', () => { - const T = Type.Not(Type.Number(), Type.String()) + it('Should validate not number', () => { + const T = Type.Not(Type.Number()) Fail(T, 1) - Ok(T, 'A') + Ok(T, '1') }) - it('Should validate with union left', () => { + it('Should validate not not number', () => { + const T = Type.Not(Type.Not(Type.Number())) + Ok(T, 1) + Fail(T, '1') + }) + it('Should validate not union', () => { // prettier-ignore const T = Type.Not(Type.Union([ Type.Literal('A'), Type.Literal('B'), Type.Literal('C') - ]), Type.String()) + ])) Fail(T, 'A') Fail(T, 'B') Fail(T, 'C') Ok(T, 'D') }) - it('Should validate with union right', () => { - // prettier-ignore - const T = Type.Not(Type.Number(), Type.Union([ - Type.String(), - Type.Boolean() - ])) - Fail(T, 1) - Ok(T, 'A') - Ok(T, true) - }) - it('Should not validate with symmetric left right', () => { - // prettier-ignore - const T = Type.Not(Type.Number(), Type.Number()) - Fail(T, 1) - Fail(T, true) // not a number, but not a number either? + it('Should validate not object intersection', () => { + const T = Type.Intersect([ + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + Type.Object({ + x: Type.Not(Type.Literal(0)), + y: Type.Not(Type.Literal(0)), + z: Type.Not(Type.Literal(0)), + }), + ]) + Fail(T, { x: 0, y: 0, z: 0 }) + Fail(T, { x: 1, y: 0, z: 0 }) + Fail(T, { x: 1, y: 1, z: 0 }) + Ok(T, { x: 1, y: 1, z: 1 }) }) }) diff --git a/test/runtime/type/extends/index.ts b/test/runtime/type/extends/index.ts index 1bd1dc959..be6eed0b2 100644 --- a/test/runtime/type/extends/index.ts +++ b/test/runtime/type/extends/index.ts @@ -7,6 +7,7 @@ import './date' import './function' import './integer' import './literal' +import './not' import './null' import './number' import './object' diff --git a/test/runtime/type/extends/not.ts b/test/runtime/type/extends/not.ts new file mode 100644 index 000000000..be5bbfa9b --- /dev/null +++ b/test/runtime/type/extends/not.ts @@ -0,0 +1,130 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// --------------------------------------------------------------------------- +// Note: Not is equivalent to Unknown with the exception of nested negation. +// --------------------------------------------------------------------------- +describe('type/extends/Not', () => { + // --------------------------------------------------------------------------- + // Nested + // --------------------------------------------------------------------------- + it('Should extend with nested negation', () => { + const T1 = Type.String() + const T2 = Type.Not(T1) + const T3 = Type.Not(T2) + const T4 = Type.Not(T3) + const T5 = Type.Not(T4) + + const R1 = TypeExtends.Extends(T1, Type.String()) + const R2 = TypeExtends.Extends(T2, Type.String()) + const R3 = TypeExtends.Extends(T3, Type.String()) + const R4 = TypeExtends.Extends(T4, Type.String()) + const R5 = TypeExtends.Extends(T5, Type.String()) + + Assert.isEqual(R1, TypeExtendsResult.True) + Assert.isEqual(R2, TypeExtendsResult.False) + Assert.isEqual(R3, TypeExtendsResult.True) + Assert.isEqual(R4, TypeExtendsResult.False) + Assert.isEqual(R5, TypeExtendsResult.True) + }) + + // --------------------------------------------------------------------------- + // Not as Unknown Tests + // --------------------------------------------------------------------------- + it('Should extend Any', () => { + type T = unknown extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Any()) + Assert.isEqual(R, TypeExtendsResult.True) + }) + it('Should extend Unknown', () => { + type T = unknown extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Unknown()) + Assert.isEqual(R, TypeExtendsResult.True) + }) + it('Should extend String', () => { + type T = unknown extends string ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.String()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Boolean', () => { + type T = unknown extends boolean ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Boolean()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Number', () => { + type T = unknown extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Number()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = unknown extends number ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Integer()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array 1', () => { + type T = unknown extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.Any())) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Array 2', () => { + type T = unknown extends Array ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.String())) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = unknown extends [number, number] ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = unknown extends {} ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({})) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Object 2', () => { + type T = unknown extends { a: number } ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 1', () => { + type T = unknown extends number | string ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Union 2', () => { + type T = unknown extends any | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.isEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 3', () => { + type T = unknown extends unknown | number ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.isEqual(R, TypeExtendsResult.True) + }) + it('Should extend Union 4', () => { + type T = unknown extends unknown | any ? 1 : 2 // 1 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.isEqual(R, TypeExtendsResult.True) + }) + it('Should extend Null', () => { + type T = unknown extends null ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Null()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = unknown extends undefined ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Undefined()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Void', () => { + type T = unknown extends void ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Void()) + Assert.isEqual(R, TypeExtendsResult.False) + }) + it('Should extend Date', () => { + type T = unknown extends Date ? 1 : 2 + const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Date()) + Assert.isEqual(R, TypeExtendsResult.False) + }) +}) diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/not.ts index b5a8bf07a..1bb7c2c87 100644 --- a/test/runtime/type/guard/not.ts +++ b/test/runtime/type/guard/not.ts @@ -4,20 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TNot', () => { it('Should guard for TNot', () => { - const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String())) + const R = TypeGuard.TNot(Type.Not(Type.String())) Assert.isEqual(R, true) }) it('Should not guard for TNot 1', () => { - const R = TypeGuard.TNot(Type.Not(null as any, Type.String())) - Assert.isEqual(R, false) - }) - it('Should not guard for TNot 2', () => { - const R = TypeGuard.TNot(Type.Not(Type.String(), null as any)) + const R = TypeGuard.TNot(Type.Not(null as any)) Assert.isEqual(R, false) }) it('Should not guard for TNot with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TNot(Type.Not(Type.String(), Type.String()), { $id: 1 }) + const R = TypeGuard.TNot(Type.Not(Type.String()), { $id: 1 }) Assert.isEqual(R, true) }) }) diff --git a/test/runtime/value/cast/not.ts b/test/runtime/value/cast/not.ts index 0fc2d1ed4..e0713d473 100644 --- a/test/runtime/value/cast/not.ts +++ b/test/runtime/value/cast/not.ts @@ -3,57 +3,50 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/Not', () => { - const T = Type.Not(Type.String(), Type.Number()) - const E = 0 + const T = Type.Not(Type.String(), { default: 0 }) it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, 0) // default }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.isEqual(result, value) }) it('Should preserve', () => { const value = 100 const result = Value.Cast(T, value) Assert.isEqual(result, 100) }) - it('Should not preserve when schema is illogical', () => { - const T = Type.Not(Type.Number(), Type.Number()) - const value = 100 - const result = Value.Cast(T, value) - Assert.isEqual(result, 0) - }) }) diff --git a/test/runtime/value/check/not.ts b/test/runtime/value/check/not.ts index abfc89597..780d08c24 100644 --- a/test/runtime/value/check/not.ts +++ b/test/runtime/value/check/not.ts @@ -30,10 +30,4 @@ describe('value/check/Not', () => { Assert.isEqual(Value.Check(T, 'A'), true) Assert.isEqual(Value.Check(T, true), true) }) - it('Should not validate with symmetric left right', () => { - // prettier-ignore - const T = Type.Not(Type.Number(), Type.Number()) - Assert.isEqual(Value.Check(T, 1), false) - Assert.isEqual(Value.Check(T, true), false) - }) }) diff --git a/test/runtime/value/create/not.ts b/test/runtime/value/create/not.ts index 433e8552f..9890372c6 100644 --- a/test/runtime/value/create/not.ts +++ b/test/runtime/value/create/not.ts @@ -3,18 +3,17 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/create/Not', () => { - it('Should create value', () => { - const T = Type.Not(Type.String(), Type.Number()) - const R = Value.Create(T) - Assert.isEqual(R, 0) + it('Should throw without default value', () => { + const T = Type.Not(Type.String()) + Assert.throws(() => Value.Create(T)) }) it('Should create value with default inner', () => { - const T = Type.Not(Type.String(), Type.Number({ default: 100 })) + const T = Type.Not(Type.String(), { default: 100 }) const R = Value.Create(T) Assert.isEqual(R, 100) }) it('Should create value with default outer', () => { - const T = Type.Not(Type.String(), Type.Number(), { default: 100 }) + const T = Type.Not(Type.String(), { default: 100 }) const R = Value.Create(T) Assert.isEqual(R, 100) }) diff --git a/test/static/not.ts b/test/static/not.ts index ea157157a..aa8a74d87 100644 --- a/test/static/not.ts +++ b/test/static/not.ts @@ -2,6 +2,10 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' { - const T = Type.Not(Type.Number(), Type.String()) - Expect(T).ToInfer() + const T = Type.Not(Type.Number()) + Expect(T).ToInfer() +} +{ + const T = Type.Not(Type.Not(Type.Number())) + Expect(T).ToInfer() } diff --git a/typebox.png b/typebox.png index ed053e1f3df23df4eca1b25d94b0df721437a1e9..d38acd1373310d36660ee8f346de3c31259a8a01 100644 GIT binary patch literal 872194 zcmZs?byOQ~)HRAru>!@dlmcyWcLJqYk+h{P?oO}}+$mnHK#K-%k>c(ew79!dfy|b0I8^494wX>DcN$wlWZ|7kwjJ6U513QGUCiAxI#4Hf&a{%7rfT)3J4 zBZWl83X%ee{()rwflB`{H*>82C)hol?*Ai5{v&iOt(~p0{%8ElZk+NDrv4B9@XwUB z+y6a{un@n5c(1*F!#|qkKibmA($N~i|JmN!OVixv-&H`&om~GRSfYYb;&B?TRR4s} z|5u^Ai@Uj_sr&!y|G&b*HpI9b|MZ;ytKRK@jQsx-#Kc6~jBNfT@c*v~5FcktJ2w|+ zdtY;Rdl%>bFH=yEUr2&;?uj@S)>AC?H;NyKQ>-MCjMR~P`k;N~hld=84-LmS z8&;pjp9+&>c|jnMf#^Y=m>|sQ!$r`;%|+$I)c_y}bMbF@Kn&yrVGsj=2f>Gu^gcG> zy`d4a(d$vnYxv!ELMP@*0D-w5Sbz4QLt!mM=)-^WVFfBMH;Biqh16KT)E*ZG3X9#{ zz)imzg8#gl;M*PtU~V-SebnGAia+V5QE?muEdEWpS`L{}j?=Pnm+qGGNTKb_-xXzY zLP2i}#@{eT|J~H%v<1Y!t)eha6{ghf-m)s0lywX|V)6N<92vE~#F8)na5Zl_c8}_O zI8I-tkciBi`*!^p6`%%>Q#n9i zzELz4<9FE?2F_b#4L+MvxO=c~0vr>{J&yMWfY67P4?T=%ijEoSA6l0HJGc1PCHl&( zp5ryGgyZShoa1AzXt6J7N;X>zYTcK@_bt-rA$FDF^`+%cjG8pRFK#3$#nmy*tweF; zdqaz=JORdZpJS-sd-T#N&ISrWVO_3FJG?rfVm;x z#^Ll%h)PGr^K{c~imfw1TZ{}!H=I!hetsXtWFxZl5GXiFdun`mXd(|FX+`C&=mL1u z-Bd$oQ|cq#MmlP*=8I;DQ@~@1VwAUM_rI?%7psh-0_qAV?o3+*Ha&wBG9qGzn3j6fR zVb~EugRWa{qr2vj{cXCpAok2Dx|Eze=EG6RekbOP!R4odtPhGLYZSCvqhJemSJ@n| zbNM>-+FvnvWq1USR@GOesD5YlPwx}_?xJ!UsNQFh#`K&ISB5b0bMFq(vwB1Ch;+;Qq60DccR7x-gRWYi%T?|B0{7CO9=W`DFS z#oG_fgOnEI$Qq#ozf_7`FP(pDj;gU79w(TL zxKoZ5iAZ*3_|@N~;?o=eMuV;6{lb=L^SXDp{e0%Y_p#KU$RQ_LuFCC;nAaNX zQ9*F!QEp|wNHWovnias440ROz-z&Nxo6D5vLvGg)JA@S)>vt| zOv9oDy?vVP(2*vq$SXTi@ToO9WhPaC;M6HKGo~(v>fM#2s)pQ(;pg)k5=wz(w+!ms zaUTB8#8dp_j-R%8zW^_9bF$J8-zMx0tapTTYli)bHdf*)v+79I`|?@&Dhq!S6)b;aXYzxp4dEERa=U) z&s8Mk)^?M*ivk(XYRFZRMi@>;;7MFN4IWdLu3p54d#=D)X|mMZ zI-_-b$aQ~dPM>&--r6840axKZ<7m5N7iR784xf=AsgJbj=ew0kx%n{A`3R3npk3V*bIha){!C1RR&K=**@l- zRtCKfU84!|H`g}SQu;ZdA;u(vyc$<5C6n=;emvfmFBhoVZ*IS1(%gQ}0f5nY1isrB z2n;s_0B)>RN$#z^%NJj#bhg}N9S7?2ina48Pz5P9udFGA4d}g0eW-`9yZd_wg4J8^$x^T7vTlh<~PG*s?pmdB4QS94ap6bXYUnfjW0v;)e;` zvQGH#ZMFNMVfx35aF2hF68<`+rm|P54X}=C;8&eJ#}yF_K0vu0x66`|b+E)Z68viN@F0s!?_l7V(&#B}&O z4?a4Y^3L68-GYp{8@K*wXwNOXRJcn0ZuRE4!of`EbbdPQyzn8+ZhNppAhi%ubJMY| zIH4kAc36g)pzz3mf#bs?sf0fI!r;DVYC&=k)AhNY^oR*LNZ?7d_0UNA4Wz80f`mDD zE1|0I^wTraj~%wznU)#dMugHPjO(2v(>6*2l~df7a0@BC4=&&-HG*-z4j>y{VrjL;KdQwp-G)2Y30DzC?MA;Vgf^9qIu0$Nz$xI|shUv6qij zI&-mS|6^$%FwRMijk)=4{Ntbt+&L?pz z1G{>YoBsy8`#d%j5|V=1;CMaUTbsRgTeSd;C9tRW$02SE9S(_KS~`p-P`y6 z6ZQG%Pn?Oxq;;|9O5|=qY6;c3;OMt|-HzzG&Lua45hPm%fP(C=Wx^RI!9lY{aALFMx*&I@^=`P6sb$q=8xK6=8#WxC$CoKQ{q`}DoX-BvcEzbVXp95d&Y zByD?SG8OK?CC=fqf!W2*^kwFBz31ukPRCXx>3~Agbug}-Qdm6$U960rd;0z-h*^5% zv|*Rbi%Nm@BRx-k*Ov8o-?7_0J##LSf-m`e9s6!Wt4%=_<~Kv3IJR`aRW`6H*_y7YLQlad1DdmWBQcgF%-m6LgH&mIr+D+NAs zL_p3%C2L$@ZjHTI|6mqt@VasG8cGq^Cq#bM&l&p^Zz8$a7DbW(Bi9 zGO)+TCRxm?JlKNg-AO|1wV)1D+b1(dc!i$68!1rN&W1bR2&$rNM()PqsyfZ`a|3)3 zUkKqFGu7MotB=G3wRc;3-OA>+vK+@UjR;+<@o3bxVNlr=47JSwv8b!L>e@I*l;T?GM5-_w3wHlP zCp`4HizPm%^v@Dn<-YD^S|6EvOBOpi%rSkZ*tV23k5#v~UrQg!R8}k^6S8p#x}aePcKbtyb@nwPuo=+6ss-M;IveU!J* zc}G^1bW5l~{wzZ&S*W}Q7IHSDI;^o0JBj=i#>rH8P|@J+@2eP?jsZZ`**o>(K}abPixc?!6|VdH3!@W>nwHS?$%cio zxRB}gp0=h63cBi^PCtiW5$-2WJ6kU@UwI!FEMIz_@OA^aiy<~0Y-;Jyem$Kh*9T}$ zv``S**JI(7>Z#ECI(s-!TRwEpU3)U$x}AN?RlwTzr*N7xpE#%d@Xt?j*j#iUqJ}^` zU0L|ZI|V+u$3$oQl*?ePOtEG5dH1BGdK6yw#e$$vmC((aC6}%Zw4kZkITn^6&YnWz z3_mb-6O6B_RKFj_tzs$^bnHiB^LaR%#6Bc*9Jml^9A3#Yy3QSc0X`JzTUCc_E&86N z!e_X39PMyZxp!l&4;Y5`L`VCq_`8KeHL{}px9e?&AqEFrb4E?8mVD$*D`{5Sn??ya zX6s&k3qLfD)inVWm1^g%-!4>?k@~7;=X|tru;&7P%P+pPiFu-H@|KJheL>l&H*l`BYH)r4%K zr(3KAM~zqtIM+xK-QvxK?aDOX(EX-*#%=cwBa{-ufJw7uoHIx)UgQ0NIT zpxm8U&3t{PoCOgffd|YTHdAvO4DVQGN`I#|DGKqF$gf#o8r zpQfn5NEFhW`I(P5v>V^N%n~hbKPA`-5N=`}J*t-JeP)uqH9|6csd9YWrJ*3pYI`gN z78ifLX(d%uR-hcr({ufY&Q9s3T{Ifx{rU(z_tW{E@7^GuuD-;xg~c`hE533L3tVa% zAXW2+Q%PVh?ZRap=;A6f;n^8XPA(D?hy7d+Jh$BRi+#jk1eYyn8c+3v?{>@-nnYDA zMeA=v+xwiE?>GW_QE&K2vdBJ|tNjnN=E~U~{n+H5u=YHg z!Bm9bzxO{`=FJGl%2I^SsVJT0$$>%w$YsryvlH?>QHZxBbYRTg==x6TyrjEfb8$if zXF6`?y36$%lh!IdQl7p~KlcjE%ZvG3i=S``7_<#y_=Xb#si*s`LWOwMs|^BdGUK60Sw# z2@kH6zv3>^HR`u!`cT0kRo2%aLCLF*#Jo;c&Aj&equ9M(6}?I4ug7mUWLu=O%btX1 z)n}Wl?*vfySVvsbY_n7|T`%VMiIba+jhJIi2SCO;9=<;k7$r9D&KL_Zc|^^iC=4QE z@*RCIL!a$ES}WJI80JyuQ?*2CvREjBXkN?(;9^y0cS-c8&H=p}iH@^%VG~i_hm99@?W)?ICsCu*O9y1XE(^Ts z+Nx;>?~e7I^u>-Z6)H#dv1S|IB0{Q>bf+I)L~Lq(o*^P^soSLeltUxW(?)JG|&~!i9j*Ufzj>U%#zn`DU}I+aczfLoB?)wt0y5>lopP%UcfQi zEW50Y;^)tm_7io~ce@e=_y_x(Yme-^`Zg<${R481Tqw${JwluU_Oi-0gHBS>Ge-u_ zV>>E8!wpiaH)QpCQLXJ99{R>or}*59bpbv-_U&I1lTFM=8LFlrAGNc!9J+I`Rc}Wy z1S^3So$bC)?_-oLI&7cF?F^(E?WhGUp2}Oa7p1U5a~|J^3k0%43rwk%SW4o&zCqXW zC9oQV9CEZg?9gGXv50GaRn5PkSf^R6gl28Wg_e5Sq7<(A{HDQL55e&>P4oce>8O(P zh5KeU&X-zLXo}S7;-c5Fa@D^ZphFsB_o#rl$E@Zl1r22Z&()8{&!K)#NOb z_$6)_7GNw%M*?%DFs};%&Rg13y~Bbn*=e?-DSRh=0q2`YdP0#2RG`Ak`f=5f1J^tE zR}*@mWGV+2lWq6K#SUpi+_-DxpVw^xEm08LFJu9|^okhOP;m)ba=Gqi&vIju=PYv{ zvq}+zTq*P&v>2hP!vYRTw+l(%-RzFgY^7Ss6aj-8jWr)qjrNyAS6#wkzn<=WbxU$G z?&QPM|GQ6OGmxO!%12RY@ueg(!$4PXGRavXHS`7J{r zWvT@HF~Me1+NcUS3&8!f$XG8dz|xC<)M z1*0>q`nAn!a6yA98|JYUhU>afBIko+FcLkm;w`*Du6T^b?|PcNkq~*Pz;3HvmT0Lrw$X3k5{9DfzFqg z3jJi`#HvNf&*29lnN53DcI84_)lYBqZH-QjVG=>*8NJQd1zT&}!}g=<$!*5E!9M-% zE1e3Yl5^A3jcnW3_px)am*&NLzt{%WI-u=&^waAz{!9%f@egx9h$83hdEYu;hh1OH zi0_=G(=W-Z_Dq_WIZg-4CxmDQ*Gj}o` zt|wW-w=x`%bl>$*2D{9c0(6OXZ1D<930AV6hpYBe{cKp6KQ$N6yfrdN}YCxpAyqa zcV`lwYbusT8_BL?%=De`zDZ=HsqQcF3b%|gcn@>lC%;vbhjj)gNo|%`B3xFv2ltD_ z3<4uHI=36gex=yo&2-(K{IG6He*Ae+8a#aU5>hb~DgD03yHSKs z2k?$$!e?s@m<)fovR^h$tNN3>mJH+?$khS%mwz*+PoS}N!OSMVsA5<<+?uc0w)SE( z%PZWnGOPest42;&Ap-$HO)bWj9(t&xSArgRetwf>=P)g@-HMFA(6QOUHK0WxdGm&J z^#yfpUOn4{nV%Tw*gLb_;eC3Lyx1S!rLD>X!=mp8>a zA)!;xPVgR@Ql|VXD}Y2saA3aDW3p&gWE>E%vxyswe)2L#xs|Pa^8P!O%~6SPbwfH) zhxHo`6FqeVv`=uX^I}K#%OoCRXxpeJZduajCaR*8Tiz&vO`d#^E&Z>@QC;O{Vd{e- ziRNGtiF<`ksqN+LCu5m!`Zr>ntnkQ-@ZI+IeQSDM+ocDAU$~LI^dypP!(QGz^H`>T z{(kFi&hE!$M_rG?+e4=%2t^yZf7pI2l^6S^a`A!9`sWiouXkZ7wg9xpcAZ~!7M*Z= zu)j%VF0kiK86d`QM|!`KIgRGfHOU>o^6WZXL*66@&{=YS+4VX!ZtDMj3lPSRkW0ZU zQr&w!OuGNtMB>F@Rq=`l{qGW;txZ8)B?5zvv9n5vA(=Z&vH~(-H2T~kzuP2QK4Ud? zxSEwcNwwc%5frB_(<1Q7y9(x2f4mo_hA|VMdsDLr%{8hX)~ppf;Q ze6~*F4O;#31!$WODrdf`wx0rL`_Nwl6Bg5DqH>Z)42X9LF z?uk!Fk)z5PP--4G7>FZc@KN6=FihbP5MTRwaBkYmu9v#JSbro*+5CeT`wPAcUA-zR zih)gM^CAMNZ3=oXOv|QQ4`cu+gN8G7hcLC&lJ>XbxR_p6N0)u&XbI#!G`6r-s4dz+ z&^aGD;CrxsS@XsR8Lixo+~Fv4s^U4x`7bD1p8?-%5-)7nVuupsZ@~(dZh#&2{$R68 zGx6qmJ*T6N#q7o7Q-+m_wy5%g&A)aFL#>#{h?s^+J8kG?3G!nZ4~fIGiGFW?myDxf zZ?8D9&N7dc?9J;nod`^%h69nhua3R&>U5|J)O55FFipeRT_8|R$G!Ibm2!6>k?4_r z^*fx3bdwv*q$BxJQ+oa2iXPW;KsDvVy-{q`%@g$2s1#fA`iy|EXjw%JtJ4j*>PSy6 z1_t#&h~sIl<#4o<8=r(?FF@>Tj`K~|2j84#;AM=;*`!KFibeoT5iUle za&HG0g4W}iw`M@Li$f&c5VDd#xsndY78|yzM&nKVg;|&!`(ea#$rdcS&PK6|wZm7g zwe>`!f7vJJ*~)J=ZMTX4MB)O;?V}nt0-HaWf}R8C-q^XfAVJxz^QkKSzug6@M0zca zJb`=&i1KM*`f1%)>xR?lVG1nMZZdHd&nV2Y(0iOuCL6T^P&pui0p6ZFTCMkR)97!n zcE$}5I`wX9=Y4nzad}8*<1PcG2+gG!?a|Mad*6nvc5IF3EK1mP4!Kd}NV;_eZViO8 z@K^O^ntJy8DAG?cAtt?ii3u&Gl#`@Q8WfrW9sEB<_xoXq`T@iMqJiIVy+oK4*^~KD%`~XLkY|;zCry=vy;Z=zN{u z-G0|P5I=4T)29f|$?&zYQxkY{Hq-R?cJpI<+3M~q*Ci9?Lvx)wBGGA5iz_2?&D*Y< zn?ek7Xv_$!CE~7?qJ$k$nz}N4dNb*({tQ+hyC&WAW^~B3eci=3S;Rx(X<=CU@mYoT z%^YOAOdS=2wscmc>`BN*7hh6z6T_9U}YN=v_@ zfX32+8DwEO{z=HwLAzEKo$%v7{ zq3n<7kd5;VXD+GmX+pneRKJudB}}0u##aoIm-GWu*R_9Htzl194*j7f5%#u)S(}GH zahZ5>lF*yEsS=W)?+jkqdn^*y{i3Y=xGWV{dA-i>ECxYdSRL?8nCe%T>RqEFC63mM zbi{cXo0=rEKx+OJnn5hbzm{_JsepL4F6?9(#rwfT^w<%2AITv*By93N72qG0r+qF~ zge;<6>@pVXokxl>M$U(+GpmZsuWA=SD_4`y{6%K@-QTvTZf`TX~t%|~b9Jq^oWTC5#nO^)Cz6ZL*LWaKmWNpjWq^zlATLw(fFl7-{VT*YA*is=~HogRzr zNH*2f)c~>#jwxFFK(X}k7DEk1SQ>RF$*iV)T|%sW!{0)&lkIKO>!Wy&$=$a zCbV`9J!6~oS`a52Z@2t-!E}G^TxYk>_MCC>!VE0lR04VsH(@_8>Dl{>Gd*47eLEwx zl5n%4Tc4>5@;0w%gPb}b3Zv%cRmzVj)`uQzFCs2>Yz;>mzwB(obuieuI{W8T(tbmK zC8G-;)lmB0TM*(1P2hcuWv@wOQWL!#@2qCKV^=a0AN{M8PDVaATy#Gmuey|oO>BM=Bs%;x_Wm6r@w;^NZ?L@0VbD2wieVTFNBfY0?!GV~*l@l% z)3Ytowmw*=3M4DkM)doSQKHYTv1Pj`MFcK3sX442;yrOc}x*0LiS@jm;ec)~XWhjx*(gb^qQ)(Yha zpox&a$i)!Nk+)nX>J`=j)J(+&AdA7b-HMx zOa0%A>1(RmN9kUei+6XosHK{YHpP&z*1N=SD|>oiyw2z@%ATW_F_%I99x2ir)ZY|{ zjwoIP$YU1~sz$&NsYSez91;;8e)g}-Z}g((7k3EF1K0pYi$wzTwTt2UhkQmc0)xN`bmCt7Ct_+cuB(7?R@S><(YYC=FHrV6mD$S;Mt4@_3Z370WPgOn%7}{O=}nzh;cpVDt20f_x*BkK z!Wa$i&M4sYz_BwgyB=;6d9uy-;JJuO<-P3r-jCzZsqKC8!^|c&{wj<R9k zdMJT+VSL;82(l^8?tCY*h?Fb8~QI< zG_Q=7G?n#i&l8j+B$9#C*AKOySj=?`UI1nmHZXpHXG$G&XIkb|gn5YrUMuZ>IBoe~ zI8PF(bYcaen}c>D=*nxuupl+9h4dGCUqLUfMntf;KFU;7cqK^W4YSrR~n!Rg3t>et;aO{)#W@lHMW?wn_ z(`?z#a{BoOg5Q3{O~l3mN;cnCfo^xM+$8an)^Fnl#;&? z|IBxn>@~@Gx6gsG6GBxrDIL zUAdc71Qtc=E%=1N+MVn;aeG;HU4;HCYO#8DI-U1(Y5-UrXIbqLJFwq|?_Nu<6`Xl) z79K5$BSIeUL?R6FLF8-&2gf8$doSbE&1cj_4j3W(t}bqOdr|{F7l|^Z8g?`y6zX## zb}qCVMij(NhV@&EUJp)ujM&b}E~leD$@81o;Jm%th{Itm=K>J{o-C6*)}Gt$AQ>hi z479xc608f{b39$T(f9_@FiI!7Aq9OqmfL2-;yfD=DsVTLtJ3h(#na0Pd@L2Wpuqec z4`FA>j98_{9#`(o2U#w*lIxkuqX5-0Kxbp+#tdpu%9(7*E|aHhZuR8&eB--qwFq++ zzJa12BfhzbdHc5(`4s+wXCZQJ^_hfde-*l-tF!%cVyg1FuOX}B9&DV+`&^8F zetX%iIp}B_CaJ43NMIzt3*{7R7ppm8H#$3CJa_KZXw%Fw$#F*o3OO;1>-(`)q20!C zdi)9)dX+lIe)ePGJ*)pO634j!TWJR6vDYPpbTT&80RbCDl_ z`$7`mOSa*Th2X+CV>=sj{z5S-nMzPwriVa- z30RD^Pt)Cf{U^K&?C@}GU8Vi3W;%ghQ(10>DRLgVWUs9qdd&UlXh?+OCh>7LVzazT zM-9a-C>8YvB?Bdl5Z!eRl>-dEDG)3$Wm#}GZA!hk$}#*U+AWi0H_S|!rv^57s!B^z zaw*^62bDMf96K)ijjHd&z+oKjvFL&$&iL5M<3z0C5Vs?NZGFXK@cVovZSY@H7h2NQ zQovv$z}EXk{nL%3JWenN!e=y_@~Kbkg?J?!DDgh;Ez$ce@IxT{`EF4F!Q#LE$JS!N zb_1WyMUBt;3yH9<9l?Y-YJ061KlrXzP{I0hZBpzf>%E>oIOK0aB)zJ31{%*tlw2;W zfFK8d52qj-w}h^j8$6wEAr}E(DC9|^?l-E0(ulOY$ub0UGyjTg#29%m1@ zy}qbe%uEQ6@SEUj;V30$U5pH$l?76qQHnV2v91}Nk+_f!iGCY3i-2%28u~!NjV5bBgiYi+)TUe)YlTa6U6UohTKXLH@frz2aiEmsV1pBfe6>Hl(8h3WLzPU2hJ3*^XL1uyx2*xyCfuEPoG2T&jrqPm zpZqQpCC8K5UXzK)B829m*?Pz=Ms)*6ifoZPvIVce8+_U2Dcdis{cWk`Hgp?B+&UO_ zH4bGK^Rkm#13e_=SHBRwa|4mPY>)4Z^lOE}x_B=4O2wJfr_6)qL3t(nEF@~8{Msr( zJArO;eZjT;z}Q9CW>0uAF=ijl#c|9T$FE-&UCTicYs4DZk>33%K2n^Hq@#d+_i>+q zN5)RKv@X)s(9mi3+u@~Zb=fN2Yt3wXJ%}AH0pn50@n*@reJGUvYUezi+7>g{Z$lG|yv*%sANsD4t7d0k^z`C2@)NoNk#wLVx?kdBs9J9+;V2ylWybV5x@e zek~2RPOH18Y{UcC*>&_ z5HuT=uIDPiIwLj&HYo@CuZ#%(A_Sz&4hbx zPjB)!nIt9l?DJ>{c_hUM72`lBvT$V+n5amVIOH%(eh>1)1Zkq#q{ zUFdsbRS`cBBM_MuP}$FWk7`2*ouTEQRkBv!WCSiy9gk7Or^&A-ge`u4q*{J5EkfHb zzOm`)NoTU1ZlWdjCyXW#bg)#P1Y_I35=352l$BCd8KyuWrje+P7*H{O6y|bwgH^7! z+?1pRY|y>z+kLU(J%3o_Lnx=g1}Bf8=(rNa{v5QmiS2H`yWk^F+yeG77J3!( zR2?kSglfX=bh(&m%li*zgfZztXWUbYl36EXcx684$0Q0PwD2&z)L`%IUcm8WQL zY&%L;EVVs;F6BNjXd5?iC9p0}24>Y6Y%>8{4ec^R9?q4K?@Aekj{2V?Ae)=?2k56EAb0-mGZsy}L#I;xJ- zPkCu? zH@ta`6aDw|2vA4Ll{qkUfqD_k#qD8Z12K7R5$&LNLZT-LI}1l`deTBm2!ZE?{ipJ+ ze!mLs+vk{)R~8L-mV886Lls&Z6$=}q?|kIo?G|IQKMy8VeV3#^Y+(&0#0lAFO(UIl z2)(xIzk%|?D#y2_YW*j_rEwY@jjhz8)1bALY&*daPn%TP{3c78CRgx@JrLc(@=soQrj7*ZEYd@sqzzPZmWcwp7UB+tpB za>P_*4j@E{#CYSTiOvbM4l?jqMOa&niwa#Qk<>oTCQJ3X)_@H})(%<9HeyjulOw5p z3_$K#(!)qA4CK(P_tXh|gGCEG{fu0AfZLLH+pLa_#$Y=B2C9wm0QL%rpcd zKgJX*l#n!Wl&*e`@v|-7O3v0}lisD)PG7mckc5Vey<#c)U7LY?+9bYK>dMMyH1(ra z?(^xo0rb~WY$Z|80A{e|%~jE#7+2KV|g|7C(Tt2evaJ5pd8A>H3Lw`>W)5Q@Z)Hih~4xzuet#}#?^Exvkq-(}$W za5fIk7j+VZW4h>#oUypKo)ru=J%@w!)9yM&UyJ**0VIj!�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�Q(I!ZRClUXSCePHnOHwULm+6Xu=yaQV+i?K>I? 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�} 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

      ga27E5x$0k!R=54el}Hnq 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

    1. >J%^VWr25l_qWXBWbf$2ZVr6|&F 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

      efb7DP^vS`;hw4JjTbuG;N^TFFX2mjyy&Hw4! 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

      _w)L4~`NmcG6$rh(kU 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 literal 946728 zcmZs?cTiJb)HVuIl`cvRO%POS=pZC0RY8g<(p%`EhfW|gDbhuygMd<`i}Vr@2%UiR zUIGLOy@nP(fA5{|-uuTpbIw_3p7pGCp1o)G%$mK|iP6G#Qo0upgi{&3E@1<(!rRL~tOQbAs`W9yE2{ZMy zb++<1Rk4LZz1-YQZERtV_O7N@Fqo|u%oOHmW9#eaYGdmuV(o0}YGbhUq#%#}1&I8w0Wg18 zh=Zq_t7Cwbm!q5O{}(AH0+fC|%J-Fsh@0qz%Ck3q#9pmbU-2t280I7kaz*-%2Dp$EikUrU$F2P7Eq4DAbgKueffoHz!8}qBjSpn6aMv zdM-CNFc|KKQN27r>X}R2TUuHK!Qq#9;6;6I8=`P5Oc@2{#nU4J@(I(C+mdjJDLIC? z&f}=!wCk$_%+8T0vMq>kJTO=9X2FZbQ5;meWswvquW_-H&{`sr>|ftz0w#54H;POX zp`PN`euzSBGyIj*8H-4EzzD+n&SkxuCq-uq=t=!BW|n+?U|Qjbw4w4$V%2F?cyO~J94TgG51YeD@tv7QVnC_@5MaY5ZBi`W2qK3sbHg;dYRMXv*{y|vz_i-5^qOFQxm@o z2`ne}U7A;X3GZa41yt&;#snB3vg`hxsiF^EZxMX{U^uZ%6wtxXFSXQK2b`WW5ybEj zDjpsVIi64OS9>lZZZ68#tJUl}c!rb{PHupK;j^JV=bEiE2x(1#i6`ZkK)OM94;SNm z{ybwM35rN#-s}7^mw>9i^Bkm}Gl>K$k{k#Vb1=8?Tui?i*D*QSk9Bq58toea&{n4L zehLin-f!>-e+AlFyYE_34X~pFZp{7+j5_hY>5Oe%%f#(qnZh0&9DL3>`cNyT_9n2L ze{ZjG5kYE_deJ4TrgU<#Zf=d_24`KISDldH^jC{AfL3l}#j5fS^19Q8(U>uXK$<9$ zmzA$*3$XJ2f`PqIUg@z9M#E;K1!6sc741(OI=BTY5o_UM@c~Wh66S;JcDMGsk}_VS z@l1P%>gEK_b?2dj*y`FiuGw-lT@}5%u%;Op{s?I%nD6Nd6SWkXw&Db}?7RKi(Axz@ zEATCWMtZ)S%<(5KdtPHu1ykCl%Npl>F|Bu{U@$b_Y2fu?zyphXnMeX_f=S@PlN3hc zKhYcpA1M0+7zJgXZ5%sJZEV+~i0+{ix%MY`cH{AipAgsY(Ibg&OAWrO7t}V3<;u_{ z_yesY38I^>R7MD7qvnzK4I%r4Rt$hWx|%GRrjP1eA=zL1P59k5HOnOj4&d4ZO3m=8#u3kU~JI%l%f;6bNdvX29 zpvxR6U2c$bsKc+4`zDmMwUr6*hVR`XcaAOx>ug2ji5q;5YpCbHCTHxFH^Ycuq zkF^643XbEskrq3ezj3#GN{FWo^Uf|rM6Vd5;JgByK+sPF5%sQ8q8TU!v|<5x(iOzR z637`|Gj(?IJ~c4Ybd|d9QtTuJ!Z&DymY$5;q5waTcyD-&4C2MXeWIc+`8_ujEsU2c zs95IN&9s)!g*q2?58mHpTn4@m$0E9p_XSbwQB<_!C6|N?2AUw;4C|%c*%M=wdCgt= zr&ZG_6%6cl8_QQ;Oj2~XR+uM4YY42mm1tr+uAk(l>>!GWYvPTeYJab#B=-I7xk)9{ zyNRYEtiN~ks|ISSjp&RMIj#s&mr}Q6iHUP9KnTA}?4Bu|{!K49aAB+E@+(f%5Z(r% z&8YFLYSdbj|d9TY}$lk4Ga$ZrO7+StDD&XQ)3>OVX_ZFo?}~@ejXpp^7murgDLR-|w`RM<6lO$K6Ux=@ zn09mV9%nbnD_wqRUQmUs%h6lxjz))n{1akLW z3cHUGDpwA$E0gdp49?YuLFsv`s=n8%&BRti&?21G{R|>y9y4BUE?K=T!9U%AqIBCa z&pJ6R{Hmei86LO@_4*>+>CCE-t4JSAriSnsY#h5Dr8kfMJOGi06W_B&6KSj98+($6Wx zdD2l^Zho1b>({X1{-d~X%-r16c%@-6TT})c1<5>hm>aaa>Fb5sg7`Fyh4#^is?;kY zh^f$<^$%2a_{~gWSyT1un+qu{MQ2)(le0HaQuol7om<Odl$rI<-B1K9+A<7Ru~WKn&6+ancEbUu5rlD zJ+6|Kn@<#QLoT;{iQOR|U2zbA`qAMsCM8XQZxn&kWk*Kfj<~trPO&k+Yd1994%m0c zRF*95A#aX$xDYp)r}$${V5W?m5pM_9-Tz~c(HP&N-%w0iX=%$YgUry13J1gf%3KtE z4>uPRvgP`p*kr1s`g+9G6vFr@-#xD*{=SvlT3KPeF~*I%OkygbHMhN9l{3|0_ z1)AkEjOB_4lSR<>d!|RM?UT92ReP8M8jl9|dVa(??q-YR5JAAWRR|bx`8`tJjyw39 zCVxEOz&9EXw>HN18j6AkqidyOCMa|@?#ghSH*iLadw%EMkSgTjgP&j5BiEQYtJkkK zm6ce!JEf$;+Bj~LR~wF32+^PLAMZXrMAX$*211!yp--FQz@+xVD`!~5C0E$h_1;Zx zV9&R{s0$j7e>D*|xI)<6ef17_KB;D+OoWzKCZwI675-0^Zs3{LY19;8xVZ%swN!~9 zKrP#_SJ$~H5n@kD0-&ZU0W+^V%%WKsJbG|ft_1|YQZ?DKsn`}U>yK%D`A4O%qPLls zH|d@A&q&0@SZ)E~;&g7$!VG(RgD{9)Dk0XmxMgo9kNBF;Q19Orz#P(&gE9V60b0AQ zE=~$OJzohOPBkq6ugf3J?IE%9M<#0}Pp4p~Zel@{vQm%kLC4^VTt%)Mu^pBa1Oq+~m|t+AA#x0NV5Csc3qyh5G4@P=X03EgOus42IXevl`gW zEqC7j=#ot9bnf4bJI0@ zr$-wn&p6KXNPZJIa>`hN{sk4PtNqY5;uC(+x_K@bN1R-!HJZiBnW2#|6m=1Fe)#Zm zPo^@@vr{}c?u@FaR!iZOfv&541}ByUC;B0I$ID+o(>#{%n?=R=MreGaN;lRBX`>7M znJ^;1rg@lZag~MP47~(zO`?gy)>5g)Lu65fOoQa?fTbb$lusqc;+pzWRPr|KZfx{W zxN)0OI|cQAEc8{kKw=_j`C97a^3n}{dYG<1P?JzXnp&}OH1Dw-+CgG{+GWbMx(Mja zhlb>|218P?VxB>nGCm^{u50d?)Xg$cWS5G1n^e~qMLnSeOGAq%^x4pF<8NasEObp$ zXJJAJC#LW#UEIdtv6>|yow9wF zUCl2Ilf_FaS(4<~wTx^q->bOk4dD2iv-XXur)F2<{O4^>I^y#OG<_CGc<|*wZd=QB z&P6q9`79&qnuBDZ*}J2j#?)!Z_}7M;|Ni?jx4kjhz11?r_NL=_jP3W?jr&I4LG2&^ ztmi$)U$X``MxDn8l9r$Jd}%u9+##3RG%t&TJC3Kx=z{|1FV!YY*d;Qd^8S|WsfM;Y z9);g)K&+OX%5N*44eTKa$w$1RQx5}RcA%KH%IxjvF_k#`EPOuilL3|C`ATIH){UTO zl?A82vudFU(%I1Kzhk+F`|Fo;O9Vo*;r`<6cvISd`VA4i?^L<>L(V!=-(Tjwym#3X zex50~Udv`+V!Z{nyAe0_7M<9hxf`u(N+p*}Vx#5KY0gFaM2M8U0^8GYM|Yc>`((&f z%h-h40vE~Euqgx4;iVh=K;>@K&D9(@;RA(AW=d_e99?r?tH+Fqsx$|RnBqQRk}@_X z#ts8PtSJ{evvDd~uhz)iV3uyy{Eu@Hm-d`Wo_|oT&Q5cF(W4&2j-3iVt~%CgewZ%; z{Bt8K(Lr2?uSAk>26x7Si%f&@$+(+SE(GoF+#G`N4uttRywCm4M#db%i#s>sfEf>r zT({!j*F#!Dc)huoTd`#){R(~y7M6$H*O<9~Qki>h_7K-&N2{x={$c2IU9ha*u<8P= zz^9Y6Qa!l2&f&!@7bKuE<5!LORCKa z5VS`6xEa01qJ8WeM@HCg78oMcG9`)fNFfp=%-HXD^wR=S?(Y@6Lc!(kAoy`;>?!Odt3 z-QUbsM*{ZRj&6p8xz?!@uchv(>?VTyu-cZl6hrvBRh8*G{Xwe=+iAS5=ym-#PBUPb zVGE-ifAVU#Yg_(QfjryiXqUjx&&mMeWPPt+VRT`aa1}lk+$)yJl-`uh7)Z8q1cc&a zq8uCfZ?2Ll0MjWocBbH!`%za__RN7tn9yHO1wlmz@5O^NuO=R2cMdMo{g}B&SedHGSS3N>1UOp^G7mR7F`7^O`J4@}HD3KBlhR zy5SCk;Q`vM-FIKzH@LFH^X7U)SyY2)RqiZgVVW7vs5VzLG)|qYL0k!G(5sbciq$IX zNMRiB06?4w>{c0c9=99(Tio}x-qCs8&w!N_oRv~b9p4lxW*(K^Z|Im1H&8ugbj3~w ztL;nw*g@4ce_S$vWrp5PH9;g{ZZyAjoD)v!ub8qv8WPi3C$RQM(Q! zbj-6?5}~EJjKs5-FIt8d4}!n@^i$nS8vm8Bi|f9%bRs@z*G1?<@FWF}qgnWt2=8As z3JEyQGS%1ju{HO~R%PLj z=Tz0gjMeq8yU6xepWAUiP@y3Sf}DAK?Z)=XAjCRHRxCMb^9 zg&qhmfrGD~oAugnu`%y&Z}euI8N1n)=wl|2jhpBaTpLthT_dAR-j&ZFu5ze(E%vCD zrH-iAI3>!%OCYHhv$2W2lta<~vS!*fG6d{}?``+;|<-f1n=47z~)A zrF$be^&#bbZVPgw@_V^Ob^=}9<`JeH`nJIgAXf1H80nxQGnmCY%QOU^rfLOAMqV-( zn-(E9W|@uMz-{oL%PU1@2g2BgTp`0@ZjcR8p%wZ?Z}8uS2$p)HQMuQa4>XN4la?bk z-2|N={_m|MyvK@xM}FUzGI?BW>WGv}R9}pnWu2ZJ@e6nMhsc{VT1$KWG|B{7C1xnZ z4CzA22g%;NRuGysWv8u{xK}uxVqqTCkOUIl7*HpLldH>t8jNurozE?`cP+JA`%C1j z!^YsNJv1)nAUOdgkpT8hJVlRpI-Yz8pB`QtLjTG-B(#>HT7B}%!29q;mPBVciTHB& zO!T6Znk^{9E2vn0I(BJ3dZ2KR&Gz^B=^$0`n=fLME&M2ZKj0n<;OqXuv(H>YmmQ(E zgxGqgJ+e@;d%FjlI^{oP>|IBInEoZn*PfFS(sBT;D)H2_Wgl~)VO8|V^zo|B(0~>d z{0Q_cOlT_FFKI^JYDOUqvS1X@Ss!c|6JAv?T`l4s&1?-(eHrXX`266;U;h)q2S}&i zhHYAGdRjYU+S<$bMj)=ZuK&Q-Nb0$ zQ*bgXDSHM%+Rs&XlXY}4G@_6nB`(l6meae9Prs(nT}(3{`-Qv!&w0_Nh`qPAV zTB;4ltUcG>ue<<8?p!#x3^H_(fUCKh1|6@~r#w$XxwL0eCSw~WxuIz6?Q$zx{&o|= zbdg45Mz|P?`LcWb%RF#0u;sLjt)wJL#xRT5@xpGqlKzb9P+jVjq@1oDqLMSH~iLq_Zp?JsbusKjUEb;^=tsbZn3{wNg(!ipyu& zJURenPBS~vQAs!0pAD-MpR9yVvsYaZ-z*QV2oDc0}^rV$!{ z;XX8d+0bz-^eF?|l#Tc>=J#OIO17Pq{4s&MEpkl)-2O#I+K{m)`Hg(1{P}2IO%pNdyx6%`qn31;cGx8!n`J@OK^1DoF2c6ZQ*lIJ$FkMkI z%)VDGywC#_Xj9<(ofaaw_2nJug1#b}vc74-WZFZs#idx@CBdCI@nju3!BB_82)Vb_D&3rT} z6<+Hfxd1gEq4bGm!w?TW;u$0XKxO6OEixX-?j1#j^MO3UH#7H0z!s-jjaVVQe(w@W z<`yao?ZBZV-d`Adz{Gkla}fIb<~%zwW2M#Ll((9Np~oZQ;9yd_GJjgIJ4%GS!ZA`3 z$m*bRR{Zc3C~ZuXqj@b%WB|O%5%8zMsg#UU@y{_7xk< zL}R`lIF&~o;scF3Dx!{ljG?|N@;KZk`s)hMsn-&$JdFe^`B*tI?uw*)K#m5eOS(aRzU{6RS==av| zClHVP5IWAoGO0+7G6Xov8+7S4)IJ^1a3<)`zI>NdFK+mJ-OK6ex0l*X4(VGD1%hu+ zr~gWbP1Xm4Zk9!Eh8PVtMq0WCvL+!E&7k?z+%I{4yXcqQ!|wh(WUklHi6f7mV=jM< zbQ+cT671O0wX)D!BJ?q-Xr`4WmL585X&Y9~!W!CWDwRp%aiWzqaYhUwDjQoC%bCT! z{3x!I3bft&mZXd4;>=Cl|3vb-BZ!?_uayetdAdTz|uN>%lZ27F1K@>y}um^`i1N)kp zSx={_c#?rF(1^flesbo4F>=W#&OUpcU-;Q&6-37mi$vB9=PDE%cV1Y8p!hI^qGtRj z80NDeBtNv4(>FKz-sXxtlZ@+0k#>GNh5zUv7=tQll^1(&dnO&V(Ngh9##KQ936&wP z0Od6pNLstdaHdJS#Em#^^zcX2J{4!_sNAbfIrIEIc@l>;qB9k-zqRA$Ox@I$zAdL2 zr^H(6!3m_e(@JB~(4?MR7TJcI&#W1gDO#8C*hd0zOL83_Yfsn4&tiW1Q97?b1DF%y!G-j_EClWT|J11o%Fe|ffLR)6{0^l z#HVygTB+jJ9>6b&j{Gw73d~vAXWDQNT$A}e@BLkk0FK8_EbDm%SK%|6l*OM)%Qj|5 z*}va~EfHI~M31JP0viv?@9^}jUMLkel8_1{EKt1fF6C)IO0`d4I;zHICznvr#SWsM zu%{?X=zv%uwiU}Cy6qutO6rg8FB_TLzp72FEF2$vy39dT2opP4%8K#au-PGILUOC- zU&&R_Y`9~lzmAG7Zc&;q7X+p`Oq_RxMD1-bl9)W1@zzEVU|*;E_$V$71GqjdHjSUe z<%++(cxZmO>Rq+mvI7*+xmRj$R!pS0TCo!xEJXTa;}gpyW@FOdloxzO^t9uHj+XN` ziIa%;gkyW>Umd^CYMB(iu9-#8oy`I;V;LAmz9PpP9i6Q3?JC2?>4OMC`+jQG1 zJac)YSPy3q^c|HgN6y{srdOm+{0KGTO$ZinY8nX9O zwH|(d2%UC2QNzeq^)6&szH1WMCXvwljVIFd^BGZ;`S_mc|Lp}J2QR*3&n>~Ky zr1p3mePp+7Qd(A;`xaj@wY*6c&w8)lvg=@$-s1FqgadB);O;PV+HvcHgdNM?t27?YV2Wf-PeyjB)n1IFy(l16gDOco@ z{RQcH?#Mj3Ks~5OjZ5)&8!EP$biPpUT2MZQNBwPK0qAVUgo{oqMnPrr*6vs=dT43; z=K<0|=>?O8yAornU6edWy;Q{KZYkXI)Lyv~_XJi*H^tDhPx+HDe8X zCZR$7w61HmSL>NM__^qsH1>}O3wM{bKQ^nkDt}4X$K4whT3_|Hxn0any)IWd3 z#4BrwkIei5uyeB`z1};o*}4LPd|rh-$WlonS3P8T#?T&9x0W&g7UHP+#-g#eyc}_# zsF$REe%G1?zIw=gaIl^@ZJLeCc{Vb%{mf~jJFa`kR2`dq@{R0P)v=sV{TE9Aa}##0 z6>&w3faHO(;IK3DiCm(etnR=n4^&sXErIAXl%Ckn#3sNv?l8qk<@1TGJPjr_jRW=N z*e31e-Ekc-zlzrfA2d5b91}-ozm|oL5LZ*xuyk7H3#P%X0Kx{6uKR zJc(Z4LTqr#gP&oupv~X9^k8D}S6w78lVy+O>-!&u+n(1ku%$vuD=Nv8*zBWZ&1MdT z5B&x*rAl(6cLiLZ|H)s1XQ~Os6Jt#n=a~y~DVDp>Z0RJ)t9~uoge)CZHAD!VzV>Ac z#GV{)S~#XvgjQ^s zv$kbGB^~+SEl~GuK=|F!^)iwy90^F~@kYB;k|}wmK!t63)+61mo{K-?CzW!8$b78F zP5+Q?%rlrbeHy*^GkUGkIMYK0Oo)za{-+iv;ny2x%~ z*Q(L*?E1Pu6>wW`vkdPF__F`}V9a9hSQLZH|Ew%WlAbyqZ}l>jcXO6naen2Qy`_5f zOWKeT_s}A{&*@RH-B)&zu?vSMtqU$wAK753`MeW2{P+aw?C%K7%l ztXKc2h8_ecB=Ohy7^&Thq+nAuQfvD+z-L#54S6rT!$g|#~(zZxk=6xfZxNLaCh3&`Z@kZPWNP?5Q% zUp6Rmh^Eos#(mR+?e-hi^YG^|Z_~74+*mPS#B692(^3JePb^QPI;U_X9mV9F#?ZaYJ*%-aJXNng$qWMWU3X*e$bd`-}wGy1w)eL(!g zrO6$2VNRvn)tt4);o@A}agZr@V9=)c)0`0!bedM7L;**u+gmG}Gu@xSd;Gc+rEGUK zIM+%EAzPM_k^NWr7j`L428e01%Mr@EDLZ1dO{<^SPwRFz65UsOES*MYT4x&E^@PI7 z_(ntBi1*lXgl*1>_5llf-$vB9=EJr|THiQ@-7HL;}5P=HE`Ov-0G>@)o+e4SIKr zYBUNlvBw|&Nr}YTle@od1e+GBs*{)jlodW)_8jwozt~SK@9P1tRkfP7yGVXq3;LxE zYbQ*{DIO&|qVc34l3V?(YVD&{Jz;Xl?+5YY>~cc*k@ET5u^)Y8sdcgIwJwy$J#Ql{AV|vf`ssCDp^#}Y!vL1luUcmGA$=Y_r76#8pH5kp*HyTCUcKzE zjT2QA#qS>B(~?c6Q88fWf8uQCnfC4EOAytPKM zytN29QZnxZNP`yN$68e>FmLj9kq0Kfg@jt-hcbA>)B~=XkoIM@oDc~RgD@H2bC$9K zme_amE4*4gUA+#gs-f&dqE3-pS(xV;nqUl%Ssv z+P2J|$CHdXMkzabgAE>?8lzVF4!A$9spSNBte`q?@K3Bq816>ZfJh+J{^~}WY5WrW zEW#egl*y%YJSp>I-Cvlcp^@($wyY%$?cG!(>McM_h`6%q;O@kkk@O#QQnmkv&}U!P zs!sck{I)om#ZtAL1tD9fmRUyXMD#FUg3F~H5E?!7JsDUWTufag?`NufncWpDPc&dp z{78B3u$~5*YDE>?|hZq@{hl?kp8(XevsbX~0 zNRIvZes4QbZhQpbm(=j;;Bm2)f$d(CHmc0>#vxCt!{uhEb_l7vjw6`;92b5G%c3;7 z(Jqlm-~77M&)$~SyB6vp*2-0eIk5Vm4Hr0BZ8-QFc&)~E-jcnhyEIwgH&KSHv0QFA z6YtHyxZ8k!*3R^U(JpPuEQn9|b}l&|9F-3P&>`-W75A+n3Rs)dv0&^Bk2haQfWf+M zClAXd*x&lxQ!KN83XOu=cqR%iw$ccWu7}TXJMQ^CFUj!sQlRM9e#*nD=`)PJ{f?BIFZs7vO)CeMj z#l?#2CJh*rP{=-5Xp*0*@H^~T9-SoMIXMiS;%{**MrWvoMs65Rl1Nxv<>mIpA^T0v zXI;K|s{0&L@G`oe0yeS6q~s&B8B;o6*3;(enN;fSlG9nQn*-AI=Koe&@IT!XQ^%Vr zgyY5m@!^Skp2ax#6y+Yn)@gNh5=7?55A57`1f}`orUEW8>|h&`xjrD<$mu4lDO9A?r9* zlH6B`(H1JK>z50oiG1ogJ>4lETQe{xbHos`mxx7g^tD?#=8J?5L~8XCA1vnoSh*lZy-JNY1UY|AI zo!GWAx*t5Hrc%ggW-e-Az0dsajbkQX_)vJehJRw6;rja%0$F=&acy}CNW>V=f1vmAUQsPn4qS5t2Gl`R{<|o77 z+ijHEUMN<$kYo?nB{oOhjtx@Nh9ZCY{Ul?h`}upMnTNdoz56v6bD~N%?32c}!JLJv zK(Dw3bMy2sTXLO<{jQh$Pe2p1=QIQ2&ZH*;(`~hFgap+e_WEVx)AnNo%fkZvd``x zLd5sVar=+Khuk!Y-PZHVqj4aESv26>tYE8!Hb1TIlu>Rd!*G3F(Q?1=wjh!@6=LkVkP*GcW<3EI_(By!=ZH3^o~8>bVe(dD1xJ2qq$GanTySvQFeKfI;xZo^5X9H-2V@ z-%$?af$nV0`s}jY>~Yr2NPx&`yRlACL{!WdYs(tbUBln~<=$aN1uJ1@LW2W}mr3E? zIBmYkz|~h#H4k+XUFJ>)<<+{`tmq>D$Z{%Zb^OLG$B9kGKB9{I^C6V(GUeQOfkhDL z8?$9hTiPW9xr!ByT>*CdsSU<}K@mb0#CIe;v=xVHf*+)StNn{!k@m=Ve={px7-K8b z7ci^u+zyk!GrbZLAq(CbKXipIpuit_tD8Ggk7dY>FP^{OT- zYfC$mW2AsYu>va>!y(4jopV`=vcYE9aydm*)1E)##IS1WDoXpnaMrmB)$7@5 zxp=U8VQLM_c({zf!detJWP1l&EDO2V04QR59sK-9MvQop#yAhlX=wLND+XSTb-mmD zM!)%);UfELQ~Hxg)}x`+oDGU-UfM1ZgM)SpTk;0HEiLOwM;%?&RlG7;f4fucSeJEM zLtLV4__|w5!(`^$M;6<28eek#yNQ=7jP6mroIY3vJI*7}h34ydIm&Q97r|6>p*ro& zI;PEP^I;r?U`76lQ`e(_X$R3??Fw&r%W z!7#N#(}d*$JmaR@Hi32T>^o69OpVj}KKn2IdS8gwFEz`g(OZdjL7Bc0$~AxsO%^S7 zlOnmaPd1tA{>zKAU-yhh8K>qy9=N|Rm3bH@Ju7FDU#b^y z7+tNP%0fZJSuPPjwTyq%9x|_f%~7QMp8u@;CfQTM-yVI-wCHkOs=72R** zhlpo*Sx?r~sy~zqEH)c2XBHfm_k>JDsZ6`D^)ll;+n}D*_QLJ?3$;()0oU`Jh;!h) z(v!t~WKs@Qf{(TZ5G2r}~IHCf1T*XKg7+2(OUy| zCZB6`FxzkyV=*G;*-~3wSygRL@&r>eXEDCj9K}-yVl791mut&fvq#F(gD>K77s_t$MU{GmNaF>#|M81A(k)Ek zd3x>7^&YNj9`WesiBn+Sc{cJ)uB3??wL=reszBxO7qK_QEt`n{~WQ>GX3{V1AY z;WA)~SuU4R^vy2c;ag!gScUf>Is>Q2iW|Wl_=&Wqe|+X~xo&6=TY$GO`t_QxrQ>ot zt08e&D*UYSw(ptsyw&v1zbCXoJA!*{@(*pf&MU?Sf-X@pQiLMKD`JvL*`EBXv;;_5 zmJmf@;B|xa(N|Z?EeXbyK|>@!t5_cWy@c#AQX!l;I*TIlf_IBeyvG}Df&SHTSAe{m zoyFgj6);nOUL6T-4oreKQ*BiS$JK2(Y@r{upeS4=H%wwrW-bxVr_80gu*4pFjA(9m zbs{~oTScuDfw8h=74zD63U@%HZ^&74JAF``o(fj$S66_PaiU5kZP1t@%-{Kd)u#Z~ zj?`2IPcsn4Tr?5s+Uhz5ruBF}kK0;L80jyPDO}c<0u(M>+q-{X=Poq=}6+1bPc z^?2u017qYoPnoBZ&m*#SP!t{`b#2psLg35Ue42M|X^MW4pJKG0>+-wP)k6MO5gQjP z9QkBb@{41I+%P4Z%8_`^ijVw`%_Ze|bw;wSb6g}>0dCq9!n0x?u|J(s&d1bD8awqm zHAtb5$!h{KC%f6yuvul+a@q_6l^v3EHa;2(FOiuENECt;;^N|OQFIEh+&+c-!rm1| zT?8}Cy^3$aKD-Et`H8nZxSk1N!zNYdpmAr(6v+oL!mq?X2Kvu^LvG(nYuUs(!fHT(yhuzZ1RM zbGNMZ4ghV{Zv#31e!co za^LE}iFad|Eof@Ji$s2iQu*Z|<(qiUOw^M$ZzXB`CV<^FD~X1mP|vd`q~sJ_y87i$J_9@;TVyp@*PP!O9ZmxK~4t32J7n&{L) zujG1s;w_=W4rvfI{i!vk5rikF zN%H@ZFvaW$Yn^k;pLe*q6xI_M`elQM4}dxVLr5*w?$;!sghI*vOL8%)Q>sS5TsT=; z-3!lLP>26Au)Xt4g}(v>31632r^``D7}D1<9h`d_lQHj?4VPF%ejMSaIR14)7RC|8=ZI@coI_GM^@8$kj8G&mnqI`m`sGiV=bY*cmH*!04=J>ehFKt*Pf;R-02#V(V z&eWa`I~J731CLI~_@=KPm~d_M)^kezwRb8do-?$RbUR04DT9v%2!o%seyiO2E$VwW z_qqtf5-QgDPJ4|U4dA4CC+5Nk+cB^=jdLFk^cbkGKu5*Htz5Fh>ILeHJ8jS(&eFGrZ3-M)&_RiKTE4q?j48fitSKhB}(^rwH9dlq2r zIxNHgGoQKOWY*zxHiWoe@Sr|;yjx+}%)lGCcp!~TfpiVrr&?=CKB!pWZM-X-=JDku zCF*;;TcxkNl~y44Tj@Jv_B7U2^9jPHs{?^&f=#7+hP!0C_5Kd2;SDt32pjKs^rU^3 z$JpwV1!umZ#pzoeTdMwq&g=I+c;$RgAVFjs*?(~^=}q-WwDmTApMufh{b{Z8AHRfv z5jW2mE>nijN4?$ca8FjQbLRDkKTEN+bSU-56AnJ1N)`#zEbgIc?3QHNEzzr8jHriI z9yy%KK_43Rstj@6@wT@rTK@o;*cXsa!MB}1Ygc#CUcn?>WDB%6HxjJQ6_f#-5%G(7 zRPw!LJue{%Ad`Au$dPiDI!=Ci!WbpYtlgr{eaMPmsInT^(iD*6i)PfBVa!<;Cj@hf{0|E8)@e z7q+&%T93TDpXDZ>r~oV%_xa6L1~09r8QLb-Z^<_`E*oBk*_e(hhDjk@q74*cKQ^2r zbu(Ymu2luXr@G`-e2;_!fRtG>e0{dFcGXfg!EDN~QfPQV(5wxB9cS04W}n48fOjNQ zG8I9k{irus6RTu?`cZ&M@l|x}?Y5fNuhCc5HYo5Pu&fe5h`;X8*asKr=hAQs{Q85^ z{7xZ1oz{7V?+=CZIY#Fr@p_{(o3Yl6&16V(@T*VzGFWR$+wn}38dlA)b5OOtVk{lP zI&{G_V{3jyow=OQ>lme9bR;K!LQhtpC;sm3y|r}@U(SZgnu7_SuXclj3KI2Rr~w;H z;oix{Ze}gAC3DY~GF;NKPH~dL)o}YV?Zwx&qL=sXawDX%=ef$H#I@d2KXAAVDt+Yl z5zgG+c*4jdhg17 zy`il8TIVGl)wA=pP5HiQ2)HX4k1mH+boKpmV4A?;m_TJ-%zWhphm>*Nxj zQXX3!w;91bB4f8ny9c@RNOehnc43g|4&)>}43O_a_9y9oY2Z^qbggze>6P13^H(zew7i{f-NS{cxrym%O#% zFo_hCHEZq%HFImEU8VY1q|Wsj)z%r-tZUbH#zX~Y2*P7w>30m2zQ_Q7w2kH31n8>Q zZgPua*A{m|`tRsPZrtr7zjjZ&#s;6a)Ez%LM-bp$>sTGW@+NK_NQhEOzeZtJa+KH zju&m$a}+4a2v3h5YKk>3nmTw))&q;?j-=zts3~*g)T8<$a$W9Rr_o<~T^=^E)jS6P z#6^_XUPt3>zEN*1vGGeR!r@B25LpL(w2JO^%`moR`ekHX52b>eSgqT}G#tDVP#=KA zE{9qJ4kkK=?VWfiS!%A@$&&y8AOJ~3K~$tJdiiTdgqEW*VLAR)>4T}}s79_M6BU_l ze};{-xnN&z{p;c40X#K^RN8cnLp$$UFJp9* zW?>qjijNQV@Mcm<>@#+jSR8Gyoz#bB7Xw3L2j}VOWzJKL% z@YL}e?r;oV{eYZ3H^7H?RYvKdflX)PAYbP88y~C~wmvh~5sziml;nZQ$_+!E9e6aa z!EDUN#RAS^RJn90fcpMxEBlJ7~M*cjXVygI!ZJqa&6SgH_9;44qJR^ z%kw42Dx%||eQT|84$Qso>rWm5%X@J$yA72u94mI^M~-CeQDaLhbk|A28sTT6n;Y7D zE;AEnzGyBdzKYZk0^tUMcu{1G{dfK$0Jg`*NFARja=&!OA}kaVm{+t zgNcF5q_MsaCNYA{yql_K8)kiqy9U+ft6E&UpIXA;U@`&we-;OxnwdpmOc=kunBr1D zl^oJw+6F~h>*zqIGO49$Xf;^-pCXU1_z6U9i8ss}1?uRu{TF+r!L=pk-aCL}!w(E5 z>6w~y4D4H&`BH^{Yl3;L$7!99cz=-q@&+qEYawR>k8VvsC7H(@$Zf`duY`09z(CV= z=ilXAH73U_R{f$#DC*bRuAdx(4@WU_*c`UsA~L5nkCB>hW2}7bBpLhrmxqmBed{FN9L^FtK+oPm*_eyJbFxN1 zCYhC=dJbtyG#aZhd2&Ql>>x17yXyhMwsEws9hV;jioHD6qkVH&?rXzdIm8pN5NMk> zzT(r*8=1UgEZ$Kt7<++c?IYHH*6m~v8%6ZI=DR4d3nW^n7zW2si@kW$7i_$>VGLHW z(Vu!pyt?ME;L2uHOmtHZdSACb>Jr5VjWKN%=ov*YlA+X^Mfz-{uxo?)@`NJ@$ojlu zy_9D1;IVPWe&gyvfHWBGdX_i&AkP}4{%y?>Y~4oOwZ?j_rpG8hXm($~y32I!1sg2v z9ICFcmW(^!8W<%?^xO~dfA8K}qWhHpd#&6f)En%)TmKj+FDCZIuWeB)?eSqZLviFQ zy6aG`MLIlVbJ`4_B78RQ#hjw)wmG$LEbFLIk@3@auuK?W!KaU~ak>Vo7aT$}Hp1}S zn$x3Zt*fVwwDuhUQAQp3F*g%>d+q6vfKet6*U9^dL_$`lm zm^4H61H;@CFu2Ntg{l}CiOpf{ZaiuufF>n1tG*%7|npP9vyc&zz4K@%4!U~)9Tb#xa z_F+e?#a~Xw9$a+1L2H-VYxK#M%}pT1J(*K%jo$KLtgix7U;!HKZLC5C8xoY-`c_C zk1edoFYonPdoxKe*Dp~yCum)>LE_;W!xLWzjC@zicBC-u_;F|7w&i%;7X7G7~pLU$F6lP2f*a<;g`l8 z7-X&k7)xFjEkX-mK~jN58uUV;jx1U*c}nu3^WwjbtINK zAUFBb;UEr2Q=?}>GY@PvieUQ88#(j!A1CABiw`=^WvrQlXAl)P&Y0uozrVLc5TuoV z2E;hzgliZ=$+2;W;graP(yh)5J>s6XQSH3&nN1vbWL)5X3d-mI%LNw2qJm`>{9?v?g-9Rm$?T5eG1|Ed^DY{C8ed8y_ zlh{01wmuUt0JZ4;;B%vx;mA0L{$+Np&bhD{iP0B$gG6Wim7p4b!EGlsyU)voML-k4;+Erzw~!4N*fnD``Sj>BQ`i>Puu%VMKr zx3>0muued4UXC}`@?P7K(=C?&<>qoXxmum_}OzY zqu6>sgAe(Mn`2F#p~7zYGCpy&=mEPM9t`k`sD(ptGQ-$`E(L(xG-C0>4C%ht+E{C> zTW!%h&9V1sH~uTeoa@2Zyo@rOHtx~MnSepbctUopar3JC)@mZg-g~&%c}L#@E_)eF zhH;1yZWgYC8V(kd)zh~%$+IY1*y*v6zYY|L>d2?cloAHTbye{Z&Jj`t~P`)BL&V+7kjGrkKh=I7ZC(;;2wr#2I zoEv`2vIJK#eg7e4@ZV!~10X#&d#;6&gJR9j?2m)hn```cek|q1tjxQF<+8SC1IK1_ zSs7l^8o*O86^lB`hwWmMHdu>?Q$C{kn`<`H(*XV#VNu%%HD` z@x?h3JBEK~jl{7S?`{8k&kb7N@_)`cb0RhaBTiks)I6yPnr9Cc`x=cm?VrZ$T_H4d!?y(zEJ(#zw`@+ z|JfE5;^7OOUq~9C&pj)!K=x*Aiig4^Ov#@0A>_3|mpQKpq2&&+&d8^!!>~4+yOop= zb^_G_pm+;&f^|PHZ7nh(9!EHAoA7GZZgmH41?#6%(4-0QVCqop`fi+&9h?r7axe#E z&AAIvAL_a12$=rokS{K+=YqV{H@IC7Fmhw{(Z3@bJmS$>s~uO;8OME)=UQEHD7Tt& zhnP8B&YE^wl~_G5$FplTnBMl8f4^!z;Iz;uwICn=iX>91Wlg9{2KyzEu*W}xp>cQ^ zSc9vpjCH)SVvM)vO!UKnxU}x238L^K#Pot?$kcZZ9dk^>Wn?BaqJ|h7>|*443XIWO z@RyX)=DiZERU-r6t0&Xq7ZJbB?0O+ zsH8aJGwyokV7wlfO*5fu`EWAEnibigD=LR5_KyP0OT5O%z_r9Vqvd}ADsK%dbuGrg zJS_CnL$D)g4Ktk-Ue+UG^y{(l56>39`izwi5Vp0y=W^F!(CF|tKxM)yLj|EBr77d_ zRop$}I=!>4l>z*x4yZ5Hqm?5)^Ob?6dSvxo$@`krxOojif#v@2ea8zt(@k$SF_lLq z=CJ|MA428EbY}yWu7w~PtUq~1`qG$l{a2SBSAPkHqOhZTxNRW#F5<{GMsfF%V)2VmLtP(` zFS>^uJFr5+^P2fhPt93J-B9MInjsazbB#Aw?5(ek9J)pVC>wk=IRG8}>;H_>+R9De z!F~}C;aQ6s!gBe6$k;X07j0uM)Y|U4#eragnNL0_`dM-Jj!a|1gEQ?##icnLWdn$W z$Lzo96(fyZ>(H_C0JH+kjMgw6f?!_DolwNwxftZJZHE|AkH~oo8r@XeNMP(ejQWEy zbriRN@(_l3aq00#hd&zYKR8TfI0}p#>pVTo8&Od>`6e=h8ejILu!3?!Hc#24YLJ|G+&VRCBQ7o;b~H_ zn8c5qBxw_F5sz&0SUu+EHf|5c$wAL^=z5QF90{`vOf2g;T(?%+pL!B=cy3Ps#%w6x z6}TJ+KRUP$e4@=9Tk)p0+MR^NbH;L@(>%v(B7z6%X+c(m-O5^2H$Bmph(OE8HcD$5B>A+qj$>xZ9_j-EX53IL z*4i#Q^M;g+*NYi#LUc06&9i#;^dmMH+O-zP zW~Sy6;mLMLA%~0qnKydptyl4Rz}`AsPMc_R;fO8zYttN;5;3ImFxc4S4|%zvAI|N{ z7+V^&%ZI;Fs7yul>IxJQoac=nMjQ%y;~`flfsDcAy@r`h>TH@Rskd~iJi3!=$ z;!PbRDki=~qGv?Kijld06;gkEHfLR8CYSMOaz%jmiApSR;d$_hk4(Ygkl6?w#eN>> z+E?a(Su+G~ZXm-ZujqXE@Cr2~YPIC#C{~T-B+{)5M9n+$z)zcaIIO=K{75)HXMTN_ zAN{CT8JEa(O6}@glT6MO+@~@Y`mL*aXd;N$%CXZH`yGQtefvxnuwt|Bu@Qk%yd7Z3Zhlk9R{p3-P!DVuT>$y2F(e_bS1q!Knkx1GXJY=DndkL{0>K>BYe;yc<2RfKM~%N=5?Q-)x-qpWNT`=%e{tKO z@=#wL3GzbAABmM8dwhLCyp8Mhsa3)1GjjdMyb(Kb4{ylPBhzO1Zj|~xy$9p?^_Q8H zLfpu^CN;owd2HOBH?2!#*VH&?f%&YpdbtkQw9PmVRo@9K_}m8_XK7 zJbGUek4#%@yxaO$fiuCJplQ}-<86;x?U)&7W6`7UT4A><);=2ZC_i}&+QL^h0qCN* zK7yCOadS9qFkWViZ+pcoUbS~$U(a$xFQ+KbO-V?|x*$^zg=afJPleXCQp|yOO;TY2cEfN)-s?L4X)(|yoK;HO((|4~))|xK2Dy;E^@GM$itjHFuhW!QaWy5orlx#Gg8M z-iX-5Lc5Nx{G7<~t90xD-8aMVXL>+pjF++EEMIzJVIK$cRx!SeS!AXdXzSF&%>x54 z%PM9=rx)|}*jUVqC(DkBj{*p=7)xq!YZ?`UG1w$B$Da7jWsJ2&23VT;i0rf+DnH!S z#3r_ttuuk4gp+@~{j>SU;`@T8SjF8EcTbZ?u3|;G<5%YsR-gJ@CT_5HeUxxS< zZ14t4-&mjE#Vta|L#XisQYgUMj#V(q&p^SXubq6DWy*Wy?Z-wSFtBM(Si)^{V@}&T zEN(e)WN$SHsH?{(Ao1fG6KwJ|EQcFQS;5p-i?genPN(`ux~5nd4X&N!SX=X9MoxUO zJT~vJ#2#+&TZD}7!*<0lfz9PajbC7q?OIq5;GUS+6Q1`B9jq8G+K&-+i>#P{O?#1T zO#13u=Luq<;sNbQ!y5M(zYKPLQL{Sen&$&oY3FjUV}VCk%#&b{GIoll>%J?tJ$dt5 zYE!liGz91d{nV+d{m)|(reDfq=6dV{zs$idE*P#*bH?dNs(JFNPLtU8^Kx^?RnFu! zIJJBn2}c2Zvlsgp4A`UM%O^7alFhgFSeFHM~%NB6g z5Dq8Aqc%fHj_{(nr?HPxKYCbWJPzg%+x5|LA~fGBb;;1bQfBYvK`##i{YxF3ht#fd zeTh~7IAloNnWv-aMjo4s9GNbou8Z@?=&LHWa9M+69?bQ;7>Q$`tE&T+QH+D!8RxD2 z$eC<>Z1%lD8)vx1-82E1n2Hty^>P`~h9s}(ma}8#IBVx<1#&=jICrA0GUjRTV^v!D zAYPi!B1Q^L{gFS7Q7Ga&OUGN2<-Z*0$;_S{^EQ1CIHPYP4Dl9g>x&k17`cnE5p_MG zLkxVejvO$0ydq;-2}kB|X^+kOwtTgXXRim1s$)R$+V}bwDfa{UEIC|8X>DRq29LZ) zU;E;Y(HfVlpHJl2D8-pvCcEv*Yp|?AeU>M=o73h|hsME(7z9W+;f4$K40iygS^F4X z35wU5rVpPN3^*917-0LKS5Ch8uKnRc1o<^D^c_jL;f`N(&}NMA-M7Yb0Ma*yM>F5Z zxL*I^!T-a~;rhOior)y>)^6_^nXl{*{nl}l`$LwSM}uzY^0?=kxWmUWhRntFtZEjK zyB6?##8y@d11`@Mf%Re&U!bw;{}A==H(;N(*zD`bmn#?(M5-Hbg_$=F3WU@WnNs`Z z+Wv`h_w2#Imt27Jm{9=b1@MYHQWvFtwe?&zeD*wB9woT3=?6DX%|95yL`q2XPt>iK z$`{Q>o%Mv)@g0Qi7Eta4-ggEwZo078wJi@^%8$AbBU5iom+wN1pFp+6*g6POyh$=B zMzUUD!lShzAoJE__sG_5wDQ;`!dZ;C5zla9Ab5NG{+(ZD0;mT>tH5TR-3BqX$1Api z4zVRrpXaf*5{)g`OW^hso49FUF~mp0&I~y*VKK&Z5{Z2tWBT%o0!ZAL<;HgPYs!56 zk_R?^xmd`=U3^Td-I2jZ)u^8==`Zqta@9mUc;v_wZ&$I_2Eht;N{EmLF=X zA$fyKOzfG?7i-h&xuQ3?#!+FMdgKcg z{J@|sM#BS;H@S@7dB$KV5^u~12o{=*52ljHaKJ`gYiC_CoOP@ZanlySNo9whvN^== z_z}ZE^n;^B1;VR#f=%tVF81&oy}1&A&zzC&HF|TsiW(?qsNXmNfuYcguaO2PWM+(; zYwi7P5Gx4WtYG6Cc8qfVDrdaEh>NX-xxMw)noNpa|8m2-$o0vwWaty;S%3M~!8CQ% z3Skx39wcOWXv7yI|8L&ph?fTjFZh$zi>zzZy|?RRq|yrlGh+2s79e-DxmGfj3kkLC z^K;%J=^oZ9$$|*sY6^MQY3l6463ybv&_u? z=^uv!&tNzJx>(D9EsY=B{_<+2vtOWi8VJ1Gtm89w#MM%x;GkoW<}Yp1%VHqRA!GV} zkBk92xNyjLYxA#S#nMJTgvw7_ChONaHFuQ4tFQfuGp+~3bsIA3icUI6CoOx&CpgAf z%rSE7J^k(ndcIXIPaXb$y52y+apl&v%}LJt{~ta{F4h7e$+P#X9;vEG5CkdNZM%DB ztT~F~D+AUaV)2iUqCdktI;oe%%m0%n{MNl#W3YYtot<^XXYBEg=ZT+$>nUmLn*$j! z=J0t40GPN%WX-)j9F*yoQp+$8-B zSo(Z>{n2;5$r}InGCX||S3gUx2e|it0oFt`e>_R%Mf_2L_X;Weuf3Xr%_*9O*+7%$ z7>B~WJwtOL?#tb!S;b-U{5&KxQ_uO$-y#&pLXXI%b-aD6yz z%(xlrM|aiTz*w%H7w4+i3j!P3$d%-5qlm8$$=HX{3Z`<@`u;mQ071ry-=lqfKCHo< z{N?0oyv*O8Yb9QiOs7kX!CVjg)z&xWeBty2N_s-Hu z6#li3$~I)g!N~OG{&M=kc)<13hxdB>A{t*=ULJUtYB`2^=IRvo5a4W|+6B(!BY)qd zKD7LhAP$ZPE9ODOPF%b>eq;=CTGkiq2FveJQ!uf?yzs6#dq)5p2kUPP*R{f>YEO^M zIP&z`L-P0ok>{W$C!un#7Y4+ZvAV1=dnDKsg%Q~C@W-?m-tc~}A#3_<{QaaF-szFa z8j+)*Pj8+?E}8?qXt-;Xb!8z!^Mbm^NWNG(fSBoW;wwYWq}MCxIgze<`-0$M@!lA0 zSG;?7kY%io-SymB>Lc%2JHeZrE%TnE(V)5yRJ~7<`~DKYgxGV0WVwjM%6>VYYlssp zbB6YSn4f<04>^Zm$N%JiLtli~6on)pK3|Vsj1c4F$eOLOSzL(5#^7ZDY<@JmC#o9^ z8O1g)>#~{B4|~ycx(YJN$G)J4LVI(VxkN5%6Y-q8@- zzwYhIXjY8&IJ^8Q!-9%H-|~8m57%f3s@AZJiMcgYO*zVLp8VD1|M|zi<>j@N8lbXD zG?o1(H|@>YpH1&`0^T}md$<}B|337#R8Oeuhd)x&cAE3_*)3e~loJU1_kW~*RUx^Li`H|IR$>$eN+Ab^d48#5rDynG$Fgjm>ycGL zf*NbCx0dHx0065$OV&B;vo@~v<(2c+sLI|x#H0(Z3~I*Gzm33}UB5^~96NvL5g&Ly z-0cTO_&T6L$7-4mwTS|WzD9ue16cDPhwCwJu5UYQ$fmsYaV9UA;jC*Ly5dWmfKDK; zF00hNy~_%&8lZN7%^wd8?xjn7Hr9TAaE{pj4%hc<>%>7L_~*mSBk60&*88WX%Mwk? zh-eOU*2ZkF5b_yl9)=kEDwf*7a?jM>4-JOeMzm)8)54CVX0*y$*zHs)P0VLp^xk9yj*y&cqn5?<3_zgXFl2PW>!JeEhgXgxZG=r88F zAjfWNq6#Txvmq)Yr->ya>t|0Kn2liJ>>Err282RKP@6B{CwL_99CQ`Wz zOXPIE-UJ6TvY^J6W7lLKG6n4zX8S3A_v;y!>zht;aMZ?q&2av%1NrIsb)@z81DtYH zGjx^}oZ8L|_p{srU=Fx2UD~sq7VY&cOeC?F3vb^5WU&D?tT=PfWB2oCZtt*`S>5P4 zpH?;w2(cUw%j`9=%on|Dudo{}rZ$$1m+m<|36ABj7~;^sA>VBuJNxJ2v8je(_YDm$ zdv)@xC+mkj9F1N!OK|hlbbx11>OZ5XRnmgll5(c?#0KL0jEe|(eZ<0JxE!s482k;S z^sT{~XPm+KLi_;JZKypf37U?sTySFPpU{T)Nff8}i!lGKZ!-OIh6GiHLnt22+&9@U zJ{*MA7dw29@37!+ES+`vp-cZ-Pu_k!i0KAX9ImYILrqwpH_@lrV6^?DJiXKoS6Q5k z&$&fDbrHT7q{~69>iB@t1iQYVm#}N;u1K{L5m(opCmAkOG4n-@j4`s-KSSKcLg>hB z4~Z+A!I+P$b(fd(S4TPMQCIJReYLUL3s2Sz)h4K6%UzFvoLVCY#PrRjxdU&$SCc$B z$yjq@2s`j>@WKcIwE24ON9JN}EMfy(9Y5e|&<_l09#J`!m55Xoo4L;od#cJTK>ZiQgVUbH%O7Cj}aGHvajZcQ$(x`AlFInx{&vznl%KPh z#O(UA_rS-sz1WYe3r-IH(Bv<_an-1qeEj+3Z5_QvILAN^deS|1iZR%DYgPz|`otP{ z&xsgda1JK(t~3#4A7t6GS0}l${*L5Z3=n8rimMp%IK7x!f{sm0PTVW*uZ}X1psa7Y zKUa4h!r|9)ginpx!(cDf1is?HGv;%h({l}QBbMV9RWsYghOitrlN*S0ubY`_t`9$) zmCzJ=nrUZD>SaEEh+m_Cd|$br&z#IyxhooBJ?8HIS)R_P-ti~)+Y@M3x7ZWJ`Wq{6 zz+B7bXX3iI2jfHqaqOY;&<4D*#1qTDxM~k9<1&3r=&Q8|wPmS3Dc>0TLLN0_%Gc61 zF8gpBaXMi|r?xsS(dD2?45q`Oh%In)4)W!U?>i3CTnX%l_e0FfzJ2M_N^9Slgw#Xc z4zpL=sVM-&7tH<~?Twas7 zH=7<91M=^j^-qdft zUBBALpt0RWkOu-dC*u~swa!`WPbU&9h37jRoSaWS<)9~~Z~j2M@4hjQnAS=S|XJBg{jB_40rsGiG znJ?XOFDg!S5pvqvHz|VM1GhI498Wev$(2FOdQeMnf`^`U^VhyLY&9>l`7(w4i#>A_b0&EOoee~fr@U;J15f>g!R@reW-Q2B9!VuAWu;5>1jcsG zm|iP-I{fL9DDH<|t6fj@sqw_oFMMT$t=IVN4<5Ut=`x$>?FDfhJh$Yp@Q07@8$MP@ zrZ3ZtX(4y-qiT3-$%+l3+$TD+$-G$C@F~a>&Vy!uCTx89p#c7?`_)vSDMClTUfsf#5__hY* z(6zE7Ab%|G?U%ZF^ASjXrk)em&Jr54b>?r4HBam z$icbmtYX*Vyd0hF0fgsc|I`DM$+SKg*v1KqYvNyF=YLJ!C;8N9x`?a3>SWJ&b1%nh zt4y+IB;O}p9e0y&2qLI~Qml&#pGt^v{gu`&_{qca9if2h}=#)?@>y9?So8uZyvntxdPA7-8@5@7q;jH>2V>H0Ly!J z>B%>YaV-WETpRDI4B)={z?J_Z3Xt&84F7bg<*;g^j&GJ~DK88OAjWtXY--PnSSg)d z5o$h^E-yB8n<=nw&aLUicYIZS*AFH+kIRQZ`>@|Ex6T9)91Ny^`R`64|T?_Q8h84A|wit9{JISYi@#Uvg)nBfi ziCm!HXXbE@68o4<$lrXoKikun0LX*>=lKx&H&UN?D9q6lnC3E|P67P^1Sl%`=?Tdg zn+aVLFym9>o2j-pq=I9;w1g1J*ZWtuYwh{H&&-uLG$El`dNz-w0Pt~lWbp@9^kSoR zr8G2NY)R}7K&`FX1~e}z3_Pqg8+x^T^ar=^Cl#gwm*bH8gpm8`x%q{TT;lfOcuq^N zL;C^}_R@6q#C)t-lB_XZo3|;Uxco0p<4zA14B0&U zm!J*Foq@GAAlb;pH?AEkExC5=w=3Zvrt{*Lxv@F_j?gy)bH9G1ANuKs`$TXJy=cT_u)n!I z%Wk$6O&A#VllQD=s1B((WvnihDkSkF&3e~kYl-K(G`lv=#3xwkkl!e#eXZ%$16F)O zuiDN>vU`BIMKL$k^2K6&?wiwa2B)7ufR4>TKDC|?9uVsReVUfls&+7UG(dtoT@JXi zl;d$*g}d?2wf#5e^Fk*eA{m>zL0@b+J(XQvYpDRC?2SDtY^E;vVe}$Ky4I9hiPNe*6*PZw^xvqz(wSuI2TW1FIUR*_>4T z)=YtOgY~+Dpm1=IT%65dA0yw^G|!FwELCM@t$%ka{~sQCrPl$^ljBo6v3*TkebmDT z3NJWI^b>O_)*G?0J+&SNKe;%DZ3S2u>rHpiYwPxm-wo^cMI5{F>d9aK_1E)g{=V;# z<8aJswI{U`_pZ9*;=wDoqPU*bx_s_sCWZk^nF!gnXiexWCXoDjxzJN=bKpHBBnZB} zfmxp+(|81&x#*s!fn8aXV{d$fEH?lM*z^4kEjhQ4+Og%UX<%!Aiph=uWA-7`*&bcb zqS!Dv61P}b;p14Jvk=AysCyCZ|744n;QGN)xjB2FJ8`ud-de~-9G?3@*>HqtH?%p4 zoDR5*1(-3N-KqR?Vxb0MuD>ggaLx@6!yf=_omos=>11h~h+TU$)TJckkGj@jD! zuN~w=a%$HYKP0R#p4m5Cu`%O<^Bs3>j#CZlYfa_{eB$b_WNSfJ`09S=81rVLS-k!( zNz(hFo`+$;M7`SJ+>Tl8MLuURreWS@Hc`i5&9aEAruz6e{aDJ8U3)~oIBNq?##`@#}JIF&9Y;&iNdG6zW`lbWVn{sTd^(7H8KRF*lfNz%S#FR?dQ^Vq#SpLE~UAB|7aay)sO>wPn70&@t=>Ajcn1+Nyojb#Np z_uyM=Y>K+PS@%8|x>VzMe87VxP2(k z<0O9vT39tO=O_1B-9PjIZo|^?P{ChiJAZ`4Q!ns0zYI@K^yxp$aMOL|H0aI`U+Dc_ zd_T0b$A|Ggrv4BpSE4UX`>q9<@q1me7LOwfuEJf3tKsn)%E`E(pt0ubb+upqdU8Ho zX!cwU?t;6>UXz*#X-;gcz-x)UbBq~95&PDno%jU#JxOH!;2M+J9AhY%rV1(U1ne5E z-;0_ZSk>TZj`pY?DfNH@**YpAG3u>vXAub$6Jz-d7GiE2S-$q*#aCnVU(uJf8RQFS z3fE)utu7m{_?!YDW<5Yyt04b}^Kh@Vz9%l*<|e9^EF4lD?nCG zg_``|eiQLKa6HZOHdaVai?OgL;R3(}f!*V%vS#ZT!8X_*XCZCG0`R|fMPDIY&_9Pz z%q}?Hjf^aJjjc)DX{pEmTLXkA|fUK_r*@2lEZ+4Y{;{?5#xPE?me~Ga$RA78T%Wv45=?vfK8ETiF4Z>GBNr^A^STj3fcMrg`g;yg9g26pa>;II*GpYUXL;WwlMZV~Q8#6BC zyn|%99`^wj^eRHc>9og3`DhKnu8dsAX^xAV3gVX=&h-F4_Vs1DwntY-KCnu*?@&_= zwq9R^ENQ$%OAYqwMi4e&-?0~^kixr~o8x#Avww7+ zf~17!xa}9D(;eUHY`R_-dzW}LOvjUNI#2skBm03*AJ6R{Q)?vN0+>#B_KU@3FL(@) zpA(0eKCb`s-+ic$#s^W@Q!7L{K11vYf>qATP@%2C{P2Fovku>`16#gVz@sa9NkOwOZo(I{S;V$b_W6iDkfeoJd9bAu!*j$|-VF%v7185)T zzTOxc+qJ|G53p&ss8_`>&@Lx4_KvM*IG)GbMXzU)s->^}#usAkF#OoYaW%8`9)W}7 zMC?Am-|?wsCT3%pdJkDg%;wFx`_<80YWr~PzHItn{ljkByKb`GTlKZJ^CwQ-mu|f9 zY`wD8^)?(x#_?ooPnMTGvi|!I{(~VN<5Vk#526F>VRko4FebD&!=_&2TgOERYBnRY zFDJ9P;TRuA^Sk%}P31|23E1-y`WWrQ39bow4t)DMQ~SA1X0TwAR6V%hMjN$V8q6Ko z=uvj{^}L)fNde7Yozs3y$Jab;R^uG4?e$~V_R(bzt@ENH$9mBJRT#|kVSXhy08IZl zvwrd(;+ZZF;T)@(JobvlosYfs282d`gUzUKG|$h?N6zZqV=OVHlXJ<)xxAab2aC!% zLHsfJC1AB(uXlAlVOi7;tB(dP`WJ{LO@x}7z{#7FtHae=oBrdIKf%i{H!QKqne}RF zf)Nr^oH5|tv#VbAjcrCekgD@Y?JNYaAO-vKE^Y&ZN6D4H}qMcl#*L8iNiPfYXv3a@j&Ci+krdzmi@)9QFMVOzoI zA$Lfc9eln^JfspeAsBUbR;!)|Y;5xpmm_%g+A=es_^_Vs@K0j)!@uaJpZz}EbNj_~ z-46v`9uhruvpPMsbMA*`WncE%D>z)lNY9+bQ%t&1abko#wUk4@A?Qwz0bR`S>nStg z#Mr-X5^#C{A}e;hPTXUSJD5sL*Q7O{_Ugh*@O$h-%ZDT6PmM9MZtM`9XNO+4QO4c& z;lTLU!SylzuP&8i?!DCf!bqf@e63we>|(Q7Kd|dd`uAQinv5!Ra?| zMG*2+MXxdF^UF!XaL@GQKV8pW96IipgMET?uEry6I2x;S{jqpsjripKt=)SEQTann zzIbDA81@{04BvSY({+G-z(o}Y}HZy<8Ls&CyOTh!?-q;iNlopPCOr+ zoesqysb83dBt#xXUyjGd{?n)InzM!2YarP^J(RS2T(&{2>}n^~@4r_IAN7dflr?Xf zG`Lo5wy0mO>JBGg{#q9t0VOFih-N-XS#iIn9z@+yWE_brmL!ej=c_(~kCS*j^>_2S zdfmR+s%1EoZzStSm}`h~Ffpjnp}E@N=4yb+qakjdQ$E+{OM2D7QRGKi4Dyp}`9vLd zB8Z)i#JuL~b7HDU;AJ9PX^*=5Y<4~2G&MF>FR_?v1Ceq0!3kSzTmR`3rF3#^y2YV(l_uIzFjcj|Kx%VK*KPzam_A~2iOs`e@ z>>3y0pE2&Qr+Nqa?pV{0&O@l@-We?qHU@ROMAIerdl($CVfq_3V?A9hO?d=IL@wQN z`gJqSFS6H~H8?qR)Jq*4^Odif#!bxp3cVQY&wztXfbC6o`Qud**ZX0dPn;qBPy_BS zrN1=^jhv_bs#^T;<~}YU=*9N__7d^!kxhuRI>$tCW3Z?@=l+{r-C^hFo%Vvw4fMT{ z_~|+0`sn&Sse|TCJV*smZ`cEfuw{Ig7=YGaL2H<;byKc?W^So%T?8g%7XgkecO%E&3_}hcl zb0*0NCGKU^?EYves7A1S4G(Vu=YD!B*%-@>Z+}AokJi`>Breh+LGqTcFBS@F#Bo z03ZNKL_t(r-*{W+TM@+&SBuog|JL7L{pN-bdwC}bnzoWfeZeWUl((8ru9yI_RkBjFnWEVKXFdivzYbo`ZZtf4nQ8Sgu83P-Fp*d^oH2lYJu~m z$M!vntqtVmx;lw#jrDOJ*IQ#T_;At%&djW)i`l{RPX>?rheHs1JZEPU5>4kHCn%i( zG+%dE+bdSAYk%@cAY%cX(VRmppZ-Z$jk`uy1dM(eOaN5UUA?in1#peBe+PT?lMf81 z8lM?_$GCXDMh_oKCgeZ-53zd;Ys#&f5Bd3#dI$-(UTD;F*L1XmoXB3=*814+-%yKj z=&fdK{uRN2k^{(tpf5qI-ukY;hi~eNfeZL51yWm#KniX>pjJ$MG3e5m5ZpYj^0v>u21NGd`N_SzCrZg_8}Z86&8Adjy}KxM5y0?bce%U`~% z>DumVBENOXM8Ijq=g9P|Yk}w;tWESa)=k`wHz2FyzEr-pL^BLtLw?AY4SO+J zxn`~*o>q5MA6$?&pPGjuE+H9z)*@YekG&ycd#_*4Myq3F_IWx={=_Xe2IXWv2`-*A zGftomPIX)f&^VTJy6uBd9Y4%;c(ThEqHkkmO$LvhgVH|2wMRx z?uTMC5xB(ILh^DQ*(Ib$rzRm+2)P6E-Z~r@+`T@n4Niy)$!l1i90~Hp?8pFR_~w<1>k*8Uvhf!>k^2Tw2rcFe@P@w1B^_q|riUxpiZ-_i``k@UF0XK^c!i2?JETAKO z8k9Y24qQ_P9pl7MsqbH(T$RJUFONP4hC2ZKIFt9K8_-oVkd-6?HOsBU_`qIPv z7%vY?YmI|1;&6d$F8YV%^br8;_}Dh0bNljpT_qLwIl)Oo800ob){l*4*?`Pk3p;f6 zgALRHfWsQtKdbdMsO88E16bNj+@5=|GO%-S>R;8X-9!MkWz8Tr$Kx>gNja|O!N<5h zZF@7A=N?NSYtlJ)QNN^o_L#?K>>KL0CPp9?POoUpq zcnmt9Q~Qc}oOQi_mHGBe;n>w4-Zvlk(vQhisQIGN65u8t95foUrf=*a$k_ZYVmw{( zJ-tmpIWS6NI43Z*G5;5^4a+T-hHRAdM&@vJa;`_gUkJ~L#0FN}=Xs{_;YEg!Lm5@f#Bfn!N*cJ>cA>kw}Xti7!r9_sPHlrgOH;iuG7|F!{pvay<-md61!)wYihkQ3bz7p#%tk+?FETnmNRVg2L_^V1Kx74(P5KRJ#+=H=K7a(ows#U&VX-=3+q&S=^}z*Ap9tlvJtcYUxk z(tDEc-9D>_>#;QFw)4~H>GHSYvo$Yf$k_wVx;qWpC#AUFXZ?WmK{jq%W)f!J^%Ee0&k6)(M{)xb(G`S!SG(Wt_{!)&b}EV}SCDnGf3zbzID`_}alZ?dWH; zjvozJPd6=~mm4gbtecYL-;m(l>C|liUS%nmF={8CQ!sY`MFcwu=wlUVk*l>t~Oy%b0-j_}cEL zV7QheL2tm!mhxu2)tdKio3K%^wAnE7(*hg*VcAbJTR0awUnd%UeX}}Yy9r>ucGIL2 z%hC}zu{Jc{jk@k{v{=P(0kcGtzgh~Xae2cE(Q^t43_vlNo@4;=l?(lkpt9>Ea3U^V9cro=^WZJzFsrxF9ByYGv^qN zo!IDjo+AYmLrmj7olBmwb~g2DhR2u+Q>);!H+wLN<-{QNv#*|*uLqY!80+dGo)7N3 ze`g7QBR`l+lsGecGIm?A_XYOKGLN~XZ!J8I!Fr_<_x>ApBVPsVcVuagI=|PUZGawa za5i`2zpWk$d{gC{u-_Tc?+^L}ge^bk+o zXf@U0*=WxmduwSBFS7e@wKb+8^{FQY)N$HgjJdE^yEaY<_4?sz&|%8?Dv|Y`JK7(@ z{ZJ<^Uita@N`5`CW%TUwFS{BR@)!Ik6m6@gz2Uz2+K|0~W4i7BZ96o8Veh}AnEkhl z@G;(d$L(j&^?E>)Vqp zMi@Bkopiard5J21@g%rm?>~BHmxL(E&|VMoau3VinzMNG4##{S;xC{K?#$0Vy#64E zUc~e570&MI6OT6)lMPjv8 z+|x(n;jMpprg1ZfcU_KgC$u_PWT2*qYGd=nJ_(8&uMzqLmj}@quhwe7ip#_GWjyZs zFBXER$v5 zrbP`;iW90@7^(#cUXX0TKQXr-a*eAJaK*cq>KNl7C8lS#&Rv)?D9gCpUoYUb7PQFm z-rxsOyPw{kcw(M$8*4eExwbwnDBKH$<$P(fGWl+n*1~yQb??KjTJ0UWPs6SWsWH88 za#@p~o^*Zk&IUW<<(=$tUgwZ06lB&tP#4{d_?xA2D)z!c^_kKuw4Dl72bPvk;Wk! z!|^TkW!LxcEYXu2Ly>Ym4Tz0D_S>LF{v^g!L-}v*Up2fJVj#%X6xn5wefx%;$3%V- z>~Ea1eSnZZ0}6Rak-y?dT9)u(|6cpCn@dc2`&S@ikPBPw;K;k&Q(!(aD4A?kt;C-2 z=Gcf|Q7_jxgc2@!O>T~- zl#y*d4UH`xS8F=w?b|!JspC~GmYkP}C=O~R#_HnRi}&RUL~*G~fzymxy|S_Pdck8L)yT-En?ckYOWqSd7 zdYOx_W{7hj?hb!r+vn%uh^QuL_8a-yczhDd7x4}_{d1bL$>^B{&S$4@r*UkyFE|Mi zL?%Yg;b6^gU@;q0CxE}CFfEQ?ER}Uwm2F(t?R5Vh~) zyvHVlyuGU61ThXw7G1-+s7acX4n2g(Hc9MuvKH zHh1q;JaAkk1z~i&_vhsWcPU}jkAabio{?fT;%XnU>nQegh;#j#RhK|2#KrU4o0DAd zfL|_Gnv9VOoy7``&E4ga*F=JS?&+%Mx!{1);3LweDF*!)6H-&g{KQ|NxjzZ2?KRLF zKd6j}HzWT#g4rJd>fdi)F3V=*^f5K&_Wp^sC)k?*3Icp}e05RFn5$!~pZO?uj}c*9 zaxC}eMSXK%o8R7LkDsHt;o&vsMiY{+JQWGY%UjI9k8u3lOnfaiTmL48aaj{ZNay4S zz&I_^&4#OK3X!$_g0ET{|AFN`E9MQ)YMe6hnyET&dxE~U@?PTVM5Cn5_RWcdFZ4cM z7VhcBJJv%#+>IH^rw>`rPefJR<0CAl7gOic>lDQ6F5kZeC5O2WjBt9E?K`@A^L?-H zi`q(!74&F)VxiKKBa-!t6OnxBaLa}O7ZN(eINpB%}qT;8+T$t8~w!zi;L|q~Y6l)!| z_99d(mK;wV-nI+&2uhE>u5x?AID;}p1Ptp%> z{I8$*nJ>Bfhbsit3Mt~2^P>27efWUlsiQrF3l31z_|r}BKRScWhdtHvjcXKKtjgDP zRnYT8mArDqObXMqsZA#w$M*xnh`d3pdc`&V6?(>jGpj4f(e`M-j#84d-iKJR74tj&!&8f2*<)h;CQ7^-;B!$0EOYqc8AyDs z1CAHR+JmUqjCfO6y7)O4PrhBR_`rNi5k5m}W%tuda(IwDBLMvi`{+c^6ckJsniu%q z1QRmca((N?YfU9Ns*8}0^#o8B`()zX)yBEUvmQQCi^)6<*5==@JrsW>_;6UpwG^+; z;^*Hy+;45ceq>HNQ}D?_N?#;u+43j-UT$4L!JknLesC>;W^)2LE_36i z5Bytag&xg=YOLS$PD}Ds8rRe)H63jyFsw{+TpEzc;=K-R?bfu%D`a?S$^owC%WZJ1 zKRSvn9T9IXg~p+VlcmPXMNIXHOyfWXS|$0=A+zV*2fPLzcx?SDk|_V?v2Nawd+(rII*U`*6AtrX9TxY5+%V9O^&LSmn^?+5|ZT za2N#;U+bB>n-(x|Fp4}KF;0gXV1bw80`!}U>v$v(eaFbpw%;5buWC#4D8p{jT}k6#U(KK(>0IlhB?WA~@`$p9pJk|nNGnl!V2h#I6`!s6g> z`FQQcv~Si+&1V|}_^-BC2mM)+Tg346EJ0WY**yAXV$TP>==B#WOmIR<7S}}s;;?aF zWBhFt*lZ9oEO<>H!R#--1^L%m+W4{7X{Y?8#0gt3Vk zUFIn-?pR)|5G?R6Z&q;SPwmir-{`}_xJ2C$Bnwf~GD*I$gH8GP;WWe?>*eqVJ7#oU zx-8z_;Km`kWTy!XUyhqt`=MK{<(Y_KrCJVvWwtKe$RB zLoPSh#Orby##~!>sk97!+uAA z3R{0B^8KmT+q-YJ)z7?IUjMv*aI+oAUUf(A-7oY5D~Bw*zQ*+j7v{{Xu5aGzXb)z$ z?rXZ}B=NC%HYW2vd-`^Ho87FOfS}VCiinH<>6vQp4f<*b>KDSY_FAcW+9 z;Vu*xc>L7TCB4emeYkU>AT!trb|rUK+-JmY^tWwfZ~i#ew@2FruvaF1%x`3Z`|D^A zXMZtEx|wp|D~GuV;&FiN*yo$}_eBH1fD$i90j(3)XW)Ov^d7f4zkb)?tE;hZ&GPWL z3~u_=Xo}dD2>$J2g!ZWgZhW7-7akAAc=A6-eG#^anI`)6<>M#=0{X_~k(R~o$X&TboW@U0c1oetog+S(m-J}&e6cdD@qFz8 zX@7&>?9~%lj@olRGOtCl@@4V}`SKI==Ceg z1a~p*=TP`re|ul-5IBkDK48wUur7D*r3=u-X+AvDHwTx<%S3P-{XVnxuRgUriGxY3 zwCFb1I;+Ug^$Y6uvvy-NRtw9GKlz#<-k<*ME)W0xz@7N=T7)&SCnLF0!gzAKdrw}Q z;8vd3A|b~AZPf0C^2#4_=6&c3d;2oIy$2xe!$$`Bf`dZdZ>kEd#ytz_L7<{gCsyafS{zViL3W3$1@@no--h;v(9xtcJ(dd*)(E z5A|&~*%LO$=k(z=2=_IK$!m24?sGc#TdV%*$@RNFSi!*d1YfTwA?m@vk@3~rdLkgk zNt~_+`0))SMth^+v4$f-&aNJ753`?w7^=Ux2gBlLTyo8QZ}hEWJqRj9Xs{F+u3Xp6 z&pi(xf@03x`hj%KJwlwnsm&sXwf*QKTXV?;+0izHYh-9<5()1*`&|z_%in7l!@*hb zrC!YN=;Co2%#Q>QxI^!+K$d}<(2)!@pD}*zCJW`g|7p$|qq9wkQ zae=fD7IB2i{iPg!O>qxq9QVQ)-SWjZJzllDSit>YDSK<+MM1`ww{I#N|NUU2dM^WcH;HIiBmSe0kLu5Sn|+yu^flM!9MrZu`NpwO%kNZLz%$ zS;x;es~?O!#{?07a?M_hVRF6Kl`Y)m%;L1%eS?9aJ5hOZu)f2ajc~Mx^L#yk>+HU9 ztIu@UyCCW_U|j9R*9o3gPN$b^)%zwf`BT1CCQ?nyBfjF*H}+n0avr}$K^R&-z#`A( zdbk1w*4WA-+7Tk(;9ssIp4aV9Y&=y>Z=3f4XJ23JXgE%!a&(_n#b?s0xCi^X?H7FD zb<**!eGg-L+Fg+=Zw%v|Wt!Yj)B89ENjM6smIdLclp9hZ6Prk%vmzc57rZ;X8=BB5FP*1GdE=o{yw_h6M7q&dH5P_}N ziS;;6b@o2zf#G{`@QzzpXIPjqd8p}ySs&X{n`;$1m*mm#00|)`ex~(BjoM?p!S(hx zI@lZ3`@~N}jvtbJsBLw}O~_Syxo})QxmuUTJ7GT8ERP#~>n9DNC)D5%a;9ZAqtGhq z=H@rSaqYTbD>8wBEpEoW{*`{lY^H~q?{g1_`KwXpbAV$>jjADz2LZMmc*m(xTX~+( z5tfAjLmQ9T)u$J`AK)@)PGEZF{{|mt?}tyX4PN~DA-h4kuV&X*Z|e+}y*Tcb9(VWs z<6kjwl=66kp)FwLZ@4u35WIPtyVo&ba7@N^Z%~C~+5Y1pr&xkwH$SI~uNk7&?jB@< z(B3?sxcJJ^#Z?k494{$XfO~@a_8eVRi#Yh@^1g>M2ML~gwp%%N4++6;268f%pKv^w z@HOAve(v@-{^kJA08M@P_IaYcxHQ5UpPNU^u6RT2Do74uey~@p{>!mC*@E}`o1|s_#a_@&UtdjROI#eQ`#VqT z;>hPgaGb^t1c^0(=E!qI9-Qhmj+0?T6aM6sOTK!D!B6?`fD>DMkN*YWW%s>ai$^nL zMf0ewLa-dqt9b#`d+i9~1GeTcKFfEMRBZG#1}_1O)`R`2dnB2M zpXrS=Vf%DkV0N9H&DS@|qL1X^!cS4B!Ff6!PO2yRqc6MXdLAyOXyx4T&DRe&`r@za zp~}h59F-Doe|{?nPIFDP^Ch~e>aqbVnc0oEJosaJtQYqt0MMhguI9`ML<84>uw9St zU?A{Ih>g*>uUu?iV`Bm#Ea6T9U#9gJc7}apF@rYtF??(=92Mxl8ObJ~S_XjMF}fel zZq)wheD4Rn-=sAhHJir2U1HUv{p@qeY6jnX>H7bmp`4GNse?7f?i6$U3OGq5lfyAp9 zr`G{~0Caoco6Q~APh8pfw;sKYhf$-zGLig!1M!hTIJ)k5`&eGi{NO-4eC%`VI$~-M z-m4|(IR$M&n?Lv~3%|9Z{o?vz5x?=-oDbb?onFdwmuDQzSayvze~Pu21k*e28t#WD zz8B&X4>K*x0MqY7Md-sA9JTb#kA{Np`Q*9ZPA`SNdA9CM&K+RB zb-G3oJoLxZ^sPO!7JM9^q1`<`|CpZ_pdfwhkMa2-hjz8Wz;A${FV;sJy^m*mbB`Qdy&MSsOI>loZ!Bp)*Q_dN z^;TOOF;Xis2VBTfn`DY$PhO&G0CXIgrFZ}4IhMONXCL=?;5BEN<%zF-cKNdYd7H;FL_C<^U3${gUt6m zfBVUG`Nkd7J3;{uY~+y>(~WZ0|6>bu`F4*%C4;?&t&bCIjP<&^oGV|R<_1*U9-Gwz z^V@58IZ=-_;l-F?E`3Ct(Fx*Wh?#a{vN>0H`WlP(_tZ81zK(AEV&yTi{T&N_kN8&e zco1=_O15(Y-tWPpibUHJuXZwB65aTiFu$m+&<(Vi8id4M>r2o+ z694cTl+$vDAwCZCFmIpbvldW8|1HZ;pT7J+HsE4h?7QuXA5t(-vX%SrQD2|K=Ys~+ zIsg*M-S0YnZLICnqa91e#TXe%=KYYtHTm;JFA3%G|BNOSW^;4|wRTE6bgMGKBk)f# zhecB_W(>jbHK8mSOuUG1E;50s9Y32|8}Q_e$2Cqz76Kc8{RmqhC_s8q9z_kquF%-g zm?trDyogs_nw9nJ1cCRB-mR+)&wLSrQODPNxdd{`IQMFKLwb|+=BKt^xYpJEmw-#q zBMY!n*T^?7`K=E>*!IO3ur*s7@qnyTo}6xOk3J8S^T_|8j-MEaqlR+_b?}BLzcrI# zUh@UPpBTo~S}8}ta&&!6u}`CR%pV)uWu_tq*H&x{td~!jWJtXEW55ps^ZtVlaK=d1 zJ~ubmW$E&*gJfkR%rro1*2IZDW13lepCJo-X1!|QL&*;F$OX!U4s|x@Vwa=1#YqHN zj;D_>Tx=2hT#;&Izi)j~U_XP_0t0h!>V>q zBksnmU#IP+1hqEy0r%@@hk=v<=Z7~N!X2!hwJ>zU!-uFmSFeuXmKt9AU5 zQ!F^}#M7kPk5@xZyYA16t=M?ZBU7=9UBu1DIZc!B<_b>97Is|KKWzEypXTX_FcY;t zdYv~J)Wi2QsW}G&?kGCdYw*u$_a?mKe*5$73+o-!_tePaR9)!dAU#)u7xH&i@OwWR zX1Fr6vjSHiT=G$ulA5d6Vzxe7RDGCf?R^hd<5rjXIKgkNW{e?squN45jJ`Z__&^Oj zV^rsR@>XEn?IXIM9Mp%{u0L85%WLPBP))AoYge1PyeF|_YN>K_?nQV5 z)M4tJJ`%G2-Pwv`Q~~&Ka2-IRVrMTD{2Vd&8pfUV@K6!wh~4fI5(7*O*3%&t98P#Y zP%cQx3vGc2V3A*wxp{nlT)p3k*DVHER}1VcdL zWCLMrckMeC;-25W%pUF1#L2Z-Pnrw7LHe5R>SdFI+xTCJBX*0xM%q#=oBSR$=A#L(J^=0hsvZgoWIRS8Gl_hkW!_NpqU!$%QQ&0Yhz%gLTRG zT1*r{u{VM>z!I$oSQu&*u={9H;P2{X$&87yWRy+9M7iz{RbZ)H0;<8 z3y(d#EUo#ggWqiIYd~=P2OD6HMOkc!!1maaoOPKVSQp^pG#8i&J@B7#1bI&q$0lPj zo#{cYErfD(=3a-|zBkJA!y!?*CPBc1f~=yZ)%iH>C}bmigOA~Rx5L!0@AEIfS6>O1 zwKcbvHS;gn2)73oP_3{u_T}peseRnQx0z4v)c!A*DihP%L#3+IZJkX9G@XBr$XrC=eSEDt?FrQW=RP?5_Q@q~S|I+6ntNSY6T2U*w$46ZEc*}b^TuKYJmctoRv{_R%>p^0?E*TNZ?%b21h|(O`}ajsXfH2$ zPP5$9@*NsusP*NH{l;vh+xj7a9MPG5!Uc0qd$G^N6D$%O{-!g|-|&GYKC>Pc92UPG zAK64r-b1B9mzlzM9}n24yT?AE@cSajR}!y2cv>g^9$8%0ahE8vGDfU&hvb) zBX!6R?(QcwUEOM+Ump$2_2)OY@e}W;Fne0VU(b)0_4a|Z@5>8Sd6$JJx;(F_Eiba{ z?*-GR%Z;ULYN)?4^mO}iQyJImt}gx@e_rcG>2g_`>p!H5g&z;Xc@c;|hk9ejrd^8W zsQV1ye@FS~0?!9fq3j#fEWc6gT5IDCylef*5iR(=Uo*2_K0nmFewx#;Hx@0_WuGx0 z7fKb1KjX=tb$Rl9Y>#w{eR>TKy)q75dnkPq;_h&98`eDLw6*U3M)zTzidCNZ?rv4oGgKyxED}>1~~dXsD_;5^tt|#@@nw6ric^V`=U7mKCRY~ zKA?>uP1gN4sb<4^EE7|g=f=bzlJ>QadwURk6os!jKVjxLeuX*wTVq7dU~%_g`#(=c{4o4z`we{?ba#lv6lUBmFQ&rt{rwbA(+0W?2nuWuI4 zxEROZZF_78`3ev(=iv#VZa-*ZJsua-xva(M&YPu}jWdE`Fyz#_F5Btpy-3#BkCzXB z&YdWNCD47*5N4pAxLP%_Wx**VBlCeK52`KMv`0sd2JDo#}vdz|BctOPVQ z0At1l_c|{u``-8MEBfU-5nw#X3D(@LuRVg^Zm6Z%3Y?$kPOs%@A%Yn4&~rXFQkPm{ z*Ig!->2L)F^g-kWLs)V}X& zms74B|7`J0Ek7hr^bGgl}pZ70cyG{*+Y4C9!vXh8!*o` z%Z7Ng)>Z~;(DsOIu-7r%=3LC57;}pVC%ogQ)biUeeeCb?etRo|TMw~!PhDPg!v57; zK^S2}d-F51>K4Yo8zN5G`7ciKF_rC&g?0Jo)Ap&)+Q+J{#GeH9A*#aUg1wxD0p(aN z@V!G1-bqv>So?Fl;2TG-mv?!w?3#*Qfa04FJ_qqxyYuyU`Mw$gd9Yg&j;?*EjE>^s zkWWC3_;M^|jb@Z?TK4!Z&!tS@c$w1)xn2x;aMatVoxwJq+;7JCPmHx^U44CDFJWv4 zIqpr^$CR86>=aslu+Yy-h9OmTpb9JDdQaPw`+UUa;YUfm|1{tnyLrg!%>nbR_Xd7# zgH-oXK{*vX*?Z{`WCFbkHt10be_w>cTWE_~ans?Go|r?)ol|9)@6jJx6(o;MHROPq ztikxhfj>>=RV3QFV6xof+lL^DL1#r)o= z<>_?~BU{hD$jQ3jP|hE^)v|hqXJ$;3Oif=xYQJJOgH>nh%whzv$g}Y0H%lO;{(KQf zeDl>N2X>ea*Cl>g8|xZRhg^*+-|bh(|lz2W&sUT_DpM<02KQ{V^7}?Zq)Y&l{2vhfW;T zxtt_;dI<)9VAXQ@#I(Nb5=Z=RBwC0AO#;sJCUYJSU^)0ta>|qYA3tzMSNN)P_Mm(` znO5qAg=5c8;4XGNAMIbR=Iynz^oz6?#D-H|o}2&q|H;P5U(vU}&6#C&JXMJj_kJjH zkWYnJ&|tVVbp7VX6V#i)y-f@cICbk^9p8H|a2sBp^2Tp2x|BZADtNjh8wow+KhHFo zPsNqO5}S709s?|Da+U87=^u$=JFgf{gxCXfVgQHjdV41Gd#{!Oy<&ow)i~~z1YAt# z8XBd`d}ages#ZptZFwO@latTTfR{^`g7_egfRs zb@Z`QXI7av*2C52FU*{FYYA`)7qhuEcOQq+FlJ+_k>^aZ!L|E|_{OFl*VWT}^#2%Y z25#?je%8c)WAW6Bo~u{%?@3%vi=WqP&8EV#C=aG<2Trybn_0KM#`hYUKRkGDg6B;Rd}4UmBNLfi=rCT+M|u%%DQjR`^j9Xi|ZSwtgj$- zrj1xMU5^W=m+uvcK# z-D+c<9M5%OpD~TDIT3!poyPV%E>$4Y37h~673%e6-%N3xFR)(6MA&068CS&Zl)g9V z#tI=bjK5=kQnZ(F7c24tI#nnCNyMK#)Ae)BrQx)rxyfmH-vn7}JK{!2(4arg(`eqO ze{l3emsZ+);C-Bw{HcY`Cq=Vh?Y%edrR4g+0fP04FZg-m|{)adSd=^HR|<<;Ku zsUs)$eN1O#Tr-<5@x4auIEr^+WZCMgzrH)4Nj(;Ok6qo-x%)IUZw8zVu=8 zK~nSaqW!|`>nSh?DuFoybM2l}bDj79cY^-96;b@P{l=A-p7WoVgm07$uz$C_UP5E? zoA$g>%6hWSkSf8Yyes~PT&%nx*fl4P>&b6734N2}%4mj~+dtO3@3=$QH_fg;oPA-s zJIFb8cyBh*b3Ns10>3d|M*aMT`TaOR@3Ehb<_een89vef z{x{e8{mC!srsY(c&wd8ab2rz3#H+K3dE-PKcO+@!b1tDi6`#1tfLocwu7Has5g-3W zWb0&)M!p0$%M*6F+LJ1C9RT?VGU9S#RP$w?55D&K%U_-NF8N`C)tH`yuW>az>&Jd? z2Gp6T%$D)!;yEEBcn@$K7@0TlXE;6P=0-!n^78!h&wa>ypCVB2g-h8zwe2<5_2{0j zrV&=J)gy^~mDY?X`W?NuetXggdKGL4ERS`sj1xXhg`9_buW`K(dq0N#sb?|Q)nC0U zdULpG9rZI@JCppsemGt$-d_6``5RZnX-;fgT{Yfc1ZKNlPXhfEg@Hfr+pkwogTaZ- z{BrJo;>{J$Cm)Qva(3Aoc2B~!+E2t2jsD?`wMXkJab?UZq40h_K)O0WRBt}K#C9Q; zx=n0%Tb}owUP$keCuJb`=2vGn+jH%-C4d-J$2QI5`A^(KU7zF)XZw3`+5b`QLjy-cL&V}@ zYsdEHg7ei;$9M@fJceTMQ#YNVr#&v2{dh!N5^#+7gPA>twT{+=-u6>4NpI+%px1b; z=xe{3djRqRY#U`I2WT%xSr_hqwYsLJULbqk=eHVZ!U}(;2ypQsH>;K zDuHzfOH;i)7wTg^7(Na1dE}V;T1h1WNh1}tWb+>XkFE1iv?Irnth~x}_w4`w<<9JM z8JXHRCjc_9s`+#?Nti$YWZGvG_3$cUj@xs8HZcN&FAcwE^ZsK5M5DnKGscB#JOroq zYnc6FI;Y6f^pE{hhWyMYHuGtxyf!wri~9rRlM6Ps7B}6uZ$7;5HSHYJz>)}`ZD(xE zF0iX(dSg*OK3wYyi>b=S>!PDL+%)A^iK5rm4a0)4$0l3DC=ZCm0WAO2VXsf^fWZdc z@lDU=SCg?{_6YL!*C6)>k?Uj|-S1EVs&tK|3!W!MF7! zKpcY+R8F&Zj1K9_z{HG$bn5C$m|`r4Cy!``R~+HUcoCP|@LoTH?i^g%)C55M$LG*T z3jSb9Ja$gwBWHhNhni~#n~4SN4{hfFEKjW~n;naFASXPDei%a~*My`rh z1}5sx3w7ty-AO>bh9jbubghT9>d&}0k_zQu%!zM(2dKW-wHD2>C$u0pTm6RHaI8j< z*=#LKP7WUNmp_KBYdV8-xMaK>!Il@sd}T|104?IwP{Y=e8#yGmr! z^iQ8CMc;Wo2I+t?`Hxt8sS$@y75p!@_8Cu{!$VPH6KgLDyo|=joVA&k1TY$0(CMCf zA+TAk^g!HsD;9Y8ua9Mj;Es_{YmSNQv**y|Zy5ZvW`nU(@yo3Kl`bb@N8;vbKh70p zTfEB)1-Vd{-t3x7u6(W0tn>Ag8S%*@Sc$oIVDq&UHb!xYL+tWlYYoT8Q5*nOK&rn- z-D67gv0sJf!F`EUNJ-H3T$4_k>%e{Gl*R@*FeK2t`;(+t40m`?6C5g5hu(8coDn-q zv2`Dr@xjN=moN{U8AqvaZ5Yv-Bc$kUJ=wR06Ax(hYZENvk;j#ZCm+$|C$h5sB&8AY z5ii;t*ZYl{&aI)AtOI?9Jpy7r8$)Zso*HUkgh&!&INf!DcR{>vsz)-uQ7uM~ThMtp zrn*xb^6z920YLo~M)&%4qCv2JddvoUH}yZ5&_re&Y_zIO zZkK1Z%^P+xdT>~6W!_s2$DQMyw|&?pk~K7`WhBuPzo;1G!}(~!P@73gDsmYeYxSH% zmEomFZO88Hfb#7Mb!r46>OpPtk6Cze9y^rx!6X)utBRjU_|u3{=un-XM9X-y#^glM zC!_4gTi=edjeo7tHa^!fUQ!b1)Eg~W!~rf})P;vC&V}_xewzr{OgU6^-ioz-?4GUM zHugLO6DLNSxAq3xYKFV9TKC1A%GGZGoC`7te;?4+cx1qO&y~*R<9-JCQ?(u!W0M+` zQ;j-zoj?B$U$v9N33y$sZMkEFg>@Lyu5D^v8^rtRe1y*&w(0|f8p0cx;QpIvhRBQ^Jl*S@B1;Ap)Ehdwquv|AAj(!|M@@-O~Vk1>x9+GVrT zggbw~PoE6dxBHzF_QW;Rd zf+%@xh&%JS7+c%Le>u0u)%sH7tuu~%%z=G#n{W~Qy%|%C!HY@R0@d>?kbTOuI;A&p zEFK52Wczw^3RV`_86-m=qJ|trR<3?G*ifkI&fbs$NDhkqPaz_sk!ZfWaojU+#AQ11 zA9h<%L^L@b>%knk`s8O|DA*Znxnco@@=j9iXTRSOM5bK3!HCS8vG_e3IPyb|->aHH zl|ee~)NuGOzwX)2mwsTz$r$%LQMJ9H%!Sx7zH>LRlwVTi3lrLqa$kR?r(3_KR@>#T zyPJ`NMvcU34u{Ve*SGeF_9rP`U);?F_XTeNklk*3A=ew?JlKwwd6Y}|a3XnR56SpN zW>Uj*K-b?I?Xj@|cMk{f`2uUeUCS9>v#c-bF`w0_m%~k@4B^BL;r%Hr#`2sXV6keU z-oW*tj+Nc$#FHO;IA0H(ZTK{HhJ&q-zq(a_Lc!sCe)8o7r2&5QFvhMPu(}M3!QAJLH{vtj zg4MlLt`B3@%jVm_<>I==fQDUy8uzTriQz-(n3-RVVj6!shw`DxR>V5yJi0<0U( zj&sF)QD2;!tPaM&xMaJ}ww$I`)Jr#l15+;BuXT0ifPIws(3)3bWQ=H{1?O^djS@9Z zz1L!mWQ_6PnN)22#)MeqRm9q1PTSh;I`7gihY|tctv{^-9HGX47>_AgViLRI z051F$qy~FWh)IpQe&wwtj+Iff?H8KyZ9wRk4rkzP#L#ztsyMiO(TX{1JopDgZALXC z&D+Kf0rf{GUg(%>Q8KB%Tawc__c`H_gKJ&IWH8m**>!O`3^sv%R8)NWHgP#I{W1SMLxA{Y+|gODCEHh zcjTMzo)agn!NhxQhyw!S%KY^sEhCVhfc55rBY1Gwm{?#24*uP@kD{0YdE9v{;UoX2U4&`))5JgV zxmU%u*P@Nj-02ZcJ$LIW-$tZHjZq$h&Mt}`*WGuXBjn!?A9VP8O=^hAix)&u+So78 zN*eQO(K{dP^VNDz{9Id`&)RFzdcc?3QnQfT+IO`?Is7i(V9Z?U7ZFt9+q^T7LTj?y z#&~e3nKkLlVB$Y(2h4agF=^=8uro+{?PsmI*qQ@&E7x>3#b?m@Yt1ZIWYoCNH9!fc zC9xYn9dYGB{}BW%=LN3$*srzjn2Iz|eG!BmnDEY?Qwu=`qxh`-9xLe7W0Q7c-1)O+ z@%MU_J#$k(G;phLw+0_UYr;VL@nh`VMY{3}0{;TCrGD(SAAjo7^L*z<5J3|Mn{0NE zju>o}(MtzBqhwQk=X+!8;-+%QEM)hlMB)BXlJMow=aut)>j!|oh?|h;_b>?^MW~U$SV9}7D@yNmKxu;fQZ$Q0f zV@x3o=mTB8+Z$*eKte`FZ0U2zBGPv_Zqf@#oK!&vZ6}r!Tu1At zpJ+vdw-d@R&65Vy!b7&in+H7f)bwI72AmHQj30QFaG~R7+!sSyfn}w|SYH;JEox(8 zTgY~nBeH4un6olA96UULU@$20^KbA3o(MZf=Bw2#$_Fln)Vp;7i<4V_1rSd7E>5)% zTz)bPe^xym;xjMcci1sQibR{;F~UBLSa_qp^xEr50@v1(J>Nw*-^~d2UeK`GgIr*} zrss`S@HanW5p!w_M{_7&t17ShJH`f*<^#VOgT*i#@V1%3m?6_7dn|fmL{BS^3owL^ zl+JM#lDX*drN?!}I<^~|wSoJS3DlwoJi0rlV1~<_`5Qle_*$=^vZnJzjoDiVwvY&% zGO%Tlan^$mRoX~I&81sMIPA?Yw1`)1lS!X8+sQjdBfawGMNVL_2TZe!i7|OEI>(Jo zW3OWP)@d04hOFbo*gZL;lXLKtD;c+_sUhcs=7>k<4^&!tB1^1%_%dXb+U#a^&~y&7 z$M3J+Y3Yd^eRGK}9cRmzyS2Z*9S5v2aU6aF8alPfH_UvrZU6s z%VP|(qt`jdV+3?#UXRRKj*mC*GfG|F{58K$o!mPo)C*5?oO2ZJ^K>NtIoA79&4^>) z^W=?1%+{+Dz$n<8e_VNpdz*WoJhEzQQ2Ex6hx<(f2{=WG9970%s|M2?@V{2&=KK;p z8ZqH5^vifGlx^K>iKadLxco9g%HQr{O)ji_GGL5R?~gKoV}u+#$gG1#)Sx;r+pwP= z>xsr35jQGg0Pl5Ta@QonGylfeG5A=}!WD4*h%s2IB9KZ3sBqe!WFg`nUud+0e@KC7 z0CvF6zSqQKtdHv^_SE2e46)H%I*sZ%2*gqJCC}6;Cdz@$B%OT8BbclZNw`lhvf7Y! z@#|l?m5j^2VCNdKyzwYn=75Cp&wL;oc(GLl^u_|aCd54X#0i_j1Dv$ZW?wGPZNYFZ zj=1>7mRR5NYoh!XQsq)|O@g)S7OvRe*s{mO_Pl2Ulbm{NRL{BE+%v!5(%+x-*>kv7 zB-!Nal)ic6nD#!GGtM2~Bj;FejT3CU zfoo0MZ05JO@@rhYysyvJA4E9N;6cT~+-oa#qt*s=xDX#jrbGbih@2s^G{5PRJ@{m& zi@}P?c|=|GyyWeDOd=9w@<88EJ#Nqlfn2j}^G_LLn(^9YFh_sS9djbX7fxa2IcBYO z78!2Q2MZVNJ;<9E^9e8OIPtN^aBGH-Q6g@=h!=evvE4b;3g2n%f>i`aphb?PeYR<_ z0cTA$LM4yt^@zb&?y&*M$N3suV1}7J1JKj7Jd0o6*wZ@a<+!}8$LNUFYwy_V1U2Gk zRu0D~PCCTEa0KaDCq&a4F$4JJi}8`AqbGuNh_QK$d+>JM))7+?V$boqxpt2#Sxqke zpO@<~UQxIH@>bP4Ss#qT4lH?rgJgWzpKm&AgcTNAM|Wo?LizPYa50b>H+$S^i(drB ze9aovVm1>Bt#urK5d@_c5yq~jvn7X!F-Omj$oMPm*=rveH9o`)SKV{nYlDu?B++=y z8P#%=00Jc3kI}>JbjqnP>E~-8wS8`&7Pb1#qllID`VRtjHRPPE99f!cn>>l5N#o1! zq4&K77_w$LI1(2$wQmKsJUvy$Om$O(~#IC4N&Zu4L&sF?R7f|AszY$ z#IZ@}SPYzz<)O%jvB8JDC)$jG)Tl)55>y`Qx{*itn0zk$c$y2C@)?}rX6S%xHw)2h z$jMkDs^=#EKA7Eyh1LpulPD3o^1zNh4|Eg9D>L@x(wmCr$O|61sHGRX^}_)yb=ejG z@L5>--R_T{v>|`m>kaQ-q>zIG#$Uz{P80=d2#RU%X zGqPP`?P&X6(9uS~yyHR)1zLBrbAa5FU~Kq1b&NU3u@8Q04Wqpx9YE-^8wqBlt zcAS0YrfVM=vDtOMxa_HA?V@AMv4m;*_M0vy0pGAJ7MJW7@xQrwf@X?diBv3 zNxry{Ck~wOgI_Pk{FGSEy&2=zx@jWJ`#jk-Juo=NL7Q0e#4j?0*_&7P;BPIPUe~eI zVhlC-6fK_I4CNflpJ?#L7hUZ5a1QMCjT=Ggir+-yfz`;_fF^eIbDSZ)yml0CuVIbB zi>bZ2@>+&jNe^C!=1q*~_F5!1oRD*4ldo9I5uS~mZN#37ln@_{rt)XA^+H^X`Z+S^ z^f)%*o-y#?3b6a4c7ES~7@WkDN_b{Hmy3eP0hxyqYKOR9@}bJqyuo6culkJL*iYE& zi=_#|aXNi$F85~L7~EIA`I>nSHbB9oJ`3GE!6;_G@c=UYmB=A}y}#%rQ@2EQ0; zu~~A_l3)>tXC$RP=CH-2T;QoOu?ORNpz27NY_Q>rhWXB&ed3B?0B!Il7IH9hg{#l3 z8F4WgOkYMVzsR32r>1H-)&b2N%sOX8TV4Z|IMl7W=hkzac}XoQ0P~5x(Ag6Aga5T3 z4{g*cbz&dOuCH2drYBAf8ZN>UzUFcSMNMRzlk8)^_W-S{|Acw1tJk^otoA*$rTsW}9*UDOp>Msf!ppOOyitlb!P`iOAssmiHJLGhbhGT4Mci zCj9=6>jXAnlykdN839yf(+U-@%z@WruIFQ+gXi7AiVT;v9j+&@D5xQE&nLFbyEbA3 zIpn{^wl?G@a@Qro;Yu9(?$dRi#KwkyTSM$R?sK?!G@H~g$L6zfvfuhYZFG<;wJDy48Ob3V!;wVP1J`}FCtuDZ+v<=}??)VZ zUXbZpxpyhVRUGPne4_>u7xdYAjiml0GGojcQ5&rIGXu=xSvzR^?}5E>sDH8P?EG{6 z=4C2M0#`)0vUF9++#+G4QT0fca#uBeI*FYeM|=_c?6s zvFmjR$NE^4sSA4WpXYtRky9J?Y{uL%vTA1CVjBbN+c5SIK`_#+lvuT`Y_@ZpGxAu- zUY_jp>0Gl$yrMaV6O|_z`W}~aE*RqLCq_16sw?K0Mcz3~C(%ZCxMEc&wlx`hjN{81 z+GChHz=b`3&Smo;e|+TWPUPjohCY%?!9Q zXw70b&%S(91{r$m#y&WVt*xlVWo_CXHuZ@V5aaJKGEuw5AZn?*0eawq_KmbO0 zukD^V!Tu0_tzYJs7ctn+*}49~&q2YLl?^5g7Q6e_k8t>6Ya9ONf*@n;)sy2WSXcDn z^ZnabED!#>%q@IBrGBH32e!0RAF#}qpsLWeYplg=k+x=7%E>wwllA*-&-x``PXfu) z7gw-}J?rq7U;de6z(Y%hlH1xa!0uTKXy$Z}h7JbPyW=Xd zg{w727rXTPvWm6&F8tnEKe=SQ7d???uEAp5`lX-T;c!dVxX*e%`QuMJH6oviTw`>d zgOPpv_Gjcde#3`z%TGW56AP+j+!PTKVpx~)Gf(Xhgdl{6xcCwtT`d?u3#L!13WfyVzE&T}Rw_#VGb3PZF*r_!nc=@bXW5 z^NH(nX%6`IffrTJfq!NFrGBZ|IW|yEaeM&a#x>aRU+!AS>(*s!PK@OpM@(&}JG>Bb zgW>9o*a>no_3*z&kUfE5b9_!q7 zh}0KrNYmnH{pyw>u`lVy>ap87?yNg zpif=EVn~x8yR~W_`bV}oaPDJ%KYsk^2UpgYeuGCYoj0_7K};m61gQ_~B3cL5G5$mr zC(b=ilXv9si2E5b!_Y z01tuwREGhW-K)?>uUP|F;ZQqYlAkq74uz}l+O+QYz>S;y&mVKI5aYd%A-8wDbPu+i z0ZNlv_N>o7SU!ZYA^&U?GVfbM=Q&bz=Cvt-C@8jcM-M{k&o>+3^@;WOp?~s(F+MFz zfhHmWa1@eoG&zlgb=torNKj1&hd#1F^5OHDJ9AVmar-NmU?)}-@O$se{H`UjjoBJd z3sw^$;BbyJ1R1kqE|9DD_=isUP!;DVc7;tR|1fLA4jk4#$4WdRqGtS&bohPX83df3 zRpX+3}W>65;j z12(U57?UHKu1#ZtM&87S_X^XEMA=h}t>>5VIrj)y2cUl+?)E=_q;B~s&(v{$O7R(l zn425eukp(PdE+ongX8S#!pt>9f*LJCrL_|aTecrh7+Y323-u&~I+{2)XA?p(E z{PbtzPfl@cmDodk*j&0<~ zCRjSfTM=@uY2>gqp3v4GmQyWpE`N})oR;p6j3-NUK}fEP|Caep{g(IUyw?ckskMmb zsS%u;8#0fNATO_S#2z~o)zDoU=E`bVFG^#`H}ncuA&!J;JT z=$(%pW{+tEv|L(cp4%x z!;YG_Qp{0Z{Rw8hd#(kNWWXW^+WHXFbs8PlB#)2i|MhF$sYOOztpgt5WHY-26KLWV znJsfqBO3nH07)%`aIk!;)y0@E@gjy}f7^q5=iDR2;KS#iehx4fQBKzVb)+&ZD>x8< z<^yyD9U^kepTnuST2ru39-LqcPsFP76DE0?#X`p27i}N2__&e{E%t`(=HNMRk6HqG zVj@4mj3x*%;Jdz~8S3@f*xICa&$YoEea=f>WC0A;&KWh|HMHv~qC~>h6Bk3=Y#gv# zCwys%xP&VBkK1qG^6;2-hVgPFr*}SJehWWNE}~`Mo`KE_M&EAZ3Ig zjX1RB9s&%+wQi%sp~nWf^Rlle7=G3A(`WkoG85ndPjBmqeKEGCgsGh&wS92OnB1`0 zSnQqa%u$ai)?el;Zob&I1{wH|+ULZQ&& zC-UgUoGkwOix(LkXOxrlXLE~@u*D|^+Py%OVb@4l$RW7()Xsd6#IzL4^RCskp1N_d zvThkWcfrf?%AaEJfBW?U$8qvY=RY_41`7TM?UxhRlVQ8HQ;@Zjy){@zvocLLFr?lu z=MZf13pY?(JL;CP$@Lt=LmTUob4(vF%ctnI-@J@tt(rsUDwgI?to1Tv2bm-5fZss- z5(eA+N-B;2+^D+p-FWbVllIPiZZ>%cPCRsP-{yvu*!hStIpbd{x7N-YH5!gvmw<@} zft(?vVfmRGjd8$_efzt%ivj1qe0e4BaKVo6!h$G9t}|`Oa`SVpGd;G^1!Zj5-8rxq z{EeB#6=PxqDa!K7Ffye4933{y%{Q)bCiCXa=bR6}=U0_UgL{*|@?gSOj^;)tym+kD zowLYjb&9%j47VPxJ23TuXE~i4vgm*YqjklXAwR{4A1`=$fe`la)^vHr$5_cLA{pml zB?ZJPAM9xD!>MbbwvMBHmxot=(2EDz+F}=*5W;YIm`ru&Me)H=C0&?&h2v;8t!NrWXF*Y>B>Auo^X`5A(d%w&; z*5J1s&-`?6%tJuf_`T03GHAuHGp6L zJ#r?O_jy4HMrufICpKyEfSX;&64!^5=&ecA+x8{&^{sv98b4m7Tpax2{5HpJw!-W~ zSxRK%0mNf9793w6^E*geyPiWsIE^ho$wQBLo?d*p+B)hpRlra9JhX0oz$k;|TovV8 zGjcaXEcc~Ps+G0R3;BG5CJ&jtj$E|$AwStA*UcO3#w5q+{oMMX?Rm1CK$@7rTB!2KuA*jIAx)Im)1Rv(-sJYI7OWxQt2K_U@7DZacU8{`uw>#pQ zuV#84Ogq#UJbpbnSCIJRK^oY{001BWNkle`LA=uQ+l!tYzoU!PM)a(MsoaqdZ7`RXw;xdwuGH`c}Mnz;4E>T2<8 z?5!($k8Lp%iG$sU3i>O0@!{Bb&$CGSeBtjpa12VC`BmqT@k=~ngP*3~&VA>Vl5&mU zSj11JZeouvU&_IasONgl%bDZ(Mh(0eQ!jA%)nt8d*ux1LXKv%S*hL?1nGamS1RIWB zPsA@MDQ-;`0}Xn3pyxjDr_V&BMKHNIJmYfqxWuCHnt*E}b$HoYuhqiwp~b7`Ear<^ zglR|t$FYkoV=gy8=Ok^I^;cQ8D13Q-boIR)Z&c1VbtQV&m$(t?m3?1wWjt=|dK%%p z6f5Q3!=G#LPoMftF1YMma899*zW3*i$(EX-qqTnQ*M4;o^xxRIb91E8mi{VtJoJCQF{qj8#z`-OfE8JYDMcb@T{m}QEHcEGm#_>?; zC%zE^=`{BS-9F4VTJ3jRZaZrIh^YOzEUtOMapiHt?&^uhv5|9lfI-nVvJe0M-~Yxm z6Uk4wl~@;Tq`q7zYLndulV(SN$tSjxu_%Av?2V{Qh?6h=(ZKLQJQ$xo{ru+VU;gpt z_uu~Cn?L{jee{!=BOIF0V$~>H@doq=!N2~Ei%n&Y8F?X34pE&&tzRt6jaB1z4`Nx-{1TZj{9H$81)jHdQTrYZMoH^vTothyrxjb-I&c2vO|hu zGA})gSsQG4*yaI_7pK3bet-V)+nYb~@dl6+o&MuXU~*#u8M>vHNO-oS`85%y%(p_t799usuJ-Q6)3 zwJ}&PuRg3*9)3AKzJ2|YHTYZhf6RFV_~1|EiF z>IfXQSlF&hFp$0Dr*+e)=g;}6=kNJ0fkO#k)LF{ruCfj(?x^=Xy1do@4cG#>1t#!-Y`p z4B5zGNc6I>p1bNWFXvr*=X?{Ft|{ulxvFDsG=Bc&U&i?DxBnIMue_8Y5=bf9AQF*r z>vd|Bapw-DBx_Fqh;LIhvBypl=(X#)LN{I$@xm=g{ppC{Bu~zPy|Duqew>f#hXE#r zWK6$Y_9f1f3-#XCcp1%2yYdM2OYQ~j{8l&DfZXo6t|DZ9;qW`_&tB)L5BU>|btynB z6Tkf6eWZqh=gcgIq?yLcL~2)n!`~dSMu+V)I<{0NKbezj$cH?&{rk7yV$zpdB7qkl z5y%y5VCFbr%nJ{Wn;s+NJYk{(-`E>JHECX%Ke!AgvE&|+4?NB<{x#zpM|_9+-#Nc< z{rHv_)7&WMs3b(To#)=9o@F^lb~7Xh)2tjB^q*lPm>nAo%KxFO|O z`TVIbdH?;}AMqu($iojNd!0A0_)$yq=D19=40*XmjH;+}Z1iSNk9@F&M`Vh3Z6mM! z;cPLn<;IzBUVg~K(Z}2${PyO{SB{;|m)P?YY{r0vzU;fUox#ErNG=Y+Fs=lnYd+ZQ z4Ybz5(<~dl%qBAHfFz=yKIgXtavlCX_gH)HvUSG(#KAkh9OKfYDAe11b@h{K)bOCvfsAE$DjOAj=05UCWJYZ6hak z)raKNy|}KCy8BQI5%H9Tqf2Ko@=%!<8z1w{tv~+!V*NycgYm;6{(X>dJl3%>qVO1l z6Gk@t(k2!}(Yf}sU#lAQ!I3=mVe382#LN&4KmErV{KMm;$09k5(?n$q3Lm0$oh!;1 zdY!{ESh~w+=eJx5T#RMhY6MnBU0aI*KVEM9{^d*VQNJXG@k;O?Zpi?cT3rlI*VeQX z)0!rH8QgqCl`!iJBy`;|SB&8gFY@lO#>>ClOXnxA{tUn0|K!=D>AkEY><152@Yi2) z;fZE5_X)J;6YMy2X{?vqa88?gkjJez-`wE0%zjGkzP|Y_$1wbw->i!Wy1v2?U6kzM zw|&@CF`kw8c*aJG(JUS`YKyGrNOOtr=8pyO@cEiQ{lfh6lTE+>p85U4b9wo2o->9H ziwZ8qWOTV~cX|x;|^elv7Li{VV*q?*7lesUQCJg$o=v zH|&?!v!-GBct*I@M12KHM?I-g?S zzzr~V>xO#gxw6)Juj{*Zz3&R3?!-uQ7eQ;8Z|vvZ<^TNir#FA*+WSB7iw^&_#kT;Z zvg=Mcs^AmntohWur>9~NXKfd!wT|<|ah`~ix}jrj>^!>ue$D(+znNeBJa5Dv-})0T zkq8$3Km#gf_ZPQ@sBwG$?|{=}b-LJG|W?B>NWnDsY5S;zcxY=gz#b3^)dY#95< zW~gz52^ZWkZh&I}-izOk0huP`#*7Sp`0*36{$yhM|M&m>p1N_5MUgHo2eWD2Ic_=^ z;L?W@fV=LP_pOh)az4pdKPp!0vJdz z0(wQi(eTN6>apymu#utTlO`E%3>iZ%`;yLV{SU1t^AeXpReXD zO8M`7qec=p-pso8q-mRa0b{HA-}{lg`^I-cd-KN&wl1ogvhB@OubR#y!r*qnQG@ms zJ3k3Fw5`XIkQ0i2EV1^$iPE_fj|(&pX}K}TPllmoO#Qr310ueK%wYa_2@4)H*Ja}L zCEe6I0a>?L=*T{e*w1-)ejrzZH9CC)(?H>;n=UpnzkK21HaDO6ytMe_U%6EQidM08 z9Zei;b{%VpAx&E7N)No&E;?oFhiUrbv~}X)$O}#tFI+U8&EW_>wJpooXABtVC0-Bu z5m$x{CK_kg{ZZ->nGd7+j#Yj#qK*3HTI8s}&8%GitW$a`~u$JW2TN}S)z=K7%>Cg!Jm zM0ZXu&tq!GIZeHozn-7@Tbulu$I4p!eJQs&hTYU5rsxjpv0V2GjTzpm_0TMQl@NV2Q(= zi<2=Tk6${}B7UVvRLhZ^WSmWTr-Gr9@6M*qod3Y$CyVw4DhM1`eKAz_#3W}v=>}`( zh$u$P{?W$%A-TLf)}eDTeV_>AcdXYppR*5U^5^*FrT<@^cZr{T^MS+Oz^5V>qIkt6 zu5)ybmk6MF=rSPBvCD`Yc_`XkBZ_&QzVTu%tome%pse0@q`N445B49CIhr?kOIlo070^9?2DH-0&m zzvf{bj_4*Dfvq7pUdL0r6>6uv92svM<3`L}BL7>9uE+3bGUmKqKO9rN{PqWP%M0Eo zM6{CHcyPDRJRdl6$QsGojaDhOGluT9ZS^CFNj)N;B;*~yXKebYKZI+~zdR>#4irLL z{P88f{pltcu;w^-aXrxn9{FtBxcuZJzhe4q#*LshLn6ZE!|B=#*h7$nvg6#m^Qui8 zR8M65?Pk#sk*jnULYs@N6DEK$C3=oFyqZ^Y4id{Lz47=W+)`BgNttad6ue{{#5rr)}*IHt17<~jx z4RalI-DsChV(hg_Ies{#xdx2SdMPgW+rAor>lTu}G-c0y0LL)DTFahqaP6_=8m_~_ zym{*}yHSsLI$c9QN*Qn5|D+?A!*x16uUupCWPulG@n`t$IT2oP;J7_4*vk{Nok435GwiWm zO8A{I@<|H6N7Y;>TwBd`oLm#JdY_-r&IwRVjt{T9q6T}YZ9edj!^q*G<6xFpIU(A% zKE@Qo3}XHx&s-(4&IaB(zW@3*zZwt6x1Rr2zubr^4=e_|b!c>Cr_r@Wm9z%wYqR$) z=rpX+Y!~NX$A_2peMSgucz@v6y0~faf64L`mx4|FeW<;33DcKH5b#0Bo0X)Kv6H`* z8w?8;83{`S;u$m9Tg;YkZj?zS7lKWc#l=a;^Z+3)Sip#y!8R1g^3O{fHuyx)KfFwz zkwTK`c=;qwxip3x3!q(Z8Y0a_$X~t0pZxI(XD%q_=7mhLe#9}9HA3_n-#C$;s5Nd4 zs23h{VRVp9hHQhBPX003`^B${!cQ5o{ppyVaQbVG_?82;JqrXV?TJ~voJyhm;FFp- zKjc%h=s3amCJ$fqYeO^)G<9wrRt!eIcCijTq{Z&daqCO}aMc^!AqTtdCJFG8aqS_g zGJF0>x|ofNBkXv9%Z=X!+9*e6PG&nUYeByB@g=tFBK^n_IUXMUL;ZE^9Bupsi~TwI zi&|{e&lveU+jC+HZQ2+ zVr>(QRPD!xa|Ux^Aw=FdfhGpzXc7B24UZtI#OWLjUUXoW+wjyLc@)GmCieJJt2t*8 z1Ct!TrWPHm#1OX8$1cRh#G`T1otc5R zYqhAc28x~j(qT=V$7%6D629IF0^c@j&#Kx?)Zukuc z5yl4-=OAO(=wh-r7mk&IHNN2zvh&)yN0D6x0atEV;b{vXv(}nP-%#vubaWa>VXNqU)gW|T%W!(Z^6-sZo#0gN+*@n@W_ znA68zDr1L^Puuc@SK4k;6MpTDdloC(#ho#+_{XN4xNh~B2B64|IQqfONV|gp){|3< z9?`IC2r>0ZuWK;T$JU?t%sS&igI@(Mv*;r;@8GX3G`8W+ePVG<+Vd&;V=oP2DU>Ug z;v&iy^dpH)uW|Kd9yt$i@0Yz5j1~D{o)!-Wrk`=oa*uPN-n_*PE=myq3{*2p(TLz2Ysen^$zs z9jD;VoDm0(Tsxx4d82H$w)^0_^Q1d=5-E2%kZWX$0ev)S&6qr-mdzf2JH`Zjj_DRB z`zYbZmU-TXV|+|7I@e=-h~GLP(-kA*=!Z|!JGREgW@?8$GS*LCqr^O4n*f;6@A;xH z^8$GI(rLUHrPo}&F2jv6bvMCigS!vy_<>!7=G$8Ff@gVP)dok5#iToRMN2M(09(7r z_;pe~oPWJ$IFXIk$avBwwmot|yR6bDE-f+EyMo6yM)2W7>$<2+r(kTZ`}eu>+sOWu zogCAL9>*z7s+?}pfW)`!@8(UU#*220*eOfL^;8=l&cD_U9Rqfa-+9Vk}` z&enS8tmz*9TWjW>J{VA@$+ul~#X?cO*x^&Z8e+3+1b_NF z_Q!Py!O2i6L9nEImpTPu0XJBz*03E(q0{sfa4BORa&E!s3EYf?x(lLocgq49+bbHk z<$wY*EQmyyljI_zwRCdA$MPW7two}6E+H`v7JQRb{l`Abpn0ZN#BzWy-G^<)){~$WU#IjZI^cWfkudUh)AHc*dXPL>V1IE*cyH=F zIN?N%Bj;v>TG%3+-g($FgDhm^#KWgIAv=d$O#8Pmd5J0(*h8lkj@tpgF=9MnmwU&Z z&vD_#a^gNfhX44JA78<9O)1XC9N8_I2d|OUC&=;LxScN%<##vr2W=s5hm_IhGkJk$ z(>SY!pCIu{F`Tr;0>rf8u*Vz>wtnoJ+au@Lo7?6)@d;Zlf$SYvz`5`nrTn1j35vK? zal_hP{kZ{wqU_kjj$`zjFO~PWE@so-0F^a=vY7GgFCgI5Jv(c}9wR)`sX}2^sv-{> z*8@)B!}`EQ8KxQWkMjs(Io>+YCd2{EC(A&1|!012cO!PelITolD^@BD! zv2K5iIaD?`j0JrJ!&h;*o@2PHV0qMSb0H6I7H_=5vv$$~COU&nD*fIZ4+aR~0hMBv zmLp}H5BE4+7+GRcV}rPP@UE?OsHL2!kFYCy40g?U4j#R0BqAJpc~{KWU_V^I3CiGjVY?nM&6$64c5I=UhlxHd8>k=_2CVlgPQi-+UjPp8C`oSWgM~#eV>QsXz0zewf%FuK2fT>{u_<5)2e=#Os4**G#3<@aploYq5~v@$#dL z25feQCB_Y70>#`q#yA47p|o?lbJv=|F8alpdz-Z-y6FIw&(@+ZvHH_}JzgL-Anf7< z7e9G)w>22s@8So~kK3eQS16Z5nx9fmd^z$z| zMi&n^?4MG2R<07_<)Be~b+?=A?zeRYk$6x#Li4&gs>m<+& z*$ofOya5sekMYTlVpx1_t09J8;>%Rf?RBzZ!PN$=M;^t}+>JOXa(&1-r6!ix$P2+3 z{H;Ux%XK({xYWnLJPoBUYA}~$_l$`@^c*UtVh;&fQ2~Bd14UV7jr7s04WgQj~ zckuTUK*s`$vGski*~AL-f{BRQyfolW%{EpoSVK>)xHkIMyS463IzG(}UXI8$9bfLU zk6e$%MNdx1{nQ5E#V}`V#mF6{6BR0tRy$B|w(RU$&vo2!Z1zD|7jbe;8UExOlt^h^&&G+YSdEKC< z6OWVjI0vIVvtK;IjX~!M!`rZH9cp&`Vq!mf8Dj?~DS8c|O+*a(H0Jo=;mb zz=2Y8FaE|g#IZIUArIc-u$R}dZ4_{(H=1iF4$3Cv9v%x+09!z$zfQ-k`GSUHjLKu` z5czZNyw(qt9L7!fY(T*O=_3s{joJHeY^h0W)Sqyz-Wm-SlErEte4_l=Dq9^sd_3J7 z2b}usxxhS|tcn{4Kjt11t@Gx}Sl#Bc{uwuBpy;!cLt~yTwGp>@TwLrqmTHMy1`+mp z8OQz;8$CP-Pks>1lK=o907*naRD-46@kdq;?MZWtjfv0TEe4jf<&7PUINOxnVS_}8 z$(n5)*Z=tDn#i>Uq8kGpc8Jl*m{B0dh?J>1y~%x4#PH_ob>6iV zLv!&bB;g!;Z)D{*If_2(hg>CBol&p_nV4XR+3{M}g<8dOKM|g_i)`a0f-gQANQe_{ zu))(s%A-X$IJ+0Klm;Ka+Jo`h76)nTz7&pKOq7jc^F@fraeP6dHjMl7=8?O$t$Qtt zUf&Z*1W-;c@uS~Q)C8G)*DK)?b53jsAvJCBowJdVlHgSl~@H6kJu zw$6LAZ0)EA57a{i8*A;~xg-9bFZQ8(3<0Ixz#W=LYj81D6)WyLXIrV(c5nwzS={7& zt9D`(?$+zw82H_n9Lu@$g&nm6 zcR$GTnNVI|^OEpj8LuI*+#8Xt*TDf(i~=?8& zndW*S9L1xTNHqa~&$gI4T6wUW2i(>u`x#jCGXyn8@v#Q{UAy&3$9~2=##gafIqTY} zIk|9rp?l;B5JuUfJM|ec`HG>*_=~sFD15yE#BfGr6t002Tv;{}XJAnG=DBvpjf87n zw6ZPXwW#mGsm)qP89{2sJpl;hmD**K(ee?nRASq<`-}#=lK2BTTjDr})Ji$tvB+5a zY>+>j_Q90n*cFpd;Bo$0Nj({1&%P>qoxgcvz!w(ITix^`rCm#S@i9*J+)Kf91`eQJL+TK29G;^zqu7DwBREhZf8vkw`MFS03oPyp(V&X3}KF;1Gzp%&}E={s+M zQ67`OA|#1|yi zXC+UWpajQ}#Qj$fw(!}yqt+K0y>UUd^IDkABLdpE7!q^0`2@6hP_V-HW-)}h}7d)QQcw)@R1^Q)sEi&G9!P$%#qEc)d5$uM+&laJ?AG%GP6Ozfg1%^0)3S4(&7V^rpJ1&6IVezybF3(3GjSq zv9~sKC;rx;?(v(2>w6~9xEq}C5*}MX#@+qs#-_2@u0MO` z0iW!%rrc+2Jod-8cAXp6UVZHR&_8Q)lpBqqwT4!Nq$&R~*m*F9tWZ?hOzJ=8QiHQM zo6VwlhqD2V=}moXz-~>Wdo*}#{E8)Kj41FVxcK%Vk9;od_=r11Sqt!NEQHvXKejd# zakCO2Z7u=xpzcPD?%J+w!aa16SlGrar&)uM1A%OjUhDCwjqb(LmTs8Qcg{`>1ZdFZ z%O{xH?L&Y{H;Yh|TGbO`y|FHqSwHe6ro1Dw4(ROnL8I*AyIi8%Xd7P)rIYhm9|Q(6wHmkgGQb(Bj68O5q!yvqk{JXTAi(CSS)USWw%Bf6puI8&hEH zxYGe7n7z0@92300F-eT|xxLsL=-~su8sEJ#OluEUn`3%{OQWNSOIFyBUyg;|IMsU~ zQz!l;LjTaRm@zK;T-zdKOm4}%bPpeJ>qY+MLA>%YcD($1u-Ug3t>4D44H#2Duc^c| zD$>FwkY5HwxAQPzv5(I609=}DR66C;HCr9=Hnwwn5~*I>g2<${;o<`UoQhed6I)v3 z$@Lg#Dz@cNP%O9T)4&mH@ha9ja^k8Uyz*#uYDwCyP1i5KUe;d;$>P)mF1T<0dtE_qOrlluk0nX)|J-?CbX<~?aPnaar-AvC!p4BPx>a3(F0pXGj5d0+honwc#U~^ z7~E!y2i16@NjxMcr>%O;0t=(&l8s8emXKMjCn9aX7 zO~2JJU8oEXy0$ts-SoZIM8U6^86$>kE7x&jr+m2AmiogX%6t}!adSmE01|3{@o_}$ zcEH6lw_2g6#_XvPQZ>PYL#-lA&sr>|d%S4uTU&<%lI$TJ(W|dE|6zl@F`_F^jIu66 ztaAp|kJ;y5%BP{2{=s$%}*f{RhWwY(sHP=Cpy!FX=@?hnNmjGH3Q&~Y8 zr~czIoTMbjQpSVuRLh*d3X_qT0u&s?t3z~$2CCTswDtm&~ew<^~=`;tBNh+p%4 zFnM%wJoEoVnH-F((X~UBJHV#rK@Ld1#Env7<8&(!i+D;bhKIZaCo+1l_QqjJh##K| zcKWh-Bx#J;mjfro{fP<|wG1JflQ`ei&KG(nA)djIpYeO8GHsZ7{m9D&a*xnjDShEvru~Z~bYXTSl z=ugEY5qFLqqov=d=|myt^cq!{_8|_5gIf!H>pxC4aj^M;#+=<2k0}H!8Vwt%$qidg z4BHph*sWjnc?sgT0k7P;PafFVCe{vje{y~5bN+&nf78TX*_GKcjKR_#Fy}B&2JTO8 zlq^2DMP>~~*Bv0w#`?Ij^20x$h}DVJ2UpiWDt*QeeVTFGbNkX=4ic-Hb5(0M#MnqZ{XJIKqjhRb%mMGYVDgxqiEptZ>vT#eR)R;JBY)+%!4hc^MoO-E4}^QMh6GAE6XSxYt8kx z$0XV_--Ee4GaDz2_81#BSjO+ z0JrEKxI{oWR@OGd=CJ#+z{EIfjxS)u)Ia*!E1l;b=GXeJ4j(zjZ{ElWGkRdRou4F8 zu;{ywFt(gOTOWr~@&(ky0K=TMr4EgcaO+A2tatuyqB$12Q6oS`a@3_=wl{AK>o@or z@5>!@wEE6k$e1*jmzdxyM?)w00qwfiIzs$ef8|lT#x}2LyG9CDPSq1pWP;e%hCR5` zAoXZ#?mns=WAdEWqtP8haBEW?`LqW{*VwJ$R_@4Stev*?+u+*#z!N@?su+u$i?<-L znBTIief9n%?c8(jF-98uf%l?Q``VajjbcD8Cg^zL)22IUGpFMze%I$nYN%6lZ!Fh9 z1l`xDdP3g0+g#-mlC|@ z(#Hm@mdKIgT7y)*`=C6JdoBlf7$804h}E^u{UvkKXP;vRpzvlNr=8bw8yz^Zh5#|x z!&9#eXht^=bj=6ZL$2M#p8gPpaeW}3w#XgT*xJy23bCDd#ND>N-Rr_2HsnY?3!^`d z5fFYT$Ok5H+x!V(-q;yT<^bml-&y17Kff_kC01=>oKZ2s>km9kjIm{R>@k`=8-Mr? z>E!_Q<)TwW6MbzT^&;sJ6M&Gmu7e%j{guad`G~QsVoNtN)|~vY8}(k(awkTxLrpIv zmrMo_LYPM2{UB?MY%yUxLDv?A=o?s%${Mx6`0h_1fMs*fgo54w+yap|U%`_Hh8XBH zK@#H>Xhql&`(<2Nd8EHM8(9- z8apXE%1Lb>=rb<$t#9_;NNaeo&~Ch*WFJ`$%}Q22b;uCO^TTLd8YJ7+uqE&Uvm8QP zKJ2~Fa)TdlJ~o*RH!ee?t_dR27lGDJ+vmnWx4rv zUY1|uS4Y3r_DGSh?AOQolihHtym{eiU53k3hsa3yrFr8!v8q7doW;?GUj3?bsJVPq z77zW#tJQ3QNX&cTk037CGN;Dcf(^IUK+SZ!j#ML-LF@b)bB}@9=h%c-n?a}l!*1iR zWdadK?CCe($(7yVN|y)cwBX$OckMi(#l%j`>aE-12!sh@b@~-eY~0mGpZr?aO7?<6 z)&~X^@s41eXYM!8@(AWboor4W&|8DxnL3-v14ddJV-V7>9e{(RK1lj;Q`KX2L+)M# zQ<`84bnwlv_!vJ(V_MdrRRQn)n5TH-sL8yr%1>iurN)bVZrioHHz7wO$3!vLsc{^M zzj0P4FF-dXI(|SKAbK(6>ojs6zXZx{0WZF(TdZQs8o1UN7Q?-o=*>40acW`XDg8*llU$x`WZ{6`8n&m7l2MPcUre{$>BO zgRTK1=()V~C0PDBAAvAj_8w*P!rdCg$+^`4PwGfaY+su!2NN63Gy;P>JGbICxBx&e z{Py8qyK=%%s2H$c4#@FAMvPKJLaU}YvB2O&60KO8)^?@tV-FrR(Qn-&1d1VTzfrw{ zVi2yFvRmUpTbuGNUi$&m@9ZMLx*i@Y8s2%?T6Df+3K4Ah3>LEO_S|w!(1DJAzZn3^ zgvowmb?(XQq@&**Ee_|vVjv*`$Esa5JGUSaeIJeuS{LJ9@A?)AuJzo99qhE77-)Mf ztK4$PV)DX1LUTLvi6%yYmgkgT5~m(RNiG(*bsDj$&|i$@2sf4S@%%M~;V0|Pb%OAa z7MHARt7&wqW8zh3^3ZDPVr~SNczD@o^xz1-l3J}bKRB|Smk)m*4zA6eNEfFw4Z}vk z#+Du38Hu^`++%7mFvw>);=A$bqdwypX`W5xDvhIUgp^D!6Pz)eQr_}GKDl7Y(8gXH zI?eT{juDpiIh^W-!Z>!^%={|vPq&fd_tbYn)fDUp{>D@}hE-SN+>X7jq7`bpPj2=p z!nQc=sCD6f03|$1g<+$0zjzCSZs(c(aZeu|_WOb3w#6xU6o&{F!P%2URN_uRAQ-bh z>kyYn{6dEkjC^gM&hp&Y)s^EVuw(YT(94B>4A;Im5F-x6dyes(Y*gf#-y9HXtwCj5 zH@QW$wL!bGT2`lgYB&K`Z9c6+QrWPpI|>tg`+&0MfR8)Q#wF*RP`T*E$;t zunDBUQwGlVuW4+-m}bq#2+p2Q7>kpR{TdsBPz*EJ938R|H!-8zTr}2U{B#|`u-kbD z;>Sje)oG<%7ezU>Xe>DgYPKV&P$3QO?F%dd<#&Hr81v16;ijY640b`)ybBAm+*E9gQo45mu@-9czkTZ z;0N8IOgU)aZWfL++PZAa)kDXaI3si){}+ssM(unPm(US_%f${|0e;x)q$ij2x?H~x;1SJ@n9Kn=H* zNyZaP`T896$3qzmEKEGDckXbqS2Up8-#svfJOXl_V=X@AP-l4GHbf%uWtoP|6EB47 z)m-q(i#Go^a&Vuz_~IYY)@LK^8pc8oHc`TLZRp*8^wW+s#!ajHt_}WozYof5w|u-U zlRA>+1LPh*u5U48VbRI}`PviDnCju4k0epxmZ+&wYZJkvdAv=2Xq<(4$;0UUD&!>X zLmI&{t}mULJK_T}JPf03Vh)CW!~#a!y6idX;BKw4$%j+eouh^##=(i+pO#=fq~F?O zfL{a%9dw7IbkPita}yfbHgx?eCb)dKnGyj4X{lB|l*#?7}F||g6pP;D7JR=ZKbW5`5@GX*z~o6kd3vpoJQjgTl_aC zZ4M!h*<_TlUxVfN*bLgix#(cl6IloiKL|GF&|G<}4lH;B)#tSxAx}Ssy~4qk|K9u|WWCDqjfSyD_iV;OboAAhU)MlA)*$)^1Qi8x zZIbz`0g7>Lz4s+YeU4iGi$V(Rv+Ivdux9T39DO1?s{%DpJ(pASaf|v9M;XTX!0n|* zLe1RPM05OyTE@+#@tAW=HhRs;eL^($k2x^@4?aQ>p`e1%CHDSy!P=BiD-WmR6JuO&nNQDm{MnZ4@4 zUa}lX&0yCBG^_2rpl6++U$1jt64V2Q7N{|La#45<#af=|WU%1NIYMH-yt7!HSgqB% zVe35tJR{r>`0bNLzp_g+K+n~So4pn|Onfcjp#a>C*!~Wma`L(J-aJ62g6P%2;vuWxPL z9*~i(!!e1$7G?L*E%&Fp}}!e#T2qnD%R#m7cF?Ku#*Sth_dJf%xBR`yLl zpp&#@P!Zi4`@p6k_pjP|?potLp2jwOYx^_M5WazkE@=67NgLyjZEMHI%1oi2FF-Nq_< z>n|_r)I)T?>!mv{uvm-u6z=eAoq-t~H1w@Qkh8Z-bsoOp+R&rx0Ot{}tQzMd-Hn4! zYR2GvD)MA5IcJ~6GX2=JUpQ@4wS;pUIEILPvK@R3WRJn(jn z4991bXP-JS2505V(|ktQpF+Ew61h%_;-MXU<^ex|d_93TR&56>ex8fSCVix%sqaHV zi2Y2!8Y4tg;Np-F|J-3f4MEODj`Mf+t3!&@t~-YI^Hb)&fH-Tgc#_D5dSkboc8=-~ zWFN8_H$J=CI;XUX$jtf7Q3&GkaR^lBNzM*2P`tYi)X^Ot>1vjtBFNCL+Tqqk@#Ys(cxy}_4jEOAWh9R{1Ed~Z zJvP)MU;x(dO4B0-I>?R9>RT5LOr-V1GO}>s=J;*aI>(3DIHHo2CdD0+FSN%f=K74w z%b>@7438u6;5tpdb@w_JJLZlTNM(sH?#E+2&H4ZH^#%%#E4!`j&Dr_?A5Kp4uoehO zp1t#`N2)3k1VKu6+wPv3iWN|M_SM4zn% z;_tP$2)LWreCY9z;q>9ZyfO9iJG5$9W-^O}GU1|;9 z>u`a4-oOt8v;A={3wZoIAIXDReC)j;-{&{dN?7>XlnX!iLuR29rE-8 zCf4Fn{lcM9$72&09Wem2mEvRbSB-f+Qx0NqiDh9vY>g)$rZRzJ&1moU!H}3bo}R2G zd>$2Ftq;fD+W~N0d7eS<@~powNFR=BJrx0ZUF0LK*OR#M=t4jV6tnv9;i~gptsjIm z)2D{Zp-B3GrhHWgtA6RfytAq4@~sYLM&s9y)>}oon=VV*NXR$-u_gBgkCDZ%o`*!9 z<`RElofbu|t}F57rW<9a{%UF7Hpzc>5!K*l`Q8`c8Du!O9Gq4ck2MB;tL?qt*&~UuR3sbM7Wja@ z7(;$ezdrOlEC%AYUkChnA0BgLS4qkzzJ;M)X`EaL=_z|7Xgrp~IORmLiCq+Uis%z#twiqy zpiC48vwBvAJP!_Y46tFlG=i6dSmWdT{VReoC}93X+zTyx)F^q)N8eyZz(&Q$BgTDJV!$n? z0Z(KWVlpG4y00(z0_T`Vna>OE=!{_>0u@z;MKx|ViE=7qcgZCwX6!F^_XoH_$j1(7 zIhdY!c>@>-oapK-bqQkWwt(Pn5j==X^DNlEh^%6;_RmbJ=>lATpZM7C?qIcln{tU( z!$-Ph?8zAa!>ztYA!$R?{pt{_HZ=7E&n!eTmy7*79)5D28NQ!PNAcJOM0ox@`4j)R z9tQG44C;H`v8{r5^I^08fBh$pXZYb1yVD2GzVX4$lLPI|+@8KFYDeU3x4gglLKr>R zk=>KqB{gQ)r+W?sp?S3Q8jR4^_&nB^_jsHF^y_Q{vus`DY63aoW&701uA{Y*^r{aD zJM25;$f?}%YTyMGF=*xcLUv7>l?)iPv+F1)>*hUkaPK~70*K}NLk4}oe{|Z$>`dYL z@Bl!+vc;jDW%$|VV6+b40mb-ii?o2J0=ct+5 z$MW?m`~xM7yFL0E_mbipi~4o(JW>xj`cZK9`P16)FIr;fm2(I2^W`ZYQC_o!o)^qGC^%@X^;@=Vav!NFs zCrB^H1?(CzJFr(+?#Zr+Kmq1+6)Feyal}MTdhWy?joJRp+G3Y*^4il986e4%FC^qJ z&+!4^LlShNk1_wS)8ouzZ3e+?zxqy7f|pl#gXI^k?ie#$wjQ^|fPVt}L24l}@|>2q z-J96aYA*~cFU-3p^^v)K9{p;4v~Cecdvd(jH(vMgg*Z6fXZu$^poFvEPx&|Pdc>ri zlQ}tUKohRo(KYVYLj)Ej>BG)`Bq4a#Qq>m>*OX`mj}2BrIk})dY7nArOLbg6{LenN zKgTSKW!@SrTHOb@@Txf(@nc8t{Cu$9Q8sqfAfdSS;M0pGAuwyU{zeFY%L*Hv9Ibmz zn0sVVih?-%+h19*?hoF|VP`dBZx63Z_uMxgdC)WeZ6sDRryLK2T=-&8EBgzkXR8^T z{YYfiZ#*Jor+nkD*iJ-+p*QSDdPYBahT=CgA#aecW~_GMY%&3f9tIm^0p^V*j) znMK~}HZ~`|Z++_^L47RV$K>?+*#f%4yWWa7&(&LkSk4okVaro{Yt8xUtW=ejzlPJd z4UeP5cq8u_rCQ`!)@+)SEailO{OtM}NpxY%*t~u_w=o6eylb|(Q9K5@o2lvQedg+V z2v`iFFRp&SQenUp&!RJWCw^ zFs9+r1u*>DFOS#uA$S%Kx3u=-TftpV?72_#w0jp#kpOpmR9yjU6&{)ZQhYnvhQ@+0 zgtmyG8Znvfe6a9FWG@nl$vUy+sGTkm2jAj%vw&|NmGqR9ZO(4G@}brdlBx+1d6j4q zYb1#^mB%+tSL6Fv&W?&V$oQUmc@pd!&)HUs+?Ox##-*YseR$-WsL-~K&cxUQURa$z zYd5`4pMXzUSqNe>K){-f<5|p*nWR+*myv-jKe4)BlEz+)=B$Z4)0+?4Qos3b|K(pT%#77xZ5Wz_ z)iZY8&3TFUSKMLwUOTkO0ggXhGi(!lnp(TjjlY`FbC4%LS|1z!}BPhJrhYn zNSv{c$1K%{&A4H{+!tUS5@~q2&;nJCwZfWXC?(Iw;RAyLaICWU>o5L#Yi)Q-R3E$D zUj~HSoQWsq`9=*P9N-DM?*$szH~8`dAy2uB;R7pyIrTXhHouPRfB4^(4L#rrO$6T% zvGyA_>tg(?>@F{6DKcY0FLiX+`_g;UzpNFjIl`B@YXcIS7;Sf-hC_cyeIb4MgYLNg zl!MH>76ULhhjR3%dU>mHdiiH1s=Id9RMHOcVx_+|sg>Rl$cWxv=OSyqoWzsZdI4u$ z;?@N_J|RTrIBfM2!+3kxzMi$AbGL_MfpXVKTl8&jI!?r6 zXLYCD(e#Kz#N|}OrGV&08@aO|Rw>yxT~+j4O!jU7 z=pe4KcKGtg(x7};#2XK;ZND$W_Sb*h50GM$UET1MNw5UXjTGN!gI5;{EUJnNRoZw`N&Ffw8<1-Fi z#`8Tr>oREeBp0iaHQ(c|m)ej1{PdW*uJ+(re+N5icK$lB`+Be``3+g>_WlXjPRBq` zv7sFs%B_Q(IjkBy!3=VtL0?TfA0pfMt)=~DkCu(~X&OL7xtQTJ)uHy@n`Qs@9sJvu z)@dm)zG+AM%)NJ7Pc^g$<~@|9qF)|S$b5$Wn3w;>6*45UL#Dhe-@VX1@A3 zy=#W9(Jzqstb6Vga`y$k^;oFc7hJN3^I^(j@qrTWy$~+?6vhWxm1S5ScHm0U$7f6# zU027;E1h0Jz(AT4PCr0B7d}5rLC1U1#G`DsYI*P7g&P9@>LV}p2|#ofY(D%zAKnih zs6~P`fN;KUG;lk);b}b&Nv+p2FV91H zD%2a}1%Ei^vl?)Y(QKdDkg0@bLP~Z5Z9WCgNyUHyVLlbzXc>Tf zqfZ`ioK4uZ|K8&Hzy^)u#Sz3l6;Q^jUBz?rI_oH2ism}80XF>YS}ow1)l z+=FU9A2#!3;r$~pr_+Y-;UeaScAoI~Faw0oIRqP8PIvFgm}{0ft?~TkM?(*+uqi)z zY!Uc_H%nd&VRmxjZ-b z1AO|$hqoO>;%;w}YF83&Rt*VJ`-c2wq9(mkISXhVL1%#V=ph zeUVi)#QCQ5=S^Rbsk?Q+U5&+(gS7S1%ilqkKXrjVq&j~i@HXNy-u;G7>{$Je7+^Me z^|AIt%Gjr4*^6r)LeS2Po!PExFQ$6{Z%lQC*~bAh90ib%T>tUE|E;Y%xJ!yp;lN{~ z2CHkhFV>#S@yiu{ayV{+CdM4xTb1{`NqwNZrsSAL6&QCZr8)W4v>fIPN@ul@3oi4! zsW#fa2^`d$o%@?%iv!|@w9s;z( zcW8WwvF=+tT`PSG&EuDGS3Rpl*JQo*)v!EYJTl>bW`z4VtS)<#y?z26?F}-Y>}gc7 z)KLd}j($wnJQw~<@IHbct5r~x3E*HY_Rc`xnzOdyu~18nw+FknRk{>BulUA;59Ug& zi|b5#FnBS=3XF*GiloM z&G8Hfru6S?&-l*NnnSQUYcKb=r}Ja$_mTFxM8qq`9<`3{7e}H#u42cPfN7W-B%}Mu zOJN&my!EAW|5rBX;RBRCZs`TX4^^@p4Rbv?Cx$#^*l+c|8IZA_R6{jMtM>1HD$y~K z)aM@l`+C05WRFiR3FgNyua@dYYmc$_d3ie$eb;c29#ZcK@>(!BkUh|4E_`Ax->soo z-Zv^Xo`>Qi2mj&9|26u4s@c>7-^CHr{N>r+dtqQ^rKO52XGSyYJw_JW+JfUN05Gk) zv8w?*s3ZTZ=?5|RGk`=udG?7scLrY_awedfNZ5EdcJKMyyhrNwKmMCqqGv3n(pT-zL%<}bG1LJt?b03}w zu7|IN+CG?P`NcvU!BEPd1#8Uz{oj4vlcy0B)M^q1c#3(x_!&pgqOPq6VMh{UJh#9j zcUqDAw}+mhOos?^^ai3&Eu5Du6RuD)X{)^03NDKEKMg|RJcUA*a(slwR$W0}1I0?4 znt0V5(Et8Bze&Q17d7yTjX@lH-Iwh);O{F_+jzFtM9)g_)CeIakMnhctoh}lZ*e?{ z=^J_Be9aIEdHMv|2(vVXnbq|m?)`KUGHUG&)oYGzy79@+iS)~txmi@17Y6qmcIp7w z>;7mV2jLmxW-b5e>bg4h;s0Q;!3U30*pV-P#A&}gF|;m>s~haT0X0@OFnnkt#&gYTh(oa7_#Zr`&A7D3V9CmH);00C#FMT zMI9J94|{C4HqZU$7gjaK2k+tGe4adGHb-pC47%9ryCd%5$@X z2XIzNCjKKwEG~fId#_78!GHaGlU3+>gwa=0)W+G>@=#`jniy(G+_2B}Rv>S}C@a0m0u%MJC!*Rgnj$Z*uveq^hOuwH z^=f_D3$rRIN7o3QSb7{@HL9|;976pE9S<`|u11e)rhn}N_uh$x%r#>J>-~U+dXR4ka^hG#X>wilw}>w zCi;AhY?q%mVr4sCeC-K*-$5Vj#j3z>|AM;0p5DlI*8vz`*Xu9m*6p!$;-urG24ahB z&Gb=cIdKKI|169TV&0y`XK)ExQng_2o;f1<(~?dIypS`kcGd|z&U5P(ctW2(gs>Q* zSn~#$a2~&z<8LiZ<-HgpdzHCwTo9T+5Dm|ULl+JPHvd3S6X&drWo-;fI%=mMv0Mlf zIz~}UBhb~_nESW_Zwzr+f$To@Cb;pE8e85eSlV~*wKxvFrv*e&e4j1vuyJKHH<SoazK|Do@L$fDvadye`vWhHGZ=AOWGR>9D-uRr9*H$)%X@0I|SGOhN8s_?Nv)yoidN9e^g$QeqFxv;$cvU}Cr<}aAMN1`PtPWiM;39K#eScVR zfvYX^b3dI1`?&;?H>Q17(W86c4>bI7sxCD2c--TjApls-^7yiI-Pt|M@R} zh`>9W{K*UZSo^`8G*WxrIoiaaO@7f?+owPbd#Hl*Hv@3A zrmP(HNJ6#*aAV$_Neal2@@OemAFf9yAY$eL#~^UQ;CtxGiECsXGdy$DJxL$qi>3Aq zVxPK$cmKp4MoqnvRg&|?Pn(s)1pr@x=HSGOd)U63ejpFun+NQ)m2Y{gOLB6uh-vpL zn`jX{L>Q%lrTQwVov~z3E_57! zV^?FY5&Gz#!pF_?Co$_9b{?<$0A{)gUW`PiZP}XCY7qZ9)DZ`HL7R&lu1#-g$jZ0B z0LWdAf92l;gsT5LdA|X2w~?p#TW2p}+4Yl`>dC`ckM?}z8?Uqf^aTzyB;Gu7RL`|8 zx%t45e_8+?^t5wpBV!B*mw`74>u2eUYsFQk2Q#AD-s{wrPvYKiNh zK5U&^!j;j@0kNsI$u4^MCqlO60csz!@Ol`9_wd+{>0kcWodo3m%U?goLF^$L=^hJF z#mW$#J>CFf>*D}i_U6A?LwJ2bT0bUw$JrWLCx#RvSmVCYzv)mDdG_KG_u`vB28&p5 zd3&8ao5ttV2LIGi4c`whOkjQTA}joV@n+aB>aGTKx9+Uaf5TD&BJQyxkG&)FvyLGF zaPub{_YZF@nDygsw-ORP*WdY+@GIq$ruy1DQvB|h7p{69R&!d5`R|1OJMihZ1dI4Q zPw-ra{_9)9_uhc+bakIG&&O+6=~BgRz75x+WRV=*L#B7ELWZ& zx$9ixr#21U@A?7XXx?)7`-|DsbE~GK*yP<^(^9Dujb)q+pxBg@8N0^-P-Qf>Yru6U zIt$aq1xIxhS00fUSnYBpkQ(~_xP2%7(UJ(%SjE|kHA!=^%z?P(Zaf@3S2^%!kQ1T1 zc5vGZlahK^yc7k zz+aa|1NvG=nDhM^8@wEhmz&)0cbfUpE3#U?*X8vSKt`c*|e_Lqs&!0t-XgDb?4Lo45(HtZX4@j8>2EBHOa{XpPgb0NbJkKG*3-3xo+;Zw}g$xD|W^DRhmCEcPS z!~@x5qa8jm5#^q(1n9Tg24l;gN5Kz(A=zJCb%O1QlLNYIrFl;~@aIq)!Lq1yN7e z&=U8sJQO_eKK27xgm04YA9FOk#XU@ytb4fH4CczT`LbM0rl+1qNA(c_2SYR`Y9H=q zayXnJJQ-QwA*?agQ`Yw)Kk>lE<(fcj@4^1l{yIWV9?q4wC!6Z(86E8z$nUTxmlqC} zvki%^r%z9Cz4%_;T)%up$ncvR_Ql3Ru1w;m!CaKqdRp0&^mys>Y+{=JX4cZ!51bs+eo(UNW zI3}m{y141AR$>|lx1yyuE-WW-I=erG`S7IimyaIoJL*xqOfOgKP>D5N)0|Y1$1@Ex zC)6=6FEb|hT(jvTH}*R+Wwh^C;v@Bmf$QQMNyv5GepG#bqpsL=y1af^LynBXy3WSX z$5BP???Gb1%6QVV2@kk`ssz$%Z6s@6EE|?MR#&Z@eW8JtT%)UHwO)m_!*PPYWx(fg zd8QwZhTQ*t%M1AhDuPXwP>1-;|NHO%3Sr-j5&)myNT!e24FBO?{w=xeVKSFFP`yN* z|Fmd54NngxXf1@xky%*yiFtXm(ej;RiPrYc46dn}!;zUZWp$DDt_D_YvxDuvF~pUq zJuWWK(w_u`r&~!VlElYBv?F^m!d)Dg*VmlvQPcd*%DVM+&n{nX&d%nJsVmc)!l--* zh<{H3Y~C7)ctf z{Ik&)r2XPv>F{2OtuwH)GW7#xa==Rje8qTO#Fn%DB+v0;&c;hZXt3rK!rGl@xw5Uz z#nBbRhG&kKtm~h8!%u4eJ-~b~+|N-7n@sW60`~CwEehyaUH+WLF{5whXUyDM+sBrM zC4=5(%3E~A)rH5#jIB7FXl&Kd2f@Xctav{p$r86YauRxxpw>%f`>XBCKP}NoPI~N2 z^o`i)5~+Lh94KnQFi3H3E$a60vv&?QL#?s(YuZ(Dy(!+`5)Qshr0NG; z$M2_lusm^nhQ$pEEH}P2kN=xG~7tS4#i@AOJ~3 zK~(c$CNF~w9!>k=@M)vgY%W)8DVVi6t_H2*&QF32QvKWanz1K_eZQZ*{lrEd!xh+A z&agfA(wU3nd@Bet)T;Hh&-UmQ`Fq0Gh~}HO^}^el>|21{W%fg}i+zk2oxN9=*!f0G zw%9Qr^^@a)yfl4I7fS^9UI`z~w`YQN*Ovsn!)l{V?Cs|gl?q4oU=I+pd3JGbuN}-E z15Wfbw~6F{xbpa(_G%}5b&P5p*k4~VP9re`-{RSyeR1SO&xSQuh$O`Eyz#I8H2K;@ z;y1--Rhpu1{b9%Mmbz&Kg#zO=oQ7po zpN+hi59b;r<^fMnRp`rUZev!5jE`vZfZxSyY(-$BzV$?{g5jnE3Ue*i;Jab24s+l7 z+z;OPt8+f;UY@=YK`Te=f$zx`Ue?tj#C5^lb5_I6Ta!d)^8MqW*L6((-4(`tdB9Cm zV}3dHU0to2ii*Mata+m4uMk1fKhFAzANKsO?0|3B4{8gxS{}!>_4qY6&i)-3RajR+ zwbxcAxU&y_J=h0+)<%2?hB2N%V_3X>-*19E7~C1CeedVZ0`#wY=bv5{9@0%d;s76eAZF2I^pVvcIDL$S#qMi*9pw(xb*76mJjC2 zCcYBTb-gI<<9qak@F^phr#G=b80^nJ=x;!L=v#k&*_MtsTluHl5pRYgV2X)}-w<5= zt2JhaVXO#e+&q2lRqXWYLoN1BUryiD@S!BvJ~3#U-#SS_QvR46(h?W<_U6$h0wwJ? zXnOgLXa61U&`i{6-~FmJC+3dEf)g73T{jjlx%pyvjyS%mS+n++llo#Wb$#cQFNutA zsIZrxYs#C{2BYb+h4Y(X_L6-23ZBs(F&9IDYQ_LD7``3=mfA^A*p8uljZyn=@4LRB zA_ts}f9c__&)*`+W;lO)N%p41ZV$q-zV11BTH&v6s@Rh=BlBBF{kVMZl&v4$J)fis z4?g!PgTC-3S-^6_QBJoxJ^!&7A^Hf_3pB$D>dQ_QHsE?-{oq8_^J z)|?U?2b`$mK6?i>@*7S8oqe_SLsf#OULvo1S0MX%`_XMgPn6>uZ8#eA)_6)EHRqqU zaZ=rC-W*mF^aGVJK;rcNut}cR)f$hy@bb1^>FyNo#L^c>v0Pg<>^j94i|H#OXTr5} zikD;=_gRB}Kgg@uGcmw+jaV-;0sG)@vcx{|>>BTUPOqsReEABG$Pn-0>U-SF^vywy zFFpa;W9v~;IkU7MmuG8Yf5sKVR4vtDJ?BFlJ;L_azqr1q_lF8F+Bh%p)v)~PJO=oz zB{AySj34d*n0}7y)mw%^#`AdLrT9I=m1A789IxEe*-nugUE{3!&prkpD+qQ#z#w;5 zofk=-aGwuueo)ExH~QFo-`<2jj`ipm|Hv6VcXOEKPQ4OIq_!rkiU}KAYv}Jx*5}(6 zz~j>V7z||hn6)(4>xD1du4^xugpH4`r){x#{k73wdISN`?s@&>z#bZ6-&~giyP%#M zfUUV+{5+jsd%Jd4)01xFkraa&@F7Pr7<+HvTRmBxR#q8bLV-00b?!@;=?g9D?uF%y z(YBB`>n~k3m234)6LL9vdZzZ%8i;Ew_ut}UbnJc`s*e|3jd7Lb-p737)dljP_`E&@ zW#IQUeCA=b>tof}$EOHR#V?oj0ZyI%P14NzaJa5o!+Q}y>tl<)`f99R0^i3ZehiO( zxMQ3)x7mAF#nrqTuKr%PX$Al%(4Ads{a7rn&rK#gbmJ}}g5law|Hsh!K8685GrImn zP}^R=0;P6v*qrg_cxx&llE-F$5P)FjjM{QPmlFFL`?OaeiXdY^sCNlJ_L7&UF>P*n z8!ZWB#$9lXFaP1>=R<>j3sEdM0^?hxrLbvDB4GG4ZUOK-&A6~{z-_vUAr7{+Edmk}2lnBnXCv>(Rg$^Z113yU7EW4@zQZz8e(A43oL zO8G_olg-@IQTWRb&w`ja$WVM`u&%bfDTbwCif8*X;P*wWIum~ymUlBju0!J-QuRaz zd-8f;n5;UhsVcub%~Ji??SJ^;YyQ(TrP{ms@+2|Ib(mJ|3(A}n*r%i1v6FK~V3qq~ zV<;=ot;*Xy0VQVN_+FppEB_mN`OI6MiVF8~XCeMDuH%DGNKPne&r`|nL7W3fJw2zL zAUTaL$wF}bvCj4x346w~G*`|0pG;e45uCe!f7`A5#<-SPq}adtE$EX7&V1Qh<`!&o zM1|_<6Wj&LO}~g}US6Jl$97@ol}EOH++b$|Hm93Ad4755Z2=E4i1%K;a3~vUta`9v z=7Y6o)Hofz22{X+;ND_8vtKC_TfUFTIv%Uo47!{r!vEsM(8%)eAr3Sqgd8g zjJfWwHY;|58rNO*R2O!1^e8LqGsM>q2re>{&uK>O)TliMRI>P7@BTYFV0zEw!P1fm zw?0VmGRoC@yQ8nW$MkYP9ge7_av?{opKAzL<5~-Au-=LP%RLAOJ*$G3yQ`Y#BewD2 zwR%rKX4n&G7V|8!Xs+|i3#>oGDGCg=@;zYpff)I~#Q&_#bf?$8-l1U};|+DkL zi8(iM{`I)vfud<#a>Te8wFbrhU-=jE#i5ZxH?mynez=z%24Rg6jutb1e8&MLAq@14 zpSWh~S>IvgikbJYTxZYjS!XxSBYycomKUVQB^rOZ%2PBwf;Ge7)U{e}e%`bM(lvc+ zCN!3g5e5Y)ldN3uXVaCgG$<}9doJrp1 zscm*J4?e8Dro7J}Kd(*6RgL_$z5d|ijd{ESX14zAS6hLe+aI{DyUB5@x5Ou&kQ0#= zE%Bf^*AjI8JO7h%2mv2DI@tU+(VWBd1I*S!B)^&Gn|}ZqhQ`qkq{@wb`dKsZ_@*?g z)wG_@PdVQ=u8qn$NyxKYC%UXL{@gc^Io6N6qU(8l^27~7oysQp>Cd!i{3-eW@z1xT z&zRJJ9bNWfVDrM!$JV~*YYDd3Z~glOLA(*%cU$xF@j(e}jPcy}70IhctP?9=(Ub*Qb+S=E;+#w z&xzR2n%=kn7(3JuySSLle~D&;iRn8#YRl$r=Rlz1O9VcXu+Oj!v3J`G#|8}82?pmrF%!KggW*O|hN-sE z5_$T*EafR4`{_o@X)7UON(zQ?X$rLQ_!k7C>sipvAG25&Pt5r%ZCIW<^G1m$>x(u9 zDw1);vHX}X1o{EsI= z+yhSur)TKZV96Tc2am=_Cz3GM5e}i*@+MfY2CF)!)|_hNp2%u}FuQIJgWHqfK|2ADdr=S@_< z@tvNFrDg}k!sC3ln}Dt_)G1PEgV9Tv^~meVYhk3Ehc(A9Kf9-Yef4X`7u8ty?HP{) zY`Y%yQ|5FUo4rbc#pgGA;=pC*FfTQ+=hbQY)^Uvbl^6&XS6cRbbT=A zS*uZR>x`XyiYQx`MejWWs#8u7sKf8nHG5Vcn+_BxWsdGop88DMlbl{Z^EbwdlFaK- z(mStzSMwvFbpfj9-D4}y#bCHWcx!T{RV}ROjfdvreR1@&y9mGHdN+_8FJ>^H9?02x zPh-dLmqN1Q!|IIYh6Rn`s8Bq9F%!+y42fltJ+U3h!=5kggxp_WJp|JpzXk`}bnx|i zvk799K16)EYvM9JIhzloHNa0K@p&!=W*B=G_bZFjjB=i`{n+`#8M7SDXzw{hkJ9I6Uo}woiN<&**h1c7kQd32V>eeB?C# zgnqSMq5At_O!f7znZXm*XqlYN1i<5UWDfUxt#IC9&eLnq*&ns-@vEuV7bqmNZ;m=W zWo*T*wi>>@R^Qbu9*v2{WaRB7BwOl`EB57U8K?0u;D>`pYz+SPNSOC>!QbS~^QgIK z9E%*}7qk4?zglH}@;87p!HoLfxqbijTrTlM61zB!ey{Tbsnfz=_eNstZoLXE{DSa* z3>4zX%z8&`*GX-C96^5YOh)F!_YHEcXHwp8i1Fbcv3~F$3~QMu!aXTud&$FYs@Mx{ zkH~`^{9^n~4q5LtCM z`2GHRYiKyALV0Z5KXT>4zxn_^AAlVG(SZnU)$B4(%yZ4AC0y1+jdi&9=LHC1ByhRy zPxk0(9)dKeQyhha?9mh;CKMT$2tOc-txQ;#KP$rPl6tw_VE@~YsGR!Rpa0DF%yGf- z{lQ4DVXp-tWym6lU)^j~Vo$JdPt18(c91NC^TU@FUmf%z>%(DNF6O@8g0*<_<@DBH z7S8fye&fIWuD0p}OhnTXckE9s@bAEPaAV+a1#kb<9|!jW*YSB@XO6KO-qZ1aEI&(i zk|?8f(dFK=>lJPe*-xOFje52DSc@j&iQN~G_IEi*mz#XOUCs8#O|KR4KBoe|Txx;e zgEWi2>nn~En0NH`|J6YZ5@F>J`~2zSB8v>pu7a~3jMpcYjP`(D1zBN$FECShpBnSJ zZ37682kB17!vUxgK64Cb^ZmexfF=j>|IcxnnO+#z=p@JS+AAN!g}uIiNKz9_?^wOB z<#*NhXz2d66Zc-r*7Y~8U}oElVy>IdjiJn%Cmx#83^q1p^vUyUI{`6?{agr30(wVp zfNsjyqKkW*DPvtOiD2*s;wczEo9nP}iodT0lv?q>|pUq%6m^;MQkXQ8>+exa<6Bj`2@Q zYB8Quoqzl0LEocA=+6^-@P;kR$T!27FY7Q#*Kz;UR+s&lK61UWE#}Kw+*tzt;lufY z6HEdQ?p8*x&F&wub=W8R*zp+MN@*^nHm7-?>y&$Hq2%y`6bxT|UL|cWHrtS^=P|yy zvD5sfp`U%TQ9Uj7(Lva!-_vQ0*mLOi6l@*Mnpk?yhnBksM2P*~S8$l!XgOJ-GvoWg zWS$kCiWUQ^(Ef)8_S?rJzrn!^hjH^Cg0L=c7Nl}uzvXLu{KXraMEf4uNZ9hVcZ_36 z2vO5GEcyCBp5rYqA-+(v2vm>*R{BEks!a>)9IP4RKPD{N>*K?T?)BfvYj_?~j)M}& z)Slfv766(f+6ezsm8=+QObl~Q5cy&Pj<0ScsAAZLi;qeE)g#UPpSk@2ozYaz&08~P ze&EtUk1*Up$3hN&M>DDm!!&{;634*y8xZkF;G8=1LiDjg0}uFvvCqZLZ+UVxxt1?2 zjK%Usgn-2P7-t2J7#5ti*HRl?k5&Q3993cu8`q;LT=FUu2ONF;=XYUZ_>O?_D=iD? z?l?)!=e-Z{#TxM8!F4wEio3o=$D=I-_5pwFPyJHe{b-UUB6d07eDHn8(}$QVgZSeB z%YlO_UzyZ&V5tUCe=VNHCm=t@$a}K;pU@|-lyXTgiD3`J2dX4+v+Et~w4ZVA@slfOkzKod_F^8ys+%=g zW1==wX6^OExKZluoO_Mq$e$NVOpmdzF81J5;hg5Du>HtdZ7 zklpe%GtOe_gY#lU*1zS8sXnjcX@H$%clF%%c{*lvP+t!23p5B8U6JZ~xK=(0i z^vw(;GiT8pwbYA;752WiJN0p3`wsZ8GW(-jZde*eOv9R!HF4uw&p=gHa9Q|E z1SBIKyvs6a%)YUJ7^@dddYC2>_deH2D^fDjboZW3l2hW~CaW0t(we%kPwg**)NI?A z^2ou7gWxQ74cSw-wLd^G*l@V&S}XF(TTG3@E<0;53?}yV-#vgU4m2$H>Lq4BC>jU0 z4K=wbKg;1Z5Bts^1r_qRj6tIf`1_153GbE!mFUf)Ot#PB2xH(P zLEMW)b5K$q;O;RzqtCG`!f zOZu1?Vm~QeKslT{hi=R0Ab0i7yrflfbGV8ooDUC&U z=sh82d8^pe@o~RAS`cGhcz(t8T%URVH&Odv|9^ia>r&I%+r3ek#Vv`(r%h>=pqA7ay0n{ z3GQ#6ek+obZ?L_0k{eCj@0n#fI_ii~8xR`NFQC=a4>|xgs4>X+`+oJnWJ*)#6m0S3 zL9=;EQE*+zv9@xwI;5e;-wdW_@=9CY#QFmW`4V%r5;xrl@_7o=)wVlqGIZ&KgCzX9 zHDLg(#RVI-8P`|vHZps4GV^AZu%9dhovpEr(|kGZKEF8PpRcUHA9{_N4CXoQ$A+jZ zIY3YmramsboAk%X7&J?Bp6uyc73}*#h*1}pU{hpnk~-1LD)a7J9pAi(BcCP;%J;Fq z@^2~n@+wA5jP~KyCd2kWzSuTLT)PH}Fz)mahVC7xg7^o+rZ%+@6U@Gd6=6)@Fag;x zx3mF3Af9Ljxq008zWDN+BY6^WSX*F>(GbA3Ia^=IC*XN8ZEVo;G-va2HqiL?Cng6O zuO~OJe(Yrd1FW^T#@I@J7y-g?EW3Kox;z(VFa9TgUEVx1P7BS6jm!@R`_llW_ih(J z1^QtFp$Xyu1x|lrjdxQ#7#^Ptr}H&}O;(xe%Yr{KAddm!XOJU?=6+|a4e2%hen25t zPx6(&S}%<$%J3$(u8)A`L@)ZT=0uL8HlERQ&+s~N4)kVIWf&ptA?M-V-o-~#4x>9K zupInvthmX<`uWWhBI?MB5!}VOR#0`wCI8`wBZ6c()(fsKE_sb-oue?ZeSCmq7}4)B zkj3b9F=8&4!SI-Gxb~L7Ncy?h9TlTq&0?tTuV0i5r6A8t6# z=S(t>+V5vpr$v$W2leVbEni)$W42x&B>bUYhIXLm7eftDdmw0^Hpjp<>1!3slGl2u z*_bNO7L(7L*N;f}89)2*su=`{nLPJ|3FdPpdF=KMl+J2P!td*c#(bGxQmxY__i+ii zYje6VwU^DMkSxEoE8`Ieez_F84VfNapZ7lZ&QOW<{?DEq-m&W+b%$JA{H75@aCR6z zDeWs_7Ek_ssU0|Y#>Iu|8PmG!$)53EtJ0Rfiok+u-t&|11eoTMFqQF@XDsn1>cW`B zt2L+T$pX|f?2ERq{kwjk??S%n#R$jGK;zk3Q|~cyH$8I?;=KPdb8UG)!T0g}Cm=7N zK|oJf^0mNNVhYl)kK3gp0&MXo@=vj0?d664Gr+y+bnv1WPRy^SY{1^}7G7TUC17@l zjFp}XzwD>)0gSgis|A#Ay7rq8;4`dI%`}7y&x^q8VfK8n=Xi5wiw^NH-wne`k>ER6 z5yxnn^R4cBJd?kf$dw_Ni`eTqDpERbvQO>R`cf^m^@S|~><}rK#$BwKmE2om*dP z!N2?#CqHj0u&0LHkS&4O=*r|zA~KV!Z$N@PlI4GMTpUGT%*p-RG(Lgq*omKlyuTSi zs)s+Mrxb8&L8Lnq-r5P#>TZtsPq(!OQr^a|FZN;ug1I1TW%ob-edO=t3*u)>56ec( z`1Sz@a`mdaJ#HxXBsCzrx^vcbpE)2nmfB1a99+}*%;0#li1)M*#^cF=@#mEUeCnKj zGzdEA`KRfH^@C6!f5yb4v??N+l{GofAI*78&mL=_!J_kcR!1>skFSu{+&-XvYB#?< zlKXR#Pr3vp@R8e<8hX8&OALA8Fn?D5d9RhB5>bXoG1ZkyQY-ZUu`V3!0!2Y1viA=@ z+-!2tms*~0`s)diQU&1iHDP)D^5IUO6S3ojs=S@biLKoAEfco-(9A9#@0(A}Xd*29 z#c&;{Mq?skMLYRTT074r;!Ig?sUFO$75j$n+rYUz6%hx39OeWU>X*H1@^%HFdfB6? zzg31=VO9YOj@7m5s2Mx+eAbEWGiIwMBdReN;i`tP(uY=4U6hr=C4h#_aM(9eDLx-=UD?0<$r23ym8GnzSB`xj9ENuwzppp z{LlxqoWj2`_>VRP9UaDeAK~8pB*04O>d&$p^2F%%dUET{Y2?JdU#(R(C3;^zy2x}# z`1q~bmeS{geAJ=0*RCAZnizne@tX%M_j;yBr6n1?_Y224w%d$tc?qSYVA z1oY#_c@^n(E9WnZgt*=mt4;$AGPdRzJy_~7W;FIv^7uM`&-<+&`*S>yjbH2&M*?!h znuBZc=sQw;*84*M+<=_x;QI8sWq~b$yH54Rel|kE6uG;m37>w6fLriy0i`m4epNF{ zPD>IRm6MiH1n3*&`Gb1yzlNF*-L=2hZ+s-}y=VHIzz|%5#xy*Iibt{NiXbrxfua)a)3$wT1xL3Z}*VyWX9)bwor{}=sX z0@{O1P`|(Thf{EHwU(LLBVZq^RQ+7!*58_{9gKCtkwDfRT*LOWJ^lJl%ORO|4I14$ zY1a(DIsc*H)dc3j-!*w`C+Q!;)ksgCSa>in|JgMqf3*MrAOJ~3K~!+9eGU7vNW3Fsa6guuhsb6K{oNa>%TvwdC7^R=`u%U{pk zKNXV<@6Z#_FT?y%cHplcoBYfN-Mq-%FWW;it~ctnt=Gdp^X2z_vm7!#?sxI_(2z9r zZ@r0t-3!yhoX7B=@};0k6)GH4wje#87+QEwh{2SL#iWfZRG-$@I+2Tef*MQ_ej(%D zuv3ID44dPto%MGQi6!I-f+wnPeiNKDK^V5bHF)y>`C?|kT{Ljz&Y5q(MEN47eSub8 zNJhuq3GFN198R2we#b%o(Pm6M)K-eB@V6^UB^xz#oIZWEO*cJQ|8PU;px^&+w9oZ{ zdof*8#k7~`&h+K;g3q`XZ+_xP{pN>{1cdpLTSWa=LjctS_Q1G^$sP-%9`;<<7h4t( zZ>Lyy+&yqCjcuWuLEmZr-&a-qeSoX!lJ z{r%?2nm@~_fT>IzkIk~gb8%ug9ggHBIi5s}??U|G&voA@y39$2D}Ww2m|SvAx@^7G z`K?_q^(|H%u=I)@V}IZBwCrNQ1BwsWe6#%g<4Vmt9JQG9@|f-JWBOg5BM)|ItR}M_@RidHy777F{R0}y}?i|xcNcJ4K&`8H-f}cQh1 z`qsTp$}&zjf)l0g!>emdaQDvxCxXknT&2L!`d&OXud3MM-|IR35RVHR8#{Xi_=YpK zMn1?EqN+Du3>+EjK(7ae>yP|w*|*0%U+(M4rALm#zvi3k+20Bqdvp8atqA>J>peYa zJ}pP_Txa+Avml+<|L}|HhqMPe71@ccUN$+9&D^Ce><(A&E&91PB2Qg?zzll4kN5UV z0n@%3veNpCH{93p(^K>W(tFy~lVb~FmA)8tqW21+kHL_U(eusNb1a^f*O=Ezmw*`? zxMvXa(BI4hT`Vb8wS+aD%2o-26F zfMb1ST#n+}|K^oGAT^vWV#eq?AYAm!{MnSF0jz!A07F3j^fgsKz}asu!|FB9+_IVl zPF94PdCA$5wP}9x>nh0wzBr>2Q`V4)%N!z>ece&+eK1Q3@ReUKWpMH+n`=TdthGze z;wioXT^-5yy0u@!jXwMlHywNafEbEhO9}up9nG07h4BZ&HLN+Qg$SdF%;>uI)l6)@ zr+h{tMXoQnh-sX=2fYsymmB?!e&nc6CGo8v-JkB`A1Qgvja}nYt#zs;o>_q`XV(VZ zsSNAUr%g}%A=*Q@k{{9LIW6d(_U8Sb-|SnV{~5P5K5OX7U>Y)UZ58qMXR#laVq%yM zW4!RkHNwp1{|N?jiOIbLU!5}^Cm@z@zEpQP3cY~gkJla;NRpA3%l&jd8d$WyQW5gu z+>%+gESPtFj6NGBPa}45EN-lp&9iS_Ed6^i-$o9(YsUW1zPuRffQRR1zpq7*kG3VE z|MbITnGx(Yxt#7bjMQ}BjREqNDEkosgMo57&U%u}X623pi-t^Yo#-iUE;K?w`oY_1%yA~&=+~2w$ zA1;raj?*0WqGGEfxB*yZJT+54;l{^mPm008-X!03Eapku0PjOX-UWzncBN=5Ozi9l+|GaH&^4&4-oM&+Jo#h;m%mz&T!RE zj8z&+#5)FoX>YXKU-skd2M|VK?BZv;+;2~YL++~u9G-dY32(YG#IcBTe2+aG)}VnO zBD|xF*(Z(nWe8@2=dJzCOSHL3MnAVETa**E1h@{0AQ(;~e1HYTm!sX=idmDfeAFl~@+xRs*qLv4?Bl znnzsnw-3Gk#Eh@lT;uQe9qN8d~kHSEzqLfGpipTMK)p`t8<|Cyhl5bUFckLYpyg-I} zwf3=2KE{Kg7pmy&Ij4RA4pEtWGyKVowH!n+lKYZYMXg*t5%&CS;tq@{zK3rlh1vrp z3hVWtUwl{t?}tS!BB=SvDWAK;-ujcr9wkZdsdCpwjm_Z9^29jh)NLHWfdS67eCA}m z4v4X=Qkst?29$JSfxG#@Hr?gXz4MPSd^W9a)^#Y(!Q2P-(|<;U&o$IK2=CD0>DH=kD-WM95@_Th)8 z5*On9_}OLCel@zOe-QCo=8FYXpp)B(cfR*OC+^9I7gqZ~PCcA1j$reAi0i(`Tae^9 z_wxha>yNp?XHd_&nSToV=;Xo37Xd`MUJ^(g5W&$m zz_Zr%##kdUcT_n0-XM<#=n!@~s`j+TzM_8k6C>^#S*=09kr+}kzL;7t#_Ey(aWFSA zA=4tkaTZ4#i${mYcoS(Ja~`{7mlrRw`J#{Jz<z>80vf*<`rHo)@HT-Gk4v9I zJRRoAk_Gl1v(u(ezN`7C*XX8~2{kLV+%cUm8xM~3DnK&HEI&_l=tnu$_%D~6oy=j> zVdEfX`3>!TgCM`)!GB*_nw$Jxu*zXR*66DS?ch&~F~n`0fytf(Yv|%fb9iyAFK)G7 z$xoj4;eo*}gJ-!RD9L)g4?ol)n$vyK&x(HdSUeTs`C=-Ry@ymZNX?g9ao9>A@%?t-H6I~YWeKJT{YsvmVGRT zdGo>f?wT7`Y_(6%@&Nzh`oTv&{K)+@ZS!-F^*$p_pBL!`Tw+YoJn*jiOZ~ifnMUe( zd%>=Cd7UJPAL7+iYHMMszWZ$nfXiq6{N|`e6fvgR&Fzg(9yRm=>$bMO1AF#Q9E<2{ zpH3@=|IJ_JpJqQuiZ)&)pipiwWs#rd@GgrzgcCWqHVypT?*)CKf2)C-7^JwD1&6`* zUS~n*ipEA@snA~Gv&IBhyVbL?Pn3!5vkYyxLH!<4&NoNXl#>W*1r7piju_S}qXj27 zI&$LLtC7GP&vm&(dd@F8>sQ0YNjQe~xz`L;jQe0P+1Q^9R%$K_XEb#%oAz>dA58Yv z^qH%)yU|$;_5@%&!8_;Sl z_=;w?-n_i4=^+bG?=iT!PMnG5bwNjMu32WR58!(2H&r*sta? zpV-#Y*L!{bT^h1H&&^`FcbkB$EKz0c4?BLelP7bIQ;x&I^({|hF^UD5IQ_A`aVT#_ zS8z8dalH?!0b_>FXOa&y-232bImiOMZ}dSC*Do?Us9`xVvfj0h?=QYzn3I`ne37wr z)jCgB>ubQIYLnW4fWcxb$MWZ=#2DdE=Cu7}nXG z>ny!8pvdRA3EYdVYv%`h7(x~fX8Kt(rk5Xt`GLiwqrDRF^sM(WJ%iI9p3#WYwRt^# zRDQ%Z-*G2DIiGtcd#^J6{SYw;? zqpE0Qvs{f|EC_TmUiQ|5t-rCIMIo3ny5Zirl;J*)I?2^z*m}R`0?c{6lZ(I@)tVXq zj>~W*V0%zFf*rkQ#@#MsQd2e#ddi5;(w70E#k2UCQpIjq?BSGNN>UDS{mR8Gr zo5{uC`M`cKXW!m5L3w8r?x~?UlbbJq8Pmd=@6(q;%*2qF<7o=Z8?fw{jT!zO+p9BR z<{c;F4L!s3$!<*ai3-5rZgPIqsq#lB$}Bda!16`${9z4``@*_MH~@!iEhILZfGI~Y z-H6VT-4kkjY)?>J6n6#o&Ch{0oo^U;*F?D4n?p{WGf+)(^+m9_!>BWBc@k3Y({Xx+ z2dm!Lo#9Xu2vV8dwOIBBx6a0}uDLP79c^fbyD9s zoU=cTi(#$^0312<8;ZxF;ZD@ypfBTLj(yQjuVR1kFZ{uk$Qnx;jQwU;R66e$yZSB} z5cBgG!`ldqZ`XN&^nM7ie*O7xi-fq*kJw|ApEVk&FMbPzd|!V2$_a6H-q6!|+%1L`i*Gn#Ne!EvVXr6K}Tm zitN@&B1R1QaWLTP)a~bmPo9wy%XR1rBC%~?IC>qLEr`*dp7o8~y`P{54cqnMxXjP` z_K#oE5XatYRm^(Yq~)tWkwEy$j3ka153}A(>G$;n%gKKvjIY94Ak?$&jfw=u5uGiN(**ZX% zYnFQkw60g~=?X|2xw~4})n*s2sJf<$9l5Qk-TK5dQ|mrlFt#9h`0mz`o|3}Rg};$3 z_En3U?l*UPpxh8H{;A*-)~iU3jTzQV;BqkS!-4CD-%r7SUrun?eqJ-g@M7Y;@$C^o z#2W0WS5$2Y#>Zz`#gL(#yRINyn~M*Lc+-up;>tUnhJQ}5$}Ei4dQfYjy)Mm-1^wUq zf&9F*Do&-mN-FvKucIOL5H4(^R|-8K9YKWp+)GXV(u?f?4Rd~mADm?yJb z!pVTxn~l)=y0Z6&rkeT1{W>r+~}n4~EbgP^FcV+WM)MJU z`G*+|`yc;l!`ACleeO+HQe({D{>mGdraYjZcPs_Aft6X5E%$_2if$r>-JbOXidjL| zq89VsJjO=BS%^+NANagQy1ybjg_L65fbT&pnoJcxgExWNqPw|cr{Wv6b<_2H29!&} zEc4-pxqy$~P#y&3K++m5_Hm%@;;_*#j)fi0s({Pg0H$&>DL z%Z~Wwfyl#=J;Ghq$9Z0Zk{F0qpm(ho1NLVC2In=nYsvb6QW?;dsk1NMEP93OgfF!o zMg*Gfi@wr_J{IsMC}8;fXb7zLIyCgYu_Qq%;Pug*1UtH8+w6&bJgw`t?^MF^d2O3~JuEq~lF|VO3NYIlhqkU|()Byi{oxvx< zy!5^KrepEO694dGpevf+0i0L^2*9;?oY@@495}O=TgR^H%_Udwqv>qW_$K-DT!yXn z3d)-A{dum-(cT3C$c)XweT1)4;;fkC!AFSE#;%{R@Wyoc3`-NCKZ?r*(h}^e{do4e zJQ*3M&&=Z;*y{}f9dK1uk{!KW`|B@r~s;fB9Mq zoQ%$_+ii_(oF2Q$VHr3x$Vpv((3Cyvz8>gW5V%`BJ&1w)&wl<- z9)d_i>~sP1^=!rKRL|}ALla`)_BY7l`|B^BE5nwxA4HZEItLuP|6kh`8N*i2pWHZh z525kx`XaD&{nSeC3?B1-AQCrsDX9KtY$CEb*e&!DW?j~I%y{g7g4A-ns|PN~k=>)r z=&9o71H{Wx@E!?kZC69W;WzT9HjZayc65-s+ zvP=M)!ExYyj}MX1-#tXeBIYxm$D|_RBggtlP>xaIc(#46d7Xgbe>4{R5^ZmB7{n1RKHqK0iZdDB`4Nj_YvT9^}e#(}QbFd0yWIW!kWQ<6&#ANB=F6X%Z+*{b3 zpLrbA!(dPB)16!I`8uy_4heW```ul|%O%-^Ap=LRjR2F!hM6JuxY+L-L;-P#V2BVaQN(IV(kG*d6B&+7df>@>(i_N-*$Zj z6QTGM@KjPQ3;G)$dXAhKKmG3uLM>Q5imR^iJw2SLAgy|hxM(>Z3{2&gv(1aGqrW9d zZP$aCE$x0y#u^f6tkR0DOx9zn77c<~@dY7upYh<|UDvx-***0nDr-16o@@Cp{>R29 zE^l{Yqgz|z>{saQv;7Ee-?)t! zVb{?&yPD4r#<;JhTa)jW)rS7oh+e)N~!lnjl_8t() zsWDmIxWJ`kdutYcC1$-*C;Py>78efL6NbKQo;t=2D1*K^2L!Yf*m8_dM|u7~y3Q=g zjwD&Gi5XQrJfR48!w)Wm`~Riz@pX3%01DJ|)XY5+)%3)TaJNBC&D>+m3qa#+PL1@# zu>Npt|53YfdQ{dN4ktV^x2M#`otIWbvc|igIBOhV>yfpHo2JL7Iw)ETl5o%4}L4D6LLU**_uw!{a6 z@Z|I%Z|cEMfvw&8#H4L74C$trc{CinjQN?^`p1(P$lA8jGfKSGvA2)Ee7e7WD~Bhx z6F+{TZ7Tmra)s;jE90K^Px9b z$R+^T=Fo)G9sAJ}>*<%Jmf7Hcx$k2-FBx#%+MJrANFwV=W%P$8fku3(1`&I#Ecn^=2~I<0|5TULP!6Mx3iKNFPF6^Lg$Je==llox*uH}@!f~!&8=Q{t6!6o-)f2n_crHu zmD!`|{1GQsevJeLe9EcLc>$f8d5cP%{OMl-Cl7%%gWs-)jkWuz{!Jssk2Q}Op!{)9 z06yIzQ!*my4BwqkEOu=zCv;w0vPZu)mcJX*t_=|)TVHd)kJuA;38CYI{N~!)$pOVU z?aNZ*lTPG|ZF)Nf%K+t|4*Pr(D#unn*_dzojaw}AG;C>S?TljcMmMzR9f$RU!P<&3 z+=#U?Ac?&=@;EU1A{QfjXh{v9#fpr!<3l4nzFvDg3#DhaBXn}4t?rSwR`hK5EVOpBzNZ!^W(M7anN>x6Iv{c|6Ln9RHYdp zWcir77lV!wgI$-d(THpnG5WlAI5(0IhJV42k}JT?pjqdxj(FS zy*9UHeo>TI*XAZJIieVFI4urB=L>y4)|xI3BNHQIV3jMVx|qybe~g1)n)l{Up8!As z9I>X4?Kb^mMV!lpxLDxb{_+53K$*Yvnpva(03ZNKL_t(|ZC8%#XPoiE9k~d^2PlpG z@>!X3glSuX-7B2Ua(K$<6GvXFAQ$BGX)?9JWaUPLV>�@ZrwxSO97vBsYf{{jfU( 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 z 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 z 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%0D_qOlSO~8l$4<98g-Mltb*5H+5 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 A|RLFM4O_ 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%0D6${Pcq+tJHh?RYA#{UyuGWFqa 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 N002ovPDHLkV1hI>{(S%d diff --git a/workbench.png b/workbench.png deleted file mode 100644 index 817b9d17445d7ab66329ade51e302536cda28385..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43713 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 From 337532d20ad74cfb2c1ba4ae594b4465f274a539 Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 3 Jul 2023 00:08:49 +0900 Subject: [PATCH 151/369] Revision 0.29.0 --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 1ff66fddc..1c1262c4b 100644 --- a/readme.md +++ b/readme.md @@ -888,7 +888,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { ### Not Types -Not types are supported with `Type.Not`. This type represents the JSON Schema `not` keyword and will statically infer as `unknown`. Note that negated (or not) types are not supported in TypeScript, but can still be partially expressed by interpreting `not` as the broad type `unknown`. When used with intersect types, the Not type can create refined assertion rules for types by leveraging TypeScript's ability to narrow from `unknown` to an intended type through intersection. +Not types are supported with `Type.Not`. This type represents the JSON Schema `not` keyword and will statically infer as `unknown`. Note that Not types are not supported in TypeScript, but can still be partially expressed by interpreting `not` as the broad type `unknown`. When used in intersections, the Not type can be used to create refined assertion rules for specific types, with the inference derived from TypeScript's ability to narrow from `unknown` to `T` via intersection. For example, consider a type which is `number` but not `1 | 2 | 3` and where the static type would still technically be a `number`. The following shows a pseudo TypeScript example using `not` followed by the TypeBox implementation. From bdeb24f15fe872d9da793cfd33820553f384f1b4 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 3 Jul 2023 03:52:38 +0900 Subject: [PATCH 152/369] Revision 0.29.1 (#485) --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 1 + src/typebox.ts | 32 ++++++++++++++++++++++++-------- test/runtime/type/extends/not.ts | 15 +++++++++++++++ test/static/not.ts | 15 +++++++++++++++ 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98d18eb2c..3cb6da902 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.29.0", + "version": "0.29.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.29.0", + "version": "0.29.1", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 6cc25a10f..cbdd26dc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.0", + "version": "0.29.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 1c1262c4b..7e7caaaac 100644 --- a/readme.md +++ b/readme.md @@ -1473,6 +1473,7 @@ The following is a list of community packages that provide general tooling and f | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from JSON schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | +| [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | diff --git a/src/typebox.ts b/src/typebox.ts index f5c04c6aa..b9acbdf05 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1540,7 +1540,7 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Not // -------------------------------------------------------------------------- - function ResolveNot(schema: T): TUnknown | TNot['not'] { + function UnwrapNot(schema: T): TUnknown | TNot['not'] { let [current, depth]: [TSchema, number] = [schema, 0] while (true) { if (!TypeGuard.TNot(current)) break @@ -1549,6 +1549,14 @@ export namespace TypeExtends { } return depth % 2 === 0 ? current : Type.Unknown() } + function Not(left: TSchema, right: TSchema) { + // TypeScript has no concept of negated types, and attempts to correctly check the negated + // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer + // the type. Instead we unwrap to either unknown or T and continue evaluating. + if (TypeGuard.TNot(left)) return Visit(UnwrapNot(left), right) + if (TypeGuard.TNot(right)) return Visit(left, UnwrapNot(right)) + throw new Error(`TypeExtends: Invalid fallthrough for Not`) + } // -------------------------------------------------------------------------- // Null // -------------------------------------------------------------------------- @@ -1764,6 +1772,17 @@ export namespace TypeExtends { return TypeGuard.TSymbol(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- + // TemplateLiteral + // -------------------------------------------------------------------------- + function TemplateLiteral(left: TSchema, right: TSchema) { + // TemplateLiteral types are resolved to either unions for finite expressions or string + // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for + // either type and continue evaluating. + if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) + if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) + throw new Error(`TypeExtends: Invalid fallthrough for TemplateLiteral`) + } + // -------------------------------------------------------------------------- // Tuple // -------------------------------------------------------------------------- function TupleRight(left: TSchema, right: TTuple) { @@ -1857,13 +1876,10 @@ export namespace TypeExtends { return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { - // Not Unwrap - if (TypeGuard.TNot(left)) return Visit(ResolveNot(left), right) - if (TypeGuard.TNot(right)) return Visit(left, ResolveNot(right)) - // Template Literal Union Unwrap - if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) - if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) - // Standard Extends + // Resolvable Types + if (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) return TemplateLiteral(left, right) + if (TypeGuard.TNot(left) || TypeGuard.TNot(right)) return Not(left, right) + // Standard Types if (TypeGuard.TAny(left)) return Any(left, right) if (TypeGuard.TArray(left)) return Array(left, right) if (TypeGuard.TBigInt(left)) return BigInt(left, right) diff --git a/test/runtime/type/extends/not.ts b/test/runtime/type/extends/not.ts index be5bbfa9b..e23e5352f 100644 --- a/test/runtime/type/extends/not.ts +++ b/test/runtime/type/extends/not.ts @@ -6,6 +6,21 @@ import { Assert } from '../../assert/index' // Note: Not is equivalent to Unknown with the exception of nested negation. // --------------------------------------------------------------------------- describe('type/extends/Not', () => { + // ------------------------------------------------------------------------- + // Issue: type T = number extends not number ? true : false // true + // type T = number extends unknown ? true : false // true + // + // TypeScript does not support type negation. The best TypeBox can do is + // treat "not" as "unknown". From this standpoint, the extends assignability + // check needs to return true for the following case to keep TypeBox aligned + // with TypeScript static inference. + // ------------------------------------------------------------------------- + it('Should extend with unknown assignability check', () => { + const A = Type.Number() + const B = Type.Not(Type.Number()) + const R = TypeExtends.Extends(A, B) + Assert.isEqual(R, TypeExtendsResult.True) // we would expect false + }) // --------------------------------------------------------------------------- // Nested // --------------------------------------------------------------------------- diff --git a/test/static/not.ts b/test/static/not.ts index aa8a74d87..de3abbf7d 100644 --- a/test/static/not.ts +++ b/test/static/not.ts @@ -1,6 +1,21 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' +{ + // ------------------------------------------------------------------------- + // Issue: type T = number extends not number ? true : false // true + // type T = number extends unknown ? true : false // true + // + // TypeScript does not support type negation. The best TypeBox can do is + // treat "not" as "unknown". From this standpoint, the extends assignability + // check needs to return true for the following case to keep TypeBox aligned + // with TypeScript static inference. + // ------------------------------------------------------------------------- + const A = Type.Number() + const B = Type.Not(Type.Number()) + const T = Type.Extends(A, B, Type.Literal(true), Type.Literal(false)) + Expect(T).ToInfer() +} { const T = Type.Not(Type.Number()) Expect(T).ToInfer() From 1504082b96fcc3702a8fabe23deb35e5507c3b9b Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 3 Jul 2023 05:09:23 +0900 Subject: [PATCH 153/369] Revision 0.29.1 --- changelog/0.29.0.md | 44 ++++++++++++++++++++++++++++++++++++++++---- readme.md | 20 +++++++++++--------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/changelog/0.29.0.md b/changelog/0.29.0.md index e4cca9f49..1291219d7 100644 --- a/changelog/0.29.0.md +++ b/changelog/0.29.0.md @@ -6,6 +6,15 @@ Revision 0.29.0 makes a minor interface and schema representation change to the As this revision constitutes a breaking representation change for `Type.Not`, a minor semver revision is required. +## Contents + +- Enhancements + - [Type.Not Representation Change](#Representation-Change) + - [Not Inversion](#Not-Inversion) + - [Inference Limitations](#Inference-Limitations) + + + ## Type.Not Representation Change The `Type.Not` was first introduced in Revision 0.26.0. This type accepted two arguments, the first is the `not` type, the second is the `allowed` type. In 0.26.0, TypeBox would treat the `allowed` type as the inferred type with the schema represented in the following form. @@ -81,11 +90,13 @@ const T = { // type T = Static // type T = number ``` -The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of the type intersection narrowing capabilities of TypeScript with respect to inference. +The 0.29.0 `Not` type properly represents the JSON Schema `not` keyword in its simplest form, as well as making better use of intersection type narrowing capabilities of TypeScript. -### Invert Not + -In revision 0.29.0, it is possible to invert the `Not` type. TypeBox will track each inversion and statically infer appropriately. +## Not Inversion + +The not type can be inverted through nesting. ```typescript // not not string @@ -105,4 +116,29 @@ const T = { // inferred as // type T = Static // type T = string -``` \ No newline at end of file +``` + + + +## Inference Limitations + +Not types are synonymous with the concept of [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. Because of this, it is not currently possible to infer negated types in a way one would naturally expect for some cases. Consider the following. + +```typescript +const T = Type.Intersect([Type.String(), Type.Not(Type.String())]) + +type T = Static // type T = string & not string + // actual: string + // expect: never +``` +As such, the use of Not types should be used with some consideration to current limitations, and reserved primarily for narrowing cases such as the following. + +```typescript +const T = Type.Intersect([Type.String(), Type.Not(Type.Literal('disallowed string'))]) + +type T = Static // type T = string & not 'disallowed string' + // actual: string + // expect: string +``` + + diff --git a/readme.md b/readme.md index 7e7caaaac..a39085005 100644 --- a/readme.md +++ b/readme.md @@ -888,14 +888,16 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { ### Not Types -Not types are supported with `Type.Not`. This type represents the JSON Schema `not` keyword and will statically infer as `unknown`. Note that Not types are not supported in TypeScript, but can still be partially expressed by interpreting `not` as the broad type `unknown`. When used in intersections, the Not type can be used to create refined assertion rules for specific types, with the inference derived from TypeScript's ability to narrow from `unknown` to `T` via intersection. - -For example, consider a type which is `number` but not `1 | 2 | 3` and where the static type would still technically be a `number`. The following shows a pseudo TypeScript example using `not` followed by the TypeBox implementation. +TypeBox has partial support for the JSON schema `not` keyword with `Type.Not`. This type is synonymous with the concept of a [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. TypeBox does provide partial inference support via the intersection of `T & not U` (where all negated types infer as `unknown`). This can be used in the following context. ```typescript -// Pseudo TypeScript +// TypeScript -type T = number & not (1 | 2 | 3) // allow all numbers except 1, 2, 3 +type T = Exclude // all numbers except 1, 2, 3 + // + // ideally expressed as: + // + // type T = number & not (1 | 2 | 3) // TypeBox @@ -914,11 +916,11 @@ const T = Type.Intersect([ // const T = { // ] // } -type T = Static // evaluates as: +type T = Static // inferred: // - // type T = (number & (not (1 | 2 | 3))) - // type T = (number & (unknown)) - // type T = (number) + // type T = number & not (1 | 2 | 3) + // type T = number & unknown + // type T = number ``` The Not type can be used with constraints to define schematics for types that would otherwise be difficult to express. From c8d8a701c7fe4d13d2d737cddf0ec99101f6160d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 4 Jul 2023 06:27:34 +0900 Subject: [PATCH 154/369] Revision 0.29.2 (#486) --- changelog/0.29.2.md | 141 +++++++ example/codegen/formatter.ts | 63 --- example/codegen/index.ts | 32 -- example/codegen/tsconfig.json | 4 - example/codegen/typebox-to-typescript.ts | 187 --------- example/codegen/typebox-to-zod.ts | 275 ------------- example/codegen/typescript-to-json-schema.ts | 360 ----------------- example/codegen/typescript-to-typebox.ts | 383 ------------------- package.json | 2 +- readme.md | 126 +++--- src/typebox.ts | 95 ++++- test/runtime/type/guard/composite.ts | 30 +- test/runtime/type/guard/indexed.ts | 38 ++ test/runtime/type/normalize/index.ts | 1 + test/runtime/type/normalize/indexed.ts | 140 +++++++ 15 files changed, 494 insertions(+), 1383 deletions(-) create mode 100644 changelog/0.29.2.md delete mode 100644 example/codegen/formatter.ts delete mode 100644 example/codegen/index.ts delete mode 100644 example/codegen/tsconfig.json delete mode 100644 example/codegen/typebox-to-typescript.ts delete mode 100644 example/codegen/typebox-to-zod.ts delete mode 100644 example/codegen/typescript-to-json-schema.ts delete mode 100644 example/codegen/typescript-to-typebox.ts create mode 100644 test/runtime/type/normalize/indexed.ts diff --git a/changelog/0.29.2.md b/changelog/0.29.2.md new file mode 100644 index 000000000..f87cfc217 --- /dev/null +++ b/changelog/0.29.2.md @@ -0,0 +1,141 @@ +## [0.29.2](https://www.npmjs.com/package/@sinclair/typebox/v/0.29.2) + +## Overview + +Revision 0.29.2 includes enhancements to `Type.Index` + +This revision contains no breaking changes + +## Contents + +- Enhancements + - [Modifier Index Resolver](#Modifier-Index-Resolver) + - [Indexed Intersect](#Indexed-Intersect) + - [Indexed Union](#Indexed-Union) + - [Composite](#Composite) + + + +## Modifier Index Resolver + +Revision 0.29.2 re-introduces optional property narrowing for Indexed Access Types. This functionality is specific when indexing overlapping properties with one or more optional modifiers. Revision 0.28.0 attempted to implement this narrowing, however was pulled due to instantiation issues raised on issue [419](https://github.com/sinclairzx81/typebox/issues/419) (specific to composite narrowing) + +Revision 0.29.2 attempts to re-introduce this functionality using a different resolution strategy. It uses Indexed Access Types as the construct in which to apply such narrowing and expands upon Union and Intersection type normalization for optional modifier unwrap and remapping. This approach unifies Composite inference with Index Access Types and makes provisions for a possible `Type.Mapped` feature in later releases. + + + +## Indexed Intersect + +The following are the index resolver cases for intersect types. + +### Case 1 +```typescript +const T = Type.Intersect([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TOptional> +``` +### Case 2 +```typescript +const T = Type.Intersect([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TUnion<[TNumber, TNumber]> +``` +### Case 3 +```typescript +const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TUnion<[TNumber, TNumber]> +``` + + +## Indexed Union + +The following are the index resolver cases for union types. + +### Case 1 +```typescript +const T = Type.Union([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TOptional> +``` +### Case 2 +```typescript +const T = Type.Union([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TOptional> +``` +### Case 3 +```typescript +const T = Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }) +]) + +const I = Type.Index(T, ['x']) + +// type I = TUnion<[TNumber, TNumber]> +``` + + +## Composite + +The following are the resolver cases for indexed types when applied to composite intersection. + +### Case 1 +```typescript +const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }) +]) + +// type T = TObject<{ +// x: TOptional> +// }> +``` +### Case 2 +```typescript +const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }) +]) + +// type T = TObject<{ +// x: TUnion<[TNumber, TNumber]> +// }> +``` +### Case 3 +```typescript +const T = Type.Composite([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }) +]) + +// type T = TObject<{ +// x: TUnion<[TNumber, TNumber]> +// }> +``` \ No newline at end of file diff --git a/example/codegen/formatter.ts b/example/codegen/formatter.ts deleted file mode 100644 index ffa6b58c2..000000000 --- a/example/codegen/formatter.ts +++ /dev/null @@ -1,63 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export namespace Formatter { - export function Includes(line: string, token: string) { - return line.includes(token) - } - function IntentBefore(line: string) { - if (Includes(line, '({') && Includes(line, '})')) return 0 - if (Includes(line, '([') && Includes(line, '])')) return 0 - if (Includes(line, '{') && Includes(line, '}')) return 0 - if (Includes(line, '])')) return -1 - if (Includes(line, '})')) return -1 - if (Includes(line, '}')) return -1 - if (Includes(line, ']')) return -1 - return 0 - } - function IndentAfter(line: string) { - if (Includes(line, '({') && Includes(line, '})')) return 0 - if (Includes(line, '([') && Includes(line, '])')) return 0 - if (Includes(line, '{') && Includes(line, '}')) return 0 - if (Includes(line, '([')) return 1 - if (Includes(line, '({')) return 1 - if (Includes(line, '{')) return 1 - if (Includes(line, '[')) return 1 - return 0 - } - export function Format(input: string): string { - const output: string[] = [] - let indent = 0 - for (const line of input.split('\n').map((n) => n.trim())) { - indent += IntentBefore(line) - output.push(`${''.padStart(indent * 2, ' ')}${line}`) - indent += IndentAfter(line) - } - return output.join('\n') - } -} diff --git a/example/codegen/index.ts b/example/codegen/index.ts deleted file mode 100644 index 0513ff9ca..000000000 --- a/example/codegen/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export { TypeBoxToTypeScript } from './typebox-to-typescript' -export { TypeBoxToZod } from './typebox-to-zod' -export { TypeScriptToJsonSchema } from './typescript-to-json-schema' -export { TypeScriptToTypeBox } from './typescript-to-typebox' diff --git a/example/codegen/tsconfig.json b/example/codegen/tsconfig.json deleted file mode 100644 index 6710ecd93..000000000 --- a/example/codegen/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "files": ["index.ts"] -} diff --git a/example/codegen/typebox-to-typescript.ts b/example/codegen/typebox-to-typescript.ts deleted file mode 100644 index f7f76713a..000000000 --- a/example/codegen/typebox-to-typescript.ts +++ /dev/null @@ -1,187 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Formatter } from './formatter' -import * as Types from '@sinclair/typebox' - -export namespace TypeBoxToTypeScript { - function Any(schema: Types.TAny) { - return 'any' - } - function Array(schema: Types.TArray) { - const items = Visit(schema.items) - return `Array<${items}>` - } - function Boolean(schema: Types.TBoolean) { - return 'boolean' - } - function Constructor(schema: Types.TConstructor) { - const params = schema.parameters.map((param) => Visit(param)).join(', ') - const returns = Visit(schema.returns) - return `new (${params}) => ${returns}` - } - function Function(schema: Types.TFunction) { - const params = schema.parameters.map((param) => Visit(param)).join(', ') - const returns = Visit(schema.returns) - return `(${params}) => ${returns}` - } - function Integer(schema: Types.TInteger) { - return 'number' - } - function Intersect(schema: Types.TIntersect) { - return `(${schema.allOf.map((schema) => Visit(schema)).join(' & ')})` - } - function Literal(schema: Types.TLiteral) { - if (typeof schema.const === 'string') { - return `'${schema.const}'` - } else { - return `${schema.const}` - } - } - function Never(schema: Types.TNever) { - return 'never' - } - function Null(schema: Types.TNull) { - return 'null' - } - function String(schema: Types.TString) { - return 'string' - } - function Number(schema: Types.TNumber) { - return 'number' - } - function Object(schema: Types.TObject) { - const properties: string = globalThis.Object.entries(schema.properties) - .map(([key, value]) => { - return `${key}: ${Visit(value)}` - }) - .join(',\n') - return `{\n${properties}\n}` - } - function Promise(schema: Types.TPromise) { - const item = Visit(schema.item) - return `Promise<${item}>` - } - function Record(schema: Types.TRecord) { - for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { - const type = Visit(value) - if (key === Types.PatternNumberExact) { - return `Record` - } else { - return `Record` - } - } - throw Error('TypeBoxToTypeScript: Unreachable') - } - function Ref(schema: Types.TRef) { - return schema.$ref - } - function This(schema: Types.TThis) { - return schema.$ref - } - function Tuple(schema: Types.TTuple) { - if (schema.items === undefined) return `[]` - const items = schema.items.map((schema) => Visit(schema)).join(', ') - return `[${items}]` - } - function UInt8Array(schema: Types.TUint8Array) { - return `Uint8Array` - } - function Undefined(schema: Types.TUndefined) { - return `undefined` - } - function Union(schema: Types.TUnion) { - return `${schema.anyOf.map((schema) => Visit(schema)).join(' | ')}` - } - function Unknown(schema: Types.TUnknown) { - return `unknown` - } - function Void(schema: Types.TVoid) { - return `void` - } - function Visit(schema: Types.TSchema): string { - if (Types.TypeGuard.TAny(schema)) { - return Any(schema) - } else if (Types.TypeGuard.TArray(schema)) { - return Array(schema) - } else if (Types.TypeGuard.TBoolean(schema)) { - return Boolean(schema) - } else if (Types.TypeGuard.TConstructor(schema)) { - return Constructor(schema) - } else if (Types.TypeGuard.TFunction(schema)) { - return Function(schema) - } else if (Types.TypeGuard.TInteger(schema)) { - return Integer(schema) - } else if (Types.TypeGuard.TIntersect(schema)) { - return Intersect(schema) - } else if (Types.TypeGuard.TLiteral(schema)) { - return Literal(schema) - } else if (Types.TypeGuard.TNever(schema)) { - return Never(schema) - } else if (Types.TypeGuard.TNull(schema)) { - return Null(schema) - } else if (Types.TypeGuard.TNumber(schema)) { - return Number(schema) - } else if (Types.TypeGuard.TObject(schema)) { - return Object(schema) - } else if (Types.TypeGuard.TPromise(schema)) { - return Promise(schema) - } else if (Types.TypeGuard.TRecord(schema)) { - return Record(schema) - } else if (Types.TypeGuard.TRef(schema)) { - return Ref(schema) - } else if (Types.TypeGuard.TString(schema)) { - return String(schema) - } else if (Types.TypeGuard.TThis(schema)) { - return This(schema) - } else if (Types.TypeGuard.TTuple(schema)) { - return Tuple(schema) - } else if (Types.TypeGuard.TUint8Array(schema)) { - return UInt8Array(schema) - } else if (Types.TypeGuard.TUndefined(schema)) { - return Undefined(schema) - } else if (Types.TypeGuard.TUnion(schema)) { - return Union(schema) - } else if (Types.TypeGuard.TUnknown(schema)) { - return Unknown(schema) - } else if (Types.TypeGuard.TVoid(schema)) { - return Void(schema) - } else { - throw Error('TypeBoxToTypeScript: Unknown type') - } - } - /** Generates TypeScript code from TypeBox types */ - export function Generate(schema: Types.TSchema, references: Types.TSchema[] = []) { - const result: string[] = [] - for (const reference of references) { - result.push(`type ${reference.$id} = ${[...Visit(reference)].join('')}`) - } - result.push(`type ${schema.$id || 'T'} = ${[...Visit(schema)].join('')}`) - return Formatter.Format(result.join('\n\n')) - } -} diff --git a/example/codegen/typebox-to-zod.ts b/example/codegen/typebox-to-zod.ts deleted file mode 100644 index bfcdc4b0c..000000000 --- a/example/codegen/typebox-to-zod.ts +++ /dev/null @@ -1,275 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Formatter } from './formatter' -import * as Types from '@sinclair/typebox' - -// -------------------------------------------------------------------------- -// Errors -// -------------------------------------------------------------------------- -export class TypeBoxToZodNonReferentialType extends Error { - constructor(message: string) { - super(`TypeBoxToZod: ${message}`) - } -} -export class TypeBoxToZodUnsupportedType extends Error { - constructor(message: string) { - super(`TypeBoxToZod: ${message}`) - } -} -// -------------------------------------------------------------------------- -// Transform -// -------------------------------------------------------------------------- -export interface ZodCodegenOptions { - imports: boolean - exports: boolean -} -export namespace TypeBoxToZod { - function Any(schema: Types.TAny) { - return `z.any()` - } - function Array(schema: Types.TArray) { - const items = Visit(schema.items) - return `z.array(${items})` - } - function Boolean(schema: Types.TBoolean) { - return `z.boolean()` - } - function Constructor(schema: Types.TConstructor): string { - throw new TypeBoxToZodUnsupportedType(`TConstructor`) - } - function Function(schema: Types.TFunction) { - const params = schema.parameters.map((param) => Visit(param)).join(`, `) - const returns = Visit(schema.returns) - return `z.function().args(${params}).returns(${returns})` - } - function Integer(schema: Types.TInteger) { - const buffer: string[] = [] - buffer.push(`z.number().int()`) - if (schema.minimum !== undefined) buffer.push(`.min(${schema.minimum})`) - if (schema.maximum !== undefined) buffer.push(`.max(${schema.maximum})`) - if (schema.exclusiveMaximum !== undefined) buffer.push(`.max(${schema.exclusiveMaximum - 1})`) - if (schema.exclusiveMinimum !== undefined) buffer.push(`.max(${schema.exclusiveMinimum + 1})`) - if (schema.multipleOf !== undefined) buffer.push(`.multipleOf(${schema.multipleOf})`) - return buffer.join(``) - } - function Intersect(schema: Types.TIntersect) { - // note: Zod only supports binary intersection. While correct, this is partially at odds with TypeScript's - // ability to distribute across (A & B & C). This code reduces intersection to binary ops. - function reduce(rest: Types.TSchema[]): string { - if (rest.length === 0) throw Error('Expected at least one intersect type') - if (rest.length === 1) return Visit(rest[0]) - const [left, right] = [rest[0], rest.slice(1)] - return `z.intersection(${Visit(left)}, ${reduce(right)})` - } - return reduce(schema.allOf) - } - function Literal(schema: Types.TLiteral) { - if (typeof schema.const === `string`) { - return `z.literal('${schema.const}')` - } else { - return `z.literal(${schema.const})` - } - } - function Never(schema: Types.TNever) { - return `z.never()` - } - function Null(schema: Types.TNull) { - return `z.null()` - } - function String(schema: Types.TString) { - const buffer: string[] = [] - buffer.push(`z.string()`) - if (schema.maxLength !== undefined) buffer.push(`.max(${schema.maxLength})`) - if (schema.minLength !== undefined) buffer.push(`.min(${schema.minLength})`) - return buffer.join(``) - } - function Number(schema: Types.TNumber) { - const buffer: string[] = [] - buffer.push(`z.number()`) - if (schema.minimum !== undefined) buffer.push(`.min(${schema.minimum})`) - if (schema.maximum !== undefined) buffer.push(`.max(${schema.maximum})`) - if (schema.exclusiveMaximum !== undefined) buffer.push(`.max(${schema.exclusiveMaximum - 1})`) - if (schema.exclusiveMinimum !== undefined) buffer.push(`.max(${schema.exclusiveMinimum + 1})`) - if (schema.multipleOf !== undefined) buffer.push(`.multipleOf(${schema.multipleOf})`) - return buffer.join(``) - } - function Object(schema: Types.TObject) { - const properties: string = globalThis.Object.entries(schema.properties) - .map(([key, value]) => { - const quoted = key.includes('-') || '1234567890'.includes(key.charAt(0)) - const property = quoted ? `'${key}'` : key - return [`Optional`, `ReadonlyOptional`].includes(value[Types.Modifier] as string) ? `${property}: ${Visit(value)}.optional()` : `${property}: ${Visit(value)}` - }) - .join(`,\n`) - const buffer: string[] = [] - buffer.push(`z.object({\n${properties}\n})`) - if (schema.additionalProperties === false) buffer.push(`.strict()`) - return buffer.join(``) - } - function Promise(schema: Types.TPromise) { - const item = Visit(schema.item) - return `${item}.promise()` - } - function Record(schema: Types.TRecord) { - for (const [key, value] of globalThis.Object.entries(schema.patternProperties)) { - const type = Visit(value) - if (key === `^(0|[1-9][0-9]*)$`) { - throw new TypeBoxToZodUnsupportedType(`TRecord`) - } else { - return `z.record(${type})` - } - } - throw Error(`TypeBoxToZod: Unreachable`) - } - function Ref(schema: Types.TRef) { - if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) - return schema.$ref - } - function This(schema: Types.TThis) { - if (!reference_map.has(schema.$ref!)) throw new TypeBoxToZodNonReferentialType(schema.$ref!) - recursive_set.add(schema.$ref) - return schema.$ref - } - function Tuple(schema: Types.TTuple) { - if (schema.items === undefined) return `[]` - const items = schema.items.map((schema) => Visit(schema)).join(`, `) - return `z.tuple([${items}])` - } - function UInt8Array(schema: Types.TUint8Array): string { - throw new TypeBoxToZodUnsupportedType(`TUint8Array`) - } - function Undefined(schema: Types.TUndefined) { - return `z.undefined()` - } - function Union(schema: Types.TUnion) { - return `z.union([${schema.anyOf.map((schema) => Visit(schema)).join(`, `)}])` - } - function Unknown(schema: Types.TUnknown) { - return `z.unknown()` - } - function Void(schema: Types.TVoid) { - return `z.void()` - } - function Visit(schema: Types.TSchema): string { - if (schema.$id !== undefined) reference_map.set(schema.$id, schema) - if (Types.TypeGuard.TAny(schema)) { - return Any(schema) - } else if (Types.TypeGuard.TArray(schema)) { - return Array(schema) - } else if (Types.TypeGuard.TBoolean(schema)) { - return Boolean(schema) - } else if (Types.TypeGuard.TConstructor(schema)) { - return Constructor(schema) - } else if (Types.TypeGuard.TFunction(schema)) { - return Function(schema) - } else if (Types.TypeGuard.TInteger(schema)) { - return Integer(schema) - } else if (Types.TypeGuard.TIntersect(schema)) { - return Intersect(schema) - } else if (Types.TypeGuard.TLiteral(schema)) { - return Literal(schema) - } else if (Types.TypeGuard.TNever(schema)) { - return Never(schema) - } else if (Types.TypeGuard.TNull(schema)) { - return Null(schema) - } else if (Types.TypeGuard.TNumber(schema)) { - return Number(schema) - } else if (Types.TypeGuard.TObject(schema)) { - return Object(schema) - } else if (Types.TypeGuard.TPromise(schema)) { - return Promise(schema) - } else if (Types.TypeGuard.TRecord(schema)) { - return Record(schema) - } else if (Types.TypeGuard.TRef(schema)) { - return Ref(schema) - } else if (Types.TypeGuard.TString(schema)) { - return String(schema) - } else if (Types.TypeGuard.TThis(schema)) { - return This(schema) - } else if (Types.TypeGuard.TTuple(schema)) { - return Tuple(schema) - } else if (Types.TypeGuard.TUint8Array(schema)) { - return UInt8Array(schema) - } else if (Types.TypeGuard.TUndefined(schema)) { - return Undefined(schema) - } else if (Types.TypeGuard.TUnion(schema)) { - return Union(schema) - } else if (Types.TypeGuard.TUnknown(schema)) { - return Unknown(schema) - } else if (Types.TypeGuard.TVoid(schema)) { - return Void(schema) - } else { - throw Error(`TypeBoxToZod: Unknown type`) - } - } - const reference_map = new Map() - const recursive_set = new Set() - /** Renders a Zod definition of a TypeBox type */ - export function Generate(schema: Types.TSchema, references: Types.TSchema[], options: ZodCodegenOptions = { imports: true, exports: false }) { - const emitted = new Set() - const exports = options.exports ? 'export' : '' - const imports_code: string[] = [] - const reference_code: string[] = [] - const type_code: string[] = [] - // initialize root schematic and reference map - if (schema.$id === undefined) schema.$id = `T_Generated` - for (const reference of references) { - if (reference.$id === undefined) throw new TypeBoxToZodNonReferentialType(JSON.stringify(reference)) - reference_map.set(reference.$id, reference) - } - // render-code: Imports required for the generated code - if (options.imports) { - imports_code.push(`import z from 'zod'`) - } - // render-type: If we detect the root schematic has been referenced, we interpret this as a recursive - // root. It`s noted that zod performs 4x slower when wrapped in a lazy(() => ...), so this is considered - // an optimization. - const typedef = [...Visit(schema)].join(``) - if (recursive_set.has(schema.$id!)) { - type_code.push(`${exports} const ${schema.$id || `T`} = z.lazy(() => ${Formatter.Format(typedef)})`) - } else { - type_code.push(`${exports} const ${schema.$id || `T`} = ${Formatter.Format(typedef)}`) - } - emitted.add(schema.$id!) - // render-reference: References may either be recursive or not. We track a recursive_set when visiting - // schemas of type TSelf. If we`ve never observed the reference through recursion, when it should be safe - // to omit the lazy(() => ...) wrap. - for (const reference of reference_map.values()) { - if (emitted.has(reference.$id!)) continue - const typedef = [...Visit(schema)].join(``) - if (recursive_set.has(reference.$id!)) { - reference_code.push(`${exports} const ${reference.$id} = z.lazy(() => ${typedef})`) - } else { - reference_code.push(`${exports} const ${reference.$id} = ${typedef}`) - } - emitted.add(reference.$id!) - } - return Formatter.Format([...imports_code, ...reference_code, ...type_code].join(`\n\n`)) - } -} diff --git a/example/codegen/typescript-to-json-schema.ts b/example/codegen/typescript-to-json-schema.ts deleted file mode 100644 index 3318fa63d..000000000 --- a/example/codegen/typescript-to-json-schema.ts +++ /dev/null @@ -1,360 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Formatter } from './formatter' -import * as ts from 'typescript' - -export namespace TypeScriptToJsonSchema { - function isReadonlyProperty(node: ts.PropertySignature): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined - } - function isOptionalProperty(node: ts.PropertySignature) { - return node.questionToken !== undefined - } - function isExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined - } - function* SourceFile(node: ts.SourceFile): IterableIterator { - for (const next of node.getChildren()) { - yield* Visit(next) - } - } - function* PropertySignature(node: ts.PropertySignature): IterableIterator { - const [readonly, optional] = [isReadonlyProperty(node), isOptionalProperty(node)] - const type = Collect(node.type) - if (readonly && optional) { - return yield `${node.name.getText()}: ${type}` - } else if (readonly) { - return yield `${node.name.getText()}: ${type}` - } else if (optional) { - return yield `${node.name.getText()}: ${type}` - } else { - return yield `${node.name.getText()}: ${type}` - } - } - function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { - const type = Collect(node.elementType) - yield `{ - type: 'array', - items: ${type} - }` - } - function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { - const types = node.elements.map((type) => Collect(type)).join(',\n') - yield `{ - type: 'array', - items: [${types}], - minItems: ${node.elements.length}, - maxItems: ${node.elements.length} - }` - } - function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { - const types = node.types.map((type) => Collect(type)).join(',\n') - yield `{ - anyOf: [ - ${types} - ] - }` - } - function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { - const types = node.types.map((type) => Collect(type)).join(',\n') - yield `{ - allOf: [ - ${types} - ] - }` - } - function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { - return yield UnknownType('TypeOperatorNode') - } - function* Parameter(node: ts.ParameterDeclaration): IterableIterator { - yield Collect(node.type) - } - function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { - return yield UnknownType('FunctionTypeNode') - } - function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { - return yield UnknownType('ConstructorTypeNode') - } - function* EnumMember(node: ts.EnumMember): IterableIterator { - if (node.initializer) { - return yield `{ - const: ${node.initializer.getText()} - }` - } - return yield `{ - const: '${node.name.getText()}' - }` - } - function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { - const exports = isExport(node) ? 'export ' : '' - const name = node.name.getText() - const members = node.members.map((member) => Collect(member)).join(',\n') - yield `${exports}const ${name} = { - anyOf: [ - ${members} - ] - }` - } - function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { - if (node.typeParameters) { - const exports = isExport(node) ? 'export ' : '' - const members = node.members.map((member) => Collect(member)).join(',\n') - // prettier-ignore - const required = node.members.filter((member) => member.questionToken === undefined).map((member) => `'${member.name?.getText()}'`).join(',\n') - const constraints = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') - const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') - const definition = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ({ - type: 'object', - properties: { - ${members} - }, - required: [ - ${required} - ], - })` - yield `${definition}` - } else { - const exports = isExport(node) ? 'export ' : '' - const members = node.members.map((member) => Collect(member)).join(',\n') - const required = node.members - .filter((member) => member.questionToken === undefined) - .map((member) => `'${member.name?.getText()}'`) - .join(',\n') - const definition = `${exports}const ${node.name.getText()} = { - type: 'object', - properties: { - ${members} - }, - required: [ - ${required} - ] - }` - yield `${definition}` - } - } - function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { - if (node.typeParameters) { - const exports = isExport(node) ? 'export ' : '' - const constraints = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') - const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') - const type = Collect(node.type) - const definition = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => (${type})` - yield `${definition}` - } else { - const exports = isExport(node) ? 'export ' : '' - const type = Collect(node.type) - const definition = `${exports}const ${node.name.getText()} = ${type}` - yield `${definition}` - } - } - function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { - yield node.name.getText() - } - function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { - yield Collect(node.type) - } - function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { - return yield UnknownType('RestTypeNode') - } - function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - return yield UnknownType('ConditionalTypeNode') - } - function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { - const name = node.typeName.getText() - const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' - if (name === 'Array') { - return yield `{ type: 'array', items: ${args} }` - } else if (name === 'Record') { - return yield UnknownType('Record') - } else if (name === 'Partial') { - return yield UnknownType('Partial') - } else if (name === 'Uint8Array') { - return yield UnknownType('Uint8Array') - } else if (name === 'Required') { - return yield UnknownType('Required') - } else if (name === 'Omit') { - return yield UnknownType('Omit') - } else if (name === 'Pick') { - return yield UnknownType('Pick') - } else if (name === 'Promise') { - return yield UnknownType('Promise') - } else if (name === 'ReturnType') { - return yield UnknownType('ReturnType') - } else if (name === 'InstanceType') { - return yield UnknownType('InstanceType') - } else if (name === 'Parameters') { - return yield UnknownType('Parameters') - } else if (name === 'ConstructorParameters') { - return yield UnknownType('ConstructorParameters') - } else if (name === 'Exclude') { - return yield UnknownType('Exclude') - } else if (name === 'Extract') { - return yield UnknownType('Extract') - } else { - return yield `${name}${args}` - } - } - function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { - const members = node.members.map((member) => Collect(member)).join(',\n') - const required = node.members - .filter((member) => member.questionToken === undefined) - .map((member) => `'${member.name?.getText()}'`) - .join(',\n') - yield `{ - type: 'object', - properties: { - ${members} - }, - required: [ - ${required} - ] - }` - } - function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { - const text = node.getText() - if (text === 'null') - return yield `{ - type: 'null' - }` - yield `{ - const: ${node.getText()} - }` - } - function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { - yield node.getText() - } - function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { - yield node.getText() - } - function Collect(node: ts.Node | undefined): string { - return `${[...Visit(node)].join('')}` - } - function CollectNewLine(node: ts.Node | undefined): string { - return [...Visit(node)].join('\n\n') - } - function UnknownType(name: string = 'Unknown'): string { - return `{ not: { /* unsupported type '${name}' */ } }` - } - function KnownType(type: string): string { - return ['{', `type: '${type}'`, '}'].join('\n') - } - function* Visit(node: ts.Node | undefined): IterableIterator { - if (node === undefined) return - if (ts.isSourceFile(node)) { - return yield* SourceFile(node) - } else if (ts.isInterfaceDeclaration(node)) { - return yield* InterfaceDeclaration(node) - } else if (ts.isTypeAliasDeclaration(node)) { - return yield* TypeAliasDeclaration(node) - } else if (ts.isParameter(node)) { - return yield* Parameter(node) - } else if (ts.isFunctionTypeNode(node)) { - return yield* FunctionTypeNode(node) - } else if (ts.isConstructorTypeNode(node)) { - return yield* ConstructorTypeNode(node) - } else if (ts.isEnumMember(node)) { - return yield* EnumMember(node) - } else if (ts.isEnumDeclaration(node)) { - return yield* EnumDeclaration(node) - } else if (ts.isPropertySignature(node)) { - return yield* PropertySignature(node) - } else if (ts.isTypeReferenceNode(node)) { - return yield* TypeReferenceNode(node) - } else if (ts.isTypeLiteralNode(node)) { - return yield* TypeLiteralNode(node) - } else if (ts.isLiteralTypeNode(node)) { - return yield* LiteralTypeNode(node) - } else if (ts.isArrayTypeNode(node)) { - return yield* ArrayTypeNode(node) - } else if (ts.isTupleTypeNode(node)) { - return yield* TupleTypeNode(node) - } else if (ts.isIntersectionTypeNode(node)) { - return yield* IntersectionTypeNode(node) - } else if (ts.isUnionTypeNode(node)) { - return yield* UnionTypeNode(node) - } else if (ts.isTypeOperatorNode(node)) { - return yield* TypeOperatorNode(node) - } else if (ts.isTypeParameterDeclaration(node)) { - return yield* TypeParameterDeclaration(node) - } else if (ts.isParenthesizedTypeNode(node)) { - return yield* ParenthesizedTypeNode(node) - } else if (ts.isRestTypeNode(node)) { - return yield* RestTypeNode(node) - } else if (ts.isFunctionDeclaration(node)) { - return yield* FunctionDeclaration(node) - } else if (ts.isClassDeclaration(node)) { - return yield* ClassDeclaration(node) - } else if (ts.isConditionalTypeNode(node)) { - return yield* ConditionalTypeNode(node) - } else if (ts.isIdentifier(node)) { - return yield node.getText() - } else if (node.kind === ts.SyntaxKind.ExportKeyword) { - return yield `export` - } else if (node.kind === ts.SyntaxKind.KeyOfKeyword) { - return yield UnknownType() - } else if (node.kind === ts.SyntaxKind.NumberKeyword) { - return yield KnownType('number') - } else if (node.kind === ts.SyntaxKind.BigIntKeyword) { - return yield UnknownType() - } else if (node.kind === ts.SyntaxKind.StringKeyword) { - return yield KnownType('string') - } else if (node.kind === ts.SyntaxKind.BooleanKeyword) { - return yield KnownType('boolean') - } else if (node.kind === ts.SyntaxKind.UndefinedKeyword) { - return yield UnknownType('undefined') - } else if (node.kind === ts.SyntaxKind.UnknownKeyword) { - return yield `{ }` - } else if (node.kind === ts.SyntaxKind.AnyKeyword) { - return yield `{ }` - } else if (node.kind === ts.SyntaxKind.NeverKeyword) { - return yield `{ not: { } }` - } else if (node.kind === ts.SyntaxKind.NullKeyword) { - return yield KnownType('null') - } else if (node.kind === ts.SyntaxKind.VoidKeyword) { - return yield UnknownType('void') - } else if (node.kind === ts.SyntaxKind.EndOfFileToken) { - return - } else if (node.kind === ts.SyntaxKind.SyntaxList) { - for (const child of node.getChildren()) { - yield* Visit(child) - } - return - } else { - console.log('Unhandled:', ts.SyntaxKind[node.kind]) - return yield node.getText() - } - } - /** Generates JsonSchema schematics from TypeScript interface and type definitions */ - export function Generate(typescriptCode: string) { - const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) - const declarations = CollectNewLine(source) - const types = Formatter.Format(declarations) - return [types].join('\n') - } -} \ No newline at end of file diff --git a/example/codegen/typescript-to-typebox.ts b/example/codegen/typescript-to-typebox.ts deleted file mode 100644 index 7f69b2451..000000000 --- a/example/codegen/typescript-to-typebox.ts +++ /dev/null @@ -1,383 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/codegen - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Formatter } from './formatter' -import * as ts from 'typescript' - -// ------------------------------------------------------------------------- -// [TypeScriptToTypeBox] -// ------------------------------------------------------------------------- -/** Generates TypeBox types from TypeScript code */ -export namespace TypeScriptToTypeBox { - // tracked for recursive types and used to associate This type references - let recursiveDeclaration: ts.TypeAliasDeclaration | ts.InterfaceDeclaration | null = null - // tracked for injecting typebox import statements - let useImports = false - // tracked for injecting TSchema import statements - let useGenerics = false - // tracked for each generated type. - const typeNames = new Set() - - function FindRecursiveParent(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration, node: ts.Node): boolean { - return (ts.isTypeReferenceNode(node) && decl.name.getText() === node.typeName.getText()) || node.getChildren().some((node) => FindRecursiveParent(decl, node)) - } - function IsRecursiveType(decl: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) { - return ts.isTypeAliasDeclaration(decl) ? [decl.type].some((node) => FindRecursiveParent(decl, node)) : decl.members.some((node) => FindRecursiveParent(decl, node)) - } - function IsReadonlyProperty(node: ts.PropertySignature): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'readonly') !== undefined - } - function IsOptionalProperty(node: ts.PropertySignature) { - return node.questionToken !== undefined - } - function IsExport(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration | ts.EnumDeclaration | ts.ModuleDeclaration): boolean { - return node.modifiers !== undefined && node.modifiers.find((modifier) => modifier.getText() === 'export') !== undefined - } - function IsNamespace(node: ts.ModuleDeclaration) { - return node.flags === ts.NodeFlags.Namespace - } - function* SourceFile(node: ts.SourceFile): IterableIterator { - for (const next of node.getChildren()) { - yield* Visit(next) - } - } - function* PropertySignature(node: ts.PropertySignature): IterableIterator { - const [readonly, optional] = [IsReadonlyProperty(node), IsOptionalProperty(node)] - const type = Collect(node.type) - if (readonly && optional) { - return yield `${node.name.getText()}: Type.ReadonlyOptional(${type})` - } else if (readonly) { - return yield `${node.name.getText()}: Type.Readonly(${type})` - } else if (optional) { - return yield `${node.name.getText()}: Type.Optional(${type})` - } else { - return yield `${node.name.getText()}: ${type}` - } - } - function* ArrayTypeNode(node: ts.ArrayTypeNode): IterableIterator { - const type = Collect(node.elementType) - yield `Type.Array(${type})` - } - function* TupleTypeNode(node: ts.TupleTypeNode): IterableIterator { - const types = node.elements.map((type) => Collect(type)).join(',\n') - yield `Type.Tuple([\n${types}\n])` - } - function* UnionTypeNode(node: ts.UnionTypeNode): IterableIterator { - const types = node.types.map((type) => Collect(type)).join(',\n') - yield `Type.Union([\n${types}\n])` - } - // ------------------------------------------------------------------------- - // TemplateLiteral - // ------------------------------------------------------------------------- - // prettier-ignore - function* TemplateLiteralTypeNode(node: ts.TemplateLiteralTypeNode) { - const collect = node.getChildren().map(node => Collect(node)).join('') - yield `Type.TemplateLiteral([${collect.slice(0, collect.length - 2)}])` // can't remove trailing here - } - // prettier-ignore - function* TemplateLiteralTypeSpan(node: ts.TemplateLiteralTypeSpan) { - const collect = node.getChildren().map(node => Collect(node)).join(', ') - if(collect.length > 0) yield `${collect}` - } - function* TemplateHead(node: ts.TemplateHead) { - if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` - } - function* TemplateMiddle(node: ts.TemplateMiddle) { - if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` - } - function* TemplateTail(node: ts.TemplateTail) { - if (node.text.length > 0) yield `Type.Literal('${node.text}'), ` - } - function* IntersectionTypeNode(node: ts.IntersectionTypeNode): IterableIterator { - const types = node.types.map((type) => Collect(type)).join(',\n') - yield `Type.Intersect([\n${types}\n])` - } - function* TypeOperatorNode(node: ts.TypeOperatorNode): IterableIterator { - if (node.operator === ts.SyntaxKind.KeyOfKeyword) { - const type = Collect(node.type) - yield `Type.KeyOf(${type})` - } - if (node.operator === ts.SyntaxKind.ReadonlyKeyword) { - yield Collect(node.type) - } - } - function* Parameter(node: ts.ParameterDeclaration): IterableIterator { - yield Collect(node.type) - } - function* FunctionTypeNode(node: ts.FunctionTypeNode): IterableIterator { - const parameters = node.parameters.map((param) => Collect(param)).join(', ') - const returns = Collect(node.type) - yield `Type.Function([${parameters}], ${returns})` - } - function* ConstructorTypeNode(node: ts.ConstructorTypeNode): IterableIterator { - const parameters = node.parameters.map((param) => Collect(param)).join(', ') - const returns = Collect(node.type) - yield `Type.Constructor([${parameters}], ${returns})` - } - function* EnumDeclaration(node: ts.EnumDeclaration): IterableIterator { - useImports = true - const exports = IsExport(node) ? 'export ' : '' - const members = node.members.map((member) => member.getText()).join(', ') - const enumType = `${exports}enum ${node.name.getText()}Enum { ${members} }` - const type = `${exports}const ${node.name.getText()} = Type.Enum(${node.name.getText()}Enum)` - yield [enumType, '', type].join('\n') - typeNames.add(node.name.getText()) - } - // Collects members on behalf of InterfaceDeclaration and TypeLiteralNode. This function may yield an object - // with additionalProperties if we find an indexer in the members set. - function CollectObjectMembers(members: ts.NodeArray): string { - const properties = members.filter((member) => !ts.isIndexSignatureDeclaration(member)) - const indexers = members.filter((member) => ts.isIndexSignatureDeclaration(member)) - const propertyCollect = properties.map((property) => Collect(property)).join(',\n') - const indexer = indexers.length > 0 ? Collect(indexers[indexers.length - 1]) : '' - if (properties.length === 0 && indexer.length > 0) { - return `{},\n{\nadditionalProperties: ${indexer}\n }` - } else if (properties.length > 0 && indexer.length > 0) { - return `{\n${propertyCollect}\n},\n{\nadditionalProperties: ${indexer}\n }` - } else { - return `{\n${propertyCollect}\n}` - } - } - function* TypeLiteralNode(node: ts.TypeLiteralNode): IterableIterator { - const members = CollectObjectMembers(node.members) - yield* `Type.Object(${members})` - } - function* InterfaceDeclaration(node: ts.InterfaceDeclaration): IterableIterator { - useImports = true - const isRecursiveType = IsRecursiveType(node) - if (isRecursiveType) recursiveDeclaration = node - const heritage = node.heritageClauses !== undefined ? node.heritageClauses.flatMap((node) => Collect(node)) : [] - if (node.typeParameters) { - useGenerics = true - const exports = IsExport(node) ? 'export ' : '' - const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') - const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') - const names = node.typeParameters.map((param) => `${Collect(param)}`).join(', ') - const members = CollectObjectMembers(node.members) - const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object(${members}))` : `Type.Object(${members})` - const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` - const typeDeclaration = `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${typeExpression}` - yield `${staticDeclaration}\n${typeDeclaration}` - } else { - const exports = IsExport(node) ? 'export ' : '' - const members = CollectObjectMembers(node.members) - const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const rawTypeExpression = IsRecursiveType(node) ? `Type.Recursive(This => Type.Object(${members}))` : `Type.Object(${members})` - const typeExpression = heritage.length === 0 ? rawTypeExpression : `Type.Intersect([${heritage.join(', ')}, ${rawTypeExpression}])` - const typeDeclaration = `${exports}const ${node.name.getText()} = ${typeExpression}` - yield `${staticDeclaration}\n${typeDeclaration}` - } - typeNames.add(node.name.getText()) - recursiveDeclaration = null - } - function* TypeAliasDeclaration(node: ts.TypeAliasDeclaration): IterableIterator { - useImports = true - const isRecursiveType = IsRecursiveType(node) - if (isRecursiveType) recursiveDeclaration = node - if (node.typeParameters) { - useGenerics = true - const exports = IsExport(node) ? 'export ' : '' - const constraints = node.typeParameters.map((param) => `${Collect(param)} extends TSchema`).join(', ') - const parameters = node.typeParameters.map((param) => `${Collect(param)}: ${Collect(param)}`).join(', ') - const names = node.typeParameters.map((param) => Collect(param)).join(', ') - const type = Collect(node.type) - const staticDeclaration = `${exports}type ${node.name.getText()}<${constraints}> = Static>>` - const typeDeclaration = isRecursiveType - ? `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => Type.Recursive(This => ${type})` - : `${exports}const ${node.name.getText()} = <${constraints}>(${parameters}) => ${type}` - yield `${staticDeclaration}\n${typeDeclaration}` - } else { - const exports = IsExport(node) ? 'export ' : '' - const type = Collect(node.type) - const staticDeclaration = `${exports}type ${node.name.getText()} = Static` - const typeDeclaration = isRecursiveType ? `${exports}const ${node.name.getText()} = Type.Recursive(This => ${type})` : `${exports}const ${node.name.getText()} = ${type}` - yield `${staticDeclaration}\n${typeDeclaration}` - } - typeNames.add(node.name.getText()) - recursiveDeclaration = null - } - function* HeritageClause(node: ts.HeritageClause): IterableIterator { - const types = node.types.map((node) => Collect(node)) - if (types.length === 1) return yield types[0] - yield `Type.Intersect([${types.join(', ')}])` - } - function* ExpressionWithTypeArguments(node: ts.ExpressionWithTypeArguments): IterableIterator { - const name = Collect(node.expression) - const typeArguments = node.typeArguments === undefined ? [] : node.typeArguments.map((node) => Collect(node)) - // todo: default type argument (resolve `= number` from `type Foo`) - return yield typeArguments.length === 0 ? `${name}` : `${name}(${typeArguments.join(', ')})` - } - function* TypeParameterDeclaration(node: ts.TypeParameterDeclaration): IterableIterator { - yield node.name.getText() - } - function* ParenthesizedTypeNode(node: ts.ParenthesizedTypeNode): IterableIterator { - yield Collect(node.type) - } - function* RestTypeNode(node: ts.RestTypeNode): IterableIterator { - yield `Type.Rest()` - } - function* ConditionalTypeNode(node: ts.ConditionalTypeNode): IterableIterator { - const checkType = Collect(node.checkType) - const extendsType = Collect(node.extendsType) - const trueType = Collect(node.trueType) - const falseType = Collect(node.falseType) - yield `Type.Extends(${checkType}, ${extendsType}, ${trueType}, ${falseType})` - } - function* isIndexSignatureDeclaration(node: ts.IndexSignatureDeclaration) { - // note: we ignore the key and just return the type. this is a mismatch between - // object and record types. Address in TypeBox by unifying validation paths - // for objects and record types. - yield Collect(node.type) - } - function* TypeReferenceNode(node: ts.TypeReferenceNode): IterableIterator { - const name = node.typeName.getText() - const args = node.typeArguments ? `(${node.typeArguments.map((type) => Collect(type)).join(', ')})` : '' - if (name === 'Array') return yield `Type.Array${args}` - if (name === 'Record') return yield `Type.Record${args}` - if (name === 'Partial') return yield `Type.Partial${args}` - if (name === 'Uint8Array') return yield `Type.Uint8Array()` - if (name === 'Date') return yield `Type.Date()` - if (name === 'Required') return yield `Type.Required${args}` - if (name === 'Omit') return yield `Type.Omit${args}` - if (name === 'Pick') return yield `Type.Pick${args}` - if (name === 'Promise') return yield `Type.Promise${args}` - if (name === 'ReturnType') return yield `Type.ReturnType${args}` - if (name === 'InstanceType') return yield `Type.InstanceType${args}` - if (name === 'Parameters') return yield `Type.Parameters${args}` - if (name === 'ConstructorParameters') return yield `Type.ConstructorParameters${args}` - if (name === 'Exclude') return yield `Type.Exclude${args}` - if (name === 'Extract') return yield `Type.Extract${args}` - if (recursiveDeclaration !== null && FindRecursiveParent(recursiveDeclaration, node)) return yield `This` - if (typeNames.has(name)) return yield `${name}${args}` - if (name in globalThis) return yield `Type.Never(/* Unsupported Type '${name}' */)` - return yield `${name}${args}` - } - function* LiteralTypeNode(node: ts.LiteralTypeNode): IterableIterator { - const text = node.getText() - if (text === 'null') return yield `Type.Null()` - yield `Type.Literal(${node.getText()})` - } - function* ModuleDeclaration(node: ts.ModuleDeclaration): IterableIterator { - const export_specifier = IsExport(node) ? 'export ' : '' - const module_specifier = IsNamespace(node) ? 'namespace' : 'module' - yield `${export_specifier}${module_specifier} ${node.name.getText()} {` - yield* Visit(node.body) - yield `}` - } - function* ModuleBlock(node: ts.ModuleBlock): IterableIterator { - for (const statement of node.statements) { - yield* Visit(statement) - } - } - function* FunctionDeclaration(node: ts.FunctionDeclaration): IterableIterator { - yield node.getText() - } - function* ClassDeclaration(node: ts.ClassDeclaration): IterableIterator { - yield node.getText() - } - function Collect(node: ts.Node | undefined): string { - return `${[...Visit(node)].join('')}` - } - function CollectNewLine(node: ts.Node | undefined): string { - return [...Visit(node)].join('\n\n') - } - function* Visit(node: ts.Node | undefined): IterableIterator { - if (node === undefined) return - if (ts.isSourceFile(node)) return yield* SourceFile(node) - if (ts.isInterfaceDeclaration(node)) return yield* InterfaceDeclaration(node) - if (ts.isTypeAliasDeclaration(node)) return yield* TypeAliasDeclaration(node) - if (ts.isParameter(node)) return yield* Parameter(node) - if (ts.isFunctionTypeNode(node)) return yield* FunctionTypeNode(node) - if (ts.isConstructorTypeNode(node)) return yield* ConstructorTypeNode(node) - if (ts.isEnumDeclaration(node)) return yield* EnumDeclaration(node) - if (ts.isPropertySignature(node)) return yield* PropertySignature(node) - if (ts.isTypeReferenceNode(node)) return yield* TypeReferenceNode(node) - if (ts.isTypeLiteralNode(node)) return yield* TypeLiteralNode(node) - if (ts.isLiteralTypeNode(node)) return yield* LiteralTypeNode(node) - if (ts.isModuleDeclaration(node)) return yield* ModuleDeclaration(node) - if (ts.isModuleBlock(node)) return yield* ModuleBlock(node) - if (ts.isArrayTypeNode(node)) return yield* ArrayTypeNode(node) - if (ts.isTupleTypeNode(node)) return yield* TupleTypeNode(node) - if (ts.isIntersectionTypeNode(node)) return yield* IntersectionTypeNode(node) - if (ts.isUnionTypeNode(node)) return yield* UnionTypeNode(node) - if (ts.isTemplateLiteralTypeNode(node)) return yield* TemplateLiteralTypeNode(node) - if (ts.isTemplateLiteralTypeSpan(node)) return yield* TemplateLiteralTypeSpan(node) - if (ts.isTemplateHead(node)) return yield* TemplateHead(node) - if (ts.isTemplateMiddle(node)) return yield* TemplateMiddle(node) - if (ts.isTemplateTail(node)) return yield* TemplateTail(node) - if (ts.isTypeOperatorNode(node)) return yield* TypeOperatorNode(node) - if (ts.isHeritageClause(node)) return yield* HeritageClause(node) - if (ts.isExpressionWithTypeArguments(node)) return yield* ExpressionWithTypeArguments(node) - if (ts.isTypeParameterDeclaration(node)) return yield* TypeParameterDeclaration(node) - if (ts.isParenthesizedTypeNode(node)) return yield* ParenthesizedTypeNode(node) - if (ts.isRestTypeNode(node)) return yield* RestTypeNode(node) - if (ts.isFunctionDeclaration(node)) return yield* FunctionDeclaration(node) - if (ts.isClassDeclaration(node)) return yield* ClassDeclaration(node) - if (ts.isConditionalTypeNode(node)) return yield* ConditionalTypeNode(node) - if (ts.isIndexSignatureDeclaration(node)) return yield* isIndexSignatureDeclaration(node) - if (ts.isIdentifier(node)) return yield node.getText() - if (node.kind === ts.SyntaxKind.ExportKeyword) return yield `export` - if (node.kind === ts.SyntaxKind.KeyOfKeyword) return yield `Type.KeyOf()` - if (node.kind === ts.SyntaxKind.NumberKeyword) return yield `Type.Number()` - if (node.kind === ts.SyntaxKind.BigIntKeyword) return yield `Type.BigInt()` - if (node.kind === ts.SyntaxKind.StringKeyword) return yield `Type.String()` - if (node.kind === ts.SyntaxKind.BooleanKeyword) return yield `Type.Boolean()` - if (node.kind === ts.SyntaxKind.UndefinedKeyword) return yield `Type.Undefined()` - if (node.kind === ts.SyntaxKind.UnknownKeyword) return yield `Type.Unknown()` - if (node.kind === ts.SyntaxKind.AnyKeyword) return yield `Type.Any()` - if (node.kind === ts.SyntaxKind.NeverKeyword) return yield `Type.Never()` - if (node.kind === ts.SyntaxKind.NullKeyword) return yield `Type.Null()` - if (node.kind === ts.SyntaxKind.VoidKeyword) return yield `Type.Void()` - if (node.kind === ts.SyntaxKind.EndOfFileToken) return - if (node.kind === ts.SyntaxKind.SyntaxList) { - for (const child of node.getChildren()) { - yield* Visit(child) - } - return - } - console.log('Unhandled:', ts.SyntaxKind[node.kind]) - return yield node.getText() - } - /** Generates TypeBox types from TypeScript interface and type definitions */ - export function Generate(typescriptCode: string) { - typeNames.clear() - useImports = false - useGenerics = false - const source = ts.createSourceFile('code.ts', typescriptCode, ts.ScriptTarget.ESNext, true) - const typeDeclarations = CollectNewLine(source) - const importStatments: string[] = [] - if (useImports) { - if (useGenerics) importStatments.push(`import { Type, Static, TSchema } from '@sinclair/typebox'`) - if (!useGenerics) importStatments.push(`import { Type, Static } from '@sinclair/typebox'`) - } - const imports = importStatments.join('\n') - const types = Formatter.Format(typeDeclarations) - return [imports, '', types].join('\n') - } -} diff --git a/package.json b/package.json index cbdd26dc4..2b017e2bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.1", + "version": "0.29.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index a39085005..7f5d64338 100644 --- a/readme.md +++ b/readme.md @@ -1496,35 +1496,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 220 ms' │ ' 6 ms' │ ' 36.67 x' │ -│ Literal_Number │ 1000 │ ' 172 ms' │ ' 4 ms' │ ' 43.00 x' │ -│ Literal_Boolean │ 1000 │ ' 162 ms' │ ' 4 ms' │ ' 40.50 x' │ +│ Literal_String │ 1000 │ ' 227 ms' │ ' 7 ms' │ ' 32.43 x' │ +│ Literal_Number │ 1000 │ ' 180 ms' │ ' 6 ms' │ ' 30.00 x' │ +│ Literal_Boolean │ 1000 │ ' 152 ms' │ ' 5 ms' │ ' 30.40 x' │ │ Primitive_Number │ 1000 │ ' 161 ms' │ ' 6 ms' │ ' 26.83 x' │ -│ Primitive_String │ 1000 │ ' 154 ms' │ ' 4 ms' │ ' 38.50 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 204 ms' │ ' 10 ms' │ ' 20.40 x' │ -│ Primitive_Boolean │ 1000 │ ' 131 ms' │ ' 4 ms' │ ' 32.75 x' │ -│ Primitive_Null │ 1000 │ ' 142 ms' │ ' 5 ms' │ ' 28.40 x' │ -│ Object_Unconstrained │ 1000 │ ' 1263 ms' │ ' 29 ms' │ ' 43.55 x' │ -│ Object_Constrained │ 1000 │ ' 1267 ms' │ ' 24 ms' │ ' 52.79 x' │ -│ Object_Vector3 │ 1000 │ ' 382 ms' │ ' 7 ms' │ ' 54.57 x' │ -│ Object_Box3D │ 1000 │ ' 1723 ms' │ ' 28 ms' │ ' 61.54 x' │ -│ Tuple_Primitive │ 1000 │ ' 495 ms' │ ' 13 ms' │ ' 38.08 x' │ -│ Tuple_Object │ 1000 │ ' 1271 ms' │ ' 16 ms' │ ' 79.44 x' │ -│ Composite_Intersect │ 1000 │ ' 656 ms' │ ' 19 ms' │ ' 34.53 x' │ -│ Composite_Union │ 1000 │ ' 529 ms' │ ' 18 ms' │ ' 29.39 x' │ -│ Math_Vector4 │ 1000 │ ' 802 ms' │ ' 14 ms' │ ' 57.29 x' │ -│ Math_Matrix4 │ 1000 │ ' 411 ms' │ ' 6 ms' │ ' 68.50 x' │ -│ Array_Primitive_Number │ 1000 │ ' 369 ms' │ ' 6 ms' │ ' 61.50 x' │ -│ Array_Primitive_String │ 1000 │ ' 369 ms' │ ' 4 ms' │ ' 92.25 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 297 ms' │ ' 3 ms' │ ' 99.00 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1582 ms' │ ' 20 ms' │ ' 79.10 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1629 ms' │ ' 19 ms' │ ' 85.74 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 652 ms' │ ' 12 ms' │ ' 54.33 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1587 ms' │ ' 16 ms' │ ' 99.19 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 1051 ms' │ ' 15 ms' │ ' 70.07 x' │ -│ Array_Composite_Union │ 1000 │ ' 733 ms' │ ' 15 ms' │ ' 48.87 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1071 ms' │ ' 12 ms' │ ' 89.25 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 636 ms' │ ' 5 ms' │ ' 127.20 x' │ +│ Primitive_String │ 1000 │ ' 150 ms' │ ' 8 ms' │ ' 18.75 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 202 ms' │ ' 9 ms' │ ' 22.44 x' │ +│ Primitive_Boolean │ 1000 │ ' 133 ms' │ ' 3 ms' │ ' 44.33 x' │ +│ Primitive_Null │ 1000 │ ' 147 ms' │ ' 3 ms' │ ' 49.00 x' │ +│ Object_Unconstrained │ 1000 │ ' 1145 ms' │ ' 31 ms' │ ' 36.94 x' │ +│ Object_Constrained │ 1000 │ ' 1241 ms' │ ' 26 ms' │ ' 47.73 x' │ +│ Object_Vector3 │ 1000 │ ' 407 ms' │ ' 7 ms' │ ' 58.14 x' │ +│ Object_Box3D │ 1000 │ ' 1781 ms' │ ' 27 ms' │ ' 65.96 x' │ +│ Tuple_Primitive │ 1000 │ ' 489 ms' │ ' 13 ms' │ ' 37.62 x' │ +│ Tuple_Object │ 1000 │ ' 1278 ms' │ ' 34 ms' │ ' 37.59 x' │ +│ Composite_Intersect │ 1000 │ ' 613 ms' │ ' 16 ms' │ ' 38.31 x' │ +│ Composite_Union │ 1000 │ ' 543 ms' │ ' 18 ms' │ ' 30.17 x' │ +│ Math_Vector4 │ 1000 │ ' 819 ms' │ ' 13 ms' │ ' 63.00 x' │ +│ Math_Matrix4 │ 1000 │ ' 407 ms' │ ' 7 ms' │ ' 58.14 x' │ +│ Array_Primitive_Number │ 1000 │ ' 372 ms' │ ' 6 ms' │ ' 62.00 x' │ +│ Array_Primitive_String │ 1000 │ ' 329 ms' │ ' 6 ms' │ ' 54.83 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 313 ms' │ ' 3 ms' │ ' 104.33 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1780 ms' │ ' 20 ms' │ ' 89.00 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1494 ms' │ ' 21 ms' │ ' 71.14 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 917 ms' │ ' 10 ms' │ ' 91.70 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1666 ms' │ ' 13 ms' │ ' 128.15 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 791 ms' │ ' 18 ms' │ ' 43.94 x' │ +│ Array_Composite_Union │ 1000 │ ' 833 ms' │ ' 17 ms' │ ' 49.00 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1161 ms' │ ' 15 ms' │ ' 77.40 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 697 ms' │ ' 10 ms' │ ' 69.70 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1538,37 +1538,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 5 ms' │ ' 1.00 x' │ -│ Literal_Number │ 1000000 │ ' 21 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Literal_String │ 1000000 │ ' 25 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 19 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ │ Literal_Boolean │ 1000000 │ ' 18 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_Number │ 1000000 │ ' 25 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 174 ms' │ ' 44 ms' │ ' 36 ms' │ ' 1.22 x' │ -│ Primitive_Boolean │ 1000000 │ ' 22 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Null │ 1000000 │ ' 22 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1065 ms' │ ' 33 ms' │ ' 25 ms' │ ' 1.32 x' │ -│ Object_Constrained │ 1000000 │ ' 1192 ms' │ ' 53 ms' │ ' 38 ms' │ ' 1.39 x' │ -│ Object_Vector3 │ 1000000 │ ' 410 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Object_Box3D │ 1000000 │ ' 1939 ms' │ ' 54 ms' │ ' 50 ms' │ ' 1.08 x' │ -│ Object_Recursive │ 1000000 │ ' 5248 ms' │ ' 355 ms' │ ' 149 ms' │ ' 2.38 x' │ -│ Tuple_Primitive │ 1000000 │ ' 163 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Tuple_Object │ 1000000 │ ' 737 ms' │ ' 29 ms' │ ' 20 ms' │ ' 1.45 x' │ -│ Composite_Intersect │ 1000000 │ ' 761 ms' │ ' 24 ms' │ ' 15 ms' │ ' 1.60 x' │ -│ Composite_Union │ 1000000 │ ' 519 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Math_Vector4 │ 1000000 │ ' 247 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1045 ms' │ ' 39 ms' │ ' 27 ms' │ ' 1.44 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 256 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Array_Primitive_String │ 1000000 │ ' 222 ms' │ ' 21 ms' │ ' 14 ms' │ ' 1.50 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 149 ms' │ ' 22 ms' │ ' 16 ms' │ ' 1.38 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 5473 ms' │ ' 67 ms' │ ' 59 ms' │ ' 1.14 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5548 ms' │ ' 130 ms' │ ' 116 ms' │ ' 1.12 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 21047 ms' │ ' 1710 ms' │ ' 584 ms' │ ' 2.93 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 691 ms' │ ' 35 ms' │ ' 29 ms' │ ' 1.21 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3075 ms' │ ' 63 ms' │ ' 50 ms' │ ' 1.26 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3126 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2086 ms' │ ' 68 ms' │ ' 33 ms' │ ' 2.06 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1069 ms' │ ' 38 ms' │ ' 23 ms' │ ' 1.65 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4559 ms' │ ' 111 ms' │ ' 88 ms' │ ' 1.26 x' │ +│ Primitive_Number │ 1000000 │ ' 26 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 160 ms' │ ' 42 ms' │ ' 36 ms' │ ' 1.17 x' │ +│ Primitive_Boolean │ 1000000 │ ' 24 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_Null │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1111 ms' │ ' 34 ms' │ ' 24 ms' │ ' 1.42 x' │ +│ Object_Constrained │ 1000000 │ ' 1262 ms' │ ' 51 ms' │ ' 37 ms' │ ' 1.38 x' │ +│ Object_Vector3 │ 1000000 │ ' 445 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Object_Box3D │ 1000000 │ ' 2029 ms' │ ' 66 ms' │ ' 48 ms' │ ' 1.38 x' │ +│ Object_Recursive │ 1000000 │ ' 5121 ms' │ ' 464 ms' │ ' 156 ms' │ ' 2.97 x' │ +│ Tuple_Primitive │ 1000000 │ ' 158 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ +│ Tuple_Object │ 1000000 │ ' 761 ms' │ ' 30 ms' │ ' 18 ms' │ ' 1.67 x' │ +│ Composite_Intersect │ 1000000 │ ' 828 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ +│ Composite_Union │ 1000000 │ ' 529 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Math_Vector4 │ 1000000 │ ' 252 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1024 ms' │ ' 38 ms' │ ' 27 ms' │ ' 1.41 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 264 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Array_Primitive_String │ 1000000 │ ' 240 ms' │ ' 20 ms' │ ' 13 ms' │ ' 1.54 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 137 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 6050 ms' │ ' 66 ms' │ ' 55 ms' │ ' 1.20 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5954 ms' │ ' 124 ms' │ ' 116 ms' │ ' 1.07 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 21074 ms' │ ' 1611 ms' │ ' 626 ms' │ ' 2.57 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 683 ms' │ ' 37 ms' │ ' 30 ms' │ ' 1.23 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3366 ms' │ ' 69 ms' │ ' 54 ms' │ ' 1.28 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3285 ms' │ ' 45 ms' │ ' 36 ms' │ ' 1.25 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2229 ms' │ ' 69 ms' │ ' 34 ms' │ ' 2.03 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1192 ms' │ ' 38 ms' │ ' 25 ms' │ ' 1.52 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4916 ms' │ ' 111 ms' │ ' 88 ms' │ ' 1.26 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1582,11 +1582,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '128.0 kb' │ ' 57.0 kb' │ '2.25 x' │ -│ typebox/errors │ '111.6 kb' │ ' 49.1 kb' │ '2.27 x' │ -│ typebox/system │ ' 77.0 kb' │ ' 31.5 kb' │ '2.45 x' │ -│ typebox/value │ '177.7 kb' │ ' 76.8 kb' │ '2.31 x' │ -│ typebox │ ' 75.9 kb' │ ' 31.0 kb' │ '2.45 x' │ +│ typebox/compiler │ '130.3 kb' │ ' 58.2 kb' │ '2.24 x' │ +│ typebox/errors │ '113.3 kb' │ ' 49.8 kb' │ '2.27 x' │ +│ typebox/system │ ' 78.8 kb' │ ' 32.2 kb' │ '2.45 x' │ +│ typebox/value │ '180.0 kb' │ ' 77.7 kb' │ '2.32 x' │ +│ typebox │ ' 77.7 kb' │ ' 31.7 kb' │ '2.45 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/typebox.ts b/src/typebox.ts index b9acbdf05..5d8c046d0 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -62,11 +62,6 @@ export type AssertProperties = T extends TProperties ? T : TProperties export type AssertRest = T extends E ? T : [] export type AssertType = T extends E ? T : TNever // -------------------------------------------------------------------------- -// Type Normalization -// -------------------------------------------------------------------------- -export type IntersectType = T extends [] ? TNever : T extends [TSchema] ? AssertType : TIntersect -export type UnionType = T extends [] ? TNever : T extends [TSchema] ? AssertType : TUnion -// -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- export type TModifier = TReadonlyOptional | TOptional | TReadonly @@ -74,6 +69,56 @@ export type TReadonly = T & { [Modifier]: 'Readonly' } export type TOptional = T & { [Modifier]: 'Optional' } export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOptional' } // -------------------------------------------------------------------------- +// Optional Unwrap +// -------------------------------------------------------------------------- +// prettier-ignore +export type OptionalUnwrapType = + T extends (TOptional | TReadonlyOptional) + ? OptionalUnwrapType + : T +// prettier-ignore +export type OptionalUnwrapRest = T extends [infer L, ...infer R] + ? L extends (TOptional | TReadonlyOptional) + ? [OptionalUnwrapType>, ...OptionalUnwrapRest>] + : [L, ...OptionalUnwrapRest>] + : [] +// -------------------------------------------------------------------------- +// IntersectType +// -------------------------------------------------------------------------- +// prettier-ignore +export type IntersectOptional = T extends [infer L, ...infer R] + ? L extends TOptional> | TReadonlyOptional> + ? IntersectOptional> + : false + : true +// prettier-ignore +export type IntersectResolve>> = IntersectOptional> extends true + ? TOptional>> + : TIntersect> +// prettier-ignore +export type IntersectType = + T extends [] ? TNever : + T extends [TSchema] ? AssertType : + IntersectResolve +// -------------------------------------------------------------------------- +// UnionType +// -------------------------------------------------------------------------- +// prettier-ignore +export type UnionOptional = T extends [infer L, ...infer R] + ? L extends (TOptional> | TReadonlyOptional>) + ? true + : UnionOptional> + : false +// prettier-ignore +export type UnionResolve>> = UnionOptional> extends true + ? TOptional>> + : TUnion> +// prettier-ignore +export type UnionType = + T extends [] ? TNever : + T extends [TSchema] ? AssertType : + UnionResolve +// -------------------------------------------------------------------------- // Key // -------------------------------------------------------------------------- export type Key = string | number @@ -297,6 +342,7 @@ export interface TFunction = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] export type TIndexProperty = K extends keyof T ? [T[K]] : [] export type TIndexTuple = K extends keyof T ? [T[K]] : [] @@ -308,6 +354,7 @@ export type TIndexType = T extends TObject ? UnionType>>> : T extends TTuple ? UnionType>>> : [] + // prettier-ignore export type TIndexRestMany = K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : @@ -320,6 +367,7 @@ export type TIndex = T extends TObject ? UnionType>> : T extends TTuple ? UnionType>> : TNever + // -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- @@ -1947,16 +1995,41 @@ export namespace TypeClone { // IndexedAccessor // -------------------------------------------------------------------------- export namespace IndexedAccessor { + function OptionalUnwrap(schema: TSchema[]): TSchema[] { + return schema.map((schema) => { + const { [Modifier]: _, ...clone } = TypeClone.Clone(schema, {}) + return clone + }) + } + function IsIntersectOptional(schema: TSchema[]): boolean { + return schema.every((schema) => TypeGuard.TOptional(schema)) + } + function IsUnionOptional(schema: TSchema[]): boolean { + return schema.some((schema) => TypeGuard.TOptional(schema)) + } + function ResolveIntersect(schema: TIntersect): TSchema { + const optional = IsIntersectOptional(schema.allOf) + return optional ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema + } + function ResolveUnion(schema: TUnion): TSchema { + const optional = IsUnionOptional(schema.anyOf) + return optional ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema + } + function ResolveOptional(schema: TSchema) { + if (schema[Kind] === 'Intersect') return ResolveIntersect(schema as TIntersect) + if (schema[Kind] === 'Union') return ResolveUnion(schema as TUnion) + return schema + } function Intersect(schema: TIntersect, key: string): TSchema { - const schemas = schema.allOf.reduce((acc, schema) => { + const resolved = schema.allOf.reduce((acc, schema) => { const indexed = Visit(schema, key) return indexed[Kind] === 'Never' ? acc : [...acc, indexed] }, [] as TSchema[]) - return Type.Intersect(schemas) + return ResolveOptional(Type.Intersect(resolved)) } function Union(schema: TUnion, key: string): TSchema { - const schemas = schema.anyOf.map((schema) => Visit(schema, key)) - return Type.Union(schemas) + const resolved = schema.anyOf.map((schema) => Visit(schema, key)) + return ResolveOptional(Type.Union(resolved)) } function Object(schema: TObject, key: string): TSchema { const property = schema.properties[key] @@ -1977,8 +2050,8 @@ export namespace IndexedAccessor { return Type.Never() } export function Resolve(schema: TSchema, keys: Key[], options: SchemaOptions = {}): TSchema { - // prettier-ignore - return Type.Union(keys.map((key) => Visit(schema, key.toString())), options) + const resolved = keys.map((key) => Visit(schema, key.toString())) + return ResolveOptional(Type.Union(resolved, options)) } } // -------------------------------------------------------------------------- diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index 86ced5e53..f473d31fd 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -27,8 +27,30 @@ describe('type/guard/TComposite', () => { // rule is "if all composite properties for a key are optional, then the composite property is optional". Defer this test and // document as minor breaking change. // - // it('Should produce optional property if all properties are optional', () => { - // const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) })]) - // Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) - // }) + it('Should produce optional property if all composited properties are optional', () => { + // prettier-ignore + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }) + ]) + Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) + Assert.isTrue(T.required === undefined) + }) + it('Should produce required property if some composited properties are not optional', () => { + // prettier-ignore + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }) + ]) + Assert.isEqual(TypeGuard.TOptional(T.properties.x), false) + Assert.isTrue(T.required!.includes('x')) + }) + it('Should preserve single optional property', () => { + // prettier-ignore + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + ]) + Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) + Assert.isTrue(T.required === undefined) + }) }) diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 822267285..4003880c3 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -277,4 +277,42 @@ describe('type/guard/TIndex', () => { // on the object. This may be resolvable in later revisions, but test for this // fall-through to ensure case is documented. For review. }) + // -------------------------------------------------------- + // Modifier Optional Indexing + // -------------------------------------------------------- + it('Should Index 31', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['x']) + Assert.isTrue(TypeGuard.TOptional(I)) + Assert.isTrue(TypeGuard.TString(I)) + }) + it('Should Index 32', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['y']) + Assert.isFalse(TypeGuard.TOptional(I)) + Assert.isTrue(TypeGuard.TNumber(I)) + }) + it('Should Index 33', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['x', 'y']) + Assert.isTrue(TypeGuard.TOptional(I)) + Assert.isTrue(TypeGuard.TUnion(I)) + Assert.isTrue(TypeGuard.TString(I.anyOf[0])) + Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + }) + it('Should Index 34', () => { + const T = Type.String() + // @ts-ignore + const I = Type.Index(T, ['x']) + Assert.isTrue(TypeGuard.TNever(I)) + }) }) diff --git a/test/runtime/type/normalize/index.ts b/test/runtime/type/normalize/index.ts index d0d7e13fd..051f3a9bb 100644 --- a/test/runtime/type/normalize/index.ts +++ b/test/runtime/type/normalize/index.ts @@ -1,5 +1,6 @@ import './exclude' import './extract' import './intersect' +import './indexed' import './record' import './union' diff --git a/test/runtime/type/normalize/indexed.ts b/test/runtime/type/normalize/indexed.ts new file mode 100644 index 000000000..eba47cb00 --- /dev/null +++ b/test/runtime/type/normalize/indexed.ts @@ -0,0 +1,140 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/normal/Index', () => { + // --------------------------------------------------------- + // Array + // --------------------------------------------------------- + it('Normalize Array 1', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Assert.isEqual(TypeGuard.TString(I), true) + }) + // --------------------------------------------------------- + // Tuple + // --------------------------------------------------------- + it('Normalize Tuple 1', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const I = Type.Index(T, Type.Number()) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + }) + it('Normalize Tuple 2', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const I = Type.Index(T, [0, 1]) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + }) + it('Normalize Tuple 3', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const I = Type.Index(T, ['0', '1']) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + }) + it('Normalize Tuple 4', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const I = Type.Index(T, ['0']) + Assert.isEqual(TypeGuard.TString(I), true) + }) + it('Normalize Tuple 5', () => { + const T = Type.Tuple([Type.String(), Type.Number()]) + const I = Type.Index(T, ['1']) + Assert.isEqual(TypeGuard.TNumber(I), true) + }) + // --------------------------------------------------------- + // Intersect + // --------------------------------------------------------- + it('Normalize Intersect 1', () => { + const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), true) + Assert.isEqual(TypeGuard.TIntersect(I), true) + Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + }) + it('Normalize Intersect 2', () => { + const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), false) + Assert.isEqual(TypeGuard.TIntersect(I), true) + Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + }) + it('Normalize Intersect 3', () => { + const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), false) + Assert.isEqual(TypeGuard.TIntersect(I), true) + Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + }) + it('Normalize Intersect 4', () => { + const T = Type.Intersect([Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TString(I), true) + }) + it('Normalize Intersect 5', () => { + const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) + const I = Type.Index(T, ['x', 'y']) + Assert.isEqual(TypeGuard.TOptional(I), false) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Normalize Intersect 6', () => { + const T = Type.Recursive(() => Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) + const I = Type.Index(T, ['x', 'y']) + Assert.isEqual(TypeGuard.TOptional(I), false) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + // --------------------------------------------------------- + // Union + // --------------------------------------------------------- + it('Normalize Union 1', () => { + const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Normalize Union 2', () => { + const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), true) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Normalize Union 3', () => { + const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TOptional(I), false) + Assert.isEqual(TypeGuard.TUnion(I), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) + Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + }) + it('Normalize Union 4', () => { + const T = Type.Union([Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.isEqual(TypeGuard.TString(I), true) + }) + it('Normalize Union 5', () => { + const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) + // @ts-ignore + const I = Type.Index(T, ['x', 'y']) + Assert.isEqual(TypeGuard.TNever(I), false) + }) + it('Normalize Union 6', () => { + const T = Type.Recursive(() => Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) + // @ts-ignore + const I = Type.Index(T, ['x', 'y']) + Assert.isEqual(TypeGuard.TNever(I), false) + }) +}) From da10cc09fa4adc159dccaaac9d6ff038f970c1e6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 4 Jul 2023 07:27:22 +0900 Subject: [PATCH 155/369] Revision 0.29.3 (#487) --- package.json | 2 +- src/typebox.ts | 4 ++-- test/static/keyof.ts | 9 +++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2b017e2bf..2971f5b17 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.2", + "version": "0.29.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 5d8c046d0..f67705d80 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -393,9 +393,9 @@ export interface TIntersect extends TSchema, In // TKeyOf // -------------------------------------------------------------------------- // prettier-ignore -export type TKeyOfProperties = Static extends infer S +export type TKeyOfProperties = Discard extends infer S ? UnionToTuple<{[K in keyof S]: TLiteral>}[keyof S]> - : [] + : [], undefined> // note: optional properties produce undefined types in tuple result. discard. // prettier-ignore export type TKeyOfIndicesArray = UnionToTuple // prettier-ignore diff --git a/test/static/keyof.ts b/test/static/keyof.ts index bda75e87f..e5ad39fed 100644 --- a/test/static/keyof.ts +++ b/test/static/keyof.ts @@ -91,3 +91,12 @@ import { Type } from '@sinclair/typebox' const K = Type.KeyOf(T) Expect(K).ToInfer<'a' | 'b' | 'c' | 'd'>() } +{ + const T = Type.Object({ + a: Type.Optional(Type.String()), + b: Type.Optional(Type.String()), + c: Type.Optional(Type.String()), + }) + const K = Type.KeyOf(T) + Expect(K).ToInfer<'a' | 'b' | 'c'>() +} From 3f50917d98e5c9160fb5d7a1c8bbe9f6ab3e560c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 5 Jul 2023 00:09:03 +0900 Subject: [PATCH 156/369] Scheduled Nightly Builds (#488) --- .github/workflows/{ci.yml => build.yml} | 8 ++--- .github/workflows/nightly.yml | 30 +++++++++++++++++ hammer.mjs | 44 ++++++++++++------------- package-lock.json | 4 +-- readme.md | 4 +-- 5 files changed, 60 insertions(+), 30 deletions(-) rename .github/workflows/{ci.yml => build.yml} (90%) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/build.yml similarity index 90% rename from .github/workflows/ci.yml rename to .github/workflows/build.yml index b1192b393..e482eb7e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,5 @@ -name: GitHub CI - +name: Build on: [push, pull_request] - jobs: TypeBox: runs-on: ${{ matrix.os }} @@ -12,11 +10,13 @@ jobs: steps: - uses: actions/checkout@v2 - name: Install Node - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} + - name: Install Packages run: npm install + - name: Build Library run: npm run build - name: Test Library diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 000000000..7ad6b9d88 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,30 @@ +name: Build Nightly +on: + schedule: + - cron: '0 18 * * *' # 6pm Daily +jobs: + TypeBox: + runs-on: ${{ matrix.os }} + strategy: + matrix: + node: [20.x] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + + - name: Install Packages + run: npm install + + - name: Install TypeScript Latest + run: npm install typescript@latest + - name: Build TypeBox + run: npm run build + + - name: Install TypeScript Next + run: npm install typescript@next + - name: Build TypeBox + run: npm run build \ No newline at end of file diff --git a/hammer.mjs b/hammer.mjs index e47b88f82..a6bca8b8b 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -5,59 +5,59 @@ import { readFileSync } from 'fs' // Clean // ------------------------------------------------------------------------------- export async function clean() { - await folder('target').delete() + await folder('target').delete() } // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') } // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- export async function start(example = 'index') { - await shell(`hammer run example/${example}.ts --dist target/example/${example}`) + await shell(`hammer run example/${example}.ts --dist target/example/${example}`) } // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- export async function benchmark() { - await compression() - await measurement() + await compression() + await measurement() } // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- export async function test_static() { - await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) + await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) } -export async function test_runtime(filter) { - await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) - await shell(`mocha target/test/runtime/index.js -g "${filter}"`) +export async function test_runtime(filter = '') { + await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) + await shell(`mocha target/test/runtime/index.js -g "${filter}"`) } export async function test(filter = '') { - await test_static() - await test_runtime(filter) + await test_static() + await test_runtime(filter) } // ------------------------------------------------------------------------------- // Build // ------------------------------------------------------------------------------- export async function build(target = 'target/build') { - await test() - await folder(target).delete() - await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) - await folder(target).add('package.json') - await folder(target).add('readme.md') - await folder(target).add('license') - await shell(`cd ${target} && npm pack`) + await test() + await folder(target).delete() + await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) + await folder(target).add('package.json') + await folder(target).add('readme.md') + await folder(target).add('license') + await shell(`cd ${target} && npm pack`) } // ------------------------------------------------------------- // Publish // ------------------------------------------------------------- export async function publish(otp, target = 'target/build') { - const { version } = JSON.parse(readFileSync('package.json', 'utf8')) - await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) - await shell(`git tag ${version}`) - await shell(`git push origin ${version}`) + const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) + await shell(`git tag ${version}`) + await shell(`git push origin ${version}`) } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3cb6da902..31f90d3b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.29.1", + "version": "0.29.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.29.1", + "version": "0.29.3", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/readme.md b/readme.md index 7f5d64338..68c792208 100644 --- a/readme.md +++ b/readme.md @@ -11,8 +11,8 @@ [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox) -[![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Build](https://github.com/sinclairzx81/typebox/actions/workflows/build.yml/badge.svg)](https://github.com/sinclairzx81/typebox/actions/workflows/build.yml) +[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) From 185eb13dc9ee1ad62ef683b0620d3cdaadad0cae Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 5 Jul 2023 05:05:16 +0900 Subject: [PATCH 157/369] General Maintenance (#489) --- example/collections/readme.md | 3 + example/experimental/experimental.ts | 109 ---- example/experimental/index.ts | 6 +- example/experimental/readme.md | 64 ++- example/experimental/readonly-object.ts | 68 +++ .../index.ts => experimental/union-enum.ts} | 22 +- .../index.ts => experimental/union-oneof.ts} | 19 +- example/formats/readme.md | 30 +- .../{intersect.ts => intersect-0.25.0.ts} | 0 example/typedef/readme.md | 315 ++++++++--- example/typedef/typedef.ts | 212 ++++---- example/typemap/readme.md | 7 - example/typemap/typemap.ts | 496 ------------------ hammer.mjs | 42 +- 14 files changed, 543 insertions(+), 850 deletions(-) create mode 100644 example/collections/readme.md delete mode 100644 example/experimental/experimental.ts create mode 100644 example/experimental/readonly-object.ts rename example/{typedef/index.ts => experimental/union-enum.ts} (56%) rename example/{legacy/index.ts => experimental/union-oneof.ts} (59%) rename example/legacy/{intersect.ts => intersect-0.25.0.ts} (100%) delete mode 100644 example/typemap/readme.md delete mode 100644 example/typemap/typemap.ts diff --git a/example/collections/readme.md b/example/collections/readme.md new file mode 100644 index 000000000..9c7adf48f --- /dev/null +++ b/example/collections/readme.md @@ -0,0 +1,3 @@ +# Collections + +This example implements runtime type safe generic `Array`, `Map` and `Set` collection types using TypeBox types as the generic type arguments. \ No newline at end of file diff --git a/example/experimental/experimental.ts b/example/experimental/experimental.ts deleted file mode 100644 index facf22474..000000000 --- a/example/experimental/experimental.ts +++ /dev/null @@ -1,109 +0,0 @@ - -/*-------------------------------------------------------------------------- - -@sinclair/typebox/experimental - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '@sinclair/typebox' -import { Value } from '@sinclair/typebox/value' - -// ------------------------------------------------------------------------------------- -// TReadonlyObject -// ------------------------------------------------------------------------------------- -export type TReadonlyArray = Types.Assert<{ [K in keyof T]: TReadonlyObject> }, Types.TSchema[]> -// prettier-ignore -export type TReadonlyProperties = Types.Evaluate ? Types.TReadonlyOptional : - T[K] extends Types.TReadonly ? Types.TReadonly : - T[K] extends Types.TOptional ? Types.TReadonlyOptional : - Types.TReadonly -}, Types.TProperties>> -// prettier-ignore -export type TReadonlyObject = - T extends Types.TIntersect ? Types.TIntersect> : - T extends Types.TUnion ? Types.TUnion> : - T extends Types.TObject ? Types.TObject> : - Types.TReadonly - -// ------------------------------------------------------------------------------------- -// TUnionEnum -// ------------------------------------------------------------------------------------- -export interface TUnionEnum extends Types.TSchema { - [Types.Kind]: 'UnionEnum' - static: T[number] - enum: T -} -// ------------------------------------------------------------------------------------- -// UnionOneOf -// ------------------------------------------------------------------------------------- -export interface UnionOneOf extends Types.TSchema { - [Types.Kind]: 'UnionOneOf' - static: { [K in keyof T]: Types.Static }[number] - oneOf: T -} -// ------------------------------------------------------------------------------------- -// ExperimentalTypeBuilder -// ------------------------------------------------------------------------------------- -export class ExperimentalTypeBuilder extends Types.ExtendedTypeBuilder { - /** `[Experimental]` Remaps a Intersect, Union or Object as readonly */ - public ReadonlyObject(schema: T): TReadonlyObject { - function Apply(property: Types.TSchema): any { - // prettier-ignore - switch (property[Types.Modifier]) { - case 'ReadonlyOptional': property[Types.Modifier] = 'ReadonlyOptional'; break - case 'Readonly': property[Types.Modifier] = 'Readonly'; break - case 'Optional': property[Types.Modifier] = 'ReadonlyOptional'; break - default: property[Types.Modifier] = 'Readonly'; break - } - return property - } - // prettier-ignore - return (Types.TypeGuard.TIntersect(schema) || Types.TypeGuard.TUnion(schema) || Types.TypeGuard.TObject(schema)) - ? Types.ObjectMap.Map>(schema, (schema) => { - globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) - return schema - }, {}) : Apply(schema) - } - /** `[Experimental]` Creates a Union type with a `enum` schema representation */ - public UnionEnum(values: [...T], options: Types.SchemaOptions = {}) { - function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { - return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) - } - if (!Types.TypeRegistry.Has('UnionEnum')) Types.TypeRegistry.Set('UnionEnum', UnionEnumCheck) - return { ...options, [Types.Kind]: 'UnionEnum', enum: values } as TUnionEnum - } - /** `[Experimental]` Creates a Union type with a `oneOf` schema representation */ - public UnionOneOf(oneOf: [...T], options: Types.SchemaOptions = {}) { - function UnionOneOfCheck(schema: UnionOneOf, value: unknown) { - return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) - } - if (!Types.TypeRegistry.Has('UnionOneOf')) Types.TypeRegistry.Set('UnionOneOf', UnionOneOfCheck) - return { ...options, [Types.Kind]: 'UnionOneOf', oneOf } as UnionOneOf - } -} - -export const Type = new ExperimentalTypeBuilder() \ No newline at end of file diff --git a/example/experimental/index.ts b/example/experimental/index.ts index 9dcc0367b..daddc72d8 100644 --- a/example/experimental/index.ts +++ b/example/experimental/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/extensions +@sinclair/typebox/experimental The MIT License (MIT) @@ -26,4 +26,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './experimental' +export * from './readonly-object' +export * from './union-enum' +export * from './union-oneof' diff --git a/example/experimental/readme.md b/example/experimental/readme.md index 16f8b3873..d22128721 100644 --- a/example/experimental/readme.md +++ b/example/experimental/readme.md @@ -1,30 +1,54 @@ -# ExperimentalTypeBuilder +# Experimental Types -An experimental TypeBox type builder with additional custom types. +These examples are a set of experiemental candidate types that may introduced into TypeBox in future. -## Overview +## ReadonlyObject -The TypeBox TypeBuilder classes are designed to be extended with user defined types. Instances where you may wish to do this are if your application is dependent on custom schematics and/or non-JSON serializable values (an example of which might be a Mongo's `ObjectId` or other such non-serializable value) +Maps an object properties as `readonly`. -## Application Type Builder +```typescript +import { ReadonlyObject } from './experimental' + +const T = ReadonlyObject(Type.Object({ + x: Type.Number() +})) + +type T = Static // type T = { + // readonly x: number + // } +``` +## UnionEnum -The following shows creating a simple `ApplicationTypeBuilder` with additional types `Nullable` and `StringEnum`. These types are fairly common in OpenAPI implementations. +Creates an `enum` union string schema representation. ```typescript -import { StandardTypeBuilder, Static, TSchema } from '@sinclair/typebox' - -export class ApplicationTypeBuilder extends StandardTypeBuilder { // only JSON Schema types - public Nullable(schema: T) { - return this.Unsafe | null>({ ...schema, nullable: true }) - } - public StringEnum(values: [...T]) { - return this.Unsafe({ type: 'string', enum: values }) - } -} - -export const Type = new ApplicationTypeBuilder() // re-export! +import { UnionEnum } from './experimental' + + +const T = UnionEnum(['A', 'B', 'C']) // const T = { + // enum: ['A', 'B', 'C'] + // } + +type T = Static // type T = 'A' | 'B' | 'C' + ``` +## UnionOneOf + +Creates a `oneOf` union representation. + + +```typescript +import { UnionOneOf } from './experimental' + + +const T = UnionOneOf([ // const T = { + Type.Literal('A'), // oneOf: [ + Type.Literal('B'), // { const: 'A' }, + Type.Literal('C') // { const: 'B' }, +]) // { const: 'C' }, + // ] + // } -## Experimental Type Builder +type T = Static // type T = 'A' | 'B' | 'C' -The `experimental.ts` file provided with this example shows advanced usage by creating complex types for potential inclusion in the TypeBox library in later revisions. It is offered for reference, experimentation and is open to contributor submission. \ No newline at end of file +``` \ No newline at end of file diff --git a/example/experimental/readonly-object.ts b/example/experimental/readonly-object.ts new file mode 100644 index 000000000..b6ce4bacc --- /dev/null +++ b/example/experimental/readonly-object.ts @@ -0,0 +1,68 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/experimental + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------- +// TReadonlyObject +// ------------------------------------------------------------------------------------- +export type TReadonlyArray = Types.Assert<{ [K in keyof T]: TReadonlyObject> }, Types.TSchema[]> +// prettier-ignore +export type TReadonlyProperties = Types.Evaluate ? Types.TReadonlyOptional : + T[K] extends Types.TReadonly ? Types.TReadonly : + T[K] extends Types.TOptional ? Types.TReadonlyOptional : + Types.TReadonly +}, Types.TProperties>> +// prettier-ignore +export type TReadonlyObject = + T extends Types.TIntersect ? Types.TIntersect> : + T extends Types.TUnion ? Types.TUnion> : + T extends Types.TObject ? Types.TObject> : + Types.TReadonly + +/** `[Experimental]` Remaps a Intersect, Union or Object as readonly */ +export function ReadonlyObject(schema: T): TReadonlyObject { + function Apply(property: Types.TSchema): any { + // prettier-ignore + switch (property[Types.Modifier]) { + case 'ReadonlyOptional': property[Types.Modifier] = 'ReadonlyOptional'; break + case 'Readonly': property[Types.Modifier] = 'Readonly'; break + case 'Optional': property[Types.Modifier] = 'ReadonlyOptional'; break + default: property[Types.Modifier] = 'Readonly'; break + } + return property + } + // prettier-ignore + return (Types.TypeGuard.TIntersect(schema) || Types.TypeGuard.TUnion(schema) || Types.TypeGuard.TObject(schema)) + ? Types.ObjectMap.Map>(schema, (schema) => { + globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) + return schema + }, {}) : Apply(schema) +} \ No newline at end of file diff --git a/example/typedef/index.ts b/example/experimental/union-enum.ts similarity index 56% rename from example/typedef/index.ts rename to example/experimental/union-enum.ts index b5db4f7aa..9f0bd77f1 100644 --- a/example/typedef/index.ts +++ b/example/experimental/union-enum.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/typedef +@sinclair/typebox/experimental The MIT License (MIT) @@ -26,4 +26,22 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './typedef' +import * as Types from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------- +// TUnionEnum +// ------------------------------------------------------------------------------------- +export interface TUnionEnum extends Types.TSchema { + [Types.Kind]: 'UnionEnum' + static: T[number] + enum: T +} + +/** `[Experimental]` Creates a Union type with a `enum` schema representation */ +export function UnionEnum(values: [...T], options: Types.SchemaOptions = {}) { + function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { + return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) + } + if (!Types.TypeRegistry.Has('UnionEnum')) Types.TypeRegistry.Set('UnionEnum', UnionEnumCheck) + return { ...options, [Types.Kind]: 'UnionEnum', enum: values } as TUnionEnum +} \ No newline at end of file diff --git a/example/legacy/index.ts b/example/experimental/union-oneof.ts similarity index 59% rename from example/legacy/index.ts rename to example/experimental/union-oneof.ts index 79f17933b..cec504644 100644 --- a/example/legacy/index.ts +++ b/example/experimental/union-oneof.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/legacy +@sinclair/typebox/experimental The MIT License (MIT) @@ -26,4 +26,19 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './intersect' \ No newline at end of file +import * as Types from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +export interface TUnionOneOf extends Types.TSchema { + [Types.Kind]: 'UnionOneOf' + static: { [K in keyof T]: Types.Static }[number] + oneOf: T +} +/** `[Experimental]` Creates a Union type with a `oneOf` schema representation */ +export function UnionOneOf(oneOf: [...T], options: Types.SchemaOptions = {}) { + function UnionOneOfCheck(schema: TUnionOneOf, value: unknown) { + return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) + } + if (!Types.TypeRegistry.Has('UnionOneOf')) Types.TypeRegistry.Set('UnionOneOf', UnionOneOfCheck) + return { ...options, [Types.Kind]: 'UnionOneOf', oneOf } as TUnionOneOf +} \ No newline at end of file diff --git a/example/formats/readme.md b/example/formats/readme.md index 03eb80d35..02d7b0c28 100644 --- a/example/formats/readme.md +++ b/example/formats/readme.md @@ -1,35 +1,11 @@ -# String Formats +# Formats -TypeBox does not implement any string formats by default. However it is possible to register user defined formats using the `FormatRegistry`. Once registered, the format becomes available to both `Value` and `TypeCompiler` modules. +This example provides TypeCompiler supported versions of the `ajv-formats` package. -## FormatRegistry - -The following shows basic usage of the format registry - -```typescript -import { Type, FormatRegistry } from '@sinclair/typebox' -import { Value } from '@sinclair/typebox/value' - -// Register the 'foo-only' format. The format checks for 'foo' only. -FormatRegistry.Set('foo-only', value => value === 'foo') - -const T = Type.String({ format: 'foo-only' }) - -// Validate -Value.Check(T, 'foo') // true -Value.Check(T, 'bar') // false -``` - -## Standard Formats +## Standard The `standard.ts` file provided with this example implements several standard string formats. -```typescript -import './standard' -``` - -The following formats are implemented by `standard.ts` - | Format | Description | | --- | --- | | `email` | Internet email address, see [RFC 5321, section 4.1.2.](http://tools.ietf.org/html/rfc5321#section-4.1.2) | diff --git a/example/legacy/intersect.ts b/example/legacy/intersect-0.25.0.ts similarity index 100% rename from example/legacy/intersect.ts rename to example/legacy/intersect-0.25.0.ts diff --git a/example/typedef/readme.md b/example/typedef/readme.md index 07837ad72..80639ff02 100644 --- a/example/typedef/readme.md +++ b/example/typedef/readme.md @@ -1,81 +1,266 @@ # TypeDef -TypeBox may offer support for [RPC8927](https://www.rfc-editor.org/rfc/rfc8927) JSON Type Definition in future revisions of the library. This specification is much simpler than JSON Schema but can be useful when describing schematics that need to be shared with nominal type languages. +TypeBox is considering support for the JSON Type Definition [RFC8927](https://www.rfc-editor.org/rfc/rfc8927) specification in future releases. This specification is similar to JSON Schema but provides a constrained type representation that enables schematics to map more naturally to [nominal type systems](https://en.wikipedia.org/wiki/Nominal_type_system) as well as offering type primitives such as `int8`, `uint32` or `float32`. JSON Type Definition can be useful in applications that need to express and share data structures in a way that can be understood by a wide range of programming languages outside of JavaScript. + +License MIT + +## Contents +- [Usage](#Usage) +- [Types](#Types) +- [Unions](#Unions) +- [Check](#Check) ## Usage -The file `typedef.ts` provided with this example contains the provisional implementation for RPC8927. +TypeBox currently doesn't publish TypeDef as part of the mainline package. However the TypeDef functionality is written to be a standalone module you can copy into your project. You will also need `@sinclair/typebox` installed. You can obtain the `typedef` module from `example/typedef/typedef.ts` contained within this repository. ```typescript -import { Value } from '@sinclair/typebox/value' import { Type, Static } from './typedef' -const T = Type.Struct('T', { // const T = { - x: Type.Float32(), // properties: { - y: Type.Float32(), // x: { type: "float32" }, - z: Type.Float32() // y: { type: 'float32' }, -}) // z: { type: 'float32' } - // } - // } - -type T = Static // type T = { - // x: number, - // z: number, - // y: number - // } - -const R = Value.Check(T, { x: 1, y: 2, z: 3 }) // const R = true +const T = Type.Struct({ // const T = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, + z: Type.Float32() // y: { type: 'float32' }, +}) // z: { type: 'float32' } + // } + // } + +type T = Static // type T = { + // x: number, + // y: number, + // z: number + // } ``` -### Unions +## Types -The JSON Type Definition has a different representation for unions and is primarily orientated towards discriminated unions. To use unions, you will need to name each struct on the first argument. TypeBox will take care of producing the union representation and static type. +The following types are supported by the typedef module. Please note these types are not compatible with the JSON Schema specification and should not be combined with the standard TypeBox types. ```typescript -import { Type, Static } from './typedef' +┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ +│ TypeBox │ TypeScript │ JSON Type Definition │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Boolean() │ type T = boolean │ const T = { │ +│ │ │ type: 'boolean' │ +│ │ │ } │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.String() │ type T = string │ const T = { │ +│ │ │ type: 'string' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Float32() │ type T = number │ const T = { │ +│ │ │ type: 'float32' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Float64() │ type T = number │ const T = { │ +│ │ │ type: 'float64' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Int8() │ type T = number │ const T = { │ +│ │ │ type: 'int8' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Int16() │ type T = number │ const T = { │ +│ │ │ type: 'int16' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Int32() │ type T = number │ const T = { │ +│ │ │ type: 'int32' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uint8() │ type T = number │ const T = { │ +│ │ │ type: 'uint8' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uint16() │ type T = number │ const T = { │ +│ │ │ type: 'uint16' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uint32() │ type T = number │ const T = { │ +│ │ │ type: 'uint32' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Timestamp() │ type T = number │ const T = { │ +│ │ │ type: 'timestamp' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Struct([ │ type T = { │ const T = { │ +│ x: Type.Float32(), │ x: number, │ properties: { │ +│ y: Type.Float32(), │ y: number │ x: number, │ +│ ]) │ } │ y: number │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Array( │ type T = number[] │ const T = { │ +│ Type.Float32() │ │ elements: { │ +│ ) │ │ type: 'float32' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Record( │ type T = Record< │ const T = { │ +│ Type.Float32() │ string, │ values: { │ +│ ) │ number │ type: 'float32' │ +│ │ > │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Enum([ │ type T = 'A' | 'B' | 'C' │ const T = { │ +│ 'A', 'B', 'C' │ │ enum: [ │ +│ ]) │ │ 'A', │ +│ │ │ 'B', │ +│ │ │ 'C' │ +│ │ │ ] │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Union([ │ type T = { │ const T = { │ +│ Type.Struct({ │ kind: '0', │ discriminator: 'kind', │ +│ x: Type.Float32() │ x: number │ mapping: { │ +│ }), │ } | { │ '0': { │ +│ Type.Struct({ │ kind: '1' │ properties: { │ +│ y: Type.Float32() │ y: number │ x: { │ +│ ]) │ } │ type: 'float32' │ +│ ], 'kind') │ │ } │ +│ │ │ } │ +│ │ │ }, │ +│ │ │ '1': { | +│ │ │ properties: { │ +│ │ │ y: { │ +│ │ │ type: 'float32' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +└────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ +``` + +## Unions -const Vector2 = Type.Struct('Vector2', { // const Vector2 = { - x: Type.Float32(), // properties: { - y: Type.Float32(), // x: { type: 'float32' }, -}) // y: { type: 'float32' } - // } - // } - -const Vector3 = Type.Struct('Vector3', { // const Vector3 = { - x: Type.Float32(), // properties: { - y: Type.Float32(), // x: { type: 'float32' }, - z: Type.Float32() // y: { type: 'float32' }, -}) // z: { type: 'float32' } - // } - // } - -const Union = Type.Union('type', [ // const Union = { - Vector2, // discriminator: 'type', - Vector3 // mapping: { -]) // Vector2: { - // properties: { - // x: { type: 'float32' }, - // y: { type: 'float32' }, - // } - // }, - // Vector3: { - // properties: { - // x: { type: 'float32' }, - // y: { type: 'float32' }, - // z: { type: 'float32' } - // } - // } - // } - // } - -type Union = Static // type Union = { - // type: 'Vector2' - // x: number - // y: number - // } | { - // type: 'Vector3' - // x: number - // y: number - // z: number - // } +TypeBox supports JSON Type Definition discriminated unions with `Type.Union`. This type works similar its JSON Schema counterpart, but can only accept types of `Type.Struct` and will infer each struct with an additional named `discriminator` field. The representation for discriminated unions are also quite different, where instead of `anyOf` or `oneOf`, a set of `mapping` properties are used for each sub type. + +```typescript +const Vector2 = Type.Struct({ // const Vector2 = { + x: Type.Float32(), // properties: { + y: Type.Float32() // x: { type: 'float32' }, +}) // y: { type: 'float32' } + // } + // } + +const Vector3 = Type.Struct({ // const Vector3 = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, + z: Type.Float32() // y: { type: 'float32' }, +}) // z: { type: 'float32' } + // } + // } + +const Vector4 = Type.Struct({ // const Vector4 = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, + z: Type.Float32(), // y: { type: 'float32' }, + w: Type.Float32() // z: { type: 'float32' }, +}) // w: { type: 'float32' } + // } + // } + +const T = Type.Union([ // const T = { + Vector2, // discriminator: 'type', + Vector3, // mapping: { + Vector4 // 0: { +]) // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' } + // } + // }, + // 1: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' }, + // z: { type: 'float32' } + // } + // }, + // 2: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' }, + // z: { type: 'float32' } + // } + // } + // } + // } + + +type T = Static // type T = { + // type: '0', + // x: number, + // y: number + // } | { + // type: '1', + // x: number, + // y: number, + // y: number + // } | { + // type: '2', + // x: number, + // y: number, + // y: number, + // w: number + // } ``` +To type check a value matching the above union, the value will need to contain the discriminator property `type` with a value matching one of the sub type `mapping` keys. The inference type shown above can be a good reference point to understand the structure of the expected value. Nominal type systems will use the discriminator to an expected target type. + +The following are examples of valid and invalid union data. + +```typescript + +const V = { x: 1, y: 1 } // invalid Vector2 + +const V = { type: '0', x: 1, y: 1 } // valid Vector2 + +const V = { type: '0', x: 1, y: 1, z: 1 } // invalid Vector2 + +const V = { type: '1', x: 1, y: 1, z: 1 } // valid Vector3 +``` + + +## Check + +TypeDef types are partially supported with the `TypeCompiler` and `Value` checking modules through the extensible type system in TypeBox. Please note these types are not optimized for JIT performance and do not provide deep error reporting support. For more fully featured validation support consider Ajv. Documentation of Ajv support can be found [here](https://ajv.js.org/json-type-definition.html). + +The following is TypeDef used with TypeBox's type checking infrastructure. + +```typescript +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Value } from '@sinclair/typebox/value' + +const T = Type.Struct({ + x: Type.Float32(), + y: Type.Float32(), + z: Type.Float32() +}) + +const V = { + x: 1, + y: 2, + z: 3 +} + +const R1 = TypeCompiler.Compile(T).Check(V) // true + +const R2 = Value.Check(T, V) // true +``` \ No newline at end of file diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index c89b52a56..6999df589 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -26,13 +26,20 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { type Static, TSchema, PropertiesReduce, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' +export { Static, Evaluate, TSchema, PropertiesReduce, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' import * as Types from '@sinclair/typebox' // -------------------------------------------------------------------------- -// Symbols +// Utility Types // -------------------------------------------------------------------------- -export const Name = Symbol.for('TypeBox:Name') +export type Assert = T extends U ? T : never +export type Base = { m: string, t: string } +export type Base16 = { m: 'F', t: '01', '0': '1', '1': '2', '2': '3', '3': '4', '4': '5', '5': '6', '6': '7', '7': '8', '8': '9', '9': 'A', 'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': '0' } +export type Base10 = { m: '9', t: '01', '0': '1', '1': '2', '2': '3', '3': '4', '4': '5', '5': '6', '6': '7', '7': '8', '8': '9', '9': '0' } +export type Reverse = T extends `${infer L}${infer R}` ? `${Reverse}${L}` : T +export type Tick = T extends keyof B ? B[T] : never +export type Next = T extends Assert['m'] ? Assert['t'] : T extends `${infer L}${infer R}` ? L extends Assert['m'] ? `${Assert, string>}${Next}` : `${Assert, string>}${R}` : never +export type Increment = Reverse, B>> // -------------------------------------------------------------------------- // TArray // -------------------------------------------------------------------------- @@ -50,6 +57,19 @@ export interface TBoolean extends Types.TSchema { type: 'boolean' } // -------------------------------------------------------------------------- +// TUnion +// -------------------------------------------------------------------------- +type InferUnion = T extends [infer L, ...infer R] + ? Types.Evaluate<{ [_ in D]: Index } & Types.Static>> | InferUnion, D, Increment>> + : never + +export interface TUnion extends Types.TSchema { + [Types.Kind]: 'TypeDef:Union' + static: InferUnion + discriminator: D, + mapping: T +} +// -------------------------------------------------------------------------- // TEnum // -------------------------------------------------------------------------- export interface TEnum extends Types.TSchema { @@ -60,7 +80,7 @@ export interface TEnum extends Types.TSchema { // -------------------------------------------------------------------------- // TFloat32 // -------------------------------------------------------------------------- -export interface TFloat32 extends Types.TSchema { +export interface TFloat32 extends Types.TSchema { [Types.Kind]: 'TypeDef:Float32' type: 'float32' static: number @@ -68,7 +88,7 @@ export interface TFloat32 extends Types.TSchema { // -------------------------------------------------------------------------- // TFloat64 // -------------------------------------------------------------------------- -export interface TFloat64 extends Types.TSchema { +export interface TFloat64 extends Types.TSchema { [Types.Kind]: 'TypeDef:Float64' type: 'float64' static: number @@ -76,7 +96,7 @@ export interface TFloat64 extends Types.TSchema { // -------------------------------------------------------------------------- // TInt8 // -------------------------------------------------------------------------- -export interface TInt8 extends Types.TSchema { +export interface TInt8 extends Types.TSchema { [Types.Kind]: 'TypeDef:Int8' type: 'int8' static: number @@ -84,7 +104,7 @@ export interface TInt8 extends Types.TSchema { // -------------------------------------------------------------------------- // TInt16 // -------------------------------------------------------------------------- -export interface TInt16 extends Types.TSchema { +export interface TInt16 extends Types.TSchema { [Types.Kind]: 'TypeDef:Int16' type: 'int16' static: number @@ -92,7 +112,7 @@ export interface TInt16 extends Types.TSchema { // -------------------------------------------------------------------------- // TInt32 // -------------------------------------------------------------------------- -export interface TInt32 extends Types.TSchema { +export interface TInt32 extends Types.TSchema { [Types.Kind]: 'TypeDef:Int32' type: 'int32' static: number @@ -100,7 +120,7 @@ export interface TInt32 extends Types.TSchema { // -------------------------------------------------------------------------- // TUint8 // -------------------------------------------------------------------------- -export interface TUint8 extends Types.TSchema { +export interface TUint8 extends Types.TSchema { [Types.Kind]: 'TypeDef:Uint8' type: 'uint8' static: number @@ -108,7 +128,7 @@ export interface TUint8 extends Types.TSchema { // -------------------------------------------------------------------------- // TUint16 // -------------------------------------------------------------------------- -export interface TUint16 extends Types.TSchema { +export interface TUint16 extends Types.TSchema { [Types.Kind]: 'TypeDef:Uint16' type: 'uint16' static: number @@ -148,12 +168,11 @@ type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TRe export interface StructOptions { additionalProperties?: boolean } -export interface TStruct extends Types.TSchema, StructOptions { - [Name]: D +export interface TStruct extends Types.TSchema, StructOptions { [Types.Kind]: 'TypeDef:Struct' static: Types.PropertiesReduce - optionalProperties: {[K in Types.Assert, keyof T>]: T[K] } - properties: {[K in Types.Assert, keyof T>]: T[K] } + optionalProperties: { [K in Types.Assert, keyof T>]: T[K] } + properties: { [K in Types.Assert, keyof T>]: T[K] } } // -------------------------------------------------------------------------- // TTimestamp @@ -163,43 +182,34 @@ export interface TTimestamp extends Types.TSchema { static: number } // -------------------------------------------------------------------------- -// TUnion -// -------------------------------------------------------------------------- -export interface TUnion extends Types.TSchema { - [Types.Kind]: 'TypeDef:Union' - static: Types.Evaluate<{ [K in keyof T]: { [key in D]: T[K][typeof Name] } & Types.Static }[number]> - discriminator: D, - mapping: T -} -// -------------------------------------------------------------------------- // TypeRegistry // -------------------------------------------------------------------------- -Types.TypeRegistry.Set('TypeDef:Array', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:String', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => TypeDefCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => TypeDefCheck.Check(schema, value)) -// -------------------------------------------------------------------------- -// TypeDefCheck -// -------------------------------------------------------------------------- -export class TypeDefCheckUnionTypeError extends Error { +Types.TypeRegistry.Set('TypeDef:Array', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:String', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => ValueCheck.Check(schema, value)) +// -------------------------------------------------------------------------- +// ValueCheck +// -------------------------------------------------------------------------- +export class ValueCheckError extends Error { constructor(public readonly schema: Types.TSchema) { - super('TypeDefCheck: Unknown type') + super('ValueCheck: Unknown type') } } -export namespace TypeDefCheck { +export namespace ValueCheck { // ------------------------------------------------------------------------ // Guards // ------------------------------------------------------------------------ - function IsObject(value: unknown): value is Record { + function IsObject(value: unknown): value is Record { return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) } function IsArray(value: unknown): value is unknown[] { @@ -253,37 +263,46 @@ export namespace TypeDefCheck { function String(schema: TString, value: unknown): boolean { return typeof value === 'string' } - function Struct(schema: TStruct, value: unknown): boolean { + function Struct(schema: TStruct, value: unknown, descriminator?: string): boolean { + if (!IsObject(value)) return false const optionalKeys = schema.optionalProperties === undefined ? [] : globalThis.Object.getOwnPropertyNames(schema.optionalProperties) const requiredKeys = schema.properties === undefined ? [] : globalThis.Object.getOwnPropertyNames(schema.properties) - if(!(IsObject(value) && - optionalKeys.every(key => key in value ? Visit((schema.optionalProperties as any)[key], value[key]) : true) && - requiredKeys.every(key => key in value && Visit(((schema as any).properties[key] as any), value[key])))) return false - if(schema.additionalProperties === true) return true const unknownKeys = globalThis.Object.getOwnPropertyNames(value) - return unknownKeys.every(key => optionalKeys.includes(key) || requiredKeys.includes(key)) + for (const requiredKey of requiredKeys) { + if (!(requiredKey in value)) return false + const requiredProperty = value[requiredKey] + const requiredSchema = (schema as any).properties[requiredKey] + if (!Visit(requiredSchema, requiredProperty)) return false + } + for (const optionalKey of optionalKeys) { + if (!(optionalKey in value)) continue + const optionalProperty = value[optionalKey] + const optionalSchema = (schema as any).properties[optionalKey] + if (!Visit(optionalSchema, optionalProperty)) return false + } + if (schema.additionalProperties === true) return true + const knownKeys = [...optionalKeys, ...requiredKeys] + for (const unknownKey of unknownKeys) if (!knownKeys.includes(unknownKey) && (descriminator !== undefined && unknownKey !== descriminator)) return false + for (const knownKey of knownKeys) if (!unknownKeys.includes(knownKey)) return false + return true } function Timestamp(schema: TString, value: unknown): boolean { return IsInt(value, 0, Number.MAX_SAFE_INTEGER) } function Union(schema: TUnion, value: unknown): boolean { - if (!( - IsObject(value) && - schema.discriminator in value && - IsString(value[schema.discriminator]) && - value[schema.discriminator] as any in schema.mapping - )) return false - // We shouldn't create objects just to omit the discriminator (optimize) - const inner = globalThis.Object.keys(value).reduce((acc, key) => { - return key === schema.discriminator ? acc : { [key]: value[key] } - }, {}) - return Visit(schema.mapping[value[schema.discriminator] as any], inner) + if (!IsObject(value)) return false + if (!(schema.discriminator in value)) return false + if (!IsString(value[schema.discriminator])) return false + if (!(value[schema.discriminator] in schema.mapping)) return false + const struct = schema.mapping[value[schema.discriminator]] as TStruct + return Struct(struct, value, schema.discriminator) } function Visit(schema: Types.TSchema, value: unknown): boolean { const anySchema = schema as any - switch(anySchema[Types.Kind]) { + switch (anySchema[Types.Kind]) { case 'TypeDef:Array': return Array(anySchema, value) case 'TypeDef:Boolean': return Boolean(anySchema, value) + case 'TypeDef:Union': return Union(anySchema, value) case 'TypeDef:Enum': return Enum(anySchema, value) case 'TypeDef:Float32': return Float32(anySchema, value) case 'TypeDef:Float64': return Float64(anySchema, value) @@ -297,11 +316,10 @@ export namespace TypeDefCheck { case 'TypeDef:String': return String(anySchema, value) case 'TypeDef:Struct': return Struct(anySchema, value) case 'TypeDef:Timestamp': return Timestamp(anySchema, value) - case 'TypeDef:Union': return Union(anySchema, value) - default: throw new TypeDefCheckUnionTypeError(anySchema) + default: throw new ValueCheckError(anySchema) } } - export function Check(schema: Types.TSchema, value: unknown): boolean { + export function Check(schema: T, value: unknown): value is Types.Static { return Visit(schema, value) } } @@ -325,83 +343,79 @@ export class TypeDefTypeBuilder extends Types.TypeBuilder { return { [Types.Modifier]: 'Readonly', ...schema } } // ------------------------------------------------------------------------ - // Modifiers + // Types // ------------------------------------------------------------------------ - /** `[Standard]` Creates a TypeDef Array type */ + /** [Standard] Creates a Array type */ public Array(elements: T): TArray { return this.Create({ [Types.Kind]: 'TypeDef:Array', elements }) } - /** `[Standard]` Creates a TypeDef Boolean type */ + /** [Standard] Creates a Boolean type */ public Boolean(): TBoolean { return this.Create({ [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }) } - /** `[Standard]` Creates a TypeDef Enum type */ + /** [Standard] Creates a Enum type */ public Enum(values: [...T]): TEnum { return this.Create({ [Types.Kind]: 'TypeDef:Enum', enum: values }) } - /** `[Standard]` Creates a TypeDef Float32 type */ + /** [Standard] Creates a Float32 type */ public Float32(): TFloat32 { return this.Create({ [Types.Kind]: 'TypeDef:Float32', type: 'float32' }) } - /** `[Standard]` Creates a TypeDef Float64 type */ + /** [Standard] Creates a Float64 type */ public Float64(): TFloat64 { return this.Create({ [Types.Kind]: 'TypeDef:Float64', type: 'float64' }) } - /** `[Standard]` Creates a TypeDef Int8 type */ + /** [Standard] Creates a Int8 type */ public Int8(): TInt8 { return this.Create({ [Types.Kind]: 'TypeDef:Int8', type: 'int8' }) } - /** `[Standard]` Creates a TypeDef Int16 type */ + /** [Standard] Creates a Int16 type */ public Int16(): TInt16 { return this.Create({ [Types.Kind]: 'TypeDef:Int16', type: 'int16' }) } - /** `[Standard]` Creates a TypeDef Int32 type */ + /** [Standard] Creates a Int32 type */ public Int32(): TInt32 { return this.Create({ [Types.Kind]: 'TypeDef:Int32', type: 'int32' }) } - /** `[Standard]` Creates a TypeDef Uint8 type */ + /** [Standard] Creates a Uint8 type */ public Uint8(): TUint8 { return this.Create({ [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }) } - /** `[Standard]` Creates a TypeDef Uint16 type */ + /** [Standard] Creates a Uint16 type */ public Uint16(): TUint16 { return this.Create({ [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }) } - /** `[Standard]` Creates a TypeDef Uint32 type */ + /** [Standard] Creates a Uint32 type */ public Uint32(): TUint32 { return this.Create({ [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }) } - /** `[Standard]` Creates a TypeDef Record type */ + /** [Standard] Creates a Record type */ public Record(values: T): TRecord { - return this.Create({ [Types.Kind]: 'TypeDef:Record',values }) + return this.Create({ [Types.Kind]: 'TypeDef:Record', values }) } - /** `[Standard]` Creates a TypeDef String type */ + /** [Standard] Creates a String type */ public String(): TString { - return this.Create({ [Types.Kind]: 'TypeDef:String',type: 'string' }) - } - /** `[Standard]` Creates a TypeDef Struct type */ - public Struct(name: N, fields: T, options?: StructOptions): TStruct { + return this.Create({ [Types.Kind]: 'TypeDef:String', type: 'string' }) + } + /** [Standard] Creates a Struct type */ + public Struct(fields: T, options?: StructOptions): TStruct { const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) - const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? {... acc } : { ...acc, [key]: fields[key] }), {} as TFields) - const optionalPropertiesObject = globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0 ? { optionalProperties: optionalProperties } : {} - const propertiesObject = globalThis.Object.getOwnPropertyNames(properties).length === 0 ? {} : { properties: properties } - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Struct', [Name]: name, ...propertiesObject, ...optionalPropertiesObject }) + const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? { ...acc } : { ...acc, [key]: fields[key] }), {} as TFields) + const optionalObject = globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0 ? { optionalProperties: optionalProperties } : {} + const requiredObject = globalThis.Object.getOwnPropertyNames(properties).length === 0 ? {} : { properties: properties } + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Struct', ...requiredObject, ...optionalObject }) + } + /** [Standard] Creates a Union type */ + public Union[], D extends string = 'type'>(structs: [...T], discriminator?: D): TUnion { + discriminator = (discriminator || 'type') as D + if (structs.length === 0) throw new Error('TypeDefTypeBuilder: Union types must contain at least one struct') + const mapping = structs.reduce((acc, current, index) => ({ ...acc, [index.toString()]: current }), {}) + return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }) } - /** `[Standard]` Creates a TypeDef Timestamp type */ + /** [Standard] Creates a Timestamp type */ public Timestamp(): TTimestamp { return this.Create({ [Types.Kind]: 'TypeDef:Timestamp', type: 'timestamp' }) } - - /** `[Standard]` Creates a TypeDef Discriminated Union type */ - public Union[]>(discriminator: D, objects: [...T]): TUnion { - if(objects.length === 0) throw new Error('TypeDefTypeBuilder: Union types must have at least one object') - const exists = objects.every(object => typeof object[Name] === 'string') - if(!exists) throw new Error('TypeDefTypeBuilder: All union objects MUST have a descriminator') - const unique = objects.reduce((set, current) => set.add(current[Name]), new Set()) - if(unique.size !== objects.length) throw new Error('TypeDefTypeBuilder: All union objects MUST unique descriminator strings') - const mapping = objects.reduce((acc, current) => ({ ...acc, [current[Name]]: current }), {}) - return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }) - } } /** JSON Type Definition Type Builder */ diff --git a/example/typemap/readme.md b/example/typemap/readme.md deleted file mode 100644 index fdadd3ab2..000000000 --- a/example/typemap/readme.md +++ /dev/null @@ -1,7 +0,0 @@ -# TypeMap - -TypeMap is a fluent type builder abstraction built on top of TypeBox. It is modelled after the Yup and Zod and libraries, but internally uses TypeBox for type composition, inference and runtime type assertion. It supports the same advanced compositional types as TypeBox (including generics, conditional, recursive and template literal types), but offers this functionality as a set of chainable lowercase types. - -Like TypeBox, TypeMap internally uses JSON schema for its type representation. It provides access to the TypeBox compiler infrastructure with the `.compile()` and `.code()` methods which are available on all types, and access to the internal schema via `.schema()`. Types also implement `.check()` and `.cast()` for convenience. - -TypeMap is implemented as an example for reference purposes only. \ No newline at end of file diff --git a/example/typemap/typemap.ts b/example/typemap/typemap.ts deleted file mode 100644 index 9352ff370..000000000 --- a/example/typemap/typemap.ts +++ /dev/null @@ -1,496 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/typemap - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { TypeCompiler, ValueError, TypeCheck } from '@sinclair/typebox/compiler' -import { Value, ValueErrorIterator } from '@sinclair/typebox/value' -import { TypeSystem } from '@sinclair/typebox/system' -import * as T from '@sinclair/typebox' - -// ------------------------------------------------------------------------------------------------- -// Formats -// ------------------------------------------------------------------------------------------------- -// prettier-ignore -TypeSystem.Format('email', (value) => /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(value)) -// prettier-ignore -TypeSystem.Format('uuid', (value) => /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i.test(value)) -// prettier-ignore -TypeSystem.Format('url', (value) => /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu.test(value)) -// prettier-ignore -TypeSystem.Format('ipv6', (value) => /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i.test(value)) -// prettier-ignore -TypeSystem.Format('ipv4', (value) => /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/.test(value)) -// ------------------------------------------------------------------------------------------------- -// Type Mapping -// ------------------------------------------------------------------------------------------------- -export type TypeToType = T extends Type ? S : never -export type TypeToTuple = { - [K in keyof T]: T[K] extends Type ? S : never -} -export type TypeToProperties = T.Assert< - { - [K in keyof T]: T[K] extends Type ? S : never - }, - T.TProperties -> -export type PropertiesType = Record -// prettier-ignore -export type TemplateLiteralType = - | Type - | Type - | Type - | Type - | Type - | Type - | Type - | Type - | Type; -// ------------------------------------------------------------------------------------------------- -// Error -// ------------------------------------------------------------------------------------------------- -export class TypeValueError extends Error { - constructor(public readonly errors: ValueError[]) { - super('TypeValueError: Invalid Value') - } -} -// ------------------------------------------------------------------------------------------------- -// Assert -// ------------------------------------------------------------------------------------------------- -export interface TypeAssert { - check(value: unknown): value is T.Static - errors(value: unknown): ValueErrorIterator - code(): string -} -export class TypeAssertDynamic implements TypeAssert { - readonly #schema: T - constructor(schema: T) { - this.#schema = schema - } - public check(value: unknown): value is T.Static { - return Value.Check(this.#schema, [], value) - } - public errors(value: unknown): ValueErrorIterator { - return Value.Errors(this.#schema, [], value) - } - public code(): string { - return TypeCompiler.Code(this.#schema) - } -} -export class TypeAssertCompiled implements TypeAssert { - readonly #typecheck: TypeCheck - constructor(schema: T) { - this.#typecheck = TypeCompiler.Compile(schema) - } - public check(value: unknown): value is T.Static { - return this.#typecheck.Check(value) - } - public errors(value: unknown): ValueErrorIterator { - return this.#typecheck.Errors(value) - } - public code(): string { - return this.#typecheck.Code() - } -} -// ----------------------------------------------------------------------------------------- -// Type -// ----------------------------------------------------------------------------------------- -export class Type { - #assert: TypeAssert - #schema: T - constructor(schema: T) { - this.#assert = new TypeAssertDynamic(schema) - this.#schema = schema - } - /** Augments this type with the given options */ - public options(options: T.SchemaOptions) { - return new Type({ ...this.schema, ...options } as T) - } - /** Maps a property as readonly and optional */ - public readonlyOptional(): Type> { - return new Type(T.Type.ReadonlyOptional(this.#schema)) - } - /** Maps a property as optional */ - public optional(): Type> { - return new Type(T.Type.Optional(this.#schema)) - } - /** Maps a property as readonly */ - public readonly(): Type> { - return new Type(T.Type.Readonly(this.#schema)) - } - /** Composes this type as a intersect with the given type */ - public and(type: U): Type]>> { - return new Type(T.Type.Intersect([this.#schema, type.schema()])) as any - } - /** Composes this type as a union with the given type */ - public or(type: U): Type]>> { - return new Type(T.Type.Union([this.#schema, type.schema()])) as any - } - /** Picks the given properties from this type */ - public pick)[]>(keys: readonly [...K]): Type> - /** Picks the given properties from this type */ - public pick[]>>(keys: K): Type>> - /** Picks the given properties from this type */ - public pick>(key: K): Type> - /** Picks the given properties from this type */ - public pick(key: K): Type> { - return new Type(T.Type.Pick(this.#schema, key)) - } - /** Omits the given properties from this type */ - public omit)[]>(keys: readonly [...K]): Type> - /** Omits the given properties from this type */ - public omit[]>>(keys: K): Type>> - /** Omits the given properties from this type */ - public omit>(key: K): Type> - /** Omits the given properties from this type */ - public omit(key: K): Type> { - return new Type(T.Type.Omit(this.#schema, key)) - } - /** Applies partial to this type */ - public partial(): Type> { - return new Type(T.Type.Partial(this.#schema)) - } - /** Applies required to this type */ - public required(): Type> { - return new Type(T.Type.Required(this.schema())) - } - /** Returns the keys of this type */ - public keyof(): Type> { - return new Type(T.Type.KeyOf(this.schema())) - } - /** Checks this value and throws if invalid */ - public assert(value: unknown): void { - if (this.#assert.check(value)) return - throw new TypeValueError([...this.#assert.errors(value)]) - } - /** Casts this value into this type */ - public cast(value: unknown): T.Static { - return Value.Cast(this.#schema, [], value) - } - /** Returns true if this value is valid for this type */ - public check(value: unknown): value is T.Static { - return this.#assert.check(value) - } - /** Returns the assertion code for this type */ - public code(): string { - return this.#assert.code() - } - /** Creates a default value for this type */ - public create(): T.Static { - return Value.Create(this.#schema, []) - } - /** Sets a default value for this type */ - public default(value: T.Static): this { - return new Type({ ...this.#schema, default: value }) as this - } - /** Parses the given value and returns the valid if valid. Otherwise throw. */ - public parse(value: unknown): T.Static { - this.assert(value) - return value - } - /** Compiles this type */ - public compile(): this { - const compiled = new Type(this.#schema) - compiled.#assert = new TypeAssertCompiled(this.#schema) - return compiled as this - } - /** Returns the schema associated with this type */ - public schema(): T { - return Value.Clone(this.#schema) - } -} -// ----------------------------------------------------------------------------------------- -// Array -// ----------------------------------------------------------------------------------------- -export class ArrayType> extends Type { - public maxItems(n: number) { - return this.options({ maxItems: n }) - } - public minItems(n: number) { - return this.options({ minItems: n }) - } - public length(n: number) { - return this.options({ minItems: n, maxItems: n }) - } - public uniqueItems() { - return this.options({ uniqueItems: true }) - } -} -// ----------------------------------------------------------------------------------------- -// Object -// ----------------------------------------------------------------------------------------- -export class ObjectType extends Type { - public strict() { - return this.options({ additionalProperties: false }) - } -} -// ----------------------------------------------------------------------------------------- -// String -// ----------------------------------------------------------------------------------------- -export class StringType extends Type { - public minLength(n: number) { - return this.options({ minLength: n }) - } - public maxLength(n: number) { - return this.options({ maxLength: n }) - } - public length(n: number) { - return this.options({ maxLength: n, minLength: n }) - } - public email() { - return this.options({ format: 'email' }) - } - public uuid() { - return this.options({ format: 'uuid' }) - } - public url() { - return this.options({ format: 'url' }) - } - public ipv6() { - return this.options({ format: 'ipv6' }) - } - public ipv4() { - return this.options({ format: 'ipv4' }) - } -} -// ----------------------------------------------------------------------------------------- -// Number -// ----------------------------------------------------------------------------------------- -export class NumberType extends Type { - public exclusiveMinimum(n: number) { - return this.options({ exclusiveMinimum: n }) - } - public minimum(n: number) { - return this.options({ minimum: n }) - } - public exclusiveMaximum(n: number) { - return this.options({ exclusiveMaximum: n }) - } - public maximum(n: number) { - return this.options({ maximum: n }) - } - public multipleOf(n: number) { - return this.options({ multipleOf: n }) - } - public positive() { - return this.options({ minimum: 0 }) - } - public negative() { - return this.options({ maximum: 0 }) - } -} -// ----------------------------------------------------------------------------------------- -// Integer -// ----------------------------------------------------------------------------------------- -export class IntegerType extends Type { - public exclusiveMinimum(n: number) { - return this.options({ exclusiveMinimum: n }) - } - public minimum(n: number) { - return this.options({ minimum: n }) - } - public exclusiveMaximum(n: number) { - return this.options({ exclusiveMaximum: n }) - } - public maximum(n: number) { - return this.options({ maximum: n }) - } - public multipleOf(n: number) { - return this.options({ multipleOf: n }) - } - public positive() { - return this.options({ minimum: 0 }) - } - public negative() { - return this.options({ maximum: 0 }) - } -} -// ----------------------------------------------------------------------------------------- -// BigInt -// ----------------------------------------------------------------------------------------- -export class BigIntType extends Type { - public exclusiveMinimum(n: bigint) { - return this.options({ exclusiveMinimum: n }) - } - public minimum(n: bigint) { - return this.options({ minimum: n }) - } - public exclusiveMaximum(n: bigint) { - return this.options({ exclusiveMaximum: n }) - } - public maximum(n: bigint) { - return this.options({ maximum: n }) - } - public multipleOf(n: bigint) { - return this.options({ multipleOf: n }) - } - public positive() { - return this.options({ minimum: BigInt(0) }) - } - public negative() { - return this.options({ maximum: BigInt(0) }) - } -} -// ----------------------------------------------------------------------------------------- -// Uint8Array -// ----------------------------------------------------------------------------------------- -export class ModelUint8Array extends Type { - public minByteLength(n: number) { - return this.options({ minByteLength: n }) - } - public maxByteLength(n: number) { - return this.options({ maxByteLength: n }) - } -} -// ----------------------------------------------------------------------------------------- -// Record -// ----------------------------------------------------------------------------------------- -export class RecordType extends Type> {} -// ----------------------------------------------------------------------------------------- -// Recursive -// ----------------------------------------------------------------------------------------- -export class ThisType extends Type {} -export class RecursiveType extends Type {} -// ----------------------------------------------------------------------------------------- -// ModelBuilder -// ----------------------------------------------------------------------------------------- -export class ModelBuilder { - /** Creates an any type */ - public any() { - return new Type(T.Type.Any()) - } - /** Creates an array type */ - public array(type: T) { - return new ArrayType(T.Type.Array(type.schema())) - } - /** Creates boolean type */ - public boolean() { - return new Type(T.Type.Boolean()) - } - /** Creates a bigint type */ - public bigint() { - return new BigIntType(T.Type.BigInt()) - } - /** Creates a date type */ - public date() { - return new Type(T.Type.Date()) - } - /** Creates a integer type */ - public integer() { - return new IntegerType(T.Type.Integer()) - } - /** Creates a number type */ - public number() { - return new NumberType(T.Type.Number()) - } - /** Creates a intersect type */ - public intersect(types: [...T]): Type>> { - const internal = types.map((type) => type.schema()) - return new Type(T.Type.Intersect(internal)) as any - } - /** Creates an keyof type */ - public keyof(type: T): Type>> { - return new Type(T.Type.KeyOf(type.schema())) as any - } - /** Creates a literal type */ - public literal(value: T) { - return new Type(T.Type.Literal(value)) - } - /** Creates a never type */ - public never() { - return new Type(T.Type.Never()) - } - /** Creates a null type */ - public null() { - return new Type(T.Type.Null()) - } - /** Creates a object type */ - public object(properties: T): Type>> { - const mapped = Object.keys(properties).reduce((acc, key) => ({ ...acc, [key]: properties[key].schema() }), {} as T.TProperties) - return new ObjectType(T.Type.Object(mapped)) as any - } - /** Creates a partial type */ - public partial(type: T): Type> { - return new Type(T.Type.Partial(type.schema())) as any - } - /** Creates a promise type */ - public promise(type: T): Type> { - return new Type(T.Type.Promise(type.schema())) as any - } - /** Creates an unknown type */ - public unknown() { - return new Type(T.Type.Unknown()) - } - /** Creates a record type */ - public record(type: T): RecordType> { - return new Type(T.Type.Record(T.Type.String(), type.schema())) as any - } - /** Creates a recursive type */ - public recursive(callback: (This: ThisType) => Type) { - // prettier-ignore - return new Type(T.Type.Recursive((This) => callback(new ThisType(This)).schema())) - } - /** Creates a required type */ - public required(type: T) { - return new Type(T.Type.Required(type.schema())) - } - /** Creates a string type */ - public string() { - return new StringType(T.Type.String()) - } - /** Creates a symbol type */ - public symbol() { - return new Type(T.Type.Symbol()) - } - /** Creates a template literal type */ - public templateLiteral(types: [...T]): Type>> { - const mapped = types.map((type) => type.schema()) - return new Type(T.Type.TemplateLiteral(mapped as any)) - } - /** Creates a tuple type */ - public tuple(types: [...T]): Type>> { - return new Type(T.Type.Tuple(types.map((type) => type.schema()))) as any - } - /** Creates a uint8array type */ - public uint8array() { - return new Type(T.Type.Uint8Array()) - } - /** Creates an undefined type */ - public undefined() { - return new Type(T.Type.Undefined()) - } - /** Creates a union type */ - public union(union: [...T]): Type>> { - const mapped = union.map((type) => type.schema()) - return new Type(T.Type.Union(mapped)) as any - } -} - -export type Infer = T extends Type ? T.Static : unknown - -export const TypeBuilder = new ModelBuilder() - -export default TypeBuilder diff --git a/hammer.mjs b/hammer.mjs index a6bca8b8b..61e8b06aa 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -5,59 +5,59 @@ import { readFileSync } from 'fs' // Clean // ------------------------------------------------------------------------------- export async function clean() { - await folder('target').delete() + await folder('target').delete() } // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') } // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- export async function start(example = 'index') { - await shell(`hammer run example/${example}.ts --dist target/example/${example}`) + await shell(`hammer run example/${example}.ts --dist target/example/${example}`) } // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- export async function benchmark() { - await compression() - await measurement() + await compression() + await measurement() } // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- export async function test_static() { - await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) + await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) } export async function test_runtime(filter = '') { - await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) - await shell(`mocha target/test/runtime/index.js -g "${filter}"`) + await shell(`hammer build ./test/runtime/index.ts --dist target/test/runtime --platform node`) + await shell(`mocha target/test/runtime/index.js -g "${filter}"`) } export async function test(filter = '') { - await test_static() - await test_runtime(filter) + await test_static() + await test_runtime(filter) } // ------------------------------------------------------------------------------- // Build // ------------------------------------------------------------------------------- export async function build(target = 'target/build') { - await test() - await folder(target).delete() - await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) - await folder(target).add('package.json') - await folder(target).add('readme.md') - await folder(target).add('license') - await shell(`cd ${target} && npm pack`) + await test() + await folder(target).delete() + await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) + await folder(target).add('package.json') + await folder(target).add('readme.md') + await folder(target).add('license') + await shell(`cd ${target} && npm pack`) } // ------------------------------------------------------------- // Publish // ------------------------------------------------------------- export async function publish(otp, target = 'target/build') { - const { version } = JSON.parse(readFileSync('package.json', 'utf8')) - await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) - await shell(`git tag ${version}`) - await shell(`git push origin ${version}`) + const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) + await shell(`git tag ${version}`) + await shell(`git push origin ${version}`) } \ No newline at end of file From 2e8818e71dbc30abbfa9ec3f4a96e85c85d2c410 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 6 Jul 2023 10:09:47 +0900 Subject: [PATCH 158/369] Composite Type Optimization (#492) --- hammer.mjs | 7 ++++ package-lock.json | 4 +- package.json | 7 +++- readme.md | 58 +++++++++++++++++----------- src/typebox.ts | 33 ++++++++++------ test/static/composite.ts | 82 ++++++++++++++++++++++++++++++++-------- 6 files changed, 137 insertions(+), 54 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 61e8b06aa..5f37c1eb7 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -29,7 +29,14 @@ export async function benchmark() { // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- +export async function test_typescript() { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', 'next', 'latest']) { + await shell(`npm install typescript@${version} --no-save`) + await test_static() + } +} export async function test_static() { + await shell(`tsc -v`) await shell(`tsc -p test/static/tsconfig.json --noEmit --strict`) } export async function test_runtime(filter = '') { diff --git a/package-lock.json b/package-lock.json index 31f90d3b1..7b9cb58d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.29.3", + "version": "0.29.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.29.3", + "version": "0.29.4", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 2971f5b17..1212620a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.3", + "version": "0.29.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -24,10 +24,13 @@ "url": "https://github.com/sinclairzx81/typebox" }, "scripts": { + "test:typescript": "hammer task test_typescript", + "test:static": "hammer task test_static", + "test:runtime": "hammer task test_runtime", + "test": "hammer task test", "clean": "hammer task clean", "format": "hammer task format", "start": "hammer task start", - "test": "hammer task test", "benchmark": "hammer task benchmark", "build": "hammer task build", "publish": "hammer task publish" diff --git a/readme.md b/readme.md index 68c792208..4bf1b532b 100644 --- a/readme.md +++ b/readme.md @@ -335,15 +335,15 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Composite([ │ type I = { │ const T = { │ +│ const T = Type.Composite([ │ type T = { │ const T = { │ │ Type.Object({ │ x: number │ type: 'object', │ │ x: Type.Number() │ } & { │ required: ['x', 'y'], │ │ }), │ y: number │ properties: { │ │ Type.Object({ │ } │ x: { │ │ y: Type.Number() │ │ type: 'number' │ -│ }) │ type T = { │ }, │ -│ ]) │ [K in keyof I]: I[K] │ y: { │ -│ │ } │ type: 'number' │ +│ }) │ │ }, │ +│ ]) │ │ y: { │ +│ │ │ type: 'number' │ │ │ │ } │ │ │ │ } │ │ │ │ } │ @@ -739,12 +739,12 @@ const R = Type.Ref(T) // const R = { ### Recursive Types -Recursive types are supported with `Type.Recursive` +Recursive types are supported with `Type.Recursive`. ```typescript -const Node = Type.Recursive(Node => Type.Object({ // const Node = { +const Node = Type.Recursive(This => Type.Object({ // const Node = { id: Type.String(), // $id: 'Node', - nodes: Type.Array(Node) // type: 'object', + nodes: Type.Array(This) // type: 'object', }), { $id: 'Node' }) // properties: { // id: { // type: 'string' @@ -776,38 +776,50 @@ function test(node: Node) { ### Conditional Types -Conditional types are supported with `Type.Extends`, `Type.Exclude` and `Type.Extract` +TypeBox supports conditional types with `Type.Extends`. This type will perform a structural assignment check for the first two parameters and return a `true` or `false` type from the second two parameters. The types `Type.Exclude` and `Type.Extract` are also supported. ```typescript // TypeScript type T0 = string extends number ? true : false // type T0 = false -type T1 = Extract // type T1 = number +type T1 = Extract<(1 | 2 | 3), 1> // type T1 = 1 -type T2 = Exclude // type T2 = string +type T2 = Exclude<(1 | 2 | 3), 1> // type T2 = 2 | 3 // TypeBox -const T0 = Type.Extends(Type.String(), Type.Number(), Type.Literal(true), Type.Literal(false)) - -const T1 = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.Number()) - -const T2 = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.Number()) - - -type T0 = Static // type T0 = false +const T0 = Type.Extends( // const T0: TLiteral + Type.String(), + Type.Number(), + Type.Literal(true), + Type.Literal(false) +) -type T1 = Static // type T1 = number +const T1 = Type.Extract( // const T1: TLiteral<1> + Type.Union([ + Type.Literal(1), + Type.Literal(2), + Type.Literal(3) + ]), + Type.Literal(1) +) -type T2 = Static // type T2 = string +const T2 = Type.Exclude( // const T2: TUnion<[ + Type.Union([ // TLiteral<2>, + Type.Literal(1), // TLiteral<3> + Type.Literal(2), // ]> + Type.Literal(3) + ]), + Type.Literal(1) +) ``` ### Template Literal Types -TypeBox supports Template Literal types using `Type.TemplateLiteral`. These types can be created using a simple template DSL syntax, however more complex template literals can be created by passing an array of literal and union types. The examples below show the template DSL syntax. +TypeBox supports template literal types with `Type.TemplateLiteral`. This type implements an embedded DSL syntax to match the TypeScript template literal syntax. This type can also be composed by passing an array of union and literal types as parameters. The following example shows the DSL syntax. ```typescript // TypeScript @@ -853,7 +865,7 @@ const R = Type.Record(T, Type.String()) // const R = { ### Indexed Access Types -TypeBox supports Indexed Access types using `Type.Index`. This feature provides a consistent way to access property types without having to extract them from the underlying schema representation. Indexed accessors are supported for object and tuples, as well as nested union and intersect types. +TypeBox supports indexed access types using `Type.Index`. This type provides a consistent way to access interior property and array element types without having to extract them from the underlying schema representation. Indexed access types are supported for object, array, tuple, union and intersect types. ```typescript const T = Type.Object({ // const T = { @@ -888,7 +900,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { ### Not Types -TypeBox has partial support for the JSON schema `not` keyword with `Type.Not`. This type is synonymous with the concept of a [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. TypeBox does provide partial inference support via the intersection of `T & not U` (where all negated types infer as `unknown`). This can be used in the following context. +TypeBox provides support for the `not` keyword with `Type.Not`. This type is synonymous with [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. Partial inference of this type can be attained via the intersection of `T & not U` (where all Not types infer as `unknown`). This approach can be used to narrow for broader types in the following context. ```typescript // TypeScript diff --git a/src/typebox.ts b/src/typebox.ts index f67705d80..0c9945cf7 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -241,16 +241,20 @@ export type TInstanceType> = T['retur // TComposite // -------------------------------------------------------------------------- // prettier-ignore -export type TCompositeReduce, K extends string[]> = K extends [infer L, ...infer R] - ? { [_ in Assert]: TIndexType> } & TCompositeReduce> - : {} +export type TCompositeKeys = T extends [infer L, ...infer R] + ? keyof Assert['properties'] | TCompositeKeys> + : never // prettier-ignore -export type TCompositeSelect> = UnionToTuple> extends infer K - ? Evaluate>> +export type TCompositeIndex, K extends string[]> = K extends [infer L, ...infer R] + ? { [_ in Assert]: TIndexType> } & TCompositeIndex> : {} // prettier-ignore -export type TComposite = TIntersect extends infer R - ? TObject>>> +export type TCompositeReduce = UnionToTuple> extends infer K + ? Evaluate, Assert>> + : {} // ^ indexed via intersection of T +// prettier-ignore +export type TComposite = TIntersect extends TIntersect + ? TObject> : TObject<{}> // -------------------------------------------------------------------------- // TConstructor @@ -640,12 +644,21 @@ export type StringFormatOption = | 'json-pointer' | 'relative-json-pointer' | 'regex' + | ({} & string) +// prettier-ignore +export type StringContentEncodingOption = + | '7bit' + | '8bit' + | 'binary' + | 'quoted-printable' + | 'base64' + | ({} & string) export interface StringOptions extends SchemaOptions { minLength?: number maxLength?: number pattern?: string - format?: string - contentEncoding?: '7bit' | '8bit' | 'binary' | 'quoted-printable' | 'base64' + format?: StringFormatOption + contentEncoding?: StringContentEncodingOption contentMediaType?: string } export interface TString extends TSchema, StringOptions { @@ -731,9 +744,7 @@ export interface TTemplateLiteral> = T extends TTuple ? AssertRest : never - export type TTupleInfer = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleInfer, P>] : [] - export interface TTuple extends TSchema { [Kind]: 'Tuple' static: TTupleInfer // { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } diff --git a/test/static/composite.ts b/test/static/composite.ts index b4399e20f..cf9bb88a0 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -1,7 +1,9 @@ import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' +import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' +// ---------------------------------------------------------------------------- // Overlapping - Non Varying +// ---------------------------------------------------------------------------- { const A = Type.Object({ A: Type.Number(), @@ -15,7 +17,9 @@ import { Type, Static } from '@sinclair/typebox' A: number }>() } +// ---------------------------------------------------------------------------- // Overlapping - Varying +// ---------------------------------------------------------------------------- { const A = Type.Object({ A: Type.Number(), @@ -29,7 +33,9 @@ import { Type, Static } from '@sinclair/typebox' A: never }>() } +// ---------------------------------------------------------------------------- // Overlapping Single Optional +// ---------------------------------------------------------------------------- { const A = Type.Object({ A: Type.Optional(Type.Number()), @@ -43,26 +49,50 @@ import { Type, Static } from '@sinclair/typebox' A: number }>() } +// ---------------------------------------------------------------------------- // Overlapping All Optional (Deferred) +// // Note for: https://github.com/sinclairzx81/typebox/issues/419 -// Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access -// call. Currently, there isn't a trivial way to perform this check without running into possibly infinite instantiation issues. -// The optional check is only specific to overlapping properties. Singular properties will continue to work as expected. The -// rule is "if all composite properties for a key are optional, then the composite property is optional". Defer this test and -// document as minor breaking change. +// ---------------------------------------------------------------------------- { - // const A = Type.Object({ - // A: Type.Optional(Type.Number()), - // }) - // const B = Type.Object({ - // A: Type.Optional(Type.Number()), - // }) - // const T = Type.Composite([A, B]) - // Expect(T).ToInfer<{ - // A: number | undefined - // }>() + const A = Type.Object({ + A: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + A: Type.Optional(Type.Number()), + }) + const T = Type.Composite([A, B]) + Expect(T).ToInfer<{ + A: number | undefined + }>() } +{ + const A = Type.Object({ + A: Type.Optional(Type.Number()), + }) + const B = Type.Object({ + A: Type.Number(), + }) + const T = Type.Composite([A, B]) + Expect(T).ToInfer<{ + A: number + }>() +} +{ + const A = Type.Object({ + A: Type.Number(), + }) + const B = Type.Object({ + A: Type.Number(), + }) + const T = Type.Composite([A, B]) + Expect(T).ToInfer<{ + A: number + }>() +} +// ---------------------------------------------------------------------------- // Distinct Properties +// ---------------------------------------------------------------------------- { const A = Type.Object({ A: Type.Number(), @@ -77,3 +107,23 @@ import { Type, Static } from '@sinclair/typebox' B: number }>() } +// ---------------------------------------------------------------------------- +// Intersection Quirk +// +// TypeScript has an evaluation quirk for the following case where the first +// type evaluates the sub property as never, but the second evaluates the +// entire type as never. There is probably a reason for this behavior, but +// TypeBox supports the former evaluation. +// +// { x: number } & { x: string } -> { x: number } & { x: string } => { x: never } +// { x: number } & { x: boolean } -> never -> ... +// ---------------------------------------------------------------------------- +{ + // prettier-ignore + const T: TObject<{ + x: TIntersect<[TNumber, TBoolean]> + }> = Type.Composite([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Boolean() }) + ]) +} From 3dce951e8b0b9d204299251e985275c72fa9c0e6 Mon Sep 17 00:00:00 2001 From: Andrew Smiley Date: Tue, 18 Jul 2023 13:04:34 -0400 Subject: [PATCH 159/369] Resolve Max Properties Error Message (#501) --- src/errors/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors/errors.ts b/src/errors/errors.ts index aa5fe28be..92663dcc8 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -342,7 +342,7 @@ export namespace ValueErrors { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.maxProperties} properties` } } const requiredKeys = globalThis.Array.isArray(schema.required) ? schema.required : ([] as string[]) const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) From b19575f16a84b5f92cfedefb01075ce402352e85 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 19 Jul 2023 02:06:39 +0900 Subject: [PATCH 160/369] Revision 0.29.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1212620a3..3077ceb3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.4", + "version": "0.29.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From 2bf9244b0a782dc0bf8438d3fae011b72fef9545 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 19 Jul 2023 02:25:31 +0900 Subject: [PATCH 161/369] Resolve Max Properties Error Message (#502) --- package.json | 2 +- src/errors/errors.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3077ceb3e..71c6e2c79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.5", + "version": "0.29.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 92663dcc8..73e76bec7 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -342,7 +342,7 @@ export namespace ValueErrors { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.maxProperties} properties` } + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } } const requiredKeys = globalThis.Array.isArray(schema.required) ? schema.required : ([] as string[]) const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) @@ -391,7 +391,7 @@ export namespace ValueErrors { yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } } if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have less than ${schema.minProperties} properties` } + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } } const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] const regex = new RegExp(patternKey) From da4944361ba87dcb959ccba05750d6c3e262bf16 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 22 Jul 2023 21:08:48 +0900 Subject: [PATCH 162/369] TypeGuards for TypeDef (#504) --- example/typedef/typedef.ts | 253 ++++++++++++++++++++++++++++++------- 1 file changed, 204 insertions(+), 49 deletions(-) diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 6999df589..cfec35e41 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -26,7 +26,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { Static, Evaluate, TSchema, PropertiesReduce, TReadonly, TReadonlyOptional, TOptional } from '@sinclair/typebox' import * as Types from '@sinclair/typebox' // -------------------------------------------------------------------------- @@ -41,6 +40,12 @@ export type Tick = T extends keyof B ? B[T] : export type Next = T extends Assert['m'] ? Assert['t'] : T extends `${infer L}${infer R}` ? L extends Assert['m'] ? `${Assert, string>}${Next}` : `${Assert, string>}${R}` : never export type Increment = Reverse, B>> // -------------------------------------------------------------------------- +// SchemaOptions +// -------------------------------------------------------------------------- +export interface SchemaOptions { + [name: string]: any +} +// -------------------------------------------------------------------------- // TArray // -------------------------------------------------------------------------- export interface TArray extends Types.TSchema { @@ -158,6 +163,7 @@ export interface TRecord extends Types. // -------------------------------------------------------------------------- export interface TString extends Types.TSchema { [Types.Kind]: 'TypeDef:String' + type: 'string' static: string } // -------------------------------------------------------------------------- @@ -165,7 +171,7 @@ export interface TString extends Types.TSchema { // -------------------------------------------------------------------------- type OptionalKeys = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? T[K] : never } type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? never : T[K] } -export interface StructOptions { +export interface StructOptions extends SchemaOptions { additionalProperties?: boolean } export interface TStruct extends Types.TSchema, StructOptions { @@ -179,24 +185,52 @@ export interface TStruct extends Types.TSchema, Str // -------------------------------------------------------------------------- export interface TTimestamp extends Types.TSchema { [Types.Kind]: 'TypeDef:Timestamp' - static: number + type: 'timestamp' + static: string } // -------------------------------------------------------------------------- -// TypeRegistry -// -------------------------------------------------------------------------- -Types.TypeRegistry.Set('TypeDef:Array', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:String', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => ValueCheck.Check(schema, value)) -Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => ValueCheck.Check(schema, value)) +// TimestampFormat +// -------------------------------------------------------------------------- +export namespace TimestampFormat { + const DATE_TIME_SEPARATOR = /t|\s/i + const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i + const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ + const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + function IsLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) + } + function IsDate(str: string): boolean { + const matches: string[] | null = DATE.exec(str) + if (!matches) return false + const year: number = +matches[1] + const month: number = +matches[2] + const day: number = +matches[3] + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) + } + function IsTime(str: string, strictTimeZone?: boolean): boolean { + const matches: string[] | null = TIME.exec(str) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tz: string | undefined = matches[4] + const tzSign: number = matches[5] === '-' ? -1 : 1 + const tzH: number = +(matches[6] || 0) + const tzM: number = +(matches[7] || 0) + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false + if (hr <= 23 && min <= 59 && sec < 60) return true + const utcMin = min - tzM * tzSign + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 + } + function IsDateTime(value: string, strictTimeZone?: boolean): boolean { + const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) + return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) + } + export function Check(value: string): boolean { + return IsDateTime(value) + } +} // -------------------------------------------------------------------------- // ValueCheck // -------------------------------------------------------------------------- @@ -287,7 +321,7 @@ export namespace ValueCheck { return true } function Timestamp(schema: TString, value: unknown): boolean { - return IsInt(value, 0, Number.MAX_SAFE_INTEGER) + return IsString(value) && TimestampFormat.Check(value) } function Union(schema: TUnion, value: unknown): boolean { if (!IsObject(value)) return false @@ -324,9 +358,130 @@ export namespace ValueCheck { } } // -------------------------------------------------------------------------- -// TypeDefTypeBuilder +// TypeGuard +// -------------------------------------------------------------------------- +export namespace TypeGuard { + // ------------------------------------------------------------------------ + // Guards + // ------------------------------------------------------------------------ + function IsObject(value: unknown): value is Record { + return typeof value === 'object' + } + function IsArray(value: unknown): value is unknown[] { + return globalThis.Array.isArray(value) + } + function IsOptionalBoolean(value: unknown): value is boolean | undefined { + return IsBoolean(value) || value === undefined + } + function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + function IsString(value: unknown): value is string { + return typeof value === 'string' + } + // ------------------------------------------------------------------------ + // Types + // ------------------------------------------------------------------------ + export function TArray(schema: unknown): schema is TArray { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Array' && TSchema(schema['elements']) + } + export function TBoolean(schema: unknown): schema is TBoolean { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Boolean' && schema['type'] === 'boolean' + } + export function TUnion(schema: unknown): schema is TUnion { + if(!(IsObject(schema) && schema[Types.Kind] === 'TypeDef:Union' && IsString(schema['discriminator']) && IsObject(schema['mapping']))) return false + return globalThis.Object.getOwnPropertyNames(schema['mapping']).every(key => TSchema((schema['mapping'] as any)[key])) + } + export function TEnum(schema: unknown): schema is TEnum { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Enum' && IsArray(schema['enum']) && schema['enum'].every(item => IsString(item)) + } + export function TFloat32(schema: unknown): schema is TFloat32 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Float32' && schema['type'] === 'float32' + } + export function TFloat64(schema: unknown): schema is TFloat64 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Float64' && schema['type'] === 'float64' + } + export function TInt8(schema: unknown): schema is TInt8 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Int8' && schema['type'] === 'int8' + } + export function TInt16(schema: unknown): schema is TInt16 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Int16' && schema['type'] === 'int16' + } + export function TInt32(schema: unknown): schema is TInt32 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Int32' && schema['type'] === 'int32' + } + export function TUint8(schema: unknown): schema is TUint8 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Uint8' && schema['type'] === 'uint8' + } + export function TUint16(schema: unknown): schema is TUint16 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Uint16' && schema['type'] === 'uint16' + } + export function TUint32(schema: unknown): schema is TUint32 { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Uint32' && schema['type'] === 'uint32' + } + export function TRecord(schema: unknown): schema is TRecord { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Record' && TSchema(schema['values']) + } + export function TString(schema: unknown): schema is TString { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:String' && schema['type'] === 'string' + } + export function TStruct(schema: unknown): schema is TStruct { + if(!(IsObject(schema) && schema[Types.Kind] === 'TypeDef:Struct' && IsOptionalBoolean(schema['additionalProperties']))) return false + const optionalProperties = schema['optionalProperties'] + const requiredProperties = schema['properties'] + const optionalCheck = optionalProperties === undefined || IsObject(optionalProperties) && globalThis.Object.getOwnPropertyNames(optionalProperties).every(key => TSchema(optionalProperties[key])) + const requiredCheck = requiredProperties === undefined || IsObject(requiredProperties) && globalThis.Object.getOwnPropertyNames(requiredProperties).every(key => TSchema(requiredProperties[key])) + return optionalCheck && requiredCheck + } + export function TTimestamp(schema: unknown): schema is TTimestamp { + return IsObject(schema) && schema[Types.Kind] === 'TypeDef:Timestamp' && schema['type'] === 'timestamp' + } + export function TKind(schema: unknown): schema is Types.TKind { + return IsObject(schema) && Types.Kind in schema && typeof (schema as any)[Types.Kind] === 'string' // TS 4.1.5: any required for symbol indexer + } + export function TSchema(schema: unknown): schema is Types.TSchema { + // prettier-ignore + return ( + TArray(schema) || + TBoolean(schema) || + TUnion(schema) || + TEnum(schema) || + TFloat32(schema) || + TFloat64(schema) || + TInt8(schema) || + TInt16(schema) || + TInt32(schema) || + TUint8(schema) || + TUint16(schema) || + TUint32(schema) || + TRecord(schema) || + TString(schema) || + TStruct(schema) || + TTimestamp(schema) || + (TKind(schema) && Types.TypeRegistry.Has(schema[Types.Kind])) + ) + } +} +// -------------------------------------------------------------------------- +// TypeRegistry +// -------------------------------------------------------------------------- +Types.TypeRegistry.Set('TypeDef:Array', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Boolean', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Union', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int8', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int16', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Int32', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint8', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint16', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Uint32', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Record', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:String', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => ValueCheck.Check(schema, value)) +Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => ValueCheck.Check(schema, value)) +// -------------------------------------------------------------------------- +// TypeDefBuilder // -------------------------------------------------------------------------- -export class TypeDefTypeBuilder extends Types.TypeBuilder { +export class TypeDefBuilder extends Types.TypeBuilder { // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ @@ -346,56 +501,56 @@ export class TypeDefTypeBuilder extends Types.TypeBuilder { // Types // ------------------------------------------------------------------------ /** [Standard] Creates a Array type */ - public Array(elements: T): TArray { - return this.Create({ [Types.Kind]: 'TypeDef:Array', elements }) + public Array(elements: T, options: SchemaOptions = {}): TArray { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Array', elements }) } /** [Standard] Creates a Boolean type */ - public Boolean(): TBoolean { - return this.Create({ [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }) + public Boolean(options: SchemaOptions = {}): TBoolean { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }) } /** [Standard] Creates a Enum type */ - public Enum(values: [...T]): TEnum { - return this.Create({ [Types.Kind]: 'TypeDef:Enum', enum: values }) + public Enum(values: [...T], options: SchemaOptions = {}): TEnum { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Enum', enum: values }) } /** [Standard] Creates a Float32 type */ - public Float32(): TFloat32 { - return this.Create({ [Types.Kind]: 'TypeDef:Float32', type: 'float32' }) + public Float32(options: SchemaOptions = {}): TFloat32 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Float32', type: 'float32' }) } /** [Standard] Creates a Float64 type */ - public Float64(): TFloat64 { - return this.Create({ [Types.Kind]: 'TypeDef:Float64', type: 'float64' }) + public Float64(options: SchemaOptions = {}): TFloat64 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Float64', type: 'float64' }) } /** [Standard] Creates a Int8 type */ - public Int8(): TInt8 { - return this.Create({ [Types.Kind]: 'TypeDef:Int8', type: 'int8' }) + public Int8(options: SchemaOptions = {}): TInt8 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int8', type: 'int8' }) } /** [Standard] Creates a Int16 type */ - public Int16(): TInt16 { - return this.Create({ [Types.Kind]: 'TypeDef:Int16', type: 'int16' }) + public Int16(options: SchemaOptions = {}): TInt16 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int16', type: 'int16' }) } /** [Standard] Creates a Int32 type */ - public Int32(): TInt32 { - return this.Create({ [Types.Kind]: 'TypeDef:Int32', type: 'int32' }) + public Int32(options: SchemaOptions = {}): TInt32 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int32', type: 'int32' }) } /** [Standard] Creates a Uint8 type */ - public Uint8(): TUint8 { - return this.Create({ [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }) + public Uint8(options: SchemaOptions = {}): TUint8 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }) } /** [Standard] Creates a Uint16 type */ - public Uint16(): TUint16 { - return this.Create({ [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }) + public Uint16(options: SchemaOptions = {}): TUint16 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }) } /** [Standard] Creates a Uint32 type */ - public Uint32(): TUint32 { - return this.Create({ [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }) + public Uint32(options: SchemaOptions = {}): TUint32 { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }) } /** [Standard] Creates a Record type */ - public Record(values: T): TRecord { - return this.Create({ [Types.Kind]: 'TypeDef:Record', values }) + public Record(values: T, options: SchemaOptions = {}): TRecord { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:Record', values }) } /** [Standard] Creates a String type */ - public String(): TString { - return this.Create({ [Types.Kind]: 'TypeDef:String', type: 'string' }) + public String(options: SchemaOptions = {}): TString { + return this.Create({ ...options, [Types.Kind]: 'TypeDef:String', type: 'string' }) } /** [Standard] Creates a Struct type */ public Struct(fields: T, options?: StructOptions): TStruct { @@ -408,7 +563,7 @@ export class TypeDefTypeBuilder extends Types.TypeBuilder { /** [Standard] Creates a Union type */ public Union[], D extends string = 'type'>(structs: [...T], discriminator?: D): TUnion { discriminator = (discriminator || 'type') as D - if (structs.length === 0) throw new Error('TypeDefTypeBuilder: Union types must contain at least one struct') + if (structs.length === 0) throw new Error('TypeDefBuilder: Union types must contain at least one struct') const mapping = structs.reduce((acc, current, index) => ({ ...acc, [index.toString()]: current }), {}) return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }) } @@ -419,5 +574,5 @@ export class TypeDefTypeBuilder extends Types.TypeBuilder { } /** JSON Type Definition Type Builder */ -export const Type = new TypeDefTypeBuilder() +export const Type = new TypeDefBuilder() From 5a093bff4486adc5587793a5def1acf8ef19f0b1 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 1 Aug 2023 15:53:05 +0900 Subject: [PATCH 163/369] Revision 0.30.0 (#513) --- benchmark/compression/index.ts | 2 +- .../compression/module/typebox-compiler.ts | 3 +- .../compression/module/typebox-errors.ts | 4 +- .../compression/module/typebox-system.ts | 2 +- benchmark/compression/module/typebox-value.ts | 3 +- benchmark/measurement/module/cases.ts | 2 +- changelog/0.30.0.md | 419 +++++ example/experimental/readme.md | 54 - example/experimental/readonly-object.ts | 68 - example/legacy/intersect-0.25.0.ts | 66 - example/trpc/readme.md | 111 -- {example => examples}/collections/array.ts | 31 - {example => examples}/collections/index.ts | 0 {example => examples}/collections/map.ts | 26 - {example => examples}/collections/readme.md | 0 {example => examples}/collections/set.ts | 16 +- {example => examples}/formats/index.ts | 0 {example => examples}/formats/readme.md | 0 {example => examples}/formats/standard.ts | 0 {example => examples}/index.ts | 0 examples/prototypes/const.ts | 61 + examples/prototypes/evaluate.ts | 135 ++ .../prototypes}/index.ts | 6 +- examples/prototypes/partial-deep.ts | 65 + examples/prototypes/readme.md | 114 ++ .../prototypes}/union-enum.ts | 18 +- .../prototypes}/union-oneof.ts | 24 +- examples/transform/index.ts | 29 + examples/transform/readme.md | 83 + examples/transform/transform.ts | 346 +++++ examples/trpc/readme.md | 46 + examples/typedef/index.ts | 29 + {example => examples}/typedef/readme.md | 0 {example => examples}/typedef/typedef.ts | 106 +- hammer.mjs | 15 +- package-lock.json | 4 +- package.json | 16 +- readme.md | 436 +++--- src/compiler/compiler.ts | 412 ++--- src/errors/errors.ts | 861 ++++++----- src/system/system.ts | 8 +- src/typebox.ts | 1369 +++++++++-------- src/value/cast.ts | 480 +++--- src/value/check.ts | 763 ++++----- src/value/clone.ts | 85 +- src/value/convert.ts | 616 ++++---- src/value/create.ts | 673 ++++---- src/value/delta.ts | 234 ++- src/value/equal.ts | 74 +- src/value/guard.ts | 145 ++ src/value/hash.ts | 261 ++-- src/value/index.ts | 7 +- src/value/is.ts | 54 - src/value/mutate.ts | 127 +- src/value/pointer.ts | 17 +- src/value/value.ts | 76 +- test/runtime/assert/assert.ts | 32 +- test/runtime/compiler/array.ts | 62 +- test/runtime/compiler/async-iterator.ts | 20 + test/runtime/compiler/bigint.ts | 4 - test/runtime/compiler/boolean.ts | 6 - test/runtime/compiler/enum.ts | 3 - test/runtime/compiler/index.ts | 4 +- test/runtime/compiler/integer.ts | 8 - test/runtime/compiler/intersect.ts | 1 - test/runtime/compiler/iterator.ts | 20 + test/runtime/compiler/keyof.ts | 3 - test/runtime/compiler/object.ts | 2 - test/runtime/compiler/partial.ts | 26 +- test/runtime/compiler/pick.ts | 3 - test/runtime/compiler/readonly-optional.ts | 4 +- test/runtime/compiler/readonly.ts | 1 - test/runtime/compiler/record.ts | 4 +- test/runtime/compiler/recursive.ts | 8 +- test/runtime/compiler/ref.ts | 9 +- test/runtime/compiler/regex.ts | 35 - test/runtime/compiler/regexp.ts | 65 + test/runtime/compiler/required.ts | 15 +- test/runtime/compiler/string.ts | 6 - test/runtime/compiler/template-literal.ts | 1 - test/runtime/compiler/tuple.ts | 8 - test/runtime/compiler/uint8array.ts | 9 - test/runtime/compiler/union.ts | 6 - test/runtime/compiler/validate.ts | 7 - test/runtime/errors/iterator.ts | 26 +- test/runtime/schema/array.ts | 65 +- test/runtime/schema/boolean.ts | 6 - test/runtime/schema/composite.ts | 2 +- test/runtime/schema/date.ts | 124 +- test/runtime/schema/enum.ts | 3 - test/runtime/schema/index.ts | 3 +- test/runtime/schema/integer.ts | 11 - test/runtime/schema/intersect.ts | 1 - test/runtime/schema/keyof.ts | 3 - test/runtime/schema/literal.ts | 4 - test/runtime/schema/modifier.ts | 3 - test/runtime/schema/null.ts | 6 - test/runtime/schema/number.ts | 7 - test/runtime/schema/object.ts | 6 - test/runtime/schema/omit.ts | 6 +- test/runtime/schema/partial.ts | 16 +- test/runtime/schema/pick.ts | 6 +- test/runtime/schema/readonly-optional.ts | 4 +- test/runtime/schema/readonly.ts | 1 - test/runtime/schema/record.ts | 4 +- test/runtime/schema/recursive.ts | 8 +- test/runtime/schema/ref.ts | 2 - test/runtime/schema/regex.ts | 35 - test/runtime/schema/regexp.ts | 65 + test/runtime/schema/required.ts | 15 +- test/runtime/schema/string.ts | 6 - test/runtime/schema/template-literal.ts | 1 - test/runtime/schema/tuple.ts | 8 - test/runtime/schema/uint8array.ts | 105 +- test/runtime/schema/union.ts | 6 - test/runtime/schema/validate.ts | 3 - test/runtime/schema/void.ts | 6 - test/runtime/system/system.ts | 8 +- test/runtime/type/extends/any.ts | 36 +- test/runtime/type/extends/array.ts | 100 +- test/runtime/type/extends/async-iterator.ts | 72 + test/runtime/type/extends/bigint.ts | 38 +- test/runtime/type/extends/boolean.ts | 34 +- test/runtime/type/extends/constructor.ts | 82 +- test/runtime/type/extends/date.ts | 36 +- test/runtime/type/extends/function.ts | 82 +- test/runtime/type/extends/index.ts | 2 + test/runtime/type/extends/integer.ts | 36 +- test/runtime/type/extends/iterator.ts | 72 + test/runtime/type/extends/literal.ts | 100 +- test/runtime/type/extends/not.ts | 50 +- test/runtime/type/extends/null.ts | 38 +- test/runtime/type/extends/number.ts | 36 +- test/runtime/type/extends/object.ts | 56 +- test/runtime/type/extends/promise.ts | 78 +- test/runtime/type/extends/record.ts | 64 +- test/runtime/type/extends/string.ts | 42 +- test/runtime/type/extends/symbol.ts | 40 +- test/runtime/type/extends/template-literal.ts | 60 +- test/runtime/type/extends/tuple.ts | 62 +- test/runtime/type/extends/uint8array.ts | 36 +- test/runtime/type/extends/undefined.ts | 34 +- test/runtime/type/extends/union.ts | 52 +- test/runtime/type/extends/unknown.ts | 38 +- test/runtime/type/extends/void.ts | 40 +- test/runtime/type/guard/any.ts | 6 +- test/runtime/type/guard/array.ts | 23 +- test/runtime/type/guard/async-iterator.ts | 22 + test/runtime/type/guard/awaited.ts | 41 + test/runtime/type/guard/bigint.ts | 6 +- test/runtime/type/guard/boolean.ts | 6 +- test/runtime/type/guard/capitalize.ts | 9 + test/runtime/type/guard/composite.ts | 24 +- test/runtime/type/guard/constructor.ts | 14 +- test/runtime/type/guard/date.ts | 20 +- test/runtime/type/guard/exclude.ts | 36 +- test/runtime/type/guard/extract.ts | 48 +- test/runtime/type/guard/function.ts | 14 +- test/runtime/type/guard/index.ts | 10 +- test/runtime/type/guard/indexed.ts | 178 +-- test/runtime/type/guard/integer.ts | 16 +- test/runtime/type/guard/intersect.ts | 4 +- test/runtime/type/guard/iterator.ts | 22 + test/runtime/type/guard/keyof.ts | 32 +- test/runtime/type/guard/literal.ts | 12 +- test/runtime/type/guard/lowercase.ts | 9 + test/runtime/type/guard/not.ts | 6 +- test/runtime/type/guard/null.ts | 6 +- test/runtime/type/guard/number.ts | 16 +- test/runtime/type/guard/object.ts | 29 +- test/runtime/type/guard/omit.ts | 36 +- test/runtime/type/guard/partial.ts | 40 +- test/runtime/type/guard/pick.ts | 38 +- test/runtime/type/guard/promise.ts | 14 +- test/runtime/type/guard/record.ts | 76 +- test/runtime/type/guard/ref.ts | 8 +- test/runtime/type/guard/required.ts | 40 + test/runtime/type/guard/rest.ts | 8 +- test/runtime/type/guard/string.ts | 17 +- test/runtime/type/guard/symbol.ts | 6 +- test/runtime/type/guard/template-literal.ts | 18 +- test/runtime/type/guard/this.ts | 4 +- test/runtime/type/guard/tuple.ts | 8 +- test/runtime/type/guard/uint8array.ts | 14 +- test/runtime/type/guard/uncapitalize.ts | 10 + test/runtime/type/guard/undefined.ts | 6 +- test/runtime/type/guard/union.ts | 20 +- test/runtime/type/guard/unknown.ts | 6 +- test/runtime/type/guard/unsafe.ts | 17 +- test/runtime/type/guard/uppercase.ts | 10 + test/runtime/type/guard/void.ts | 6 +- test/runtime/type/index.ts | 1 + test/runtime/type/normalize/exclude.ts | 6 +- test/runtime/type/normalize/extract.ts | 10 +- test/runtime/type/normalize/indexed.ts | 96 +- test/runtime/type/normalize/intersect.ts | 6 +- test/runtime/type/normalize/record.ts | 2 +- test/runtime/type/normalize/union.ts | 6 +- test/runtime/type/registry/format.ts | 6 +- test/runtime/type/registry/type.ts | 6 +- test/runtime/type/template/finite.ts | 24 +- test/runtime/type/template/generate.ts | 74 +- test/runtime/type/template/parser.ts | 78 +- test/runtime/type/template/pattern.ts | 10 +- test/runtime/type/value/guard.ts | 109 ++ test/runtime/type/value/index.ts | 1 + test/runtime/value/cast/any.ts | 18 +- test/runtime/value/cast/array.ts | 44 +- test/runtime/value/cast/async-iterator.ts | 37 + test/runtime/value/cast/bigint.ts | 18 +- test/runtime/value/cast/boolean.ts | 18 +- test/runtime/value/cast/composite.ts | 20 +- test/runtime/value/cast/custom.ts | 16 +- test/runtime/value/cast/date.ts | 12 +- test/runtime/value/cast/enum.ts | 20 +- test/runtime/value/cast/index.ts | 4 +- test/runtime/value/cast/integer.ts | 14 +- test/runtime/value/cast/intersect.ts | 16 +- test/runtime/value/cast/iterator.ts | 37 + test/runtime/value/cast/keyof.ts | 18 +- test/runtime/value/cast/literal.ts | 18 +- test/runtime/value/cast/never.ts | 16 +- test/runtime/value/cast/not.ts | 18 +- test/runtime/value/cast/null.ts | 18 +- test/runtime/value/cast/number.ts | 16 +- test/runtime/value/cast/object.ts | 28 +- test/runtime/value/cast/record.ts | 21 +- test/runtime/value/cast/recursive.ts | 18 +- .../value/cast/{regex.ts => regexp.ts} | 20 +- test/runtime/value/cast/string.ts | 14 +- test/runtime/value/cast/symbol.ts | 18 +- test/runtime/value/cast/template-literal.ts | 18 +- test/runtime/value/cast/tuple.ts | 26 +- test/runtime/value/cast/uint8array.ts | 18 +- test/runtime/value/cast/undefined.ts | 18 +- test/runtime/value/cast/union.ts | 39 +- test/runtime/value/cast/unknown.ts | 18 +- test/runtime/value/cast/void.ts | 18 +- test/runtime/value/check/any.ts | 16 +- test/runtime/value/check/array.ts | 76 +- test/runtime/value/check/async-iterator.ts | 24 + test/runtime/value/check/bigint.ts | 17 +- test/runtime/value/check/boolean.ts | 16 +- test/runtime/value/check/composite.ts | 15 +- test/runtime/value/check/custom.ts | 40 +- test/runtime/value/check/date.ts | 18 +- test/runtime/value/check/enum.ts | 11 +- test/runtime/value/check/index.ts | 4 +- test/runtime/value/check/integer.ts | 16 +- test/runtime/value/check/intersect.ts | 54 +- test/runtime/value/check/iterator.ts | 24 + test/runtime/value/check/keyof.ts | 12 +- test/runtime/value/check/literal.ts | 8 +- test/runtime/value/check/never.ts | 14 +- test/runtime/value/check/not.ts | 18 +- test/runtime/value/check/null.ts | 16 +- test/runtime/value/check/number.ts | 24 +- test/runtime/value/check/object.ts | 73 +- test/runtime/value/check/record.ts | 46 +- test/runtime/value/check/recursive.ts | 10 +- test/runtime/value/check/ref.ts | 20 +- test/runtime/value/check/regex.ts | 19 - test/runtime/value/check/regexp.ts | 36 + test/runtime/value/check/string.ts | 16 +- test/runtime/value/check/symbol.ts | 18 +- test/runtime/value/check/template-literal.ts | 134 +- test/runtime/value/check/tuple.ts | 10 +- test/runtime/value/check/uint8array.ts | 8 +- test/runtime/value/check/undefined.ts | 16 +- test/runtime/value/check/union.ts | 12 +- test/runtime/value/check/unknown.ts | 16 +- test/runtime/value/check/void.ts | 16 +- test/runtime/value/clone/clone.ts | 69 +- test/runtime/value/convert/any.ts | 14 +- test/runtime/value/convert/array.ts | 20 +- test/runtime/value/convert/async-iterator.ts | 20 + test/runtime/value/convert/bigint.ts | 16 +- test/runtime/value/convert/boolean.ts | 58 +- test/runtime/value/convert/composite.ts | 2 +- test/runtime/value/convert/constructor.ts | 20 + test/runtime/value/convert/custom.ts | 6 +- test/runtime/value/convert/date.ts | 18 +- test/runtime/value/convert/function.ts | 20 + test/runtime/value/convert/index.ts | 7 +- test/runtime/value/convert/integer.ts | 28 +- test/runtime/value/convert/iterator.ts | 20 + test/runtime/value/convert/literal.ts | 30 +- test/runtime/value/convert/never.ts | 6 +- test/runtime/value/convert/null.ts | 6 +- test/runtime/value/convert/number.ts | 29 +- test/runtime/value/convert/object.ts | 2 +- test/runtime/value/convert/promise.ts | 21 + test/runtime/value/convert/record.ts | 2 +- .../value/convert/{regex.ts => regexp.ts} | 0 test/runtime/value/convert/string.ts | 26 +- test/runtime/value/convert/symbol.ts | 4 +- test/runtime/value/convert/tuple.ts | 6 +- test/runtime/value/convert/undefined.ts | 4 +- test/runtime/value/convert/union.ts | 8 +- test/runtime/value/convert/unknown.ts | 14 +- test/runtime/value/create/any.ts | 4 +- test/runtime/value/create/array.ts | 6 +- test/runtime/value/create/async-iterator.ts | 16 + test/runtime/value/create/bigint.ts | 4 +- test/runtime/value/create/boolean.ts | 4 +- test/runtime/value/create/composite.ts | 6 +- test/runtime/value/create/constructor.ts | 5 +- test/runtime/value/create/custom.ts | 4 +- test/runtime/value/create/enum.ts | 4 +- test/runtime/value/create/function.ts | 5 +- test/runtime/value/create/index.ts | 4 +- test/runtime/value/create/integer.ts | 4 +- test/runtime/value/create/intersect.ts | 19 +- test/runtime/value/create/iterator.ts | 16 + test/runtime/value/create/keyof.ts | 4 +- test/runtime/value/create/literal.ts | 8 +- test/runtime/value/create/never.ts | 2 +- test/runtime/value/create/not.ts | 6 +- test/runtime/value/create/null.ts | 4 +- test/runtime/value/create/number.ts | 4 +- test/runtime/value/create/object.ts | 14 +- test/runtime/value/create/record.ts | 4 +- test/runtime/value/create/recursive.ts | 10 +- test/runtime/value/create/ref.ts | 6 +- .../value/create/{regex.ts => regexp.ts} | 8 +- test/runtime/value/create/string.ts | 4 +- test/runtime/value/create/symbol.ts | 4 +- test/runtime/value/create/template-literal.ts | 12 +- test/runtime/value/create/tuple.ts | 8 +- test/runtime/value/create/uint8array.ts | 18 +- test/runtime/value/create/undefined.ts | 4 +- test/runtime/value/create/union.ts | 8 +- test/runtime/value/create/unknown.ts | 4 +- test/runtime/value/create/void.ts | 5 +- test/runtime/value/delta/diff.ts | 159 +- test/runtime/value/delta/patch.ts | 157 +- test/runtime/value/equal/equal.ts | 60 +- test/runtime/value/guard/guard.ts | 302 ++++ test/runtime/value/guard/index.ts | 1 + test/runtime/value/hash/hash.ts | 140 +- test/runtime/value/index.ts | 1 + test/runtime/value/mutate/mutate.ts | 40 +- test/runtime/value/pointer/pointer.ts | 245 ++- test/static/async-iterator.ts | 4 + test/static/awaited.ts | 20 + test/static/capitalize.ts | 4 + test/static/index.ts | 9 +- test/static/iterator.ts | 4 + test/static/lowercase.ts | 4 + test/static/modifier.ts | 2 +- test/static/readonly-optional.ts | 2 +- test/static/record.ts | 2 +- test/static/{regex.ts => regexp.ts} | 2 +- test/static/required.ts | 3 - test/static/uncapitalize.ts | 4 + test/static/uppercase.ts | 4 + tsconfig.json | 33 + 357 files changed, 8977 insertions(+), 6875 deletions(-) create mode 100644 changelog/0.30.0.md delete mode 100644 example/experimental/readme.md delete mode 100644 example/experimental/readonly-object.ts delete mode 100644 example/legacy/intersect-0.25.0.ts delete mode 100644 example/trpc/readme.md rename {example => examples}/collections/array.ts (99%) rename {example => examples}/collections/index.ts (100%) rename {example => examples}/collections/map.ts (97%) rename {example => examples}/collections/readme.md (100%) rename {example => examples}/collections/set.ts (99%) rename {example => examples}/formats/index.ts (100%) rename {example => examples}/formats/readme.md (100%) rename {example => examples}/formats/standard.ts (100%) rename {example => examples}/index.ts (100%) create mode 100644 examples/prototypes/const.ts create mode 100644 examples/prototypes/evaluate.ts rename {example/experimental => examples/prototypes}/index.ts (92%) create mode 100644 examples/prototypes/partial-deep.ts create mode 100644 examples/prototypes/readme.md rename {example/experimental => examples/prototypes}/union-enum.ts (78%) rename {example/experimental => examples/prototypes}/union-oneof.ts (61%) create mode 100644 examples/transform/index.ts create mode 100644 examples/transform/readme.md create mode 100644 examples/transform/transform.ts create mode 100644 examples/trpc/readme.md create mode 100644 examples/typedef/index.ts rename {example => examples}/typedef/readme.md (100%) rename {example => examples}/typedef/typedef.ts (87%) create mode 100644 src/value/guard.ts delete mode 100644 src/value/is.ts create mode 100644 test/runtime/compiler/async-iterator.ts create mode 100644 test/runtime/compiler/iterator.ts delete mode 100644 test/runtime/compiler/regex.ts create mode 100644 test/runtime/compiler/regexp.ts delete mode 100644 test/runtime/schema/modifier.ts delete mode 100644 test/runtime/schema/regex.ts create mode 100644 test/runtime/schema/regexp.ts create mode 100644 test/runtime/type/extends/async-iterator.ts create mode 100644 test/runtime/type/extends/iterator.ts create mode 100644 test/runtime/type/guard/async-iterator.ts create mode 100644 test/runtime/type/guard/awaited.ts create mode 100644 test/runtime/type/guard/capitalize.ts create mode 100644 test/runtime/type/guard/iterator.ts create mode 100644 test/runtime/type/guard/lowercase.ts create mode 100644 test/runtime/type/guard/required.ts create mode 100644 test/runtime/type/guard/uncapitalize.ts create mode 100644 test/runtime/type/guard/uppercase.ts create mode 100644 test/runtime/type/value/guard.ts create mode 100644 test/runtime/type/value/index.ts create mode 100644 test/runtime/value/cast/async-iterator.ts create mode 100644 test/runtime/value/cast/iterator.ts rename test/runtime/value/cast/{regex.ts => regexp.ts} (78%) create mode 100644 test/runtime/value/check/async-iterator.ts create mode 100644 test/runtime/value/check/iterator.ts delete mode 100644 test/runtime/value/check/regex.ts create mode 100644 test/runtime/value/check/regexp.ts create mode 100644 test/runtime/value/convert/async-iterator.ts create mode 100644 test/runtime/value/convert/constructor.ts create mode 100644 test/runtime/value/convert/function.ts create mode 100644 test/runtime/value/convert/iterator.ts create mode 100644 test/runtime/value/convert/promise.ts rename test/runtime/value/convert/{regex.ts => regexp.ts} (100%) create mode 100644 test/runtime/value/create/async-iterator.ts create mode 100644 test/runtime/value/create/iterator.ts rename test/runtime/value/create/{regex.ts => regexp.ts} (66%) create mode 100644 test/runtime/value/guard/guard.ts create mode 100644 test/runtime/value/guard/index.ts create mode 100644 test/static/async-iterator.ts create mode 100644 test/static/awaited.ts create mode 100644 test/static/capitalize.ts create mode 100644 test/static/iterator.ts create mode 100644 test/static/lowercase.ts rename test/static/{regex.ts => regexp.ts} (62%) create mode 100644 test/static/uncapitalize.ts create mode 100644 test/static/uppercase.ts diff --git a/benchmark/compression/index.ts b/benchmark/compression/index.ts index fc49651ab..521031315 100644 --- a/benchmark/compression/index.ts +++ b/benchmark/compression/index.ts @@ -19,7 +19,7 @@ export async function compression() { const tests = readdirSync('benchmark/compression/module').map((name) => basename(name, extname(name))) const results = await Promise.all(tests.map((test) => measure(test))) const present = results.reduce((acc, c) => { - return { ...acc, [c.test.replace('-', '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } } + return { ...acc, [c.test.replace(/-/g, '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } } }, {}) console.table(present) } diff --git a/benchmark/compression/module/typebox-compiler.ts b/benchmark/compression/module/typebox-compiler.ts index 97a54ba07..4d23bb2f2 100644 --- a/benchmark/compression/module/typebox-compiler.ts +++ b/benchmark/compression/module/typebox-compiler.ts @@ -1,4 +1,3 @@ import { TypeCompiler } from '@sinclair/typebox/compiler' -import { Type } from '@sinclair/typebox' -const T = TypeCompiler.Compile(Type.String()) +console.log(TypeCompiler) diff --git a/benchmark/compression/module/typebox-errors.ts b/benchmark/compression/module/typebox-errors.ts index 883bf9614..4a6324bbb 100644 --- a/benchmark/compression/module/typebox-errors.ts +++ b/benchmark/compression/module/typebox-errors.ts @@ -1,3 +1,3 @@ -import { ValueErrorType } from '@sinclair/typebox/errors' +import * as ValueErrors from '@sinclair/typebox/errors' -console.log(ValueErrorType) +console.log(ValueErrors) diff --git a/benchmark/compression/module/typebox-system.ts b/benchmark/compression/module/typebox-system.ts index 929092b50..f148a55cf 100644 --- a/benchmark/compression/module/typebox-system.ts +++ b/benchmark/compression/module/typebox-system.ts @@ -1,3 +1,3 @@ import { TypeSystem } from '@sinclair/typebox/system' -TypeSystem.AllowNaN = true +console.log(TypeSystem) diff --git a/benchmark/compression/module/typebox-value.ts b/benchmark/compression/module/typebox-value.ts index f616f8256..a5e563352 100644 --- a/benchmark/compression/module/typebox-value.ts +++ b/benchmark/compression/module/typebox-value.ts @@ -1,4 +1,3 @@ import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' -const T = Value.Create(Type.String()) +console.log(Value) diff --git a/benchmark/measurement/module/cases.ts b/benchmark/measurement/module/cases.ts index 36ec2d7d9..8325c80ba 100644 --- a/benchmark/measurement/module/cases.ts +++ b/benchmark/measurement/module/cases.ts @@ -11,7 +11,7 @@ export namespace Cases { export const Primitive_String = Type.String() - export const Primitive_String_Pattern = Type.RegEx(/foo/, { default: 'foo' }) + export const Primitive_String_Pattern = Type.RegExp(/foo/, { default: 'foo' }) export const Primitive_Boolean = Type.Boolean() diff --git a/changelog/0.30.0.md b/changelog/0.30.0.md new file mode 100644 index 000000000..31899d3fe --- /dev/null +++ b/changelog/0.30.0.md @@ -0,0 +1,419 @@ +## [0.30.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.30.0) + +## Overview + +Revision 0.30.0 is a milestone revision for the TypeBox project. It is primarily focused on internal optimizations, refactoring work to reduce package and bundle sizes, enable increased modularity of internal sub modules (with some considerations given to future ESM publishing), renaming internal functions to address react native bundling issues and consolidating shared internal modules to reduce overall code overhead. + +This revision also implements several new features, including new validation constraints for Array, new types for iterators, new utility types, a TypeScript code generation option for the compiler, enhancements made to modifiers and better options for TypeScript to TypeBox code translation. This revision also includes new examples including a transform type for handling IO encode an decode as well as a reference implementation for JSON Type Definition specification. + +This revision includes breaking changes and some deprecations. It requires a minor semver revision. + +## Contents + +- Enhancements + - [TypeScript Code Generation](#TypeScript-Code-Generation) + - [Optional and Readonly](#Optional-and-Readonly) + - [Iterator and AsyncIterator](#Iterator-and-AsyncIterator) + - [Order Independent References](#Order-Independent-References) + - [Value Submodules](#Value-Submodules) + - [Array Contains Constraint](#Array-Contains-Constraint) + - [Additional Utility Types](#Additional-Utility-Types) + - [Reduced Package Size](#Reduced-Package-Size) + - [TypeBox Codegen](#TypeBox-Codegen) +- Examples + - [JSON Type Definition](#JSON-Type-Definition) + - [Prototype Types](#Prototype-Types) + - [Transform Types](#Transform-Types) +- Breaking + - [Extended Type Representation-Change](#Extended-Type-Representation-Change) + - [Modifier Symbol Deprecated](#Modifier-Deprecated) + - [RegEx Renamed To RegExp](#RegEx-Renamed-To-RegExp) + - [ValueErrorType Custom Renamed To Kind](#ValueErrorType-Custom-Renamed-To-Kind) + + + +## TypeScript Code Generation + +Revision 0.30.0 adds TypeScript code generation support to the TypeCompiler. By specifying the language option on the `.Code()` function, TypeBox will add type annotations to the compiled output. This functionality can be used to produce typed TS functions for projects that preference AOT compilation. + +```typescript +const Code = TypeCompiler.Code(Type.String(), { // return function check(value: any): boolean { + language: 'typescript' // return ( +}) // (typeof value === 'string') + // ) + // } +``` + + + +## Optional and Readonly + +Revision 0.30.0 deprecates the `[Modifier]` symbol and introduces two new symbols, `[Readonly]` and `[Optional]`. This change is carried out to simplify type inference as well as to simplify runtime mapping logic. This change should not implicate users leveraging the `Type.*` purely for type composition, however implementors using TypeBox for reflection and code generation should update to the new symbols. + +```typescript +// Revision 0.29.0 +// +const A = Type.ReadonlyOptional(Type.Number()) // const A: TReadonlyOptional = { + // type: 'number', + // [TypeBox.Modifier]: 'ReadonlyOptional' + // } + +const B = Type.Readonly(Type.Number()) // const B: TReadonly = { + // type: 'number', + // [TypeBox.Modifier]: 'Readonly' + // } + +const C = Type.Optional(Type.Number()) // const C: TOptional = { + // type: 'number', + // [TypeBox.Modifier]: 'Optional' + // } + +// Revision 0.30.0 +// +const A = Type.ReadonlyOptional(Type.Number()) // const A: TReadonly> = { + // type: 'number', + // [TypeBox.Readonly]: 'Readonly', + // [TypeBox.Optional]: 'Optional' + // } + +const B = Type.Readonly(Type.Number()) // const B: TReadonly = { + // type: 'number', + // [TypeBox.Readonly]: 'Readonly' + // } + +const C = Type.Optional(Type.Number()) // const C: TOptional = { + // type: 'number', + // [TypeBox.Optional]: 'Optional' + // } +``` + + + +## Iterator and AsyncIterator + +Revision 0.30.0 adds the types `Iterator` and `AsyncIterator`. These types add to the existing non-validatable extended type set and can be used build callable generator functions. These types are written primarily to describe RPC network interfaces that return multiple values. Examples of which may include web socket streams or reading database result cursors over a network. + +```typescript +// Revision 0.30.0 +// +const Enumerable = (T: T) => Type.Function([ + Type.Number({ description: 'Start index' }), + Type.Number({ description: 'End index' }) +], Type.Iterator(T)) + +const EnumerableNumber = Enumerable(Type.Number()) + +const Range: Static = function * (start: number, end: number) { + for(let i = start; i < end; i++) yield i +} + +const R = [...Range(10, 20)] // const R = [10, 11, 12, ..., 19] +``` + + + +## Order Independent References + +Revision 0.30.0 adds an overload for `Ref` to enable non order dependent type referencing. Prior to this revision, reference targets needed to be defined first before being referenced. Revision 0.30.0 lifts this restriction and allows referencing of "yet to be defined" targets through the use of `typeof` operator. This overload borrows on TypeScript's ability to derive type information irrespective of topological ordering. + +This overload is implemented for "TypeScript to TypeBox" code generation utilities where TypeScript types are not guaranteed ordered in a runtime sorted fashion. + +```typescript +// Revision 0.29.0 +// +const R = Type.Ref(T) // Error: T isn't defined yet + +const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}, { $id: 'T' }) + +// Revision 0.30.0 +// +const R = Type.Ref('T') // Ok: infer from typeof T + +const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}, { $id: 'T' }) +``` + + + +## Value Submodules + +Revision 0.30.0 carries out a number of refactorings for the `Value.*` modules to enable each submodule to be imported individually. These refactorings are support better "pay to play" library characteristics, allowing users to import only the submodules they need. This update also makes provisions for ESM publishing by removing internal namespaces. + +The top level `Value.*` namespace will remain on all subsequent versions of TypeBox. + +```typescript +// Revision 0.29.0 +// +import { Value } from '@sinclair/typebox/value' // Value.* namespace + +const A = Value.Create(Type.String()) + +// Revision 0.30.0 +// +import { Create } from '@sinclair/typebox/value/create' // Only Create() + +const A = Create(Type.String()) +``` + + + +## Array Contains Constraint + +Revision 0.30.0 implements validation support for the `contains` keyword as well as the draft 2019-09 `minContains` and `maxContains` constraints on `Array`. Documentation on these constraints can be found https://json-schema.org/understanding-json-schema/reference/array.html#contains + +```typescript +// Revision 0.30.0 +// +const T = Type.Array(Type.Number(), { + contains: Type.Literal(1), + minContains: 3, + maxContains: 5 +}) + +Value.Check(T, [1, 1, 1]) // true - between 3 and 5 instances of 1 +Value.Check(T, [1, 1, 1, 1, 1]) // true - between 3 and 5 instances of 1 +Value.Check(T, [0, 1, 1, 1, 1, 1]) // true - between 3 and 5 instances of 1 +Value.Check(T, [1, 1]) // false - less than 3 instances of 1 +Value.Check(T, [1, 1, 1, 1, 1, 1]) // false - more than 5 instances of 1 +Value.Check(T, [0]) // false - no instances of 1 +``` + + + +## Additional Utility Types + +Revision 0.30.0 adds the utility types `Awaited`, `Uppercase`, `Lowercase`, `Capitalize`, and `Uncapitalize` to the supported type set. + +```typescript +// Revision 0.30.0 +const T1 = Type.Awaited(Type.Promise(Type.String())) // const T1: TString + +const T2 = Type.Uppercase(Type.Literal('hello')) // const T2: TLiteral<'HELLO'> + +const T3 = Type.Lowercase(Type.Literal('HELLO')) // const T3: TLiteral<'hello'> + +const T4 = Type.Capitalize(Type.Literal('hello')) // const T4: TLiteral<'Hello'> + +const T5 = Type.Uncapitalize(Type.Literal('HELLO')) // const T5: TLiteral<'hELLO'> +``` +A full list of TypeScript utility types can be found at this [link](https://www.typescriptlang.org/docs/handbook/utility-types.html). + + + +## Reduced Package Size + +Revision 0.30.0 carries out several internal refactorings to reduce package and bundle sizes. This work is largely an ongoing process with provisional work carried out across `type`, `value` and `compiler` modules. Revision 0.30.0 manages to weigh in slightly less than Revision 0.29.0 with the additional functionality provided on the revision. + +```typescript +// Revision 0.29.0 +// +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '130.3 kb' │ ' 58.2 kb' │ '2.24 x' │ +│ typebox/errors │ '113.3 kb' │ ' 49.8 kb' │ '2.27 x' │ +│ typebox/system │ ' 78.8 kb' │ ' 32.2 kb' │ '2.45 x' │ +│ typebox/value │ '180.0 kb' │ ' 77.7 kb' │ '2.32 x' │ +│ typebox │ ' 77.7 kb' │ ' 31.7 kb' │ '2.45 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ + +// Revision 0.30.0 +// +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '129.4 kb' │ ' 58.6 kb' │ '2.21 x' │ +│ typebox/errors │ '111.6 kb' │ ' 50.1 kb' │ '2.23 x' │ +│ typebox/system │ ' 76.5 kb' │ ' 31.7 kb' │ '2.41 x' │ +│ typebox/value │ '180.7 kb' │ ' 79.3 kb' │ '2.28 x' │ +│ typebox │ ' 75.4 kb' │ ' 31.3 kb' │ '2.41 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ +``` + + + +## TypeBox Codegen + +Revision 0.30.0 offers an external code generation API tool which can be used to programmatically convert TypeScript types into TypeBox types. + +[TypeBox-Code Project](https://github.com/sinclairzx81/typebox-codegen) + +```typescript +import * as Codegen from '@sinclair/typebox-codegen' + +const Code = Codegen.TypeScriptToTypeBox.Generate(` + type T = { x: number, y: number, z: number } +`) + +console.log(Code) + +// Output: +// +// import { Type, Static } from '@sinclair/typebox' +// +// type T = Static +// const T = Type.Object({ +// x: Type.Number(), +// y: Type.Number(), +// z: Type.Number() +// }) +``` + + + +## JSON Type Definition + +Revision 0.30.0 includes a reference implementation for JSON Type Definition (RFC 8927). This specification is currently under consideration for inclusion in the TypeBox library as an alternative schema representation for nominal type systems. The implementation currently contains all types expressed in the JSON Type Definition spec, but omits constraints such and `minimum` and `maximum` values (which are not formally represented in the specification). + +The implementation is offered as a single file which can be copied in to projects with TypeBox installed. This implementation may be enhanced over the next few revisions (with some potential to implement mapping types such as partial, required, omit, pick, keyof). This specification will be considered for inclusion under `@sinclair/typebox/typedef` if there is enough interest. + +```typescript +import { Type } from './typedef' // from: examples/typedef/typedef.ts + +const T3 = Type.Struct({ // const T3 = { + x: Type.Float32(), // properties: { + y: Type.Float32(), // x: { type: 'float32' }, + z: Type.Float32() // y: { type: 'float32' }, +}) // z: { type: 'float32' } + // } + // } + +const T2 = Type.Struct({ // const T3 = { + x: Type.Float32(), // properties: { + y: Type.Float32() // x: { type: 'float32' }, +}) // y: { type: 'float32' } + // } + // } + +const U = Type.Union([ // const U = { + T3, // discriminator: 'type', + T2 // mapping: { +]) // 0: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' }, + // z: { type: 'float32' } + // } + // }, + // 1: { + // properties: { + // x: { type: 'float32' }, + // y: { type: 'float32' } + // } + // } + // } + // } +``` + + + +## Prototype Types + +Revision 0.30.0 renames `Experimental` types to `Prototype` types within the examples directory. Updates here include additional documentation and rationales for the existing types `UnionOneOf`, `UnionEnum`, `Const`, and includes two new types `Evaluate` and `PartialDeep`. These types are written as standalone modules and can be copied into a project for direct use. The TypeBox project is open to community discussions around the inclusion of these types in future revisions. + + + +## Transform Types + +Revision 0.30.0 provides a reference implementation for Transform types. There has been some interest from users to offer combinators similar to Zod's `.transform()` function that permits remapping values during `.parse()` like operations. As TypeBox types do not have fluent combinators or a parse function (and are just JSON Schema objects), introducing similar functionality without augmenting types or implementing a `.parse()` on all types has proven to be particularily challenging. + +The reference Transform implementation implements a workable design by augmenting TypeBox types with codec functions outside the type system. These functions allow values to be structurally encoded and decoded through the `.parseLike()` functions `Encode()` and `Decode()`. TypeBox adopts the `io-ts` perspective for value transformation, viewing the act of transforming values primarily the role of dedicated codec system. As much of this functionality is considered high level and above and beyond the type system, Transform types will not likely be added to TypeBox type system; but rather added as an optional import in later revisions. + +```typescript +import { Transform, Encode, Decode } from './transform' + +const Timestamp = Transform(Type.Number(), { // The Transform function wraps a TypeBox type with two codec + decode: (value) => new Date(value), // functions which implement logic to decode a received value + encode: (value) => value.getTime(), // (i.e. number) into a application type (Date). The encode +}) // function handles the reverse mapping. + +type N = Static // type N = { timestamp: number } + // +const N = Type.Object({ // Transform types are to be used like any other type and will + timestamp: Timestamp // infer as the original TypeBox type. For example, the type `N` +}) // above will infer as { timestamp: number } (as derived from + // the TB type) + + + +const D = Decode(N, { timestamp: 123 }) // const D = { timestamp: Date(123) } + // + // The Decode function accepts any type plus a value. The Decode + // function return type will be that of the transforms decode() + // return type (which is Date), with the second argument statically + // typed as N. This function acts as a kind of parse() that returns + // the decoded type or throws on validation error. + + +const E = Encode(N, { timestamp: new Date(123) }) // const E = { timestamp: 123 } + // + // The encode function performs the inverse, accepting the + // decoded type { timestamp: Date } and re-encoding to the + // target type { timestamp: number }. This function will + // also throw on validation error. +``` + + + +## Extended Type Representation Change + +Revision 0.30.0 updates representations for all extended types. This change is made due to TypeBox's observed role as a general purpose JavaScript validation library as well as to deprecate support for extended type validation in Ajv which was only ever partially functional at best. + +Attempts were made on Revision 0.25.0 to restructure extended types to provide Ajv hooks for custom type configuration. These hooks used the `type` property where `{ type: 'object', instanceOf: 'Type' }` was used to configure schematics for JavaScript objects, and `{ type: 'null', typeOf: 'Type' }` was used for JavaScript primitives. Despite these hooks, Ajv would still struggle with validation of primitive types (such as `undefined`), and for the types `Function`, `Constructor` and `Promise`; these were meaningless to Ajv and it did not make sense to try provide hooks for a validator that could not make use of them. + +This change represents a move towards a formal specification to express pure JavaScript constructs which is partially under discussion within the runtime type community. This change will implicate the use of `Uint8Array` and `Date` objects when configuring for Ajv. A supplimentary fallback will be provided in the `/examples` directory using `Type.Unsafe` + +```typescript +// Revision 0.29.0 +// +const T = Type.Date() // const T: TDate = { type: 'object', instanceOf: 'Date' } + +const U = Type.Undefined() // const U: TUndefined = { type: 'null', typeOf: 'Undefined' } + +// Revision 0.30.0 +// +const T = Type.Date() // const T: TDate = { type: 'Date' } + +const U = Type.Undefined() // const U: TUndefined = { type: 'undefined' } +``` + + + +## RegEx Renamed To RegExp + +Revision 0.30.0 marks `Type.RegEx` as deprecated but provides `Type.RegExp` as an alternative (matching the JavaScript `RegExp` type name). Additionally this type has also been moved from the `Standard` to `Extended` type set. The `RegExp` type will no longer considered part of the Standard type set due to JavaScript Regular Expressions supporting a wider range of symbols and control characeters than is supported by the ECMA262 subset used by the JSON Schema specification. Information on the ECMA262 subset supported by JSON Schema can be found at the following Url https://json-schema.org/understanding-json-schema/reference/regular_expressions.html + +As `Type.RegEx()` is widely used, this function will be retained under the `@deprecated` annotation for the 0.30.0 revision. + +```typescript +// Revision 0.29.0 + +const T = Type.RegEx(/abc/) + +// Revision 0.30.0 + +const A = Type.RegEx(/abc/) // deprecation warning! + +const B = Type.RegExp(/abc/) // Extended Type + +const T = Type.String({ pattern: /abc/.source }) // Standard Type +``` +For Unicode (UTF-16) support on 0.30.0, the recommendation is to continue using user defined formats. + +```typescript +import { Type, FormatRegistry } from '@sinclair/typebox' + +FormatRegistry.Set('emoji', value => /|\p{Extended_Pictographic}/gu.test(value)) + +const T = Type.String({ format: 'emoji' }) + +Value.Check(T, '♥️♦️♠️♣️') // Ok +``` +For information on configuring custom formats on Ajv, refer to https://ajv.js.org/guide/formats.html#user-defined-formats diff --git a/example/experimental/readme.md b/example/experimental/readme.md deleted file mode 100644 index d22128721..000000000 --- a/example/experimental/readme.md +++ /dev/null @@ -1,54 +0,0 @@ -# Experimental Types - -These examples are a set of experiemental candidate types that may introduced into TypeBox in future. - -## ReadonlyObject - -Maps an object properties as `readonly`. - -```typescript -import { ReadonlyObject } from './experimental' - -const T = ReadonlyObject(Type.Object({ - x: Type.Number() -})) - -type T = Static // type T = { - // readonly x: number - // } -``` -## UnionEnum - -Creates an `enum` union string schema representation. - -```typescript -import { UnionEnum } from './experimental' - - -const T = UnionEnum(['A', 'B', 'C']) // const T = { - // enum: ['A', 'B', 'C'] - // } - -type T = Static // type T = 'A' | 'B' | 'C' - -``` -## UnionOneOf - -Creates a `oneOf` union representation. - - -```typescript -import { UnionOneOf } from './experimental' - - -const T = UnionOneOf([ // const T = { - Type.Literal('A'), // oneOf: [ - Type.Literal('B'), // { const: 'A' }, - Type.Literal('C') // { const: 'B' }, -]) // { const: 'C' }, - // ] - // } - -type T = Static // type T = 'A' | 'B' | 'C' - -``` \ No newline at end of file diff --git a/example/experimental/readonly-object.ts b/example/experimental/readonly-object.ts deleted file mode 100644 index b6ce4bacc..000000000 --- a/example/experimental/readonly-object.ts +++ /dev/null @@ -1,68 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/experimental - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '@sinclair/typebox' - -// ------------------------------------------------------------------------------------- -// TReadonlyObject -// ------------------------------------------------------------------------------------- -export type TReadonlyArray = Types.Assert<{ [K in keyof T]: TReadonlyObject> }, Types.TSchema[]> -// prettier-ignore -export type TReadonlyProperties = Types.Evaluate ? Types.TReadonlyOptional : - T[K] extends Types.TReadonly ? Types.TReadonly : - T[K] extends Types.TOptional ? Types.TReadonlyOptional : - Types.TReadonly -}, Types.TProperties>> -// prettier-ignore -export type TReadonlyObject = - T extends Types.TIntersect ? Types.TIntersect> : - T extends Types.TUnion ? Types.TUnion> : - T extends Types.TObject ? Types.TObject> : - Types.TReadonly - -/** `[Experimental]` Remaps a Intersect, Union or Object as readonly */ -export function ReadonlyObject(schema: T): TReadonlyObject { - function Apply(property: Types.TSchema): any { - // prettier-ignore - switch (property[Types.Modifier]) { - case 'ReadonlyOptional': property[Types.Modifier] = 'ReadonlyOptional'; break - case 'Readonly': property[Types.Modifier] = 'Readonly'; break - case 'Optional': property[Types.Modifier] = 'ReadonlyOptional'; break - default: property[Types.Modifier] = 'Readonly'; break - } - return property - } - // prettier-ignore - return (Types.TypeGuard.TIntersect(schema) || Types.TypeGuard.TUnion(schema) || Types.TypeGuard.TObject(schema)) - ? Types.ObjectMap.Map>(schema, (schema) => { - globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) - return schema - }, {}) : Apply(schema) -} \ No newline at end of file diff --git a/example/legacy/intersect-0.25.0.ts b/example/legacy/intersect-0.25.0.ts deleted file mode 100644 index d2fbd9545..000000000 --- a/example/legacy/intersect-0.25.0.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/legacy - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { TObject, ObjectOptions, Modifier, Kind, TSchema, Static } from '@sinclair/typebox' - -export type IntersectLegacyReduce = T extends [infer A, ...infer B] ? IntersectLegacyReduce : I extends object ? I : {} -export type IntersectLegacyEvaluate = { [K in keyof T]: T[K] extends TSchema ? Static : never } -export type IntersectLegacyProperties = { - [K in keyof T]: T[K] extends TObject ? P : {} -} -export interface TIntersectLegacy extends TObject { - static: IntersectLegacyReduce> - properties: IntersectLegacyReduce> -} - -/** `Legacy` Creates a legacy intersect type. */ -export function IntersectLegacy(objects: [...T], options: ObjectOptions = {}): TIntersectLegacy { - const isOptional = (schema: TSchema) => (schema[Modifier] && schema[Modifier] === 'Optional') || schema[Modifier] === 'ReadonlyOptional' - const [required, optional] = [new Set(), new Set()] - for (const object of objects) { - for (const [key, schema] of Object.entries(object.properties)) { - if (isOptional(schema)) optional.add(key) - } - } - for (const object of objects) { - for (const key of Object.keys(object.properties)) { - if (!optional.has(key)) required.add(key) - } - } - const properties = {} as Record - for (const object of objects) { - for (const [key, schema] of Object.entries(object.properties)) { - properties[key] = properties[key] === undefined ? schema : { [Kind]: 'Union', anyOf: [properties[key], { ...schema }] } - } - } - if (required.size > 0) { - return { ...options, [Kind]: 'Object', type: 'object', properties, required: [...required] } as any - } else { - return { ...options, [Kind]: 'Object', type: 'object', properties } as any - } -} diff --git a/example/trpc/readme.md b/example/trpc/readme.md deleted file mode 100644 index adcb43aa7..000000000 --- a/example/trpc/readme.md +++ /dev/null @@ -1,111 +0,0 @@ -# Using TypeBox with TRPC - -To use TypeBox with TRPC, you will need to wrap types passed to procedures within a TRPC compatible assertion function. Once wrapped, TypeBox can provide TRPC auto type inference for procedures, enhanced runtime checking performance as well as provide options to enabling publishable procedure schematics based on the JSON Schema specification. - -## Contents - -- [Example](#Example) -- [RpcType](#RpcType) - - [TypeCompiler](#TypeCompiler) - - [Value](#Value) - - [Ajv](#Ajv) - - - -## Example - -The following shows a minimal TypeBox + TRPC setup with the `RpcType` wrapper function described in the sections below. - -```typescript -import { Type } from '@sinclair/typebox' -import { initTRPC } from '@trpc/server' -import { RpcType } from './rpctype' - -const addInput = RpcType( - Type.Object({ - a: Type.Number(), - b: Type.Number(), - }), -) - -const addOutput = RpcType(Type.Number()) - -const t = initTRPC.create() - -export const appRouter = t.router({ - add: t.procedure - .input(addInput) - .output(addOutput) - .query(({ input }) => input.a + input.b), // type-safe -}) -``` - - - -## RpcType - -Unlike Zod, Yup, Superstruct, TypeBox types are pure JSON Schema and do not implement a built in `parse()` or `validate()` function. Because of this, you will need to wrap the Type in a TRPC compatible function which implements `parse()` on behalf of TRPC. As JSON Schema is a formal specification, there are a number of ways you may choose to implement this function. The following are some recommended implementations. - - - -### With [TypeCompiler](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgFQJ5gKYGELmAGwyjgF84AzKXOAcgAEBnYAOwGN8BDYKAehnQwAjCAA8erXGAJEaAWABQoSLEQoAyqwAWGEB1JwDhytXpM2nbnwHCRcxeGjwkyAEoAFLAFEoVYmSNUILR0MFBgrDwMRABuMgoKGCLK8OQArmwwwBDMcC7haJgAPMhwiTAYzAAmDOpaOhwAfAAUDHW6AFwoADRwUBjkRBWsGAydyBraugDaALpwALxwswCUiAoGEswM8HWsANYLKAI4eIRQAHQnUoQtbRw9fQN9bCPL670YMKlQOU3RHPhUhhOuk9swIAB3ZireYNNbyQxwYDkOBNXZ7S7afZ-AFA5arPpfH5wf6AjDvDbZbaqMAcGCaHogEYMDgAcww+kW6PO3l8DBxZOW5wAYtxtk1lgBCClwelUCFwZgYBWuDy86BNJBMhgs9mdAAGABIENrdRgyORoHBjbT6SR9T0JJVgbQAEIAQQAIgB9FyeACKAFVPGpkDRSG8EaQFCQgA) - -The following uses the TypeCompiler to perform JIT optimized type checking for TRPC. - -```typescript -import { TypeCompiler } from '@sinclair/typebox/compiler' -import { TSchema } from '@sinclair/typebox' -import { TRPCError } from '@trpc/server' - -export function RpcType(schema: T, references: TSchema[] = []) { - const check = TypeCompiler.Compile(schema, references) - return (value: unknown) => { - if (check.Check(value)) return value - const { path, message } = check.Errors(value).First()! - throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) - } -} -``` - - - -### With [Value](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgNQIYBsCuBTOBfOQwgMyghDgHIABAZ2ADsBjdVYKAehgE8xsAjCAA8OANww5KAWABQoSLERwAKgGUmAC2whU+IqXJU6jFm048+godLnho8JMoBKABQDCAUShkoegxRoYKDAmDlpsKFEIm1lsIQV4YkxmGGAIBjgnEOVebAAeZTg4mGwGABNaFXUtHQA+AApaTW1UAC4VABo4KGxiCNKmbFp2tWadAG0AXTgAXjgpgEpEWUIemEwoDPrxLGx25IBrBggAdwYlmdrlmSI4YGI4erRdgDo3LSYDxrHULp6+nrMIZdHY4BZLNYbDKg7ArIhMdK0BxwMCoGAaLogIa0VAAc1wBDmzxwLy8Plo3xqvzgMIWLwAYuwkfUFgBCOGEdFkE5wBjYHnOdxk6D1JBY2g4-HtAAGABIEOLJdgCMRoHB5aj0XhpV0EWU9lQAEIAQQAIgB9JweACKAFUPKplJR8As4XhZHggA) - -The following uses dynamic type checking without code evaluation. This is slower to validate, but may be required in restricted code evaluation environments. - -```typescript -import { Value } from '@sinclair/typebox/value' -import { TSchema } from '@sinclair/typebox' -import { TRPCError } from '@trpc/server' - -export function RpcType(schema: T, references: TSchema[] = []) { - return (value: unknown) => { - if (Value.Check(schema, references, value)) return value - const { path, message } = Value.Errors(schema, value).First()! - throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) - } -} -``` - - - -### With [Ajv](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgFQMoGMAWBTEBDAGjlRjxmHTgF84AzKCEOAcgAEBnYAO3QBs9gUAPQwAnmGwAjCAA9mAWABQoSLEQoASgAUAwgFEoDKNTimzdBkzYwoYdEPbYoANycLl4aPACCAK2cWjCx4-kIAJsDsMEIATAAM8e5K2DKq8LQArjzkEFxwGnbI4tgAPMhwKTDYXGHsKBg4+AB8ABTsWLh4AFwoRFDYtE7V6NjsPWgd+ADaALpwALxwswCUiEqm6LlRcCEBi1zYAO5wfs4ty+tw-YP9PKMAdLTQenhYLS3XQ3er8007-vc8GEwg1Oh8Bl8RssLooNlt4B10ABrBb-Zz3TbgYC8bBtSZ4GGmfowDJQPItZx4XgZbA9LJIrgQQ5cZY9EhkChlP6-NawszAWhwFqIpEUqk06FXbAkslwSnU7CXOFcbZIECjdh4ADm2CI3CieDuWjImBMixF9ycRnYAEIpnEZkq4DBMAxjgdjshtPpDNAWmqNdraXAAAYAEgQ6vYmp1NCexgj+tIRpNVBDRE2YWDzAAQt4ACIAfQ0egAigBVPSoZDMaiE6hKKhAA) - -The following uses Ajv to perform more generalized JSON Schema checks across the complete JSON Schema specification. - -```typescript -import { TSchema, Static } from '@sinclair/typebox' -import { TRPCError } from '@trpc/server' -import Ajv from 'ajv/dist/2020' - -export function RpcType(schema: T, references: TSchema[] = []) { - const ajv = new Ajv() - references.forEach((reference) => ajv.addSchema(reference)) - const check = ajv.compile(schema) - return (value: unknown): Static => { - if (check(value)) return value - const { message, instancePath } = check.errors![0] - throw new TRPCError({ message: `${message} for ${instancePath}`, code: 'BAD_REQUEST' }) - } -} -``` diff --git a/example/collections/array.ts b/examples/collections/array.ts similarity index 99% rename from example/collections/array.ts rename to examples/collections/array.ts index 8bc93a37b..1a7a7550b 100644 --- a/example/collections/array.ts +++ b/examples/collections/array.ts @@ -33,7 +33,6 @@ import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeArrayError // ---------------------------------------------------------------- - export class TypeArrayError extends Error { constructor(message: string) { super(`${message}`) @@ -44,15 +43,12 @@ export class TypeArrayLengthError extends Error { super('arrayLength not a number') } } - // ---------------------------------------------------------------- // TypeArray // ---------------------------------------------------------------- - export class TypeArray implements Iterable> { readonly #typeCheck: TypeCheck readonly #values: Static[] - constructor(schema: T, arrayLength: number = 0) { if (typeof arrayLength !== 'number') throw new TypeArrayLengthError() this.#typeCheck = TypeCompiler.Compile(schema) @@ -61,33 +57,27 @@ export class TypeArray implements Iterable> { this.#values[i] = Value.Create(schema) } } - // --------------------------------------------------- // Indexer // --------------------------------------------------- - /** Sets the value at the given index */ public set(index: number, item: Static): void { this.#assertIndexInBounds(index) this.#assertItem(index, item) this.#values[index] = item } - // --------------------------------------------------- // Array // --------------------------------------------------- - /** Iterator for values in this array */ public [Symbol.iterator](): IterableIterator> { return this.#values[Symbol.iterator]() as IterableIterator } - /** Gets the value at the given index */ public at(index: number): Static { this.#assertIndexInBounds(index) return this.#values[index] as T } - /** * Gets the length of the array. This is a number one higher than the highest index in the array. */ @@ -106,7 +96,6 @@ export class TypeArray implements Iterable> { public toLocaleString(): string { return this.#values.toLocaleString() } - /** * Removes the last element from an array and returns it. * If the array is empty, undefined is returned and the array is not modified. @@ -128,7 +117,6 @@ export class TypeArray implements Iterable> { * @param items Additional arrays and/or items to add to the end of the array. */ public concat(...items: ConcatArray>[]): Static[] - /** * Combines two or more arrays. * This method returns a new array without modifying any existing arrays. @@ -138,7 +126,6 @@ export class TypeArray implements Iterable> { this.#assertItems(items) return this.#values.concat(...items) as Static[] } - /** * Adds all the elements of an array into a string, separated by the specified separator string. * @param separator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma. @@ -160,7 +147,6 @@ export class TypeArray implements Iterable> { public shift(): Static | undefined { return this.#values.shift() as Static | undefined } - /** * Returns a copy of a section of an array. * For both start and end, a negative index can be used to indicate an offset from the end of the array. @@ -173,7 +159,6 @@ export class TypeArray implements Iterable> { public slice(start?: number, end?: number): Static[] { return this.#values.slice(start, end) as Static[] } - /** * Sorts an array in place. * This method mutates the array and returns a reference to the same array. @@ -188,7 +173,6 @@ export class TypeArray implements Iterable> { this.#values.sort(compareFn as any) return this } - /** * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. * @param start The zero-based location in the array from which to start removing elements. @@ -196,7 +180,6 @@ export class TypeArray implements Iterable> { * @returns An array containing the elements that were deleted. */ public splice(start: number, deleteCount?: number): Static[] - /** * Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. * @param start The zero-based location in the array from which to start removing elements. @@ -208,7 +191,6 @@ export class TypeArray implements Iterable> { this.#assertItems(items) return this.#values.splice(start, deleteCount, items) as Static[] } - /** * Inserts new elements at the start of an array, and returns the new length of the array. * @param items Elements to insert at the start of the array. @@ -217,7 +199,6 @@ export class TypeArray implements Iterable> { this.#assertItems(items) return this.#values.unshift(items) } - /** * Returns the index of the first occurrence of a value in an array, or -1 if it is not present. * @param searchElement The value to locate in the array. @@ -226,7 +207,6 @@ export class TypeArray implements Iterable> { public indexOf(searchElement: Static, fromIndex?: number): number { return this.#values.indexOf(searchElement, fromIndex) } - /** * Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present. * @param searchElement The value to locate in the array. @@ -235,7 +215,6 @@ export class TypeArray implements Iterable> { public lastIndexOf(searchElement: Static, fromIndex?: number): number { return this.#values.lastIndexOf(searchElement, fromIndex) } - /** * Determines whether all the members of an array satisfy the specified test. * @param predicate A function that accepts up to three arguments. The every method calls @@ -245,7 +224,6 @@ export class TypeArray implements Iterable> { * If thisArg is omitted, undefined is used as the this value. */ public every(predicate: (value: Static, index: number, array: Static[]) => value is Static, thisArg?: any): this is Static[] - /** * Determines whether all the members of an array satisfy the specified test. * @param predicate A function that accepts up to three arguments. The every method calls @@ -257,7 +235,6 @@ export class TypeArray implements Iterable> { public every(predicate: (value: Static, index: number, array: Static[]) => unknown, thisArg?: any): boolean { return this.#values.every(predicate as any, thisArg) } - /** * Determines whether the specified callback function returns true for any element of an array. * @param predicate A function that accepts up to three arguments. The some method calls @@ -269,7 +246,6 @@ export class TypeArray implements Iterable> { public some(predicate: (value: Static, index: number, array: T[]) => unknown, thisArg?: any): boolean { return this.#values.some(predicate as any, thisArg) } - /** * Performs the specified action for each element in an array. * @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. @@ -286,7 +262,6 @@ export class TypeArray implements Iterable> { public map(callbackfn: (value: Static, index: number, array: Static[]) => U, thisArg?: any): U[] { return this.#values.map(callbackfn as any, thisArg) } - /** * Returns the elements of an array that meet the condition specified in a callback function. * @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array. @@ -302,7 +277,6 @@ export class TypeArray implements Iterable> { public filter(predicate: any, thisArg: any): any { return this.#values.filter(predicate as any, thisArg) } - /** * Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. * @param callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array. @@ -335,11 +309,9 @@ export class TypeArray implements Iterable> { public reduceRight(callbackfn: any, initialValue?: any): any { return this.#values.reduceRight(callbackfn, initialValue) } - // --------------------------------------------------- // Assertions // --------------------------------------------------- - #formatError(errors: ValueError[]) { return errors.map((error) => `${error.message} ${error.path}`).join('. ') } @@ -348,21 +320,18 @@ export class TypeArray implements Iterable> { if (index >= 0 && index < this.#values.length) return throw new TypeArrayError(`Index ${index} is outside the bounds of this Array.`) } - /** Asserts the given values */ #assertItem(index: number, item: unknown): asserts item is Static { if (this.#typeCheck.Check(item)) return const message = this.#formatError([...this.#typeCheck.Errors(item)]) throw new TypeArrayError(`Item at Index ${index} is invalid. ${message}`) } - /** Asserts the given values */ #assertItems(items: unknown[]): asserts items is Static[] { for (let i = 0; i < items.length; i++) { this.#assertItem(i, items[i]) } } - /** Creates a typed array from an existing array */ public static from(schema: T, iterable: IterableIterator> | Array>): TypeArray { if (globalThis.Array.isArray(iterable)) { diff --git a/example/collections/index.ts b/examples/collections/index.ts similarity index 100% rename from example/collections/index.ts rename to examples/collections/index.ts diff --git a/example/collections/map.ts b/examples/collections/map.ts similarity index 97% rename from example/collections/map.ts rename to examples/collections/map.ts index debda1af6..2fa3dffed 100644 --- a/example/collections/map.ts +++ b/examples/collections/map.ts @@ -33,39 +33,29 @@ import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeMapKeyError // ---------------------------------------------------------------- - export class TypeMapKeyError extends Error { constructor(message: string) { super(`${message} for key`) } } - -// ---------------------------------------------------------------- -// TypeMapKeyError -// ---------------------------------------------------------------- - export class TypeMapValueError extends Error { constructor(key: unknown, message: string) { super(`${message} for key ${JSON.stringify(key)}`) } } - // ---------------------------------------------------------------- // TypeMap // ---------------------------------------------------------------- - // prettier-ignore type TypeMapEntries = | Iterable<[Static, Static]> | Array<[Static, Static]> - /** Runtime type checked Map collection */ export class TypeMap { readonly #keycheck: TypeCheck readonly #valuecheck: TypeCheck readonly #keys: Map> readonly #values: Map> - /** Constructs a new HashMap of the given key and value types. */ constructor(key: K, value: V, entries: TypeMapEntries = []) { this.#keycheck = TypeCompiler.Compile(key) @@ -76,40 +66,33 @@ export class TypeMap { this.set(key, value) } } - /** Iterator for this TypeMap */ public *[Symbol.iterator](): IterableIterator<[Static, Static]> { for (const [key, value] of this.#values) { yield [this.#keys.get(key)!, value] } } - /** Iterator for the keys in this TypeMap */ public *keys(): IterableIterator> { yield* this.#keys.values() } - /** Iterator for the values in this TypeMap */ public *values(): IterableIterator> { yield* this.#values.values() } - /** Clears all entries in this map */ public clear(): void { this.#values.clear() this.#keys.clear() } - /** Executes a provided function once per each key/value pair in the Map, in insertion order. */ public forEach(callbackfn: (value: Static, key: Static, map: TypeMap) => void, thisArg?: any): void { this.#values.forEach((value, key) => callbackfn(value, this.#keys.get(key)!, this)) } - /** @returns the number of elements in the TypeMap. */ public get size(): number { return this.#values.size } - /** * @returns boolean indicating whether an element with the specified key exists or not. */ @@ -117,7 +100,6 @@ export class TypeMap { this.#assertKey(key) return this.#values.has(this.#encodeKey(key)) } - /** * Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map. * @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned. @@ -126,7 +108,6 @@ export class TypeMap { this.#assertKey(key) return this.#values.get(this.#encodeKey(key))! } - /** * Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated. */ @@ -137,7 +118,6 @@ export class TypeMap { this.#keys.set(encodedKey, key) this.#values.set(encodedKey, value) } - /** * @returns true if an element in the Map existed and has been removed, or false if the element does not exist. */ @@ -147,33 +127,27 @@ export class TypeMap { this.#keys.delete(encodedKey) return this.#values.delete(encodedKey) } - // --------------------------------------------------- // Encoder // --------------------------------------------------- - /** Encodes the key as a 64bit numeric */ #encodeKey(key: Static) { return Value.Hash(key) } - // --------------------------------------------------- // Assertions // --------------------------------------------------- - #formatError(errors: ValueError[]) { return errors .map((error) => `${error.message} ${error.path}`) .join('. ') .trim() } - /** Asserts the key matches the key schema */ #assertKey(key: unknown): asserts key is Static { if (this.#keycheck.Check(key)) return throw new TypeMapKeyError(this.#formatError([...this.#keycheck.Errors(key)])) } - /** Asserts the key matches the value schema */ #assertValue(key: Static, value: unknown): asserts value is Static { if (this.#valuecheck.Check(value)) return diff --git a/example/collections/readme.md b/examples/collections/readme.md similarity index 100% rename from example/collections/readme.md rename to examples/collections/readme.md diff --git a/example/collections/set.ts b/examples/collections/set.ts similarity index 99% rename from example/collections/set.ts rename to examples/collections/set.ts index 6ca6e055a..4e8d31213 100644 --- a/example/collections/set.ts +++ b/examples/collections/set.ts @@ -31,19 +31,16 @@ import { TSchema, Static } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- -// TypeSetError +// Errors // ---------------------------------------------------------------- - export class TypeSetError extends Error { constructor(message: string) { super(`${message}`) } } - // ---------------------------------------------------------------- // TypeSet // ---------------------------------------------------------------- - /** Runtime type checked Set collection */ export class TypeSet { readonly #valuecheck: TypeCheck @@ -55,19 +52,16 @@ export class TypeSet { this.add(value) } } - /** Adds a value to this set */ public add(value: Static): this { this.#assertValue(value) this.values.set(this.#encodeKey(value), value) return this } - /** Clears the values in this set */ public clear(): void { this.values.clear() } - /** * Removes a specified value from the Set. * @returns Returns true if an element in the Set existed and has been removed, or false if the element does not exist. @@ -75,38 +69,31 @@ export class TypeSet { public delete(value: Static): boolean { return this.values.delete(this.#encodeKey(value)) } - /** Executes a provided function once per each value in the Set object, in insertion order. */ public forEach(callbackfn: (value: Static, value2: Static, set: TypeSet) => void, thisArg?: any): void { this.values.forEach((value, value2) => callbackfn(value, value2, this), thisArg) } - /** * @returns a boolean indicating whether an element with the specified value exists in the Set or not. */ public has(value: Static): boolean { return this.values.has(this.#encodeKey(value)) } - /** * @returns the number of (unique) elements in Set. */ public get size(): number { return this.values.size } - // --------------------------------------------------- // Encoder // --------------------------------------------------- - #encodeKey(value: Static) { return Value.Hash(value) } - // --------------------------------------------------- // Assertions // --------------------------------------------------- - /** Formats errors */ #formatError(errors: ValueError[]) { return errors @@ -114,7 +101,6 @@ export class TypeSet { .join('. ') .trim() } - /** Asserts the key matches the value schema */ #assertValue(value: unknown): asserts value is Static { if (this.#valuecheck.Check(value)) return diff --git a/example/formats/index.ts b/examples/formats/index.ts similarity index 100% rename from example/formats/index.ts rename to examples/formats/index.ts diff --git a/example/formats/readme.md b/examples/formats/readme.md similarity index 100% rename from example/formats/readme.md rename to examples/formats/readme.md diff --git a/example/formats/standard.ts b/examples/formats/standard.ts similarity index 100% rename from example/formats/standard.ts rename to examples/formats/standard.ts diff --git a/example/index.ts b/examples/index.ts similarity index 100% rename from example/index.ts rename to examples/index.ts diff --git a/examples/prototypes/const.ts b/examples/prototypes/const.ts new file mode 100644 index 000000000..75399a0aa --- /dev/null +++ b/examples/prototypes/const.ts @@ -0,0 +1,61 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Type, ObjectMap, TypeGuard, TSchema, TIntersect, TUnion, TObject, TProperties, TReadonly, AssertProperties, AssertType, AssertRest, Evaluate, ReadonlyUnwrapType } from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------- +// TConst +// ------------------------------------------------------------------------------------- +// prettier-ignore +export type TConstArray = T extends [infer L, ...infer R] + ? [TConst, E>, ...TConstArray, E>] + : [] +// prettier-ignore +export type TConstProperties = Evaluate, E> +}>> +// prettier-ignore +export type TConst = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + E extends true ? T : TReadonly +// ------------------------------------------------------------------------------------- +// Const +// ------------------------------------------------------------------------------------- +/** `[Experimental]` Assigns readonly to all interior properties */ +export function Const(schema: T): TConst { + const mappable = (TypeGuard.TIntersect(schema) || TypeGuard.TUnion(schema) || TypeGuard.TObject(schema)) + // prettier-ignore + return mappable ? ObjectMap.Map(schema, (object) => { + const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { + return { ...acc, [key]: Type.Readonly(object.properties[key] )} + }, {} as TProperties) + return Type.Object(properties, {...object}) + }, {}) : schema as any +} \ No newline at end of file diff --git a/examples/prototypes/evaluate.ts b/examples/prototypes/evaluate.ts new file mode 100644 index 000000000..5fa7b2128 --- /dev/null +++ b/examples/prototypes/evaluate.ts @@ -0,0 +1,135 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { + AssertRest, + AssertType, + Static, + Type, + TypeGuard, + TSchema, + TObject, + Evaluate, + TArray, + TFunction, + TConstructor, + TPromise, + TIterator, + TAsyncIterator, + TTuple, + TProperties, + TIntersect, + IntersectType, + TUnion, + TNever +} from '@sinclair/typebox' + +// -------------------------------------------------------------------------- +// TEvaluate +// -------------------------------------------------------------------------- +// prettier-ignore +export type TEvaluateIntersectType = ( + Static extends Static ? + Static extends Static ? R : + Static extends Static ? L : + TNever : + Static extends Static ? R : + TNever +) +// prettier-ignore +export type TEvaluateIntersectRest = + T extends [infer L, infer R, ...infer Rest] ? TEvaluateIntersectRest<[TEvaluateIntersectType, AssertType>, ...AssertRest]> : + T +// prettier-ignore +export type TEvaluateProperties = Evaluate<{ + [K in keyof T]: TEvaluate +}> +// prettier-ignore +export type TEvaluateArray = T extends [infer L, ...infer R] ? + [TEvaluate>, ...TEvaluateArray>] : + [] +// prettier-ignore +export type TEvaluate = + T extends TIntersect ? IntersectType> : + T extends TUnion ? TUnion> : + T extends TConstructor ? TConstructor, TEvaluate> : + T extends TFunction ? TFunction, TEvaluate> : + T extends TObject ? TObject> : + T extends TTuple ? TTuple> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : + T extends TIterator ? TIterator> : + T extends TAsyncIterator ? TAsyncIterator> : + T +// -------------------------------------------------------------------------- +// Evaluate +// -------------------------------------------------------------------------- +// prettier-ignore +export function EvaluateIntersectType(L: X, R: Y) { + return Type.Extends(L, R, + Type.Extends(R, L, R, + Type.Extends(L, R, L, + Type.Never())), + Type.Extends(R, L, R, + Type.Never())) +} +// prettier-ignore +export function EvaluateIntersectRest(T: [...T]): TEvaluateIntersectRest { + if(T.length >= 2) { + const [L, R, ...Rest] = T + return EvaluateIntersectRest([EvaluateIntersectType(L, R), ...Rest]) as any + } else { + return T as any + } +} +export function EvaluateProperties(properties: T) { + return Object.getOwnPropertyNames(properties).reduce((acc, key) => { + return { ...acc, [key]: Evaluate(properties[key]) } + }, {} as TProperties) +} +export function EvaluateArray(rest: T) { + if (rest === undefined) return [] // for tuple items + return rest.map((schema) => Evaluate(schema)) +} +// prettier-ignore +export function Evaluate(schema: T): TEvaluate { + return ( + TypeGuard.TIntersect(schema) ? Type.Intersect(EvaluateIntersectRest(schema.allOf)) : + TypeGuard.TUnion(schema) ? Type.Union(EvaluateArray(schema.anyOf)) : + TypeGuard.TAsyncIterator(schema) ? Type.AsyncIterator(Evaluate(schema.items)) : + TypeGuard.TIterator(schema) ? Type.Iterator(Evaluate(schema.items)) : + TypeGuard.TObject(schema) ? Type.Object(EvaluateProperties(schema.properties)) : + TypeGuard.TConstructor(schema) ? Type.Constructor(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : + TypeGuard.TFunction(schema) ? Type.Function(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : + TypeGuard.TTuple(schema) ? Type.Tuple(EvaluateArray(schema.items)) : + TypeGuard.TArray(schema) ? Type.Promise(Evaluate(schema.items)) : + TypeGuard.TPromise(schema) ? Type.Promise(Evaluate(schema.item)) : + schema + ) as TEvaluate +} + diff --git a/example/experimental/index.ts b/examples/prototypes/index.ts similarity index 92% rename from example/experimental/index.ts rename to examples/prototypes/index.ts index daddc72d8..ef44e7d95 100644 --- a/example/experimental/index.ts +++ b/examples/prototypes/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/experimental +@sinclair/typebox/prototypes The MIT License (MIT) @@ -26,6 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './readonly-object' +export * from './const' +export * from './evaluate' +export * from './partial-deep' export * from './union-enum' export * from './union-oneof' diff --git a/examples/prototypes/partial-deep.ts b/examples/prototypes/partial-deep.ts new file mode 100644 index 000000000..1315b65b0 --- /dev/null +++ b/examples/prototypes/partial-deep.ts @@ -0,0 +1,65 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TProperties, AssertRest, AssertType, Evaluate } from '@sinclair/typebox' + +// ------------------------------------------------------------------------------------- +// TDeepPartial +// ------------------------------------------------------------------------------------- +export type TPartialDeepProperties = { + [K in keyof T]: TPartial +} +export type TPartialDeepRest = T extends [infer L, ...infer R] + ? [TPartial>, ...TPartialDeepRest>] + : [] +export type TPartialDeep = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TPartial>>> : + T +// ------------------------------------------------------------------------------------- +// DeepPartial +// ------------------------------------------------------------------------------------- +function PartialDeepProperties(properties: T) { + return Object.getOwnPropertyNames(properties).reduce((acc, key) => { + return {...acc, [key]: Type.Partial(properties[key])} + }, {} as TProperties) +} +function PartialDeepRest(rest: [...T]): TPartialDeepRest { + const [L, ...R] = rest + return (R.length > 0) ? [Type.Partial(L), ...PartialDeepRest(R)] : [] as any +} +/** Maps the given schema as deep partial, making all properties and sub properties optional */ +export function PartialDeep(type: T): TPartialDeep { + return ( + TypeGuard.TIntersect(type) ? Type.Intersect(PartialDeepRest(type.allOf)) : + TypeGuard.TUnion(type) ? Type.Union(PartialDeepRest(type.anyOf)) : + TypeGuard.TObject(type) ? Type.Partial(Type.Object(PartialDeepProperties(type.properties))) : + type + ) as any +} \ No newline at end of file diff --git a/examples/prototypes/readme.md b/examples/prototypes/readme.md new file mode 100644 index 000000000..77a962c70 --- /dev/null +++ b/examples/prototypes/readme.md @@ -0,0 +1,114 @@ +# TypeBox Prototypes + +TypeBox prototypes are a set of types that are either under consideration for inclusion into the library, or have been requested by users but cannot be added to the library either due to complexity, using schematics that fall outside the supported TypeBox or should be expressed by users via advanced type composition. + +Each prototype is written as a standalone module that can be copied into projects and used directly, or integrated into extended type builders. + +## Const + +This type will wrap all interior properties as `readonly` leaving the outer type unwrapped. This type is analogous to the `Readonly` TypeScript utility type, but as TypeBox uses this name as a property modifier, the name `Const` is used. + +```typescript +import { Const } from './prototypes' + +const T = Const(Type.Object({ // const T: TObject<{ + x: Type.Number() // x: Type.Readonly(Type.Number()) +})) // }> + +type T = Static // type T = { + // readonly x: number + // } +``` +## Evaluate + +This type is an advanced mapping type will evaluate for schema redundancy by reducing evaluating intersection rest arguments. This type detects if intersection would produce illogical `never`, removes duplicates and handles intersection type narrowing. This type is a strong candidate for inclusion into the TypeBox library but is pending an equivalent redundancy check for `union` rest arguments. + +```typescript +import { Evaluate } from './prototypes' + +// Evaluate for Duplicates +// +const T = Type.Intersect([ Type.Number(), Type.Number(), Type.Number() ]) + +const E = Evaluate(T) // const E: TNumber + +// Evaluate for TNever +// +const T = Type.Intersect([ Type.Number(), Type.String() ]) + +const E = Evaluate(T) // const E: TIntersect<[TNumber, TString]> + +// Evaluate for most narrowed type +// +const T = Type.Intersect([ Type.Number(), Type.Literal(1) ]) + +const E = Evaluate(T) // const E: TLiteral<1> +``` + +## PartialDeep + +Maps the given schema as deep partial, making all properties and sub properties optional. This type is asked for on occation, but as there is no TypeScript equivalent and because this type is typically handled through end user type mapping, this type is left out of TypeBox. + +```typescript +import { PartialDeep } from './prototypes' + +const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) +}) + +const P = PartialDeep(T) + +type P = Static // type P = { + // x?: { + // x?: number, + // y?: number + // }, + // y?: { + // x?: number, + // y?: number + // }, + // } +``` + +## UnionEnum + +Creates an `enum` union string schema representation. This type is often requested by OpenAPI users, particularily for documentation presentation. As TypeBox standardizes on `anyOf` for all unions, this type is generally at odds with TypeBox's internal representation. Some considerations for internally remapping this type into a `anyOf` through composition have been considered (and would be feasible), but as TypeScript doesn't have multiple representations for unions, neither should TypeBox, making this type an unlikely candidate. + +```typescript +import { UnionEnum } from './prototypes' + + +const T = UnionEnum(['A', 'B', 'C']) // const T = { + // enum: ['A', 'B', 'C'] + // } + +type T = Static // type T = 'A' | 'B' | 'C' + +``` +## UnionOneOf + +Creates a `oneOf` union representation. This type is often requested by users looking for discriminated union support (which is not formally supported by JSON Schema). TypeBox omits this type as `oneOf` has the potential to create illogical schematics where values match more than one sub schema (making type inference extremely difficult). TypeBox preferences users explicitly narrowing on a overlapping union post type check, making `anyOf` the ideal representation, leaving the `oneOf` type an unlikely candidate for inclusion in the library. + + +```typescript +import { UnionOneOf } from './prototypes' + + +const T = UnionOneOf([ // const T = { + Type.Literal('A'), // oneOf: [ + Type.Literal('B'), // { const: 'A' }, + Type.Literal('C') // { const: 'B' }, +]) // { const: 'C' }, + // ] + // } + +type T = Static // type T = 'A' | 'B' | 'C' + +``` \ No newline at end of file diff --git a/example/experimental/union-enum.ts b/examples/prototypes/union-enum.ts similarity index 78% rename from example/experimental/union-enum.ts rename to examples/prototypes/union-enum.ts index 9f0bd77f1..ee55d202c 100644 --- a/example/experimental/union-enum.ts +++ b/examples/prototypes/union-enum.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/experimental +@sinclair/typebox/prototypes The MIT License (MIT) @@ -26,22 +26,24 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '@sinclair/typebox' +import { TypeRegistry, Kind, TSchema, SchemaOptions } from '@sinclair/typebox' // ------------------------------------------------------------------------------------- // TUnionEnum // ------------------------------------------------------------------------------------- -export interface TUnionEnum extends Types.TSchema { - [Types.Kind]: 'UnionEnum' +export interface TUnionEnum extends TSchema { + [Kind]: 'UnionEnum' static: T[number] enum: T } - +// ------------------------------------------------------------------------------------- +// UnionEnum +// ------------------------------------------------------------------------------------- /** `[Experimental]` Creates a Union type with a `enum` schema representation */ -export function UnionEnum(values: [...T], options: Types.SchemaOptions = {}) { +export function UnionEnum(values: [...T], options: SchemaOptions = {}) { function UnionEnumCheck(schema: TUnionEnum<(string | number)[]>, value: unknown) { return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value) } - if (!Types.TypeRegistry.Has('UnionEnum')) Types.TypeRegistry.Set('UnionEnum', UnionEnumCheck) - return { ...options, [Types.Kind]: 'UnionEnum', enum: values } as TUnionEnum + if (!TypeRegistry.Has('UnionEnum')) TypeRegistry.Set('UnionEnum', UnionEnumCheck) + return { ...options, [Kind]: 'UnionEnum', enum: values } as TUnionEnum } \ No newline at end of file diff --git a/example/experimental/union-oneof.ts b/examples/prototypes/union-oneof.ts similarity index 61% rename from example/experimental/union-oneof.ts rename to examples/prototypes/union-oneof.ts index cec504644..5129d457a 100644 --- a/example/experimental/union-oneof.ts +++ b/examples/prototypes/union-oneof.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/experimental +@sinclair/typebox/prototypes The MIT License (MIT) @@ -26,19 +26,25 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '@sinclair/typebox' +import { TypeRegistry, Kind, Static, TSchema, SchemaOptions } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' -export interface TUnionOneOf extends Types.TSchema { - [Types.Kind]: 'UnionOneOf' - static: { [K in keyof T]: Types.Static }[number] +// ------------------------------------------------------------------------------------- +// TUnionOneOf +// ------------------------------------------------------------------------------------- +export interface TUnionOneOf extends TSchema { + [Kind]: 'UnionOneOf' + static: { [K in keyof T]: Static }[number] oneOf: T } +// ------------------------------------------------------------------------------------- +// UnionOneOf +// ------------------------------------------------------------------------------------- /** `[Experimental]` Creates a Union type with a `oneOf` schema representation */ -export function UnionOneOf(oneOf: [...T], options: Types.SchemaOptions = {}) { - function UnionOneOfCheck(schema: TUnionOneOf, value: unknown) { +export function UnionOneOf(oneOf: [...T], options: SchemaOptions = {}) { + function UnionOneOfCheck(schema: TUnionOneOf, value: unknown) { return 1 === schema.oneOf.reduce((acc: number, schema: any) => (Value.Check(schema, value) ? acc + 1 : acc), 0) } - if (!Types.TypeRegistry.Has('UnionOneOf')) Types.TypeRegistry.Set('UnionOneOf', UnionOneOfCheck) - return { ...options, [Types.Kind]: 'UnionOneOf', oneOf } as TUnionOneOf + if (!TypeRegistry.Has('UnionOneOf')) TypeRegistry.Set('UnionOneOf', UnionOneOfCheck) + return { ...options, [Kind]: 'UnionOneOf', oneOf } as TUnionOneOf } \ No newline at end of file diff --git a/examples/transform/index.ts b/examples/transform/index.ts new file mode 100644 index 000000000..d0bddc5f1 --- /dev/null +++ b/examples/transform/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/transform + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './transform' \ No newline at end of file diff --git a/examples/transform/readme.md b/examples/transform/readme.md new file mode 100644 index 000000000..bb02bc888 --- /dev/null +++ b/examples/transform/readme.md @@ -0,0 +1,83 @@ +# Transform Types + +Transform Types are an experimental codec system that supports deferred encode and decode of typed data structures received over IO interfaces. This system is undergoing consideration for inclusion in the TypeBox library. + +### Design + +The following is the high level design for Transform types. It consists of three functions, `Transform`, `Decode` and `Encode`. The following is the general usage. + +```typescript +import { Transform, Encode, Decode } from './transform' + +const Timestamp = Transform(Type.Number(), { // The Transform function wraps a TypeBox type with two codec + decode: (value) => new Date(value), // functions which implement logic to decode a received value + encode: (value) => value.getTime(), // (i.e. number) into a application type (Date). The encode +}) // function handles the reverse mapping. + +type N = Static // type N = { timestamp: number } + // +const N = Type.Object({ // Transform types are to be used like any other type and will + timestamp: Timestamp // infer as the original TypeBox type. For example, the type `N` +}) // above will infer as { timestamp: number } (as derived from + // the TB type) + + + +const D = Decode(N, { timestamp: 123 }) // const D = { timestamp: Date(123) } + // + // The Decode function accepts any type plus a value. The Decode + // function return type will be that of the transforms decode() + // return type (which is Date), with the second argument statically + // typed as N. This function acts as a kind of parse() that returns + // the decoded type or throws on validation error. + + +const E = Encode(N, { timestamp: new Date(123) }) // const E = { timestamp: 123 } + // + // The encode function performs the inverse, accepting the + // decoded type { timestamp: Date } and re-encoding to the + // target type { timestamp: number }. This function will + // also throw on validation error. +``` +### Integration + +A practical use case for Transform types is the automatic encode and decode of values received over the network. The following is an example implementation using the Fastify web framework with the TypeBox Type Provider (used for auto type inference). Note that this usage wouldn't be exclusive to Fastify. The implementation below uses explicit Decode and Encode within a route handler. + +```typescript +import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox' +import Fastify from 'fastify' + +const Data = Type.Object({ + timestamp: Transform(Type.Number(), { + decode: (value) => new Date(value), + encode: (value) => value.getTime(), + }) +}) + +Fastify().withTypeProvider().post('/', { + schema: { + body: Data + response: { 200: Data } + } +}, (req, res) => { + const decoded = Decode(Data, req.body) // decode { timestamp: number } + // as { timestamp: Date } + + // ... some operations on decoded value + // + // i.e. decoded.timestamp.getTime() + + const encoded = Encode(Data, decoded) // encode { timetamp: Date } + // as { timestamp: number } + + res.status(200).send(encoded) // send! +}) +``` + +### Current Status + +From the example above, the current design of Transform types would require explicit Encode and Decode within a route handler (or other function in receipt of data); and it would be very challenging for general purpose frameworks to integrate this functionality in a more concise fashion (currently). + +Possible options to make this more cohesive may be to provide route wrapping functions to handle codec execution and type inference. More integrated solutions would involve framework maintainers offering codec supportive type definitions to act as hooks for auto static inference (on both decode and encode phases). The latter option is the preference but would require a fair amount of engineering in downstream frameworks. + +Given the many unknowns and general complexity surrounding this feature, TypeBox offers the Transform implementation to the community (as a single `transform.ts` module) for experimentation and general feedback. TypeBox welcomes third party packages, example user integrations, code enhancements or general insight into end user usage patterns. \ No newline at end of file diff --git a/examples/transform/transform.ts b/examples/transform/transform.ts new file mode 100644 index 000000000..31d20f26c --- /dev/null +++ b/examples/transform/transform.ts @@ -0,0 +1,346 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/transform + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' +import * as ValueGuard from '@sinclair/typebox/value/guard' +import * as ValueCheck from '@sinclair/typebox/value/check' + +// -------------------------------------------------------------------------- +// Symbols +// -------------------------------------------------------------------------- +export const TransformSymbol = Symbol.for('TypeBox.Transform') + +// ---------------------------------------------------------------------------------------------- +// Errors +// ---------------------------------------------------------------------------------------------- +export class TransformDecodeError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { + super('ValueTransform: The value passed to decode was invalid') + } +} +export class TransformEncodeError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { + super('ValueTransform: The encode function returned an invalid value') + } +} +export class ValueTransformUnknownTypeError extends Error { + constructor(public readonly schema: Types.TSchema) { + super('Unknown type') + } +} +export class ValueTransformDereferenceError extends Error { + constructor(public readonly schema: Types.TRef | Types.TThis) { + super(`Unable to dereference type with $id '${schema.$ref}'`) + } +} +export class ValueTransformFallthroughError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public mode: ValueTransformMode) { + super('Unexpected transform error') + } +} +export class ValueTransformCodecError extends Error { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public mode: ValueTransformMode, error: any) { + super(`${error instanceof Error ? error.message : 'Unknown'}`) + } +} +export type ValueTransformMode = 'encode' | 'decode' + +// ---------------------------------------------------------------------------------------------- +// Apply +// ---------------------------------------------------------------------------------------------- +function Apply(schema: Types.TSchema, value: any, mode: ValueTransformMode) { + try { + if (!HasTransform(schema)) return value + const transform = schema[TransformSymbol] as unknown as TransformCodec + if (mode === 'decode') return transform.decode(value) + if (mode === 'encode') return transform.encode(value) + } catch (error) { + throw new ValueTransformCodecError(schema, value, mode, error) + } +} +// -------------------------------------------------------------------------- +// Guards +// -------------------------------------------------------------------------- +function HasTransform(schema: Types.TSchema): schema is Types.TSchema & { [TransformSymbol]: TransformCodec } { + return ValueGuard.HasPropertyKey(schema, TransformSymbol) +} +// ---------------------------------------------------------------------------------------------- +// Transform +// ---------------------------------------------------------------------------------------------- +function TAny(schema: Types.TAny, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { + return Apply(schema, value, mode) +} +function TArray(schema: Types.TArray, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { + const transformed = Apply(schema, value, mode) + return transformed.map((value: any) => Visit(schema.items, references, value, mode)) +} +function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TDate(schema: Types.TDate, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TNever(schema: Types.TNever, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TNull(schema: Types.TNull, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TObject(schema: Types.TObject, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Object.keys(schema.properties).reduce((acc, key) => { + return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key], mode) } : { ...acc } + }, value) +} +function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + const transformed = Apply(schema, value, mode) + const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[propertyKey] + const result = {} as Record + for (const [propKey, propValue] of Object.entries(transformed)) { + result[propKey] = Visit(property, references, propValue, mode) + } + return result +} +function TRef(schema: Types.TRef, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueTransformDereferenceError(schema) + const target = references[index] + const resolved = Visit(target, references, value, mode) + return Apply(schema, resolved, mode) +} +function TString(schema: Types.TString, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TThis(schema: Types.TThis, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueTransformDereferenceError(schema) + const target = references[index] + const resolved = Visit(target, references, value, mode) + return Apply(schema, resolved, mode) +} +function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + const transformed = Apply(schema, value, mode) + // prettier-ignore + return Apply(schema, transformed.map((value: any, index: any) => { + return index < schema.items!.length ? Visit(schema.items![index], references, value, mode) : value + }), mode) +} +function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + const transformed = Apply(schema, value, mode) + for (const subschema of schema.anyOf) { + const inner = Visit(subschema, references, transformed, mode) + if (ValueCheck.Check(subschema, references, inner)) { + return Apply(schema, inner, mode) + } + } + return transformed +} +function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode) { + return Apply(schema, value, mode) +} +function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { + const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + case 'Any': + return TAny(schema_, references_, value, mode) + case 'Array': + return TArray(schema_, references_, value, mode) + case 'BigInt': + return TBigInt(schema_, references_, value, mode) + case 'Boolean': + return TBoolean(schema_, references_, value, mode) + case 'Constructor': + return TConstructor(schema_, references_, value, mode) + case 'Date': + return TDate(schema_, references_, value, mode) + case 'Function': + return TFunction(schema_, references_, value, mode) + case 'Integer': + return TInteger(schema_, references_, value, mode) + case 'Intersect': + return TIntersect(schema_, references_, value, mode) + case 'Literal': + return TLiteral(schema_, references_, value, mode) + case 'Never': + return TNever(schema_, references_, value, mode) + case 'Null': + return TNull(schema_, references_, value, mode) + case 'Number': + return TNumber(schema_, references_, value, mode) + case 'Object': + return TObject(schema_, references_, value, mode) + case 'Promise': + return TPromise(schema_, references_, value, mode) + case 'Record': + return TRecord(schema_, references_, value, mode) + case 'Ref': + return TRef(schema_, references_, value, mode) + case 'String': + return TString(schema_, references_, value, mode) + case 'Symbol': + return TSymbol(schema_, references_, value, mode) + case 'TemplateLiteral': + return TTemplateLiteral(schema_, references_, value, mode) + case 'This': + return TThis(schema_, references_, value, mode) + case 'Tuple': + return TTuple(schema_, references_, value, mode) + case 'Undefined': + return TUndefined(schema_, references_, value, mode) + case 'Union': + return TUnion(schema_, references_, value, mode) + case 'Uint8Array': + return TUint8Array(schema_, references_, value, mode) + case 'Unknown': + return TUnknown(schema_, references_, value, mode) + case 'Void': + return TVoid(schema_, references_, value, mode) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueTransformUnknownTypeError(schema_) + return TKind(schema_, references_, value, mode) + } +} +// -------------------------------------------------------------------------- +// Transform Unwrap +// -------------------------------------------------------------------------- +// prettier-ignore +export type TransformUnwrapProperties = { + [K in keyof T]: TransformUnwrap +} +// prettier-ignore +export type TransformUnwrapRest = T extends [infer L, ...infer R] + ? [TransformUnwrap>, ...TransformUnwrapRest>] + : [] +// prettier-ignore +export type TransformUnwrap = + T extends TTransform ? Types.TUnsafe : + T extends Types.TConstructor ? Types.TConstructor>, TransformUnwrap> : + T extends Types.TFunction ? Types.TFunction>, TransformUnwrap> : + T extends Types.TIntersect ? Types.TIntersect>> : + T extends Types.TUnion ? Types.TUnion>> : + T extends Types.TNot ? Types.TNot> : + T extends Types.TTuple ? Types.TTuple>> : + T extends Types.TAsyncIterator ? Types.TAsyncIterator> : + T extends Types.TIterator ? Types.TIterator> : + T extends Types.TObject ? Types.TObject>> : + T extends Types.TRecord ? Types.TRecord> : + T extends Types.TArray ? Types.TArray> : + T extends Types.TPromise ? Types.TPromise> : + T +// -------------------------------------------------------------------------- +// Transform Types +// -------------------------------------------------------------------------- +export type TransformFunction = (value: T) => U + +export interface TransformCodec { + decode: TransformFunction, Output> + encode: TransformFunction> +} +export interface TTransform extends Types.TSchema { + static: Types.Static + [TransformSymbol]: TransformCodec + [key: string]: any +} +// -------------------------------------------------------------------------- +// Transform Functions +// -------------------------------------------------------------------------- +/** Creates a transform type by applying transform codec */ +export function Transform(schema: Input, codec: TransformCodec): TTransform { + if (!HasTransform(schema)) return { ...schema, [TransformSymbol]: codec } as TTransform + const mapped_encode = (value: unknown) => schema[TransformSymbol].encode(codec.encode(value as any)) + const mapped_decode = (value: unknown) => codec.decode(schema[TransformSymbol].decode(value)) + const mapped_codec = { encode: mapped_encode, decode: mapped_decode } + return { ...schema, [TransformSymbol]: mapped_codec } as TTransform +} +/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ +export function Decode(schema: T, references: Types.TSchema[], value: Types.Static): Types.Static> +/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ +export function Decode(schema: T, value: Types.Static): Types.Static> +/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ +export function Decode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + const check = ValueCheck.Check(schema, references, value) + if (!check) throw new TransformDecodeError(schema, value) + const decoded = Visit(schema, references, value, 'decode') + return decoded +} +/** Encodes a value using the given type. Will apply an transforms found for any sub type */ +export function Encode(schema: T, references: Types.TSchema[], value: Types.Static>): Types.Static +/** Encodes a value using the given type. Will apply an transforms found for any sub type */ +export function Encode(schema: T, value: Types.Static>): Types.Static +/** Encodes a value using the given type. Will apply an transforms found for any sub type */ +export function Encode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + const encoded = Visit(schema, references, value, 'encode') + const check = ValueCheck.Check(schema, references, encoded) + if (!check) throw new TransformEncodeError(schema, value) + return encoded +} diff --git a/examples/trpc/readme.md b/examples/trpc/readme.md new file mode 100644 index 000000000..c5c7051e8 --- /dev/null +++ b/examples/trpc/readme.md @@ -0,0 +1,46 @@ +# Using TypeBox with TRPC + +To use TypeBox with TRPC, you will need to wrap types in a TRPC compatible validation function. The following shows wrapping a type using the TypeCompiler. + + + +## Example + +[TypeScript Link Here](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzsAdsGAVASgBQMIA0c2+AolFNHAL5wBmlIcA5AAIxRgDGA9AM4BTKADchzALAAoUJFiJiATzAC8EcMAA2QmvUYtWfVFw0BDYFB4wlAgEYQAHjy5qwmsVJnR4SDNaIYAZS4ACwEQEx0GNX1DFGMzCytlO3sJSSkeHjgAWly8-ILCouKS0rLyvIys1XUtPjgI32UGlAATOCgBGABXKBR6iOMIPl6BeioSPCqcitm5+YWCqQF7WXg6briYYAgUOCxuJoEAHgw4FZgBNvrAkLCTAD4ACj478IAuYiJOuiErrgEfE+t1C4QA2gBdOAAXjgkIAlIgpHA4M5+vA7lwANYwxTKGquLRQAB0BLcLzeJm+Al+nTigPhyI6XV6eyewhMGm6Ak+myxKAgAHcUIjoQ8kZIUSjgHQ4E9MVjSaFsezOdz4YjOj0+nAOVyBEyUWi+N44GATDBgkQQIC+CYAOZjWiwhXE8iUKB8VX6+HEgBi5hNT3hAEJDXBLZRBXAUAJo5N3dAnkgbXw7Y7PgADAAkCFT6YEtDoVFz5st1EzRGcrR5LAAQgBBAAiAH0sKQAIoAVVIAQwzBojMlNCk1Gmiwnk6nlUkmTgXYL4+ny5XZSkxvg8FhqHQk2JXE6FoEwfXuxNDVa7VhMGJYEoANaoyZxNQYG6MCeBy4Rye4aOxIAeRsAArAQuA-BBwxRExgWsYkADluhAGwhGDAgoLgGxYOUBCkJQqA0PDaghxRDVnwgd83w-L8fz-ODEOQ1CSNI5jiQAR25KAFCeZNkBQKjBxhcVX3fYkIgAaj4qjiRsRE5ySARsjtX4pGWVYvFRM94BMMAwCwCjLigXEb0od9UKQJkTEvOBR3hIA) + +```typescript +import { initTRPC, TRPCError } from '@trpc/server' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Type, TSchema } from '@sinclair/typebox' + +// --------------------------------------------------------------------------------- +// Compiles a Type and returns a closure for TRPC +// --------------------------------------------------------------------------------- +export function RpcType(schema: T, references: TSchema[] = []) { + const check = TypeCompiler.Compile(schema, references) + return (value: unknown) => { + if (check.Check(value)) return value + const { path, message } = check.Errors(value).First()! + throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) + } +} +// --------------------------------------------------------------------------------- +// Usage +// --------------------------------------------------------------------------------- +const t = initTRPC.create() +const add = t.procedure + .input(RpcType( + Type.Object({ + a: Type.Number(), + b: Type.Number(), + }) + )) + .output(RpcType( + Type.Number() + )) + .query(({ input }) => input.a + input.b) // type-safe + +export const appRouter = t.router({ + add +}) +``` \ No newline at end of file diff --git a/examples/typedef/index.ts b/examples/typedef/index.ts new file mode 100644 index 000000000..495e365c3 --- /dev/null +++ b/examples/typedef/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/typedef + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './typedef' \ No newline at end of file diff --git a/example/typedef/readme.md b/examples/typedef/readme.md similarity index 100% rename from example/typedef/readme.md rename to examples/typedef/readme.md diff --git a/example/typedef/typedef.ts b/examples/typedef/typedef.ts similarity index 87% rename from example/typedef/typedef.ts rename to examples/typedef/typedef.ts index cfec35e41..9c959318d 100644 --- a/example/typedef/typedef.ts +++ b/examples/typedef/typedef.ts @@ -42,7 +42,7 @@ export type Increment = Reverse = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? T[K] : never } -type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TReadonlyOptional | Types.TOptional) ? never : T[K] } -export interface StructOptions extends SchemaOptions { +type OptionalKeys = { [K in keyof T]: T[K] extends (Types.TOptional) ? T[K] : never } +type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TOptional) ? never : T[K] } +export interface StructMetadata extends Metadata { additionalProperties?: boolean } -export interface TStruct extends Types.TSchema, StructOptions { +export interface TStruct extends Types.TSchema, StructMetadata { [Types.Kind]: 'TypeDef:Struct' static: Types.PropertiesReduce optionalProperties: { [K in Types.Assert, keyof T>]: T[K] } @@ -188,6 +188,11 @@ export interface TTimestamp extends Types.TSchema { type: 'timestamp' static: string } +// -------------------------------------------------------------------------- +// Static +// -------------------------------------------------------------------------- +export type Static = Types.Static + // -------------------------------------------------------------------------- // TimestampFormat // -------------------------------------------------------------------------- @@ -481,95 +486,102 @@ Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => Value // -------------------------------------------------------------------------- // TypeDefBuilder // -------------------------------------------------------------------------- -export class TypeDefBuilder extends Types.TypeBuilder { +export class TypeDefBuilder { + // ------------------------------------------------------------------------ + // Core + // ------------------------------------------------------------------------ + protected Create(schema: Record, metadata: Record): any { + const keys = globalThis.Object.getOwnPropertyNames(metadata) + return keys.length > 0 ? { ...schema, metadata: { ...metadata } } : { ...schema } + } + /** [Standard] Removes compositing symbols from this schema */ + public Strict(schema: T): T { + return JSON.parse(JSON.stringify(schema)) as T + } // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ - /** `[Modifier]` Creates a Optional property */ + /** `[Standard]` Creates an Optional property */ public Optional(schema: T): Types.TOptional { - return { [Types.Modifier]: 'Optional', ...Types.TypeClone.Clone(schema, {}) } - } - /** `[Modifier]` Creates a ReadonlyOptional property */ - public ReadonlyOptional(schema: T): Types.TReadonlyOptional { - return { [Types.Modifier]: 'ReadonlyOptional', ...Types.TypeClone.Clone(schema, {}) } + return this.Optional(schema) } - /** `[Modifier]` Creates a Readonly object or property */ + /** `[Standard]` Creates a Readonly property */ public Readonly(schema: T): Types.TReadonly { - return { [Types.Modifier]: 'Readonly', ...schema } + return this.Readonly(schema) } // ------------------------------------------------------------------------ // Types // ------------------------------------------------------------------------ /** [Standard] Creates a Array type */ - public Array(elements: T, options: SchemaOptions = {}): TArray { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Array', elements }) + public Array(elements: T, metadata: Metadata = {}): TArray { + return this.Create({ [Types.Kind]: 'TypeDef:Array', elements }, metadata) } /** [Standard] Creates a Boolean type */ - public Boolean(options: SchemaOptions = {}): TBoolean { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }) + public Boolean(metadata: Metadata = {}): TBoolean { + return this.Create({ [Types.Kind]: 'TypeDef:Boolean', type: 'boolean' }, metadata) } /** [Standard] Creates a Enum type */ - public Enum(values: [...T], options: SchemaOptions = {}): TEnum { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Enum', enum: values }) + public Enum(values: [...T], metadata: Metadata = {}): TEnum { + return this.Create({[Types.Kind]: 'TypeDef:Enum', enum: values }, metadata ) } /** [Standard] Creates a Float32 type */ - public Float32(options: SchemaOptions = {}): TFloat32 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Float32', type: 'float32' }) + public Float32(metadata: Metadata = {}): TFloat32 { + return this.Create({ [Types.Kind]: 'TypeDef:Float32', type: 'float32' }, metadata) } /** [Standard] Creates a Float64 type */ - public Float64(options: SchemaOptions = {}): TFloat64 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Float64', type: 'float64' }) + public Float64(metadata: Metadata = {}): TFloat64 { + return this.Create({ [Types.Kind]: 'TypeDef:Float64', type: 'float64' }, metadata) } /** [Standard] Creates a Int8 type */ - public Int8(options: SchemaOptions = {}): TInt8 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int8', type: 'int8' }) + public Int8(metadata: Metadata = {}): TInt8 { + return this.Create({ [Types.Kind]: 'TypeDef:Int8', type: 'int8' }, metadata) } /** [Standard] Creates a Int16 type */ - public Int16(options: SchemaOptions = {}): TInt16 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int16', type: 'int16' }) + public Int16(metadata: Metadata = {}): TInt16 { + return this.Create({ [Types.Kind]: 'TypeDef:Int16', type: 'int16' }, metadata) } /** [Standard] Creates a Int32 type */ - public Int32(options: SchemaOptions = {}): TInt32 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Int32', type: 'int32' }) + public Int32(metadata: Metadata = {}): TInt32 { + return this.Create({ [Types.Kind]: 'TypeDef:Int32', type: 'int32' }, metadata) } /** [Standard] Creates a Uint8 type */ - public Uint8(options: SchemaOptions = {}): TUint8 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }) + public Uint8(metadata: Metadata = {}): TUint8 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint8', type: 'uint8' }, metadata) } /** [Standard] Creates a Uint16 type */ - public Uint16(options: SchemaOptions = {}): TUint16 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }) + public Uint16(metadata: Metadata = {}): TUint16 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint16', type: 'uint16' }, metadata) } /** [Standard] Creates a Uint32 type */ - public Uint32(options: SchemaOptions = {}): TUint32 { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }) + public Uint32(metadata: Metadata = {}): TUint32 { + return this.Create({ [Types.Kind]: 'TypeDef:Uint32', type: 'uint32' }, metadata) } /** [Standard] Creates a Record type */ - public Record(values: T, options: SchemaOptions = {}): TRecord { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Record', values }) + public Record(values: T, metadata: Metadata = {}): TRecord { + return this.Create({ [Types.Kind]: 'TypeDef:Record', values },metadata) } /** [Standard] Creates a String type */ - public String(options: SchemaOptions = {}): TString { - return this.Create({ ...options, [Types.Kind]: 'TypeDef:String', type: 'string' }) + public String(metadata: Metadata = {}): TString { + return this.Create({ [Types.Kind]: 'TypeDef:String', type: 'string' }, metadata) } /** [Standard] Creates a Struct type */ - public Struct(fields: T, options?: StructOptions): TStruct { - const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) - const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) || Types.TypeGuard.TReadonlyOptional(fields[key]) ? { ...acc } : { ...acc, [key]: fields[key] }), {} as TFields) + public Struct(fields: T, metadata: StructMetadata = {}): TStruct { + const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) + const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) ? { ...acc } : { ...acc, [key]: fields[key] }), {} as TFields) const optionalObject = globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0 ? { optionalProperties: optionalProperties } : {} const requiredObject = globalThis.Object.getOwnPropertyNames(properties).length === 0 ? {} : { properties: properties } - return this.Create({ ...options, [Types.Kind]: 'TypeDef:Struct', ...requiredObject, ...optionalObject }) + return this.Create({ [Types.Kind]: 'TypeDef:Struct', ...requiredObject, ...optionalObject }, metadata) } /** [Standard] Creates a Union type */ public Union[], D extends string = 'type'>(structs: [...T], discriminator?: D): TUnion { discriminator = (discriminator || 'type') as D if (structs.length === 0) throw new Error('TypeDefBuilder: Union types must contain at least one struct') const mapping = structs.reduce((acc, current, index) => ({ ...acc, [index.toString()]: current }), {}) - return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }) + return this.Create({ [Types.Kind]: 'TypeDef:Union', discriminator, mapping }, {}) } /** [Standard] Creates a Timestamp type */ - public Timestamp(): TTimestamp { - return this.Create({ [Types.Kind]: 'TypeDef:Timestamp', type: 'timestamp' }) + public Timestamp(metadata: Metadata = {}): TTimestamp { + return this.Create({ [Types.Kind]: 'TypeDef:Timestamp', type: 'timestamp' }, metadata) } } diff --git a/hammer.mjs b/hammer.mjs index 5f37c1eb7..3bf3406f6 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -11,13 +11,13 @@ export async function clean() { // Format // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test example/index.ts benchmark') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test examples/index.ts benchmark') } // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- -export async function start(example = 'index') { - await shell(`hammer run example/${example}.ts --dist target/example/${example}`) +export async function start() { + await shell(`hammer run examples/index.ts --dist target/examples`) } // ------------------------------------------------------------------------------- // Benchmark @@ -64,7 +64,16 @@ export async function build(target = 'target/build') { // ------------------------------------------------------------- export async function publish(otp, target = 'target/build') { const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + if(version.includes('-dev')) throw Error(`package version should not include -dev specifier`) await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) await shell(`git tag ${version}`) await shell(`git push origin ${version}`) +} +// ------------------------------------------------------------- +// Publish-Dev +// ------------------------------------------------------------- +export async function publish_dev(otp, target = 'target/build') { + const { version } = JSON.parse(readFileSync(`${target}/package.json`, 'utf8')) + if(!version.includes('-dev')) throw Error(`development package version should include -dev specifier`) + await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp} --tag dev`) } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 7b9cb58d1..2c3f6f9a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.29.4", + "version": "0.30.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.29.4", + "version": "0.30.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 71c6e2c79..7f372b309 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.29.6", + "version": "0.30.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -16,6 +16,17 @@ "./compiler": "./compiler/index.js", "./errors": "./errors/index.js", "./system": "./system/index.js", + "./value/cast": "./value/cast.js", + "./value/check": "./value/check.js", + "./value/clone": "./value/clone.js", + "./value/convert": "./value/convert.js", + "./value/create": "./value/create.js", + "./value/delta": "./value/delta.js", + "./value/equal": "./value/equal.js", + "./value/guard": "./value/guard.js", + "./value/hash": "./value/hash.js", + "./value/mutate": "./value/mutate.js", + "./value/pointer": "./value/pointer.js", "./value": "./value/index.js", ".": "./typebox.js" }, @@ -33,7 +44,8 @@ "start": "hammer task start", "benchmark": "hammer task benchmark", "build": "hammer task build", - "publish": "hammer task publish" + "publish": "hammer task publish", + "publish:dev": "hammer task publish_dev" }, "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/readme.md b/readme.md index 4bf1b532b..2e8f6e443 100644 --- a/readme.md +++ b/readme.md @@ -63,7 +63,7 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder 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 assertion rules of the TypeScript compiler. TypeBox enables one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. +TypeBox is a runtime type builder 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 assertion rules of the TypeScript language. TypeBox allows one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. @@ -72,19 +72,20 @@ License MIT ## Contents - [Install](#install) - [Overview](#overview) +- [Features](#features) - [Usage](#usage) - [Types](#types) - [Standard](#types-standard) - [Extended](#types-extended) - - [Modifiers](#types-modifiers) - [Options](#types-options) + - [Properties](#types-properties) - [Generics](#types-generics) - [References](#types-references) - [Recursive](#types-recursive) - [Conditional](#types-conditional) - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) - - [Not](#types-not) + - [Negated](#types-negated) - [Rest](#types-rest) - [Guards](#types-guards) - [Unsafe](#types-unsafe) @@ -109,7 +110,7 @@ License MIT - [Types](#typesystem-types) - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) -- [Workbench](#workbench) +- [Transform](#Transform) - [Ecosystem](#ecosystem) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) @@ -197,13 +198,13 @@ function receive(value: T) { // ... as a Static Type ## Types -TypeBox types are JSON schema fragments that can be composed into more complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. +TypeBox types are JSON schema fragments that compose into complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. ### Standard Types -The following table lists the Standard TypeBox types. These types are fully compatible with the JSON Schema Draft 6 specification. +The following table lists the Standard TypeBox types. These types are fully compatible with the JSON Schema Draft 7 specification. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -336,10 +337,10 @@ The following table lists the Standard TypeBox types. These types are fully comp │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Composite([ │ type T = { │ const T = { │ -│ Type.Object({ │ x: number │ type: 'object', │ -│ x: Type.Number() │ } & { │ required: ['x', 'y'], │ -│ }), │ y: number │ properties: { │ -│ Type.Object({ │ } │ x: { │ +│ Type.Object({ │ x: number, │ type: 'object', │ +│ x: Type.Number() │ y: number │ required: ['x', 'y'], │ +│ }), │ } │ properties: { │ +│ Type.Object({ │ │ x: { │ │ y: Type.Number() │ │ type: 'number' │ │ }) │ │ }, │ │ ]) │ │ y: { │ @@ -386,6 +387,12 @@ The following table lists the Standard TypeBox types. These types are fully comp │ ) │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Pattern('^xy$') │ type T = string │ const T = { │ +│ │ │ type: 'string', │ +│ │ │ pattern: '^xy$' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const U = Type.Union([ │ type U = 'open' | 'close' │ const T = { │ │ Type.Literal('open'), │ │ type: 'string', │ │ Type.Literal('close') │ type T = `on${U}` │ pattern: '^on(open|close)$' │ @@ -504,10 +511,9 @@ TypeBox provides several extended types that can be used to produce schematics f │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Constructor([ │ type T = new ( │ const T = { │ -│ Type.String(), │ arg0: string, │ type: 'object', │ -│ Type.Number() │ arg1: number │ instanceOf: 'Constructor', │ -│ ], Type.Boolean()) │ ) => boolean │ parameters: [{ │ -│ │ │ type: 'string' │ +│ Type.String(), │ arg0: string, │ type: 'constructor', │ +│ Type.Number() │ arg0: number │ parameters: [{ │ +│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ │ │ │ type: 'number' │ │ │ │ }], │ @@ -518,10 +524,9 @@ TypeBox provides several extended types that can be used to produce schematics f │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Function([ │ type T = ( │ const T = { │ -| Type.String(), │ arg0: string, │ type : 'object', │ -│ Type.Number() │ arg1: number │ instanceOf: 'Function', │ -│ ], Type.Boolean()) │ ) => boolean │ parameters: [{ │ -│ │ │ type: 'string' │ +| Type.String(), │ arg0: string, │ type: 'function', │ +│ Type.Number() │ arg1: number │ parameters: [{ │ +│ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ │ │ │ type: 'number' │ │ │ │ }], │ @@ -532,64 +537,95 @@ TypeBox provides several extended types that can be used to produce schematics f │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Promise( │ type T = Promise │ const T = { │ -│ Type.String() │ │ type: 'object', │ -│ ) │ │ instanceOf: 'Promise', │ -│ │ │ item: { │ +│ Type.String() │ │ type: 'Promise', │ +│ ) │ │ item: { │ │ │ │ type: 'string' │ │ │ │ } │ │ │ │ } │ │ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Iterator( │ type T = │ const T = { │ +│ Type.String() │ IterableIterator │ type: 'Iterator', │ +│ ) │ │ items: { │ +│ │ │ type: 'string' │ +│ │ │ } │ +│ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │ -│ │ │ type: 'object', │ -│ │ │ instanceOf: 'Uint8Array' │ +│ const T = │ type T = │ const T = { │ +│ Type.AsyncIterator( │ AsyncIterableIterator< │ type: 'AsyncIterator', │ +│ Type.String() │ string │ items: { │ +│ ) │ > │ type: 'string' │ +│ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Date() │ type T = Date │ const T = { │ -│ │ │ type: 'object', │ -│ │ │ instanceOf: 'Date' │ +│ const T = Type.Uint8Array() │ type T = Uint8Array │ const T = { │ +│ │ │ type: 'Uint8Array' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Undefined() │ type T = undefined │ const T = { │ -│ │ │ type: 'null', │ -│ │ │ typeOf: 'Undefined' │ +│ const T = Type.Date() │ type T = Date │ const T = { │ +│ │ │ type: 'Date' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.RegEx(/foo/) │ type T = string │ const T = { │ -│ │ │ type: 'string', │ -│ │ │ pattern: 'foo' │ +│ const T = Type.Undefined() │ type T = undefined │ const T = { │ +│ │ │ type: 'undefined' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Symbol() │ type T = symbol │ const T = { │ -│ │ │ type: 'null', │ -│ │ │ typeOf: 'Symbol' │ +│ │ │ type: 'symbol' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.BigInt() │ type T = bigint │ const T = { │ -│ │ │ type: 'null', │ -│ │ │ typeOf: 'BigInt' │ +│ │ │ type: 'bigint' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Void() │ type T = void │ const T = { │ -│ │ │ type: 'null' │ -│ │ │ typeOf: 'Void' │ +│ │ │ type: 'void' │ │ │ │ } │ │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - + + +### Options + +You can pass JSON Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. + +```typescript +// String must be an email +const T = Type.String({ // const T = { + format: 'email' // type: 'string', +}) // format: 'email' + // } + +// Number must be a multiple of 2 +const T = Type.Number({ // const T = { + multipleOf: 2 // type: 'number', +}) // multipleOf: 2 + // } + +// Array must have at least 5 integer values +const T = Type.Array(Type.Integer(), { // const T = { + minItems: 5 // type: 'array', +}) // minItems: 5, + // items: { + // type: 'integer' + // } + // } +``` + + -### Modifiers +### Properties -TypeBox provides modifiers that allow schema properties to be statically inferred as `readonly` or `optional`. The following table shows the supported modifiers and how they map between TypeScript and JSON Schema. +Object properties can be modified with `readonly` or `optional`. The following table shows how these modifiers map between TypeScript and JSON Schema. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -597,7 +633,7 @@ TypeBox provides modifiers that allow schema properties to be statically inferre │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.Optional( │ name?: string │ type: 'object', │ +│ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │ │ Type.String() │ } │ properties: { │ │ ) │ │ name: { │ │ }) │ │ type: 'string' │ @@ -618,7 +654,7 @@ TypeBox provides modifiers that allow schema properties to be statically inferre │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │ +│ name: Type.Optional( │ name?: string │ type: 'object', │ │ Type.String() │ } │ properties: { │ │ ) │ │ name: { │ │ }) │ │ type: 'string' │ @@ -628,42 +664,11 @@ TypeBox provides modifiers that allow schema properties to be statically inferre │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - - - -### Options - -You can pass JSON Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. - -```typescript -// String must be an email -const T = Type.String({ // const T = { - format: 'email' // type: 'string', -}) // format: 'email' - // } - -// Number must be a multiple of 2 -const T = Type.Number({ // const T = { - multipleOf: 2 // type: 'number', -}) // multipleOf: 2 - // } - -// Array must have at least 5 integer values -const T = Type.Array(Type.Integer(), { // const T = { - minItems: 5 // type: 'array', -}) // minItems: 5, - // items: { - // type: 'integer' - // } - // } - -``` - ### Generic Types -Generic types can be created with generic functions constrained to type `TSchema`. The following creates a generic `Vector` type. +Generic types are created with generic functions. All TypeBox types extend the sub type `TSchema` so it is common to constrain function arguments to this type. The following creates a generic `Vector` type. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' @@ -685,25 +690,9 @@ type NumberVector = Static // type NumberVector = { // y: number, // z: number // } - -const BooleanVector = Vector(Type.Boolean()) // const BooleanVector = { - // type: 'object', - // required: ['x', 'y', 'z'], - // properties: { - // x: { type: 'boolean' }, - // y: { type: 'boolean' }, - // z: { type: 'boolean' } - // } - // } - -type BooleanVector = Static // type BooleanVector = { - // x: boolean, - // y: boolean, - // z: boolean - // } ``` -The following creates a generic `Nullable` type. +Generic types can be used to create aliases for more complex types. The following creates a `Nullable` type. ```typescript const Nullable = (schema: T) => Type.Union([schema, Type.Null()]) @@ -722,24 +711,26 @@ type T = Static // type T = string | null ### Reference Types -Reference types are supported with `Type.Ref`. The target type must specify a valid `$id`. +Reference types are supported with `Ref`. ```typescript const T = Type.String({ $id: 'T' }) // const T = { - // $id: 'T', - // type: 'string' + // $id: 'T', + // type: 'string' // } -const R = Type.Ref(T) // const R = { - // $ref: 'T' +const R = Type.Ref('T') // const R = { + // $ref: 'T' // } + +type R = Static // type R = string ``` ### Recursive Types -Recursive types are supported with `Type.Recursive`. +Recursive types are supported with `Recursive`. Recursive type inference is also supported. ```typescript const Node = Type.Recursive(This => Type.Object({ // const Node = { @@ -776,7 +767,7 @@ function test(node: Node) { ### Conditional Types -TypeBox supports conditional types with `Type.Extends`. This type will perform a structural assignment check for the first two parameters and return a `true` or `false` type from the second two parameters. The types `Type.Exclude` and `Type.Extract` are also supported. +TypeBox supports conditional types with `Extends`. This type performs a structural assignment check against the first two parameters and returns either the `true` or `false` type as given from the second two parameters. The conditional types `Exclude` and `Extract` are also supported. ```typescript // TypeScript @@ -789,17 +780,17 @@ type T2 = Exclude<(1 | 2 | 3), 1> // type T2 = 2 | 3 // TypeBox -const T0 = Type.Extends( // const T0: TLiteral - Type.String(), - Type.Number(), - Type.Literal(true), +const T0 = Type.Extends( // const T0: TLiteral = { + Type.String(), // type: 'boolean', + Type.Number(), // const: false + Type.Literal(true), // } Type.Literal(false) ) -const T1 = Type.Extract( // const T1: TLiteral<1> - Type.Union([ - Type.Literal(1), - Type.Literal(2), +const T1 = Type.Extract( // const T1: TLiteral<1> = { + Type.Union([ // type: 'number', + Type.Literal(1), // const: 1 + Type.Literal(2), // } Type.Literal(3) ]), Type.Literal(1) @@ -808,42 +799,38 @@ const T1 = Type.Extract( // const T1: TLiteral<1> const T2 = Type.Exclude( // const T2: TUnion<[ Type.Union([ // TLiteral<2>, Type.Literal(1), // TLiteral<3> - Type.Literal(2), // ]> - Type.Literal(3) - ]), - Type.Literal(1) -) + Type.Literal(2), // ]> = { + Type.Literal(3) // anyOf: [{ + ]), // type: 'number', + Type.Literal(1) // const: 2 +) // }, { + // type: 'number', + // const: 3 + // }] + // } ``` ### Template Literal Types -TypeBox supports template literal types with `Type.TemplateLiteral`. This type implements an embedded DSL syntax to match the TypeScript template literal syntax. This type can also be composed by passing an array of union and literal types as parameters. The following example shows the DSL syntax. +TypeBox supports template literal types with `TemplateLiteral`. This type provides an embedded DSL syntax that is similar to the TypeScript template literal syntax. These type can also be composed by passing a tuple of exterior union and literal types. The following example shows the DSL syntax. ```typescript // TypeScript -type P = `/post/${string}/user/${number}` // type P = `/post/${string}/user/${number}` - -type T = `option${'A'|'B'}` // type T = 'optionA' | 'optionB' +type T = `option${'A'|'B'|'C'}` // type T = 'optionA' | 'optionB' | 'optionC' type R = Record // type R = { // optionA: string // optionB: string + // optionC: string // } // TypeBox -const P = Type.TemplateLiteral('/post/${string}/user/${number}') - - // const P = { - // type: 'string', - // pattern: '^/post/(.*)/user/(0|[1-9][0-9]*)$' - // } - -const T = Type.TemplateLiteral('option${A|B}') // const T = { - // pattern: '^option(A|B)$', +const T = Type.TemplateLiteral('option${A|B|C}') // const T = { + // pattern: '^option(A|B|C)$', // type: 'string' // } @@ -857,6 +844,9 @@ const R = Type.Record(T, Type.String()) // const R = { // optionB: { // type: 'string' // } + // optionC: { + // type: 'string' + // } // } // } ``` @@ -865,7 +855,7 @@ const R = Type.Record(T, Type.String()) // const R = { ### Indexed Access Types -TypeBox supports indexed access types using `Type.Index`. This type provides a consistent way to access interior property and array element types without having to extract them from the underlying schema representation. Indexed access types are supported for object, array, tuple, union and intersect types. +TypeBox supports indexed access types using `Index`. This type provides a consistent way of accessing interior property and array element types without having to extract them from the underlying schema representation. Indexed access types are supported for object, array, tuple, union and intersect types. ```typescript const T = Type.Object({ // const T = { @@ -896,20 +886,27 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { // } ``` - + + +### Negated Types -### Not Types +TypeBox has support for type negation with `Not`. This type will always infer as `unknown`. -TypeBox provides support for the `not` keyword with `Type.Not`. This type is synonymous with [negated types](https://github.com/microsoft/TypeScript/issues/4196) which are not supported in the TypeScript language. Partial inference of this type can be attained via the intersection of `T & not U` (where all Not types infer as `unknown`). This approach can be used to narrow for broader types in the following context. +```typescript +const T = Type.Not(Type.String()) // const T = { + // not: { type: 'string' } + // } + +type T = Static // type T = unknown + // + // where T could be any type other than string +``` +Type negation can be useful for certain forms of type narrowing. For example, consider a type that represents a `number` but not the numbers `1, 2, 3`. The example below shows an imaginary TypeScript syntax to express such a type followed by the TypeBox representation. ```typescript // TypeScript -type T = Exclude // all numbers except 1, 2, 3 - // - // ideally expressed as: - // - // type T = number & not (1 | 2 | 3) +type T = number & not (1 | 2 | 3) // not actual syntax // TypeBox @@ -928,14 +925,9 @@ const T = Type.Intersect([ // const T = { // ] // } -type T = Static // inferred: - // - // type T = number & not (1 | 2 | 3) - // type T = number & unknown - // type T = number +type T = Static // type T = number ``` - -The Not type can be used with constraints to define schematics for types that would otherwise be difficult to express. +This type can be used with constraints to create schematics that would otherwise be difficult to express. ```typescript const Even = Type.Number({ multipleOf: 2 }) @@ -945,7 +937,7 @@ const Odd = Type.Intersect([Type.Number(), Type.Not(Even)]) ### Rest Types -Rest parameters are supported with `Type.Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatenation as well as for variadic functions. +Rest parameters are supported with `Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatenation as well function parameter assignment. ```typescript // TypeScript @@ -983,7 +975,7 @@ const F = Type.Function(Type.Rest(C), Type.Void()) // const F: TFunction<[ ### Unsafe Types -Use `Type.Unsafe` to create custom schematics with user defined inference rules. +TypeBox supports the creation of user defined schematics with user defined inference rules using the Unsafe type. ```typescript const T = Type.Unsafe({ type: 'number' }) // const T = { @@ -993,7 +985,7 @@ const T = Type.Unsafe({ type: 'number' }) // const T = { type T = Static // type T = string ``` -The `Type.Unsafe` type can be useful to express specific OpenAPI schema representations. +This type can be useful to create various extended schematics, such as those used by OpenAPI. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' @@ -1028,12 +1020,12 @@ type T = Static // type T = 'A' | 'B' | 'C' ### Type Guards -TypeBox provides a `TypeGuard` module that can be used for reflection and asserting values as types. +TypeBox provides a TypeGuard module to assert JavaScript values are valid TypeBox types. ```typescript -import { Type, TypeGuard } from '@sinclair/typebox' +import { Type, Kind, TypeGuard } from '@sinclair/typebox' -const T = Type.String() +const T = { [Kind]: 'String', type: 'string' } if(TypeGuard.TString(T)) { @@ -1045,7 +1037,7 @@ if(TypeGuard.TString(T)) { ### Strict -TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are used for type composition and reflection. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict` function that will omit these properties if necessary. +TypeBox types contain various symbol properties that are used for reflection, composition and compilation. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Strict` function that will omit these properties if necessary. ```typescript const T = Type.Object({ // const T = { @@ -1053,9 +1045,9 @@ const T = Type.Object({ // const T = { }) // type: 'object', // properties: { // name: { - // [Kind]: 'String', // type: 'string', - // [Modifier]: 'Optional' + // [Kind]: 'String', + // [Optional]: 'Optional' // } // } // } @@ -1118,7 +1110,7 @@ const R = Value.Check(T, { x: 1 }) // const R = true ### Convert -Use the Convert function to convert a value into its target type if a reasonable conversion is possible. +Use the Convert function to convert a value into its target type if a reasonable conversion is possible. This function may return an invalid value and should be checked before use. It's return type is `unknown`. ```typescript const T = Type.Object({ x: Type.Number() }) @@ -1270,7 +1262,7 @@ ValuePointer.Set(A, '/z', 1) // const A' = { x: 1, y: 1, ## TypeCheck -TypeBox types target JSON Schema draft 6 so are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for high performance compilation and value assertion. +TypeBox types target JSON Schema draft 7 so are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for high performance compilation and value assertion. The following sections detail using Ajv and TypeBox's compiler infrastructure. @@ -1465,13 +1457,13 @@ TypeSystem.AllowArrayObjects = true TypeSystem.AllowNaN = true ``` - + -## Workbench +## TypeBox Transform -TypeBox offers a web based code generation tool that can be used to convert TypeScript types into TypeBox types as well as a variety of other runtime type representations. +TypeBox offers a small web based code generation tool that can be used to convert TypeScript types into TypeBox types as well as a variety of other runtime type representations. -[Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) +[TypeBox Transform Link Here](https://sinclairzx81.github.io/typebox-transform/) @@ -1508,35 +1500,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 227 ms' │ ' 7 ms' │ ' 32.43 x' │ -│ Literal_Number │ 1000 │ ' 180 ms' │ ' 6 ms' │ ' 30.00 x' │ -│ Literal_Boolean │ 1000 │ ' 152 ms' │ ' 5 ms' │ ' 30.40 x' │ -│ Primitive_Number │ 1000 │ ' 161 ms' │ ' 6 ms' │ ' 26.83 x' │ -│ Primitive_String │ 1000 │ ' 150 ms' │ ' 8 ms' │ ' 18.75 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 202 ms' │ ' 9 ms' │ ' 22.44 x' │ -│ Primitive_Boolean │ 1000 │ ' 133 ms' │ ' 3 ms' │ ' 44.33 x' │ -│ Primitive_Null │ 1000 │ ' 147 ms' │ ' 3 ms' │ ' 49.00 x' │ -│ Object_Unconstrained │ 1000 │ ' 1145 ms' │ ' 31 ms' │ ' 36.94 x' │ -│ Object_Constrained │ 1000 │ ' 1241 ms' │ ' 26 ms' │ ' 47.73 x' │ -│ Object_Vector3 │ 1000 │ ' 407 ms' │ ' 7 ms' │ ' 58.14 x' │ -│ Object_Box3D │ 1000 │ ' 1781 ms' │ ' 27 ms' │ ' 65.96 x' │ -│ Tuple_Primitive │ 1000 │ ' 489 ms' │ ' 13 ms' │ ' 37.62 x' │ -│ Tuple_Object │ 1000 │ ' 1278 ms' │ ' 34 ms' │ ' 37.59 x' │ -│ Composite_Intersect │ 1000 │ ' 613 ms' │ ' 16 ms' │ ' 38.31 x' │ -│ Composite_Union │ 1000 │ ' 543 ms' │ ' 18 ms' │ ' 30.17 x' │ -│ Math_Vector4 │ 1000 │ ' 819 ms' │ ' 13 ms' │ ' 63.00 x' │ -│ Math_Matrix4 │ 1000 │ ' 407 ms' │ ' 7 ms' │ ' 58.14 x' │ -│ Array_Primitive_Number │ 1000 │ ' 372 ms' │ ' 6 ms' │ ' 62.00 x' │ -│ Array_Primitive_String │ 1000 │ ' 329 ms' │ ' 6 ms' │ ' 54.83 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 313 ms' │ ' 3 ms' │ ' 104.33 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1780 ms' │ ' 20 ms' │ ' 89.00 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1494 ms' │ ' 21 ms' │ ' 71.14 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 917 ms' │ ' 10 ms' │ ' 91.70 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1666 ms' │ ' 13 ms' │ ' 128.15 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 791 ms' │ ' 18 ms' │ ' 43.94 x' │ -│ Array_Composite_Union │ 1000 │ ' 833 ms' │ ' 17 ms' │ ' 49.00 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1161 ms' │ ' 15 ms' │ ' 77.40 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 697 ms' │ ' 10 ms' │ ' 69.70 x' │ +│ Literal_String │ 1000 │ ' 232 ms' │ ' 8 ms' │ ' 29.00 x' │ +│ Literal_Number │ 1000 │ ' 179 ms' │ ' 6 ms' │ ' 29.83 x' │ +│ Literal_Boolean │ 1000 │ ' 154 ms' │ ' 3 ms' │ ' 51.33 x' │ +│ Primitive_Number │ 1000 │ ' 160 ms' │ ' 7 ms' │ ' 22.86 x' │ +│ Primitive_String │ 1000 │ ' 149 ms' │ ' 6 ms' │ ' 24.83 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 191 ms' │ ' 9 ms' │ ' 21.22 x' │ +│ Primitive_Boolean │ 1000 │ ' 135 ms' │ ' 4 ms' │ ' 33.75 x' │ +│ Primitive_Null │ 1000 │ ' 144 ms' │ ' 6 ms' │ ' 24.00 x' │ +│ Object_Unconstrained │ 1000 │ ' 1144 ms' │ ' 30 ms' │ ' 38.13 x' │ +│ Object_Constrained │ 1000 │ ' 1228 ms' │ ' 24 ms' │ ' 51.17 x' │ +│ Object_Vector3 │ 1000 │ ' 380 ms' │ ' 9 ms' │ ' 42.22 x' │ +│ Object_Box3D │ 1000 │ ' 1771 ms' │ ' 30 ms' │ ' 59.03 x' │ +│ Tuple_Primitive │ 1000 │ ' 471 ms' │ ' 11 ms' │ ' 42.82 x' │ +│ Tuple_Object │ 1000 │ ' 1272 ms' │ ' 15 ms' │ ' 84.80 x' │ +│ Composite_Intersect │ 1000 │ ' 606 ms' │ ' 17 ms' │ ' 35.65 x' │ +│ Composite_Union │ 1000 │ ' 560 ms' │ ' 22 ms' │ ' 25.45 x' │ +│ Math_Vector4 │ 1000 │ ' 824 ms' │ ' 14 ms' │ ' 58.86 x' │ +│ Math_Matrix4 │ 1000 │ ' 419 ms' │ ' 9 ms' │ ' 46.56 x' │ +│ Array_Primitive_Number │ 1000 │ ' 382 ms' │ ' 6 ms' │ ' 63.67 x' │ +│ Array_Primitive_String │ 1000 │ ' 324 ms' │ ' 6 ms' │ ' 54.00 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 301 ms' │ ' 4 ms' │ ' 75.25 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1734 ms' │ ' 21 ms' │ ' 82.57 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1509 ms' │ ' 20 ms' │ ' 75.45 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 824 ms' │ ' 14 ms' │ ' 58.86 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1619 ms' │ ' 16 ms' │ ' 101.19 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 773 ms' │ ' 16 ms' │ ' 48.31 x' │ +│ Array_Composite_Union │ 1000 │ ' 822 ms' │ ' 17 ms' │ ' 48.35 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1131 ms' │ ' 13 ms' │ ' 87.00 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 661 ms' │ ' 10 ms' │ ' 66.10 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1550,37 +1542,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 25 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 19 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Literal_Boolean │ 1000000 │ ' 18 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_Number │ 1000000 │ ' 26 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_String │ 1000000 │ ' 25 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 160 ms' │ ' 42 ms' │ ' 36 ms' │ ' 1.17 x' │ -│ Primitive_Boolean │ 1000000 │ ' 24 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_Null │ 1000000 │ ' 24 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1111 ms' │ ' 34 ms' │ ' 24 ms' │ ' 1.42 x' │ -│ Object_Constrained │ 1000000 │ ' 1262 ms' │ ' 51 ms' │ ' 37 ms' │ ' 1.38 x' │ -│ Object_Vector3 │ 1000000 │ ' 445 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Object_Box3D │ 1000000 │ ' 2029 ms' │ ' 66 ms' │ ' 48 ms' │ ' 1.38 x' │ -│ Object_Recursive │ 1000000 │ ' 5121 ms' │ ' 464 ms' │ ' 156 ms' │ ' 2.97 x' │ -│ Tuple_Primitive │ 1000000 │ ' 158 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ -│ Tuple_Object │ 1000000 │ ' 761 ms' │ ' 30 ms' │ ' 18 ms' │ ' 1.67 x' │ -│ Composite_Intersect │ 1000000 │ ' 828 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ -│ Composite_Union │ 1000000 │ ' 529 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Math_Vector4 │ 1000000 │ ' 252 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1024 ms' │ ' 38 ms' │ ' 27 ms' │ ' 1.41 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 264 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Array_Primitive_String │ 1000000 │ ' 240 ms' │ ' 20 ms' │ ' 13 ms' │ ' 1.54 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 137 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 6050 ms' │ ' 66 ms' │ ' 55 ms' │ ' 1.20 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5954 ms' │ ' 124 ms' │ ' 116 ms' │ ' 1.07 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 21074 ms' │ ' 1611 ms' │ ' 626 ms' │ ' 2.57 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 683 ms' │ ' 37 ms' │ ' 30 ms' │ ' 1.23 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3366 ms' │ ' 69 ms' │ ' 54 ms' │ ' 1.28 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3285 ms' │ ' 45 ms' │ ' 36 ms' │ ' 1.25 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2229 ms' │ ' 69 ms' │ ' 34 ms' │ ' 2.03 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1192 ms' │ ' 38 ms' │ ' 25 ms' │ ' 1.52 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4916 ms' │ ' 111 ms' │ ' 88 ms' │ ' 1.26 x' │ +│ Literal_String │ 1000000 │ ' 18 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 15 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Literal_Boolean │ 1000000 │ ' 13 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ +│ Primitive_Number │ 1000000 │ ' 21 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ +│ Primitive_String │ 1000000 │ ' 19 ms' │ ' 16 ms' │ ' 10 ms' │ ' 1.60 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 150 ms' │ ' 41 ms' │ ' 35 ms' │ ' 1.17 x' │ +│ Primitive_Boolean │ 1000000 │ ' 17 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_Null │ 1000000 │ ' 18 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1001 ms' │ ' 31 ms' │ ' 24 ms' │ ' 1.29 x' │ +│ Object_Constrained │ 1000000 │ ' 1288 ms' │ ' 50 ms' │ ' 36 ms' │ ' 1.39 x' │ +│ Object_Vector3 │ 1000000 │ ' 439 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Object_Box3D │ 1000000 │ ' 2109 ms' │ ' 52 ms' │ ' 45 ms' │ ' 1.16 x' │ +│ Object_Recursive │ 1000000 │ ' 5337 ms' │ ' 356 ms' │ ' 162 ms' │ ' 2.20 x' │ +│ Tuple_Primitive │ 1000000 │ ' 164 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Tuple_Object │ 1000000 │ ' 744 ms' │ ' 29 ms' │ ' 18 ms' │ ' 1.61 x' │ +│ Composite_Intersect │ 1000000 │ ' 764 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Composite_Union │ 1000000 │ ' 516 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Math_Vector4 │ 1000000 │ ' 262 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1089 ms' │ ' 37 ms' │ ' 27 ms' │ ' 1.37 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 276 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ +│ Array_Primitive_String │ 1000000 │ ' 228 ms' │ ' 21 ms' │ ' 14 ms' │ ' 1.50 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 159 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 5695 ms' │ ' 77 ms' │ ' 69 ms' │ ' 1.12 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5701 ms' │ ' 127 ms' │ ' 110 ms' │ ' 1.15 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 21267 ms' │ ' 1664 ms' │ ' 573 ms' │ ' 2.90 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 702 ms' │ ' 40 ms' │ ' 32 ms' │ ' 1.25 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3141 ms' │ ' 68 ms' │ ' 51 ms' │ ' 1.33 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3145 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2134 ms' │ ' 68 ms' │ ' 31 ms' │ ' 2.19 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1197 ms' │ ' 37 ms' │ ' 25 ms' │ ' 1.48 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 5323 ms' │ ' 111 ms' │ ' 96 ms' │ ' 1.16 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1594,11 +1586,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '130.3 kb' │ ' 58.2 kb' │ '2.24 x' │ -│ typebox/errors │ '113.3 kb' │ ' 49.8 kb' │ '2.27 x' │ -│ typebox/system │ ' 78.8 kb' │ ' 32.2 kb' │ '2.45 x' │ -│ typebox/value │ '180.0 kb' │ ' 77.7 kb' │ '2.32 x' │ -│ typebox │ ' 77.7 kb' │ ' 31.7 kb' │ '2.45 x' │ +│ typebox/compiler │ '129.4 kb' │ ' 58.6 kb' │ '2.21 x' │ +│ typebox/errors │ '111.6 kb' │ ' 50.1 kb' │ '2.23 x' │ +│ typebox/system │ ' 76.5 kb' │ ' 31.7 kb' │ '2.41 x' │ +│ typebox/value │ '180.7 kb' │ ' 79.3 kb' │ '2.28 x' │ +│ typebox │ ' 75.4 kb' │ ' 31.3 kb' │ '2.41 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 49e8ddb95..9509e0d29 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -26,10 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '../typebox' -import { ValueErrors, ValueErrorIterator } from '../errors/index' import { TypeSystem } from '../system/index' -import { ValueHash } from '../value/hash' +import * as Types from '../typebox' +import * as ValueErrors from '../errors/index' +import * as ValueHash from '../value/hash' +import * as ValueGuard from '../value/guard' // ------------------------------------------------------------------- // CheckFunction @@ -45,7 +46,7 @@ export class TypeCheck { return this.code } /** Returns an iterator for each error in this value. */ - public Errors(value: unknown): ValueErrorIterator { + public Errors(value: unknown): ValueErrors.ValueErrorIterator { return ValueErrors.Errors(this.schema, this.references, value) } /** Returns true if the value matches the compiled type. */ @@ -112,7 +113,7 @@ namespace Identifier { } } // ------------------------------------------------------------------- -// TypeCompiler +// Errors // ------------------------------------------------------------------- export class TypeCompilerUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { @@ -121,7 +122,7 @@ export class TypeCompilerUnknownTypeError extends Error { } export class TypeCompilerDereferenceError extends Error { constructor(public readonly schema: Types.TRef) { - super(`TypeCompiler: Unable to dereference schema with $id '${schema.$ref}'`) + super(`TypeCompiler: Unable to dereference type with $id '${schema.$ref}'`) } } export class TypeCompilerTypeGuardError extends Error { @@ -129,26 +130,17 @@ export class TypeCompilerTypeGuardError extends Error { super('TypeCompiler: Preflight validation check failed to guard for the given schema') } } - +// ------------------------------------------------------------------- +// TypeCompiler +// ------------------------------------------------------------------- +export type TypeCompilerLanguageOption = 'typescript' | 'javascript' export interface TypeCompilerOptions { - language?: 'typescript' | 'javascript' + language?: TypeCompilerLanguageOption } /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { - // ------------------------------------------------------------------- - // Guards - // ------------------------------------------------------------------- - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - function IsNumber(value: unknown): value is number { - return typeof value === 'number' && globalThis.Number.isFinite(value) - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } // ---------------------------------------------------------------------- - // SchemaGuards + // Guards // ---------------------------------------------------------------------- function IsAnyOrUnknown(schema: Types.TSchema) { return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' @@ -176,94 +168,113 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- // Types // ------------------------------------------------------------------- - function* Any(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { + function* TAny(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { yield 'true' } - function* Array(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { + function* TArray(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` - if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` - if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` - if (schema.uniqueItems === true) yield `((function() { const set = new Set(); for(const element of ${value}) { const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())` - const expression = CreateExpression(schema.items, references, 'value') - const parameter = CreateParameter('value', 'any') - yield `${value}.every((${parameter}) => ${expression})` + const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')] + if (ValueGuard.IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` + if (ValueGuard.IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` + const elementExpression = CreateExpression(schema.items, references, 'value') + yield `${value}.every((${parameter}) => ${elementExpression})` + if (Types.TypeGuard.TSchema(schema.contains) || ValueGuard.IsNumber(schema.minContains) || ValueGuard.IsNumber(schema.maxContains)) { + const containsSchema = Types.TypeGuard.TSchema(schema.contains) ? schema.contains : Types.Type.Never() + const checkExpression = CreateExpression(containsSchema, references, 'value') + const checkMinContains = ValueGuard.IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] + const checkMaxContains = ValueGuard.IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] + const checkCount = `const count = ${value}.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` + const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ') + yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})` + } + if (schema.uniqueItems === true) { + const check = `const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true` + const block = `const set = new Set(); for(const element of value) { ${check} }` + yield `((${parameter}) => { ${block} )(${value})` + } + } + function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})` } - function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { + function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` - if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` - if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` - if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` - if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` - if (IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` + if (ValueGuard.IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` + if (ValueGuard.IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` + if (ValueGuard.IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` + if (ValueGuard.IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` + if (ValueGuard.IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` } - function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { + function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'boolean')` } - function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { + function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { yield* Visit(schema.returns, references, `${value}.prototype`) } - function* Date(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { + function* TDate(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())` - if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` - if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` - if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` - if (IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` + if (ValueGuard.IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` + if (ValueGuard.IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` + if (ValueGuard.IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` + if (ValueGuard.IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` } - function* Function(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { + function* TFunction(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } - function* Integer(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { + function* TInteger(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` - if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` - if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` - if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` - if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` - if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + if (ValueGuard.IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` + if (ValueGuard.IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (ValueGuard.IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (ValueGuard.IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (ValueGuard.IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` } - function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { + function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ') if (schema.unevaluatedProperties === false) { - const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))` yield `(${check1} && ${check2})` } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const keyCheck = PushLocal(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})` yield `(${check1} && ${check2})` } else { yield `(${check1})` } } - function* Literal(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* TIterator(schema: Types.TIterator, references: Types.TSchema[], value: string): IterableIterator { + yield `(typeof value === 'object' && Symbol.iterator in ${value})` + } + function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { yield `(${value} === ${schema.const})` } else { yield `(${value} === '${schema.const}')` } } - function* Never(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { + function* TNever(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { yield `false` } - function* Not(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { + function* TNot(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { const expression = CreateExpression(schema.not, references, value) yield `(!${expression})` } - function* Null(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { + function* TNull(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { yield `(${value} === null)` } - function* Number(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { + function* TNumber(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { yield IsNumberCheck(value) - if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` - if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` - if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` - if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` - if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + if (ValueGuard.IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` + if (ValueGuard.IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (ValueGuard.IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (ValueGuard.IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (ValueGuard.IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` } - function* Object(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { + function* TObject(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { yield IsObjectCheck(value) - if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` - if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` - const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) + if (ValueGuard.IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (ValueGuard.IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` + const knownKeys = Object.getOwnPropertyNames(schema.properties) for (const knownKey of knownKeys) { const memberExpression = MemberExpression.Encode(value, knownKey) const property = schema.properties[knownKey] @@ -289,55 +300,54 @@ export namespace TypeCompiler { yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } } - function* Promise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { + function* TPromise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } - function* Record(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { + function* TRecord(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { yield IsRecordCheck(value) - if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` - if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` - const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const local = PushLocal(`new RegExp(/${patternKey}/)`) + if (ValueGuard.IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (ValueGuard.IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` + const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] + const variable = CreateVariable(`new RegExp(/${patternKey}/)`) const check1 = CreateExpression(patternSchema, references, 'value') const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' - const expression = `(${local}.test(key) ? ${check1} : ${check2})` + const expression = `(${variable}.test(key) ? ${check1} : ${check2})` yield `(Object.entries(${value}).every(([key, value]) => ${expression}))` } - function* Ref(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { + function* TRef(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { const index = references.findIndex((foreign) => foreign.$id === schema.$ref) if (index === -1) throw new TypeCompilerDereferenceError(schema) const target = references[index] - // Reference: If we have seen this reference before we can just yield and - // return the function call. If this isn't the case we defer to visit to - // generate and set the function for subsequent passes. + // Reference: If we have seen this reference before we can just yield and return the function call. + // If this isn't the case we defer to visit to generate and set the function for subsequent passes. if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } - function* String(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { + function* TString(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` - if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` - if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` + if (ValueGuard.IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` + if (ValueGuard.IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` if (schema.pattern !== undefined) { - const local = PushLocal(`${new RegExp(schema.pattern)};`) - yield `${local}.test(${value})` + const variable = CreateVariable(`${new RegExp(schema.pattern)};`) + yield `${variable}.test(${value})` } if (schema.format !== undefined) { yield `format('${schema.format}', ${value})` } } - function* Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { + function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'symbol')` } - function* TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` - const local = PushLocal(`${new RegExp(schema.pattern)};`) - yield `${local}.test(${value})` + const variable = CreateVariable(`${new RegExp(schema.pattern)};`) + yield `${variable}.test(${value})` } - function* This(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { + function* TThis(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { const func = CreateFunctionName(schema.$ref) yield `${func}(${value})` } - function* Tuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { + function* TTuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` if (schema.items === undefined) return yield `${value}.length === 0` yield `(${value}.length === ${schema.maxItems})` @@ -346,108 +356,110 @@ export namespace TypeCompiler { yield `${expression}` } } - function* Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { + function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { yield `${value} === undefined` } - function* Union(schema: Types.TUnion, references: Types.TSchema[], value: string): IterableIterator { + function* TUnion(schema: Types.TUnion, references: Types.TSchema[], value: string): IterableIterator { const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) yield `(${expressions.join(' || ')})` } - function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { + function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { yield `${value} instanceof Uint8Array` - if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` - if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` + if (ValueGuard.IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` + if (ValueGuard.IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } - function* Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { + function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { yield 'true' } - function* Void(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { + function* TVoid(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { yield IsVoidCheck(value) } - function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { - const schema_key = `schema_key_${state.customs.size}` - state.customs.set(schema_key, schema) - yield `custom('${schema[Types.Kind]}', '${schema_key}', ${value})` + function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { + yield `kind('${schema[Types.Kind]}', ${value})` } - function* Visit(schema: T, references: Types.TSchema[], value: string, root = false): IterableIterator { - const references_ = IsString(schema.$id) ? [...references, schema] : references + function* Visit(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator { + const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - // Rule: Types with identifiers are hoisted into their own functions. - // The following will generate a function for the schema and yield the - // call to that function. This call is only made if NOT the root type - // which allows the generated function to yield its expression. The - // root argument is only true when making calls via CreateFunction(). - // Note there is potential to omit the root argument and conditional - // by refactoring the logic below. Consider for review. - if (IsString(schema.$id)) { - const name = CreateFunctionName(schema.$id) - if (!state.functions.has(schema.$id)) { - state.functions.add(schema.$id) - const body = CreateFunction(name, schema, references, 'value') - PushFunction(body) + // ---------------------------------------------------------------------------------- + // Hoisting + // ---------------------------------------------------------------------------------- + if (useHoisting && ValueGuard.IsString(schema.$id)) { + const functionName = CreateFunctionName(schema.$id) + if (state.functions.has(functionName)) { + return yield `${functionName}(${value})` + } else { + const functionCode = CreateFunction(functionName, schema, references, 'value', false) + state.functions.set(functionName, functionCode) + return yield `${functionName}(${value})` } - if (!root) return yield `${name}(${value})` } + // ---------------------------------------------------------------------------------- + // Types + // ---------------------------------------------------------------------------------- switch (schema_[Types.Kind]) { case 'Any': - return yield* Any(schema_, references_, value) + return yield* TAny(schema_, references_, value) case 'Array': - return yield* Array(schema_, references_, value) + return yield* TArray(schema_, references_, value) + case 'AsyncIterator': + return yield* TAsyncIterator(schema_, references_, value) case 'BigInt': - return yield* BigInt(schema_, references_, value) + return yield* TBigInt(schema_, references_, value) case 'Boolean': - return yield* Boolean(schema_, references_, value) + return yield* TBoolean(schema_, references_, value) case 'Constructor': - return yield* Constructor(schema_, references_, value) + return yield* TConstructor(schema_, references_, value) case 'Date': - return yield* Date(schema_, references_, value) + return yield* TDate(schema_, references_, value) case 'Function': - return yield* Function(schema_, references_, value) + return yield* TFunction(schema_, references_, value) case 'Integer': - return yield* Integer(schema_, references_, value) + return yield* TInteger(schema_, references_, value) case 'Intersect': - return yield* Intersect(schema_, references_, value) + return yield* TIntersect(schema_, references_, value) + case 'Iterator': + return yield* TIterator(schema_, references_, value) case 'Literal': - return yield* Literal(schema_, references_, value) + return yield* TLiteral(schema_, references_, value) case 'Never': - return yield* Never(schema_, references_, value) + return yield* TNever(schema_, references_, value) case 'Not': - return yield* Not(schema_, references_, value) + return yield* TNot(schema_, references_, value) case 'Null': - return yield* Null(schema_, references_, value) + return yield* TNull(schema_, references_, value) case 'Number': - return yield* Number(schema_, references_, value) + return yield* TNumber(schema_, references_, value) case 'Object': - return yield* Object(schema_, references_, value) + return yield* TObject(schema_, references_, value) case 'Promise': - return yield* Promise(schema_, references_, value) + return yield* TPromise(schema_, references_, value) case 'Record': - return yield* Record(schema_, references_, value) + return yield* TRecord(schema_, references_, value) case 'Ref': - return yield* Ref(schema_, references_, value) + return yield* TRef(schema_, references_, value) case 'String': - return yield* String(schema_, references_, value) + return yield* TString(schema_, references_, value) case 'Symbol': - return yield* Symbol(schema_, references_, value) + return yield* TSymbol(schema_, references_, value) case 'TemplateLiteral': - return yield* TemplateLiteral(schema_, references_, value) + return yield* TTemplateLiteral(schema_, references_, value) case 'This': - return yield* This(schema_, references_, value) + return yield* TThis(schema_, references_, value) case 'Tuple': - return yield* Tuple(schema_, references_, value) + return yield* TTuple(schema_, references_, value) case 'Undefined': - return yield* Undefined(schema_, references_, value) + return yield* TUndefined(schema_, references_, value) case 'Union': - return yield* Union(schema_, references_, value) + return yield* TUnion(schema_, references_, value) case 'Uint8Array': - return yield* Uint8Array(schema_, references_, value) + return yield* TUint8Array(schema_, references_, value) case 'Unknown': - return yield* Unknown(schema_, references_, value) + return yield* TUnknown(schema_, references_, value) case 'Void': - return yield* Void(schema_, references_, value) + return yield* TVoid(schema_, references_, value) default: if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) - return yield* UserDefined(schema_, references_, value) + return yield* TKind(schema_, references_, value) } } // ------------------------------------------------------------------- @@ -455,16 +467,30 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- // prettier-ignore const state = { - language: 'javascript' as TypeCompilerOptions['language'], // target language - variables: new Set(), // local variables - functions: new Set(), // local functions - customs: new Map(), // custom type data + language: 'javascript', // target language + functions: new Map(), // local functions + variables: new Map(), // local variables + } + // ------------------------------------------------------------------- + // Compiler Factory + // ------------------------------------------------------------------- + function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})` } function CreateFunctionName($id: string) { return `check_${Identifier.Encode($id)}` } - function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string): string { - return `(${[...Visit(schema, references, value)].join(' && ')})` + function CreateVariable(expression: string) { + const variableName = `local_${state.variables.size}` + state.variables.set(variableName, `const ${variableName} = ${expression}`) + return variableName + } + function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + const [newline, pad] = ['\n', (length: number) => ''.padStart(length, ' ')] + const parameter = CreateParameter('value', 'any') + const returns = CreateReturns('boolean') + const expression = [...Visit(schema, references, value, useHoisting)].map((expression) => `${pad(4)}${expression}`).join(` &&${newline}`) + return `function ${name}(${parameter})${returns} {${newline}${pad(2)}return (${newline}${expression}${newline}${pad(2)})\n}` } function CreateParameter(name: string, type: string) { const annotation = state.language === 'typescript' ? `: ${type}` : '' @@ -473,68 +499,62 @@ export namespace TypeCompiler { function CreateReturns(type: string) { return state.language === 'typescript' ? `: ${type}` : '' } - function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string): string { - const expression = [...Visit(schema, references, value, true)].map((condition) => ` ${condition}`).join(' &&\n') - const parameter = CreateParameter('value', 'any') - const returns = CreateReturns('boolean') - return `function ${name}(${parameter})${returns} {\n return (\n${expression}\n )\n}` - } - function PushFunction(functionBody: string) { - state.variables.add(functionBody) - } - function PushLocal(expression: string) { - const local = `local_${state.variables.size}` - state.variables.add(`const ${local} = ${expression}`) - return local - } - function GetLocals() { - return [...state.variables.values()] - } // ------------------------------------------------------------------- // Compile // ------------------------------------------------------------------- - function Build(schema: T, references: Types.TSchema[]): string { - const check = CreateFunction('check', schema, references, 'value') // interior visit - const locals = GetLocals() + function Build(schema: T, references: Types.TSchema[], options: TypeCompilerOptions): string { + const functionCode = CreateFunction('check', schema, references, 'value') // will populate functions and variables const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') + const functions = [...state.functions.values()] + const variables = [...state.variables.values()] // prettier-ignore - return IsString(schema.$id) // ensure top level schemas with $id's are hoisted - ? `${locals.join('\n')}\nreturn function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}` - : `${locals.join('\n')}\nreturn ${check}` + const checkFunction = ValueGuard.IsString(schema.$id) // ensure top level schemas with $id's are hoisted + ? `return function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}` + : `return ${functionCode}` + return [...variables, ...functions, checkFunction].join('\n') } /** Returns the generated assertion code used to validate this type. */ - export function Code(schema: T, references: Types.TSchema[] = [], options: TypeCompilerOptions = { language: 'javascript' }) { + export function Code(schema: T, references: Types.TSchema[], options?: TypeCompilerOptions): string + /** Returns the generated assertion code used to validate this type. */ + export function Code(schema: T, options?: TypeCompilerOptions): string + /** Returns the generated assertion code used to validate this type. */ + export function Code(...args: any[]) { + const defaults = { language: 'javascript' } + // prettier-ignore + const [schema, references, options] = ( + args.length === 2 && ValueGuard.IsArray(args[1]) ? [args[0], args[1], defaults] : + args.length === 2 && !ValueGuard.IsArray(args[1]) ? [args[0], [], args[1]] : + args.length === 3 ? [args[0], args[1], args[2]] : + args.length === 1 ? [args[0], [], defaults] : + [null, [], defaults] + ) // compiler-reset state.language = options.language state.variables.clear() state.functions.clear() - state.customs.clear() if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) - return Build(schema, references) + return Build(schema, references, options) } /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */ export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { - const code = Code(schema, references, { language: 'javascript' }) - const customs = new Map(state.customs) - const compiledFunction = globalThis.Function('custom', 'format', 'hash', code) - const checkFunction = compiledFunction( - (kind: string, schema_key: string, value: unknown) => { - if (!Types.TypeRegistry.Has(kind) || !customs.has(schema_key)) return false - const schema = customs.get(schema_key)! - const func = Types.TypeRegistry.Get(kind)! - return func(schema, value) - }, - (format: string, value: string) => { - if (!Types.FormatRegistry.Has(format)) return false - const func = Types.FormatRegistry.Get(format)! - return func(value) - }, - (value: unknown) => { - return ValueHash.Create(value) - }, - ) - return new TypeCheck(schema, references, checkFunction, code) + const generatedCode = Code(schema, references, { language: 'javascript' }) + const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) + function typeRegistryFunction(kind: string, value: unknown) { + if (!Types.TypeRegistry.Has(kind)) return false + const checkFunc = Types.TypeRegistry.Get(kind)! + return checkFunc(schema, value) + } + function formatRegistryFunction(format: string, value: string) { + if (!Types.FormatRegistry.Has(format)) return false + const checkFunc = Types.FormatRegistry.Get(format)! + return checkFunc(value) + } + function valueHashFunction(value: unknown) { + return ValueHash.Hash(value) + } + const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, valueHashFunction) + return new TypeCheck(schema, references, checkFunction, generatedCode) } } diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 73e76bec7..f7ee21a19 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -25,18 +25,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '../typebox' + import { TypeSystem } from '../system/index' -import { ValueHash } from '../value/hash' +import * as Types from '../typebox' +import * as ValueHash from '../value/hash' +import * as ValueGuard from '../value/guard' -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- // ValueErrorType -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- export enum ValueErrorType { Array, ArrayMinItems, ArrayMaxItems, + ArrayContains, + ArrayMinContains, + ArrayMaxContains, ArrayUniqueItems, + AsyncIterator, BigInt, BigIntMultipleOf, BigIntExclusiveMinimum, @@ -58,6 +64,7 @@ export enum ValueErrorType { IntegerMaximum, Intersect, IntersectUnevaluatedProperties, + Iterator, Literal, Never, Not, @@ -91,11 +98,11 @@ export enum ValueErrorType { Uint8ArrayMinByteLength, Uint8ArrayMaxByteLength, Void, - Custom, + Kind, } -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- // ValueError -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- export interface ValueError { type: ValueErrorType schema: Types.TSchema @@ -103,9 +110,9 @@ export interface ValueError { value: unknown message: string } -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- // ValueErrorIterator -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- export class ValueErrorIterator { constructor(private readonly iterator: IterableIterator) {} public [Symbol.iterator]() { @@ -117,9 +124,9 @@ export class ValueErrorIterator { return next.done ? undefined : next.value } } -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- // ValueErrors -// ------------------------------------------------------------------- +// -------------------------------------------------------------------------- export class ValueErrorsUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueErrors: Unknown type') @@ -127,468 +134,490 @@ export class ValueErrorsUnknownTypeError extends Error { } export class ValueErrorsDereferenceError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueErrors: Unable to dereference schema with $id '${schema.$ref}'`) + super(`ValueErrors: Unable to dereference type with $id '${schema.$ref}'`) } } -/** Provides functionality to generate a sequence of errors against a TypeBox type. */ -export namespace ValueErrors { - // ---------------------------------------------------------------------- - // Guards - // ---------------------------------------------------------------------- - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' +// -------------------------------------------------------------------------- +// Guards +// -------------------------------------------------------------------------- +function IsDefined(value: unknown): value is T { + return value !== undefined +} +// -------------------------------------------------------------------------- +// Policies +// -------------------------------------------------------------------------- +function IsExactOptionalProperty(value: Record, key: string) { + return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined +} +function IsObject(value: unknown): value is Record { + const isObject = ValueGuard.IsObject(value) + return TypeSystem.AllowArrayObjects ? isObject : isObject && !ValueGuard.IsArray(value) +} +function IsRecordObject(value: unknown): value is Record { + return IsObject(value) && !(value instanceof Date) && !(value instanceof Uint8Array) +} +function IsNumber(value: unknown): value is number { + const isNumber = ValueGuard.IsNumber(value) + return TypeSystem.AllowNaN ? isNumber : isNumber && Number.isFinite(value) +} +function IsVoid(value: unknown): value is void { + const isUndefined = ValueGuard.IsUndefined(value) + return TypeSystem.AllowVoidNull ? isUndefined || value === null : isUndefined +} +// -------------------------------------------------------------------------- +// Types +// -------------------------------------------------------------------------- +function* TAny(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} +function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsArray(value)) { + return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } } - function IsInteger(value: unknown): value is number { - return globalThis.Number.isInteger(value) + if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { + yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } } - function IsString(value: unknown): value is string { - return typeof value === 'string' + if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { + yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } } - function IsDefined(value: unknown): value is T { - return value !== undefined + + for (let i = 0; i < value.length; i++) { + yield* Visit(schema.items, references, `${path}/${i}`, value[i]) } - // ---------------------------------------------------------------------- - // Policies - // ---------------------------------------------------------------------- - function IsExactOptionalProperty(value: Record, key: string) { - return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + // prettier-ignore + if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = ValueHash.Hash(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { + yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } } - function IsObject(value: unknown): value is Record { - const result = typeof value === 'object' && value !== null - return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) + // contains + if (!(IsDefined(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains))) { + return } - function IsRecordObject(value: unknown): value is Record { - return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) + const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() + const containsCount = value.reduce((acc: number, value, index) => (Visit(containsSchema, references, `${path}${index}`, value).next().done === true ? acc + 1 : acc), 0) + if (containsCount === 0) { + yield { type: ValueErrorType.ArrayContains, schema, path, value, message: `Expected array to contain at least one matching type` } } - function IsNumber(value: unknown): value is number { - const result = typeof value === 'number' - return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) + if (ValueGuard.IsNumber(schema.minContains) && containsCount < schema.minContains) { + yield { type: ValueErrorType.ArrayMinContains, schema, path, value, message: `Expected array to contain at least ${schema.minContains} matching types` } } - function IsVoid(value: unknown): value is void { - const result = value === undefined - return TypeSystem.AllowVoidNull ? result || value === null : result + if (ValueGuard.IsNumber(schema.maxContains) && containsCount > schema.maxContains) { + yield { type: ValueErrorType.ArrayMaxContains, schema, path, value, message: `Expected array to contain no more than ${schema.maxContains} matching types` } } - - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - function* Any(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} - function* Array(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!globalThis.Array.isArray(value)) { - return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } - } - if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { - yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } - } - if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { - yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } - } - // prettier-ignore - if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = ValueHash.Create(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { - yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } - } - for (let i = 0; i < value.length; i++) { - yield* Visit(schema.items, references, `${path}/${i}`, value[i]) - } +} +function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsAsyncIterator(value)) { + yield { type: ValueErrorType.AsyncIterator, schema, path, value, message: `Expected value to be an async iterator` } } - function* BigInt(schema: Types.TBigInt, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsBigInt(value)) { - return yield { type: ValueErrorType.BigInt, schema, path, value, message: `Expected bigint` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === globalThis.BigInt(0))) { - yield { type: ValueErrorType.BigIntMultipleOf, schema, path, value, message: `Expected bigint to be a multiple of ${schema.multipleOf}` } - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.BigIntExclusiveMinimum, schema, path, value, message: `Expected bigint to be greater than ${schema.exclusiveMinimum}` } - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.BigIntExclusiveMaximum, schema, path, value, message: `Expected bigint to be less than ${schema.exclusiveMaximum}` } - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.BigIntMinimum, schema, path, value, message: `Expected bigint to be greater or equal to ${schema.minimum}` } - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.BigIntMaximum, schema, path, value, message: `Expected bigint to be less or equal to ${schema.maximum}` } - } +} +function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsBigInt(value)) { + return yield { type: ValueErrorType.BigInt, schema, path, value, message: `Expected bigint` } } - function* Boolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'boolean')) { - return yield { type: ValueErrorType.Boolean, schema, path, value, message: `Expected boolean` } - } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { + yield { type: ValueErrorType.BigIntMultipleOf, schema, path, value, message: `Expected bigint to be a multiple of ${schema.multipleOf}` } } - function* Constructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { - yield* Visit(schema.returns, references, path, value.prototype) + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.BigIntExclusiveMinimum, schema, path, value, message: `Expected bigint to be greater than ${schema.exclusiveMinimum}` } } - function* Date(schema: Types.TDate, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value instanceof globalThis.Date)) { - return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } - } - if (!globalThis.isFinite(value.getTime())) { - return yield { type: ValueErrorType.Date, schema, path, value, message: `Invalid Date` } - } - if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } - } - if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } - } - if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { - yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } - } - if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { - yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } - } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.BigIntExclusiveMaximum, schema, path, value, message: `Expected bigint to be less than ${schema.exclusiveMaximum}` } } - function* Function(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'function')) { - return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } - } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + yield { type: ValueErrorType.BigIntMinimum, schema, path, value, message: `Expected bigint to be greater or equal to ${schema.minimum}` } } - function* Integer(schema: Types.TInteger, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsInteger(value)) { - return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } - } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield { type: ValueErrorType.BigIntMaximum, schema, path, value, message: `Expected bigint to be less or equal to ${schema.maximum}` } } - function* Intersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { - for (const inner of schema.allOf) { - const next = Visit(inner, references, path, value).next() - if (!next.done) { - yield next.value - yield { type: ValueErrorType.Intersect, schema, path, value, message: `Expected all sub schemas to be valid` } - return - } +} +function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsBoolean(value)) { + return yield { type: ValueErrorType.Boolean, schema, path, value, message: `Expected boolean` } + } +} +function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { + yield* Visit(schema.returns, references, path, value.prototype) +} +function* TDate(schema: Types.TDate, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsDate(value)) { + return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } + } + if (!isFinite(value.getTime())) { + return yield { type: ValueErrorType.Date, schema, path, value, message: `Invalid Date` } + } + if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } + } + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } + } + if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { + yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } + } + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } + } +} +function* TFunction(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsFunction(value)) { + return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } + } +} +function* TInteger(schema: Types.TInteger, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsInteger(value)) { + return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } + } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } + } + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } + } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } + } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } + } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } + } +} +function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { + for (const inner of schema.allOf) { + const next = Visit(inner, references, path, value).next() + if (!next.done) { + yield next.value + yield { type: ValueErrorType.Intersect, schema, path, value, message: `Expected all sub schemas to be valid` } + return } - if (schema.unevaluatedProperties === false) { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) - for (const valueKey of globalThis.Object.getOwnPropertyNames(value)) { - if (!keyCheck.test(valueKey)) { - yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Unexpected property` } - } + } + if (schema.unevaluatedProperties === false) { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + for (const valueKey of Object.getOwnPropertyNames(value)) { + if (!keyCheck.test(valueKey)) { + yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Unexpected property` } } } - if (typeof schema.unevaluatedProperties === 'object') { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) - for (const valueKey of globalThis.Object.getOwnPropertyNames(value)) { - if (!keyCheck.test(valueKey)) { - const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() - if (!next.done) { - yield next.value - yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Invalid additional property` } - return - } + } + if (typeof schema.unevaluatedProperties === 'object') { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + for (const valueKey of Object.getOwnPropertyNames(value)) { + if (!keyCheck.test(valueKey)) { + const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() + if (!next.done) { + yield next.value + yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Invalid additional property` } + return } } } } - function* Literal(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === schema.const)) { - const error = typeof schema.const === 'string' ? `'${schema.const}'` : schema.const - return yield { type: ValueErrorType.Literal, schema, path, value, message: `Expected ${error}` } - } +} +function* TIterator(schema: Types.TIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(IsObject(value) && Symbol.iterator in value)) { + yield { type: ValueErrorType.Iterator, schema, path, value, message: `Expected value to be an iterator` } } - function* Never(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { - yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } +} +function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === schema.const)) { + const error = typeof schema.const === 'string' ? `'${schema.const}'` : schema.const + return yield { type: ValueErrorType.Literal, schema, path, value, message: `Expected ${error}` } } - function* Not(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (Visit(schema.not, references, path, value).next().done === true) { - yield { type: ValueErrorType.Not, schema, path, value, message: `Value should not validate` } - } +} +function* TNever(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { + yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } +} +function* TNot(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (Visit(schema.not, references, path, value).next().done === true) { + yield { type: ValueErrorType.Not, schema, path, value, message: `Value should not validate` } } - function* Null(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === null)) { - return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } - } +} +function* TNull(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsNull(value)) { + return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } } - function* Number(schema: Types.TNumber, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsNumber(value)) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.NumberMinimum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } - } +} +function* TNumber(schema: Types.TNumber, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsNumber(value)) { + return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } } - function* Object(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsObject(value)) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } - if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } - } - if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } - } - const requiredKeys = globalThis.Array.isArray(schema.required) ? schema.required : ([] as string[]) - const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) - const unknownKeys = globalThis.Object.getOwnPropertyNames(value) - for (const knownKey of knownKeys) { - const property = schema.properties[knownKey] - if (schema.required && schema.required.includes(knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) - if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { - yield { type: ValueErrorType.ObjectRequiredProperties, schema: property, path: `${path}/${knownKey}`, value: undefined, message: `Expected required property` } - } - } else { - if (IsExactOptionalProperty(value, knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) - } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } + } + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } + } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } + } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + yield { type: ValueErrorType.NumberMinimum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } + } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } + } +} +function* TObject(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsObject(value)) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } + } + if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + } + if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } + } + const requiredKeys = Array.isArray(schema.required) ? schema.required : ([] as string[]) + const knownKeys = Object.getOwnPropertyNames(schema.properties) + const unknownKeys = Object.getOwnPropertyNames(value) + for (const knownKey of knownKeys) { + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { + yield { type: ValueErrorType.ObjectRequiredProperties, schema: property, path: `${path}/${knownKey}`, value: undefined, message: `Expected required property` } } - } - for (const requiredKey of requiredKeys) { - if (unknownKeys.includes(requiredKey)) continue - yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } - } - if (schema.additionalProperties === false) { - for (const valueKey of unknownKeys) { - if (!knownKeys.includes(valueKey)) { - yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${valueKey}`, value: value[valueKey], message: `Unexpected property` } - } + } else { + if (IsExactOptionalProperty(value, knownKey)) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) } } - if (typeof schema.additionalProperties === 'object') { - for (const valueKey of unknownKeys) { - if (knownKeys.includes(valueKey)) continue - yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey]) + } + for (const requiredKey of requiredKeys) { + if (unknownKeys.includes(requiredKey)) continue + yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } + } + if (schema.additionalProperties === false) { + for (const valueKey of unknownKeys) { + if (!knownKeys.includes(valueKey)) { + yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${valueKey}`, value: value[valueKey], message: `Unexpected property` } } } } - function* Promise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'object' && typeof value.then === 'function')) { - yield { type: ValueErrorType.Promise, schema, path, value, message: `Expected Promise` } + if (typeof schema.additionalProperties === 'object') { + for (const valueKey of unknownKeys) { + if (knownKeys.includes(valueKey)) continue + yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey]) } } - function* Record(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsRecordObject(value)) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected record object` } - } - if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } - } - if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } - } - const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const regex = new RegExp(patternKey) - for (const [propertyKey, propertyValue] of globalThis.Object.entries(value)) { - if (regex.test(propertyKey)) { - yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) - continue - } - if (typeof schema.additionalProperties === 'object') { - yield* Visit(schema.additionalProperties, references, `${path}/${propertyKey}`, propertyValue) - } - if (schema.additionalProperties === false) { - const propertyPath = `${path}/${propertyKey}` - const message = `Unexpected property '${propertyPath}'` - return yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: propertyPath, value: propertyValue, message } - } - } +} +function* TPromise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsPromise(value)) { + yield { type: ValueErrorType.Promise, schema, path, value, message: `Expected Promise` } } - function* Ref(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueErrorsDereferenceError(schema) - const target = references[index] - yield* Visit(target, references, path, value) +} +function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsRecordObject(value)) { + return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected record object` } } - function* String(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsString(value)) { - return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } - } - if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { - yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } - } - if (IsDefined(schema.maxLength) && !(value.length <= schema.maxLength)) { - yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } + if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + } + if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } + } + const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] + const regex = new RegExp(patternKey) + for (const [propertyKey, propertyValue] of Object.entries(value)) { + if (regex.test(propertyKey)) { + yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) + continue } - if (schema.pattern !== undefined) { - const regex = new RegExp(schema.pattern) - if (!regex.test(value)) { - yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } - } + if (typeof schema.additionalProperties === 'object') { + yield* Visit(schema.additionalProperties, references, `${path}/${propertyKey}`, propertyValue) } - if (schema.format !== undefined) { - if (!Types.FormatRegistry.Has(schema.format)) { - yield { type: ValueErrorType.StringFormatUnknown, schema, path, value, message: `Unknown string format '${schema.format}'` } - } else { - const format = Types.FormatRegistry.Get(schema.format)! - if (!format(value)) { - yield { type: ValueErrorType.StringFormat, schema, path, value, message: `Expected string to match format '${schema.format}'` } - } - } + if (schema.additionalProperties === false) { + const propertyPath = `${path}/${propertyKey}` + const message = `Unexpected property '${propertyPath}'` + return yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: propertyPath, value: propertyValue, message } } } - function* Symbol(schema: Types.TSymbol, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(typeof value === 'symbol')) { - return yield { type: ValueErrorType.Symbol, schema, path, value, message: 'Expected symbol' } - } +} +function* TRef(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueErrorsDereferenceError(schema) + const target = references[index] + yield* Visit(target, references, path, value) +} +function* TString(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsString(value)) { + return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } } - function* TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsString(value)) { - return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } - } + if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { + yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } + } + if (IsDefined(schema.maxLength) && !(value.length <= schema.maxLength)) { + yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } + } + if (ValueGuard.IsString(schema.pattern)) { const regex = new RegExp(schema.pattern) if (!regex.test(value)) { yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } } } - function* This(schema: Types.TThis, references: Types.TSchema[], path: string, value: any): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueErrorsDereferenceError(schema) - const target = references[index] - yield* Visit(target, references, path, value) - } - function* Tuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!globalThis.Array.isArray(value)) { - return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } - } - if (schema.items === undefined && !(value.length === 0)) { - return yield { type: ValueErrorType.TupleZeroLength, schema, path, value, message: 'Expected tuple to have 0 elements' } - } - if (!(value.length === schema.maxItems)) { - yield { type: ValueErrorType.TupleLength, schema, path, value, message: `Expected tuple to have ${schema.maxItems} elements` } - } - if (!schema.items) { - return - } - for (let i = 0; i < schema.items.length; i++) { - yield* Visit(schema.items[i], references, `${path}/${i}`, value[i]) + if (ValueGuard.IsString(schema.format)) { + if (!Types.FormatRegistry.Has(schema.format)) { + yield { type: ValueErrorType.StringFormatUnknown, schema, path, value, message: `Unknown string format '${schema.format}'` } + } else { + const format = Types.FormatRegistry.Get(schema.format)! + if (!format(value)) { + yield { type: ValueErrorType.StringFormat, schema, path, value, message: `Expected string to match format '${schema.format}'` } + } } } - function* Undefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === undefined)) { - yield { type: ValueErrorType.Undefined, schema, path, value, message: `Expected undefined` } - } +} +function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsSymbol(value)) { + return yield { type: ValueErrorType.Symbol, schema, path, value, message: 'Expected symbol' } } - function* Union(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { - const errors: ValueError[] = [] - for (const inner of schema.anyOf) { - const variantErrors = [...Visit(inner, references, path, value)] - if (variantErrors.length === 0) return - errors.push(...variantErrors) - } - if (errors.length > 0) { - yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } - } - for (const error of errors) { - yield error - } +} +function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsString(value)) { + return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } } - function* Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value instanceof globalThis.Uint8Array)) { - return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } - } - if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { - yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } - } - if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { - yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } - } + const regex = new RegExp(schema.pattern) + if (!regex.test(value)) { + yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } } - function* Unknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} - function* Void(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsVoid(value)) { - return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected void` } - } +} +function* TThis(schema: Types.TThis, references: Types.TSchema[], path: string, value: any): IterableIterator { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueErrorsDereferenceError(schema) + const target = references[index] + yield* Visit(target, references, path, value) +} +function* TTuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsArray(value)) { + return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } } - function* UserDefined(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { - const check = Types.TypeRegistry.Get(schema[Types.Kind])! - if (!check(schema, value)) { - return yield { type: ValueErrorType.Custom, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } - } + if (schema.items === undefined && !(value.length === 0)) { + return yield { type: ValueErrorType.TupleZeroLength, schema, path, value, message: 'Expected tuple to have 0 elements' } } - function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { - const references_ = IsDefined(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema_[Types.Kind]) { - case 'Any': - return yield* Any(schema_, references_, path, value) - case 'Array': - return yield* Array(schema_, references_, path, value) - case 'BigInt': - return yield* BigInt(schema_, references_, path, value) - case 'Boolean': - return yield* Boolean(schema_, references_, path, value) - case 'Constructor': - return yield* Constructor(schema_, references_, path, value) - case 'Date': - return yield* Date(schema_, references_, path, value) - case 'Function': - return yield* Function(schema_, references_, path, value) - case 'Integer': - return yield* Integer(schema_, references_, path, value) - case 'Intersect': - return yield* Intersect(schema_, references_, path, value) - case 'Literal': - return yield* Literal(schema_, references_, path, value) - case 'Never': - return yield* Never(schema_, references_, path, value) - case 'Not': - return yield* Not(schema_, references_, path, value) - case 'Null': - return yield* Null(schema_, references_, path, value) - case 'Number': - return yield* Number(schema_, references_, path, value) - case 'Object': - return yield* Object(schema_, references_, path, value) - case 'Promise': - return yield* Promise(schema_, references_, path, value) - case 'Record': - return yield* Record(schema_, references_, path, value) - case 'Ref': - return yield* Ref(schema_, references_, path, value) - case 'String': - return yield* String(schema_, references_, path, value) - case 'Symbol': - return yield* Symbol(schema_, references_, path, value) - case 'TemplateLiteral': - return yield* TemplateLiteral(schema_, references_, path, value) - case 'This': - return yield* This(schema_, references_, path, value) - case 'Tuple': - return yield* Tuple(schema_, references_, path, value) - case 'Undefined': - return yield* Undefined(schema_, references_, path, value) - case 'Union': - return yield* Union(schema_, references_, path, value) - case 'Uint8Array': - return yield* Uint8Array(schema_, references_, path, value) - case 'Unknown': - return yield* Unknown(schema_, references_, path, value) - case 'Void': - return yield* Void(schema_, references_, path, value) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) - return yield* UserDefined(schema_, references_, path, value) - } + if (!(value.length === schema.maxItems)) { + yield { type: ValueErrorType.TupleLength, schema, path, value, message: `Expected tuple to have ${schema.maxItems} elements` } + } + if (!schema.items) { + return + } + for (let i = 0; i < schema.items.length; i++) { + yield* Visit(schema.items[i], references, `${path}/${i}`, value[i]) + } +} +function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!(value === undefined)) { + yield { type: ValueErrorType.Undefined, schema, path, value, message: `Expected undefined` } + } +} +function* TUnion(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { + const errors: ValueError[] = [] + for (const inner of schema.anyOf) { + const variantErrors = [...Visit(inner, references, path, value)] + if (variantErrors.length === 0) return + errors.push(...variantErrors) + } + if (errors.length > 0) { + yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } + } + for (const error of errors) { + yield error } - export function Errors(schema: T, references: Types.TSchema[], value: any): ValueErrorIterator { - const iterator = Visit(schema, references, '', value) - return new ValueErrorIterator(iterator) +} +function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!ValueGuard.IsUint8Array(value)) { + return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } + } + if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { + yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } + } + if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { + yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } + } +} +function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} +function* TVoid(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { + if (!IsVoid(value)) { + return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected void` } } } +function* TKind(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { + const check = Types.TypeRegistry.Get(schema[Types.Kind])! + if (!check(schema, value)) { + return yield { type: ValueErrorType.Kind, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } + } +} +function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { + const references_ = IsDefined(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { + case 'Any': + return yield* TAny(schema_, references_, path, value) + case 'Array': + return yield* TArray(schema_, references_, path, value) + case 'AsyncIterator': + return yield* TAsyncIterator(schema_, references_, path, value) + case 'BigInt': + return yield* TBigInt(schema_, references_, path, value) + case 'Boolean': + return yield* TBoolean(schema_, references_, path, value) + case 'Constructor': + return yield* TConstructor(schema_, references_, path, value) + case 'Date': + return yield* TDate(schema_, references_, path, value) + case 'Function': + return yield* TFunction(schema_, references_, path, value) + case 'Integer': + return yield* TInteger(schema_, references_, path, value) + case 'Intersect': + return yield* TIntersect(schema_, references_, path, value) + case 'Iterator': + return yield* TIterator(schema_, references_, path, value) + case 'Literal': + return yield* TLiteral(schema_, references_, path, value) + case 'Never': + return yield* TNever(schema_, references_, path, value) + case 'Not': + return yield* TNot(schema_, references_, path, value) + case 'Null': + return yield* TNull(schema_, references_, path, value) + case 'Number': + return yield* TNumber(schema_, references_, path, value) + case 'Object': + return yield* TObject(schema_, references_, path, value) + case 'Promise': + return yield* TPromise(schema_, references_, path, value) + case 'Record': + return yield* TRecord(schema_, references_, path, value) + case 'Ref': + return yield* TRef(schema_, references_, path, value) + case 'String': + return yield* TString(schema_, references_, path, value) + case 'Symbol': + return yield* TSymbol(schema_, references_, path, value) + case 'TemplateLiteral': + return yield* TTemplateLiteral(schema_, references_, path, value) + case 'This': + return yield* TThis(schema_, references_, path, value) + case 'Tuple': + return yield* TTuple(schema_, references_, path, value) + case 'Undefined': + return yield* TUndefined(schema_, references_, path, value) + case 'Union': + return yield* TUnion(schema_, references_, path, value) + case 'Uint8Array': + return yield* TUint8Array(schema_, references_, path, value) + case 'Unknown': + return yield* TUnknown(schema_, references_, path, value) + case 'Void': + return yield* TVoid(schema_, references_, path, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) + return yield* TKind(schema_, references_, path, value) + } +} +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(...args: any[]) { + const iterator = args.length === 3 ? Visit(args[0], args[1], '', args[2]) : Visit(args[0], [], '', args[1]) + return new ValueErrorIterator(iterator) +} diff --git a/src/system/system.ts b/src/system/system.ts index 880f800e4..4b5291fd2 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -38,12 +38,8 @@ export class TypeSystemDuplicateFormat extends Error { super(`Duplicate string format '${kind}' detected`) } } - /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { - // ------------------------------------------------------------------------ - // Assertion Policies - // ------------------------------------------------------------------------ /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ export let ExactOptionalPropertyTypes: boolean = false /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ @@ -52,9 +48,7 @@ export namespace TypeSystem { export let AllowNaN: boolean = false /** Sets whether `null` should validate for void types. The default is `false` */ export let AllowVoidNull: boolean = false - // ------------------------------------------------------------------------ - // String Formats and Types - // ------------------------------------------------------------------------ + /** Creates a new type */ export function Type(kind: string, check: (options: Options, value: unknown) => boolean) { if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) diff --git a/src/typebox.ts b/src/typebox.ts index 0c9945cf7..2e7c407cf 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -29,7 +29,8 @@ THE SOFTWARE. // -------------------------------------------------------------------------- // Symbols // -------------------------------------------------------------------------- -export const Modifier = Symbol.for('TypeBox.Modifier') +export const Readonly = Symbol.for('TypeBox.Readonly') +export const Optional = Symbol.for('TypeBox.Optional') export const Hint = Symbol.for('TypeBox.Hint') export const Kind = Symbol.for('TypeBox.Kind') // -------------------------------------------------------------------------- @@ -64,21 +65,34 @@ export type AssertType = T extends E ? T : TNeve // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- -export type TModifier = TReadonlyOptional | TOptional | TReadonly -export type TReadonly = T & { [Modifier]: 'Readonly' } -export type TOptional = T & { [Modifier]: 'Optional' } -export type TReadonlyOptional = T & { [Modifier]: 'ReadonlyOptional' } +export type TModifier = TOptional | TReadonly +export type TReadonly = T & { [Readonly]: 'Readonly' } +export type TOptional = T & { [Optional]: 'Optional' } +// -------------------------------------------------------------------------- +// Readonly Unwrap +// -------------------------------------------------------------------------- +// prettier-ignore +export type ReadonlyUnwrapType = + T extends TReadonly ? ReadonlyUnwrapType : + T extends TOptional ? TOptional> : + T +// prettier-ignore +export type ReadonlyUnwrapRest = T extends [infer L, ...infer R] + ? L extends TReadonly + ? [ReadonlyUnwrapType>, ...ReadonlyUnwrapRest>] + : [L, ...ReadonlyUnwrapRest>] + : [] // -------------------------------------------------------------------------- // Optional Unwrap // -------------------------------------------------------------------------- // prettier-ignore export type OptionalUnwrapType = - T extends (TOptional | TReadonlyOptional) - ? OptionalUnwrapType - : T + T extends TReadonly ? TReadonly> : + T extends TOptional ? OptionalUnwrapType : + T // prettier-ignore export type OptionalUnwrapRest = T extends [infer L, ...infer R] - ? L extends (TOptional | TReadonlyOptional) + ? L extends TOptional ? [OptionalUnwrapType>, ...OptionalUnwrapRest>] : [L, ...OptionalUnwrapRest>] : [] @@ -87,7 +101,7 @@ export type OptionalUnwrapRest = T extends [infer L, ...inf // -------------------------------------------------------------------------- // prettier-ignore export type IntersectOptional = T extends [infer L, ...infer R] - ? L extends TOptional> | TReadonlyOptional> + ? L extends TOptional> ? IntersectOptional> : false : true @@ -105,7 +119,7 @@ export type IntersectType = // -------------------------------------------------------------------------- // prettier-ignore export type UnionOptional = T extends [infer L, ...infer R] - ? L extends (TOptional> | TReadonlyOptional>) + ? L extends (TOptional>) ? true : UnionOptional> : false @@ -119,10 +133,6 @@ export type UnionType = T extends [TSchema] ? AssertType : UnionResolve // -------------------------------------------------------------------------- -// Key -// -------------------------------------------------------------------------- -export type Key = string | number -// -------------------------------------------------------------------------- // TSchema // -------------------------------------------------------------------------- export interface SchemaOptions { @@ -137,13 +147,18 @@ export interface SchemaOptions { default?: any /** Example values matching this schema */ examples?: any + /** Optional annotation for readOnly */ + readOnly?: boolean + /** Optional annotation for writeOnly */ + writeOnly?: boolean [prop: string]: any } export interface TKind { [Kind]: string } export interface TSchema extends SchemaOptions, TKind { - [Modifier]?: string + [Readonly]?: string + [Optional]?: string [Hint]?: string params: unknown[] static: unknown @@ -155,6 +170,7 @@ export type TAnySchema = | TSchema | TAny | TArray + | TAsyncIterator | TBigInt | TBoolean | TConstructor @@ -163,6 +179,7 @@ export type TAnySchema = | TFunction | TInteger | TIntersect + | TIterator | TLiteral | TNot | TNull @@ -202,9 +219,18 @@ export interface TAny extends TSchema { // TArray // -------------------------------------------------------------------------- export interface ArrayOptions extends SchemaOptions { - uniqueItems?: boolean + /** The minimum number of items in this array */ minItems?: number + /** The maximum number of items in this array */ maxItems?: number + /** Should this schema contain unique items */ + uniqueItems?: boolean + /** A schema for which some elements should match */ + contains?: TSchema + /** A minimum number of contains schema matches */ + minContains?: number + /** A maximum number of contains schema matches */ + maxContains?: number } export interface TArray extends TSchema, ArrayOptions { [Kind]: 'Array' @@ -213,13 +239,34 @@ export interface TArray extends TSchema, ArrayOptio items: T } // -------------------------------------------------------------------------- +// TAsyncIterator +// -------------------------------------------------------------------------- +export interface TAsyncIterator extends TSchema { + [Kind]: 'AsyncIterator' + static: AsyncIterableIterator> + type: 'AsyncIterator' + items: T +} +// ------------------------------------------------------------------------------- +// Awaited +// ------------------------------------------------------------------------------- +// prettier-ignore +export type TAwaitedRest = T extends [infer L, ...infer R] + ? [TAwaited>, ...TAwaitedRest>] + : [] +// prettier-ignore +export type TAwaited = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TPromise ? TAwaited : + T +// -------------------------------------------------------------------------- // TBigInt // -------------------------------------------------------------------------- export interface TBigInt extends TSchema, NumericOptions { [Kind]: 'BigInt' static: bigint - type: 'null' - typeOf: 'BigInt' + type: 'bigint' } // -------------------------------------------------------------------------- // TBoolean @@ -263,8 +310,7 @@ export type TConstructorParameterArray extends TSchema { [Kind]: 'Constructor' static: new (...param: TConstructorParameterArray) => Static - type: 'object' - instanceOf: 'Constructor' + type: 'constructor' parameters: T returns: U } @@ -272,16 +318,19 @@ export interface TConstructor = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] -export interface TFunction extends TSchema { +export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export interface TFunction extends TSchema { [Kind]: 'Function' static: (...param: TFunctionParameters) => Static - type: 'object' - instanceOf: 'Function' + type: 'function' parameters: T returns: U } @@ -347,31 +395,29 @@ export interface TFunction = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] -export type TIndexProperty = K extends keyof T ? [T[K]] : [] -export type TIndexTuple = K extends keyof T ? [T[K]] : [] +export type TIndexRest = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] +export type TIndexProperty = K extends keyof T ? [T[K]] : [] +export type TIndexTuple = K extends keyof T ? [T[K]] : [] // prettier-ignore -export type TIndexType = +export type TIndexType = T extends TRecursive ? TIndexType : T extends TIntersect ? IntersectType>, TNever>>> : T extends TUnion ? UnionType>>> : T extends TObject ? UnionType>>> : T extends TTuple ? UnionType>>> : [] - // prettier-ignore -export type TIndexRestMany = - K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : +export type TIndexRestMany = + K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : [] // prettier-ignore -export type TIndex = +export type TIndex = T extends TRecursive ? TIndex : T extends TIntersect ? UnionType>> : T extends TUnion ? UnionType>> : T extends TObject ? UnionType>> : T extends TTuple ? UnionType>> : TNever - // -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- @@ -394,6 +440,15 @@ export interface TIntersect extends TSchema, In allOf: [...T] } // -------------------------------------------------------------------------- +// TIterator +// -------------------------------------------------------------------------- +export interface TIterator extends TSchema { + [Kind]: 'Iterator' + static: IterableIterator> + type: 'Iterator' + items: T +} +// -------------------------------------------------------------------------- // TKeyOf // -------------------------------------------------------------------------- // prettier-ignore @@ -420,7 +475,10 @@ export type TKeyOf = ( // -------------------------------------------------------------------------- // TLiteral // -------------------------------------------------------------------------- -export type TLiteralValue = string | number | boolean // | bigint - supported but variant disable due to potential numeric type conflicts +export type TLiteralValue = boolean | number | string // | bigint - supported but variant disable due to potential numeric type conflicts +export type TLiteralBoolean = TLiteral +export type TLiteralNumber = TLiteral +export type TLiteralString = TLiteral export interface TLiteral extends TSchema { [Kind]: 'Literal' static: T @@ -461,9 +519,9 @@ export interface TNumber extends TSchema, NumericOptions { // -------------------------------------------------------------------------- // TObject // -------------------------------------------------------------------------- -export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonlyOptional ? K : never }[keyof T] -export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? K : never }[keyof T] -export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? K : never }[keyof T] +export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? K : never) : never }[keyof T] +export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? never : K) : never }[keyof T] +export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? (T[K] extends TReadonly ? never : K) : never }[keyof T] export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> // prettier-ignore export type PropertiesReducer> = Evaluate<( @@ -476,7 +534,8 @@ export type PropertiesReducer = PropertiesReducer }> -export type TProperties = Record +export type TPropertyKey = string | number +export type TProperties = Record export type ObjectProperties = T extends TObject ? U : never export type ObjectPropertyKeys = T extends TObject ? keyof U : never export type TAdditionalProperties = undefined | TSchema | boolean @@ -496,13 +555,13 @@ export interface TObject extends TSchema, O // -------------------------------------------------------------------------- // TOmit // -------------------------------------------------------------------------- -export type TOmitArray = AssertRest<{ [K2 in keyof T]: TOmit, K> }> export type TOmitProperties = Evaluate>> +export type TOmitRest = AssertRest<{ [K2 in keyof T]: TOmit, K> }> // prettier-ignore export type TOmit = T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : T extends TObject ? TObject> : T // -------------------------------------------------------------------------- @@ -513,26 +572,21 @@ export type TParameters = Ensure> // TPartial // -------------------------------------------------------------------------- export type TPartialObjectArray = AssertRest<{ [K in keyof T]: TPartial> }, TObject[]> -export type TPartialArray = AssertRest<{ [K in keyof T]: TPartial> }> +export type TPartialRest = AssertRest<{ [K in keyof T]: TPartial> }> // prettier-ignore export type TPartialProperties = Evaluate ? TReadonlyOptional : - T[K] extends TReadonly ? TReadonlyOptional : - T[K] extends TOptional ? TOptional : - TOptional + [K in keyof T]: TOptional }>> // prettier-ignore export type TPartial = T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : T extends TObject ? TObject> : T // -------------------------------------------------------------------------- // TPick // -------------------------------------------------------------------------- -export type TPickArray = { [K2 in keyof T]: TPick, K> } // Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, // we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. // prettier-ignore @@ -540,11 +594,12 @@ export type TPickProperties = Pick, keyof T>> extends infer R ? ({ [K in keyof R]: AssertType extends TSchema ? R[K] : never }): never +export type TPickRest = { [K2 in keyof T]: TPick, K> } // prettier-ignore export type TPick = T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : T extends TObject ? TObject> : T // -------------------------------------------------------------------------- @@ -553,8 +608,7 @@ export type TPick export interface TPromise extends TSchema { [Kind]: 'Promise' static: Promise> - type: 'object' - instanceOf: 'Promise' + type: 'promise' item: TSchema } // -------------------------------------------------------------------------- @@ -606,20 +660,16 @@ export type TReturnType = T['returns'] // -------------------------------------------------------------------------- // TRequired // -------------------------------------------------------------------------- -export type TRequiredArray = AssertRest<{ [K in keyof T]: TRequired> }> +export type TRequiredRest = AssertRest<{ [K in keyof T]: TRequired> }> // prettier-ignore export type TRequiredProperties = Evaluate ? TReadonly : - T[K] extends TReadonly ? TReadonly : - T[K] extends TOptional ? U : - T[K] + [K in keyof T]: T[K] extends TOptional ? S : T[K] }>> // prettier-ignore export type TRequired = T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : T extends TObject ? TObject> : T // -------------------------------------------------------------------------- @@ -654,11 +704,17 @@ export type StringContentEncodingOption = | 'base64' | ({} & string) export interface StringOptions extends SchemaOptions { - minLength?: number + /** The maximum string length */ maxLength?: number + /** The minimum string length */ + minLength?: number + /** A regular expression pattern this string should match */ pattern?: string + /** A format this string should match */ format?: StringFormatOption + /** The content encoding for this string */ contentEncoding?: StringContentEncodingOption + /** The content media type for this string */ contentMediaType?: string } export interface TString extends TSchema, StringOptions { @@ -673,8 +729,7 @@ export type SymbolValue = string | number | undefined export interface TSymbol extends TSchema, SchemaOptions { [Kind]: 'Symbol' static: symbol - type: 'null' - typeOf: 'Symbol' + type: 'symbol' } // ------------------------------------------------------------------------- // TTemplateLiteralParserDsl @@ -733,7 +788,7 @@ export type TTemplateLiteralConst = export type TTemplateLiteralUnion = T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc -export type TTemplateLiteralKeyRest = Assert>, Key[]> +export type TTemplateLiteralKeyRest = Assert>, TPropertyKey[]> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' static: TTemplateLiteralUnion @@ -743,7 +798,6 @@ export interface TTemplateLiteral> = T extends TTuple ? AssertRest : never export type TTupleInfer = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleInfer, P>] : [] export interface TTuple extends TSchema { [Kind]: 'Tuple' @@ -760,8 +814,7 @@ export interface TTuple extends TSchema { export interface TUndefined extends TSchema { [Kind]: 'Undefined' static: undefined - type: 'null' - typeOf: 'Undefined' + type: 'undefined' } // -------------------------------------------------------------------------- // TUnionLiteral @@ -794,8 +847,7 @@ export interface Uint8ArrayOptions extends SchemaOptions { export interface TUint8Array extends TSchema, Uint8ArrayOptions { [Kind]: 'Uint8Array' static: Uint8Array - instanceOf: 'Uint8Array' - type: 'object' + type: 'uint8array' } // -------------------------------------------------------------------------- // TUnknown @@ -820,8 +872,7 @@ export interface TUnsafe extends TSchema { export interface TVoid extends TSchema { [Kind]: 'Void' static: void - type: 'null' - typeOf: 'Void' + type: 'void' } // -------------------------------------------------------------------------- // Static @@ -843,6 +894,10 @@ export namespace TypeRegistry { export function Clear() { return map.clear() } + /** Deletes a registered type */ + export function Delete(kind: string) { + return map.delete(kind) + } /** Returns true if this registry contains this kind */ export function Has(kind: string) { return map.has(kind) @@ -871,6 +926,10 @@ export namespace FormatRegistry { export function Clear() { return map.clear() } + /** Deletes a registered format */ + export function Delete(format: string) { + return map.delete(format) + } /** Returns true if the user defined string format exists */ export function Has(format: string) { return map.has(format) @@ -885,6 +944,35 @@ export namespace FormatRegistry { } } // -------------------------------------------------------------------------- +// ValueGuard +// -------------------------------------------------------------------------- +export namespace ValueGuard { + export function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null + } + export function IsArray(value: unknown): value is unknown[] { + return Array.isArray(value) + } + export function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' + } + export function IsNull(value: unknown): value is null { + return value === null + } + export function IsUndefined(value: unknown): value is undefined { + return value === undefined + } + export function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + export function IsNumber(value: unknown): value is number { + return typeof value === 'number' + } + export function IsString(value: unknown): value is string { + return typeof value === 'string' + } +} +// -------------------------------------------------------------------------- // TypeGuard // -------------------------------------------------------------------------- export class TypeGuardUnknownTypeError extends Error { @@ -894,12 +982,6 @@ export class TypeGuardUnknownTypeError extends Error { } /** Provides functions to test if JavaScript values are TypeBox types */ export namespace TypeGuard { - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !Array.isArray(value) - } - function IsArray(value: unknown): value is any[] { - return typeof value === 'object' && value !== null && Array.isArray(value) - } function IsPattern(value: unknown): value is string { try { new RegExp(value as string) @@ -909,7 +991,7 @@ export namespace TypeGuard { } } function IsControlCharacterFree(value: unknown): value is string { - if (typeof value !== 'string') return false + if (!ValueGuard.IsString(value)) return false for (let i = 0; i < value.length; i++) { const code = value.charCodeAt(i) if ((code >= 7 && code <= 13) || code === 27 || code === 127) { @@ -921,64 +1003,66 @@ export namespace TypeGuard { function IsAdditionalProperties(value: unknown): value is TAdditionalProperties { return IsOptionalBoolean(value) || TSchema(value) } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - function IsNumber(value: unknown): value is number { - return typeof value === 'number' && globalThis.Number.isFinite(value) - } - function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } function IsOptionalBigInt(value: unknown): value is bigint | undefined { - return value === undefined || (value !== undefined && IsBigInt(value)) + return ValueGuard.IsUndefined(value) || ValueGuard.IsBigInt(value) } function IsOptionalNumber(value: unknown): value is number | undefined { - return value === undefined || (value !== undefined && IsNumber(value)) + return ValueGuard.IsUndefined(value) || ValueGuard.IsNumber(value) } function IsOptionalBoolean(value: unknown): value is boolean | undefined { - return value === undefined || (value !== undefined && IsBoolean(value)) + return ValueGuard.IsUndefined(value) || ValueGuard.IsBoolean(value) } function IsOptionalString(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value)) + return ValueGuard.IsUndefined(value) || ValueGuard.IsString(value) } function IsOptionalPattern(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) + return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) } function IsOptionalFormat(value: unknown): value is string | undefined { - return value === undefined || (value !== undefined && IsString(value) && IsControlCharacterFree(value)) + return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value)) } function IsOptionalSchema(value: unknown): value is boolean | undefined { - return value === undefined || TSchema(value) + return ValueGuard.IsUndefined(value) || TSchema(value) } /** Returns true if the given schema is TAny */ export function TAny(schema: unknown): schema is TAny { - return TKind(schema) && schema[Kind] === 'Any' && IsOptionalString(schema.$id) + // prettier-ignore + return ( + TKindOf(schema, 'Any') && + IsOptionalString(schema.$id) + ) } /** Returns true if the given schema is TArray */ export function TArray(schema: unknown): schema is TArray { return ( - TKind(schema) && - schema[Kind] === 'Array' && + TKindOf(schema, 'Array') && schema.type === 'array' && IsOptionalString(schema.$id) && TSchema(schema.items) && IsOptionalNumber(schema.minItems) && IsOptionalNumber(schema.maxItems) && - IsOptionalBoolean(schema.uniqueItems) + IsOptionalBoolean(schema.uniqueItems) && + IsOptionalSchema(schema.contains) && + IsOptionalNumber(schema.minContains) && + IsOptionalNumber(schema.maxContains) + ) + } + /** Returns true if the given schema is TAsyncIterator */ + export function TAsyncIterator(schema: unknown): schema is TAsyncIterator { + // prettier-ignore + return ( + TKindOf(schema, 'AsyncIterator') && + schema.type === 'AsyncIterator' && + IsOptionalString(schema.$id) && + TSchema(schema.items) ) } /** Returns true if the given schema is TBigInt */ export function TBigInt(schema: unknown): schema is TBigInt { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'BigInt' && - schema.type === 'null' && - schema.typeOf === 'BigInt' && + TKindOf(schema, 'BigInt') && + schema.type === 'bigint' && IsOptionalString(schema.$id) && IsOptionalBigInt(schema.multipleOf) && IsOptionalBigInt(schema.minimum) && @@ -991,8 +1075,7 @@ export namespace TypeGuard { export function TBoolean(schema: unknown): schema is TBoolean { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Boolean' && + TKindOf(schema, 'Boolean') && schema.type === 'boolean' && IsOptionalString(schema.$id) ) @@ -1001,12 +1084,10 @@ export namespace TypeGuard { export function TConstructor(schema: unknown): schema is TConstructor { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Constructor' && - schema.type === 'object' && - schema.instanceOf === 'Constructor' && + TKindOf(schema, 'Constructor') && + schema.type === 'constructor' && IsOptionalString(schema.$id) && - IsArray(schema.parameters) && + ValueGuard.IsArray(schema.parameters) && TSchema(schema.returns)) ) { return false @@ -1019,10 +1100,8 @@ export namespace TypeGuard { /** Returns true if the given schema is TDate */ export function TDate(schema: unknown): schema is TDate { return ( - TKind(schema) && - schema[Kind] === 'Date' && - schema.type === 'object' && - schema.instanceOf === 'Date' && + TKindOf(schema, 'Date') && + schema.type === 'Date' && IsOptionalString(schema.$id) && IsOptionalNumber(schema.minimumTimestamp) && IsOptionalNumber(schema.maximumTimestamp) && @@ -1034,12 +1113,10 @@ export namespace TypeGuard { export function TFunction(schema: unknown): schema is TFunction { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Function' && - schema.type === 'object' && - schema.instanceOf === 'Function' && + TKindOf(schema, 'Function') && + schema.type === 'function' && IsOptionalString(schema.$id) && - IsArray(schema.parameters) && + ValueGuard.IsArray(schema.parameters) && TSchema(schema.returns)) ) { return false @@ -1052,8 +1129,7 @@ export namespace TypeGuard { /** Returns true if the given schema is TInteger */ export function TInteger(schema: unknown): schema is TInteger { return ( - TKind(schema) && - schema[Kind] === 'Integer' && + TKindOf(schema, 'Integer') && schema.type === 'integer' && IsOptionalString(schema.$id) && IsOptionalNumber(schema.multipleOf) && @@ -1067,9 +1143,8 @@ export namespace TypeGuard { export function TIntersect(schema: unknown): schema is TIntersect { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Intersect' && - IsArray(schema.allOf) && + TKindOf(schema, 'Intersect') && + ValueGuard.IsArray(schema.allOf) && IsOptionalString(schema.type) && (IsOptionalBoolean(schema.unevaluatedProperties) || IsOptionalSchema(schema.unevaluatedProperties)) && IsOptionalString(schema.$id)) @@ -1084,36 +1159,62 @@ export namespace TypeGuard { } return true } + /** Returns true if the given schema is TIterator */ + export function TIterator(schema: unknown): schema is TIterator { + // prettier-ignore + return ( + TKindOf(schema, 'Iterator') && + schema.type === 'Iterator' && + IsOptionalString(schema.$id) && + TSchema(schema.items) + ) + } + /** Returns true if the given schema is a TKind with the given name. */ + export function TKindOf(schema: unknown, kind: T): schema is Record & { [Kind]: T } { + return TKind(schema) && schema[Kind] === kind + } /** Returns true if the given schema is TKind */ - export function TKind(schema: unknown): schema is Record { - return IsObject(schema) && Kind in schema && typeof (schema as any)[Kind] === 'string' // TS 4.1.5: any required for symbol indexer + export function TKind(schema: unknown): schema is Record & { [Kind]: string } { + return ValueGuard.IsObject(schema) && Kind in schema && ValueGuard.IsString(schema[Kind]) } /** Returns true if the given schema is TLiteral */ export function TLiteralString(schema: unknown): schema is TLiteral { - return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'string' + return TLiteral(schema) && ValueGuard.IsString(schema.const) } /** Returns true if the given schema is TLiteral */ export function TLiteralNumber(schema: unknown): schema is TLiteral { - return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'number' + return TLiteral(schema) && ValueGuard.IsNumber(schema.const) } /** Returns true if the given schema is TLiteral */ export function TLiteralBoolean(schema: unknown): schema is TLiteral { - return TKind(schema) && schema[Kind] === 'Literal' && IsOptionalString(schema.$id) && typeof schema.const === 'boolean' + return TLiteral(schema) && ValueGuard.IsBoolean(schema.const) } /** Returns true if the given schema is TLiteral */ export function TLiteral(schema: unknown): schema is TLiteral { - return TLiteralString(schema) || TLiteralNumber(schema) || TLiteralBoolean(schema) + // prettier-ignore + return ( + TKindOf(schema, 'Literal') && + IsOptionalString(schema.$id) && ( + ValueGuard.IsBoolean(schema.const) || + ValueGuard.IsNumber(schema.const) || + ValueGuard.IsString(schema.const) + ) + ) } /** Returns true if the given schema is TNever */ export function TNever(schema: unknown): schema is TNever { - return TKind(schema) && schema[Kind] === 'Never' && IsObject(schema.not) && globalThis.Object.getOwnPropertyNames(schema.not).length === 0 + // prettier-ignore + return ( + TKindOf(schema, 'Never') && + ValueGuard.IsObject(schema.not) && + Object.getOwnPropertyNames(schema.not).length === 0 + ) } /** Returns true if the given schema is TNot */ export function TNot(schema: unknown): schema is TNot { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Not' && + TKindOf(schema, 'Not') && TSchema(schema.not) ) } @@ -1121,8 +1222,7 @@ export namespace TypeGuard { export function TNull(schema: unknown): schema is TNull { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Null' && + TKindOf(schema, 'Null') && schema.type === 'null' && IsOptionalString(schema.$id) ) @@ -1130,8 +1230,7 @@ export namespace TypeGuard { /** Returns true if the given schema is TNumber */ export function TNumber(schema: unknown): schema is TNumber { return ( - TKind(schema) && - schema[Kind] === 'Number' && + TKindOf(schema, 'Number') && schema.type === 'number' && IsOptionalString(schema.$id) && IsOptionalNumber(schema.multipleOf) && @@ -1145,11 +1244,10 @@ export namespace TypeGuard { export function TObject(schema: unknown): schema is TObject { if ( !( - TKind(schema) && - schema[Kind] === 'Object' && + TKindOf(schema, 'Object') && schema.type === 'object' && IsOptionalString(schema.$id) && - IsObject(schema.properties) && + ValueGuard.IsObject(schema.properties) && IsAdditionalProperties(schema.additionalProperties) && IsOptionalNumber(schema.minProperties) && IsOptionalNumber(schema.maxProperties) @@ -1167,10 +1265,8 @@ export namespace TypeGuard { export function TPromise(schema: unknown): schema is TPromise { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Promise' && - schema.type === 'object' && - schema.instanceOf === 'Promise' && + TKindOf(schema, 'Promise') && + schema.type === 'Promise' && IsOptionalString(schema.$id) && TSchema(schema.item) ) @@ -1179,16 +1275,15 @@ export namespace TypeGuard { export function TRecord(schema: unknown): schema is TRecord { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Record' && + TKindOf(schema, 'Record') && schema.type === 'object' && IsOptionalString(schema.$id) && IsAdditionalProperties(schema.additionalProperties) && - IsObject(schema.patternProperties)) + ValueGuard.IsObject(schema.patternProperties)) ) { return false } - const keys = Object.keys(schema.patternProperties) + const keys = Object.getOwnPropertyNames(schema.patternProperties) if (keys.length !== 1) { return false } @@ -1204,33 +1299,23 @@ export namespace TypeGuard { export function TRef(schema: unknown): schema is TRef { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Ref' && + TKindOf(schema, 'Ref') && IsOptionalString(schema.$id) && - IsString(schema.$ref) + ValueGuard.IsString(schema.$ref) ) } /** Returns true if the given schema is TString */ export function TString(schema: unknown): schema is TString { return ( - TKind(schema) && - schema[Kind] === 'String' && - schema.type === 'string' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minLength) && - IsOptionalNumber(schema.maxLength) && - IsOptionalPattern(schema.pattern) && - IsOptionalFormat(schema.format) + TKindOf(schema, 'String') && schema.type === 'string' && IsOptionalString(schema.$id) && IsOptionalNumber(schema.minLength) && IsOptionalNumber(schema.maxLength) && IsOptionalPattern(schema.pattern) && IsOptionalFormat(schema.format) ) } /** Returns true if the given schema is TSymbol */ export function TSymbol(schema: unknown): schema is TSymbol { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Symbol' && - schema.type === 'null' && - schema.typeOf === 'Symbol' && + TKindOf(schema, 'Symbol') && + schema.type === 'symbol' && IsOptionalString(schema.$id) ) } @@ -1238,10 +1323,9 @@ export namespace TypeGuard { export function TTemplateLiteral(schema: unknown): schema is TTemplateLiteral { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'TemplateLiteral' && + TKindOf(schema, 'TemplateLiteral') && schema.type === 'string' && - IsString(schema.pattern) && + ValueGuard.IsString(schema.pattern) && schema.pattern[0] === '^' && schema.pattern[schema.pattern.length - 1] === '$' ) @@ -1250,30 +1334,28 @@ export namespace TypeGuard { export function TThis(schema: unknown): schema is TThis { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'This' && + TKindOf(schema, 'This') && IsOptionalString(schema.$id) && - IsString(schema.$ref) + ValueGuard.IsString(schema.$ref) ) } /** Returns true if the given schema is TTuple */ export function TTuple(schema: unknown): schema is TTuple { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Tuple' && + TKindOf(schema, 'Tuple') && schema.type === 'array' && IsOptionalString(schema.$id) && - IsNumber(schema.minItems) && - IsNumber(schema.maxItems) && + ValueGuard.IsNumber(schema.minItems) && + ValueGuard.IsNumber(schema.maxItems) && schema.minItems === schema.maxItems) ) { return false } - if (schema.items === undefined && schema.additionalItems === undefined && schema.minItems === 0) { + if (ValueGuard.IsUndefined(schema.items) && ValueGuard.IsUndefined(schema.additionalItems) && schema.minItems === 0) { return true } - if (!IsArray(schema.items)) { + if (!ValueGuard.IsArray(schema.items)) { return false } for (const inner of schema.items) { @@ -1285,10 +1367,8 @@ export namespace TypeGuard { export function TUndefined(schema: unknown): schema is TUndefined { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Undefined' && - schema.type === 'null' && - schema.typeOf === 'Undefined' && + TKindOf(schema, 'Undefined') && + schema.type === 'undefined' && IsOptionalString(schema.$id) ) } @@ -1300,9 +1380,8 @@ export namespace TypeGuard { export function TUnion(schema: unknown): schema is TUnion { // prettier-ignore if (!( - TKind(schema) && - schema[Kind] === 'Union' && - IsArray(schema.anyOf) && + TKindOf(schema, 'Union') && + ValueGuard.IsArray(schema.anyOf) && IsOptionalString(schema.$id)) ) { return false @@ -1314,61 +1393,59 @@ export namespace TypeGuard { } /** Returns true if the given schema is TUint8Array */ export function TUint8Array(schema: unknown): schema is TUint8Array { - return TKind(schema) && schema[Kind] === 'Uint8Array' && schema.type === 'object' && IsOptionalString(schema.$id) && schema.instanceOf === 'Uint8Array' && IsOptionalNumber(schema.minByteLength) && IsOptionalNumber(schema.maxByteLength) + // prettier-ignore + return ( + TKindOf(schema, 'Uint8Array') && + schema.type === 'Uint8Array' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minByteLength) && + IsOptionalNumber(schema.maxByteLength) + ) } /** Returns true if the given schema is TUnknown */ export function TUnknown(schema: unknown): schema is TUnknown { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Unknown' && + TKindOf(schema, 'Unknown') && IsOptionalString(schema.$id) ) } /** Returns true if the given schema is a raw TUnsafe */ export function TUnsafe(schema: unknown): schema is TUnsafe { - // prettier-ignore - return ( - TKind(schema) && - schema[Kind] === 'Unsafe' - ) + return TKindOf(schema, 'Unsafe') } /** Returns true if the given schema is TVoid */ export function TVoid(schema: unknown): schema is TVoid { // prettier-ignore return ( - TKind(schema) && - schema[Kind] === 'Void' && - schema.type === 'null' && - schema.typeOf === 'Void' && + TKindOf(schema, 'Void') && + schema.type === 'void' && IsOptionalString(schema.$id) ) } - /** Returns true if this schema has the ReadonlyOptional modifier */ - export function TReadonlyOptional(schema: T): schema is TReadonlyOptional { - return IsObject(schema) && schema[Modifier] === 'ReadonlyOptional' - } /** Returns true if this schema has the Readonly modifier */ export function TReadonly(schema: T): schema is TReadonly { - return IsObject(schema) && schema[Modifier] === 'Readonly' + return ValueGuard.IsObject(schema) && schema[Readonly] === 'Readonly' } /** Returns true if this schema has the Optional modifier */ export function TOptional(schema: T): schema is TOptional { - return IsObject(schema) && schema[Modifier] === 'Optional' + return ValueGuard.IsObject(schema) && schema[Optional] === 'Optional' } /** Returns true if the given schema is TSchema */ export function TSchema(schema: unknown): schema is TSchema { return ( - typeof schema === 'object' && + ValueGuard.IsObject(schema) && (TAny(schema) || TArray(schema) || TBoolean(schema) || TBigInt(schema) || + TAsyncIterator(schema) || TConstructor(schema) || TDate(schema) || TFunction(schema) || TInteger(schema) || TIntersect(schema) || + TIterator(schema) || TLiteral(schema) || TNever(schema) || TNot(schema) || @@ -1430,13 +1507,34 @@ export namespace TypeExtends { return result === TypeExtendsResult.False ? TypeExtendsResult.False : TypeExtendsResult.True } // -------------------------------------------------------------------------- + // StructuralRight + // -------------------------------------------------------------------------- + function IsStructuralRight(right: TSchema): boolean { + // prettier-ignore + return ( + TypeGuard.TNever(right) || + TypeGuard.TIntersect(right) || + TypeGuard.TUnion(right) || + TypeGuard.TUnknown(right) || + TypeGuard.TAny(right) + ) + } + function StructuralRight(left: TSchema, right: TSchema) { + if (TypeGuard.TNever(right)) return TNeverRight(left, right) + if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) + if (TypeGuard.TUnion(right)) return TUnionRight(left, right) + if (TypeGuard.TUnknown(right)) return TUnknownRight(left, right) + if (TypeGuard.TAny(right)) return TAnyRight(left, right) + throw Error('TypeExtends: StructuralRight') + } + // -------------------------------------------------------------------------- // Any // -------------------------------------------------------------------------- - function AnyRight(left: TSchema, right: TAny) { + function TAnyRight(left: TSchema, right: TAny) { return TypeExtendsResult.True } - function Any(left: TAny, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) + function TAny(left: TAny, right: TSchema) { + if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) if (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) return TypeExtendsResult.True if (TypeGuard.TUnion(right)) return TypeExtendsResult.Union if (TypeGuard.TUnknown(right)) return TypeExtendsResult.True @@ -1446,60 +1544,54 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Array // -------------------------------------------------------------------------- - function ArrayRight(left: TSchema, right: TArray) { + function TArrayRight(left: TSchema, right: TArray) { if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False if (TypeGuard.TAny(left)) return TypeExtendsResult.Union if (TypeGuard.TNever(left)) return TypeExtendsResult.True return TypeExtendsResult.False } - function Array(left: TArray, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) + function TArray(left: TArray, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True if (!TypeGuard.TArray(right)) return TypeExtendsResult.False return IntoBooleanResult(Visit(left.items, right.items)) } // -------------------------------------------------------------------------- + // AsyncIterator + // -------------------------------------------------------------------------- + function TAsyncIterator(left: TAsyncIterator, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (!TypeGuard.TAsyncIterator(right)) return TypeExtendsResult.False + return IntoBooleanResult(Visit(left.items, right.items)) + } + // -------------------------------------------------------------------------- // BigInt // -------------------------------------------------------------------------- - function BigInt(left: TBigInt, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TBigInt(left: TBigInt, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TBigInt(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Boolean // -------------------------------------------------------------------------- - function BooleanRight(left: TSchema, right: TBoolean) { - if (TypeGuard.TLiteral(left) && typeof left.const === 'boolean') return TypeExtendsResult.True + function TBooleanRight(left: TSchema, right: TBoolean) { + if (TypeGuard.TLiteral(left) && ValueGuard.IsBoolean(left.const)) return TypeExtendsResult.True return TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Boolean(left: TBoolean, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TBoolean(left: TBoolean, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TBoolean(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Constructor // -------------------------------------------------------------------------- - function Constructor(left: TConstructor, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) + function TConstructor(left: TConstructor, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) if (!TypeGuard.TConstructor(right)) return TypeExtendsResult.False if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { @@ -1510,24 +1602,18 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Date // -------------------------------------------------------------------------- - function Date(left: TDate, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TDate(left: TDate, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TDate(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Function // -------------------------------------------------------------------------- - function Function(left: TFunction, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) + function TFunction(left: TFunction, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) if (!TypeGuard.TFunction(right)) return TypeExtendsResult.False if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { @@ -1538,68 +1624,59 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Integer // -------------------------------------------------------------------------- - function IntegerRight(left: TSchema, right: TInteger) { - if (TypeGuard.TLiteral(left) && typeof left.const === 'number') return TypeExtendsResult.True + function TIntegerRight(left: TSchema, right: TInteger) { + if (TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const)) return TypeExtendsResult.True return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Integer(left: TInteger, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TInteger(left: TInteger, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Intersect // -------------------------------------------------------------------------- - function IntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { + function TIntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Intersect(left: TIntersect, right: TSchema) { + function TIntersect(left: TIntersect, right: TSchema) { return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- + // Iterator + // -------------------------------------------------------------------------- + function TIterator(left: TIterator, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (!TypeGuard.TIterator(right)) return TypeExtendsResult.False + return IntoBooleanResult(Visit(left.items, right.items)) + } + // -------------------------------------------------------------------------- // Literal // -------------------------------------------------------------------------- - function IsLiteralString(schema: TLiteral) { - return typeof schema.const === 'string' - } - function IsLiteralNumber(schema: TLiteral) { - return typeof schema.const === 'number' - } - function IsLiteralBoolean(schema: TLiteral) { - return typeof schema.const === 'boolean' - } - function Literal(left: TLiteral, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) - if (TypeGuard.TString(right)) return StringRight(left, right) - if (TypeGuard.TNumber(right)) return NumberRight(left, right) - if (TypeGuard.TInteger(right)) return IntegerRight(left, right) - if (TypeGuard.TBoolean(right)) return BooleanRight(left, right) + function TLiteral(left: TLiteral, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) + if (TypeGuard.TString(right)) return TStringRight(left, right) + if (TypeGuard.TNumber(right)) return TNumberRight(left, right) + if (TypeGuard.TInteger(right)) return TIntegerRight(left, right) + if (TypeGuard.TBoolean(right)) return TBooleanRight(left, right) return TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Never // -------------------------------------------------------------------------- - function NeverRight(left: TSchema, right: TNever) { + function TNeverRight(left: TSchema, right: TNever) { return TypeExtendsResult.False } - function Never(left: TNever, right: TSchema) { + function TNever(left: TNever, right: TSchema) { return TypeExtendsResult.True } // -------------------------------------------------------------------------- // Not // -------------------------------------------------------------------------- - function UnwrapNot(schema: T): TUnknown | TNot['not'] { + function UnwrapTNot(schema: T): TUnknown | TNot['not'] { let [current, depth]: [TSchema, number] = [schema, 0] while (true) { if (!TypeGuard.TNot(current)) break @@ -1608,49 +1685,41 @@ export namespace TypeExtends { } return depth % 2 === 0 ? current : Type.Unknown() } - function Not(left: TSchema, right: TSchema) { + function TNot(left: TSchema, right: TSchema) { // TypeScript has no concept of negated types, and attempts to correctly check the negated // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer // the type. Instead we unwrap to either unknown or T and continue evaluating. - if (TypeGuard.TNot(left)) return Visit(UnwrapNot(left), right) - if (TypeGuard.TNot(right)) return Visit(left, UnwrapNot(right)) + if (TypeGuard.TNot(left)) return Visit(UnwrapTNot(left), right) + if (TypeGuard.TNot(right)) return Visit(left, UnwrapTNot(right)) throw new Error(`TypeExtends: Invalid fallthrough for Not`) } // -------------------------------------------------------------------------- // Null // -------------------------------------------------------------------------- - function Null(left: TNull, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TNull(left: TNull, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TNull(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Number // -------------------------------------------------------------------------- - function NumberRight(left: TSchema, right: TNumber) { - if (TypeGuard.TLiteral(left) && IsLiteralNumber(left)) return TypeExtendsResult.True + function TNumberRight(left: TSchema, right: TNumber) { + if (TypeGuard.TLiteralNumber(left)) return TypeExtendsResult.True return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Number(left: TNumber, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TNumber(left: TNumber, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Object // -------------------------------------------------------------------------- function IsObjectPropertyCount(schema: TObject, count: number) { - return globalThis.Object.keys(schema.properties).length === count + return Object.getOwnPropertyNames(schema.properties).length === count } function IsObjectStringLike(schema: TObject) { return IsObjectArrayLike(schema) @@ -1705,13 +1774,13 @@ export namespace TypeExtends { if (TypeGuard.TOptional(left) && !TypeGuard.TOptional(right)) return TypeExtendsResult.False return TypeExtendsResult.True } - function ObjectRight(left: TSchema, right: TObject) { + function TObjectRight(left: TSchema, right: TObject) { if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False if (TypeGuard.TAny(left)) return TypeExtendsResult.Union if (TypeGuard.TNever(left)) return TypeExtendsResult.True - if (TypeGuard.TLiteral(left) && IsLiteralString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True - if (TypeGuard.TLiteral(left) && IsLiteralNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True - if (TypeGuard.TLiteral(left) && IsLiteralBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True + if (TypeGuard.TLiteralString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True + if (TypeGuard.TLiteralNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True + if (TypeGuard.TLiteralBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True if (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) return TypeExtendsResult.True if (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) return TypeExtendsResult.True if (TypeGuard.TString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True @@ -1733,14 +1802,11 @@ export namespace TypeExtends { } return TypeExtendsResult.False } - function Object(left: TObject, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TObject(left: TObject, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) if (!TypeGuard.TObject(right)) return TypeExtendsResult.False - for (const key of globalThis.Object.keys(right.properties)) { + for (const key of Object.getOwnPropertyNames(right.properties)) { if (!(key in left.properties)) return TypeExtendsResult.False if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { return TypeExtendsResult.False @@ -1751,11 +1817,8 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Promise // -------------------------------------------------------------------------- - function Promise(left: TPromise, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) + function TPromise(left: TPromise, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) if (TypeGuard.TObject(right) && IsObjectPromiseLike(right)) return TypeExtendsResult.True if (!TypeGuard.TPromise(right)) return TypeExtendsResult.False return IntoBooleanResult(Visit(left.item, right.item)) @@ -1773,15 +1836,15 @@ export namespace TypeExtends { if (PatternStringExact in schema.patternProperties) return schema.patternProperties[PatternStringExact] throw Error('TypeExtends: Cannot get record value') } - function RecordRight(left: TSchema, right: TRecord) { + function TRecordRight(left: TSchema, right: TRecord) { const Key = RecordKey(right) const Value = RecordValue(right) - if (TypeGuard.TLiteral(left) && IsLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) return TypeExtendsResult.True + if (TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) return TypeExtendsResult.True if (TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) if (TypeGuard.TString(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) if (TypeGuard.TArray(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) if (TypeGuard.TObject(left)) { - for (const key of globalThis.Object.keys(left.properties)) { + for (const key of Object.getOwnPropertyNames(left.properties)) { if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { return TypeExtendsResult.False } @@ -1790,50 +1853,39 @@ export namespace TypeExtends { } return TypeExtendsResult.False } - function Record(left: TRecord, right: TSchema) { + function TRecord(left: TRecord, right: TSchema) { const Value = RecordValue(left) - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) if (!TypeGuard.TRecord(right)) return TypeExtendsResult.False return Visit(Value, RecordValue(right)) } // -------------------------------------------------------------------------- // String // -------------------------------------------------------------------------- - function StringRight(left: TSchema, right: TString) { - if (TypeGuard.TLiteral(left) && typeof left.const === 'string') return TypeExtendsResult.True + function TStringRight(left: TSchema, right: TString) { + if (TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const)) return TypeExtendsResult.True return TypeGuard.TString(left) ? TypeExtendsResult.True : TypeExtendsResult.False } - function String(left: TString, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TString(left: TString, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TString(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Symbol // -------------------------------------------------------------------------- - function Symbol(left: TSymbol, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TSymbol(left: TSymbol, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TSymbol(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // TemplateLiteral // -------------------------------------------------------------------------- - function TemplateLiteral(left: TSchema, right: TSchema) { + function TTemplateLiteral(left: TSchema, right: TSchema) { // TemplateLiteral types are resolved to either unions for finite expressions or string // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for // either type and continue evaluating. @@ -1844,79 +1896,75 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- // Tuple // -------------------------------------------------------------------------- - function TupleRight(left: TSchema, right: TTuple) { + function IsArrayOfTuple(left: TTuple, right: TSchema) { + // prettier-ignore + return ( + TypeGuard.TArray(right) && + left.items !== undefined && + left.items.every((schema) => Visit(schema, right.items) === TypeExtendsResult.True) + ) + } + function TTupleRight(left: TSchema, right: TTuple) { + if (TypeGuard.TNever(left)) return TypeExtendsResult.True if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False if (TypeGuard.TAny(left)) return TypeExtendsResult.Union - if (TypeGuard.TNever(left)) return TypeExtendsResult.True return TypeExtendsResult.False } - function IsArrayOfTuple(left: TTuple, right: TSchema) { - return TypeGuard.TArray(right) && left.items !== undefined && left.items.every((schema) => Visit(schema, right.items) === TypeExtendsResult.True) - } - function Tuple(left: TTuple, right: TSchema): TypeExtendsResult { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) + function TTuple(left: TTuple, right: TSchema): TypeExtendsResult { + if (IsStructuralRight(right)) return StructuralRight(left, right) if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True if (TypeGuard.TArray(right) && IsArrayOfTuple(left, right)) return TypeExtendsResult.True if (!TypeGuard.TTuple(right)) return TypeExtendsResult.False - if ((left.items === undefined && right.items !== undefined) || (left.items !== undefined && right.items === undefined)) return TypeExtendsResult.False - if (left.items === undefined && right.items === undefined) return TypeExtendsResult.True + if ((ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items))) return TypeExtendsResult.False + if (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) return TypeExtendsResult.True return left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Uint8Array // -------------------------------------------------------------------------- - function Uint8Array(left: TUint8Array, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TUint8Array(left: TUint8Array, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) return TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Undefined // -------------------------------------------------------------------------- - function Undefined(left: TUndefined, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TNever(right)) return NeverRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) - if (TypeGuard.TRecord(right)) return RecordRight(left, right) + function TUndefined(left: TUndefined, right: TSchema) { + if (IsStructuralRight(right)) return StructuralRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) + if (TypeGuard.TRecord(right)) return TRecordRight(left, right) if (TypeGuard.TVoid(right)) return VoidRight(left, right) return TypeGuard.TUndefined(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Union // -------------------------------------------------------------------------- - function UnionRight(left: TSchema, right: TUnion): TypeExtendsResult { + function TUnionRight(left: TSchema, right: TUnion): TypeExtendsResult { return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Union(left: TUnion, right: TSchema): TypeExtendsResult { + function TUnion(left: TUnion, right: TSchema): TypeExtendsResult { return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Unknown // -------------------------------------------------------------------------- - function UnknownRight(left: TSchema, right: TUnknown) { + function TUnknownRight(left: TSchema, right: TUnknown) { return TypeExtendsResult.True } - function Unknown(left: TUnknown, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TString(right)) return StringRight(left, right) - if (TypeGuard.TNumber(right)) return NumberRight(left, right) - if (TypeGuard.TInteger(right)) return IntegerRight(left, right) - if (TypeGuard.TBoolean(right)) return BooleanRight(left, right) - if (TypeGuard.TArray(right)) return ArrayRight(left, right) - if (TypeGuard.TTuple(right)) return TupleRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) + function TUnknown(left: TUnknown, right: TSchema) { + if (TypeGuard.TNever(right)) return TNeverRight(left, right) + if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) + if (TypeGuard.TUnion(right)) return TUnionRight(left, right) + if (TypeGuard.TAny(right)) return TAnyRight(left, right) + if (TypeGuard.TString(right)) return TStringRight(left, right) + if (TypeGuard.TNumber(right)) return TNumberRight(left, right) + if (TypeGuard.TInteger(right)) return TIntegerRight(left, right) + if (TypeGuard.TBoolean(right)) return TBooleanRight(left, right) + if (TypeGuard.TArray(right)) return TArrayRight(left, right) + if (TypeGuard.TTuple(right)) return TTupleRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) return TypeGuard.TUnknown(right) ? TypeExtendsResult.True : TypeExtendsResult.False } // -------------------------------------------------------------------------- @@ -1926,43 +1974,45 @@ export namespace TypeExtends { if (TypeGuard.TUndefined(left)) return TypeExtendsResult.True return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : TypeExtendsResult.False } - function Void(left: TVoid, right: TSchema) { - if (TypeGuard.TIntersect(right)) return IntersectRight(left, right) - if (TypeGuard.TUnion(right)) return UnionRight(left, right) - if (TypeGuard.TUnknown(right)) return UnknownRight(left, right) - if (TypeGuard.TAny(right)) return AnyRight(left, right) - if (TypeGuard.TObject(right)) return ObjectRight(left, right) + function TVoid(left: TVoid, right: TSchema) { + if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) + if (TypeGuard.TUnion(right)) return TUnionRight(left, right) + if (TypeGuard.TUnknown(right)) return TUnknownRight(left, right) + if (TypeGuard.TAny(right)) return TAnyRight(left, right) + if (TypeGuard.TObject(right)) return TObjectRight(left, right) return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { // Resolvable Types - if (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) return TemplateLiteral(left, right) - if (TypeGuard.TNot(left) || TypeGuard.TNot(right)) return Not(left, right) + if (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) return TTemplateLiteral(left, right) + if (TypeGuard.TNot(left) || TypeGuard.TNot(right)) return TNot(left, right) // Standard Types - if (TypeGuard.TAny(left)) return Any(left, right) - if (TypeGuard.TArray(left)) return Array(left, right) - if (TypeGuard.TBigInt(left)) return BigInt(left, right) - if (TypeGuard.TBoolean(left)) return Boolean(left, right) - if (TypeGuard.TConstructor(left)) return Constructor(left, right) - if (TypeGuard.TDate(left)) return Date(left, right) - if (TypeGuard.TFunction(left)) return Function(left, right) - if (TypeGuard.TInteger(left)) return Integer(left, right) - if (TypeGuard.TIntersect(left)) return Intersect(left, right) - if (TypeGuard.TLiteral(left)) return Literal(left, right) - if (TypeGuard.TNever(left)) return Never(left, right) - if (TypeGuard.TNull(left)) return Null(left, right) - if (TypeGuard.TNumber(left)) return Number(left, right) - if (TypeGuard.TObject(left)) return Object(left, right) - if (TypeGuard.TRecord(left)) return Record(left, right) - if (TypeGuard.TString(left)) return String(left, right) - if (TypeGuard.TSymbol(left)) return Symbol(left, right) - if (TypeGuard.TTuple(left)) return Tuple(left, right) - if (TypeGuard.TPromise(left)) return Promise(left, right) - if (TypeGuard.TUint8Array(left)) return Uint8Array(left, right) - if (TypeGuard.TUndefined(left)) return Undefined(left, right) - if (TypeGuard.TUnion(left)) return Union(left, right) - if (TypeGuard.TUnknown(left)) return Unknown(left, right) - if (TypeGuard.TVoid(left)) return Void(left, right) + if (TypeGuard.TAny(left)) return TAny(left, right) + if (TypeGuard.TArray(left)) return TArray(left, right) + if (TypeGuard.TBigInt(left)) return TBigInt(left, right) + if (TypeGuard.TBoolean(left)) return TBoolean(left, right) + if (TypeGuard.TAsyncIterator(left)) return TAsyncIterator(left, right) + if (TypeGuard.TConstructor(left)) return TConstructor(left, right) + if (TypeGuard.TDate(left)) return TDate(left, right) + if (TypeGuard.TFunction(left)) return TFunction(left, right) + if (TypeGuard.TInteger(left)) return TInteger(left, right) + if (TypeGuard.TIntersect(left)) return TIntersect(left, right) + if (TypeGuard.TIterator(left)) return TIterator(left, right) + if (TypeGuard.TLiteral(left)) return TLiteral(left, right) + if (TypeGuard.TNever(left)) return TNever(left, right) + if (TypeGuard.TNull(left)) return TNull(left, right) + if (TypeGuard.TNumber(left)) return TNumber(left, right) + if (TypeGuard.TObject(left)) return TObject(left, right) + if (TypeGuard.TRecord(left)) return TRecord(left, right) + if (TypeGuard.TString(left)) return TString(left, right) + if (TypeGuard.TSymbol(left)) return TSymbol(left, right) + if (TypeGuard.TTuple(left)) return TTuple(left, right) + if (TypeGuard.TPromise(left)) return TPromise(left, right) + if (TypeGuard.TUint8Array(left)) return TUint8Array(left, right) + if (TypeGuard.TUndefined(left)) return TUndefined(left, right) + if (TypeGuard.TUnion(left)) return TUnion(left, right) + if (TypeGuard.TUnknown(left)) return TUnknown(left, right) + if (TypeGuard.TVoid(left)) return TVoid(left, right) throw Error(`TypeExtends: Unknown left type operand '${left[Kind]}'`) } export function Extends(left: TSchema, right: TSchema): TypeExtendsResult { @@ -1974,31 +2024,21 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- /** Specialized Clone for Types */ export namespace TypeClone { - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null - } - function IsArray(value: unknown): value is unknown[] { - return globalThis.Array.isArray(value) + function ObjectType(value: Record) { + const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) + const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) + return { ...clonedProperties, ...clonedSymbols } } - function Array(value: unknown[]) { + function ArrayType(value: unknown[]) { return (value as any).map((value: unknown) => Visit(value as any)) } - function Object(value: Record) { - const clonedProperties = globalThis.Object.getOwnPropertyNames(value).reduce((acc, key) => { - return { ...acc, [key]: Visit(value[key]) } - }, {}) - const clonedSymbols = globalThis.Object.getOwnPropertySymbols(value).reduce((acc, key) => { - return { ...acc, [key]: Visit(value[key as any]) } - }, {}) - return { ...clonedProperties, ...clonedSymbols } - } function Visit(value: unknown): any { - if (IsArray(value)) return Array(value) - if (IsObject(value)) return Object(value) + if (ValueGuard.IsArray(value)) return ArrayType(value) + if (ValueGuard.IsObject(value)) return ObjectType(value) return value } /** Clones a type. */ - export function Clone(schema: T, options: SchemaOptions): T { + export function Clone(schema: T, options: SchemaOptions = {}): T { return { ...Visit(schema), ...options } } } @@ -2008,7 +2048,7 @@ export namespace TypeClone { export namespace IndexedAccessor { function OptionalUnwrap(schema: TSchema[]): TSchema[] { return schema.map((schema) => { - const { [Modifier]: _, ...clone } = TypeClone.Clone(schema, {}) + const { [Optional]: _, ...clone } = TypeClone.Clone(schema) return clone }) } @@ -2031,36 +2071,36 @@ export namespace IndexedAccessor { if (schema[Kind] === 'Union') return ResolveUnion(schema as TUnion) return schema } - function Intersect(schema: TIntersect, key: string): TSchema { + function TIntersect(schema: TIntersect, key: string): TSchema { const resolved = schema.allOf.reduce((acc, schema) => { const indexed = Visit(schema, key) return indexed[Kind] === 'Never' ? acc : [...acc, indexed] }, [] as TSchema[]) return ResolveOptional(Type.Intersect(resolved)) } - function Union(schema: TUnion, key: string): TSchema { + function TUnion(schema: TUnion, key: string): TSchema { const resolved = schema.anyOf.map((schema) => Visit(schema, key)) return ResolveOptional(Type.Union(resolved)) } - function Object(schema: TObject, key: string): TSchema { + function TObject(schema: TObject, key: string): TSchema { const property = schema.properties[key] - return property === undefined ? Type.Never() : Type.Union([property]) + return ValueGuard.IsUndefined(property) ? Type.Never() : Type.Union([property]) } - function Tuple(schema: TTuple, key: string): TSchema { + function TTuple(schema: TTuple, key: string): TSchema { const items = schema.items - if (items === undefined) return Type.Never() + if (ValueGuard.IsUndefined(items)) return Type.Never() const element = items[key as any as number] // - if (element === undefined) return Type.Never() + if (ValueGuard.IsUndefined(element)) return Type.Never() return element } function Visit(schema: TSchema, key: string): TSchema { - if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, key) - if (schema[Kind] === 'Union') return Union(schema as TUnion, key) - if (schema[Kind] === 'Object') return Object(schema as TObject, key) - if (schema[Kind] === 'Tuple') return Tuple(schema as TTuple, key) + if (schema[Kind] === 'Intersect') return TIntersect(schema as TIntersect, key) + if (schema[Kind] === 'Union') return TUnion(schema as TUnion, key) + if (schema[Kind] === 'Object') return TObject(schema as TObject, key) + if (schema[Kind] === 'Tuple') return TTuple(schema as TTuple, key) return Type.Never() } - export function Resolve(schema: TSchema, keys: Key[], options: SchemaOptions = {}): TSchema { + export function Resolve(schema: TSchema, keys: TPropertyKey[], options: SchemaOptions = {}): TSchema { const resolved = keys.map((key) => Visit(schema, key.toString())) return ResolveOptional(Type.Union(resolved, options)) } @@ -2069,15 +2109,15 @@ export namespace IndexedAccessor { // ObjectMap // -------------------------------------------------------------------------- export namespace ObjectMap { - function Intersect(schema: TIntersect, callback: (object: TObject) => TObject) { + function TIntersect(schema: TIntersect, callback: (object: TObject) => TObject) { // prettier-ignore return Type.Intersect(schema.allOf.map((inner) => Visit(inner, callback)), { ...schema }) } - function Union(schema: TUnion, callback: (object: TObject) => TObject) { + function TUnion(schema: TUnion, callback: (object: TObject) => TObject) { // prettier-ignore return Type.Union(schema.anyOf.map((inner) => Visit(inner, callback)), { ...schema }) } - function Object(schema: TObject, callback: (object: TObject) => TObject) { + function TObject(schema: TObject, callback: (object: TObject) => TObject) { return callback(schema) } function Visit(schema: TSchema, callback: (object: TObject) => TObject): TSchema { @@ -2085,13 +2125,13 @@ export namespace ObjectMap { // prevent sub schema mapping as unregistered kinds will not pass TSchema checks. This is notable in the // case of TObject where unregistered property kinds cause the TObject check to fail. As mapping is only // used for composition, we use explicit checks instead. - if (schema[Kind] === 'Intersect') return Intersect(schema as TIntersect, callback) - if (schema[Kind] === 'Union') return Union(schema as TUnion, callback) - if (schema[Kind] === 'Object') return Object(schema as TObject, callback) + if (schema[Kind] === 'Intersect') return TIntersect(schema as TIntersect, callback) + if (schema[Kind] === 'Union') return TUnion(schema as TUnion, callback) + if (schema[Kind] === 'Object') return TObject(schema as TObject, callback) return schema } export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { - return { ...Visit(TypeClone.Clone(schema, {}), callback), ...options } as unknown as T + return { ...Visit(TypeClone.Clone(schema), callback), ...options } as unknown as T } } // -------------------------------------------------------------------------- @@ -2104,24 +2144,24 @@ export namespace KeyResolver { function UnwrapPattern(key: string) { return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key } - function Intersect(schema: TIntersect, options: KeyResolverOptions): string[] { + function TIntersect(schema: TIntersect, options: KeyResolverOptions): string[] { return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) } - function Union(schema: TUnion, options: KeyResolverOptions): string[] { + function TUnion(schema: TUnion, options: KeyResolverOptions): string[] { const sets = schema.anyOf.map((inner) => Visit(inner, options)) return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] } - function Object(schema: TObject, options: KeyResolverOptions): string[] { - return globalThis.Object.keys(schema.properties) + function TObject(schema: TObject, options: KeyResolverOptions): string[] { + return Object.getOwnPropertyNames(schema.properties) } - function Record(schema: TRecord, options: KeyResolverOptions): string[] { - return options.includePatterns ? globalThis.Object.keys(schema.patternProperties) : [] + function TRecord(schema: TRecord, options: KeyResolverOptions): string[] { + return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] } function Visit(schema: TSchema, options: KeyResolverOptions): string[] { - if (TypeGuard.TIntersect(schema)) return Intersect(schema, options) - if (TypeGuard.TUnion(schema)) return Union(schema, options) - if (TypeGuard.TObject(schema)) return Object(schema, options) - if (TypeGuard.TRecord(schema)) return Record(schema, options) + if (TypeGuard.TIntersect(schema)) return TIntersect(schema, options) + if (TypeGuard.TUnion(schema)) return TUnion(schema, options) + if (TypeGuard.TObject(schema)) return TObject(schema, options) + if (TypeGuard.TRecord(schema)) return TRecord(schema, options) return [] } /** Resolves an array of keys in this schema */ @@ -2141,7 +2181,7 @@ export namespace KeyResolver { export namespace KeyArrayResolver { /** Resolves an array of string[] keys from the given schema or array type. */ export function Resolve(schema: TSchema | string[]): string[] { - if (globalThis.Array.isArray(schema)) return schema + if (Array.isArray(schema)) return schema if (TypeGuard.TUnionLiteral(schema)) return schema.anyOf.map((schema) => schema.const.toString()) if (TypeGuard.TLiteral(schema)) return [schema.const as string] if (TypeGuard.TTemplateLiteral(schema)) { @@ -2156,10 +2196,10 @@ export namespace KeyArrayResolver { // UnionResolver // -------------------------------------------------------------------------- export namespace UnionResolver { - function* Union(union: TUnion): IterableIterator { + function* TUnion(union: TUnion): IterableIterator { for (const schema of union.anyOf) { if (schema[Kind] === 'Union') { - yield* Union(schema as TUnion) + yield* TUnion(schema as TUnion) } else { yield schema } @@ -2167,7 +2207,7 @@ export namespace UnionResolver { } /** Returns a resolved union with interior unions flattened */ export function Resolve(union: TUnion): TUnion { - return Type.Union([...Union(union)], { ...union }) + return Type.Union([...TUnion(union)], { ...union }) } } // -------------------------------------------------------------------------- @@ -2179,11 +2219,9 @@ export namespace TemplateLiteralPattern { } function Visit(schema: TSchema, acc: string): string { if (TypeGuard.TTemplateLiteral(schema)) { - const pattern = schema.pattern.slice(1, schema.pattern.length - 1) - return pattern + return schema.pattern.slice(1, schema.pattern.length - 1) } else if (TypeGuard.TUnion(schema)) { - const tokens = schema.anyOf.map((schema) => Visit(schema, acc)).join('|') - return `(${tokens})` + return `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` } else if (TypeGuard.TNumber(schema)) { return `${acc}${PatternNumber}` } else if (TypeGuard.TInteger(schema)) { @@ -2456,6 +2494,11 @@ export class TypeBuilder { protected Create(schema: Omit): T { return schema as any } + /** `[Utility]` Discards a property key from the given schema */ + protected Discard(schema: TSchema, key: PropertyKey): TSchema { + const { [key as any]: _, ...rest } = schema + return rest as TSchema + } /** `[Standard]` Omits compositing symbols from this schema */ public Strict(schema: T): T { return JSON.parse(JSON.stringify(schema)) @@ -2468,17 +2511,17 @@ export class StandardTypeBuilder extends TypeBuilder { // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ - /** `[Modifier]` Creates a Optional property */ - public Optional(schema: T): TOptional { - return { [Modifier]: 'Optional', ...TypeClone.Clone(schema, {}) } - } - /** `[Modifier]` Creates a ReadonlyOptional property */ - public ReadonlyOptional(schema: T): TReadonlyOptional { - return { [Modifier]: 'ReadonlyOptional', ...TypeClone.Clone(schema, {}) } + /** `[Standard]` Creates a Readonly and Optional property */ + public ReadonlyOptional(schema: T): TReadonly> { + return this.Readonly(this.Optional(schema)) } - /** `[Modifier]` Creates a Readonly object or property */ + /** `[Standard]` Creates a Readonly property */ public Readonly(schema: T): TReadonly { - return { [Modifier]: 'Readonly', ...schema } + return { ...TypeClone.Clone(schema), [Readonly]: 'Readonly' } + } + /** `[Standard]` Creates an Optional property */ + public Optional(schema: T): TOptional { + return { ...TypeClone.Clone(schema), [Optional]: 'Optional' } } // ------------------------------------------------------------------------ // Types @@ -2488,14 +2531,19 @@ export class StandardTypeBuilder extends TypeBuilder { return this.Create({ ...options, [Kind]: 'Any' }) } /** `[Standard]` Creates an Array type */ - public Array(items: T, options: ArrayOptions = {}): TArray { - return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Clone(items, {}) }) + public Array(schema: T, options: ArrayOptions = {}): TArray { + return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Clone(schema) }) } /** `[Standard]` Creates a Boolean type */ public Boolean(options: SchemaOptions = {}): TBoolean { return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) } - /** `[Standard]` Creates a Composite object type. */ + /** `[Standard]` Capitalize a LiteralString type */ + public Capitalize>(schema: T, options: SchemaOptions = {}): TLiteral> { + const [first, rest] = [schema.const.slice(0, 1), schema.const.slice(1)] + return Type.Literal(`${first.toUpperCase()}${rest}` as Capitalize, options) + } + /** `[Standard]` Creates a Composite object type */ public Composite(objects: [...T], options?: ObjectOptions): TComposite { const intersect: any = Type.Intersect(objects, {}) const keys = KeyResolver.ResolveKeys(intersect, { includePatterns: false }) @@ -2505,11 +2553,11 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { // prettier-ignore - const values = globalThis.Object.keys(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const anyOf = values.map((value) => (typeof value === 'string' ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) + const values = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] + const anyOf = values.map((value) => (ValueGuard.IsString(value) ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) return this.Create({ ...options, [Kind]: 'Union', anyOf }) } - /** `[Standard]` A conditional type expression that will return the true type if the left type extends the right */ + /** `[Standard]` Creates a Conditional type */ public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { switch (TypeExtends.Extends(left, right)) { case TypeExtendsResult.Union: @@ -2520,53 +2568,53 @@ export class StandardTypeBuilder extends TypeBuilder { return TypeClone.Clone(falseType, options) as unknown as TExtends } } - /** `[Standard]` Excludes from the left type any type that is not assignable to the right */ - public Exclude(left: L, right: R, options: SchemaOptions = {}): TExclude { - if (TypeGuard.TTemplateLiteral(left)) return this.Exclude(TemplateLiteralResolver.Resolve(left), right, options) as TExclude - if (TypeGuard.TTemplateLiteral(right)) return this.Exclude(left, TemplateLiteralResolver.Resolve(right), options) as any as TExclude - if (TypeGuard.TUnion(left)) { - const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) === TypeExtendsResult.False) + /** `[Standard]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + public Exclude(unionType: L, excludedMembers: R, options: SchemaOptions = {}): TExclude { + if (TypeGuard.TTemplateLiteral(unionType)) return this.Exclude(TemplateLiteralResolver.Resolve(unionType), excludedMembers, options) as TExclude + if (TypeGuard.TTemplateLiteral(excludedMembers)) return this.Exclude(unionType, TemplateLiteralResolver.Resolve(excludedMembers), options) as any as TExclude + if (TypeGuard.TUnion(unionType)) { + const narrowed = unionType.anyOf.filter((inner) => TypeExtends.Extends(inner, excludedMembers) === TypeExtendsResult.False) return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExclude } else { - return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? this.Never(options) : TypeClone.Clone(left, options)) as any + return (TypeExtends.Extends(unionType, excludedMembers) !== TypeExtendsResult.False ? this.Never(options) : TypeClone.Clone(unionType, options)) as any } } - /** `[Standard]` Extracts from the left type any type that is assignable to the right */ - public Extract(left: L, right: R, options: SchemaOptions = {}): TExtract { - if (TypeGuard.TTemplateLiteral(left)) return this.Extract(TemplateLiteralResolver.Resolve(left), right, options) as TExtract - if (TypeGuard.TTemplateLiteral(right)) return this.Extract(left, TemplateLiteralResolver.Resolve(right), options) as any as TExtract - if (TypeGuard.TUnion(left)) { - const narrowed = left.anyOf.filter((inner) => TypeExtends.Extends(inner, right) !== TypeExtendsResult.False) + /** `[Standard]` Constructs a type by extracting from type all union members that are assignable to union */ + public Extract(type: L, union: R, options: SchemaOptions = {}): TExtract { + if (TypeGuard.TTemplateLiteral(type)) return this.Extract(TemplateLiteralResolver.Resolve(type), union, options) as TExtract + if (TypeGuard.TTemplateLiteral(union)) return this.Extract(type, TemplateLiteralResolver.Resolve(union), options) as any as TExtract + if (TypeGuard.TUnion(type)) { + const narrowed = type.anyOf.filter((inner) => TypeExtends.Extends(inner, union) !== TypeExtendsResult.False) return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExtract } else { - return (TypeExtends.Extends(left, right) !== TypeExtendsResult.False ? TypeClone.Clone(left, options) : this.Never(options)) as any + return (TypeExtends.Extends(type, union) !== TypeExtendsResult.False ? TypeClone.Clone(type, options) : this.Never(options)) as any } } - /** `[Standard]` Returns indexed property types for the given keys */ + /** `[Standard]` Returns an Indexed property type for the given keys */ public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> - /** `[Standard]` Returns indexed property types for the given keys */ + /** `[Standard]` Returns an Indexed property type for the given keys */ public Index(schema: T, keys: K, options?: SchemaOptions): AssertType - /** `[Standard]` Returns indexed property types for the given keys */ + /** `[Standard]` Returns an Indexed property type for the given keys */ public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex - /** `[Standard]` Returns indexed property types for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ - public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns indexed property types for the given keys */ + /** `[Standard]` Returns an Indexed property type for the given keys */ + public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex + /** `[Standard]` Returns an Indexed property type for the given keys */ + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> + /** `[Standard]` Returns an Indexed property type for the given keys */ + public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> + /** `[Standard]` Returns an Indexed property type for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TSchema - /** `[Standard]` Returns indexed property types for the given keys */ + /** `[Standard]` Returns an Indexed property type for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { return TypeClone.Clone(schema.items, options) } else if (TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved)) { - const items = schema.items === undefined ? [] : schema.items - const cloned = items.map((schema) => TypeClone.Clone(schema, {})) + const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items + const cloned = items.map((schema) => TypeClone.Clone(schema)) return this.Union(cloned, options) } else { const keys = KeyArrayResolver.Resolve(unresolved) - const clone = TypeClone.Clone(schema, {}) + const clone = TypeClone.Clone(schema) return IndexedAccessor.Resolve(clone, keys, options) } } @@ -2574,18 +2622,19 @@ export class StandardTypeBuilder extends TypeBuilder { public Integer(options: NumericOptions = {}): TInteger { return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) } - /** `[Standard]` Creates a Intersect type */ + /** `[Standard]` Creates an Intersect type */ public Intersect(allOf: [], options?: SchemaOptions): TNever - /** `[Standard]` Creates a Intersect type */ + /** `[Standard]` Creates an Intersect type */ public Intersect(allOf: [...T], options?: SchemaOptions): T[0] - // /** `[Standard]` Creates a Intersect type */ + /** `[Standard]` Creates an Intersect type */ public Intersect(allOf: [...T], options?: IntersectOptions): TIntersect + /** `[Standard]` Creates an Intersect type */ public Intersect(allOf: TSchema[], options: IntersectOptions = {}) { if (allOf.length === 0) return Type.Never() if (allOf.length === 1) return TypeClone.Clone(allOf[0], options) const objects = allOf.every((schema) => TypeGuard.TObject(schema)) - const cloned = allOf.map((schema) => TypeClone.Clone(schema, {})) - const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) ? { unevaluatedProperties: TypeClone.Clone(options.unevaluatedProperties, {}) } : {} + const cloned = allOf.map((schema) => TypeClone.Clone(schema)) + const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) ? { unevaluatedProperties: TypeClone.Clone(options.unevaluatedProperties) } : {} if (options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects) { return this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) } else { @@ -2600,7 +2649,7 @@ export class StandardTypeBuilder extends TypeBuilder { if (pattern === PatternStringExact) return this.String(options) as unknown as TKeyOf throw Error('StandardTypeBuilder: Unable to resolve key type from Record key pattern') } else if (TypeGuard.TTuple(schema)) { - const items = schema.items === undefined ? [] : schema.items + const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items const literals = items.map((_, index) => Type.Literal(index)) return this.Union(literals, options) as unknown as TKeyOf } else if (TypeGuard.TArray(schema)) { @@ -2616,13 +2665,17 @@ export class StandardTypeBuilder extends TypeBuilder { public Literal(value: T, options: SchemaOptions = {}): TLiteral { return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) } + /** `[Standard]` Lowercase a LiteralString type */ + public Lowercase>(schema: T, options: SchemaOptions = {}): TLiteral> { + return Type.Literal(schema.const.toLowerCase() as Lowercase, options) + } /** `[Standard]` Creates a Never type */ public Never(options: SchemaOptions = {}): TNever { return this.Create({ ...options, [Kind]: 'Never', not: {} }) } /** `[Standard]` Creates a Not type */ - public Not(not: T, options?: SchemaOptions): TNot { - return this.Create({ ...options, [Kind]: 'Not', not }) + public Not(schema: T, options?: SchemaOptions): TNot { + return this.Create({ ...options, [Kind]: 'Not', not: TypeClone.Clone(schema) }) } /** `[Standard]` Creates a Null type */ public Null(options: SchemaOptions = {}): TNull { @@ -2634,81 +2687,75 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Creates an Object type */ public Object(properties: T, options: ObjectOptions = {}): TObject { - const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) - const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key]) || TypeGuard.TReadonlyOptional(properties[key])) + const propertyKeys = Object.getOwnPropertyNames(properties) + const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key])) const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) - const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Clone(options.additionalProperties, {}) } : {} - const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(properties[key], {}) }), {} as TProperties) + const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Clone(options.additionalProperties) } : {} + const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(properties[key]) }), {} as TProperties) if (requiredKeys.length > 0) { return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) } else { return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) } } - /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit - /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> - /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> - /** `[Standard]` Creates a mapped type whose keys are omitted from the given type */ + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit + /** `[Standard]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { - if (schema.required) { - schema.required = schema.required.filter((key: string) => !keys.includes(key as any)) - if (schema.required.length === 0) delete schema.required + return ObjectMap.Map(TypeClone.Clone(schema), (object) => { + if (ValueGuard.IsArray(object.required)) { + object.required = object.required.filter((key: string) => !keys.includes(key as any)) + if (object.required.length === 0) delete object.required } - for (const key of globalThis.Object.keys(schema.properties)) { - if (keys.includes(key as any)) delete schema.properties[key] + for (const key of Object.getOwnPropertyNames(object.properties)) { + if (keys.includes(key as any)) delete object.properties[key] } - return this.Create(schema) + return this.Create(object) }, options) } - /** `[Standard]` Creates a mapped type where all properties are Optional */ + /** `[Standard]` Constructs a type where all properties are optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { - function Apply(schema: TSchema) { - // prettier-ignore - switch (schema[Modifier]) { - case 'ReadonlyOptional': schema[Modifier] = 'ReadonlyOptional'; break; - case 'Readonly': schema[Modifier] = 'ReadonlyOptional'; break; - case 'Optional': schema[Modifier] = 'Optional'; break; - default: schema[Modifier] = 'Optional'; break; - } - } // prettier-ignore - return ObjectMap.Map>(TypeClone.Clone(schema, {}), (schema) => { - delete schema.required - globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) - return schema + return ObjectMap.Map(schema, (object) => { + const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { + return { ...acc, [key]: this.Optional(object.properties[key]) } + }, {} as TProperties) + return this.Object(properties, this.Discard(object, 'required') /* object used as options to retain other constraints */) }, options) } - /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick - /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> - /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick>(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> - /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick + /** `[Standard]` Constructs a type whose keys are picked from the given type */ public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(TypeClone.Clone(schema, {}), (schema) => { - if (schema.required) { - schema.required = schema.required.filter((key: any) => keys.includes(key)) - if (schema.required.length === 0) delete schema.required + return ObjectMap.Map(TypeClone.Clone(schema), (object) => { + if (ValueGuard.IsArray(object.required)) { + object.required = object.required.filter((key: any) => keys.includes(key)) + if (object.required.length === 0) delete object.required } - for (const key of globalThis.Object.keys(schema.properties)) { - if (!keys.includes(key as any)) delete schema.properties[key] + for (const key of Object.getOwnPropertyNames(object.properties)) { + if (!keys.includes(key as any)) delete object.properties[key] } - return this.Create(schema) + return this.Create(object) }, options) } /** `[Standard]` Creates a Record type */ @@ -2727,79 +2774,75 @@ export class StandardTypeBuilder extends TypeBuilder { const expression = TemplateLiteralParser.ParseExact(key.pattern) // prettier-ignore return TemplateLiteralFinite.Check(expression) - ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema, {}) }), {} as TProperties), options)) - : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema, {}) }}) + ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema) }), {} as TProperties), options)) + : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema) }}) } else if (TypeGuard.TUnion(key)) { const union = UnionResolver.Resolve(key) if (TypeGuard.TUnionLiteral(union)) { - const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema, {}) }), {} as TProperties) + const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema) }), {} as TProperties) return this.Object(properties, { ...options, [Hint]: 'Record' }) - } else throw Error('TypeBuilder: Record key of type union contains non-literal types') + } else throw Error('StandardTypeBuilder: Record key of type union contains non-literal types') } else if (TypeGuard.TLiteral(key)) { - if (typeof key.const === 'string' || typeof key.const === 'number') { - return this.Object({ [key.const]: TypeClone.Clone(schema, {}) }, options) - } else throw Error('TypeBuilder: Record key of type literal is not of type string or number') + if (ValueGuard.IsString(key.const) || ValueGuard.IsNumber(key.const)) { + return this.Object({ [key.const]: TypeClone.Clone(schema) }, options) + } else throw Error('StandardTypeBuilder: Record key of type literal is not of type string or number') } else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) { - const pattern = PatternNumberExact - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [PatternNumberExact]: TypeClone.Clone(schema) } }) } else if (TypeGuard.TString(key)) { - const pattern = key.pattern === undefined ? PatternStringExact : key.pattern - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema, {}) } }) + const pattern = ValueGuard.IsUndefined(key.pattern) ? PatternStringExact : key.pattern + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema) } }) } else { throw Error(`StandardTypeBuilder: Record key is an invalid type`) } } /** `[Standard]` Creates a Recursive type */ public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { - if (options.$id === undefined) (options as any).$id = `T${TypeOrdinal++}` + if (ValueGuard.IsUndefined(options.$id)) (options as any).$id = `T${TypeOrdinal++}` const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) thisType.$id = options.$id return this.Create({ ...options, [Hint]: 'Recursive', ...thisType } as any) } /** `[Standard]` Creates a Ref type. The referenced type must contain a $id */ - public Ref(schema: T, options: SchemaOptions = {}): TRef { - if (schema.$id === undefined) throw Error('StandardTypeBuilder.Ref: Target type must specify an $id') - return this.Create({ ...options, [Kind]: 'Ref', $ref: schema.$id! }) - } - /** `[Standard]` Creates a mapped type where all properties are Required */ + public Ref(schema: T, options?: SchemaOptions): TRef + /** `[Standard]` Creates a Ref type. */ + public Ref($ref: string, options?: SchemaOptions): TRef + /** `[Standard]` Creates a Ref type. */ + public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { + if (ValueGuard.IsString(unresolved)) return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved }) + if (ValueGuard.IsUndefined(unresolved.$id)) throw Error('StandardTypeBuilder.Ref: Target type must specify an $id') + return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved.$id! }) + } + /** `[Standard]` Constructs a type where all properties are required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { - function Apply(schema: TSchema) { - // prettier-ignore - switch (schema[Modifier]) { - case 'ReadonlyOptional': schema[Modifier] = 'Readonly'; break - case 'Readonly': schema[Modifier] = 'Readonly'; break - case 'Optional': delete schema[Modifier]; break - default: delete schema[Modifier]; break - } - } // prettier-ignore - return ObjectMap.Map>(TypeClone.Clone(schema, {}), (schema) => { - schema.required = globalThis.Object.keys(schema.properties) - globalThis.Object.keys(schema.properties).forEach(key => Apply(schema.properties[key])) - return schema + return ObjectMap.Map(schema, (object) => { + const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { + return { ...acc, [key]: this.Discard(object.properties[key], Optional) as TSchema } + }, {} as TProperties) + return this.Object(properties, object /* object used as options to retain other constraints */) }, options) } - /** `[Standard]` Returns a schema array which allows types to compose with the JavaScript spread operator */ + /** `[Standard]` Extracts the rest array from a Tuple */ public Rest(schema: T): TRest { if (TypeGuard.TTuple(schema)) { - if (schema.items === undefined) return [] as TSchema[] as TRest - return schema.items.map((schema) => TypeClone.Clone(schema, {})) as TRest + if (ValueGuard.IsUndefined(schema.items)) return [] as TSchema[] as TRest + return schema.items.map((schema) => TypeClone.Clone(schema)) as TRest } else { - return [TypeClone.Clone(schema, {})] as TRest + return [TypeClone.Clone(schema)] as TRest } } /** `[Standard]` Creates a String type */ public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } - /** `[Experimental]` Creates a template literal type from dsl string */ - public TemplateLiteral(dsl: T, options?: SchemaOptions): TTemplateLiteralDslParser - /** `[Standard]` Creates a template literal type */ + /** `[Standard]` Creates a TemplateLiteral type from template dsl string */ + public TemplateLiteral(templateDsl: T, options?: SchemaOptions): TTemplateLiteralDslParser + /** `[Standard]` Creates a TemplateLiteral type */ public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral - /** `[Standard]` Creates a template literal type */ + /** `[Standard]` Creates a TemplateLiteral type */ public TemplateLiteral(unresolved: unknown, options: SchemaOptions = {}) { // prettier-ignore - const pattern = (typeof unresolved === 'string') + const pattern = (ValueGuard.IsString(unresolved)) ? TemplateLiteralPattern.Create(TemplateLiteralDslParser.Parse(unresolved)) : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) @@ -2807,21 +2850,27 @@ export class StandardTypeBuilder extends TypeBuilder { /** `[Standard]` Creates a Tuple type */ public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { const [additionalItems, minItems, maxItems] = [false, items.length, items.length] - const clonedItems = items.map((item) => TypeClone.Clone(item, {})) + const clonedItems = items.map((item) => TypeClone.Clone(item)) // prettier-ignore const schema = (items.length > 0 ? { ...options, [Kind]: 'Tuple', type: 'array', items: clonedItems, additionalItems, minItems, maxItems } : { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any return this.Create(schema) } + /** `[Standard]` Uncapitalize a LiteralString type */ + public Uncapitalize>(schema: T, options: SchemaOptions = {}): TLiteral> { + const [first, rest] = [schema.const.slice(0, 1), schema.const.slice(1)] + return Type.Literal(`${first.toLocaleLowerCase()}${rest}` as Uncapitalize, options) + } /** `[Standard]` Creates a Union type */ public Union(anyOf: [], options?: SchemaOptions): TNever /** `[Standard]` Creates a Union type */ public Union(anyOf: [...T], options?: SchemaOptions): T[0] /** `[Standard]` Creates a Union type */ public Union(anyOf: [...T], options?: SchemaOptions): TUnion - /** `[Experimental]` Remaps a TemplateLiteral into a Union representation. This function is known to cause TS compiler crashes for finite templates with large generation counts. Use with caution. */ + /** `[Experimental]` Converts a TemplateLiteral into a Union */ public Union(template: T): TUnionTemplateLiteral + /** `[Standard]` Creates a Union type */ public Union(union: TSchema[] | TTemplateLiteral, options: SchemaOptions = {}) { if (TypeGuard.TTemplateLiteral(union)) { return TemplateLiteralResolver.Resolve(union) @@ -2829,7 +2878,7 @@ export class StandardTypeBuilder extends TypeBuilder { const anyOf = union if (anyOf.length === 0) return this.Never(options) if (anyOf.length === 1) return this.Create(TypeClone.Clone(anyOf[0], options)) - const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema, {})) + const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema)) return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) } } @@ -2837,18 +2886,41 @@ export class StandardTypeBuilder extends TypeBuilder { public Unknown(options: SchemaOptions = {}): TUnknown { return this.Create({ ...options, [Kind]: 'Unknown' }) } - /** `[Standard]` Creates a Unsafe type that infers for the generic argument */ + /** `[Standard]` Creates a Unsafe type that will infers as the generic argument T */ public Unsafe(options: UnsafeOptions = {}): TUnsafe { return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) } + /** `[Standard]` Uppercase a LiteralString type */ + public Uppercase>(schema: T, options: SchemaOptions = {}): TLiteral> { + return Type.Literal(schema.const.toUpperCase() as Uppercase, options) + } } // -------------------------------------------------------------------------- // ExtendedTypeBuilder // -------------------------------------------------------------------------- export class ExtendedTypeBuilder extends StandardTypeBuilder { + /** `[Extended]` Creates a AsyncIterator type */ + public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { + return this.Create({ ...options, [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: TypeClone.Clone(items) }) + } + /** `[Extended]` Constructs a type by recursively unwrapping Promise types */ + public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { + const AwaitedRest = (rest: TSchema[]): TSchema[] => { + if (rest.length === 0) return rest + const [L, ...R] = rest + return [this.Awaited(L), ...AwaitedRest(R)] + } + // prettier-ignore + return ( + TypeGuard.TIntersect(schema) ? Type.Intersect(AwaitedRest(schema.allOf)) : + TypeGuard.TUnion(schema) ? Type.Union(AwaitedRest(schema.anyOf)) : + TypeGuard.TPromise(schema) ? this.Awaited(schema.item) : + TypeClone.Clone(schema, options) + ) as TAwaited + } /** `[Extended]` Creates a BigInt type */ public BigInt(options: NumericOptions = {}): TBigInt { - return this.Create({ ...options, [Kind]: 'BigInt', type: 'null', typeOf: 'BigInt' }) + return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) } /** `[Extended]` Extracts the ConstructorParameters from the given Constructor type */ public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { @@ -2856,60 +2928,73 @@ export class ExtendedTypeBuilder extends StandardTypeBuilder { } /** `[Extended]` Creates a Constructor type */ public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - const clonedReturns = TypeClone.Clone(returns, {}) - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Constructor', type: 'object', instanceOf: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) + const clonedReturns = TypeClone.Clone(returns) + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter)) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters: clonedParameters, returns: clonedReturns }) } /** `[Extended]` Creates a Date type */ public Date(options: DateOptions = {}): TDate { - return this.Create({ ...options, [Kind]: 'Date', type: 'object', instanceOf: 'Date' }) + return this.Create({ ...options, [Kind]: 'Date', type: 'Date' }) } /** `[Extended]` Creates a Function type */ public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { const clonedReturns = TypeClone.Clone(returns, {}) - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter, {})) - return this.Create({ ...options, [Kind]: 'Function', type: 'object', instanceOf: 'Function', parameters: clonedParameters, returns: clonedReturns }) + const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter)) + return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters: clonedParameters, returns: clonedReturns }) } - /** `[Extended]` Extracts the InstanceType from the given Constructor */ + /** `[Extended]` Extracts the InstanceType from the given Constructor type */ public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { return TypeClone.Clone(schema.returns, options) } + /** `[Extended]` Creates an Iterator type */ + public Iterator(items: T, options: SchemaOptions = {}): TIterator { + return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Clone(items) }) + } /** `[Extended]` Extracts the Parameters from the given Function type */ public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { return this.Tuple(schema.parameters, { ...options }) } /** `[Extended]` Creates a Promise type */ public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'object', instanceOf: 'Promise', item: TypeClone.Clone(item, {}) }) - } - /** `[Extended]` Creates a regular expression type */ + return this.Create({ ...options, [Kind]: 'Promise', type: 'Promise', item: TypeClone.Clone(item) }) + } + /** `[Extended]` Creates a String type from a Regular Expression pattern */ + public RegExp(pattern: string, options?: SchemaOptions): TString + /** `[Extended]` Creates a String type from a Regular Expression */ + public RegExp(regex: RegExp, options?: SchemaOptions): TString + /** `[Extended]` Creates a String type */ + public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { + const pattern = ValueGuard.IsString(unresolved) ? unresolved : unresolved.source + return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern }) + } + /** + * @deprecated Use `Type.RegExp` + */ public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { - return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern: regex.source }) + return this.RegExp(regex, options) } - /** `[Extended]` Extracts the ReturnType from the given Function */ + /** `[Extended]` Extracts the ReturnType from the given Function type */ public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { return TypeClone.Clone(schema.returns, options) } /** `[Extended]` Creates a Symbol type */ public Symbol(options?: SchemaOptions): TSymbol { - return this.Create({ ...options, [Kind]: 'Symbol', type: 'null', typeOf: 'Symbol' }) + return this.Create({ ...options, [Kind]: 'Symbol', type: 'symbol' }) } /** `[Extended]` Creates a Undefined type */ public Undefined(options: SchemaOptions = {}): TUndefined { - return this.Create({ ...options, [Kind]: 'Undefined', type: 'null', typeOf: 'Undefined' }) + return this.Create({ ...options, [Kind]: 'Undefined', type: 'undefined' }) } /** `[Extended]` Creates a Uint8Array type */ public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'object', instanceOf: 'Uint8Array' }) + return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' }) } /** `[Extended]` Creates a Void type */ public Void(options: SchemaOptions = {}): TVoid { - return this.Create({ ...options, [Kind]: 'Void', type: 'null', typeOf: 'Void' }) + return this.Create({ ...options, [Kind]: 'Void', type: 'void' }) } } - -/** JSON Schema TypeBuilder with Static Resolution for TypeScript */ +/** JSON Schema Type Builder with Static Resolution for TypeScript */ export const StandardType = new StandardTypeBuilder() - -/** JSON Schema TypeBuilder with Static Resolution for TypeScript */ +/** JSON Schema Type Builder with Static Resolution for TypeScript */ export const Type = new ExtendedTypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast.ts index 1ff3db94c..5b284dcbc 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -27,13 +27,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { ValueCreate } from './create' -import { ValueCheck } from './check' -import { ValueClone } from './clone' +import * as ValueCreate from './create' +import * as ValueCheck from './check' +import * as ValueClone from './clone' +import * as ValueGuard from './guard' -// ---------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------- // Errors -// ---------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------- export class ValueCastReferenceTypeError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) @@ -61,21 +62,22 @@ export class ValueCastUnknownTypeError extends Error { } export class ValueCastDereferenceError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCast: Unable to dereference schema with $id '${schema.$ref}'`) + super(`ValueCast: Unable to dereference type with $id '${schema.$ref}'`) } } -// ---------------------------------------------------------------------------------------------- -// The following will score a schema against a value. For objects, the score is the tally of -// points awarded for each property of the value. Property points are (1.0 / propertyCount) -// to prevent large property counts biasing results. Properties that match literal values are -// maximally awarded as literals are typically used as union discriminator fields. -// ---------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------- +// The following will score a schema against a value. For objects, the score +// is the tally of points awarded for each property of the value. Property +// points are (1.0 / propertyCount) to prevent large property counts biasing +// results. Properties that match literal values are maximally awarded as +// literals are typically used as union discriminator fields. +// -------------------------------------------------------------------------- namespace UnionCastCreate { function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { - if (schema[Types.Kind] === 'Object' && typeof value === 'object' && value !== null) { + if (schema[Types.Kind] === 'Object' && typeof value === 'object' && !ValueGuard.IsNull(value)) { const object = schema as Types.TObject - const keys = Object.keys(value) - const entries = globalThis.Object.entries(object.properties) + const keys = Object.getOwnPropertyNames(value) + const entries = Object.entries(object.properties) const [point, max] = [1 / entries.length, entries.length] return entries.reduce((acc, [key, schema]) => { const literal = schema[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0 @@ -99,237 +101,237 @@ namespace UnionCastCreate { return select } export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { - if (union.default !== undefined) { + if ('default' in union) { return union.default } else { const schema = Select(union, references, value) - return ValueCast.Cast(schema, references, value) + return Cast(schema, references, value) } } } - -export namespace ValueCast { - // ---------------------------------------------------------------------------------------------- - // Guards - // ---------------------------------------------------------------------------------------------- - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) - } - function IsArray(value: unknown): value is unknown[] { - return typeof value === 'object' && globalThis.Array.isArray(value) - } - function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !isNaN(value) - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - // ---------------------------------------------------------------------------------------------- - // Cast - // ---------------------------------------------------------------------------------------------- - function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - const created = IsArray(value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...globalThis.Array.from({ length: schema.minItems - created.length }, () => null)] : created - const maximum = IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum - const casted = maximum.map((value: unknown) => Visit(schema.items, references, value)) - if (schema.uniqueItems !== true) return casted - const unique = [...new Set(casted)] - if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) - return unique - } - function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) - const required = new Set(schema.returns.required || []) - const result = function () {} - for (const [key, property] of globalThis.Object.entries(schema.returns.properties)) { - if (!required.has(key) && value.prototype[key] === undefined) continue - result.prototype[key] = Visit(property as Types.TSchema, references, value.prototype[key]) - } - return result - } - function Date(schema: Types.TDate, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any { - const created = ValueCreate.Create(schema, references) - const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value - return ValueCheck.Check(schema, references, mapped) ? mapped : ValueCreate.Create(schema, references) - } - function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Never(schema: Types.TNever, references: Types.TSchema[], value: any): any { - throw new ValueCastNeverTypeError(schema) - } - function Not(schema: Types.TNot, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Null(schema: Types.TNull, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Object(schema: Types.TObject, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return value - if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) - const required = new Set(schema.required || []) - const result = {} as Record - for (const [key, property] of globalThis.Object.entries(schema.properties)) { - if (!required.has(key) && value[key] === undefined) continue - result[key] = Visit(property, references, value[key]) - } - // additional schema properties - if (typeof schema.additionalProperties === 'object') { - const propertyNames = globalThis.Object.getOwnPropertyNames(schema.properties) - for (const propertyName of globalThis.Object.getOwnPropertyNames(value)) { - if (propertyNames.includes(propertyName)) continue - result[propertyName] = Visit(schema.additionalProperties, references, value[propertyName]) - } - } - return result - } - function Promise(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - if (value === null || typeof value !== 'object' || globalThis.Array.isArray(value) || value instanceof globalThis.Date) return ValueCreate.Create(schema, references) - const subschemaPropertyName = globalThis.Object.getOwnPropertyNames(schema.patternProperties)[0] - const subschema = schema.patternProperties[subschemaPropertyName] - const result = {} as Record - for (const [propKey, propValue] of globalThis.Object.entries(value)) { - result[propKey] = Visit(subschema, references, propValue) - } - return result - } - function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): any { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCastDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } - function String(schema: Types.TString, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) - } - function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function TemplateLiteral(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function This(schema: Types.TThis, references: Types.TSchema[], value: any): any { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCastDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } - function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - if (!globalThis.Array.isArray(value)) return ValueCreate.Create(schema, references) - if (schema.items === undefined) return [] - return schema.items.map((schema, index) => Visit(schema, references, value[index])) - } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : UnionCastCreate.Create(schema, references, value) - } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - } - export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Any': - return Any(schema_, references_, value) - case 'Array': - return Array(schema_, references_, value) - case 'BigInt': - return BigInt(schema_, references_, value) - case 'Boolean': - return Boolean(schema_, references_, value) - case 'Constructor': - return Constructor(schema_, references_, value) - case 'Date': - return Date(schema_, references_, value) - case 'Function': - return Function(schema_, references_, value) - case 'Integer': - return Integer(schema_, references_, value) - case 'Intersect': - return Intersect(schema_, references_, value) - case 'Literal': - return Literal(schema_, references_, value) - case 'Never': - return Never(schema_, references_, value) - case 'Not': - return Not(schema_, references_, value) - case 'Null': - return Null(schema_, references_, value) - case 'Number': - return Number(schema_, references_, value) - case 'Object': - return Object(schema_, references_, value) - case 'Promise': - return Promise(schema_, references_, value) - case 'Record': - return Record(schema_, references_, value) - case 'Ref': - return Ref(schema_, references_, value) - case 'String': - return String(schema_, references_, value) - case 'Symbol': - return Symbol(schema_, references_, value) - case 'TemplateLiteral': - return TemplateLiteral(schema_, references_, value) - case 'This': - return This(schema_, references_, value) - case 'Tuple': - return Tuple(schema_, references_, value) - case 'Undefined': - return Undefined(schema_, references_, value) - case 'Union': - return Union(schema_, references_, value) - case 'Uint8Array': - return Uint8Array(schema_, references_, value) - case 'Unknown': - return Unknown(schema_, references_, value) - case 'Void': - return Void(schema_, references_, value) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) - return UserDefined(schema_, references_, value) +// -------------------------------------------------------------------------- +// Cast +// -------------------------------------------------------------------------- +function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + const created = ValueGuard.IsArray(value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + const minimum = ValueGuard.IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created + const maximum = ValueGuard.IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum + const casted = maximum.map((value: unknown) => Visit(schema.items, references, value)) + if (schema.uniqueItems !== true) return casted + const unique = [...new Set(casted)] + if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) + return unique +} +function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) + const required = new Set(schema.returns.required || []) + const result = function () {} + for (const [key, property] of Object.entries(schema.returns.properties)) { + if (!required.has(key) && value.prototype[key] === undefined) continue + result.prototype[key] = Visit(property as Types.TSchema, references, value.prototype[key]) + } + return result +} +function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any { + const created = ValueCreate.Create(schema, references) + const mapped = ValueGuard.IsPlainObject(created) && ValueGuard.IsPlainObject(value) ? { ...(created as any), ...value } : value + return ValueCheck.Check(schema, references, mapped) ? mapped : ValueCreate.Create(schema, references) +} +function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): any { + throw new ValueCastNeverTypeError(schema) +} +function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return value + if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) + const required = new Set(schema.required || []) + const result = {} as Record + for (const [key, property] of Object.entries(schema.properties)) { + if (!required.has(key) && value[key] === undefined) continue + result[key] = Visit(property, references, value[key]) + } + // additional schema properties + if (typeof schema.additionalProperties === 'object') { + const propertyNames = Object.getOwnPropertyNames(schema.properties) + for (const propertyName of Object.getOwnPropertyNames(value)) { + if (propertyNames.includes(propertyName)) continue + result[propertyName] = Visit(schema.additionalProperties, references, value[propertyName]) } } - export function Cast(schema: T, references: Types.TSchema[], value: any): Types.Static { - return Visit(schema, references, ValueClone.Clone(value)) + return result +} +function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return ValueCreate.Create(schema, references) + const subschemaPropertyName = Object.getOwnPropertyNames(schema.patternProperties)[0] + const subschema = schema.patternProperties[subschemaPropertyName] + const result = {} as Record + for (const [propKey, propValue] of Object.entries(value)) { + result[propKey] = Visit(subschema, references, propValue) + } + return result +} +function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): any { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCastDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TString(schema: Types.TString, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) +} +function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TTemplateLiteral(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): any { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCastDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { + if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) + if (!ValueGuard.IsArray(value)) return ValueCreate.Create(schema, references) + if (schema.items === undefined) return [] + return schema.items.map((schema, index) => Visit(schema, references, value[index])) +} +function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : UnionCastCreate.Create(schema, references, value) +} +function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +} +function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + case 'Any': + return TAny(schema_, references_, value) + case 'Array': + return TArray(schema_, references_, value) + case 'AsyncIterator': + return TAsyncIterator(schema_, references_, value) + case 'BigInt': + return TBigInt(schema_, references_, value) + case 'Boolean': + return TBoolean(schema_, references_, value) + case 'Constructor': + return TConstructor(schema_, references_, value) + case 'Date': + return TDate(schema_, references_, value) + case 'Function': + return TFunction(schema_, references_, value) + case 'Integer': + return TInteger(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) + case 'Iterator': + return TIterator(schema_, references_, value) + case 'Literal': + return TLiteral(schema_, references_, value) + case 'Never': + return TNever(schema_, references_, value) + case 'Not': + return TNot(schema_, references_, value) + case 'Null': + return TNull(schema_, references_, value) + case 'Number': + return TNumber(schema_, references_, value) + case 'Object': + return TObject(schema_, references_, value) + case 'Promise': + return TPromise(schema_, references_, value) + case 'Record': + return TRecord(schema_, references_, value) + case 'Ref': + return TRef(schema_, references_, value) + case 'String': + return TString(schema_, references_, value) + case 'Symbol': + return TSymbol(schema_, references_, value) + case 'TemplateLiteral': + return TTemplateLiteral(schema_, references_, value) + case 'This': + return TThis(schema_, references_, value) + case 'Tuple': + return TTuple(schema_, references_, value) + case 'Undefined': + return TUndefined(schema_, references_, value) + case 'Union': + return TUnion(schema_, references_, value) + case 'Uint8Array': + return TUint8Array(schema_, references_, value) + case 'Unknown': + return TUnknown(schema_, references_, value) + case 'Void': + return TVoid(schema_, references_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) + return TKind(schema_, references_, value) } } +// -------------------------------------------------------------------------- +// Cast +// -------------------------------------------------------------------------- +/** Casts a value into a given type and references. The return value will retain as much information of the original value as possible. */ +export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static +/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ +export function Cast(schema: T, value: unknown): Types.Static +/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ +export function Cast(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/check.ts b/src/value/check.ts index a0458cb8f..624e08eda 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -26,13 +26,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '../typebox' import { TypeSystem } from '../system/index' -import { ValueHash } from './hash' +import * as Types from '../typebox' +import * as ValueGuard from './guard' +import * as ValueHash from './hash' -// ------------------------------------------------------------------------- +// -------------------------------------------------------------------------- // Errors -// ------------------------------------------------------------------------- +// -------------------------------------------------------------------------- export class ValueCheckUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super(`ValueCheck: ${schema[Types.Kind] ? `Unknown type '${schema[Types.Kind]}'` : 'Unknown type'}`) @@ -40,423 +41,445 @@ export class ValueCheckUnknownTypeError extends Error { } export class ValueCheckDereferenceError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCheck: Unable to dereference schema with $id '${schema.$ref}'`) + super(`ValueCheck: Unable to dereference type with $id '${schema.$ref}'`) } } -export namespace ValueCheck { - // ---------------------------------------------------------------------- - // Guards - // ---------------------------------------------------------------------- - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' +// -------------------------------------------------------------------------- +// TypeGuards +// -------------------------------------------------------------------------- +function IsAnyOrUnknown(schema: Types.TSchema) { + return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' +} +// -------------------------------------------------------------------------- +// Guards +// -------------------------------------------------------------------------- +function IsDefined(value: unknown): value is T { + return value !== undefined +} +// -------------------------------------------------------------------------- +// Policies +// -------------------------------------------------------------------------- +function IsExactOptionalProperty(value: Record, key: string) { + return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined +} +function IsObject(value: unknown): value is Record { + const isObject = ValueGuard.IsObject(value) + return TypeSystem.AllowArrayObjects ? isObject : isObject && !ValueGuard.IsArray(value) +} +function IsRecordObject(value: unknown): value is Record { + return IsObject(value) && !(value instanceof Date) && !(value instanceof Uint8Array) +} +function IsNumber(value: unknown): value is number { + const isNumber = ValueGuard.IsNumber(value) + return TypeSystem.AllowNaN ? isNumber : isNumber && Number.isFinite(value) +} +function IsVoid(value: unknown): value is void { + const isUndefined = ValueGuard.IsUndefined(value) + return TypeSystem.AllowVoidNull ? isUndefined || value === null : isUndefined +} +// -------------------------------------------------------------------------- +// Types +// -------------------------------------------------------------------------- +function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { + return true +} +function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { + if (!Array.isArray(value)) { + return false } - function IsInteger(value: unknown): value is number { - return globalThis.Number.isInteger(value) + if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { + return false } - function IsString(value: unknown): value is string { - return typeof value === 'string' + if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { + return false } - function IsDefined(value: unknown): value is T { - return value !== undefined + if (!value.every((value) => Visit(schema.items, references, value))) { + return false } - // ---------------------------------------------------------------------- - // SchemaGuards - // ---------------------------------------------------------------------- - function IsAnyOrUnknown(schema: Types.TSchema) { - return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' + // prettier-ignore + if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { + return false + } + // contains + if (!(IsDefined(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains))) { + return true // exit } - // ---------------------------------------------------------------------- - // Policies - // ---------------------------------------------------------------------- - function IsExactOptionalProperty(value: Record, key: string) { - return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() + const containsCount = value.reduce((acc, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0) + if (containsCount === 0) { + return false } - function IsObject(value: unknown): value is Record { - const result = typeof value === 'object' && value !== null - return TypeSystem.AllowArrayObjects ? result : result && !globalThis.Array.isArray(value) + if (IsNumber(schema.minContains) && containsCount < schema.minContains) { + return false } - function IsRecordObject(value: unknown): value is Record { - return IsObject(value) && !(value instanceof globalThis.Date) && !(value instanceof globalThis.Uint8Array) + if (IsNumber(schema.maxContains) && containsCount > schema.maxContains) { + return false } - function IsNumber(value: unknown): value is number { - const result = typeof value === 'number' - return TypeSystem.AllowNaN ? result : result && globalThis.Number.isFinite(value) + return true +} +function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): boolean { + return IsObject(value) && Symbol.asyncIterator in value +} +function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { + if (!ValueGuard.IsBigInt(value)) { + return false } - function IsVoid(value: unknown): value is void { - const result = value === undefined - return TypeSystem.AllowVoidNull ? result || value === null : result + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { + return false } - // ---------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------- - function Any(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { - return true + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + return false } - function Array(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { - if (!globalThis.Array.isArray(value)) { - return false - } - if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { - return false - } - if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { - return false - } - // prettier-ignore - if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Create(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { - return false - } - return value.every((value) => Visit(schema.items, references, value)) + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + return false } - function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { - if (!IsBigInt(value)) { - return false - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === globalThis.BigInt(0))) { - return false - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - return false - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - return false - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - return false - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - return false - } - return true + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + return false } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { - return typeof value === 'boolean' + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + return false } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { - return Visit(schema.returns, references, value.prototype) + return true +} +function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { + return typeof value === 'boolean' +} +function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { + return Visit(schema.returns, references, value.prototype) +} +function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { + if (!(value instanceof Date)) { + return false } - function Date(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { - if (!(value instanceof globalThis.Date)) { - return false - } - if (!IsNumber(value.getTime())) { - return false - } - if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { - return false - } - if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { - return false - } - if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { - return false - } - if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { - return false - } - return true + if (!IsNumber(value.getTime())) { + return false } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { - return typeof value === 'function' + if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { + return false } - function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { - if (!IsInteger(value)) { - return false - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - return false - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - return false - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - return false - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - return false - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - return false - } - return true + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + return false } - function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): boolean { - const check1 = schema.allOf.every((schema) => Visit(schema, references, value)) - if (schema.unevaluatedProperties === false) { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) - const check2 = globalThis.Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key)) - return check1 && check2 - } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) - const check2 = globalThis.Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) - return check1 && check2 - } else { - return check1 - } + if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { + return false } - function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { - return value === schema.const + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + return false } - function Never(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { + return true +} +function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { + return typeof value === 'function' +} +function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { + if (!ValueGuard.IsInteger(value)) { return false } - function Not(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { - return !Visit(schema.not, references, value) + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + return false } - function Null(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { - return value === null + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + return false } - function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { - if (!IsNumber(value)) { - return false - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - return false - } - if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - return false - } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - return false - } - if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - return false - } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - return false - } - return true + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + return false } - function Object(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { - if (!IsObject(value)) { - return false - } - if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - return false - } - if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - return false - } - const knownKeys = globalThis.Object.getOwnPropertyNames(schema.properties) - for (const knownKey of knownKeys) { - const property = schema.properties[knownKey] - if (schema.required && schema.required.includes(knownKey)) { - if (!Visit(property, references, value[knownKey])) { - return false - } - if ((Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) { - return false - } - } else { - if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { - return false - } - } - } - if (schema.additionalProperties === false) { - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - // optimization: value is valid if schemaKey length matches the valueKey length - if (schema.required && schema.required.length === knownKeys.length && valueKeys.length === knownKeys.length) { - return true - } else { - return valueKeys.every((valueKey) => knownKeys.includes(valueKey)) - } - } else if (typeof schema.additionalProperties === 'object') { - const valueKeys = globalThis.Object.getOwnPropertyNames(value) - return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as Types.TSchema, references, value[key])) - } else { - return true - } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + return false } - function Promise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { - return typeof value === 'object' && typeof value.then === 'function' + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + return false } - function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (!IsRecordObject(value)) { - return false - } - if (IsDefined(schema.minProperties) && !(globalThis.Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - return false - } - if (IsDefined(schema.maxProperties) && !(globalThis.Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - return false - } - const [patternKey, patternSchema] = globalThis.Object.entries(schema.patternProperties)[0] - const regex = new RegExp(patternKey) - return globalThis.Object.entries(value).every(([key, value]) => { - if (regex.test(key)) { - return Visit(patternSchema, references, value) + return true +} +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): boolean { + const check1 = schema.allOf.every((schema) => Visit(schema, references, value)) + if (schema.unevaluatedProperties === false) { + const keyPattern = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const check2 = Object.getOwnPropertyNames(value).every((key) => keyPattern.test(key)) + return check1 && check2 + } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { + const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const check2 = Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) + return check1 && check2 + } else { + return check1 + } +} +function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): boolean { + return IsObject(value) && Symbol.iterator in value +} +function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { + return value === schema.const +} +function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { + return false +} +function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { + return !Visit(schema.not, references, value) +} +function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { + return value === null +} +function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { + if (!IsNumber(value)) { + return false + } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + return false + } + if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { + return false + } + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + return false + } + if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { + return false + } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + return false + } + return true +} +function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { + if (!IsObject(value)) { + return false + } + if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + return false + } + if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + return false + } + const knownKeys = Object.getOwnPropertyNames(schema.properties) + for (const knownKey of knownKeys) { + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { + if (!Visit(property, references, value[knownKey])) { + return false } - if (typeof schema.additionalProperties === 'object') { - return Visit(schema.additionalProperties, references, value) + if ((Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) { + return false } - if (schema.additionalProperties === false) { + } else { + if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { return false } - return true - }) - } - function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCheckDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } - function String(schema: Types.TString, references: Types.TSchema[], value: any): boolean { - if (!IsString(value)) { - return false - } - if (IsDefined(schema.minLength)) { - if (!(value.length >= schema.minLength)) return false } - if (IsDefined(schema.maxLength)) { - if (!(value.length <= schema.maxLength)) return false - } - if (IsDefined(schema.pattern)) { - const regex = new RegExp(schema.pattern) - if (!regex.test(value)) return false - } - if (IsDefined(schema.format)) { - if (!Types.FormatRegistry.Has(schema.format)) return false - const func = Types.FormatRegistry.Get(schema.format)! - return func(value) - } - return true } - function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'symbol')) { - return false + if (schema.additionalProperties === false) { + const valueKeys = Object.getOwnPropertyNames(value) + // optimization: value is valid if schemaKey length matches the valueKey length + if (schema.required && schema.required.length === knownKeys.length && valueKeys.length === knownKeys.length) { + return true + } else { + return valueKeys.every((valueKey) => knownKeys.includes(valueKey)) } + } else if (typeof schema.additionalProperties === 'object') { + const valueKeys = Object.getOwnPropertyNames(value) + return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as Types.TSchema, references, value[key])) + } else { return true } - function TemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { - if (!IsString(value)) { - return false - } - return new RegExp(schema.pattern).test(value) +} +function TPromise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { + return typeof value === 'object' && typeof value.then === 'function' +} +function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { + if (!IsRecordObject(value)) { + return false + } + if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { + return false } - function This(schema: Types.TThis, references: Types.TSchema[], value: any): boolean { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCheckDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { + return false } - function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { - if (!globalThis.Array.isArray(value)) { - return false + const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] + const regex = new RegExp(patternKey) + return Object.entries(value).every(([key, value]) => { + if (regex.test(key)) { + return Visit(patternSchema, references, value) } - if (schema.items === undefined && !(value.length === 0)) { - return false + if (typeof schema.additionalProperties === 'object') { + return Visit(schema.additionalProperties, references, value) } - if (!(value.length === schema.maxItems)) { + if (schema.additionalProperties === false) { return false } - if (!schema.items) { - return true - } - for (let i = 0; i < schema.items.length; i++) { - if (!Visit(schema.items[i], references, value[i])) return false - } return true + }) +} +function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCheckDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TString(schema: Types.TString, references: Types.TSchema[], value: any): boolean { + if (!ValueGuard.IsString(value)) { + return false } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { - return value === undefined + if (IsDefined(schema.minLength)) { + if (!(value.length >= schema.minLength)) return false } - function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { - return schema.anyOf.some((inner) => Visit(inner, references, value)) + if (IsDefined(schema.maxLength)) { + if (!(value.length <= schema.maxLength)) return false } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { - if (!(value instanceof globalThis.Uint8Array)) { - return false - } - if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { - return false - } - if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { - return false - } - return true + if (IsDefined(schema.pattern)) { + const regex = new RegExp(schema.pattern) + if (!regex.test(value)) return false } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { + if (IsDefined(schema.format)) { + if (!Types.FormatRegistry.Has(schema.format)) return false + const func = Types.FormatRegistry.Get(schema.format)! + return func(value) + } + return true +} +function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): boolean { + if (!(typeof value === 'symbol')) { + return false + } + return true +} +function TTemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { + if (!ValueGuard.IsString(value)) { + return false + } + return new RegExp(schema.pattern).test(value) +} +function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): boolean { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCheckDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { + if (!ValueGuard.IsArray(value)) { + return false + } + if (schema.items === undefined && !(value.length === 0)) { + return false + } + if (!(value.length === schema.maxItems)) { + return false + } + if (!schema.items) { return true } - function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { - return IsVoid(value) - } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { - if (!Types.TypeRegistry.Has(schema[Types.Kind])) return false - const func = Types.TypeRegistry.Get(schema[Types.Kind])! - return func(schema, value) - } - function Visit(schema: T, references: Types.TSchema[], value: any): boolean { - const references_ = IsDefined(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema_[Types.Kind]) { - case 'Any': - return Any(schema_, references_, value) - case 'Array': - return Array(schema_, references_, value) - case 'BigInt': - return BigInt(schema_, references_, value) - case 'Boolean': - return Boolean(schema_, references_, value) - case 'Constructor': - return Constructor(schema_, references_, value) - case 'Date': - return Date(schema_, references_, value) - case 'Function': - return Function(schema_, references_, value) - case 'Integer': - return Integer(schema_, references_, value) - case 'Intersect': - return Intersect(schema_, references_, value) - case 'Literal': - return Literal(schema_, references_, value) - case 'Never': - return Never(schema_, references_, value) - case 'Not': - return Not(schema_, references_, value) - case 'Null': - return Null(schema_, references_, value) - case 'Number': - return Number(schema_, references_, value) - case 'Object': - return Object(schema_, references_, value) - case 'Promise': - return Promise(schema_, references_, value) - case 'Record': - return Record(schema_, references_, value) - case 'Ref': - return Ref(schema_, references_, value) - case 'String': - return String(schema_, references_, value) - case 'Symbol': - return Symbol(schema_, references_, value) - case 'TemplateLiteral': - return TemplateLiteral(schema_, references_, value) - case 'This': - return This(schema_, references_, value) - case 'Tuple': - return Tuple(schema_, references_, value) - case 'Undefined': - return Undefined(schema_, references_, value) - case 'Union': - return Union(schema_, references_, value) - case 'Uint8Array': - return Uint8Array(schema_, references_, value) - case 'Unknown': - return Unknown(schema_, references_, value) - case 'Void': - return Void(schema_, references_, value) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCheckUnknownTypeError(schema_) - return UserDefined(schema_, references_, value) - } + for (let i = 0; i < schema.items.length; i++) { + if (!Visit(schema.items[i], references, value[i])) return false } - // ------------------------------------------------------------------------- - // Check - // ------------------------------------------------------------------------- - export function Check(schema: T, references: Types.TSchema[], value: any): boolean { - return Visit(schema, references, value) + return true +} +function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { + return value === undefined +} +function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { + return schema.anyOf.some((inner) => Visit(inner, references, value)) +} +function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { + if (!(value instanceof Uint8Array)) { + return false + } + if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { + return false + } + if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { + return false } + return true +} +function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { + return true +} +function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { + return IsVoid(value) +} +function TKind(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { + if (!Types.TypeRegistry.Has(schema[Types.Kind])) return false + const func = Types.TypeRegistry.Get(schema[Types.Kind])! + return func(schema, value) +} +function Visit(schema: T, references: Types.TSchema[], value: any): boolean { + const references_ = IsDefined(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { + case 'Any': + return TAny(schema_, references_, value) + case 'Array': + return TArray(schema_, references_, value) + case 'AsyncIterator': + return TAsyncIterator(schema_, references_, value) + case 'BigInt': + return TBigInt(schema_, references_, value) + case 'Boolean': + return TBoolean(schema_, references_, value) + case 'Constructor': + return TConstructor(schema_, references_, value) + case 'Date': + return TDate(schema_, references_, value) + case 'Function': + return TFunction(schema_, references_, value) + case 'Integer': + return TInteger(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) + case 'Iterator': + return TIterator(schema_, references_, value) + case 'Literal': + return TLiteral(schema_, references_, value) + case 'Never': + return TNever(schema_, references_, value) + case 'Not': + return TNot(schema_, references_, value) + case 'Null': + return TNull(schema_, references_, value) + case 'Number': + return TNumber(schema_, references_, value) + case 'Object': + return TObject(schema_, references_, value) + case 'Promise': + return TPromise(schema_, references_, value) + case 'Record': + return TRecord(schema_, references_, value) + case 'Ref': + return TRef(schema_, references_, value) + case 'String': + return TString(schema_, references_, value) + case 'Symbol': + return TSymbol(schema_, references_, value) + case 'TemplateLiteral': + return TTemplateLiteral(schema_, references_, value) + case 'This': + return TThis(schema_, references_, value) + case 'Tuple': + return TTuple(schema_, references_, value) + case 'Undefined': + return TUndefined(schema_, references_, value) + case 'Union': + return TUnion(schema_, references_, value) + case 'Uint8Array': + return TUint8Array(schema_, references_, value) + case 'Unknown': + return TUnknown(schema_, references_, value) + case 'Void': + return TVoid(schema_, references_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCheckUnknownTypeError(schema_) + return TKind(schema_, references_, value) + } +} +// -------------------------------------------------------------------------- +// Check +// -------------------------------------------------------------------------- +/** Returns true if the value matches the given type. */ +export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static +/** Returns true if the value matches the given type. */ +export function Check(schema: T, value: unknown): value is Types.Static +/** Returns true if the value matches the given type. */ +export function Check(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) } diff --git a/src/value/clone.ts b/src/value/clone.ts index e8d3d2185..ef1ecb51d 100644 --- a/src/value/clone.ts +++ b/src/value/clone.ts @@ -26,38 +26,55 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' - -export namespace ValueClone { - function Array(value: ArrayType): any { - return value.map((element: any) => Clone(element)) - } - function Date(value: Date): any { - return new globalThis.Date(value.toISOString()) - } - function Object(value: ObjectType): any { - const keys = [...globalThis.Object.keys(value), ...globalThis.Object.getOwnPropertySymbols(value)] - return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) - } - function TypedArray(value: TypedArrayType): any { - return value.slice() - } - function Value(value: ValueType): any { - return value - } - export function Clone(value: T): T { - if (Is.Date(value)) { - return Date(value) - } else if (Is.Object(value)) { - return Object(value) - } else if (Is.Array(value)) { - return Array(value) - } else if (Is.TypedArray(value)) { - return TypedArray(value) - } else if (Is.Value(value)) { - return Value(value) - } else { - throw new Error('ValueClone: Unable to clone value') - } - } +import * as ValueGuard from './guard' + +// -------------------------------------------------------------------------- +// Clonable +// -------------------------------------------------------------------------- +function ObjectType(value: ValueGuard.ObjectType): any { + const keys = [...Object.getOwnPropertyNames(value), ...Object.getOwnPropertySymbols(value)] + return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) +} +function ArrayType(value: ValueGuard.ArrayType): any { + return value.map((element: any) => Clone(element)) +} +function TypedArrayType(value: ValueGuard.TypedArrayType): any { + return value.slice() +} +function DateType(value: Date): any { + return new Date(value.toISOString()) +} +function ValueType(value: ValueGuard.ValueType): any { + return value +} +// -------------------------------------------------------------------------- +// Non-Clonable +// -------------------------------------------------------------------------- +function AsyncIteratorType(value: AsyncIterableIterator): any { + return value +} +function IteratorType(value: IterableIterator): any { + return value +} +function FunctionType(value: Function): any { + return value +} +function PromiseType(value: Promise): any { + return value +} +// -------------------------------------------------------------------------- +// Clone +// -------------------------------------------------------------------------- +/** Returns a clone of the given value */ +export function Clone(value: T): T { + if (ValueGuard.IsArray(value)) return ArrayType(value) + if (ValueGuard.IsAsyncIterator(value)) return AsyncIteratorType(value) + if (ValueGuard.IsFunction(value)) return FunctionType(value) + if (ValueGuard.IsIterator(value)) return IteratorType(value) + if (ValueGuard.IsPromise(value)) return PromiseType(value) + if (ValueGuard.IsDate(value)) return DateType(value) + if (ValueGuard.IsPlainObject(value)) return ObjectType(value) + if (ValueGuard.IsTypedArray(value)) return TypedArrayType(value) + if (ValueGuard.IsValueType(value)) return ValueType(value) + throw new Error('ValueClone: Unable to clone value') } diff --git a/src/value/convert.ts b/src/value/convert.ts index 00f397b9b..c38be43e3 100644 --- a/src/value/convert.ts +++ b/src/value/convert.ts @@ -27,12 +27,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { ValueClone } from './clone' -import { ValueCheck } from './check' +import * as ValueClone from './clone' +import * as ValueCheck from './check' +import * as ValueGuard from './guard' -// ---------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------- // Errors -// ---------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------- export class ValueConvertUnknownTypeError extends Error { constructor(public readonly schema: Types.TSchema) { super('ValueConvert: Unknown type') @@ -40,320 +41,313 @@ export class ValueConvertUnknownTypeError extends Error { } export class ValueConvertDereferenceError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueConvert: Unable to dereference schema with $id '${schema.$ref}'`) + super(`ValueConvert: Unable to dereference type with $id '${schema.$ref}'`) } } -export namespace ValueConvert { - // ---------------------------------------------------------------------------------------------- - // Guards - // ---------------------------------------------------------------------------------------------- - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !globalThis.Array.isArray(value) - } - function IsArray(value: unknown): value is unknown[] { - return typeof value === 'object' && globalThis.Array.isArray(value) - } - function IsDate(value: unknown): value is Date { - return typeof value === 'object' && value instanceof globalThis.Date - } - function IsSymbol(value: unknown): value is symbol { - return typeof value === 'symbol' - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - function IsNumber(value: unknown): value is number { - return typeof value === 'number' && !isNaN(value) - } - function IsStringNumeric(value: unknown): value is string { - return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) - } - function IsValueToString(value: unknown): value is { toString: () => string } { - return IsBigInt(value) || IsBoolean(value) || IsNumber(value) - } - function IsValueTrue(value: unknown): value is true { - return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === globalThis.BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) - } - function IsValueFalse(value: unknown): value is true { - return value === false || (IsNumber(value) && value === 0) || (IsBigInt(value) && value === globalThis.BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0')) - } - function IsTimeStringWithTimeZone(value: unknown): value is string { - return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) - } - function IsTimeStringWithoutTimeZone(value: unknown): value is string { - return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) - } - function IsDateTimeStringWithTimeZone(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) - } - function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) - } - function IsDateString(value: unknown): value is string { - return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) - } - // ---------------------------------------------------------------------------------------------- - // Convert - // ---------------------------------------------------------------------------------------------- - function TryConvertLiteralString(value: unknown, target: string) { - const conversion = TryConvertString(value) - return conversion === target ? conversion : value - } - function TryConvertLiteralNumber(value: unknown, target: number) { - const conversion = TryConvertNumber(value) - return conversion === target ? conversion : value - } - function TryConvertLiteralBoolean(value: unknown, target: boolean) { - const conversion = TryConvertBoolean(value) - return conversion === target ? conversion : value - } - function TryConvertLiteral(schema: Types.TLiteral, value: unknown) { - if (typeof schema.const === 'string') { - return TryConvertLiteralString(value, schema.const) - } else if (typeof schema.const === 'number') { - return TryConvertLiteralNumber(value, schema.const) - } else if (typeof schema.const === 'boolean') { - return TryConvertLiteralBoolean(value, schema.const) - } else { - return ValueClone.Clone(value) - } - } - function TryConvertBoolean(value: unknown) { - return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value - } - function TryConvertBigInt(value: unknown) { - return IsStringNumeric(value) ? globalThis.BigInt(parseInt(value)) : IsNumber(value) ? globalThis.BigInt(value | 0) : IsValueFalse(value) ? 0 : IsValueTrue(value) ? 1 : value - } - function TryConvertString(value: unknown) { - return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value - } - function TryConvertNumber(value: unknown) { - return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value - } - function TryConvertInteger(value: unknown) { - return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value - } - function TryConvertNull(value: unknown) { - return IsString(value) && value.toLowerCase() === 'null' ? null : value - } - function TryConvertUndefined(value: unknown) { - return IsString(value) && value === 'undefined' ? undefined : value - } - function TryConvertDate(value: unknown) { - // note: this function may return an invalid dates for the regex tests - // above. Invalid dates will however be checked during the casting - // function and will return a epoch date if invalid. Consider better - // string parsing for the iso dates in future revisions. - return IsDate(value) - ? value - : IsNumber(value) - ? new globalThis.Date(value) - : IsValueTrue(value) - ? new globalThis.Date(1) - : IsValueFalse(value) - ? new globalThis.Date(0) - : IsStringNumeric(value) - ? new globalThis.Date(parseInt(value)) - : IsTimeStringWithoutTimeZone(value) - ? new globalThis.Date(`1970-01-01T${value}.000Z`) - : IsTimeStringWithTimeZone(value) - ? new globalThis.Date(`1970-01-01T${value}`) - : IsDateTimeStringWithoutTimeZone(value) - ? new globalThis.Date(`${value}.000Z`) - : IsDateTimeStringWithTimeZone(value) - ? new globalThis.Date(value) - : IsDateString(value) - ? new globalThis.Date(`${value}T00:00:00.000Z`) - : value - } - - // ---------------------------------------------------------------------------------------------- - // Cast - // ---------------------------------------------------------------------------------------------- - function Any(schema: Types.TAny, references: Types.TSchema[], value: any): any { - return value - } - function Array(schema: Types.TArray, references: Types.TSchema[], value: any): any { - if (IsArray(value)) { - return value.map((value) => Visit(schema.items, references, value)) - } - return value - } - function BigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): unknown { - return TryConvertBigInt(value) - } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { - return TryConvertBoolean(value) - } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): unknown { +// -------------------------------------------------------------------------- +// Conversions +// -------------------------------------------------------------------------- +function IsStringNumeric(value: unknown): value is string { + return ValueGuard.IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) +} +function IsValueToString(value: unknown): value is { toString: () => string } { + return ValueGuard.IsBigInt(value) || ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) +} +function IsValueTrue(value: unknown): value is true { + return value === true || (ValueGuard.IsNumber(value) && value === 1) || (ValueGuard.IsBigInt(value) && value === BigInt('1')) || (ValueGuard.IsString(value) && (value.toLowerCase() === 'true' || value === '1')) +} +function IsValueFalse(value: unknown): value is false { + return value === false || (ValueGuard.IsNumber(value) && value === 0) || (ValueGuard.IsBigInt(value) && value === BigInt('0')) || (ValueGuard.IsString(value) && (value.toLowerCase() === 'false' || value === '0')) +} +function IsTimeStringWithTimeZone(value: unknown): value is string { + return ValueGuard.IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) +} +function IsTimeStringWithoutTimeZone(value: unknown): value is string { + return ValueGuard.IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) +} +function IsDateTimeStringWithTimeZone(value: unknown): value is string { + return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) +} +function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { + return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) +} +function IsDateString(value: unknown): value is string { + return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) +} +// -------------------------------------------------------------------------- +// Convert +// -------------------------------------------------------------------------- +function TryConvertLiteralString(value: unknown, target: string) { + const conversion = TryConvertString(value) + return conversion === target ? conversion : value +} +function TryConvertLiteralNumber(value: unknown, target: number) { + const conversion = TryConvertNumber(value) + return conversion === target ? conversion : value +} +function TryConvertLiteralBoolean(value: unknown, target: boolean) { + const conversion = TryConvertBoolean(value) + return conversion === target ? conversion : value +} +function TryConvertLiteral(schema: Types.TLiteral, value: unknown) { + if (typeof schema.const === 'string') { + return TryConvertLiteralString(value, schema.const) + } else if (typeof schema.const === 'number') { + return TryConvertLiteralNumber(value, schema.const) + } else if (typeof schema.const === 'boolean') { + return TryConvertLiteralBoolean(value, schema.const) + } else { return ValueClone.Clone(value) } - function Date(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { - return TryConvertDate(value) - } - function Function(schema: Types.TFunction, references: Types.TSchema[], value: any): unknown { - return value - } - function Integer(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { - return TryConvertInteger(value) - } - function Intersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { - return value - } - function Literal(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { - return TryConvertLiteral(schema, value) - } - function Never(schema: Types.TNever, references: Types.TSchema[], value: any): unknown { - return value - } - function Null(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { - return TryConvertNull(value) - } - function Number(schema: Types.TNumber, references: Types.TSchema[], value: any): unknown { - return TryConvertNumber(value) - } - function Object(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { - if (IsObject(value)) - return globalThis.Object.keys(schema.properties).reduce((acc, key) => { - return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } - }, value) - return value - } - function Promise(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - return value - } - function Record(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { - const propertyKey = globalThis.Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[propertyKey] - const result = {} as Record - for (const [propKey, propValue] of globalThis.Object.entries(value)) { - result[propKey] = Visit(property, references, propValue) - } - return result - } - function Ref(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueConvertDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } - function String(schema: Types.TString, references: Types.TSchema[], value: any): unknown { - return TryConvertString(value) - } - function Symbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { - return value - } - function TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any) { - return value - } - function This(schema: Types.TThis, references: Types.TSchema[], value: any): unknown { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueConvertDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) - } - function Tuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { - if (IsArray(value) && schema.items !== undefined) { - return value.map((value, index) => { - return index < schema.items!.length ? Visit(schema.items![index], references, value) : value - }) - } - return value - } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { - return TryConvertUndefined(value) - } - function Union(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { - for (const subschema of schema.anyOf) { - const converted = Visit(subschema, references, value) - if (ValueCheck.Check(subschema, references, converted)) { - return converted - } - } - return value - } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): unknown { - return value - } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): unknown { - return value - } - function Void(schema: Types.TVoid, references: Types.TSchema[], value: any): unknown { - return value +} +function TryConvertBoolean(value: unknown) { + return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value +} +function TryConvertBigInt(value: unknown) { + return IsStringNumeric(value) ? BigInt(parseInt(value)) : ValueGuard.IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? 0 : IsValueTrue(value) ? 1 : value +} +function TryConvertString(value: unknown) { + return IsValueToString(value) ? value.toString() : ValueGuard.IsSymbol(value) && value.description !== undefined ? value.description.toString() : value +} +function TryConvertNumber(value: unknown) { + return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value +} +function TryConvertInteger(value: unknown) { + return IsStringNumeric(value) ? parseInt(value) : ValueGuard.IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value +} +function TryConvertNull(value: unknown) { + return ValueGuard.IsString(value) && value.toLowerCase() === 'null' ? null : value +} +function TryConvertUndefined(value: unknown) { + return ValueGuard.IsString(value) && value === 'undefined' ? undefined : value +} +function TryConvertDate(value: unknown) { + // -------------------------------------------------------------------------- + // note: this function may return an invalid dates for the regex tests + // above. Invalid dates will however be checked during the casting function + // and will return a epoch date if invalid. Consider better string parsing + // for the iso dates in future revisions. + // -------------------------------------------------------------------------- + return ValueGuard.IsDate(value) + ? value + : ValueGuard.IsNumber(value) + ? new Date(value) + : IsValueTrue(value) + ? new Date(1) + : IsValueFalse(value) + ? new Date(0) + : IsStringNumeric(value) + ? new Date(parseInt(value)) + : IsTimeStringWithoutTimeZone(value) + ? new Date(`1970-01-01T${value}.000Z`) + : IsTimeStringWithTimeZone(value) + ? new Date(`1970-01-01T${value}`) + : IsDateTimeStringWithoutTimeZone(value) + ? new Date(`${value}.000Z`) + : IsDateTimeStringWithTimeZone(value) + ? new Date(value) + : IsDateString(value) + ? new Date(`${value}T00:00:00.000Z`) + : value +} +// -------------------------------------------------------------------------- +// Cast +// -------------------------------------------------------------------------- +function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): any { + return value +} +function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { + if (ValueGuard.IsArray(value)) { + return value.map((value) => Visit(schema.items, references, value)) } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - return value + return value +} +function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): any { + return value +} +function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): unknown { + return TryConvertBigInt(value) +} +function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { + return TryConvertBoolean(value) +} +function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): unknown { + return ValueClone.Clone(value) +} +function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { + return TryConvertDate(value) +} +function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): unknown { + return value +} +function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { + return TryConvertInteger(value) +} +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { + return value +} +function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): unknown { + return value +} +function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { + return TryConvertLiteral(schema, value) +} +function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): unknown { + return value +} +function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { + return TryConvertNull(value) +} +function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): unknown { + return TryConvertNumber(value) +} +function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { + if (ValueGuard.IsObject(value)) + return Object.getOwnPropertyNames(schema.properties).reduce((acc, key) => { + return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } + }, value) + return value +} +function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + return value +} +function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { + const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[propertyKey] + const result = {} as Record + for (const [propKey, propValue] of Object.entries(value)) { + result[propKey] = Visit(property, references, propValue) + } + return result +} +function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueConvertDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TString(schema: Types.TString, references: Types.TSchema[], value: any): unknown { + return TryConvertString(value) +} +function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { + return value +} +function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any) { + return value +} +function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): unknown { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueConvertDereferenceError(schema) + const target = references[index] + return Visit(target, references, value) +} +function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { + if (ValueGuard.IsArray(value) && !ValueGuard.IsUndefined(schema.items)) { + return value.map((value, index) => { + return index < schema.items!.length ? Visit(schema.items![index], references, value) : value + }) } - export function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - const references_ = IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Any': - return Any(schema_, references_, value) - case 'Array': - return Array(schema_, references_, value) - case 'BigInt': - return BigInt(schema_, references_, value) - case 'Boolean': - return Boolean(schema_, references_, value) - case 'Constructor': - return Constructor(schema_, references_, value) - case 'Date': - return Date(schema_, references_, value) - case 'Function': - return Function(schema_, references_, value) - case 'Integer': - return Integer(schema_, references_, value) - case 'Intersect': - return Intersect(schema_, references_, value) - case 'Literal': - return Literal(schema_, references_, value) - case 'Never': - return Never(schema_, references_, value) - case 'Null': - return Null(schema_, references_, value) - case 'Number': - return Number(schema_, references_, value) - case 'Object': - return Object(schema_, references_, value) - case 'Promise': - return Promise(schema_, references_, value) - case 'Record': - return Record(schema_, references_, value) - case 'Ref': - return Ref(schema_, references_, value) - case 'String': - return String(schema_, references_, value) - case 'Symbol': - return Symbol(schema_, references_, value) - case 'TemplateLiteral': - return TemplateLiteral(schema_, references_, value) - case 'This': - return This(schema_, references_, value) - case 'Tuple': - return Tuple(schema_, references_, value) - case 'Undefined': - return Undefined(schema_, references_, value) - case 'Union': - return Union(schema_, references_, value) - case 'Uint8Array': - return Uint8Array(schema_, references_, value) - case 'Unknown': - return Unknown(schema_, references_, value) - case 'Void': - return Void(schema_, references_, value) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) - return UserDefined(schema_, references_, value) + return value +} +function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { + return TryConvertUndefined(value) +} +function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { + for (const subschema of schema.anyOf) { + const converted = Visit(subschema, references, value) + if (ValueCheck.Check(subschema, references, converted)) { + return converted } } - export function Convert(schema: T, references: Types.TSchema[], value: any): unknown { - return Visit(schema, references, ValueClone.Clone(value)) + return value +} +function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): unknown { + return value +} +function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): unknown { + return value +} +function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): unknown { + return value +} +function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + return value +} +function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { + const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + case 'Any': + return TAny(schema_, references_, value) + case 'Array': + return TArray(schema_, references_, value) + case 'AsyncIterator': + return TAsyncIterator(schema_, references_, value) + case 'BigInt': + return TBigInt(schema_, references_, value) + case 'Boolean': + return TBoolean(schema_, references_, value) + case 'Constructor': + return TConstructor(schema_, references_, value) + case 'Date': + return TDate(schema_, references_, value) + case 'Function': + return TFunction(schema_, references_, value) + case 'Integer': + return TInteger(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) + case 'Iterator': + return TIterator(schema_, references_, value) + case 'Literal': + return TLiteral(schema_, references_, value) + case 'Never': + return TNever(schema_, references_, value) + case 'Null': + return TNull(schema_, references_, value) + case 'Number': + return TNumber(schema_, references_, value) + case 'Object': + return TObject(schema_, references_, value) + case 'Promise': + return TPromise(schema_, references_, value) + case 'Record': + return TRecord(schema_, references_, value) + case 'Ref': + return TRef(schema_, references_, value) + case 'String': + return TString(schema_, references_, value) + case 'Symbol': + return TSymbol(schema_, references_, value) + case 'TemplateLiteral': + return TTemplateLiteral(schema_, references_, value) + case 'This': + return TThis(schema_, references_, value) + case 'Tuple': + return TTuple(schema_, references_, value) + case 'Undefined': + return TUndefined(schema_, references_, value) + case 'Union': + return TUnion(schema_, references_, value) + case 'Uint8Array': + return TUint8Array(schema_, references_, value) + case 'Unknown': + return TUnknown(schema_, references_, value) + case 'Void': + return TVoid(schema_, references_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) + return TKind(schema_, references_, value) } } +// -------------------------------------------------------------------------- +// Convert +// -------------------------------------------------------------------------- +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +export function Convert(schema: T, value: unknown): unknown +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +export function Convert(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/create.ts b/src/value/create.ts index 66973e3d4..a2387b6f1 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -27,7 +27,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import * as Types from '../typebox' -import { ValueCheck } from './check' +import * as ValueCheck from './check' +import * as ValueGuard from './guard' // -------------------------------------------------------------------------- // Errors @@ -59,7 +60,7 @@ export class ValueCreateTempateLiteralTypeError extends Error { } export class ValueCreateDereferenceError extends Error { constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCreate: Unable to dereference schema with $id '${schema.$ref}'`) + super(`ValueCreate: Unable to dereference type with $id '${schema.$ref}'`) } } export class ValueCreateRecursiveInstantiationError extends Error { @@ -68,372 +69,386 @@ export class ValueCreateRecursiveInstantiationError extends Error { } } // -------------------------------------------------------------------------- -// ValueCreate +// Types // -------------------------------------------------------------------------- -export namespace ValueCreate { - // -------------------------------------------------------- - // Guards - // -------------------------------------------------------- - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - // -------------------------------------------------------- - // Types - // -------------------------------------------------------- - function Any(schema: Types.TAny, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return {} - } +function TAny(schema: Types.TAny, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return {} } - function Array(schema: Types.TArray, references: Types.TSchema[]): any { - if (schema.uniqueItems === true && schema.default === undefined) { - throw new Error('ValueCreate.Array: Arrays with uniqueItems require a default value') - } else if ('default' in schema) { - return schema.default - } else if (schema.minItems !== undefined) { - return globalThis.Array.from({ length: schema.minItems }).map((item) => { - return Visit(schema.items, references) - }) - } else { - return [] - } +} +function TArray(schema: Types.TArray, references: Types.TSchema[]): any { + if (schema.uniqueItems === true && !ValueGuard.HasPropertyKey(schema, 'default')) { + throw new Error('ValueCreate.Array: Array with the uniqueItems constraint requires a default value') + } else if ('contains' in schema && !ValueGuard.HasPropertyKey(schema, 'default')) { + throw new Error('ValueCreate.Array: Array with the contains constraint requires a default value') + } else if ('default' in schema) { + return schema.default + } else if (schema.minItems !== undefined) { + return Array.from({ length: schema.minItems }).map((item) => { + return Visit(schema.items, references) + }) + } else { + return [] } - function BigInt(schema: Types.TBigInt, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return globalThis.BigInt(0) - } +} +function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]) { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return (async function* () {})() } - function Boolean(schema: Types.TBoolean, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return false - } +} +function TBigInt(schema: Types.TBigInt, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return BigInt(0) } - function Constructor(schema: Types.TConstructor, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - const value = Visit(schema.returns, references) as any - if (typeof value === 'object' && !globalThis.Array.isArray(value)) { - return class { - constructor() { - for (const [key, val] of globalThis.Object.entries(value)) { - const self = this as any - self[key] = val - } +} +function TBoolean(schema: Types.TBoolean, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return false + } +} +function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + const value = Visit(schema.returns, references) as any + if (typeof value === 'object' && !Array.isArray(value)) { + return class { + constructor() { + for (const [key, val] of Object.entries(value)) { + const self = this as any + self[key] = val } } - } else { - return class {} } - } - } - function Date(schema: Types.TDate, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else if (schema.minimumTimestamp !== undefined) { - return new globalThis.Date(schema.minimumTimestamp) - } else { - return new globalThis.Date(0) - } - } - function Function(schema: Types.TFunction, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default } else { - return () => Visit(schema.returns, references) + return class {} } } - function Integer(schema: Types.TInteger, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else if (schema.minimum !== undefined) { - return schema.minimum - } else { - return 0 - } +} +function TDate(schema: Types.TDate, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (schema.minimumTimestamp !== undefined) { + return new Date(schema.minimumTimestamp) + } else { + return new Date(0) } - function Intersect(schema: Types.TIntersect, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - // Note: The best we can do here is attempt to instance each sub type and apply through object assign. For non-object - // sub types, we just escape the assignment and just return the value. In the latter case, this is typically going to - // be a consequence of an illogical intersection. - const value = schema.allOf.reduce((acc, schema) => { - const next = Visit(schema, references) as any - return typeof next === 'object' ? { ...acc, ...next } : next - }, {}) - if (!ValueCheck.Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) - return value - } +} +function TFunction(schema: Types.TFunction, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return () => Visit(schema.returns, references) } - function Literal(schema: Types.TLiteral, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return schema.const - } +} +function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (schema.minimum !== undefined) { + return schema.minimum + } else { + return 0 } - function Never(schema: Types.TNever, references: Types.TSchema[]): any { - throw new ValueCreateNeverTypeError(schema) +} +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + // Note: The best we can do here is attempt to instance each sub type and apply through object assign. For non-object + // sub types, we just escape the assignment and just return the value. In the latter case, this is typically going to + // be a consequence of an illogical intersection. + const value = schema.allOf.reduce((acc, schema) => { + const next = Visit(schema, references) as any + return typeof next === 'object' ? { ...acc, ...next } : next + }, {}) + if (!ValueCheck.Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) + return value } - function Not(schema: Types.TNot, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - throw new ValueCreateNotTypeError(schema) - } +} +function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return (function* () {})() } - function Null(schema: Types.TNull, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return null - } +} +function TLiteral(schema: Types.TLiteral, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return schema.const } - function Number(schema: Types.TNumber, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else if (schema.minimum !== undefined) { - return schema.minimum - } else { - return 0 - } +} +function TNever(schema: Types.TNever, references: Types.TSchema[]): any { + throw new ValueCreateNeverTypeError(schema) +} +function TNot(schema: Types.TNot, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + throw new ValueCreateNotTypeError(schema) } - function Object(schema: Types.TObject, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - const required = new Set(schema.required) - return ( - schema.default || - globalThis.Object.entries(schema.properties).reduce((acc, [key, schema]) => { - return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc } - }, {}) - ) - } +} +function TNull(schema: Types.TNull, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return null } - function Promise(schema: Types.TPromise, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return globalThis.Promise.resolve(Visit(schema.item, references)) - } +} +function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (schema.minimum !== undefined) { + return schema.minimum + } else { + return 0 } - function Record(schema: Types.TRecord, references: Types.TSchema[]): any { - const [keyPattern, valueSchema] = globalThis.Object.entries(schema.patternProperties)[0] - if ('default' in schema) { - return schema.default - } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { - const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') - return propertyKeys.reduce((acc, key) => { - return { ...acc, [key]: Visit(valueSchema, references) } +} +function TObject(schema: Types.TObject, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + const required = new Set(schema.required) + return ( + schema.default || + Object.entries(schema.properties).reduce((acc, [key, schema]) => { + return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc } }, {}) - } else { - return {} - } + ) } - function Ref(schema: Types.TRef, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCreateDereferenceError(schema) - const target = references[index] - return Visit(target, references) - } +} +function TPromise(schema: Types.TPromise, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return Promise.resolve(Visit(schema.item, references)) } - function String(schema: Types.TString, references: Types.TSchema[]): any { - if (schema.pattern !== undefined) { - if (!('default' in schema)) { - throw new Error('ValueCreate.String: String types with patterns must specify a default value') - } else { - return schema.default - } - } else if (schema.format !== undefined) { - if (!('default' in schema)) { - throw new Error('ValueCreate.String: String types with formats must specify a default value') - } else { - return schema.default - } - } else { - if ('default' in schema) { - return schema.default - } else if (schema.minLength !== undefined) { - return globalThis.Array.from({ length: schema.minLength }) - .map(() => '.') - .join('') - } else { - return '' - } - } +} +function TRecord(schema: Types.TRecord, references: Types.TSchema[]): any { + const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0] + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { + const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') + return propertyKeys.reduce((acc, key) => { + return { ...acc, [key]: Visit(valueSchema, references) } + }, {}) + } else { + return {} } - function Symbol(schema: Types.TString, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else if ('value' in schema) { - return globalThis.Symbol.for(schema.value) - } else { - return globalThis.Symbol() - } +} +function TRef(schema: Types.TRef, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCreateDereferenceError(schema) + const target = references[index] + return Visit(target, references) } - function TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { - if ('default' in schema) { - return schema.default - } - const expression = Types.TemplateLiteralParser.ParseExact(schema.pattern) - if (!Types.TemplateLiteralFinite.Check(expression)) throw new ValueCreateTempateLiteralTypeError(schema) - const sequence = Types.TemplateLiteralGenerator.Generate(expression) - return sequence.next().value - } - function This(schema: Types.TThis, references: Types.TSchema[]): any { - if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) - if ('default' in schema) { - return schema.default +} +function TString(schema: Types.TString, references: Types.TSchema[]): any { + if (schema.pattern !== undefined) { + if (!ValueGuard.HasPropertyKey(schema, 'default')) { + throw new Error('ValueCreate.String: String types with patterns must specify a default value') } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCreateDereferenceError(schema) - const target = references[index] - return Visit(target, references) - } - } - function Tuple(schema: Types.TTuple, references: Types.TSchema[]): any { - if ('default' in schema) { return schema.default } - if (schema.items === undefined) { - return [] + } else if (schema.format !== undefined) { + if (!ValueGuard.HasPropertyKey(schema, 'default')) { + throw new Error('ValueCreate.String: String types with formats must specify a default value') } else { - return globalThis.Array.from({ length: schema.minItems }).map((_, index) => Visit((schema.items as any[])[index], references)) - } - } - function Undefined(schema: Types.TUndefined, references: Types.TSchema[]): any { - if ('default' in schema) { return schema.default - } else { - return undefined } - } - function Union(schema: Types.TUnion, references: Types.TSchema[]): any { - if ('default' in schema) { + } else { + if (ValueGuard.HasPropertyKey(schema, 'default')) { return schema.default - } else if (schema.anyOf.length === 0) { - throw new Error('ValueCreate.Union: Cannot create Union with zero variants') + } else if (schema.minLength !== undefined) { + return Array.from({ length: schema.minLength }) + .map(() => '.') + .join('') } else { - return Visit(schema.anyOf[0], references) + return '' } } - function Uint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else if (schema.minByteLength !== undefined) { - return new globalThis.Uint8Array(schema.minByteLength) - } else { - return new globalThis.Uint8Array(0) - } +} +function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if ('value' in schema) { + return Symbol.for(schema.value) + } else { + return Symbol() } - function Unknown(schema: Types.TUnknown, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return {} - } +} +function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } + const expression = Types.TemplateLiteralParser.ParseExact(schema.pattern) + if (!Types.TemplateLiteralFinite.Check(expression)) throw new ValueCreateTempateLiteralTypeError(schema) + const sequence = Types.TemplateLiteralGenerator.Generate(expression) + return sequence.next().value +} +function TThis(schema: Types.TThis, references: Types.TSchema[]): any { + if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + const index = references.findIndex((foreign) => foreign.$id === schema.$ref) + if (index === -1) throw new ValueCreateDereferenceError(schema) + const target = references[index] + return Visit(target, references) } - function Void(schema: Types.TVoid, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - return void 0 - } +} +function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default } - function UserDefined(schema: Types.TSchema, references: Types.TSchema[]): any { - if ('default' in schema) { - return schema.default - } else { - throw new Error('ValueCreate.UserDefined: User defined types must specify a default value') - } + if (schema.items === undefined) { + return [] + } else { + return Array.from({ length: schema.minItems }).map((_, index) => Visit((schema.items as any[])[index], references)) } - /** Creates a value from the given schema. If the schema specifies a default value, then that value is returned. */ - export function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { - const references_ = IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema_[Types.Kind]) { - case 'Any': - return Any(schema_, references_) - case 'Array': - return Array(schema_, references_) - case 'BigInt': - return BigInt(schema_, references_) - case 'Boolean': - return Boolean(schema_, references_) - case 'Constructor': - return Constructor(schema_, references_) - case 'Date': - return Date(schema_, references_) - case 'Function': - return Function(schema_, references_) - case 'Integer': - return Integer(schema_, references_) - case 'Intersect': - return Intersect(schema_, references_) - case 'Literal': - return Literal(schema_, references_) - case 'Never': - return Never(schema_, references_) - case 'Not': - return Not(schema_, references_) - case 'Null': - return Null(schema_, references_) - case 'Number': - return Number(schema_, references_) - case 'Object': - return Object(schema_, references_) - case 'Promise': - return Promise(schema_, references_) - case 'Record': - return Record(schema_, references_) - case 'Ref': - return Ref(schema_, references_) - case 'String': - return String(schema_, references_) - case 'Symbol': - return Symbol(schema_, references_) - case 'TemplateLiteral': - return TemplateLiteral(schema_, references_) - case 'This': - return This(schema_, references_) - case 'Tuple': - return Tuple(schema_, references_) - case 'Undefined': - return Undefined(schema_, references_) - case 'Union': - return Union(schema_, references_) - case 'Uint8Array': - return Uint8Array(schema_, references_) - case 'Unknown': - return Unknown(schema_, references_) - case 'Void': - return Void(schema_, references_) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCreateUnknownTypeError(schema_) - return UserDefined(schema_, references_) - } +} +function TUndefined(schema: Types.TUndefined, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return undefined } - // -------------------------------------------------------- - // State - // -------------------------------------------------------- - const recursiveMaxDepth = 512 - let recursiveDepth = 0 - - /** Creates a value from the given schema and references */ - export function Create(schema: T, references: Types.TSchema[]): Types.Static { - recursiveDepth = 0 - return Visit(schema, references) +} +function TUnion(schema: Types.TUnion, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (schema.anyOf.length === 0) { + throw new Error('ValueCreate.Union: Cannot create Union with zero variants') + } else { + return Visit(schema.anyOf[0], references) } } +function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else if (schema.minByteLength !== undefined) { + return new Uint8Array(schema.minByteLength) + } else { + return new Uint8Array(0) + } +} +function TUnknown(schema: Types.TUnknown, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return {} + } +} +function TVoid(schema: Types.TVoid, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + return void 0 + } +} +function TKind(schema: Types.TSchema, references: Types.TSchema[]): any { + if (ValueGuard.HasPropertyKey(schema, 'default')) { + return schema.default + } else { + throw new Error('ValueCreate: User defined types must specify a default value') + } +} +function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { + const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Types.Kind]) { + case 'Any': + return TAny(schema_, references_) + case 'Array': + return TArray(schema_, references_) + case 'AsyncIterator': + return TAsyncIterator(schema_, references_) + case 'BigInt': + return TBigInt(schema_, references_) + case 'Boolean': + return TBoolean(schema_, references_) + case 'Constructor': + return TConstructor(schema_, references_) + case 'Date': + return TDate(schema_, references_) + case 'Function': + return TFunction(schema_, references_) + case 'Integer': + return TInteger(schema_, references_) + case 'Intersect': + return TIntersect(schema_, references_) + case 'Iterator': + return TIterator(schema_, references_) + case 'Literal': + return TLiteral(schema_, references_) + case 'Never': + return TNever(schema_, references_) + case 'Not': + return TNot(schema_, references_) + case 'Null': + return TNull(schema_, references_) + case 'Number': + return TNumber(schema_, references_) + case 'Object': + return TObject(schema_, references_) + case 'Promise': + return TPromise(schema_, references_) + case 'Record': + return TRecord(schema_, references_) + case 'Ref': + return TRef(schema_, references_) + case 'String': + return TString(schema_, references_) + case 'Symbol': + return TSymbol(schema_, references_) + case 'TemplateLiteral': + return TTemplateLiteral(schema_, references_) + case 'This': + return TThis(schema_, references_) + case 'Tuple': + return TTuple(schema_, references_) + case 'Undefined': + return TUndefined(schema_, references_) + case 'Union': + return TUnion(schema_, references_) + case 'Uint8Array': + return TUint8Array(schema_, references_) + case 'Unknown': + return TUnknown(schema_, references_) + case 'Void': + return TVoid(schema_, references_) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCreateUnknownTypeError(schema_) + return TKind(schema_, references_) + } +} +// -------------------------------------------------------------------------- +// State +// -------------------------------------------------------------------------- +const recursiveMaxDepth = 512 +let recursiveDepth = 0 +// -------------------------------------------------------------------------- +// Create +// -------------------------------------------------------------------------- +/** Creates a value from the given schema and references */ +export function Create(schema: T, references: Types.TSchema[]): Types.Static +/** Creates a value from the given schema */ +export function Create(schema: T): Types.Static +/** Creates a value from the given schema */ +export function Create(...args: any[]) { + recursiveDepth = 0 + return args.length === 2 ? Visit(args[0], args[1]) : Visit(args[0], []) +} diff --git a/src/value/delta.ts b/src/value/delta.ts index 427a1d675..5a8074645 100644 --- a/src/value/delta.ts +++ b/src/value/delta.ts @@ -27,41 +27,35 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { Type, Static } from '../typebox' -import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' -import { ValueClone } from './clone' import { ValuePointer } from './pointer' +import * as ValueGuard from './guard' +import * as ValueClone from './clone' -// --------------------------------------------------------------------- -// Operations -// --------------------------------------------------------------------- - +// -------------------------------------------------------------------------- +// Commands +// -------------------------------------------------------------------------- export type Insert = Static export const Insert = Type.Object({ type: Type.Literal('insert'), path: Type.String(), value: Type.Unknown(), }) - export type Update = Static export const Update = Type.Object({ type: Type.Literal('update'), path: Type.String(), value: Type.Unknown(), }) - export type Delete = Static export const Delete = Type.Object({ type: Type.Literal('delete'), path: Type.String(), }) - export type Edit = Static export const Edit = Type.Union([Insert, Update, Delete]) - -// --------------------------------------------------------------------- +// -------------------------------------------------------------------------- // Errors -// --------------------------------------------------------------------- - +// -------------------------------------------------------------------------- export class ValueDeltaObjectWithSymbolKeyError extends Error { constructor(public readonly key: unknown) { super('ValueDelta: Cannot diff objects with symbol keys') @@ -72,136 +66,112 @@ export class ValueDeltaUnableToDiffUnknownValue extends Error { super('ValueDelta: Unable to create diff edits for unknown value') } } - -// --------------------------------------------------------------------- -// ValueDelta -// --------------------------------------------------------------------- - -export namespace ValueDelta { - // --------------------------------------------------------------------- - // Edits - // --------------------------------------------------------------------- - - function Update(path: string, value: unknown): Edit { - return { type: 'update', path, value } as any +// -------------------------------------------------------------------------- +// Command Factory +// -------------------------------------------------------------------------- +function CreateUpdate(path: string, value: unknown): Edit { + return { type: 'update', path, value } +} +function CreateInsert(path: string, value: unknown): Edit { + return { type: 'insert', path, value } +} +function CreateDelete(path: string): Edit { + return { type: 'delete', path } +} +// -------------------------------------------------------------------------- +// Diffing Generators +// -------------------------------------------------------------------------- +function* ObjectType(path: string, current: ValueGuard.ObjectType, next: unknown): IterableIterator { + if (!ValueGuard.IsPlainObject(next)) return yield CreateUpdate(path, next) + const currentKeys = [...Object.keys(current), ...Object.getOwnPropertySymbols(current)] + const nextKeys = [...Object.keys(next), ...Object.getOwnPropertySymbols(next)] + for (const key of currentKeys) { + if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (ValueGuard.IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${String(key)}`, undefined) } - - function Insert(path: string, value: unknown): Edit { - return { type: 'insert', path, value } as any + for (const key of nextKeys) { + if (ValueGuard.IsUndefined(current[key]) || ValueGuard.IsUndefined(next[key])) continue + if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + yield* Visit(`${path}/${String(key)}`, current[key], next[key]) } - - function Delete(path: string): Edit { - return { type: 'delete', path } as any + for (const key of nextKeys) { + if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (ValueGuard.IsUndefined(current[key])) yield CreateInsert(`${path}/${String(key)}`, next[key]) } - - // --------------------------------------------------------------------- - // Diff - // --------------------------------------------------------------------- - - function* Object(path: string, current: ObjectType, next: unknown): IterableIterator { - if (!Is.Object(next)) return yield Update(path, next) - const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] - const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] - for (const key of currentKeys) { - if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) - if (next[key] === undefined && nextKeys.includes(key)) yield Update(`${path}/${String(key)}`, undefined) - } - for (const key of nextKeys) { - if (current[key] === undefined || next[key] === undefined) continue - if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) - yield* Visit(`${path}/${String(key)}`, current[key], next[key]) - } - for (const key of nextKeys) { - if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) - if (current[key] === undefined) yield Insert(`${path}/${String(key)}`, next[key]) - } - for (const key of currentKeys.reverse()) { - if (typeof key === 'symbol') throw new ValueDeltaObjectWithSymbolKeyError(key) - if (next[key] === undefined && !nextKeys.includes(key)) yield Delete(`${path}/${String(key)}`) - } + for (const key of currentKeys.reverse()) { + if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (ValueGuard.IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${String(key)}`) } - - function* Array(path: string, current: ArrayType, next: unknown): IterableIterator { - if (!Is.Array(next)) return yield Update(path, next) - for (let i = 0; i < Math.min(current.length, next.length); i++) { - yield* Visit(`${path}/${i}`, current[i], next[i]) - } - for (let i = 0; i < next.length; i++) { - if (i < current.length) continue - yield Insert(`${path}/${i}`, next[i]) - } - for (let i = current.length - 1; i >= 0; i--) { - if (i < next.length) continue - yield Delete(`${path}/${i}`) - } - } - - function* TypedArray(path: string, current: TypedArrayType, next: unknown): IterableIterator { - if (!Is.TypedArray(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield Update(path, next) - for (let i = 0; i < Math.min(current.length, next.length); i++) { - yield* Visit(`${path}/${i}`, current[i], next[i]) - } +} +function* ArrayType(path: string, current: ValueGuard.ArrayType, next: unknown): IterableIterator { + if (!ValueGuard.IsArray(next)) return yield CreateUpdate(path, next) + for (let i = 0; i < Math.min(current.length, next.length); i++) { + yield* Visit(`${path}/${i}`, current[i], next[i]) } - - function* Value(path: string, current: ValueType, next: unknown): IterableIterator { - if (current === next) return - yield Update(path, next) + for (let i = 0; i < next.length; i++) { + if (i < current.length) continue + yield CreateInsert(`${path}/${i}`, next[i]) } - - function* Visit(path: string, current: unknown, next: unknown): IterableIterator { - if (Is.Object(current)) { - return yield* Object(path, current, next) - } else if (Is.Array(current)) { - return yield* Array(path, current, next) - } else if (Is.TypedArray(current)) { - return yield* TypedArray(path, current, next) - } else if (Is.Value(current)) { - return yield* Value(path, current, next) - } else { - throw new ValueDeltaUnableToDiffUnknownValue(current) - } + for (let i = current.length - 1; i >= 0; i--) { + if (i < next.length) continue + yield CreateDelete(`${path}/${i}`) } - - export function Diff(current: unknown, next: unknown): Edit[] { - return [...Visit('', current, next)] +} +function* TypedArrayType(path: string, current: ValueGuard.TypedArrayType, next: unknown): IterableIterator { + if (!ValueGuard.IsTypedArray(next) || current.length !== next.length || Object.getPrototypeOf(current).constructor.name !== Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next) + for (let i = 0; i < Math.min(current.length, next.length); i++) { + yield* Visit(`${path}/${i}`, current[i], next[i]) } - - // --------------------------------------------------------------------- - // Patch - // --------------------------------------------------------------------- - - function IsRootUpdate(edits: Edit[]): edits is [Update] { - return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' +} +function* ValueType(path: string, current: ValueGuard.ValueType, next: unknown): IterableIterator { + if (current === next) return + yield CreateUpdate(path, next) +} +function* Visit(path: string, current: unknown, next: unknown): IterableIterator { + if (ValueGuard.IsPlainObject(current)) return yield* ObjectType(path, current, next) + if (ValueGuard.IsArray(current)) return yield* ArrayType(path, current, next) + if (ValueGuard.IsTypedArray(current)) return yield* TypedArrayType(path, current, next) + if (ValueGuard.IsValueType(current)) return yield* ValueType(path, current, next) + throw new ValueDeltaUnableToDiffUnknownValue(current) +} +// --------------------------------------------------------------------- +// Diff +// --------------------------------------------------------------------- +export function Diff(current: unknown, next: unknown): Edit[] { + return [...Visit('', current, next)] +} +// --------------------------------------------------------------------- +// Patch +// --------------------------------------------------------------------- +function IsRootUpdate(edits: Edit[]): edits is [Update] { + return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' +} +function IsIdentity(edits: Edit[]) { + return edits.length === 0 +} +export function Patch(current: unknown, edits: Edit[]): T { + if (IsRootUpdate(edits)) { + return ValueClone.Clone(edits[0].value) as T } - - function IsIdentity(edits: Edit[]) { - return edits.length === 0 + if (IsIdentity(edits)) { + return ValueClone.Clone(current) as T } - - export function Patch(current: unknown, edits: Edit[]): T { - if (IsRootUpdate(edits)) { - return ValueClone.Clone(edits[0].value) as T - } - if (IsIdentity(edits)) { - return ValueClone.Clone(current) as T - } - const clone = ValueClone.Clone(current) - for (const edit of edits) { - switch (edit.type) { - case 'insert': { - ValuePointer.Set(clone, edit.path, edit.value) - break - } - case 'update': { - ValuePointer.Set(clone, edit.path, edit.value) - break - } - case 'delete': { - ValuePointer.Delete(clone, edit.path) - break - } + const clone = ValueClone.Clone(current) + for (const edit of edits) { + switch (edit.type) { + case 'insert': { + ValuePointer.Set(clone, edit.path, edit.value) + break + } + case 'update': { + ValuePointer.Set(clone, edit.path, edit.value) + break + } + case 'delete': { + ValuePointer.Delete(clone, edit.path) + break } } - return clone as T } + return clone as T } diff --git a/src/value/equal.ts b/src/value/equal.ts index ba3e2424c..48a5f9e5b 100644 --- a/src/value/equal.ts +++ b/src/value/equal.ts @@ -26,43 +26,41 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Is, ObjectType, ArrayType, TypedArrayType, ValueType } from './is' +import * as ValueGuard from './guard' -export namespace ValueEqual { - function Object(left: ObjectType, right: unknown): boolean { - if (!Is.Object(right)) return false - const leftKeys = [...globalThis.Object.keys(left), ...globalThis.Object.getOwnPropertySymbols(left)] - const rightKeys = [...globalThis.Object.keys(right), ...globalThis.Object.getOwnPropertySymbols(right)] - if (leftKeys.length !== rightKeys.length) return false - return leftKeys.every((key) => Equal(left[key], right[key])) - } - function Date(left: Date, right: unknown): any { - return Is.Date(right) && left.getTime() === right.getTime() - } - function Array(left: ArrayType, right: unknown): any { - if (!Is.Array(right) || left.length !== right.length) return false - return left.every((value, index) => Equal(value, right[index])) - } - function TypedArray(left: TypedArrayType, right: unknown): any { - if (!Is.TypedArray(right) || left.length !== right.length || globalThis.Object.getPrototypeOf(left).constructor.name !== globalThis.Object.getPrototypeOf(right).constructor.name) return false - return left.every((value, index) => Equal(value, right[index])) - } - function Value(left: ValueType, right: unknown): any { - return left === right - } - export function Equal(left: T, right: unknown): right is T { - if (Is.Object(left)) { - return Object(left, right) - } else if (Is.Date(left)) { - return Date(left, right) - } else if (Is.TypedArray(left)) { - return TypedArray(left, right) - } else if (Is.Array(left)) { - return Array(left, right) - } else if (Is.Value(left)) { - return Value(left, right) - } else { - throw new Error('ValueEquals: Unable to compare value') - } - } +// -------------------------------------------------------------------------- +// Equality Checks +// -------------------------------------------------------------------------- +function ObjectType(left: ValueGuard.ObjectType, right: unknown): boolean { + if (!ValueGuard.IsPlainObject(right)) return false + const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] + const rightKeys = [...Object.keys(right), ...Object.getOwnPropertySymbols(right)] + if (leftKeys.length !== rightKeys.length) return false + return leftKeys.every((key) => Equal(left[key], right[key])) +} +function DateType(left: Date, right: unknown): any { + return ValueGuard.IsDate(right) && left.getTime() === right.getTime() +} +function ArrayType(left: ValueGuard.ArrayType, right: unknown): any { + if (!ValueGuard.IsArray(right) || left.length !== right.length) return false + return left.every((value, index) => Equal(value, right[index])) +} +function TypedArrayType(left: ValueGuard.TypedArrayType, right: unknown): any { + if (!ValueGuard.IsTypedArray(right) || left.length !== right.length || Object.getPrototypeOf(left).constructor.name !== Object.getPrototypeOf(right).constructor.name) return false + return left.every((value, index) => Equal(value, right[index])) +} +function ValueType(left: ValueGuard.ValueType, right: unknown): any { + return left === right +} +// -------------------------------------------------------------------------- +// Equal +// -------------------------------------------------------------------------- +/** Returns true if the left value deep-equals the right */ +export function Equal(left: T, right: unknown): right is T { + if (ValueGuard.IsPlainObject(left)) return ObjectType(left, right) + if (ValueGuard.IsDate(left)) return DateType(left, right) + if (ValueGuard.IsTypedArray(left)) return TypedArrayType(left, right) + if (ValueGuard.IsArray(left)) return ArrayType(left, right) + if (ValueGuard.IsValueType(left)) return ValueType(left, right) + throw new Error('ValueEquals: Unable to compare value') } diff --git a/src/value/guard.ts b/src/value/guard.ts new file mode 100644 index 000000000..ce47fb7cd --- /dev/null +++ b/src/value/guard.ts @@ -0,0 +1,145 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// -------------------------------------------------------------------------- +// Types +// -------------------------------------------------------------------------- +export type ObjectType = Record +export type ArrayType = unknown[] +export type ValueType = null | undefined | symbol | bigint | number | boolean | string +// prettier-ignore +export type TypedArrayType = + | Int8Array + | Uint8Array + | Uint8ClampedArray + | Int16Array + | Uint16Array + | Int32Array + | Uint32Array + | Float32Array + | Float64Array + | BigInt64Array + | BigUint64Array +// -------------------------------------------------------------------------- +// Iterators +// -------------------------------------------------------------------------- +/** Returns true if this value is an async iterator */ +export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator { + return IsObject(value) && Symbol.asyncIterator in value +} +/** Returns true if this value is an iterator */ +export function IsIterator(value: unknown): value is IterableIterator { + return IsObject(value) && Symbol.iterator in value +} +// -------------------------------------------------------------------------- +// Nominal +// -------------------------------------------------------------------------- +/** Returns true if this value is a typed array */ +export function IsTypedArray(value: unknown): value is TypedArrayType { + return ArrayBuffer.isView(value) +} +/** Returns true if this value is a Promise */ +export function IsPromise(value: unknown): value is Promise { + return value instanceof Promise +} +/** Returns true if the value is a Uint8Array */ +export function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof Uint8Array +} +/** Returns true if this value is a Date */ +export function IsDate(value: unknown): value is Date { + return value instanceof Date +} +// -------------------------------------------------------------------------- +// Standard +// -------------------------------------------------------------------------- +/** Returns true if this value has this property key */ +export function HasPropertyKey(value: Record, key: K): value is ObjectType & Record { + return key in value +} +/** Returns true if this object is not an instance of any other type */ +export function IsPlainObject(value: unknown): value is ObjectType { + return IsObject(value) && IsFunction(value.constructor) && value.constructor.name === 'Object' +} +/** Returns true of this value is an object type */ +export function IsObject(value: unknown): value is ObjectType { + return value !== null && typeof value === 'object' +} +/** Returns true if this value is an array, but not a typed array */ +export function IsArray(value: unknown): value is ArrayType { + return Array.isArray(value) && !ArrayBuffer.isView(value) +} +/** Returns true if this value is an undefined */ +export function IsUndefined(value: unknown): value is undefined { + return value === undefined +} +/** Returns true if this value is an null */ +export function IsNull(value: unknown): value is null { + return value === null +} +/** Returns true if this value is an boolean */ +export function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' +} +/** Returns true if this value is an number */ +export function IsNumber(value: unknown): value is number { + return typeof value === 'number' +} +/** Returns true if this value is an integer */ +export function IsInteger(value: unknown): value is number { + return IsNumber(value) && Number.isInteger(value) +} +/** Returns true if this value is bigint */ +export function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' +} +/** Returns true if this value is string */ +export function IsString(value: unknown): value is string { + return typeof value === 'string' +} +/** Returns true if this value is a function */ +export function IsFunction(value: unknown): value is Function { + return typeof value === 'function' +} +/** Returns true if this value is a symbol */ +export function IsSymbol(value: unknown): value is symbol { + return typeof value === 'symbol' +} +/** Returns true if this value is a value type such as number, string, boolean */ +export function IsValueType(value: unknown): value is ValueType { + // prettier-ignore + return ( + IsBigInt(value) || + IsBoolean(value) || + IsNull(value) || + IsNumber(value) || + IsString(value) || + IsSymbol(value) || + IsUndefined(value) + ) +} diff --git a/src/value/hash.ts b/src/value/hash.ts index 5231ecce3..eeafffe07 100644 --- a/src/value/hash.ts +++ b/src/value/hash.ts @@ -26,166 +26,125 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import * as ValueGuard from './guard' + +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- export class ValueHashError extends Error { constructor(public readonly value: unknown) { super(`Hash: Unable to hash value`) } } -export namespace ValueHash { - enum ByteMarker { - Undefined, - Null, - Boolean, - Number, - String, - Object, - Array, - Date, - Uint8Array, - Symbol, - BigInt, - } - - // ---------------------------------------------------- - // State - // ---------------------------------------------------- - let Hash = globalThis.BigInt('14695981039346656037') - const [Prime, Size] = [globalThis.BigInt('1099511628211'), globalThis.BigInt('2') ** globalThis.BigInt('64')] - const Bytes = globalThis.Array.from({ length: 256 }).map((_, i) => globalThis.BigInt(i)) - const F64 = new globalThis.Float64Array(1) - const F64In = new globalThis.DataView(F64.buffer) - const F64Out = new globalThis.Uint8Array(F64.buffer) - // ---------------------------------------------------- - // Guards - // ---------------------------------------------------- - function IsDate(value: unknown): value is Date { - return value instanceof globalThis.Date - } - function IsUint8Array(value: unknown): value is Uint8Array { - return value instanceof globalThis.Uint8Array - } - function IsArray(value: unknown): value is Array { - return globalThis.Array.isArray(value) - } - function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - function IsNull(value: unknown): value is null { - return value === null - } - function IsNumber(value: unknown): value is number { - return typeof value === 'number' - } - function IsSymbol(value: unknown): value is symbol { - return typeof value === 'symbol' - } - function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null && !IsArray(value) && !IsDate(value) && !IsUint8Array(value) - } - function IsString(value: unknown): value is string { - return typeof value === 'string' - } - function IsUndefined(value: unknown): value is undefined { - return value === undefined - } - // ---------------------------------------------------- - // Encoding - // ---------------------------------------------------- - function Array(value: Array) { - FNV1A64(ByteMarker.Array) - for (const item of value) { - Visit(item) - } - } - function Boolean(value: boolean) { - FNV1A64(ByteMarker.Boolean) - FNV1A64(value ? 1 : 0) - } - function BigInt(value: bigint) { - FNV1A64(ByteMarker.BigInt) - F64In.setBigInt64(0, value) - for (const byte of F64Out) { - FNV1A64(byte) - } - } - function Date(value: Date) { - FNV1A64(ByteMarker.Date) - Visit(value.getTime()) - } - function Null(value: null) { - FNV1A64(ByteMarker.Null) - } - function Number(value: number) { - FNV1A64(ByteMarker.Number) - F64In.setFloat64(0, value) - for (const byte of F64Out) { - FNV1A64(byte) - } - } - function Object(value: Record) { - FNV1A64(ByteMarker.Object) - for (const key of globalThis.Object.keys(value).sort()) { - Visit(key) - Visit(value[key]) - } - } - function String(value: string) { - FNV1A64(ByteMarker.String) - for (let i = 0; i < value.length; i++) { - FNV1A64(value.charCodeAt(i)) - } - } - function Symbol(value: symbol) { - FNV1A64(ByteMarker.Symbol) - Visit(value.description) +// -------------------------------------------------------------------------- +// ByteMarker +// -------------------------------------------------------------------------- +export enum ByteMarker { + Undefined, + Null, + Boolean, + Number, + String, + Object, + Array, + Date, + Uint8Array, + Symbol, + BigInt, +} +// -------------------------------------------------------------------------- +// State +// -------------------------------------------------------------------------- +let Accumulator = BigInt('14695981039346656037') +const [Prime, Size] = [BigInt('1099511628211'), BigInt('2') ** BigInt('64')] +const Bytes = Array.from({ length: 256 }).map((_, i) => BigInt(i)) +const F64 = new Float64Array(1) +const F64In = new DataView(F64.buffer) +const F64Out = new Uint8Array(F64.buffer) +// -------------------------------------------------------------------------- +// Hashing Functions +// -------------------------------------------------------------------------- +function ArrayType(value: Array) { + FNV1A64(ByteMarker.Array) + for (const item of value) { + Visit(item) } - function Uint8Array(value: Uint8Array) { - FNV1A64(ByteMarker.Uint8Array) - for (let i = 0; i < value.length; i++) { - FNV1A64(value[i]) - } +} +function BooleanType(value: boolean) { + FNV1A64(ByteMarker.Boolean) + FNV1A64(value ? 1 : 0) +} +function BigIntType(value: bigint) { + FNV1A64(ByteMarker.BigInt) + F64In.setBigInt64(0, value) + for (const byte of F64Out) { + FNV1A64(byte) } - function Undefined(value: undefined) { - return FNV1A64(ByteMarker.Undefined) +} +function DateType(value: Date) { + FNV1A64(ByteMarker.Date) + Visit(value.getTime()) +} +function NullType(value: null) { + FNV1A64(ByteMarker.Null) +} +function NumberType(value: number) { + FNV1A64(ByteMarker.Number) + F64In.setFloat64(0, value) + for (const byte of F64Out) { + FNV1A64(byte) } - function Visit(value: any) { - if (IsArray(value)) { - Array(value) - } else if (IsBoolean(value)) { - Boolean(value) - } else if (IsBigInt(value)) { - BigInt(value) - } else if (IsDate(value)) { - Date(value) - } else if (IsNull(value)) { - Null(value) - } else if (IsNumber(value)) { - Number(value) - } else if (IsObject(value)) { - Object(value) - } else if (IsString(value)) { - String(value) - } else if (IsSymbol(value)) { - Symbol(value) - } else if (IsUint8Array(value)) { - Uint8Array(value) - } else if (IsUndefined(value)) { - Undefined(value) - } else { - throw new ValueHashError(value) - } +} +function ObjectType(value: Record) { + FNV1A64(ByteMarker.Object) + for (const key of globalThis.Object.keys(value).sort()) { + Visit(key) + Visit(value[key]) } - function FNV1A64(byte: number) { - Hash = Hash ^ Bytes[byte] - Hash = (Hash * Prime) % Size +} +function StringType(value: string) { + FNV1A64(ByteMarker.String) + for (let i = 0; i < value.length; i++) { + FNV1A64(value.charCodeAt(i)) } - /** Creates a FNV1A-64 non cryptographic hash of the given value */ - export function Create(value: unknown) { - Hash = globalThis.BigInt('14695981039346656037') - Visit(value) - return Hash +} +function SymbolType(value: symbol) { + FNV1A64(ByteMarker.Symbol) + Visit(value.description) +} +function Uint8ArrayType(value: Uint8Array) { + FNV1A64(ByteMarker.Uint8Array) + for (let i = 0; i < value.length; i++) { + FNV1A64(value[i]) } } +function UndefinedType(value: undefined) { + return FNV1A64(ByteMarker.Undefined) +} +function Visit(value: any) { + if (ValueGuard.IsArray(value)) return ArrayType(value) + if (ValueGuard.IsBoolean(value)) return BooleanType(value) + if (ValueGuard.IsBigInt(value)) return BigIntType(value) + if (ValueGuard.IsDate(value)) return DateType(value) + if (ValueGuard.IsNull(value)) return NullType(value) + if (ValueGuard.IsNumber(value)) return NumberType(value) + if (ValueGuard.IsPlainObject(value)) return ObjectType(value) + if (ValueGuard.IsString(value)) return StringType(value) + if (ValueGuard.IsSymbol(value)) return SymbolType(value) + if (ValueGuard.IsUint8Array(value)) return Uint8ArrayType(value) + if (ValueGuard.IsUndefined(value)) return UndefinedType(value) + throw new ValueHashError(value) +} +function FNV1A64(byte: number) { + Accumulator = Accumulator ^ Bytes[byte] + Accumulator = (Accumulator * Prime) % Size +} +// -------------------------------------------------------------------------- +// Hash +// -------------------------------------------------------------------------- +/** Creates a FNV1A-64 non cryptographic hash of the given value */ +export function Hash(value: unknown) { + Accumulator = BigInt('14695981039346656037') + Visit(value) + return Accumulator +} diff --git a/src/value/index.ts b/src/value/index.ts index a2235b01f..e8beb3bbd 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -26,9 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { ValueError, ValueErrorIterator, ValueErrorType } from '../errors/index' -export { ValueHash } from './hash' +export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' export { Edit, Insert, Update, Delete } from './delta' export { Mutable } from './mutate' -export * from './pointer' -export * from './value' +export { ValuePointer } from './pointer' +export { Value } from './value' diff --git a/src/value/is.ts b/src/value/is.ts deleted file mode 100644 index c808f9db6..000000000 --- a/src/value/is.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export type ValueType = null | undefined | Function | symbol | bigint | number | boolean | string -export type ObjectType = Record -export type TypedArrayType = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array -export type ArrayType = unknown[] - -export namespace Is { - export function Object(value: unknown): value is ObjectType { - return value !== null && typeof value === 'object' && !globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) && !(value instanceof globalThis.Date) - } - - export function Date(value: unknown): value is Date { - return value instanceof globalThis.Date - } - - export function Array(value: unknown): value is ArrayType { - return globalThis.Array.isArray(value) && !ArrayBuffer.isView(value) - } - - export function Value(value: unknown): value is ValueType { - return value === null || value === undefined || typeof value === 'function' || typeof value === 'symbol' || typeof value === 'bigint' || typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string' - } - - export function TypedArray(value: unknown): value is TypedArrayType { - return ArrayBuffer.isView(value) - } -} diff --git a/src/value/mutate.ts b/src/value/mutate.ts index 44d11a6e7..bd4a952bf 100644 --- a/src/value/mutate.ts +++ b/src/value/mutate.ts @@ -26,10 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Is, TypedArrayType } from './is' import { ValuePointer } from './pointer' -import { ValueClone } from './clone' +import * as ValueClone from './clone' +import * as ValueGuard from './guard' +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- export class ValueMutateTypeMismatchError extends Error { constructor() { super('ValueMutate: Cannot assign due type mismatch of assignable values') @@ -40,71 +43,79 @@ export class ValueMutateInvalidRootMutationError extends Error { super('ValueMutate: Only object and array types can be mutated at the root level') } } +// -------------------------------------------------------------------------- +// Mutators +// -------------------------------------------------------------------------- export type Mutable = { [key: string]: unknown } | unknown[] -export namespace ValueMutate { - function Object(root: Mutable, path: string, current: unknown, next: Record) { - if (!Is.Object(current)) { - ValuePointer.Set(root, path, ValueClone.Clone(next)) - } else { - const currentKeys = globalThis.Object.keys(current) - const nextKeys = globalThis.Object.keys(next) - for (const currentKey of currentKeys) { - if (!nextKeys.includes(currentKey)) { - delete current[currentKey] - } - } - for (const nextKey of nextKeys) { - if (!currentKeys.includes(nextKey)) { - current[nextKey] = null - } - } - for (const nextKey of nextKeys) { - Visit(root, `${path}/${nextKey}`, current[nextKey], next[nextKey]) +function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { + if (!ValueGuard.IsPlainObject(current)) { + ValuePointer.Set(root, path, ValueClone.Clone(next)) + } else { + const currentKeys = Object.keys(current) + const nextKeys = Object.keys(next) + for (const currentKey of currentKeys) { + if (!nextKeys.includes(currentKey)) { + delete current[currentKey] } } - } - function Array(root: Mutable, path: string, current: unknown, next: unknown[]) { - if (!Is.Array(current)) { - ValuePointer.Set(root, path, ValueClone.Clone(next)) - } else { - for (let index = 0; index < next.length; index++) { - Visit(root, `${path}/${index}`, current[index], next[index]) + for (const nextKey of nextKeys) { + if (!currentKeys.includes(nextKey)) { + current[nextKey] = null } - current.splice(next.length) } - } - function TypedArray(root: Mutable, path: string, current: unknown, next: TypedArrayType) { - if (Is.TypedArray(current) && current.length === next.length) { - for (let i = 0; i < current.length; i++) { - current[i] = next[i] - } - } else { - ValuePointer.Set(root, path, ValueClone.Clone(next)) + for (const nextKey of nextKeys) { + Visit(root, `${path}/${nextKey}`, current[nextKey], next[nextKey]) } } - function Value(root: Mutable, path: string, current: unknown, next: unknown) { - if (current === next) return - ValuePointer.Set(root, path, next) - } - function Visit(root: Mutable, path: string, current: unknown, next: unknown) { - if (Is.Array(next)) { - return Array(root, path, current, next) - } else if (Is.TypedArray(next)) { - return TypedArray(root, path, current, next) - } else if (Is.Object(next)) { - return Object(root, path, current, next) - } else if (Is.Value(next)) { - return Value(root, path, current, next) +} +function ArrayType(root: Mutable, path: string, current: unknown, next: unknown[]) { + if (!ValueGuard.IsArray(current)) { + ValuePointer.Set(root, path, ValueClone.Clone(next)) + } else { + for (let index = 0; index < next.length; index++) { + Visit(root, `${path}/${index}`, current[index], next[index]) } + current.splice(next.length) } - /** Performs a deep mutable value assignment while retaining internal references. */ - export function Mutate(current: Mutable, next: Mutable): void { - if (Is.TypedArray(current) || Is.Value(current) || Is.TypedArray(next) || Is.Value(next)) { - throw new ValueMutateInvalidRootMutationError() - } - if ((Is.Object(current) && Is.Array(next)) || (Is.Array(current) && Is.Object(next))) { - throw new ValueMutateTypeMismatchError() +} +function TypedArrayType(root: Mutable, path: string, current: unknown, next: ValueGuard.TypedArrayType) { + if (ValueGuard.IsTypedArray(current) && current.length === next.length) { + for (let i = 0; i < current.length; i++) { + current[i] = next[i] } - Visit(current, '', current, next) + } else { + ValuePointer.Set(root, path, ValueClone.Clone(next)) } } +function ValueType(root: Mutable, path: string, current: unknown, next: unknown) { + if (current === next) return + ValuePointer.Set(root, path, next) +} +function Visit(root: Mutable, path: string, current: unknown, next: unknown) { + if (ValueGuard.IsArray(next)) return ArrayType(root, path, current, next) + if (ValueGuard.IsTypedArray(next)) return TypedArrayType(root, path, current, next) + if (ValueGuard.IsPlainObject(next)) return ObjectType(root, path, current, next) + if (ValueGuard.IsValueType(next)) return ValueType(root, path, current, next) +} +// -------------------------------------------------------------------------- +// Mutate +// -------------------------------------------------------------------------- +function IsNonMutableValue(value: unknown): value is Mutable { + return ValueGuard.IsTypedArray(value) || ValueGuard.IsValueType(value) +} +function IsMismatchedValue(current: unknown, next: unknown) { + // prettier-ignore + return ( + (ValueGuard.IsPlainObject(current) && ValueGuard.IsArray(next)) || + (ValueGuard.IsArray(current) && ValueGuard.IsPlainObject(next)) + ) +} +// -------------------------------------------------------------------------- +// Mutate +// -------------------------------------------------------------------------- +/** Performs a deep mutable value assignment while retaining internal references */ +export function Mutate(current: Mutable, next: Mutable): void { + if (IsNonMutableValue(current) || IsNonMutableValue(next)) throw new ValueMutateInvalidRootMutationError() + if (IsMismatchedValue(current, next)) throw new ValueMutateTypeMismatchError() + Visit(current, '', current, next) +} diff --git a/src/value/pointer.ts b/src/value/pointer.ts index 1e7718e89..6b03c2f1e 100644 --- a/src/value/pointer.ts +++ b/src/value/pointer.ts @@ -26,24 +26,27 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- export class ValuePointerRootSetError extends Error { constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { super('ValuePointer: Cannot set root value') } } - export class ValuePointerRootDeleteError extends Error { constructor(public readonly value: unknown, public readonly path: string) { super('ValuePointer: Cannot delete root value') } } - +// -------------------------------------------------------------------------- +// ValuePointer +// -------------------------------------------------------------------------- /** Provides functionality to update values through RFC6901 string pointers */ export namespace ValuePointer { function Escape(component: string) { return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') } - /** Formats the given pointer into navigable key components */ export function* Format(pointer: string): IterableIterator { if (pointer === '') return @@ -64,7 +67,6 @@ export namespace ValuePointer { } yield Escape(pointer.slice(start)) } - /** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ export function Set(value: any, pointer: string, update: unknown): void { if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) @@ -77,7 +79,6 @@ export namespace ValuePointer { } owner[key] = update } - /** Deletes a value at the given pointer */ export function Delete(value: any, pointer: string): void { if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) @@ -88,14 +89,13 @@ export namespace ValuePointer { next = next[component] key = component } - if (globalThis.Array.isArray(owner)) { + if (Array.isArray(owner)) { const index = parseInt(key) owner.splice(index, 1) } else { delete owner[key] } } - /** Returns true if a value exists at the given pointer */ export function Has(value: any, pointer: string): boolean { if (pointer === '') return true @@ -106,9 +106,8 @@ export namespace ValuePointer { next = next[component] key = component } - return globalThis.Object.getOwnPropertyNames(owner).includes(key) + return Object.getOwnPropertyNames(owner).includes(key) } - /** Gets the value at the given pointer */ export function Get(value: any, pointer: string): any { if (pointer === '') return value diff --git a/src/value/value.ts b/src/value/value.ts index 96bf2982f..242edc490 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -26,82 +26,82 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import * as ValueErrors from '../errors/index' +import * as ValueMutate from './mutate' +import * as ValueHash from './hash' +import * as ValueEqual from './equal' +import * as ValueCast from './cast' +import * as ValueClone from './clone' +import * as ValueConvert from './convert' +import * as ValueCreate from './create' +import * as ValueCheck from './check' +import * as ValueDelta from './delta' import * as Types from '../typebox' -import { ValueErrors, ValueErrorIterator } from '../errors/index' -import { ValueMutate, Mutable } from './mutate' -import { ValueHash } from './hash' -import { ValueEqual } from './equal' -import { ValueCast } from './cast' -import { ValueClone } from './clone' -import { ValueConvert } from './convert' -import { ValueCreate } from './create' -import { ValueCheck } from './check' -import { ValueDelta, Edit } from './delta' -/** Provides functions to perform structural updates to JavaScript values */ +/** Functions to perform structural operations on JavaScript values */ export namespace Value { - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number, boolean and date values if a reasonable conversion is possible. */ - export function Cast(schema: T, references: [...R], value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. Cast will convert string, number, boolean and date values if a reasonable conversion is possible. */ + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ + export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(schema: T, value: unknown): Types.Static + /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - return ValueCast.Cast(schema, references, value) + return ValueCast.Cast.apply(ValueCast, args as any) } - /** Creates a value from the given type */ - export function Create(schema: T, references: [...R]): Types.Static + /** Creates a value from the given type and references */ + export function Create(schema: T, references: Types.TSchema[]): Types.Static /** Creates a value from the given type */ export function Create(schema: T): Types.Static + /** Creates a value from the given type */ export function Create(...args: any[]) { - const [schema, references] = args.length === 2 ? [args[0], args[1]] : [args[0], []] - return ValueCreate.Create(schema, references) + return ValueCreate.Create.apply(ValueCreate, args as any) } - /** Returns true if the value matches the given type. */ - export function Check(schema: T, references: [...R], value: unknown): value is Types.Static - /** Returns true if the value matches the given type. */ + /** Returns true if the value matches the given type and references */ + export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static + /** Returns true if the value matches the given type */ export function Check(schema: T, value: unknown): value is Types.Static + /** Returns true if the value matches the given type */ export function Check(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - return ValueCheck.Check(schema, references, value) + return ValueCheck.Check.apply(ValueCheck, args as any) } - /** Converts any type mismatched values to their target type if a conversion is possible. */ - export function Convert(schema: T, references: [...R], value: unknown): unknown - /** Converts any type mismatched values to their target type if a conversion is possible. */ + /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ + export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown + /** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ export function Convert(schema: T, value: unknown): unknown + /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ export function Convert(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - return ValueConvert.Convert(schema, references, value) + return ValueConvert.Convert.apply(ValueConvert, args as any) } /** Returns a structural clone of the given value */ export function Clone(value: T): T { return ValueClone.Clone(value) } /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, references: [...R], value: unknown): ValueErrorIterator + export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrors.ValueErrorIterator + /** Returns an iterator for each error in this value. */ + export function Errors(schema: T, value: unknown): ValueErrors.ValueErrorIterator /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, value: unknown): ValueErrorIterator export function Errors(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - return ValueErrors.Errors(schema, references, value) as ValueErrorIterator + return ValueErrors.Errors.apply(ValueErrors, args as any) } /** Returns true if left and right values are structurally equal */ export function Equal(left: T, right: unknown): right is T { return ValueEqual.Equal(left, right) } /** Returns edits to transform the current value into the next value */ - export function Diff(current: unknown, next: unknown): Edit[] { + export function Diff(current: unknown, next: unknown): ValueDelta.Edit[] { return ValueDelta.Diff(current, next) } /** Returns a FNV1A-64 non cryptographic hash of the given value */ export function Hash(value: unknown): bigint { - return ValueHash.Create(value) + return ValueHash.Hash(value) } /** Returns a new value with edits applied to the given value */ - export function Patch(current: unknown, edits: Edit[]): T { + export function Patch(current: unknown, edits: ValueDelta.Edit[]): T { return ValueDelta.Patch(current, edits) as T } /** Performs a deep mutable value assignment while retaining internal references. */ - export function Mutate(current: Mutable, next: Mutable): void { + export function Mutate(current: ValueMutate.Mutable, next: ValueMutate.Mutable): void { ValueMutate.Mutate(current, next) } } diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index 83ab9a5d5..bfb4f391d 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -1,33 +1,27 @@ import * as assert from 'assert' export namespace Assert { - let port = 9000 - /** Generates a new port used for host binding */ - export function nextPort() { - const next = port++ - return next - } - export function isTrue(value: boolean) { + export function IsTrue(value: boolean): asserts value is true { return assert.strictEqual(value, true) } - export function isFalse(value: boolean) { + export function IsFalse(value: boolean): asserts value is false { return assert.strictEqual(value, false) } - export function isEqual(actual: unknown, expect: unknown) { + export function IsEqual(actual: unknown, expect: unknown) { if (actual instanceof Uint8Array && expect instanceof Uint8Array) { assert.equal(actual.length, expect.length) for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i]) } return assert.deepEqual(actual, expect) } - export function notEqual(actual: unknown, expect: unknown) { + export function NotEqual(actual: unknown, expect: unknown) { return assert.notEqual(actual, expect) } let nextIdOrdinal = 0 - export function nextId() { - return `nextID${nextIdOrdinal++}` + export function NextId() { + return `$id-${nextIdOrdinal++}` } - export function throws(callback: Function) { + export function Throws(callback: Function) { try { callback() } catch { @@ -35,7 +29,7 @@ export namespace Assert { } throw Error('Expected throw') } - export async function throwsAsync(callback: Function) { + export async function ThrowsAsync(callback: Function) { try { await callback() } catch { @@ -43,12 +37,12 @@ export namespace Assert { } throw Error('Expected throw') } - export function isTypeOf(value: any, type: any) { - if (typeof value === type) return - throw Error(`Value is not typeof ${type}`) - } - export function isInstanceOf(value: any, constructor: any) { + export function IsInstanceOf(value: any, constructor: any) { if (value instanceof constructor) return throw Error(`Value is not instance of ${constructor}`) } + export function IsTypeOf(value: any, type: any) { + if (typeof value === type) return + throw Error(`Value is not typeof ${type}`) + } } diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index a2ccad658..1b004d8d2 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -6,27 +6,22 @@ describe('type/compiler/Array', () => { const T = Type.Array(Type.Any()) Ok(T, [0, true, 'hello', {}]) }) - it('Should not validate varying array when item is number', () => { const T = Type.Array(Type.Number()) Fail(T, [1, 2, 3, 'hello']) }) - it('Should validate for an array of unions', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Ok(T, [1, 'hello', 3, 'world']) }) - it('Should not validate for an array of unions where item is not in union.', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Fail(T, [1, 'hello', 3, 'world', true]) }) - it('Should validate for an empty array', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Ok(T, []) }) - it('Should validate for an array of intersection types', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.String() }) @@ -38,7 +33,6 @@ describe('type/compiler/Array', () => { { a: 'hello', b: 'hello' }, ]) }) - it('Should not validate for an array of composite types when passing additionalProperties false', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.String() }) @@ -50,7 +44,6 @@ describe('type/compiler/Array', () => { { a: 'hello', b: 'hello', c: 'additional' }, ]) }) - it('Should validate an array of tuples', () => { const A = Type.String() const B = Type.Number() @@ -62,7 +55,6 @@ describe('type/compiler/Array', () => { ['hello', 1], ]) }) - it('Should not validate an array of tuples when tuple values are incorrect', () => { const A = Type.String() const B = Type.Number() @@ -74,7 +66,6 @@ describe('type/compiler/Array', () => { [1, 'hello'], ]) }) - it('Should not validate array with failed minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) Fail(T, [0, 1]) @@ -83,6 +74,9 @@ describe('type/compiler/Array', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) Fail(T, [0, 1, 2, 3]) }) + // --------------------------------------------------------- + // Unique Items + // --------------------------------------------------------- it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) Ok(T, [ @@ -101,4 +95,54 @@ describe('type/compiler/Array', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) Fail(T, [0, 0]) }) + // --------------------------------------------------------- + // Contains + // --------------------------------------------------------- + it('Should validate for contains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1) }) + Ok(T, [1]) + Ok(T, [1, 2]) + Fail(T, []) + Fail(T, [2]) + }) + it('Should validate for minContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3 }) + Ok(T, [1, 1, 1, 2]) + Ok(T, [2, 1, 1, 1, 2]) + Ok(T, [1, 1, 1]) + Fail(T, []) + Fail(T, [1, 1]) + Fail(T, [2]) + }) + it('Should validate for maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), maxContains: 3 }) + Ok(T, [1, 1, 1]) + Ok(T, [1, 1]) + Ok(T, [2, 2, 2, 2, 1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + }) + it('Should validate for minContains and maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Ok(T, [1, 1, 1]) + Ok(T, [1, 1, 1, 1]) + Ok(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) + it('Should not validate minContains or maxContains when contains is unspecified', () => { + const T = Type.Array(Type.Number(), { minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Fail(T, [1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) + it('Should produce illogical schema when contains is not sub type of items', () => { + const T = Type.Array(Type.Number(), { contains: Type.String(), minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Fail(T, [1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) }) diff --git a/test/runtime/compiler/async-iterator.ts b/test/runtime/compiler/async-iterator.ts new file mode 100644 index 000000000..f2afa82f6 --- /dev/null +++ b/test/runtime/compiler/async-iterator.ts @@ -0,0 +1,20 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/AsyncIterator', () => { + it('Should validate a async iterator 1', () => { + async function* f() {} + const T = Type.AsyncIterator(Type.Any()) + Ok(T, f()) + }) + it('Should validate a async iterator 2', () => { + const T = Type.AsyncIterator(Type.Any()) + Ok(T, { + [Symbol.asyncIterator]: () => {}, + }) + }) + it('Should not validate a async iterator', () => { + const T = Type.AsyncIterator(Type.Any()) + Fail(T, {}) + }) +}) diff --git a/test/runtime/compiler/bigint.ts b/test/runtime/compiler/bigint.ts index 484573395..2a04cbc35 100644 --- a/test/runtime/compiler/bigint.ts +++ b/test/runtime/compiler/bigint.ts @@ -59,25 +59,21 @@ describe('type/compiler/BigInt', () => { Fail(T, BigInt(9)) Ok(T, BigInt(10)) }) - it('Should validate maximum', () => { const T = Type.BigInt({ maximum: BigInt(10) }) Ok(T, BigInt(10)) Fail(T, BigInt(11)) }) - it('Should validate Date exclusiveMinimum', () => { const T = Type.BigInt({ exclusiveMinimum: BigInt(10) }) Fail(T, BigInt(10)) Ok(T, BigInt(11)) }) - it('Should validate Date exclusiveMaximum', () => { const T = Type.BigInt({ exclusiveMaximum: BigInt(10) }) Ok(T, BigInt(9)) Fail(T, BigInt(10)) }) - it('Should not validate NaN', () => { Fail(Type.Number(), NaN) }) diff --git a/test/runtime/compiler/boolean.ts b/test/runtime/compiler/boolean.ts index 03f01d890..bbc54d942 100644 --- a/test/runtime/compiler/boolean.ts +++ b/test/runtime/compiler/boolean.ts @@ -7,32 +7,26 @@ describe('type/compiler/Boolean', () => { Ok(T, true) Ok(T, false) }) - it('Should not validate a number', () => { const T = Type.Boolean() Fail(T, 1) }) - it('Should not validate a string', () => { const T = Type.Boolean() Fail(T, 'true') }) - it('Should not validate an array', () => { const T = Type.Boolean() Fail(T, [true]) }) - it('Should not validate an object', () => { const T = Type.Boolean() Fail(T, {}) }) - it('Should not validate an null', () => { const T = Type.Boolean() Fail(T, null) }) - it('Should not validate an undefined', () => { const T = Type.Boolean() Fail(T, undefined) diff --git a/test/runtime/compiler/enum.ts b/test/runtime/compiler/enum.ts index 08460de6f..268e2c8cb 100644 --- a/test/runtime/compiler/enum.ts +++ b/test/runtime/compiler/enum.ts @@ -20,7 +20,6 @@ describe('type/compiler/Enum', () => { Fail(T, 'Foo') Fail(T, 'Bar') }) - it('Should validate when emum has defined string values', () => { enum Kind { Foo = 'foo', @@ -30,7 +29,6 @@ describe('type/compiler/Enum', () => { Ok(T, 'foo') Ok(T, 'bar') }) - it('Should not validate when emum has defined string values and user passes numeric', () => { enum Kind { Foo = 'foo', @@ -40,7 +38,6 @@ describe('type/compiler/Enum', () => { Fail(T, 0) Fail(T, 1) }) - it('Should validate when enum has one or more string values', () => { enum Kind { Foo, diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index 1e2fa7629..d0120684b 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,5 +1,6 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './composite' @@ -9,6 +10,7 @@ import './unicode' import './enum' import './integer' import './intersect' +import './iterator' import './keyof' import './literal' import './modifier' @@ -26,7 +28,7 @@ import './readonly' import './recursive' import './record' import './ref' -import './regex' +import './regexp' import './required' import './string' import './symbol' diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts index 598aa04b4..683842fbc 100644 --- a/test/runtime/compiler/integer.ts +++ b/test/runtime/compiler/integer.ts @@ -22,27 +22,22 @@ describe('type/compiler/Integer', () => { const T = Type.Integer() Ok(T, 1) }) - it('Should not validate string', () => { const T = Type.Integer() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Integer() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Integer() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Integer() Fail(T, { a: 1, b: 2 }) }) - it('Should not validate null', () => { const T = Type.Integer() Fail(T, null) @@ -64,19 +59,16 @@ describe('type/compiler/Integer', () => { Fail(T, 9) Ok(T, 10) }) - it('Should validate maximum', () => { const T = Type.Integer({ maximum: 10 }) Ok(T, 10) Fail(T, 11) }) - it('Should validate Date exclusiveMinimum', () => { const T = Type.Integer({ exclusiveMinimum: 10 }) Fail(T, 10) Ok(T, 11) }) - it('Should validate Date exclusiveMaximum', () => { const T = Type.Integer({ exclusiveMaximum: 10 }) Ok(T, 9) diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index ce7f934c7..d7aae2e77 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -87,7 +87,6 @@ describe('type/compiler/Intersect', () => { const T = Type.Intersect([A, B]) Fail(T, { x: 1, y: 1 }) }) - it('unevaluatedProperties with Record 1', () => { const T = Type.Intersect( [ diff --git a/test/runtime/compiler/iterator.ts b/test/runtime/compiler/iterator.ts new file mode 100644 index 000000000..630504929 --- /dev/null +++ b/test/runtime/compiler/iterator.ts @@ -0,0 +1,20 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/Iterator', () => { + it('Should validate a iterator 1', () => { + function* f() {} + const T = Type.Iterator(Type.Any()) + Ok(T, f()) + }) + it('Should validate a iterator 2', () => { + const T = Type.Iterator(Type.Any()) + Ok(T, { + [Symbol.iterator]: () => {}, + }) + }) + it('Should not validate a iterator', () => { + const T = Type.Iterator(Type.Any()) + Fail(T, {}) + }) +}) diff --git a/test/runtime/compiler/keyof.ts b/test/runtime/compiler/keyof.ts index e96ad8762..5acec21da 100644 --- a/test/runtime/compiler/keyof.ts +++ b/test/runtime/compiler/keyof.ts @@ -16,7 +16,6 @@ describe('type/compiler/KeyOf', () => { Ok(T, 'z') Fail(T, 'w') }) - it('Should validate when using pick', () => { const T = Type.KeyOf( Type.Pick( @@ -32,7 +31,6 @@ describe('type/compiler/KeyOf', () => { Ok(T, 'y') Fail(T, 'z') }) - it('Should validate when using omit', () => { const T = Type.KeyOf( Type.Omit( @@ -44,7 +42,6 @@ describe('type/compiler/KeyOf', () => { ['x', 'y'], ), ) - Fail(T, 'x') Fail(T, 'y') Ok(T, 'z') diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 5b3079344..9e526de49 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -242,7 +242,6 @@ describe('type/compiler/Object', () => { z: 3, }) }) - it('Should validate nested schema additional properties of string', () => { const T = Type.Object({ nested: Type.Object( @@ -326,7 +325,6 @@ describe('type/compiler/Object', () => { }, }) }) - it('Should check for property key if property type is undefined', () => { const T = Type.Object({ x: Type.Undefined() }) Ok(T, { x: undefined }) diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 684282403..72ab48c9e 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -1,10 +1,10 @@ import { TypeSystem } from '@sinclair/typebox/system' -import { Type, Kind, Modifier } from '@sinclair/typebox' +import { Type, Kind, Optional, Readonly } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' describe('type/compiler/Partial', () => { - it('Should convert a required object into a partial.', () => { + it('Should convert a required object into a partial', () => { const A = Type.Object( { x: Type.Number(), @@ -14,16 +14,16 @@ describe('type/compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - Ok(T, { x: 1, y: 1, z: 1 }) - Ok(T, { x: 1, y: 1 }) - Ok(T, { x: 1 }) - Ok(T, {}) + console.log(T) + //Ok(T, { x: 1, y: 1, z: 1 }) + //Ok(T, { x: 1, y: 1 }) + //Ok(T, { x: 1 }) + //Ok(T, {}) }) - it('Should update modifier types correctly when converting to partial', () => { const A = Type.Object( { - x: Type.ReadonlyOptional(Type.Number()), + x: Type.Readonly(Type.Optional(Type.Number())), y: Type.Readonly(Type.Number()), z: Type.Optional(Type.Number()), w: Type.Number(), @@ -31,10 +31,12 @@ describe('type/compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(T.properties.x[Modifier], 'ReadonlyOptional') - strictEqual(T.properties.y[Modifier], 'ReadonlyOptional') - strictEqual(T.properties.z[Modifier], 'Optional') - strictEqual(T.properties.w[Modifier], 'Optional') + strictEqual(T.properties.x[Readonly], 'Readonly') + strictEqual(T.properties.x[Optional], 'Optional') + strictEqual(T.properties.y[Readonly], 'Readonly') + strictEqual(T.properties.y[Optional], 'Optional') + strictEqual(T.properties.z[Optional], 'Optional') + strictEqual(T.properties.w[Optional], 'Optional') }) it('Should inherit options from the source object', () => { diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts index dd8f234b6..556aa9859 100644 --- a/test/runtime/compiler/pick.ts +++ b/test/runtime/compiler/pick.ts @@ -15,7 +15,6 @@ describe('type/compiler/Pick', () => { const T = Type.Pick(Vector3, ['x', 'y']) Ok(T, { x: 1, y: 1 }) }) - it('Should remove required properties on the target schema', () => { const A = Type.Object( { @@ -28,7 +27,6 @@ describe('type/compiler/Pick', () => { const T = Type.Pick(A, ['x', 'y']) strictEqual(T.required!.includes('z'), false) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -42,7 +40,6 @@ describe('type/compiler/Pick', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) - it('Should pick with keyof object', () => { const A = Type.Object({ x: Type.Number(), diff --git a/test/runtime/compiler/readonly-optional.ts b/test/runtime/compiler/readonly-optional.ts index 90ec45097..fdaef8b4e 100644 --- a/test/runtime/compiler/readonly-optional.ts +++ b/test/runtime/compiler/readonly-optional.ts @@ -6,7 +6,7 @@ describe('type/compiler/ReadonlyOptional', () => { it('Should validate object with optional', () => { const T = Type.Object( { - a: Type.ReadonlyOptional(Type.String()), + a: Type.Readonly(Type.Optional(Type.String())), b: Type.String(), }, { additionalProperties: false }, @@ -17,7 +17,7 @@ describe('type/compiler/ReadonlyOptional', () => { it('Should remove required value from schema', () => { const T = Type.Object( { - a: Type.ReadonlyOptional(Type.String()), + a: Type.Readonly(Type.Optional(Type.String())), b: Type.String(), }, { additionalProperties: false }, diff --git a/test/runtime/compiler/readonly.ts b/test/runtime/compiler/readonly.ts index 3edbab2a3..1bc82d758 100644 --- a/test/runtime/compiler/readonly.ts +++ b/test/runtime/compiler/readonly.ts @@ -13,7 +13,6 @@ describe('type/compiler/Readonly', () => { ) Ok(T, { a: 'hello', b: 'world' }) }) - it('Should retain required array on object', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 8bee7fa76..ab8381fb4 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -86,7 +86,7 @@ describe('type/compiler/Record', () => { Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) it('Should should validate when specifying regular expressions', () => { - const K = Type.RegEx(/^op_.*$/) + const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number()) Ok(T, { op_a: 1, @@ -95,7 +95,7 @@ describe('type/compiler/Record', () => { }) }) it('Should should not validate when specifying regular expressions and passing invalid property', () => { - const K = Type.RegEx(/^op_.*$/) + const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { op_a: 1, diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts index b5b1c37ac..513864ec6 100644 --- a/test/runtime/compiler/recursive.ts +++ b/test/runtime/compiler/recursive.ts @@ -10,9 +10,8 @@ describe('type/compiler/Recursive', () => { nodes: Type.Array(Node), }), ) - Assert.isEqual(Node.$id === undefined, false) + Assert.IsEqual(Node.$id === undefined, false) }) - it('Should override default ordinal $id if specified', () => { const Node = Type.Recursive( (Node) => @@ -22,9 +21,8 @@ describe('type/compiler/Recursive', () => { }), { $id: 'Node' }, ) - Assert.isEqual(Node.$id === 'Node', true) + Assert.IsEqual(Node.$id === 'Node', true) }) - it('Should validate recursive node type', () => { const Node = Type.Recursive((This) => Type.Object({ @@ -40,7 +38,6 @@ describe('type/compiler/Recursive', () => { ], }) }) - it('Should validate wrapped recursive node type', () => { const Node = Type.Tuple([ Type.Recursive((This) => @@ -60,7 +57,6 @@ describe('type/compiler/Recursive', () => { }, ]) }) - it('Should not validate wrapped recursive node type with invalid id', () => { const Node = Type.Tuple([ Type.Recursive((This) => diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index ef23a872e..88d564e40 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -10,7 +10,7 @@ describe('type/compiler/Ref', () => { y: Type.Number(), z: Type.Number(), }, - { $id: Assert.nextId() }, + { $id: Assert.NextId() }, ) const R = Type.Ref(T) Ok( @@ -23,7 +23,6 @@ describe('type/compiler/Ref', () => { [T], ) }) - it('Should not validate when passing invalid data', () => { const T = Type.Object( { @@ -31,7 +30,7 @@ describe('type/compiler/Ref', () => { y: Type.Number(), z: Type.Number(), }, - { $id: Assert.nextId() }, + { $id: Assert.NextId() }, ) const R = Type.Ref(T) Fail( @@ -43,7 +42,6 @@ describe('type/compiler/Ref', () => { [T], ) }) - it('Should de-reference object property schema', () => { const T = Type.Object( { @@ -51,7 +49,6 @@ describe('type/compiler/Ref', () => { }, { $id: 'R' }, ) - const R = Type.Object( { x: Type.Number(), @@ -61,13 +58,11 @@ describe('type/compiler/Ref', () => { }, { $id: 'T' }, ) - Ok(R, { x: 1, y: 2, z: 3 }, [T]) Ok(R, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [T]) Fail(R, { x: 1, y: 2, z: 3, r: { name: 1 } }, [T]) Fail(R, { x: 1, y: 2, z: 3, r: {} }, [T]) }) - it('Should reference recursive schema', () => { const T = Type.Recursive((Node) => Type.Object({ diff --git a/test/runtime/compiler/regex.ts b/test/runtime/compiler/regex.ts deleted file mode 100644 index 5f3418047..000000000 --- a/test/runtime/compiler/regex.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' - -describe('type/compiler/RegEx', () => { - it('Should validate numeric value', () => { - const T = Type.RegEx(/[012345]/) - Ok(T, '0') - Ok(T, '1') - Ok(T, '2') - Ok(T, '3') - Ok(T, '4') - Ok(T, '5') - }) - - it('Should validate true or false string value', () => { - const T = Type.RegEx(/true|false/) - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'false') - Ok(T, 'false') - Ok(T, 'false') - Fail(T, '6') - }) - - it('Should not validate failed regex test', () => { - const T = Type.RegEx(/true|false/) - Fail(T, 'unknown') - }) - - it('Should pass numeric 5 digit test', () => { - const T = Type.RegEx(/[\d]{5}/) - Ok(T, '12345') - }) -}) diff --git a/test/runtime/compiler/regexp.ts b/test/runtime/compiler/regexp.ts new file mode 100644 index 000000000..9a926893f --- /dev/null +++ b/test/runtime/compiler/regexp.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/compiler/RegExp', () => { + //----------------------------------------------------- + // Regular Expression + //----------------------------------------------------- + it('Should validate regular expression 1', () => { + const T = Type.RegExp(/[012345]/) + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate regular expression 2', () => { + const T = Type.RegExp(/true|false/) + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate regular expression 3', () => { + const T = Type.RegExp(/true|false/) + Fail(T, 'unknown') + }) + it('Should validate regular expression 4', () => { + const T = Type.RegExp(/[\d]{5}/) + Ok(T, '12345') + }) + //----------------------------------------------------- + // Regular Pattern + //----------------------------------------------------- + it('Should validate pattern 1', () => { + const T = Type.RegExp('[012345]') + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate pattern 2', () => { + const T = Type.RegExp('true|false') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate pattern 3', () => { + const T = Type.RegExp('true|false') + Fail(T, 'unknown') + }) + it('Should validate pattern 4', () => { + const T = Type.RegExp('[\\d]{5}') + Ok(T, '12345') + }) +}) diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index fc6f32f83..80360c949 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -1,4 +1,4 @@ -import { Type, Modifier } from '@sinclair/typebox' +import { Type, Readonly, Optional } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -18,21 +18,19 @@ describe('type/compiler/compiler/Required', () => { Fail(T, { x: 1 }) Fail(T, {}) }) - it('Should update modifier types correctly when converting to required', () => { const A = Type.Object({ - x: Type.ReadonlyOptional(Type.Number()), + x: Type.Readonly(Type.Optional(Type.Number())), y: Type.Readonly(Type.Number()), z: Type.Optional(Type.Number()), w: Type.Number(), }) const T = Type.Required(A) - strictEqual(T.properties.x[Modifier], 'Readonly') - strictEqual(T.properties.y[Modifier], 'Readonly') - strictEqual(T.properties.z[Modifier], undefined) - strictEqual(T.properties.w[Modifier], undefined) + strictEqual(T.properties.x[Readonly], 'Readonly') + strictEqual(T.properties.y[Readonly], 'Readonly') + strictEqual(T.properties.z[Optional], undefined) + strictEqual(T.properties.w[Optional], undefined) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -46,7 +44,6 @@ describe('type/compiler/compiler/Required', () => { strictEqual(A.additionalPropeties, false) strictEqual(T.additionalPropeties, false) }) - // it('Should construct new object when targetting reference', () => { // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) // const R = Type.Ref(T) diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts index 0f43dc025..99beae9a0 100644 --- a/test/runtime/compiler/string.ts +++ b/test/runtime/compiler/string.ts @@ -42,34 +42,28 @@ describe('type/compiler/String', () => { const T = Type.String({ format: 'email' }) Ok(T, 'name@domain.com') }) - it('Should validate string format as uuid', () => { const T = Type.String({ format: 'uuid' }) Ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') }) - it('Should validate string format as iso8601 date', () => { const T = Type.String({ format: 'date-time' }) Ok(T, '2021-06-11T20:30:00-04:00') }) - it('Should validate minLength', () => { const T = Type.String({ minLength: 4 }) Ok(T, '....') Fail(T, '...') }) - it('Should validate maxLength', () => { const T = Type.String({ maxLength: 4 }) Ok(T, '....') Fail(T, '.....') }) - it('Should pass numeric 5 digit test', () => { const T = Type.String({ pattern: '[\\d]{5}' }) Ok(T, '12345') }) - it('Should should escape characters in the pattern', () => { const T = Type.String({ pattern: '/a/' }) Ok(T, '/a/') diff --git a/test/runtime/compiler/template-literal.ts b/test/runtime/compiler/template-literal.ts index d0357ae96..c967cf9ee 100644 --- a/test/runtime/compiler/template-literal.ts +++ b/test/runtime/compiler/template-literal.ts @@ -130,7 +130,6 @@ describe('type/compiler/TemplateLiteral', () => { Ok(T, 'ccc') Ok(T, 'dddd') }) - it('Should validate infinite pattern 5', () => { // prettier-ignore const T = Type.TemplateLiteral([ diff --git a/test/runtime/compiler/tuple.ts b/test/runtime/compiler/tuple.ts index 63a436182..9e1b82052 100644 --- a/test/runtime/compiler/tuple.ts +++ b/test/runtime/compiler/tuple.ts @@ -9,50 +9,42 @@ describe('type/compiler/Tuple', () => { const T = Type.Tuple([A, B]) Ok(T, ['hello', 42]) }) - it('Should not validate tuple of [string, number] when reversed', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) Fail(T, [42, 'hello']) }) - it('Should validate with empty tuple', () => { const T = Type.Tuple([]) Ok(T, []) }) - it('Should not validate with empty tuple with more items', () => { const T = Type.Tuple([]) Fail(T, [1]) }) - it('Should not validate with empty tuple with less items', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) Fail(T, [1]) }) - it('Should validate tuple of objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Ok(T, [{ a: 'hello' }, { b: 42 }]) }) - it('Should not validate tuple of objects when reversed', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Fail(T, [{ b: 42 }, { a: 'hello' }]) }) - it('Should not validate tuple when array is less than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Fail(T, [{ a: 'hello' }]) }) - it('Should not validate tuple when array is greater than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) diff --git a/test/runtime/compiler/uint8array.ts b/test/runtime/compiler/uint8array.ts index ae94d7872..274975b23 100644 --- a/test/runtime/compiler/uint8array.ts +++ b/test/runtime/compiler/uint8array.ts @@ -6,48 +6,39 @@ describe('type/compiler/Uint8Array', () => { const T = Type.Uint8Array() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Uint8Array() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Uint8Array() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Uint8Array() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Uint8Array() Fail(T, { a: 1, b: 2 }) }) - it('Should not validate null', () => { const T = Type.Uint8Array() Fail(T, null) }) - it('Should not validate undefined', () => { const T = Type.Uint8Array() Fail(T, undefined) }) - it('Should validate Uint8Array', () => { const T = Type.Uint8Array() Ok(T, new Uint8Array(100)) }) - it('Should validate minByteLength', () => { const T = Type.Uint8Array({ minByteLength: 4 }) Ok(T, new Uint8Array(4)) Fail(T, new Uint8Array(3)) }) - it('Should validate maxByteLength', () => { const T = Type.Uint8Array({ maxByteLength: 4 }) Ok(T, new Uint8Array(4)) diff --git a/test/runtime/compiler/union.ts b/test/runtime/compiler/union.ts index 8ad6e4f64..a1e1fe724 100644 --- a/test/runtime/compiler/union.ts +++ b/test/runtime/compiler/union.ts @@ -11,7 +11,6 @@ describe('type/compiler/Union', () => { Ok(T, true) Ok(T, 42) }) - it('Should validate union of objects', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) @@ -19,7 +18,6 @@ describe('type/compiler/Union', () => { Ok(T, { a: 'hello' }) Ok(T, { b: 'world' }) }) - it('Should fail to validate for descriminated union types', () => { const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) @@ -27,7 +25,6 @@ describe('type/compiler/Union', () => { Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } }) - it('Should validate union of objects where properties overlap', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) @@ -35,7 +32,6 @@ describe('type/compiler/Union', () => { Ok(T, { a: 'hello' }) // A Ok(T, { a: 'hello', b: 'world' }) // B }) - it('Should validate union of overlapping property of varying type', () => { const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) @@ -43,7 +39,6 @@ describe('type/compiler/Union', () => { Ok(T, { a: 'hello', b: 42 }) // A Ok(T, { a: 'hello', b: 'world' }) // B }) - it('Should validate union of literal strings', () => { const A = Type.Literal('hello') const B = Type.Literal('world') @@ -51,7 +46,6 @@ describe('type/compiler/Union', () => { Ok(T, 'hello') // A Ok(T, 'world') // B }) - it('Should not validate union of literal strings for unknown string', () => { const A = Type.Literal('hello') const B = Type.Literal('world') diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index b85a91c20..eb3ab6a0e 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -21,7 +21,6 @@ const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function isLeapYear(year: number): boolean { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } - function isDate(str: string): boolean { const matches: string[] | null = DATE.exec(str) if (!matches) return false @@ -30,7 +29,6 @@ function isDate(str: string): boolean { const day: number = +matches[3] return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]) } - function isTime(str: string, strictTimeZone?: boolean): boolean { const matches: string[] | null = TIME.exec(str) if (!matches) return false @@ -48,20 +46,16 @@ function isTime(str: string, strictTimeZone?: boolean): boolean { const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 } - function isDateTime(str: string, strictTimeZone?: boolean): boolean { const dateTime: string[] = str.split(DATE_TIME_SEPARATOR) return dateTime.length === 2 && isDate(dateTime[0]) && isTime(dateTime[1], strictTimeZone) } - // ------------------------------------------------------------------------- // Use Formats // ------------------------------------------------------------------------- - FormatRegistry.Set('email', (value) => EMAIL.test(value)) FormatRegistry.Set('uuid', (value) => UUID.test(value)) FormatRegistry.Set('date-time', (value) => isDateTime(value, true)) - export function Ok(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) @@ -92,7 +86,6 @@ export function Ok(schema: T, data: unknown, references: any[ throw Error('expected ok') } } - export function Fail(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) diff --git a/test/runtime/errors/iterator.ts b/test/runtime/errors/iterator.ts index e0baf4404..1f1ef8858 100644 --- a/test/runtime/errors/iterator.ts +++ b/test/runtime/errors/iterator.ts @@ -1,32 +1,32 @@ import { Type } from '@sinclair/typebox' -import { ValueErrors } from '@sinclair/typebox/errors' +import { Errors } from '@sinclair/typebox/errors' import { Assert } from '../assert' describe('errors/ValueErrorIterator', () => { it('Should return undefined for non error', () => { - const R = ValueErrors.Errors(Type.Number(), [], 1).First() - Assert.isEqual(R, undefined) + const R = Errors(Type.Number(), [], 1).First() + Assert.IsEqual(R, undefined) }) it('Should return a value error when error', () => { - const { type, path, message } = ValueErrors.Errors(Type.Number(), [], '').First()! - Assert.isTypeOf(type, 'number') - Assert.isTypeOf(path, 'string') - Assert.isTypeOf(message, 'string') + const { type, path, message } = Errors(Type.Number(), [], '').First()! + Assert.IsTypeOf(type, 'number') + Assert.IsTypeOf(path, 'string') + Assert.IsTypeOf(message, 'string') }) it('Should yield empty array for non error', () => { - const R = [...ValueErrors.Errors(Type.Number(), [], 1)] - Assert.isEqual(R.length, 0) + const R = [...Errors(Type.Number(), [], 1)] + Assert.IsEqual(R.length, 0) }) it('Should yield array with 1 error when error', () => { - const R = [...ValueErrors.Errors(Type.Number(), [], 'foo')] - Assert.isEqual(R.length, 1) + const R = [...Errors(Type.Number(), [], 'foo')] + Assert.IsEqual(R.length, 1) }) it('Should yield array with N errors when error', () => { // prettier-ignore - const R = [...ValueErrors.Errors(Type.Object({ + const R = [...Errors(Type.Object({ x: Type.Number(), y: Type.Number() }), [], {})] // require object to invoke internal check - Assert.isEqual(R.length > 1, true) + Assert.IsEqual(R.length > 1, true) }) }) diff --git a/test/runtime/schema/array.ts b/test/runtime/schema/array.ts index 3b9b972b6..d20e504c4 100644 --- a/test/runtime/schema/array.ts +++ b/test/runtime/schema/array.ts @@ -6,27 +6,22 @@ describe('type/schema/Array', () => { const T = Type.Array(Type.Any()) Ok(T, [0, true, 'hello', {}]) }) - it('Should not validate varying array when item is number', () => { const T = Type.Array(Type.Number()) Fail(T, [1, 2, 3, 'hello']) }) - it('Should validate for an array of unions', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Ok(T, [1, 'hello', 3, 'world']) }) - it('Should not validate for an array of unions where item is not in union.', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Fail(T, [1, 'hello', 3, 'world', true]) }) - it('Should validate for an empty array', () => { const T = Type.Array(Type.Union([Type.Number(), Type.String()])) Ok(T, []) }) - it('Should validate for an array of intersection types', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.String() }) @@ -38,11 +33,10 @@ describe('type/schema/Array', () => { { a: 'hello', b: 'hello' }, ]) }) - - it('Should not validate for an array of intersection types when passing additionalProperties false', () => { + it('Should not validate for an array of composite types when passing additionalProperties false', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.String() }) - const C = Type.Intersect([A, B], { additionalProperties: false }) + const C = Type.Composite([A, B], { additionalProperties: false }) const T = Type.Array(C) Fail(T, [ { a: 'hello', b: 'hello' }, @@ -50,7 +44,6 @@ describe('type/schema/Array', () => { { a: 'hello', b: 'hello', c: 'additional' }, ]) }) - it('Should validate an array of tuples', () => { const A = Type.String() const B = Type.Number() @@ -62,7 +55,6 @@ describe('type/schema/Array', () => { ['hello', 1], ]) }) - it('Should not validate an array of tuples when tuple values are incorrect', () => { const A = Type.String() const B = Type.Number() @@ -82,6 +74,9 @@ describe('type/schema/Array', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) Fail(T, [0, 1, 2, 3]) }) + // --------------------------------------------------------- + // Unique Items + // --------------------------------------------------------- it('Should validate array with uniqueItems when items are distinct objects', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) Ok(T, [ @@ -100,4 +95,54 @@ describe('type/schema/Array', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) Fail(T, [0, 0]) }) + // --------------------------------------------------------- + // Contains + // --------------------------------------------------------- + it('Should validate for contains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1) }) + Ok(T, [1]) + Ok(T, [1, 2]) + Fail(T, []) + Fail(T, [2]) + }) + it('Should validate for minContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3 }) + Ok(T, [1, 1, 1, 2]) + Ok(T, [2, 1, 1, 1, 2]) + Ok(T, [1, 1, 1]) + Fail(T, []) + Fail(T, [1, 1]) + Fail(T, [2]) + }) + it('Should validate for maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), maxContains: 3 }) + Ok(T, [1, 1, 1]) + Ok(T, [1, 1]) + Ok(T, [2, 2, 2, 2, 1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + }) + it('Should validate for minContains and maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Ok(T, [1, 1, 1]) + Ok(T, [1, 1, 1, 1]) + Ok(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) + it('Should not validate minContains or maxContains when contains is unspecified', () => { + const T = Type.Array(Type.Number(), { minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Fail(T, [1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) + it('Should produce illogical schema when contains is not sub type of items', () => { + const T = Type.Array(Type.Number(), { contains: Type.String(), minContains: 3, maxContains: 5 }) + Fail(T, [1, 1]) + Fail(T, [1, 1, 1]) + Fail(T, [1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1]) + Fail(T, [1, 1, 1, 1, 1, 1]) + }) }) diff --git a/test/runtime/schema/boolean.ts b/test/runtime/schema/boolean.ts index a784cf6b9..f8f75a55e 100644 --- a/test/runtime/schema/boolean.ts +++ b/test/runtime/schema/boolean.ts @@ -7,32 +7,26 @@ describe('type/schema/Boolean', () => { Ok(T, true) Ok(T, false) }) - it('Should not validate a number', () => { const T = Type.Boolean() Fail(T, 1) }) - it('Should not validate a string', () => { const T = Type.Boolean() Fail(T, 'true') }) - it('Should not validate an array', () => { const T = Type.Boolean() Fail(T, [true]) }) - it('Should not validate an object', () => { const T = Type.Boolean() Fail(T, {}) }) - it('Should not validate an null', () => { const T = Type.Boolean() Fail(T, null) }) - it('Should not validate an undefined', () => { const T = Type.Boolean() Fail(T, undefined) diff --git a/test/runtime/schema/composite.ts b/test/runtime/schema/composite.ts index 93061fca4..23e5d1055 100644 --- a/test/runtime/schema/composite.ts +++ b/test/runtime/schema/composite.ts @@ -89,6 +89,6 @@ describe('type/schema/Composite', () => { }) const A = Type.Composite([T]) const B = Type.Composite([T]) - Assert.isEqual(A, B) + Assert.IsEqual(A, B) }) }) diff --git a/test/runtime/schema/date.ts b/test/runtime/schema/date.ts index 7843e4af1..659c704b9 100644 --- a/test/runtime/schema/date.ts +++ b/test/runtime/schema/date.ts @@ -1,66 +1,70 @@ -import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' +// --------------------------------------------------- +// No Longer Supported +// --------------------------------------------------- + +// import { Type } from '@sinclair/typebox' +// import { Ok, Fail } from './validate' // ---------------------------------------------------- // These tests are implemented by way of .addKeyword() // which are configured to use Value.Check() // ---------------------------------------------------- -describe('type/schema/Date', () => { - it('Should not validate number', () => { - const T = Type.Date() - Fail(T, 1) - }) - it('Should not validate string', () => { - const T = Type.Date() - Fail(T, 'hello') - }) - it('Should not validate boolean', () => { - const T = Type.Date() - Fail(T, true) - }) - it('Should not validate array', () => { - const T = Type.Date() - Fail(T, [1, 2, 3]) - }) - it('Should not validate object', () => { - const T = Type.Date() - Fail(T, { a: 1, b: 2 }) - }) - it('Should not validate null', () => { - const T = Type.Date() - Fail(T, null) - }) - it('Should not validate undefined', () => { - const T = Type.Date() - Fail(T, undefined) - }) - it('Should validate Date', () => { - const T = Type.Date() - Ok(T, new Date()) - }) - it('Should not validate Date if is invalid', () => { - const T = Type.Date() - Fail(T, new Date('not-a-valid-date')) - }) - it('Should validate Date minimumTimestamp', () => { - const T = Type.Date({ minimumTimestamp: 10 }) - Fail(T, new Date(9)) - Ok(T, new Date(10)) - }) - it('Should validate Date maximumTimestamp', () => { - const T = Type.Date({ maximumTimestamp: 10 }) - Ok(T, new Date(10)) - Fail(T, new Date(11)) - }) - it('Should validate Date exclusiveMinimumTimestamp', () => { - const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) - Fail(T, new Date(10)) - Ok(T, new Date(11)) - }) - it('Should validate Date exclusiveMaximumTimestamp', () => { - const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) - Ok(T, new Date(9)) - Fail(T, new Date(10)) - }) -}) +// describe('type/schema/Date', () => { +// it('Should not validate number', () => { +// const T = Type.Date() +// Fail(T, 1) +// }) +// it('Should not validate string', () => { +// const T = Type.Date() +// Fail(T, 'hello') +// }) +// it('Should not validate boolean', () => { +// const T = Type.Date() +// Fail(T, true) +// }) +// it('Should not validate array', () => { +// const T = Type.Date() +// Fail(T, [1, 2, 3]) +// }) +// it('Should not validate object', () => { +// const T = Type.Date() +// Fail(T, { a: 1, b: 2 }) +// }) +// it('Should not validate null', () => { +// const T = Type.Date() +// Fail(T, null) +// }) +// it('Should not validate undefined', () => { +// const T = Type.Date() +// Fail(T, undefined) +// }) +// it('Should validate Date', () => { +// const T = Type.Date() +// Ok(T, new Date()) +// }) +// it('Should not validate Date if is invalid', () => { +// const T = Type.Date() +// Fail(T, new Date('not-a-valid-date')) +// }) +// it('Should validate Date minimumTimestamp', () => { +// const T = Type.Date({ minimumTimestamp: 10 }) +// Fail(T, new Date(9)) +// Ok(T, new Date(10)) +// }) +// it('Should validate Date maximumTimestamp', () => { +// const T = Type.Date({ maximumTimestamp: 10 }) +// Ok(T, new Date(10)) +// Fail(T, new Date(11)) +// }) +// it('Should validate Date exclusiveMinimumTimestamp', () => { +// const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) +// Fail(T, new Date(10)) +// Ok(T, new Date(11)) +// }) +// it('Should validate Date exclusiveMaximumTimestamp', () => { +// const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) +// Ok(T, new Date(9)) +// Fail(T, new Date(10)) +// }) +// }) diff --git a/test/runtime/schema/enum.ts b/test/runtime/schema/enum.ts index 4e52e0ead..910e16e27 100644 --- a/test/runtime/schema/enum.ts +++ b/test/runtime/schema/enum.ts @@ -20,7 +20,6 @@ describe('type/schema/Enum', () => { Fail(T, 'Foo') Fail(T, 'Bar') }) - it('Should validate when emum has defined string values', () => { enum Kind { Foo = 'foo', @@ -30,7 +29,6 @@ describe('type/schema/Enum', () => { Ok(T, 'foo') Ok(T, 'bar') }) - it('Should not validate when emum has defined string values and user passes numeric', () => { enum Kind { Foo = 'foo', @@ -40,7 +38,6 @@ describe('type/schema/Enum', () => { Fail(T, 0) Fail(T, 1) }) - it('Should validate when enum has one or more string values', () => { enum Kind { Foo, diff --git a/test/runtime/schema/index.ts b/test/runtime/schema/index.ts index 6f60c4ce7..0c6e11f6a 100644 --- a/test/runtime/schema/index.ts +++ b/test/runtime/schema/index.ts @@ -8,7 +8,6 @@ import './integer' import './intersect' import './keyof' import './literal' -import './modifier' import './never' import './not' import './null' @@ -23,7 +22,7 @@ import './readonly' import './recursive' import './record' import './ref' -import './regex' +import './regexp' import './required' import './string' import './template-literal' diff --git a/test/runtime/schema/integer.ts b/test/runtime/schema/integer.ts index 281774ac3..5d984691f 100644 --- a/test/runtime/schema/integer.ts +++ b/test/runtime/schema/integer.ts @@ -6,32 +6,26 @@ describe('type/schema/Integer', () => { const T = Type.Integer() Fail(T, 3.14) }) - it('Should validate integer', () => { const T = Type.Integer() Ok(T, 1) }) - it('Should not validate string', () => { const T = Type.Integer() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Integer() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Integer() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Integer() Fail(T, { a: 1, b: 2 }) }) - it('Should not validate null', () => { const T = Type.Integer() Fail(T, null) @@ -40,31 +34,26 @@ describe('type/schema/Integer', () => { const T = Type.Integer() Fail(T, undefined) }) - it('Should validate minimum', () => { const T = Type.Integer({ minimum: 10 }) Fail(T, 9) Ok(T, 10) }) - it('Should validate maximum', () => { const T = Type.Integer({ maximum: 10 }) Ok(T, 10) Fail(T, 11) }) - it('Should validate Date exclusiveMinimum', () => { const T = Type.Integer({ exclusiveMinimum: 10 }) Fail(T, 10) Ok(T, 11) }) - it('Should validate Date exclusiveMaximum', () => { const T = Type.Integer({ exclusiveMaximum: 10 }) Ok(T, 9) Fail(T, 10) }) - it('Should not validate NaN', () => { Fail(Type.Integer(), NaN) }) diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts index 55f4f0659..5e4b64a4f 100644 --- a/test/runtime/schema/intersect.ts +++ b/test/runtime/schema/intersect.ts @@ -80,7 +80,6 @@ describe('type/schema/Intersect', () => { const T = Type.Intersect([A, B]) Fail(T, { x: 1, y: 1 }) }) - it('unevaluatedProperties with Record 1', () => { const T = Type.Intersect( [ diff --git a/test/runtime/schema/keyof.ts b/test/runtime/schema/keyof.ts index f637ee5f0..17b36914f 100644 --- a/test/runtime/schema/keyof.ts +++ b/test/runtime/schema/keyof.ts @@ -16,7 +16,6 @@ describe('type/schema/KeyOf', () => { Ok(T, 'z') Fail(T, 'w') }) - it('Should validate when using pick', () => { const T = Type.KeyOf( Type.Pick( @@ -32,7 +31,6 @@ describe('type/schema/KeyOf', () => { Ok(T, 'y') Fail(T, 'z') }) - it('Should validate when using omit', () => { const T = Type.KeyOf( Type.Omit( @@ -44,7 +42,6 @@ describe('type/schema/KeyOf', () => { ['x', 'y'], ), ) - Fail(T, 'x') Fail(T, 'y') Ok(T, 'z') diff --git a/test/runtime/schema/literal.ts b/test/runtime/schema/literal.ts index fe5e0f578..d517697d3 100644 --- a/test/runtime/schema/literal.ts +++ b/test/runtime/schema/literal.ts @@ -10,12 +10,10 @@ describe('type/schema/Literal', () => { const T = Type.Literal('hello') Ok(T, 'hello') }) - it('Should validate literal boolean', () => { const T = Type.Literal(true) Ok(T, true) }) - it('Should not validate invalid literal number', () => { const T = Type.Literal(42) Fail(T, 43) @@ -28,13 +26,11 @@ describe('type/schema/Literal', () => { const T = Type.Literal(false) Fail(T, true) }) - it('Should validate literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) Ok(T, 42) Ok(T, 'hello') }) - it('Should not validate invalid literal union', () => { const T = Type.Union([Type.Literal(42), Type.Literal('hello')]) Fail(T, 43) diff --git a/test/runtime/schema/modifier.ts b/test/runtime/schema/modifier.ts deleted file mode 100644 index 43e76c2b1..000000000 --- a/test/runtime/schema/modifier.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Type } from '@sinclair/typebox' - -describe('type/schema/Modifier', () => {}) diff --git a/test/runtime/schema/null.ts b/test/runtime/schema/null.ts index bd9c709bb..b338d78eb 100644 --- a/test/runtime/schema/null.ts +++ b/test/runtime/schema/null.ts @@ -6,32 +6,26 @@ describe('type/schema/Null', () => { const T = Type.Null() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Null() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Null() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Null() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Null() Fail(T, { a: 1, b: 2 }) }) - it('Should not validate null', () => { const T = Type.Null() Ok(T, null) }) - it('Should not validate undefined', () => { const T = Type.Null() Fail(T, undefined) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts index 99140d591..880a85e4a 100644 --- a/test/runtime/schema/number.ts +++ b/test/runtime/schema/number.ts @@ -6,12 +6,10 @@ describe('type/schema/Number', () => { const T = Type.Number() Ok(T, 3.14) }) - it('Should validate integer', () => { const T = Type.Number() Ok(T, 1) }) - it('Should not validate string', () => { const T = Type.Number() Fail(T, 'hello') @@ -36,31 +34,26 @@ describe('type/schema/Number', () => { const T = Type.Number() Fail(T, undefined) }) - it('Should validate minimum', () => { const T = Type.Number({ minimum: 10 }) Fail(T, 9) Ok(T, 10) }) - it('Should validate maximum', () => { const T = Type.Number({ maximum: 10 }) Ok(T, 10) Fail(T, 11) }) - it('Should validate Date exclusiveMinimum', () => { const T = Type.Number({ exclusiveMinimum: 10 }) Fail(T, 10) Ok(T, 11) }) - it('Should validate Date exclusiveMaximum', () => { const T = Type.Number({ exclusiveMaximum: 10 }) Ok(T, 9) Fail(T, 10) }) - it('Should not validate NaN', () => { Fail(Type.Number(), NaN) }) diff --git a/test/runtime/schema/object.ts b/test/runtime/schema/object.ts index 98b9a2761..cabe95979 100644 --- a/test/runtime/schema/object.ts +++ b/test/runtime/schema/object.ts @@ -6,27 +6,22 @@ describe('type/schema/Object', () => { const T = Type.Object({}) Fail(T, 42) }) - it('Should not validate a string', () => { const T = Type.Object({}) Fail(T, 'hello') }) - it('Should not validate a boolean', () => { const T = Type.Object({}) Fail(T, true) }) - it('Should not validate a null', () => { const T = Type.Object({}) Fail(T, null) }) - it('Should not validate an array', () => { const T = Type.Object({}) Fail(T, [1, 2]) }) - it('Should validate with correct property values', () => { const T = Type.Object({ a: Type.Number(), @@ -43,7 +38,6 @@ describe('type/schema/Object', () => { e: { x: 10, y: 20 }, }) }) - it('Should not validate with incorrect property values', () => { const T = Type.Object({ a: Type.Number(), diff --git a/test/runtime/schema/omit.ts b/test/runtime/schema/omit.ts index 3ba6a3aa3..7a1d6e108 100644 --- a/test/runtime/schema/omit.ts +++ b/test/runtime/schema/omit.ts @@ -15,7 +15,6 @@ describe('type/schema/Omit', () => { const T = Type.Omit(A, ['z']) Ok(T, { x: 1, y: 1 }) }) - it('Should remove required properties on the target schema', () => { const A = Type.Object( { @@ -28,12 +27,11 @@ describe('type/schema/Omit', () => { const T = Type.Omit(A, ['z']) strictEqual(T.required!.includes('z'), false) }) - it('Should delete the required property if no required properties remain', () => { const A = Type.Object( { x: Type.Optional(Type.Number()), - y: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Optional(Type.Number())), z: Type.Number(), }, { additionalProperties: false }, @@ -41,7 +39,6 @@ describe('type/schema/Omit', () => { const T = Type.Omit(A, ['z']) strictEqual(T.required, undefined) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -55,7 +52,6 @@ describe('type/schema/Omit', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) - it('Should omit with keyof object', () => { const A = Type.Object({ x: Type.Number(), diff --git a/test/runtime/schema/partial.ts b/test/runtime/schema/partial.ts index 9b9490288..c28f1368e 100644 --- a/test/runtime/schema/partial.ts +++ b/test/runtime/schema/partial.ts @@ -1,4 +1,4 @@ -import { Type, Modifier } from '@sinclair/typebox' +import { Type, Readonly, Optional } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -18,11 +18,10 @@ describe('type/schema/Partial', () => { Ok(T, { x: 1 }) Ok(T, {}) }) - it('Should update modifier types correctly when converting to partial', () => { const A = Type.Object( { - x: Type.ReadonlyOptional(Type.Number()), + x: Type.Readonly(Type.Optional(Type.Number())), y: Type.Readonly(Type.Number()), z: Type.Optional(Type.Number()), w: Type.Number(), @@ -30,12 +29,13 @@ describe('type/schema/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(T.properties.x[Modifier], 'ReadonlyOptional') - strictEqual(T.properties.y[Modifier], 'ReadonlyOptional') - strictEqual(T.properties.z[Modifier], 'Optional') - strictEqual(T.properties.w[Modifier], 'Optional') + strictEqual(T.properties.x[Readonly], 'Readonly') + strictEqual(T.properties.x[Optional], 'Optional') + strictEqual(T.properties.y[Readonly], 'Readonly') + strictEqual(T.properties.y[Optional], 'Optional') + strictEqual(T.properties.z[Optional], 'Optional') + strictEqual(T.properties.w[Optional], 'Optional') }) - it('Should inherit options from the source object', () => { const A = Type.Object( { diff --git a/test/runtime/schema/pick.ts b/test/runtime/schema/pick.ts index 99f49f700..39dde3a06 100644 --- a/test/runtime/schema/pick.ts +++ b/test/runtime/schema/pick.ts @@ -15,7 +15,6 @@ describe('type/schema/Pick', () => { const T = Type.Pick(Vector3, ['x', 'y']) Ok(T, { x: 1, y: 1 }) }) - it('Should remove required properties on the target schema', () => { const A = Type.Object( { @@ -28,12 +27,11 @@ describe('type/schema/Pick', () => { const T = Type.Pick(A, ['x', 'y']) strictEqual(T.required!.includes('z'), false) }) - it('Should delete the required property if no required properties remain', () => { const A = Type.Object( { x: Type.Optional(Type.Number()), - y: Type.ReadonlyOptional(Type.Number()), + y: Type.Readonly(Type.Optional(Type.Number())), z: Type.Number(), }, { additionalProperties: false }, @@ -41,7 +39,6 @@ describe('type/schema/Pick', () => { const T = Type.Pick(A, ['x', 'y']) strictEqual(T.required, undefined) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -55,7 +52,6 @@ describe('type/schema/Pick', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) - it('Should pick with keyof object', () => { const A = Type.Object({ x: Type.Number(), diff --git a/test/runtime/schema/readonly-optional.ts b/test/runtime/schema/readonly-optional.ts index 59f2f4baf..6025c3cf1 100644 --- a/test/runtime/schema/readonly-optional.ts +++ b/test/runtime/schema/readonly-optional.ts @@ -6,7 +6,7 @@ describe('type/schema/ReadonlyOptional', () => { it('Should validate object with optional', () => { const T = Type.Object( { - a: Type.ReadonlyOptional(Type.String()), + a: Type.Readonly(Type.Optional(Type.String())), b: Type.String(), }, { additionalProperties: false }, @@ -17,7 +17,7 @@ describe('type/schema/ReadonlyOptional', () => { it('Should remove required value from schema', () => { const T = Type.Object( { - a: Type.ReadonlyOptional(Type.String()), + a: Type.Readonly(Type.Optional(Type.String())), b: Type.String(), }, { additionalProperties: false }, diff --git a/test/runtime/schema/readonly.ts b/test/runtime/schema/readonly.ts index 50f75eb99..a651d3af3 100644 --- a/test/runtime/schema/readonly.ts +++ b/test/runtime/schema/readonly.ts @@ -13,7 +13,6 @@ describe('type/schema/Readonly', () => { ) Ok(T, { a: 'hello', b: 'world' }) }) - it('Should retain required array on object', () => { const T = Type.Object( { diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index d772d8817..6a6ba818c 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -55,7 +55,7 @@ describe('type/schema/Record', () => { Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) it('Should should validate when specifying regular expressions', () => { - const K = Type.RegEx(/^op_.*$/) + const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number()) Ok(T, { op_a: 1, @@ -64,7 +64,7 @@ describe('type/schema/Record', () => { }) }) it('Should should not validate when specifying regular expressions and passing invalid property', () => { - const K = Type.RegEx(/^op_.*$/) + const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { op_a: 1, diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts index 8ec132c0c..b2f90d756 100644 --- a/test/runtime/schema/recursive.ts +++ b/test/runtime/schema/recursive.ts @@ -10,9 +10,8 @@ describe('type/schema/Recursive', () => { nodes: Type.Array(Node), }), ) - Assert.isEqual(Node.$id === undefined, false) + Assert.IsEqual(Node.$id === undefined, false) }) - it('Should override default ordinal $id if specified', () => { const Node = Type.Recursive( (Node) => @@ -22,9 +21,8 @@ describe('type/schema/Recursive', () => { }), { $id: 'Node' }, ) - Assert.isEqual(Node.$id === 'Node', true) + Assert.IsEqual(Node.$id === 'Node', true) }) - it('Should validate recursive node type', () => { const Node = Type.Recursive((This) => Type.Object({ @@ -40,7 +38,6 @@ describe('type/schema/Recursive', () => { ], }) }) - it('Should validate wrapped recursive node type', () => { const Node = Type.Tuple([ Type.Recursive((This) => @@ -60,7 +57,6 @@ describe('type/schema/Recursive', () => { }, ]) }) - it('Should not validate wrapped recursive node type with invalid id', () => { const Node = Type.Tuple([ Type.Recursive((This) => diff --git a/test/runtime/schema/ref.ts b/test/runtime/schema/ref.ts index cfab94010..a3d522f89 100644 --- a/test/runtime/schema/ref.ts +++ b/test/runtime/schema/ref.ts @@ -23,7 +23,6 @@ describe('type/schema/Ref', () => { [T], ) }) - it('Should not validate when passing invalid data', () => { const T = Type.Object( { @@ -60,7 +59,6 @@ describe('type/schema/Ref', () => { }, { $id: 'T' }, ) - Ok(T, { x: 1, y: 2, z: 3 }, [R]) Ok(T, { x: 1, y: 2, z: 3, r: { name: 'hello' } }, [R]) Fail(T, { x: 1, y: 2, z: 3, r: { name: 1 } }, [R]) diff --git a/test/runtime/schema/regex.ts b/test/runtime/schema/regex.ts deleted file mode 100644 index 3c3b49a39..000000000 --- a/test/runtime/schema/regex.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' - -describe('type/schema/RegEx', () => { - it('Should validate numeric value', () => { - const T = Type.RegEx(/[012345]/) - Ok(T, '0') - Ok(T, '1') - Ok(T, '2') - Ok(T, '3') - Ok(T, '4') - Ok(T, '5') - }) - - it('Should validate true or false string value', () => { - const T = Type.RegEx(/true|false/) - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'false') - Ok(T, 'false') - Ok(T, 'false') - Fail(T, '6') - }) - - it('Should not validate failed regex test', () => { - const T = Type.RegEx(/true|false/) - Fail(T, 'unknown') - }) - - it('Should pass numeric 5 digit test', () => { - const T = Type.RegEx(/[\d]{5}/) - Ok(T, '12345') - }) -}) diff --git a/test/runtime/schema/regexp.ts b/test/runtime/schema/regexp.ts new file mode 100644 index 000000000..643b43b81 --- /dev/null +++ b/test/runtime/schema/regexp.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('type/schema/RegExp', () => { + //----------------------------------------------------- + // Regular Expression + //----------------------------------------------------- + it('Should validate regular expression 1', () => { + const T = Type.RegExp(/[012345]/) + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate regular expression 2', () => { + const T = Type.RegExp(/true|false/) + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate regular expression 3', () => { + const T = Type.RegExp(/true|false/) + Fail(T, 'unknown') + }) + it('Should validate regular expression 4', () => { + const T = Type.RegExp(/[\d]{5}/) + Ok(T, '12345') + }) + //----------------------------------------------------- + // Regular Pattern + //----------------------------------------------------- + it('Should validate pattern 1', () => { + const T = Type.RegExp('[012345]') + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate pattern 2', () => { + const T = Type.RegExp('true|false') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate pattern 3', () => { + const T = Type.RegExp('true|false') + Fail(T, 'unknown') + }) + it('Should validate pattern 4', () => { + const T = Type.RegExp('[\\d]{5}') + Ok(T, '12345') + }) +}) diff --git a/test/runtime/schema/required.ts b/test/runtime/schema/required.ts index fc8b6149e..19ab20f5c 100644 --- a/test/runtime/schema/required.ts +++ b/test/runtime/schema/required.ts @@ -1,4 +1,4 @@ -import { Type, Modifier } from '@sinclair/typebox' +import { Type, Readonly, Optional } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -18,21 +18,19 @@ describe('type/schema/Required', () => { Fail(T, { x: 1 }) Fail(T, {}) }) - it('Should update modifier types correctly when converting to required', () => { const A = Type.Object({ - x: Type.ReadonlyOptional(Type.Number()), + x: Type.Readonly(Type.Optional(Type.Number())), y: Type.Readonly(Type.Number()), z: Type.Optional(Type.Number()), w: Type.Number(), }) const T = Type.Required(A) - strictEqual(T.properties.x[Modifier], 'Readonly') - strictEqual(T.properties.y[Modifier], 'Readonly') - strictEqual(T.properties.z[Modifier], undefined) - strictEqual(T.properties.w[Modifier], undefined) + strictEqual(T.properties.x[Readonly], 'Readonly') + strictEqual(T.properties.y[Readonly], 'Readonly') + strictEqual(T.properties.z[Optional], undefined) + strictEqual(T.properties.w[Optional], undefined) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -46,7 +44,6 @@ describe('type/schema/Required', () => { strictEqual(A.additionalPropeties, false) strictEqual(T.additionalPropeties, false) }) - // it('Should construct new object when targetting reference', () => { // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) // const R = Type.Ref(T) diff --git a/test/runtime/schema/string.ts b/test/runtime/schema/string.ts index 3a536bddd..73b62efa2 100644 --- a/test/runtime/schema/string.ts +++ b/test/runtime/schema/string.ts @@ -30,34 +30,28 @@ describe('type/schema/String', () => { const T = Type.String() Fail(T, undefined) }) - it('Should validate string format as email', () => { const T = Type.String({ format: 'email' }) Ok(T, 'name@domain.com') }) - it('Should validate string format as uuid', () => { const T = Type.String({ format: 'uuid' }) Ok(T, '4a7a17c9-2492-4a53-8e13-06ea2d3f3bbf') }) - it('Should validate string format as iso8601 date', () => { const T = Type.String({ format: 'date-time' }) Ok(T, '2021-06-11T20:30:00-04:00') }) - it('Should validate minLength', () => { const T = Type.String({ minLength: 4 }) Ok(T, '....') Fail(T, '...') }) - it('Should validate maxLength', () => { const T = Type.String({ maxLength: 4 }) Ok(T, '....') Fail(T, '.....') }) - it('Should pass numeric 5 digit test', () => { const T = Type.String({ pattern: '[\\d]{5}' }) Ok(T, '12345') diff --git a/test/runtime/schema/template-literal.ts b/test/runtime/schema/template-literal.ts index 4b3b7985f..80c862b58 100644 --- a/test/runtime/schema/template-literal.ts +++ b/test/runtime/schema/template-literal.ts @@ -130,7 +130,6 @@ describe('type/schema/TemplateLiteral', () => { Ok(T, 'ccc') Ok(T, 'dddd') }) - it('Should validate infinite pattern 5', () => { // prettier-ignore const T = Type.TemplateLiteral([ diff --git a/test/runtime/schema/tuple.ts b/test/runtime/schema/tuple.ts index 773586e04..22f9999dc 100644 --- a/test/runtime/schema/tuple.ts +++ b/test/runtime/schema/tuple.ts @@ -9,50 +9,42 @@ describe('type/schema/Tuple', () => { const T = Type.Tuple([A, B]) Ok(T, ['hello', 42]) }) - it('Should not validate tuple of [string, number] when reversed', () => { const A = Type.String() const B = Type.Number() const T = Type.Tuple([A, B]) Fail(T, [42, 'hello']) }) - it('Should validate with empty tuple', () => { const T = Type.Tuple([]) Ok(T, []) }) - it('Should not validate with empty tuple with more items', () => { const T = Type.Tuple([]) Fail(T, [1]) }) - it('Should not validate with empty tuple with less items', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) Fail(T, [1]) }) - it('Should validate tuple of objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Ok(T, [{ a: 'hello' }, { b: 42 }]) }) - it('Should not validate tuple of objects when reversed', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Fail(T, [{ b: 42 }, { a: 'hello' }]) }) - it('Should not validate tuple when array is less than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) const T = Type.Tuple([A, B]) Fail(T, [{ a: 'hello' }]) }) - it('Should not validate tuple when array is greater than tuple length', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) diff --git a/test/runtime/schema/uint8array.ts b/test/runtime/schema/uint8array.ts index 04cb8f732..96e2dd39c 100644 --- a/test/runtime/schema/uint8array.ts +++ b/test/runtime/schema/uint8array.ts @@ -1,56 +1,49 @@ -import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' - -describe('type/schema/Uint8Array', () => { - it('Should not validate number', () => { - const T = Type.Uint8Array() - Fail(T, 1) - }) - - it('Should not validate string', () => { - const T = Type.Uint8Array() - Fail(T, 'hello') - }) - - it('Should not validate boolean', () => { - const T = Type.Uint8Array() - Fail(T, true) - }) - - it('Should not validate array', () => { - const T = Type.Uint8Array() - Fail(T, [1, 2, 3]) - }) - - it('Should not validate object', () => { - const T = Type.Uint8Array() - Fail(T, { a: 1, b: 2 }) - }) - - it('Should not validate null', () => { - const T = Type.Uint8Array() - Fail(T, null) - }) - - it('Should not validate undefined', () => { - const T = Type.Uint8Array() - Fail(T, undefined) - }) - - it('Should validate Uint8Array', () => { - const T = Type.Uint8Array() - Ok(T, new Uint8Array(100)) - }) - - it('Should validate minByteLength', () => { - const T = Type.Uint8Array({ minByteLength: 4 }) - Ok(T, new Uint8Array(4)) - Fail(T, new Uint8Array(3)) - }) - - it('Should validate maxByteLength', () => { - const T = Type.Uint8Array({ maxByteLength: 4 }) - Ok(T, new Uint8Array(4)) - Fail(T, new Uint8Array(5)) - }) -}) +// --------------------------------------------------- +// No Longer Supported +// --------------------------------------------------- +// import { Type } from '@sinclair/typebox' +// import { Ok, Fail } from './validate' +// describe('type/schema/Uint8Array', () => { +// it('Should not validate number', () => { +// const T = Type.Uint8Array() +// Fail(T, 1) +// }) +// it('Should not validate string', () => { +// const T = Type.Uint8Array() +// Fail(T, 'hello') +// }) +// it('Should not validate boolean', () => { +// const T = Type.Uint8Array() +// Fail(T, true) +// }) +// it('Should not validate array', () => { +// const T = Type.Uint8Array() +// Fail(T, [1, 2, 3]) +// }) +// it('Should not validate object', () => { +// const T = Type.Uint8Array() +// Fail(T, { a: 1, b: 2 }) +// }) +// it('Should not validate null', () => { +// const T = Type.Uint8Array() +// Fail(T, null) +// }) +// it('Should not validate undefined', () => { +// const T = Type.Uint8Array() +// Fail(T, undefined) +// }) +// it('Should validate Uint8Array', () => { +// const T = Type.Uint8Array() +// Ok(T, new Uint8Array(100)) +// }) +// it('Should validate minByteLength', () => { +// const T = Type.Uint8Array({ minByteLength: 4 }) +// Ok(T, new Uint8Array(4)) +// Fail(T, new Uint8Array(3)) +// }) +// it('Should validate maxByteLength', () => { +// const T = Type.Uint8Array({ maxByteLength: 4 }) +// Ok(T, new Uint8Array(4)) +// Fail(T, new Uint8Array(5)) +// }) +// }) diff --git a/test/runtime/schema/union.ts b/test/runtime/schema/union.ts index 10d3e5081..fc6493c91 100644 --- a/test/runtime/schema/union.ts +++ b/test/runtime/schema/union.ts @@ -11,7 +11,6 @@ describe('type/schema/Union', () => { Ok(T, true) Ok(T, 42) }) - it('Should validate union of objects', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ b: Type.String() }, { additionalProperties: false }) @@ -19,7 +18,6 @@ describe('type/schema/Union', () => { Ok(T, { a: 'hello' }) Ok(T, { b: 'world' }) }) - it('Should fail to validate for descriminated union types', () => { const A = Type.Object({ kind: Type.Literal('A'), value: Type.String() }) const B = Type.Object({ kind: Type.Literal('B'), value: Type.Number() }) @@ -27,7 +25,6 @@ describe('type/schema/Union', () => { Fail(T, { kind: 'A', value: 42 }) // expect { kind: 'A', value: string } Fail(T, { kind: 'B', value: 'hello' }) // expect { kind: 'B', value: number } }) - it('Should validate union of objects where properties overlap', () => { const A = Type.Object({ a: Type.String() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) @@ -35,7 +32,6 @@ describe('type/schema/Union', () => { Ok(T, { a: 'hello' }) // A Ok(T, { a: 'hello', b: 'world' }) // B }) - it('Should validate union of overlapping property of varying type', () => { const A = Type.Object({ a: Type.String(), b: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ a: Type.String(), b: Type.String() }, { additionalProperties: false }) @@ -43,7 +39,6 @@ describe('type/schema/Union', () => { Ok(T, { a: 'hello', b: 42 }) // A Ok(T, { a: 'hello', b: 'world' }) // B }) - it('Should validate union of literal strings', () => { const A = Type.Literal('hello') const B = Type.Literal('world') @@ -51,7 +46,6 @@ describe('type/schema/Union', () => { Ok(T, 'hello') // A Ok(T, 'world') // B }) - it('Should not validate union of literal strings for unknown string', () => { const A = Type.Literal('hello') const B = Type.Literal('world') diff --git a/test/runtime/schema/validate.ts b/test/runtime/schema/validate.ts index 6e9cf4eac..12648225b 100644 --- a/test/runtime/schema/validate.ts +++ b/test/runtime/schema/validate.ts @@ -25,7 +25,6 @@ function schemaOf(schemaOf: string, value: unknown, schema: unknown) { return false } } - export function createAjv(references: AnySchema[]) { return addFormats(new Ajv({}), ['date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex']) .addKeyword({ type: 'object', keyword: 'instanceOf', validate: schemaOf }) @@ -38,7 +37,6 @@ export function createAjv(references: AnySchema[]) { .addKeyword('maxByteLength') .addSchema(references) } - export function Ok(type: T, data: unknown, additional: AnySchema[] = []) { const ajv = createAjv(additional) function execute() { @@ -65,7 +63,6 @@ export function Ok(type: T, data: unknown, additional: AnySch throw Error('expected ok') } } - export function Fail(type: T, data: unknown, additional: AnySchema[] = []) { const ajv = createAjv(additional) function execute() { diff --git a/test/runtime/schema/void.ts b/test/runtime/schema/void.ts index 99d984c3e..1aab66be4 100644 --- a/test/runtime/schema/void.ts +++ b/test/runtime/schema/void.ts @@ -6,32 +6,26 @@ describe('type/schema/Void', () => { const T = Type.Void() Fail(T, 1) }) - it('Should not validate string', () => { const T = Type.Void() Fail(T, 'hello') }) - it('Should not validate boolean', () => { const T = Type.Void() Fail(T, true) }) - it('Should not validate array', () => { const T = Type.Void() Fail(T, [1, 2, 3]) }) - it('Should not validate object', () => { const T = Type.Void() Fail(T, { a: 1, b: 2 }) }) - it('Should validate null', () => { const T = Type.Null() Ok(T, null) }) - it('Should not validate undefined', () => { const T = Type.Void() Fail(T, undefined) diff --git a/test/runtime/system/system.ts b/test/runtime/system/system.ts index 33e3373db..9528e6dc8 100644 --- a/test/runtime/system/system.ts +++ b/test/runtime/system/system.ts @@ -38,7 +38,6 @@ describe('system/TypeSystem/ExactOptionalPropertyTypes', () => { Ok(T, { x: undefined }) }) }) - describe('system/TypeSystem/AllowNaN', () => { before(() => { TypeSystem.AllowNaN = true @@ -97,7 +96,6 @@ describe('system/TypeSystem/AllowNaN', () => { Fail(T, -Infinity) }) }) - describe('system/TypeSystem/AllowArrayObjects', () => { before(() => { TypeSystem.AllowArrayObjects = true @@ -174,7 +172,6 @@ describe('system/TypeSystem/AllowVoidNull', () => { Ok(T, null) }) }) - describe('system/TypeSystem/Format', () => { it('Should create and validate a format', () => { TypeSystem.Format('CreateFormat0', (value) => value === value.toLowerCase()) @@ -184,10 +181,9 @@ describe('system/TypeSystem/Format', () => { }) it('Should throw if registering the same format twice', () => { TypeSystem.Format('CreateFormat1', (value) => true) - Assert.throws(() => TypeSystem.Format('CreateFormat1', (value) => true)) + Assert.Throws(() => TypeSystem.Format('CreateFormat1', (value) => true)) }) }) - describe('system/TypeSystem/Type', () => { it('Should create and validate a type', () => { type BigNumberOptions = { minimum?: bigint; maximum?: bigint } @@ -204,6 +200,6 @@ describe('system/TypeSystem/Type', () => { }) it('Should throw if registering the same type twice', () => { TypeSystem.Type('CreateType1', () => true) - Assert.throws(() => TypeSystem.Type('CreateType1', () => true)) + Assert.Throws(() => TypeSystem.Type('CreateType1', () => true)) }) }) diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts index ae1ae1597..3dd3e83ee 100644 --- a/test/runtime/type/extends/any.ts +++ b/test/runtime/type/extends/any.ts @@ -6,91 +6,91 @@ describe('type/extends/Any', () => { it('Should extend Any', () => { type T = any extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = any extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = any extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Boolean', () => { type T = any extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Number', () => { type T = any extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Integer', () => { type T = any extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Array 1', () => { type T = any extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Array 2', () => { type T = any extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Tuple', () => { type T = any extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 1', () => { type T = any extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 2', () => { type T = any extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Object 3', () => { type T = any extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 1', () => { type T = any extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 2', () => { type T = any extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = any extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Undefined', () => { type T = any extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Void', () => { type T = any extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Date', () => { type T = any extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) }) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts index ac7b04e45..b734bc8ab 100644 --- a/test/runtime/type/extends/array.ts +++ b/test/runtime/type/extends/array.ts @@ -9,22 +9,22 @@ describe('type/extends/Array', () => { it('Should extend Array Varying 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 3', () => { type T = Array extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array Varying 4', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Any @@ -32,112 +32,112 @@ describe('type/extends/Array', () => { it('Should extend Any', () => { type T = Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Constrained @@ -145,121 +145,121 @@ describe('type/extends/Array', () => { it('Should extend constrained Any', () => { type T = Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained String', () => { type T = Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Array 1', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Array 2', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Array 3', () => { type T = Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Tuple', () => { type T = Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Array extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 3', () => { type T = Array extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 5', () => { type T = Array extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 3', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 4', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 5', () => { type T = Array extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Null', () => { type T = Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Void', () => { type T = Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/async-iterator.ts b/test/runtime/type/extends/async-iterator.ts new file mode 100644 index 000000000..4eba4c020 --- /dev/null +++ b/test/runtime/type/extends/async-iterator.ts @@ -0,0 +1,72 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/AsyncIterator', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + it('Should extend AsyncIterator 1', () => { + type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Any()), Type.AsyncIterator(Type.Any())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend AsyncIterator 2', () => { + type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend AsyncIterator 3', () => { + type T = AsyncIterableIterator<'hello'> extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Literal('hello')), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend AsyncIterator 4', () => { + type T = AsyncIterableIterator extends AsyncIterableIterator<'hello'> ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Literal('hello'))) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend AsyncIterator 5', () => { + type T = AsyncIterableIterator extends AsyncIterableIterator<'hello' | number> ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend AsyncIterator 6', () => { + type T = AsyncIterableIterator<'hello' | number> extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + // -------------------------------------------------------------------- + // Structural + // -------------------------------------------------------------------- + it('Should extends Any 1', () => { + type T = AsyncIterableIterator extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extends Any 2', () => { + type T = any extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.Union) + }) + it('Should extends Unknown 1', () => { + type T = AsyncIterableIterator extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extends Unknown 2', () => { + type T = unknown extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extends Never 1', () => { + type T = AsyncIterableIterator extends never ? 1 : 2 + const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extends Never 2', () => { + type T = never extends AsyncIterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Never(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) +}) diff --git a/test/runtime/type/extends/bigint.ts b/test/runtime/type/extends/bigint.ts index 454e22cbe..5346cfa39 100644 --- a/test/runtime/type/extends/bigint.ts +++ b/test/runtime/type/extends/bigint.ts @@ -6,96 +6,96 @@ describe('type/extends/BigInt', () => { it('Should extend Any', () => { type T = bigint extends any ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = bigint extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = bigint extends string ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = bigint extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = bigint extends number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = bigint extends number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = bigint extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = bigint extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = bigint extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = bigint extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = bigint extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = bigint extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = bigint extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = bigint extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = bigint extends boolean | bigint ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = bigint extends null ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = bigint extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = bigint extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = bigint extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.BigInt(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts index 8bb9b460f..1d56e21eb 100644 --- a/test/runtime/type/extends/boolean.ts +++ b/test/runtime/type/extends/boolean.ts @@ -6,86 +6,86 @@ describe('type/extends/Boolean', () => { it('Should extend Any', () => { type T = boolean extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = boolean extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = boolean extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Number', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = boolean extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = boolean extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = boolean extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = boolean extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = boolean extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = boolean extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = boolean extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = boolean extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = boolean extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = boolean extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = boolean extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = boolean extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = boolean extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts index 82211cbe1..f5b6151d9 100644 --- a/test/runtime/type/extends/constructor.ts +++ b/test/runtime/type/extends/constructor.ts @@ -6,206 +6,206 @@ describe('type/extends/Constructor', () => { it('Should extend Function', () => { type T = (new () => number) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 2', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 3', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 4', () => { type T = (new (a: number) => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 5', () => { type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 6', () => { type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 7', () => { type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 8', () => { type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => number) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => any) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 10', () => { type T = (new () => Array) extends new () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 11', () => { type T = (new () => Array) extends new () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 12', () => { type T = (new () => object) extends new () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Constructor 13', () => { type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 14', () => { type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 15', () => { type T = (new () => any) extends new () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Constructor 16', () => { type T = (new () => unknown) extends new () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Any', () => { type T = (new () => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = (new () => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = (new () => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = (new () => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = (new () => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = (new () => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = (new () => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = (new () => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = (new () => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = (new () => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = (new () => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = (new () => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = (new () => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = (new () => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = (new () => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = (new () => number) extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = (new () => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts index 7aba817c8..04df6c661 100644 --- a/test/runtime/type/extends/date.ts +++ b/test/runtime/type/extends/date.ts @@ -6,91 +6,91 @@ describe('type/extends/Date', () => { it('Should extend Any', () => { type T = Date extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Date extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Date extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Date extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Date extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Date extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Date extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Date extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Date extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Date extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Date extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Date extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Date extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Date extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Date extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Date extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Date extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Date(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts index 7823aa382..0b334087d 100644 --- a/test/runtime/type/extends/function.ts +++ b/test/runtime/type/extends/function.ts @@ -6,206 +6,206 @@ describe('type/extends/Function', () => { it('Should extend Constructor 1', () => { type T = (() => number) extends new () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Function 1', () => { type T = (() => number) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 2', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 3', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 4', () => { type T = ((a: number) => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Function 5', () => { type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 6', () => { type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Function 7', () => { type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Function 8', () => { type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => number) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => any) extends () => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 10', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 11', () => { type T = (() => Array) extends () => object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 12', () => { type T = (() => object) extends () => Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Function 13', () => { type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 14', () => { type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 15', () => { type T = (() => any) extends () => unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Function 16', () => { type T = (() => unknown) extends () => any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Any', () => { type T = (() => number) extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = (() => number) extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = (() => number) extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = (() => number) extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = (() => number) extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = (() => number) extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = (() => number) extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = (() => number) extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = (() => number) extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 4', () => { type T = (() => number) extends { length: '1' } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = (() => number) extends { length: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = (() => number) extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = (() => number) extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 5', () => { type T = (() => number) extends any | Array ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = (() => number) extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = (() => number) extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = (() => number) extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/index.ts b/test/runtime/type/extends/index.ts index be6eed0b2..c826cc0fe 100644 --- a/test/runtime/type/extends/index.ts +++ b/test/runtime/type/extends/index.ts @@ -1,11 +1,13 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './constructor' import './date' import './function' import './integer' +import './iterator' import './literal' import './not' import './null' diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts index d0113e0f2..1c5d00995 100644 --- a/test/runtime/type/extends/integer.ts +++ b/test/runtime/type/extends/integer.ts @@ -6,91 +6,91 @@ describe('type/extends/Integer', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Integer(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/iterator.ts b/test/runtime/type/extends/iterator.ts new file mode 100644 index 000000000..1cf5b6bfd --- /dev/null +++ b/test/runtime/type/extends/iterator.ts @@ -0,0 +1,72 @@ +import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/extends/Iterator', () => { + // ---------------------------------------------- + // Generic Varying + // ---------------------------------------------- + it('Should extend Iterator 1', () => { + type T = IterableIterator extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Any()), Type.Iterator(Type.Any())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend Iterator 2', () => { + type T = IterableIterator extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend Iterator 3', () => { + type T = IterableIterator<'hello'> extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Literal('hello')), Type.Iterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extend Iterator 4', () => { + type T = IterableIterator extends IterableIterator<'hello'> ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Literal('hello'))) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend Iterator 5', () => { + type T = IterableIterator extends IterableIterator<'hello' | number> ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend Iterator 6', () => { + type T = IterableIterator<'hello' | number> extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.Iterator(Type.String())) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + // -------------------------------------------------------------------- + // Structural + // -------------------------------------------------------------------- + it('Should extends Any 1', () => { + type T = IterableIterator extends any ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extends Any 2', () => { + type T = any extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Any(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.Union) + }) + it('Should extends Unknown 1', () => { + type T = IterableIterator extends unknown ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, TypeExtendsResult.True) + }) + it('Should extends Unknown 2', () => { + type T = unknown extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Unknown(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extends Never 1', () => { + type T = IterableIterator extends never ? 1 : 2 + const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extends Never 2', () => { + type T = never extends IterableIterator ? 1 : 2 + const R = TypeExtends.Extends(Type.Never(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, TypeExtendsResult.True) + }) +}) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts index a99e26a95..7c23890fc 100644 --- a/test/runtime/type/extends/literal.ts +++ b/test/runtime/type/extends/literal.ts @@ -9,77 +9,77 @@ describe('type/extends/Literal', () => { it('Should extend Any (String)', () => { type T = 'hello' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (String)', () => { type T = 'hello' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String (String)', () => { type T = 'hello' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (String)', () => { type T = 'hello' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (String)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array (String)', () => { type T = 'hello' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (String)', () => { type T = 'hello' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (String)', () => { type T = 'hello' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (String)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (String)', () => { type T = 'hello' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (String)', () => { type T = 'hello' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (String)', () => { type T = 'hello' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null (String)', () => { type T = 'hello' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (String)', () => { type T = 'hello' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // Number Literal @@ -87,77 +87,77 @@ describe('type/extends/Literal', () => { it('Should extend Any (Number)', () => { type T = 10 extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (Number)', () => { type T = 10 extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String (Number)', () => { type T = 10 extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean (Number)', () => { type T = 10 extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Integer (Number)', () => { type T = 10 extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array (Number)', () => { type T = 10 extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (Number)', () => { type T = 10 extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (Number)', () => { type T = 10 extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (Number)', () => { type T = 10 extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (Number)', () => { type T = 10 extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (Number)', () => { type T = 10 extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (Number)', () => { type T = 10 extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null (Number)', () => { type T = 10 extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (Number)', () => { type T = 10 extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // Boolean Literal @@ -165,101 +165,101 @@ describe('type/extends/Literal', () => { it('Should extend Any (Boolean)', () => { type T = true extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (Boolean)', () => { type T = true extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String (Boolean)', () => { type T = true extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean (Boolean)', () => { type T = true extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Number (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (Boolean)', () => { type T = true extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array (Boolean)', () => { type T = true extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (Boolean)', () => { type T = true extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = 'hello' extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = 10 extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 3', () => { type T = true extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (Boolean)', () => { type T = true extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (Boolean)', () => { type T = true extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (Boolean)', () => { type T = true extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2 (Boolean)', () => { type T = true extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (Boolean)', () => { type T = true extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null (Boolean)', () => { type T = true extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (Boolean)', () => { type T = true extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = true extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = true extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/not.ts b/test/runtime/type/extends/not.ts index e23e5352f..53f8d1e40 100644 --- a/test/runtime/type/extends/not.ts +++ b/test/runtime/type/extends/not.ts @@ -19,7 +19,7 @@ describe('type/extends/Not', () => { const A = Type.Number() const B = Type.Not(Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) // we would expect false + Assert.IsEqual(R, TypeExtendsResult.True) // we would expect false }) // --------------------------------------------------------------------------- // Nested @@ -37,11 +37,11 @@ describe('type/extends/Not', () => { const R4 = TypeExtends.Extends(T4, Type.String()) const R5 = TypeExtends.Extends(T5, Type.String()) - Assert.isEqual(R1, TypeExtendsResult.True) - Assert.isEqual(R2, TypeExtendsResult.False) - Assert.isEqual(R3, TypeExtendsResult.True) - Assert.isEqual(R4, TypeExtendsResult.False) - Assert.isEqual(R5, TypeExtendsResult.True) + Assert.IsEqual(R1, TypeExtendsResult.True) + Assert.IsEqual(R2, TypeExtendsResult.False) + Assert.IsEqual(R3, TypeExtendsResult.True) + Assert.IsEqual(R4, TypeExtendsResult.False) + Assert.IsEqual(R5, TypeExtendsResult.True) }) // --------------------------------------------------------------------------- @@ -50,96 +50,96 @@ describe('type/extends/Not', () => { it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts index dc9077b94..702e16271 100644 --- a/test/runtime/type/extends/null.ts +++ b/test/runtime/type/extends/null.ts @@ -6,96 +6,96 @@ describe('type/extends/Null', () => { it('Should extend Any', () => { type T = null extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = null extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = null extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = null extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = null extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = null extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = null extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = null extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = null extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = null extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = null extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = null extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = null extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = null extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = null extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Undefined', () => { type T = null extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = null extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = null extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Null(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts index c34c88573..aa647eca6 100644 --- a/test/runtime/type/extends/number.ts +++ b/test/runtime/type/extends/number.ts @@ -6,91 +6,91 @@ describe('type/extends/Number', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts index 0ab311516..172eb46bf 100644 --- a/test/runtime/type/extends/object.ts +++ b/test/runtime/type/extends/object.ts @@ -11,42 +11,42 @@ describe('type/extends/Object', () => { const A = Type.Object({ x: Type.Number(), y: Type.Number() }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = { x: number } extends { x: number; y: number } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = { x: number; y: string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.String() }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 4', () => { type T = { x: number } extends { x: number; y: string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.String() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 5', () => { type T = { x: number | string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const B = Type.Object({ x: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 6', () => { type T = { x: number } extends { x: number | string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) // ---------------------------------------------------------- // Record @@ -56,35 +56,35 @@ describe('type/extends/Object', () => { const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 5', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 6', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) // ---------------------------------------------------------- // Standard @@ -92,86 +92,86 @@ describe('type/extends/Object', () => { it('Should extend Any', () => { type T = { a: number } extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = { a: number } extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = { a: number } extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = { a: number } extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = { a: number } extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = { a: number } extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = { a: number } extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = { a: number } extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = { a: number } extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = { a: number } extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = { a: number } extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = { a: number } extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = { a: number } extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = { a: number } extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = { a: number } extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = { a: number } extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = { a: number } extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts index 7ebd71de5..843786a64 100644 --- a/test/runtime/type/extends/promise.ts +++ b/test/runtime/type/extends/promise.ts @@ -9,22 +9,22 @@ describe('type/extends/Promise', () => { it('Should extend Promise Varying 1', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 2', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 3', () => { type T = Promise extends Promise ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Promise Varying 4', () => { type T = Promise extends Promise ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Any @@ -32,87 +32,87 @@ describe('type/extends/Promise', () => { it('Should extend Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Promise extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ---------------------------------------------- // Constrained @@ -120,91 +120,91 @@ describe('type/extends/Promise', () => { it('Should extend constrained Any', () => { type T = Promise extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Promise extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained String', () => { type T = Promise extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Promise extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Promise extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Array', () => { type T = Promise extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Promise extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Object 3', () => { type T = Promise extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Promise extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Promise extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend constrained Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Null', () => { type T = Promise extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Promise extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Promise extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Promise extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/record.ts b/test/runtime/type/extends/record.ts index 369e6050a..7ac3301c2 100644 --- a/test/runtime/type/extends/record.ts +++ b/test/runtime/type/extends/record.ts @@ -8,77 +8,77 @@ describe('type/extends/Record', () => { const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) // ----- it('Should extend Record 12', () => { @@ -86,28 +86,28 @@ describe('type/extends/Record', () => { const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) const R = TypeExtends.Extends(A, B) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) // ------------------------------------------------------------------- // Standard @@ -115,86 +115,86 @@ describe('type/extends/Record', () => { it('Should extend Any', () => { type T = Record extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Record extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = Record extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Record extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts index dc8954666..0dd95ecbe 100644 --- a/test/runtime/type/extends/string.ts +++ b/test/runtime/type/extends/string.ts @@ -6,106 +6,106 @@ describe('type/extends/String', () => { it('Should extend Any', () => { type T = string extends any ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = string extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = string extends string ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean', () => { type T = string extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = string extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = string extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = string extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = string extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = string extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = number extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/symbol.ts b/test/runtime/type/extends/symbol.ts index e80838409..03dc7aaf0 100644 --- a/test/runtime/type/extends/symbol.ts +++ b/test/runtime/type/extends/symbol.ts @@ -6,101 +6,101 @@ describe('type/extends/Symbol', () => { it('Should extend Any', () => { type T = symbol extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = symbol extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = symbol extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = symbol extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = symbol extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = symbol extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = symbol extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = symbol extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = symbol extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = symbol extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = symbol extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = symbol extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = symbol extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = symbol extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = symbol extends boolean | symbol ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = symbol extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = symbol extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = symbol extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = symbol extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol extends symbol ? 1 : 2 const R = TypeExtends.Extends(Type.Symbol(), Type.Symbol()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/template-literal.ts b/test/runtime/type/extends/template-literal.ts index c8a98aa19..3dd6ff3d2 100644 --- a/test/runtime/type/extends/template-literal.ts +++ b/test/runtime/type/extends/template-literal.ts @@ -9,77 +9,77 @@ describe('type/extends/TemplateLiteral', () => { it('Should extend Any (hello)', () => { type T = 'hello' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (hello)', () => { type T = 'hello' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String (hello)', () => { type T = 'hello' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (hello)', () => { type T = 'hello' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number (hello)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (hello)', () => { type T = 'hello' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array (hello)', () => { type T = 'hello' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (hello)', () => { type T = 'hello' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (hello)', () => { type T = 'hello' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (hello)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (hello)', () => { type T = 'hello' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (hello)', () => { type T = 'hello' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (hello)', () => { type T = 'hello' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null (hello)', () => { type T = 'hello' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (hello)', () => { type T = 'hello' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) // ------------------------------------------------------------------- // String Literal 'hello' | 'world' @@ -87,76 +87,76 @@ describe('type/extends/TemplateLiteral', () => { it('Should extend Any (hello | world)', () => { type T = 'hello' | 'world' extends any ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown (hello | world)', () => { type T = 'hello' | 'world' extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String (hello | world)', () => { type T = 'hello' | 'world' extends string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Boolean (hello | world)', () => { type T = 'hello' | 'world' extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array (hello | world)', () => { type T = 'hello' | 'world' extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple (hello | world)', () => { type T = 'hello' | 'world' extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1 (hello | world)', () => { type T = 'hello' | 'world' extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2 (hello | world)', () => { type T = 'hello' | 'world' extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1 (hello | world)', () => { type T = 'hello' | 'world' extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2 (hello | world)', () => { type T = 'hello' | 'world' extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3 (hello | world)', () => { type T = 'hello' | 'world' extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null (hello | world)', () => { type T = 'hello' | 'world' extends null ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined (hello | world)', () => { type T = 'hello' | 'world' extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/tuple.ts b/test/runtime/type/extends/tuple.ts index f763fb884..b297655ab 100644 --- a/test/runtime/type/extends/tuple.ts +++ b/test/runtime/type/extends/tuple.ts @@ -6,156 +6,156 @@ describe('type/extends/Tuple', () => { it('Should extend Any', () => { type T = [string, number] extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = [string, number] extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = [string, number] extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = [string, number] extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = [string, number] extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array 2', () => { type T = [string, number] extends Array ? 1 : 2 // 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 3', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Array 4', () => { type T = [string, number] extends Array ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 1', () => { type T = [string, number] extends [string, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 2', () => { type T = [string, number] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 3', () => { type T = [string, any] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 4', () => { type T = [string, number] extends [string, any] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 5', () => { type T = [string, unknown] extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 6', () => { type T = [string, number] extends [string, unknown] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Tuple 7', () => { type T = [] extends [string, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple 8', () => { type T = [string, number] extends [] ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record 1', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 2', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 3', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Record 4', () => { type T = [string, number] extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = [string, number] extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = [string, number] extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = [string, number] extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 1', () => { type T = [string, number] extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = [string, number] extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = [string, number] extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = [string, number] extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = [string, number] extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = [string, number] extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = [string, number] extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts index 286e00d9e..828163dd1 100644 --- a/test/runtime/type/extends/uint8array.ts +++ b/test/runtime/type/extends/uint8array.ts @@ -6,91 +6,91 @@ describe('type/extends/Uint8Array', () => { it('Should extend Any', () => { type T = Uint8Array extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = Uint8Array extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = Uint8Array extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = Uint8Array extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = Uint8Array extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = Uint8Array extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Record', () => { type T = Uint8Array extends Record ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 1', () => { type T = Uint8Array extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = Uint8Array extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = Uint8Array extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = Uint8Array extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = Uint8Array extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = Uint8Array extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = Uint8Array extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = Uint8Array extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = Uint8Array extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts index 78b8f4a2b..664126124 100644 --- a/test/runtime/type/extends/undefined.ts +++ b/test/runtime/type/extends/undefined.ts @@ -6,86 +6,86 @@ describe('type/extends/Undefined', () => { it('Should extend Any', () => { type T = undefined extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = undefined extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = undefined extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = undefined extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = undefined extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = undefined extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = undefined extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = undefined extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = undefined extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = undefined extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = undefined extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = undefined extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = undefined extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = undefined extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Date', () => { type T = undefined extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/union.ts b/test/runtime/type/extends/union.ts index 35b00def8..e9eb05cef 100644 --- a/test/runtime/type/extends/union.ts +++ b/test/runtime/type/extends/union.ts @@ -6,131 +6,131 @@ describe('type/extends/Union', () => { it('Should extend Any', () => { type T = number | string extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = number | string extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = number | string extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = number | string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = number | string extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array', () => { type T = number | string extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = number | string extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = number | string extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Object 2', () => { type T = number | string extends { a: 10 } ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = number | string extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 2', () => { type T = number | string extends any | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = number | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 4', () => { type T = any | boolean extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 5', () => { type T = any | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 6', () => { type T = any | {} extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 7', () => { type T = any extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.Union) + Assert.IsEqual(R, TypeExtendsResult.Union) }) it('Should extend Union 8', () => { type T = unknown | string extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 9', () => { type T = unknown extends boolean | number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Null', () => { type T = number | string extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = number | string extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = number | string extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void 2', () => { type T = number | string | void extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date 2', () => { type T = Date | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend BigInt', () => { type T = bigint | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol | number | string | void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts index 4a5adba9d..85dbf8654 100644 --- a/test/runtime/type/extends/unknown.ts +++ b/test/runtime/type/extends/unknown.ts @@ -6,96 +6,96 @@ describe('type/extends/Unknown', () => { it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts index 8cf53aee2..106e1759e 100644 --- a/test/runtime/type/extends/void.ts +++ b/test/runtime/type/extends/void.ts @@ -6,101 +6,101 @@ describe('type/extends/Void', () => { it('Should extend Any', () => { type T = void extends any ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Any()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Unknown', () => { type T = void extends unknown ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend String', () => { type T = void extends string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.String()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Boolean', () => { type T = void extends boolean ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Number', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Number()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Integer', () => { type T = void extends number ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Integer()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 1', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Array 2', () => { type T = void extends Array ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Tuple', () => { type T = void extends [number, number] ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 1', () => { type T = void extends object ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 2', () => { type T = void extends {} ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Object 3', () => { type T = void extends { a: number } ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 1', () => { type T = void extends number | string ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Union 2', () => { type T = void extends any | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 3', () => { type T = void extends unknown | number ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Union 4', () => { type T = void extends unknown | any ? 1 : 2 // 1 const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Null', () => { type T = void extends null ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Null()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Undefined', () => { type T = void extends undefined ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) it('Should extend Void', () => { type T = void extends void ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Void()) - Assert.isEqual(R, TypeExtendsResult.True) + Assert.IsEqual(R, TypeExtendsResult.True) }) it('Should extend Date', () => { type T = void extends Date ? 1 : 2 const R = TypeExtends.Extends(Type.Void(), Type.Date()) - Assert.isEqual(R, TypeExtendsResult.False) + Assert.IsEqual(R, TypeExtendsResult.False) }) }) diff --git a/test/runtime/type/guard/any.ts b/test/runtime/type/guard/any.ts index ba3eaed09..bb35ccc03 100644 --- a/test/runtime/type/guard/any.ts +++ b/test/runtime/type/guard/any.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TAny', () => { it('Should guard for TAny', () => { const R = TypeGuard.TAny(Type.Any()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TAny', () => { const R = TypeGuard.TAny(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TAny with invalid $id', () => { // @ts-ignore const R = TypeGuard.TAny(Type.Any({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/array.ts b/test/runtime/type/guard/array.ts index 80a185979..95b0063aa 100644 --- a/test/runtime/type/guard/array.ts +++ b/test/runtime/type/guard/array.ts @@ -5,14 +5,12 @@ import { Assert } from '../../assert/index' describe('type/guard/TArray', () => { it('Should guard for TArray', () => { const R = TypeGuard.TArray(Type.Array(Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TArray', () => { const R = TypeGuard.TArray(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should guard for nested object TArray', () => { const R = TypeGuard.TArray( Type.Array( @@ -22,9 +20,8 @@ describe('type/guard/TArray', () => { }), ), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for nested object TArray', () => { const R = TypeGuard.TArray( Type.Array( @@ -34,30 +31,26 @@ describe('type/guard/TArray', () => { }), ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TArray with invalid $id', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.Number(), { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TArray with invalid minItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { minItems: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TArray with invalid maxItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { maxItems: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TArray with invalid uniqueItems', () => { // @ts-ignore const R = TypeGuard.TArray(Type.Array(Type.String(), { uniqueItems: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/async-iterator.ts b/test/runtime/type/guard/async-iterator.ts new file mode 100644 index 000000000..8b3567e5b --- /dev/null +++ b/test/runtime/type/guard/async-iterator.ts @@ -0,0 +1,22 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TAsyncIterator', () => { + it('Should guard for TAsyncIterator', () => { + const T = Type.AsyncIterator(Type.Any()) + const R = TypeGuard.TAsyncIterator(T) + Assert.IsTrue(R) + }) + it('Should not guard for TAsyncIterator', () => { + const T = null + const R = TypeGuard.TAsyncIterator(T) + Assert.IsFalse(R) + }) + it('Should not guard for TAsyncIterator with invalid $id', () => { + //@ts-ignore + const T = Type.AsyncIterator(Type.Any(), { $id: 1 }) + const R = TypeGuard.TAsyncIterator(T) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/awaited.ts b/test/runtime/type/guard/awaited.ts new file mode 100644 index 000000000..ef535a9ef --- /dev/null +++ b/test/runtime/type/guard/awaited.ts @@ -0,0 +1,41 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/Awaited', () => { + it('Should guard for Awaited 1', () => { + const T = Type.Awaited(Type.String()) + const R = TypeGuard.TString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 2', () => { + const T = Type.Awaited(Type.Promise(Type.String())) + const R = TypeGuard.TString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 3', () => { + const T = Type.Awaited(Type.Awaited(Type.Promise(Type.String()))) + const R = TypeGuard.TString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 4', () => { + const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String()))])) + Assert.IsTrue(TypeGuard.TString(T)) + }) + it('Should guard for Awaited 5', () => { + const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()])) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.TString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(T.anyOf[1])) + }) + it('Should guard for Awaited 6', () => { + const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String()))])) + Assert.IsTrue(TypeGuard.TString(T)) + }) + it('Should guard for Awaited 7', () => { + const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()])) + Assert.IsTrue(TypeGuard.TIntersect(T)) + Assert.IsTrue(TypeGuard.TString(T.allOf[0])) + Assert.IsTrue(TypeGuard.TNumber(T.allOf[1])) + }) +}) diff --git a/test/runtime/type/guard/bigint.ts b/test/runtime/type/guard/bigint.ts index 200294d8b..bc16722a0 100644 --- a/test/runtime/type/guard/bigint.ts +++ b/test/runtime/type/guard/bigint.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TBigInt', () => { it('Should guard for TBigInt', () => { const R = TypeGuard.TBigInt(Type.BigInt()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TBigInt', () => { const R = TypeGuard.TBigInt(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for BigInt with invalid $id', () => { // @ts-ignore const R = TypeGuard.TBigInt(Type.BigInt({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/boolean.ts b/test/runtime/type/guard/boolean.ts index ec9d8b3cd..ecc50b90d 100644 --- a/test/runtime/type/guard/boolean.ts +++ b/test/runtime/type/guard/boolean.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TBoolean', () => { it('Should guard for TBoolean', () => { const R = TypeGuard.TBoolean(Type.Boolean()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TBoolean', () => { const R = TypeGuard.TBoolean(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TBoolean with invalid $id', () => { // @ts-ignore const R = TypeGuard.TBoolean(Type.Boolean({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/capitalize.ts b/test/runtime/type/guard/capitalize.ts new file mode 100644 index 000000000..9e60fa035 --- /dev/null +++ b/test/runtime/type/guard/capitalize.ts @@ -0,0 +1,9 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/Capitalize', () => { + it('Should guard for TCapitalize', () => { + const T = Type.Capitalize(Type.Literal('hello')) + Assert.IsEqual(T.const, 'Hello') + }) +}) diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index f473d31fd..7d4fc1f4b 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -5,20 +5,20 @@ import { Assert } from '../../assert/index' describe('type/guard/TComposite', () => { it('Should guard for distinct properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) }) it('Should guard for overlapping properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) - Assert.isEqual(TypeGuard.TIntersect(T.properties.x), true) + Assert.IsTrue(TypeGuard.TIntersect(T.properties.x)) // @ts-ignore - Assert.isEqual(TypeGuard.TNumber(T.properties.x.allOf[0]), true) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x.allOf[0])) // @ts-ignore - Assert.isEqual(TypeGuard.TNumber(T.properties.x.allOf[1]), true) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x.allOf[1])) }) it('Should not produce optional property if all properties are not optional', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) - Assert.isEqual(TypeGuard.TOptional(T.properties.x), false) + Assert.IsFalse(TypeGuard.TOptional(T.properties.x)) }) // Note for: https://github.com/sinclairzx81/typebox/issues/419 // Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access @@ -33,8 +33,8 @@ describe('type/guard/TComposite', () => { Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) }) ]) - Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) - Assert.isTrue(T.required === undefined) + Assert.IsTrue(TypeGuard.TOptional(T.properties.x)) + Assert.IsEqual(T.required, undefined) }) it('Should produce required property if some composited properties are not optional', () => { // prettier-ignore @@ -42,15 +42,15 @@ describe('type/guard/TComposite', () => { Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() }) ]) - Assert.isEqual(TypeGuard.TOptional(T.properties.x), false) - Assert.isTrue(T.required!.includes('x')) + Assert.IsFalse(TypeGuard.TOptional(T.properties.x)) + Assert.IsTrue(T.required!.includes('x')) }) it('Should preserve single optional property', () => { // prettier-ignore const T = Type.Composite([ Type.Object({ x: Type.Optional(Type.Number()) }), ]) - Assert.isEqual(TypeGuard.TOptional(T.properties.x), true) - Assert.isTrue(T.required === undefined) + Assert.IsTrue(TypeGuard.TOptional(T.properties.x)) + Assert.IsEqual(T.required, undefined) }) }) diff --git a/test/runtime/type/guard/constructor.ts b/test/runtime/type/guard/constructor.ts index b60155859..967d04c1b 100644 --- a/test/runtime/type/guard/constructor.ts +++ b/test/runtime/type/guard/constructor.ts @@ -5,31 +5,31 @@ import { Assert } from '../../assert/index' describe('type/guard/TConstructor', () => { it('Should guard for TConstructor', () => { const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TConstructor', () => { const R = TypeGuard.TConstructor(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid $id', () => { // @ts-ignore const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid Params', () => { const R = TypeGuard.TConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid Return', () => { const R = TypeGuard.TConstructor(Type.Constructor([], {} as any)) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should guard for TConstructor with empty Rest Tuple', () => { const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([])), Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TConstructor with Rest Tuple', () => { const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/date.ts b/test/runtime/type/guard/date.ts index 98fb8db2f..2571c324a 100644 --- a/test/runtime/type/guard/date.ts +++ b/test/runtime/type/guard/date.ts @@ -5,41 +5,35 @@ import { Assert } from '../../assert/index' describe('type/guard/TDate', () => { it('Should guard for TDate', () => { const R = TypeGuard.TDate(Type.Date()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TDate', () => { const R = TypeGuard.TDate(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid $id', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ minimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ maximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ exclusiveMinimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TDate(Type.Number({ exclusiveMaximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts index 4f6d3a29f..fe4f06ded 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/exclude.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExclude', () => { it('Should extract string from number', () => { const T = Type.Exclude(Type.String(), Type.Number()) - Assert.isEqual(TypeGuard.TString(T), true) + Assert.IsTrue(TypeGuard.TString(T)) }) it('Should extract string from string', () => { const T = Type.Exclude(Type.String(), Type.String()) - Assert.isEqual(TypeGuard.TNever(T), true) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should extract string | number | boolean from string', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.isEqual(TypeGuard.TUnion(T), true) - Assert.isEqual(TypeGuard.TNumber(T.anyOf[0]), true) - Assert.isEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.TNumber(T.anyOf[0])) + Assert.IsTrue(TypeGuard.TBoolean(T.anyOf[1])) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.isEqual(TypeGuard.TNumber(T), true) + Assert.IsTrue(TypeGuard.TNumber(T)) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -28,20 +28,20 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.isEqual(TypeGuard.TNever(T), true) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Assert.isEqual(['C'].includes(T.const), true) + Assert.IsTrue(['C'].includes(T.const)) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) }) // ------------------------------------------------------------------------ // TemplateLiteral | Union 1 @@ -50,20 +50,20 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) - Assert.isEqual(TypeGuard.TNever(T), true) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Exclude(A, B) - Assert.isEqual(['C'].includes(T.const), true) + Assert.IsTrue(['C'].includes(T.const)) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Exclude(A, B) - Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) }) // ------------------------------------------------------------------------ // Union | TemplateLiteral 1 @@ -72,19 +72,19 @@ describe('type/guard/TExclude', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.isEqual(TypeGuard.TNever(T), true) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Assert.isEqual(['C'].includes(T.const), true) + Assert.IsTrue(['C'].includes(T.const)) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Assert.isEqual(['C', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['C', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) }) }) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts index 13d59bd0c..9fe5f35ea 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/extract.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExtract', () => { it('Should extract string from number', () => { const T = Type.Extract(Type.String(), Type.Number()) - Assert.isEqual(TypeGuard.TNever(T), true) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should extract string from string', () => { const T = Type.Extract(Type.String(), Type.String()) - Assert.isEqual(TypeGuard.TString(T), true) + Assert.IsTrue(TypeGuard.TString(T)) }) it('Should extract string | number | boolean from string', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.isEqual(TypeGuard.TString(T), true) + Assert.IsTrue(TypeGuard.TString(T)) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.isEqual(TypeGuard.TUnion(T), true) - Assert.isEqual(TypeGuard.TString(T.anyOf[0]), true) - Assert.isEqual(TypeGuard.TBoolean(T.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.TString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.TBoolean(T.anyOf[1])) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -28,22 +28,22 @@ describe('type/guard/TExtract', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A'].includes(T.const), true) + Assert.IsTrue(['A'].includes(T.const)) }) // ------------------------------------------------------------------------ // TemplateLiteral | Union 1 @@ -52,22 +52,22 @@ describe('type/guard/TExtract', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Extract(A, B) - Assert.isEqual(['A'].includes(T.const), true) + Assert.IsTrue(['A'].includes(T.const)) }) // ------------------------------------------------------------------------ // Union | TemplateLiteral 1 @@ -76,21 +76,21 @@ describe('type/guard/TExtract', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[1].const), true) - Assert.isEqual(['A', 'B', 'C'].includes(T.anyOf[2].const), true) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A', 'B'].includes(T.anyOf[0].const), true) - Assert.isEqual(['A', 'B'].includes(T.anyOf[1].const), true) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Assert.isEqual(['A'].includes(T.const), true) + Assert.IsTrue(['A'].includes(T.const)) }) }) diff --git a/test/runtime/type/guard/function.ts b/test/runtime/type/guard/function.ts index 2608266fd..79e66ba6f 100644 --- a/test/runtime/type/guard/function.ts +++ b/test/runtime/type/guard/function.ts @@ -5,31 +5,31 @@ import { Assert } from '../../assert/index' describe('type/guard/TFunction', () => { it('Should guard for TFunction', () => { const R = TypeGuard.TFunction(Type.Function([], Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TFunction', () => { const R = TypeGuard.TFunction(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid $id', () => { // @ts-ignore const R = TypeGuard.TFunction(Type.Function([], Type.Number(), { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid Params', () => { const R = TypeGuard.TFunction(Type.Function([{} as any, {} as any], Type.Number())) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid Return', () => { const R = TypeGuard.TFunction(Type.Function([], {} as any)) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should guard for TFunction with empty Rest Tuple', () => { const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([])), Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TFunction with Rest Tuple', () => { const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 969112f91..33ee2fb61 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -1,7 +1,10 @@ import './any' import './array' +import './async-iterator' +import './awaited' import './bigint' import './boolean' +import './capitalize' import './composite' import './constructor' import './date' @@ -10,9 +13,11 @@ import './extract' import './function' import './indexed' import './integer' -import './literal' import './intersect' +import './iterator' import './keyof' +import './literal' +import './lowercase' import './not' import './null' import './number' @@ -23,6 +28,7 @@ import './pick' import './promise' import './record' import './ref' +import './required' import './rest' import './string' import './symbol' @@ -30,8 +36,10 @@ import './template-literal' import './this' import './tuple' import './uint8array' +import './uncapitalize' import './undefined' import './union' import './unknown' import './unsafe' +import './uppercase' import './void' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 4003880c3..2a680f843 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -9,7 +9,7 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 2', () => { const T = Type.Object({ @@ -17,9 +17,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x', 'y']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 3', () => { const T = Type.Object({ @@ -27,9 +27,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, Type.KeyOf(T)) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 4', () => { const T = Type.Object({ @@ -37,108 +37,108 @@ describe('type/guard/TIndex', () => { ac: Type.String(), }) const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 5', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 6', () => { const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Should Index 7', () => { const T = Type.Array(Type.Null()) const I = Type.Index(T, Type.Number()) - Assert.isTrue(TypeGuard.TNull(I)) + Assert.IsTrue(TypeGuard.TNull(I)) }) it('Should Index 6', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0]) - Assert.isTrue(TypeGuard.TLiteralString(I)) - Assert.isEqual(I.const, 'hello') + Assert.IsTrue(TypeGuard.TLiteralString(I)) + Assert.IsEqual(I.const, 'hello') }) it('Should Index 8', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1]) - Assert.isTrue(TypeGuard.TLiteralString(I)) - Assert.isEqual(I.const, 'world') + Assert.IsTrue(TypeGuard.TLiteralString(I)) + Assert.IsEqual(I.const, 'world') }) it('Should Index 9', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 1]) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isEqual(I.anyOf[0].const, 'hello') - Assert.isEqual(I.anyOf[1].const, 'world') + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'hello') + Assert.IsEqual(I.anyOf[1].const, 'world') }) it('Should Index 10', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1, 0]) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isEqual(I.anyOf[0].const, 'world') - Assert.isEqual(I.anyOf[1].const, 'hello') + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'world') + Assert.IsEqual(I.anyOf[1].const, 'hello') }) it('Should Index 11', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 0, 0, 1]) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isEqual(I.anyOf[0].const, 'hello') - Assert.isEqual(I.anyOf[1].const, 'hello') - Assert.isEqual(I.anyOf[2].const, 'hello') - Assert.isEqual(I.anyOf[3].const, 'world') + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'hello') + Assert.IsEqual(I.anyOf[1].const, 'hello') + Assert.IsEqual(I.anyOf[2].const, 'hello') + Assert.IsEqual(I.anyOf[3].const, 'world') }) it('Should Index 12', () => { const T = Type.Tuple([Type.String(), Type.Boolean()]) const I = Type.Index(T, Type.Number()) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TBoolean(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TBoolean(I.anyOf[1])) }) it('Should Index 13', () => { const T = Type.Tuple([Type.String()]) const I = Type.Index(T, Type.Number()) - Assert.isTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.TString(I)) }) it('Should Index 14', () => { const T = Type.Tuple([]) const I = Type.Index(T, Type.Number()) - Assert.isTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.TNever(I)) }) it('Should Index 15', () => { const T = Type.Object({ 0: Type.Number(), }) const I = Type.Index(T, Type.Literal(0)) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 16', () => { const T = Type.Object({ 0: Type.Number(), }) const I = Type.Index(T, Type.Literal('0')) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 17', () => { const T = Type.Object({ '0': Type.Number(), }) const I = Type.Index(T, Type.Literal(0)) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 18', () => { const T = Type.Object({ '0': Type.Number(), }) const I = Type.Index(T, Type.Literal('0')) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 19', () => { const T = Type.Object({ @@ -147,9 +147,9 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)])) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.isTrue(TypeGuard.TBoolean(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TBoolean(I.anyOf[1])) }) it('Should Index 20', () => { const T = Type.Object({ @@ -158,7 +158,7 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.BigInt()) - Assert.isTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.TNever(I)) }) it('Should Index 21', () => { const T = Type.Object({ @@ -167,7 +167,7 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.Object({})) - Assert.isTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.TNever(I)) }) it('Should Index 22', () => { const A = Type.Object({ x: Type.Literal('A') }) @@ -176,11 +176,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TIntersect(I)) - Assert.isTrue(TypeGuard.TLiteral(I.allOf[0])) - Assert.isTrue(TypeGuard.TLiteral(I.allOf[1])) - Assert.isTrue(TypeGuard.TLiteral(I.allOf[2])) - Assert.isTrue(TypeGuard.TLiteral(I.allOf[3])) + Assert.IsTrue(TypeGuard.TIntersect(I)) + Assert.IsTrue(TypeGuard.TLiteral(I.allOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(I.allOf[1])) + Assert.IsTrue(TypeGuard.TLiteral(I.allOf[2])) + Assert.IsTrue(TypeGuard.TLiteral(I.allOf[3])) }) it('Should Index 23', () => { const A = Type.Object({ x: Type.Literal('A') }) @@ -189,11 +189,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Union([A, B, C, D]) const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TLiteral(I.anyOf[0])) - Assert.isTrue(TypeGuard.TLiteral(I.anyOf[1])) - Assert.isTrue(TypeGuard.TLiteral(I.anyOf[2])) - Assert.isTrue(TypeGuard.TLiteral(I.anyOf[3])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[2])) + Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[3])) }) it('Should Index 24', () => { const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) @@ -202,9 +202,9 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x', 'y']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TIntersect(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Should Index 25', () => { const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) @@ -213,11 +213,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x', 'y']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TIntersect(I.anyOf[0])) - Assert.isTrue(TypeGuard.TIntersect(I.anyOf[1])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1].allOf[0])) - Assert.isTrue(TypeGuard.TString(I.anyOf[1].allOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1].allOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1].allOf[1])) }) it('Should Index 26', () => { const T = Type.Recursive((This) => @@ -228,10 +228,10 @@ describe('type/guard/TIndex', () => { }), ) const I = Type.Index(T, ['x', 'y', 'z']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) - Assert.isTrue(TypeGuard.TThis(I.anyOf[2])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TThis(I.anyOf[2])) }) it('Should Index 27', () => { const T = Type.Object({ @@ -239,9 +239,9 @@ describe('type/guard/TIndex', () => { 1: Type.Number(), }) const I = Type.Index(T, [0, 1]) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Should Index 28', () => { const T = Type.Object({ @@ -249,9 +249,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, [0, '1']) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Should Index 29', () => { const T = Type.Object({ @@ -259,9 +259,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal('1')])) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Should Index 30', () => { const T = Type.Object({ @@ -269,9 +269,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) // Note: Expect TNever for anyOf[1] but permit for TNumber due to IndexedAccess // Resolve() which currently cannot differentiate between string and numeric keys // on the object. This may be resolvable in later revisions, but test for this @@ -286,8 +286,8 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TOptional(I)) - Assert.isTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TString(I)) }) it('Should Index 32', () => { const T = Type.Object({ @@ -295,8 +295,8 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['y']) - Assert.isFalse(TypeGuard.TOptional(I)) - Assert.isTrue(TypeGuard.TNumber(I)) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TNumber(I)) }) it('Should Index 33', () => { const T = Type.Object({ @@ -304,15 +304,15 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['x', 'y']) - Assert.isTrue(TypeGuard.TOptional(I)) - Assert.isTrue(TypeGuard.TUnion(I)) - Assert.isTrue(TypeGuard.TString(I.anyOf[0])) - Assert.isTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Should Index 34', () => { const T = Type.String() // @ts-ignore const I = Type.Index(T, ['x']) - Assert.isTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.TNever(I)) }) }) diff --git a/test/runtime/type/guard/integer.ts b/test/runtime/type/guard/integer.ts index 1938d4d23..849aabdca 100644 --- a/test/runtime/type/guard/integer.ts +++ b/test/runtime/type/guard/integer.ts @@ -5,40 +5,40 @@ import { Assert } from '../../assert/index' describe('type/guard/TInteger', () => { it('Should guard for TInteger', () => { const R = TypeGuard.TInteger(Type.Integer()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TInteger', () => { const R = TypeGuard.TInteger(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid $id', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ multipleOf: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ minimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ maximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMinimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TInteger(Type.Integer({ exclusiveMaximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/intersect.ts index 9ed33e6b2..5cf94db7a 100644 --- a/test/runtime/type/guard/intersect.ts +++ b/test/runtime/type/guard/intersect.ts @@ -14,7 +14,7 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TIntersect', () => { const R = TypeGuard.TIntersect( @@ -27,6 +27,6 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/iterator.ts b/test/runtime/type/guard/iterator.ts new file mode 100644 index 000000000..b0dd08c4a --- /dev/null +++ b/test/runtime/type/guard/iterator.ts @@ -0,0 +1,22 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TIterator', () => { + it('Should guard for TIterator', () => { + const T = Type.Iterator(Type.Any()) + const R = TypeGuard.TIterator(T) + Assert.IsTrue(R) + }) + it('Should not guard for TIterator', () => { + const T = null + const R = TypeGuard.TIterator(T) + Assert.IsFalse(R) + }) + it('Should not guard for TIterator with invalid $id', () => { + //@ts-ignore + const T = Type.Iterator(Type.Any(), { $id: 1 }) + const R = TypeGuard.TIterator(T) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/keyof.ts index cca0b0dae..d8b24ad74 100644 --- a/test/runtime/type/guard/keyof.ts +++ b/test/runtime/type/guard/keyof.ts @@ -9,9 +9,9 @@ describe('type/guard/TKeyOf', () => { y: Type.Number(), }) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TUnion(K), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(K)) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) }) it('Should KeyOf 2', () => { const T = Type.Recursive((Self) => @@ -21,9 +21,9 @@ describe('type/guard/TKeyOf', () => { }), ) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TUnion(K), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(K)) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) }) it('Should KeyOf 3', () => { const T = Type.Intersect([ @@ -35,9 +35,9 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TUnion(K), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[0]), true) - Assert.isEqual(TypeGuard.TLiteral(K.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(K)) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) }) it('Should KeyOf 4', () => { const T = Type.Union([ @@ -49,28 +49,28 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TNever(K), true) + Assert.IsTrue(TypeGuard.TNever(K)) }) it('Should KeyOf 5', () => { const T = Type.Null() const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TNever(K), true) + Assert.IsTrue(TypeGuard.TNever(K)) }) it('Should KeyOf 6', () => { const T = Type.Array(Type.Number()) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TNumber(K), true) + Assert.IsTrue(TypeGuard.TNumber(K)) }) it('Should KeyOf 7', () => { const T = Type.Tuple([]) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TNever(K), true) + Assert.IsTrue(TypeGuard.TNever(K)) }) it('Should KeyOf 8', () => { const T = Type.Tuple([Type.Number(), Type.Null()]) const K = Type.KeyOf(T) - Assert.isEqual(TypeGuard.TUnion(K), true) - Assert.isEqual(K.anyOf[0].const, '0') - Assert.isEqual(K.anyOf[1].const, '1') + Assert.IsTrue(TypeGuard.TUnion(K)) + Assert.IsEqual(K.anyOf[0].const, '0') + Assert.IsEqual(K.anyOf[1].const, '1') }) }) diff --git a/test/runtime/type/guard/literal.ts b/test/runtime/type/guard/literal.ts index 70d568f39..b982a5e7e 100644 --- a/test/runtime/type/guard/literal.ts +++ b/test/runtime/type/guard/literal.ts @@ -5,28 +5,28 @@ import { Assert } from '../../assert/index' describe('type/guard/TLiteral', () => { it('Should guard for TLiteral of String', () => { const R = TypeGuard.TLiteral(Type.Literal('hello')) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TLiteral of Number', () => { const R = TypeGuard.TLiteral(Type.Literal(42)) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TLiteral of Boolean', () => { const R = TypeGuard.TLiteral(Type.Literal(true)) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TLiteral of Null', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(null)) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TLiteral', () => { const R = TypeGuard.TLiteral(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TLiteral with invalid $id', () => { // @ts-ignore const R = TypeGuard.TLiteral(Type.Literal(42, { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/lowercase.ts b/test/runtime/type/guard/lowercase.ts new file mode 100644 index 000000000..3a269cd2e --- /dev/null +++ b/test/runtime/type/guard/lowercase.ts @@ -0,0 +1,9 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/Lowercase', () => { + it('Should guard for Lowercase', () => { + const T = Type.Lowercase(Type.Literal('HELLO')) + Assert.IsEqual(T.const, 'hello') + }) +}) diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/not.ts index 1bb7c2c87..71e5695e4 100644 --- a/test/runtime/type/guard/not.ts +++ b/test/runtime/type/guard/not.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TNot', () => { it('Should guard for TNot', () => { const R = TypeGuard.TNot(Type.Not(Type.String())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TNot 1', () => { const R = TypeGuard.TNot(Type.Not(null as any)) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNot with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNot(Type.Not(Type.String()), { $id: 1 }) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/null.ts b/test/runtime/type/guard/null.ts index 7fc7f1931..dbeca23ae 100644 --- a/test/runtime/type/guard/null.ts +++ b/test/runtime/type/guard/null.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TNull', () => { it('Should guard for TNull', () => { const R = TypeGuard.TNull(Type.Null()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TNull', () => { const R = TypeGuard.TNull(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNull with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNull(Type.Null({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/number.ts b/test/runtime/type/guard/number.ts index de8782045..02b911665 100644 --- a/test/runtime/type/guard/number.ts +++ b/test/runtime/type/guard/number.ts @@ -5,40 +5,40 @@ import { Assert } from '../../assert/index' describe('type/guard/TNumber', () => { it('Should guard for TNumber', () => { const R = TypeGuard.TNumber(Type.Number()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TNumber', () => { const R = TypeGuard.TNumber(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid $id', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid multipleOf', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ multipleOf: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid minimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ minimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid maximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ maximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid exclusiveMinimum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMinimum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid exclusiveMaximum', () => { // @ts-ignore const R = TypeGuard.TNumber(Type.Number({ exclusiveMaximum: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/object.ts b/test/runtime/type/guard/object.ts index 4811794bd..4f7cfcddf 100644 --- a/test/runtime/type/guard/object.ts +++ b/test/runtime/type/guard/object.ts @@ -10,23 +10,20 @@ describe('type/guard/TObject', () => { y: Type.Number(), }), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TObject', () => { const R = TypeGuard.TObject(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with escape characters in property key', () => { const R = TypeGuard.TObject( Type.Object({ 'hello\nworld': Type.Number(), }), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with invalid property values', () => { const R = TypeGuard.TObject( Type.Object({ @@ -34,9 +31,8 @@ describe('type/guard/TObject', () => { y: {} as any, }), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with invalid additionalProperties', () => { const R = TypeGuard.TObject( Type.Object( @@ -49,9 +45,8 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with invalid $id', () => { const R = TypeGuard.TObject( Type.Object( @@ -64,9 +59,8 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with invalid minProperties', () => { const R = TypeGuard.TObject( Type.Object( @@ -79,9 +73,8 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with invalid maxProperties', () => { const R = TypeGuard.TObject( Type.Object( @@ -94,9 +87,8 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should guard for TObject with invalid additional properties', () => { const R = TypeGuard.TObject( Type.Object( @@ -110,9 +102,8 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TObject with valid additional properties schema', () => { const R = TypeGuard.TObject( Type.Object( @@ -125,6 +116,6 @@ describe('type/guard/TObject', () => { }, ), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 0c2ccaf1a..b7d52a17f 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -8,11 +8,11 @@ describe('type/guard/TOmit', () => { // ------------------------------------------------------------------------- it('Should support TUnsafe omit properties with no Kind', () => { const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) - Assert.isEqual(T.required, ['y']) + Assert.IsEqual(T.required, ['y']) }) it('Should support TUnsafe omit properties with unregistered Kind', () => { const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownOmitType' }), y: Type.Number() }), ['x']) - Assert.isEqual(T.required, ['y']) + Assert.IsEqual(T.required, ['y']) }) // ------------------------------------------------------------------------- // Standard Tests @@ -25,8 +25,8 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.isEqual(T.required, ['y']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsEqual(T.required, ['y']) }) it('Should Omit 2', () => { const T = Type.Omit( @@ -36,8 +36,8 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.isEqual(T.required, undefined) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsEqual(T.required, undefined) }) it('Should Omit 3', () => { const L = Type.Literal('x') @@ -48,15 +48,15 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.isEqual(T.required, ['y']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsEqual(T.required, ['y']) }) it('Should Omit 4', () => { const L = Type.Literal('x') const T = Type.Omit(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.isEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) + Assert.IsEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) // @ts-ignore - Assert.isEqual(T.allOf[1].properties.x, undefined) + Assert.IsEqual(T.allOf[1].properties.x, undefined) }) it('Should Omit 5', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) @@ -68,11 +68,11 @@ describe('type/guard/TOmit', () => { L, ) // @ts-ignore - Assert.isEqual(T.properties.x, undefined) + Assert.IsEqual(T.properties.x, undefined) // @ts-ignore - Assert.isEqual(T.properties.y, undefined) + Assert.IsEqual(T.properties.y, undefined) // @ts-ignore - Assert.isEqual(T.required, undefined) + Assert.IsEqual(T.required, undefined) }) it('Should Omit 6', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) @@ -84,11 +84,11 @@ describe('type/guard/TOmit', () => { L, ) // @ts-ignore - Assert.isEqual(T.properties.x, undefined) + Assert.IsEqual(T.properties.x, undefined) // @ts-ignore - Assert.isEqual(T.properties.y, undefined) + Assert.IsEqual(T.properties.y, undefined) // @ts-ignore - Assert.isEqual(T.required, undefined) + Assert.IsEqual(T.required, undefined) }) it('Should Omit 7', () => { const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) @@ -100,7 +100,7 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.ad), true) - Assert.isEqual(T.required, ['ad']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.ad)) + Assert.IsEqual(T.required, ['ad']) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index 523f2acc3..f7f996d59 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -1,23 +1,43 @@ -import { TypeSystem } from '@sinclair/typebox/system' -import { TypeGuard } from '@sinclair/typebox' -import { Type, Kind } from '@sinclair/typebox' +import { TypeGuard, TypeRegistry, Type, Kind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TPartial', () => { + it('Should produce a valid TSchema', () => { + const T = Type.Partial(Type.Object({ x: Type.Number() })) + Assert.IsTrue(TypeGuard.TSchema(T)) + }) // ------------------------------------------------------------------------- // case: https://github.com/sinclairzx81/typebox/issues/364 // ------------------------------------------------------------------------- it('Should support TUnsafe partial properties with no Kind', () => { const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) - Assert.isEqual(T.required, undefined) + Assert.IsEqual(T.required, undefined) }) - it('Should support TUnsafe partial properties with unregistered Kind', () => { + it('Should support TUnsafe partial properties with unknown Kind', () => { const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) - Assert.isEqual(T.required, undefined) + Assert.IsEqual(T.required, undefined) + }) + it('Should support TUnsafe partial properties with known Kind', () => { + TypeRegistry.Set('KnownPartialType', () => true) + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'KnownPartialType', x: 1 }) })) + Assert.IsEqual(T.required, undefined) + }) + it('Should support applying partial to intersect', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const I = Type.Intersect([A, B]) + const T = Type.Partial(I) + Assert.IsEqual(T.allOf.length, 2) + Assert.IsEqual(T.allOf[0].required, undefined) + Assert.IsEqual(T.allOf[1].required, undefined) }) - it('Should support TUnsafe partial properties with registered Kind', () => { - const U = TypeSystem.Type('CustomPartialType', () => true) - const T = Type.Partial(Type.Object({ x: U() })) - Assert.isEqual(T.required, undefined) + it('Should support applying partial to union', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const I = Type.Union([A, B]) + const T = Type.Partial(I) + Assert.IsEqual(T.anyOf.length, 2) + Assert.IsEqual(T.anyOf[0].required, undefined) + Assert.IsEqual(T.anyOf[1].required, undefined) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 43a22ce80..029160552 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -14,11 +14,11 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.isEqual(T.required, ['x']) + Assert.IsEqual(T.required, ['x']) }) it('Should support TUnsafe omit properties with unregistered Kind', () => { const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownPickType' }), y: Type.Number() }), ['x']) - Assert.isEqual(T.required, ['x']) + Assert.IsEqual(T.required, ['x']) }) // ------------------------------------------------------------------------- // Standard Tests @@ -31,8 +31,8 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(T.required, ['x']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsEqual(T.required, ['x']) }) it('Should Pick 2', () => { const T = Type.Pick( @@ -42,8 +42,8 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(T.required, undefined) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsEqual(T.required, undefined) }) it('Should Pick 3', () => { const L = Type.Literal('x') @@ -54,16 +54,16 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(T.required, ['x']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsEqual(T.required, ['x']) }) it('Should Pick 4', () => { const L = Type.Literal('x') const T = Type.Pick(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.isEqual(TypeGuard.TNumber(T.allOf[0].properties.x), true) + Assert.IsTrue(TypeGuard.TNumber(T.allOf[0].properties.x)) // @ts-ignore - Assert.isEqual(T.allOf[1].properties.y, undefined) + Assert.IsEqual(T.allOf[1].properties.y, undefined) }) it('Should Pick 5', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) @@ -74,9 +74,9 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.isEqual(T.required, ['x', 'y']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsEqual(T.required, ['x', 'y']) }) it('Should Pick 6', () => { const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) @@ -87,9 +87,9 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.x), true) - Assert.isEqual(TypeGuard.TNumber(T.properties.y), true) - Assert.isEqual(T.required, ['x', 'y']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsEqual(T.required, ['x', 'y']) }) it('Should Pick 7', () => { const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) @@ -101,8 +101,8 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.isEqual(TypeGuard.TNumber(T.properties.ab), true) - Assert.isEqual(TypeGuard.TNumber(T.properties.ac), true) - Assert.isEqual(T.required, ['ab', 'ac']) + Assert.IsTrue(TypeGuard.TNumber(T.properties.ab)) + Assert.IsTrue(TypeGuard.TNumber(T.properties.ac)) + Assert.IsEqual(T.required, ['ab', 'ac']) }) }) diff --git a/test/runtime/type/guard/promise.ts b/test/runtime/type/guard/promise.ts index 96e49b0bd..f00ecc78a 100644 --- a/test/runtime/type/guard/promise.ts +++ b/test/runtime/type/guard/promise.ts @@ -5,20 +5,17 @@ import { Assert } from '../../assert/index' describe('type/guard/TPromise', () => { it('Should guard for TPromise', () => { const R = TypeGuard.TPromise(Type.Promise(Type.Number())) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TPromise', () => { const R = TypeGuard.TPromise(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TPromise with invalid $id', () => { // @ts-ignore const R = TypeGuard.TPromise(Type.Promise(Type.Number(), { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should guard for TPromise with nested TObject', () => { const R = TypeGuard.TPromise( Type.Promise( @@ -28,9 +25,8 @@ describe('type/guard/TPromise', () => { }), ), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TPromise with nested TObject', () => { const R = TypeGuard.TPromise( Type.Promise( @@ -40,6 +36,6 @@ describe('type/guard/TPromise', () => { }), ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 94bea05ec..2703dd85d 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -8,74 +8,74 @@ describe('type/guard/TRecord', () => { // ------------------------------------------------------------- it('Should guard overload 1', () => { const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TObject(T)) - Assert.isTrue(TypeGuard.TString(T.properties.A)) - Assert.isTrue(TypeGuard.TString(T.properties.B)) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsTrue(TypeGuard.TString(T.properties.B)) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 2', () => { const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal - Assert.isTrue(TypeGuard.TObject(T)) - Assert.isTrue(TypeGuard.TString(T.properties.A)) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 3', () => { // @ts-ignore - Assert.throws(() => Type.Record(Type.Union([]), Type.String(), { extra: 1 })) + Assert.Throws(() => Type.Record(Type.Union([]), Type.String(), { extra: 1 })) }) it('Should guard overload 4', () => { const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TObject(T)) - Assert.isTrue(TypeGuard.TString(T.properties.A)) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 5', () => { const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TObject(T)) - Assert.isTrue(TypeGuard.TString(T.properties.helloA)) - Assert.isTrue(TypeGuard.TString(T.properties.helloB)) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.TString(T.properties.helloA)) + Assert.IsTrue(TypeGuard.TString(T.properties.helloB)) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 6', () => { const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TRecord(T)) - Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TRecord(T)) + Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 7', () => { const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TRecord(T)) - Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TRecord(T)) + Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 8', () => { const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TRecord(T)) - Assert.isTrue(TypeGuard.TString(T.patternProperties[PatternStringExact])) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TRecord(T)) + Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternStringExact])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 9', () => { const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.isTrue(TypeGuard.TRecord(T)) - Assert.isTrue(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`])) - Assert.isEqual(T.extra, 1) + Assert.IsTrue(TypeGuard.TRecord(T)) + Assert.IsTrue(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 10', () => { const L = Type.Union([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) const T = Type.Record(L, Type.String()) - Assert.isTrue(TypeGuard.TObject(T)) - Assert.isTrue(TypeGuard.TString(T.properties.A)) - Assert.isTrue(TypeGuard.TString(T.properties.B)) - Assert.isTrue(TypeGuard.TString(T.properties.C)) + Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsTrue(TypeGuard.TString(T.properties.B)) + Assert.IsTrue(TypeGuard.TString(T.properties.C)) }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- it('Should guard for TRecord', () => { const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) - Assert.isTrue(R) + Assert.IsTrue(R) }) it('Should guard for TRecord with TObject value', () => { const R = TypeGuard.TRecord( @@ -87,16 +87,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.isTrue(R) + Assert.IsTrue(R) }) it('Should not guard for TRecord', () => { const R = TypeGuard.TRecord(null) - Assert.isFalse(R) + Assert.IsFalse(R) }) it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) - Assert.isFalse(R) + Assert.IsFalse(R) }) it('Should not guard for TRecord with TObject value with invalid Property', () => { const R = TypeGuard.TRecord( @@ -108,16 +108,16 @@ describe('type/guard/TRecord', () => { }), ), ) - Assert.isFalse(R) + Assert.IsFalse(R) }) it('Transform: Should should transform to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.isTrue(R) + Assert.IsTrue(R) }) it('Transform: Should should transform to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) - Assert.isTrue(R) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/ref.ts b/test/runtime/type/guard/ref.ts index 53b381954..83682d29d 100644 --- a/test/runtime/type/guard/ref.ts +++ b/test/runtime/type/guard/ref.ts @@ -6,20 +6,18 @@ describe('type/guard/TRef', () => { it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = TypeGuard.TRef(Type.Ref(T)) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TRef', () => { const R = TypeGuard.TRef(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TRef with invalid $ref', () => { const T = Type.Number({ $id: 'T' }) const S = Type.Ref(T) // @ts-ignore S.$ref = 1 const R = TypeGuard.TRef(S) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/required.ts new file mode 100644 index 000000000..4090a91fa --- /dev/null +++ b/test/runtime/type/guard/required.ts @@ -0,0 +1,40 @@ +import { TypeGuard, TypeRegistry, Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TRequired', () => { + it('Should produce a valid TSchema', () => { + const T = Type.Required(Type.Object({ x: Type.Number() })) + Assert.IsTrue(TypeGuard.TSchema(T)) + }) + it('Should support TUnsafe required properties with no Kind', () => { + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support TUnsafe required properties with unknown Kind', () => { + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ [Kind]: 'UnknownRequiredType', x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support TUnsafe required properties with known Kind', () => { + TypeRegistry.Set('KnownRequiredType', () => true) + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ [Kind]: 'KnownRequiredType', x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support applying required to intersect', () => { + const A = Type.Object({ x: Type.Optional(Type.Number()) }) + const B = Type.Object({ y: Type.Optional(Type.Number()) }) + const I = Type.Intersect([A, B]) + const T = Type.Required(I) + Assert.IsEqual(T.allOf.length, 2) + Assert.IsEqual(T.allOf[0].required, ['x']) + Assert.IsEqual(T.allOf[1].required, ['y']) + }) + it('Should support applying required to union', () => { + const A = Type.Object({ x: Type.Optional(Type.Number()) }) + const B = Type.Object({ y: Type.Optional(Type.Number()) }) + const I = Type.Union([A, B]) + const T = Type.Required(I) + Assert.IsEqual(T.anyOf.length, 2) + Assert.IsEqual(T.anyOf[0].required, ['x']) + Assert.IsEqual(T.anyOf[1].required, ['y']) + }) +}) diff --git a/test/runtime/type/guard/rest.ts b/test/runtime/type/guard/rest.ts index 1e9e831ee..c911da85c 100644 --- a/test/runtime/type/guard/rest.ts +++ b/test/runtime/type/guard/rest.ts @@ -6,17 +6,17 @@ describe('type/guard/TRest', () => { it('Should guard Rest 1', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const R = Type.Rest(T) - Assert.isTrue(TypeGuard.TString(R[0])) - Assert.isTrue(TypeGuard.TNumber(R[1])) + Assert.IsTrue(TypeGuard.TString(R[0])) + Assert.IsTrue(TypeGuard.TNumber(R[1])) }) it('Should guard Rest 2', () => { const T = Type.Tuple([]) const R = Type.Rest(T) - Assert.isEqual(R.length, 0) + Assert.IsEqual(R.length, 0) }) it('Should guard Rest 3', () => { const T = Type.String() const R = Type.Rest(T) - Assert.isTrue(TypeGuard.TString(R[0])) + Assert.IsTrue(TypeGuard.TString(R[0])) }) }) diff --git a/test/runtime/type/guard/string.ts b/test/runtime/type/guard/string.ts index 308da7e62..50205a1da 100644 --- a/test/runtime/type/guard/string.ts +++ b/test/runtime/type/guard/string.ts @@ -5,35 +5,30 @@ import { Assert } from '../../assert/index' describe('type/guard/TString', () => { it('Should guard for TString', () => { const R = TypeGuard.TString(Type.String()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TString', () => { const R = TypeGuard.TString(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TString with invalid $id', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TString with invalid minLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ minLength: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TString with invalid maxLength', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ maxLength: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TString with invalid pattern', () => { // @ts-ignore const R = TypeGuard.TString(Type.String({ pattern: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/symbol.ts b/test/runtime/type/guard/symbol.ts index a22a53fd7..28041bfe2 100644 --- a/test/runtime/type/guard/symbol.ts +++ b/test/runtime/type/guard/symbol.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TSymbol', () => { it('Should guard for TSymbol', () => { const R = TypeGuard.TSymbol(Type.Symbol()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TSymbol', () => { const R = TypeGuard.TSymbol(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TSymbol with invalid $id', () => { // @ts-ignore const R = TypeGuard.TSymbol(Type.Symbol({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/template-literal.ts b/test/runtime/type/guard/template-literal.ts index 79112c294..7637133c6 100644 --- a/test/runtime/type/guard/template-literal.ts +++ b/test/runtime/type/guard/template-literal.ts @@ -5,43 +5,43 @@ import { Assert } from '../../assert/index' describe('type/guard/TTemplateLiteral', () => { it('Should guard for empty TemplateLiteral', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TSchema', () => { const R = TypeGuard.TSchema(Type.TemplateLiteral([])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TTemplateLiteral)', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TLiteral)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TString)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.String()])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TNumber)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Number()])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TBoolean)', () => { const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for missing ^ expression prefix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(1) - Assert.isEqual(TypeGuard.TTemplateLiteral(T), false) + Assert.IsFalse(TypeGuard.TTemplateLiteral(T)) }) it('Should not guard for missing $ expression postfix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(0, T.pattern.length - 1) - Assert.isEqual(TypeGuard.TTemplateLiteral(T), false) + Assert.IsFalse(TypeGuard.TTemplateLiteral(T)) }) }) diff --git a/test/runtime/type/guard/this.ts b/test/runtime/type/guard/this.ts index 1668c93ad..93508d53c 100644 --- a/test/runtime/type/guard/this.ts +++ b/test/runtime/type/guard/this.ts @@ -6,7 +6,7 @@ describe('type/guard/TThis', () => { it('Should guard for TThis', () => { Type.Recursive((This) => { const R = TypeGuard.TThis(This) - Assert.isEqual(R, true) + Assert.IsTrue(R) return Type.Object({ nodes: Type.Array(This) }) }) }) @@ -15,7 +15,7 @@ describe('type/guard/TThis', () => { // @ts-ignore This.$ref = 1 const R = TypeGuard.TThis(This) - Assert.isEqual(R, false) + Assert.IsFalse(R) return Type.Object({ nodes: Type.Array(This) }) }) }) diff --git a/test/runtime/type/guard/tuple.ts b/test/runtime/type/guard/tuple.ts index d438753b6..9326c23d2 100644 --- a/test/runtime/type/guard/tuple.ts +++ b/test/runtime/type/guard/tuple.ts @@ -5,19 +5,19 @@ import { Assert } from '../../assert/index' describe('type/guard/TTuple', () => { it('Should guard for TTuple', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TTuple', () => { const R = TypeGuard.TTuple(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TTuple with invalid $id', () => { // @ts-ignore const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TTuple with invalid Items', () => { const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), {} as any])) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uint8array.ts b/test/runtime/type/guard/uint8array.ts index c2657f6af..cc058d8b3 100644 --- a/test/runtime/type/guard/uint8array.ts +++ b/test/runtime/type/guard/uint8array.ts @@ -5,29 +5,25 @@ import { Assert } from '../../assert/index' describe('type/guard/TUint8Array', () => { it('Should guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(Type.Uint8Array()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not guard for TUint8Array', () => { const R = TypeGuard.TUint8Array(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TUint8Array with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TUint8Array with invalid minByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ minByteLength: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not guard for TUint8Array with invalid maxByteLength', () => { // @ts-ignore const R = TypeGuard.TUint8Array(Type.Uint8Array({ maxByteLength: '1' })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uncapitalize.ts b/test/runtime/type/guard/uncapitalize.ts new file mode 100644 index 000000000..ece6195a3 --- /dev/null +++ b/test/runtime/type/guard/uncapitalize.ts @@ -0,0 +1,10 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/Uncapitalize', () => { + it('Should guard for Uncapitalize', () => { + const T = Type.Uncapitalize(Type.Literal('HELLO')) + Assert.IsEqual(T.const, 'hELLO') + }) +}) diff --git a/test/runtime/type/guard/undefined.ts b/test/runtime/type/guard/undefined.ts index 7f6d4a593..8393bff76 100644 --- a/test/runtime/type/guard/undefined.ts +++ b/test/runtime/type/guard/undefined.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TUndefined', () => { it('Should guard for TUndefined', () => { const R = TypeGuard.TUndefined(Type.Undefined()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TUndefined', () => { const R = TypeGuard.TUndefined(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TUndefined with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUndefined(Type.Undefined({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/union.ts b/test/runtime/type/guard/union.ts index b28b0052c..62b485e40 100644 --- a/test/runtime/type/guard/union.ts +++ b/test/runtime/type/guard/union.ts @@ -14,11 +14,11 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TUnion', () => { const R = TypeGuard.TUnion(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should guard for TUnion with invalid $id', () => { const R = TypeGuard.TUnion( @@ -38,7 +38,7 @@ describe('type/guard/TUnion', () => { }, ), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TUnion with invalid variant', () => { const R = TypeGuard.TUnion( @@ -49,7 +49,7 @@ describe('type/guard/TUnion', () => { {} as any, ]), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TUnion with invalid object variant', () => { const R = TypeGuard.TUnion( @@ -62,25 +62,25 @@ describe('type/guard/TUnion', () => { }), ]), ) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Transform: Should transform to never for zero length union', () => { const T = Type.Union([]) const R = TypeGuard.TNever(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Transform: Should unwrap union type for array of length === 1', () => { const T = Type.Union([Type.String()]) const R = TypeGuard.TString(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Transform: Should retain union if array length > 1', () => { const T = Type.Union([Type.String(), Type.Number()]) const R1 = TypeGuard.TUnion(T) const R2 = TypeGuard.TString(T.anyOf[0]) const R3 = TypeGuard.TNumber(T.anyOf[1]) - Assert.isEqual(R1, true) - Assert.isEqual(R2, true) - Assert.isEqual(R3, true) + Assert.IsTrue(R1) + Assert.IsTrue(R2) + Assert.IsTrue(R3) }) }) diff --git a/test/runtime/type/guard/unknown.ts b/test/runtime/type/guard/unknown.ts index 2c2dce22e..bd5cfcd03 100644 --- a/test/runtime/type/guard/unknown.ts +++ b/test/runtime/type/guard/unknown.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TUnknown', () => { it('Should guard for TUnknown', () => { const R = TypeGuard.TUnknown(Type.Unknown()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TUnknown', () => { const R = TypeGuard.TUnknown(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TUnknown with invalid $id', () => { // @ts-ignore const R = TypeGuard.TUnknown(Type.Unknown({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/unsafe.ts b/test/runtime/type/guard/unsafe.ts index b54e1f16b..f43a2d78d 100644 --- a/test/runtime/type/guard/unsafe.ts +++ b/test/runtime/type/guard/unsafe.ts @@ -6,27 +6,28 @@ describe('type/guard/TUnsafe', () => { it('Should guard raw TUnsafe', () => { const T = Type.Unsafe({ x: 1 }) const R = TypeGuard.TUnsafe(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard raw TUnsafe as TSchema', () => { const T = Type.Unsafe({ x: 1 }) const R = TypeGuard.TSchema(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should guard override TUnsafe as TSchema when registered', () => { - TypeRegistry.Set('type/guard/TUnsafe/Type1', () => true) - const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type1' }) + TypeRegistry.Set('UnsafeType', () => true) + const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) const R = TypeGuard.TSchema(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) + TypeRegistry.Delete('UnsafeType') }) it('Should not guard TUnsafe with unregistered kind', () => { - const T = Type.Unsafe({ [Kind]: 'type/guard/TUnsafe/Type2' }) + const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) const R = TypeGuard.TUnsafe(T) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TString', () => { const T = Type.String() const R = TypeGuard.TUnsafe(T) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uppercase.ts b/test/runtime/type/guard/uppercase.ts new file mode 100644 index 000000000..f85bdad43 --- /dev/null +++ b/test/runtime/type/guard/uppercase.ts @@ -0,0 +1,10 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/Uppercase', () => { + it('Should guard for Uppercase', () => { + const T = Type.Uppercase(Type.Literal('hello')) + Assert.IsEqual(T.const, 'HELLO') + }) +}) diff --git a/test/runtime/type/guard/void.ts b/test/runtime/type/guard/void.ts index 976639621..fb60e97a7 100644 --- a/test/runtime/type/guard/void.ts +++ b/test/runtime/type/guard/void.ts @@ -5,15 +5,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TVoid', () => { it('Should guard for TVoid', () => { const R = TypeGuard.TVoid(Type.Void()) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not guard for TVoid', () => { const R = TypeGuard.TVoid(null) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not guard for TVoid with invalid $id', () => { // @ts-ignore const R = TypeGuard.TVoid(Type.Void({ $id: 1 })) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index 0d61b85fd..dd13431a2 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -4,3 +4,4 @@ import './guard/index' import './normalize/index' import './registry/index' import './template/index' +import './value/index' diff --git a/test/runtime/type/normalize/exclude.ts b/test/runtime/type/normalize/exclude.ts index 16a91ede6..299309d93 100644 --- a/test/runtime/type/normalize/exclude.ts +++ b/test/runtime/type/normalize/exclude.ts @@ -6,16 +6,16 @@ describe('type/normal/Exclude', () => { it('Normalize 1', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) const R = TypeGuard.TUnion(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.String()) const R = TypeGuard.TNumber(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Exclude(Type.Union([Type.String()]), Type.String()) const R = TypeGuard.TNever(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/extract.ts b/test/runtime/type/normalize/extract.ts index f4b87a649..3e9c6f7e0 100644 --- a/test/runtime/type/normalize/extract.ts +++ b/test/runtime/type/normalize/extract.ts @@ -6,26 +6,26 @@ describe('type/normal/Extract', () => { it('Normalize 1', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Number()])) const R = TypeGuard.TUnion(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) const R = TypeGuard.TString(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.String()) const R = TypeGuard.TString(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 4', () => { const T = Type.Extract(Type.Union([Type.String()]), Type.String()) const R = TypeGuard.TString(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 5', () => { const T = Type.Extract(Type.Union([]), Type.String()) const R = TypeGuard.TNever(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/indexed.ts b/test/runtime/type/normalize/indexed.ts index eba47cb00..8e726db80 100644 --- a/test/runtime/type/normalize/indexed.ts +++ b/test/runtime/type/normalize/indexed.ts @@ -9,7 +9,7 @@ describe('type/normal/Index', () => { it('Normalize Array 1', () => { const T = Type.Array(Type.String()) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TString(I), true) + Assert.IsTrue(TypeGuard.TString(I)) }) // --------------------------------------------------------- // Tuple @@ -17,33 +17,33 @@ describe('type/normal/Index', () => { it('Normalize Tuple 1', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, Type.Number()) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Normalize Tuple 2', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, [0, 1]) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Normalize Tuple 3', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['0', '1']) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TNumber(I.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) }) it('Normalize Tuple 4', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['0']) - Assert.isEqual(TypeGuard.TString(I), true) + Assert.IsTrue(TypeGuard.TString(I)) }) it('Normalize Tuple 5', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['1']) - Assert.isEqual(TypeGuard.TNumber(I), true) + Assert.IsTrue(TypeGuard.TNumber(I)) }) // --------------------------------------------------------- // Intersect @@ -51,47 +51,47 @@ describe('type/normal/Index', () => { it('Normalize Intersect 1', () => { const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), true) - Assert.isEqual(TypeGuard.TIntersect(I), true) - Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + Assert.IsTrue(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TIntersect(I)) + Assert.IsTrue(TypeGuard.TString(I.allOf[0])) + Assert.IsTrue(TypeGuard.TString(I.allOf[1])) }) it('Normalize Intersect 2', () => { const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), false) - Assert.isEqual(TypeGuard.TIntersect(I), true) - Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TIntersect(I)) + Assert.IsTrue(TypeGuard.TString(I.allOf[0])) + Assert.IsTrue(TypeGuard.TString(I.allOf[1])) }) it('Normalize Intersect 3', () => { const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), false) - Assert.isEqual(TypeGuard.TIntersect(I), true) - Assert.isEqual(TypeGuard.TString(I.allOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.allOf[1]), true) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TIntersect(I)) + Assert.IsTrue(TypeGuard.TString(I.allOf[0])) + Assert.IsTrue(TypeGuard.TString(I.allOf[1])) }) it('Normalize Intersect 4', () => { const T = Type.Intersect([Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TString(I), true) + Assert.IsTrue(TypeGuard.TString(I)) }) it('Normalize Intersect 5', () => { const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TOptional(I), false) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Normalize Intersect 6', () => { const T = Type.Recursive(() => Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TOptional(I), false) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) // --------------------------------------------------------- // Union @@ -99,42 +99,42 @@ describe('type/normal/Index', () => { it('Normalize Union 1', () => { const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), true) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Normalize Union 2', () => { const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), true) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.IsTrue(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Normalize Union 3', () => { const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TOptional(I), false) - Assert.isEqual(TypeGuard.TUnion(I), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[0]), true) - Assert.isEqual(TypeGuard.TString(I.anyOf[1]), true) + Assert.IsFalse(TypeGuard.TOptional(I)) + Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) }) it('Normalize Union 4', () => { const T = Type.Union([Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.isEqual(TypeGuard.TString(I), true) + Assert.IsTrue(TypeGuard.TString(I)) }) it('Normalize Union 5', () => { const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) // @ts-ignore const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TNever(I), false) + Assert.IsFalse(TypeGuard.TNever(I)) }) it('Normalize Union 6', () => { const T = Type.Recursive(() => Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) // @ts-ignore const I = Type.Index(T, ['x', 'y']) - Assert.isEqual(TypeGuard.TNever(I), false) + Assert.IsFalse(TypeGuard.TNever(I)) }) }) diff --git a/test/runtime/type/normalize/intersect.ts b/test/runtime/type/normalize/intersect.ts index 3b712b5ea..3d449ad25 100644 --- a/test/runtime/type/normalize/intersect.ts +++ b/test/runtime/type/normalize/intersect.ts @@ -6,16 +6,16 @@ describe('type/normal/Intersect', () => { it('Normalize 1', () => { const T = Type.Intersect([Type.Number(), Type.String()]) const R = TypeGuard.TIntersect(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Intersect([Type.Number()]) const R = TypeGuard.TNumber(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Intersect([]) const R = TypeGuard.TNever(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/record.ts b/test/runtime/type/normalize/record.ts index 99b03383a..f7ad9334b 100644 --- a/test/runtime/type/normalize/record.ts +++ b/test/runtime/type/normalize/record.ts @@ -7,6 +7,6 @@ describe('type/normal/Record', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Record(K, Type.String()) const R = TypeGuard.TObject(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/union.ts b/test/runtime/type/normalize/union.ts index aff9483fc..12e056fad 100644 --- a/test/runtime/type/normalize/union.ts +++ b/test/runtime/type/normalize/union.ts @@ -6,16 +6,16 @@ describe('type/normal/Union', () => { it('Normalize 1', () => { const T = Type.Union([Type.Number(), Type.String()]) const R = TypeGuard.TUnion(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Union([Type.Number()]) const R = TypeGuard.TNumber(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Union([]) const R = TypeGuard.TNever(T) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/registry/format.ts b/test/runtime/type/registry/format.ts index df2568182..ffa3d4c78 100644 --- a/test/runtime/type/registry/format.ts +++ b/test/runtime/type/registry/format.ts @@ -8,15 +8,15 @@ describe('type/FormatRegistry', () => { it('Should get format', () => { FormatRegistry.Set('test#format2', () => true) const format = FormatRegistry.Get('test#format2') - Assert.isEqual(typeof format, 'function') + Assert.IsEqual(typeof format, 'function') }) it('Should return true if exists', () => { FormatRegistry.Set('test#format3', () => true) - Assert.isEqual(FormatRegistry.Has('test#format3'), true) + Assert.IsTrue(FormatRegistry.Has('test#format3')) }) it('Should clear formats', () => { FormatRegistry.Set('test#format4', () => true) FormatRegistry.Clear() - Assert.isEqual(FormatRegistry.Has('test#format4'), false) + Assert.IsFalse(FormatRegistry.Has('test#format4')) }) }) diff --git a/test/runtime/type/registry/type.ts b/test/runtime/type/registry/type.ts index 0ad9a6613..60151ec9a 100644 --- a/test/runtime/type/registry/type.ts +++ b/test/runtime/type/registry/type.ts @@ -8,15 +8,15 @@ describe('type/TypeRegistry', () => { it('Should get type', () => { TypeRegistry.Set('test#type2', () => true) const format = TypeRegistry.Get('test#type2') - Assert.isEqual(typeof format, 'function') + Assert.IsEqual(typeof format, 'function') }) it('Should return true if exists', () => { TypeRegistry.Set('test#type3', () => true) - Assert.isEqual(TypeRegistry.Has('test#type3'), true) + Assert.IsTrue(TypeRegistry.Has('test#type3')) }) it('Should clear types', () => { TypeRegistry.Set('test#type4', () => true) TypeRegistry.Clear() - Assert.isEqual(TypeRegistry.Has('test#type4'), false) + Assert.IsFalse(TypeRegistry.Has('test#type4')) }) }) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts index 0941a39c2..0df00de7d 100644 --- a/test/runtime/type/template/finite.ts +++ b/test/runtime/type/template/finite.ts @@ -8,27 +8,27 @@ describe('type/TemplateLiteralFinite', () => { it('Finite 1', () => { const E = TemplateLiteralParser.Parse(`A`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Finite 2', () => { const E = TemplateLiteralParser.Parse(`A|B`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Finite 3', () => { const E = TemplateLiteralParser.Parse(`A(B|C)`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Finite 4', () => { const E = TemplateLiteralParser.Parse(`${PatternBoolean}`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Finite 5', () => { const E = TemplateLiteralParser.Parse(`\\.\\*`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) // --------------------------------------------------------------- // Infinite @@ -36,36 +36,36 @@ describe('type/TemplateLiteralFinite', () => { it('Infinite 1', () => { const E = TemplateLiteralParser.Parse(`${PatternString}`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 2', () => { const E = TemplateLiteralParser.Parse(`${PatternNumber}`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 3', () => { const E = TemplateLiteralParser.Parse(`A|${PatternString}`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 4', () => { const E = TemplateLiteralParser.Parse(`A|${PatternNumber}`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 5', () => { const E = TemplateLiteralParser.Parse(`A(${PatternString})`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 6', () => { const E = TemplateLiteralParser.Parse(`A(${PatternNumber})`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Infinite 7', () => { const E = TemplateLiteralParser.Parse(`${PatternString}_foo`) const R = TemplateLiteralFinite.Check(E) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts index 24ee76f25..eef3b8101 100644 --- a/test/runtime/type/template/generate.ts +++ b/test/runtime/type/template/generate.ts @@ -8,12 +8,12 @@ describe('type/TemplateLiteralGenerator', () => { it('Exact 1', () => { const E = TemplateLiteralParser.Parse('^$') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['^$']) + Assert.IsEqual(R, ['^$']) }) it('Exact 2', () => { const E = TemplateLiteralParser.Parse('^A$') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['^A$']) + Assert.IsEqual(R, ['^A$']) }) // --------------------------------------------------------------- // Patterns @@ -21,17 +21,17 @@ describe('type/TemplateLiteralGenerator', () => { it('Pattern 1', () => { const E = TemplateLiteralParser.Parse('(true|false)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['true', 'false']) + Assert.IsEqual(R, ['true', 'false']) }) it('Pattern 2', () => { const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['0', '[1-9][0-9]*']) + Assert.IsEqual(R, ['0', '[1-9][0-9]*']) }) it('Pattern 3', () => { const E = TemplateLiteralParser.Parse('.*') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['.*']) + Assert.IsEqual(R, ['.*']) }) // --------------------------------------------------------------- // Expression @@ -39,161 +39,161 @@ describe('type/TemplateLiteralGenerator', () => { it('Expression 1', () => { const E = TemplateLiteralParser.Parse(')') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, [')']) + Assert.IsEqual(R, [')']) }) it('Expression 2', () => { const E = TemplateLiteralParser.Parse('\\)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['\\)']) + Assert.IsEqual(R, ['\\)']) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['\\(']) + Assert.IsEqual(R, ['\\(']) }) it('Expression 4', () => { const E = TemplateLiteralParser.Parse('') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['']) + Assert.IsEqual(R, ['']) }) it('Expression 5', () => { const E = TemplateLiteralParser.Parse('\\') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['\\']) + Assert.IsEqual(R, ['\\']) }) it('Expression 6', () => { const E = TemplateLiteralParser.Parse('()') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['']) + Assert.IsEqual(R, ['']) }) it('Expression 7', () => { const E = TemplateLiteralParser.Parse('(a)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['a']) + Assert.IsEqual(R, ['a']) }) it('Expression 8', () => { const E = TemplateLiteralParser.Parse('()))') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['))']) + Assert.IsEqual(R, ['))']) }) it('Expression 9', () => { const E = TemplateLiteralParser.Parse('())') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, [')']) + Assert.IsEqual(R, [')']) }) it('Expression 10', () => { const E = TemplateLiteralParser.Parse('A|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', 'B']) + Assert.IsEqual(R, ['A', 'B']) }) it('Expression 11', () => { const E = TemplateLiteralParser.Parse('A|(B)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', 'B']) + Assert.IsEqual(R, ['A', 'B']) }) it('Expression 12', () => { const E = TemplateLiteralParser.Parse('A(B)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['AB']) + Assert.IsEqual(R, ['AB']) }) it('Expression 13', () => { const E = TemplateLiteralParser.Parse('(A)B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['AB']) + Assert.IsEqual(R, ['AB']) }) it('Expression 14', () => { const E = TemplateLiteralParser.Parse('(A)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', 'B']) + Assert.IsEqual(R, ['A', 'B']) }) it('Expression 15', () => { const E = TemplateLiteralParser.Parse('|') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['']) + Assert.IsEqual(R, ['']) }) it('Expression 16', () => { const E = TemplateLiteralParser.Parse('||') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['']) + Assert.IsEqual(R, ['']) }) it('Expression 17', () => { const E = TemplateLiteralParser.Parse('||A') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A']) + Assert.IsEqual(R, ['A']) }) it('Expression 18', () => { const E = TemplateLiteralParser.Parse('A||') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A']) + Assert.IsEqual(R, ['A']) }) it('Expression 19', () => { const E = TemplateLiteralParser.Parse('A||B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', 'B']) + Assert.IsEqual(R, ['A', 'B']) }) it('Expression 20', () => { const E = TemplateLiteralParser.Parse('A|()|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', '', 'B']) + Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 21', () => { const E = TemplateLiteralParser.Parse('A|(|)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', '', 'B']) + Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 22', () => { const E = TemplateLiteralParser.Parse('A|(||)|B') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', '', 'B']) + Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 23', () => { const E = TemplateLiteralParser.Parse('|A(||)B|') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['AB']) + Assert.IsEqual(R, ['AB']) }) it('Expression 24', () => { const E = TemplateLiteralParser.Parse('A(B)(C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['ABC']) + Assert.IsEqual(R, ['ABC']) }) it('Expression 25', () => { const E = TemplateLiteralParser.Parse('A(B)|(C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['AB', 'C']) + Assert.IsEqual(R, ['AB', 'C']) }) it('Expression 26', () => { const E = TemplateLiteralParser.Parse('A(B|C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['AB', 'AC']) + Assert.IsEqual(R, ['AB', 'AC']) }) it('Expression 27', () => { const E = TemplateLiteralParser.Parse('A|(B|C)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['A', 'B', 'C']) + Assert.IsEqual(R, ['A', 'B', 'C']) }) it('Expression 28', () => { const E = TemplateLiteralParser.Parse('((A)B)C') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['ABC']) + Assert.IsEqual(R, ['ABC']) }) it('Expression 29', () => { const E = TemplateLiteralParser.Parse('(0|1)(0|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['00', '01', '10', '11']) + Assert.IsEqual(R, ['00', '01', '10', '11']) }) it('Expression 30', () => { const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['0', '1', '0', '1']) + Assert.IsEqual(R, ['0', '1', '0', '1']) }) it('Expression 31', () => { const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['0', '1', '0', '1']) + Assert.IsEqual(R, ['0', '1', '0', '1']) }) it('Expression 32', () => { const E = TemplateLiteralParser.Parse('(0(1|0)1)') const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.isEqual(R, ['011', '001']) + Assert.IsEqual(R, ['011', '001']) }) }) diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts index 5c9654b78..1ed72176e 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template/parser.ts @@ -6,24 +6,24 @@ describe('type/TemplateLiteralParser', () => { // Throws // --------------------------------------------------------------- it('Throw 1', () => { - Assert.throws(() => TemplateLiteralParser.Parse('(')) + Assert.Throws(() => TemplateLiteralParser.Parse('(')) }) it('Throw 2', () => { - Assert.throws(() => TemplateLiteralParser.Parse('(')) + Assert.Throws(() => TemplateLiteralParser.Parse('(')) }) // --------------------------------------------------------------- // Exact (No Default Unwrap) // --------------------------------------------------------------- it('Exact 1', () => { const E = TemplateLiteralParser.Parse('^$') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '^$', }) }) it('Exact 2', () => { const E = TemplateLiteralParser.Parse('^A$') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '^A$', }) @@ -33,7 +33,7 @@ describe('type/TemplateLiteralParser', () => { // --------------------------------------------------------------- it('Pattern 1', () => { const E = TemplateLiteralParser.Parse('(true|false)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -49,7 +49,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Pattern 2', () => { const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -65,7 +65,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Pattern 3', () => { const E = TemplateLiteralParser.Parse('.*') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '.*', }) @@ -75,56 +75,56 @@ describe('type/TemplateLiteralParser', () => { // --------------------------------------------------------------- it('Expression 1', () => { const E = TemplateLiteralParser.Parse(')') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: ')', }) }) it('Expression 2', () => { const E = TemplateLiteralParser.Parse('\\)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '\\)', }) }) it('Expression 3', () => { const E = TemplateLiteralParser.Parse('\\(') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '\\(', }) }) it('Expression 4', () => { const E = TemplateLiteralParser.Parse('') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 5', () => { const E = TemplateLiteralParser.Parse('\\') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '\\', }) }) it('Expression 6', () => { const E = TemplateLiteralParser.Parse('()') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 7', () => { const E = TemplateLiteralParser.Parse('(a)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: 'a', }) }) it('Expression 8', () => { const E = TemplateLiteralParser.Parse('()))') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -140,7 +140,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 9', () => { const E = TemplateLiteralParser.Parse('())') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -156,7 +156,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 10', () => { const E = TemplateLiteralParser.Parse('A|B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -172,7 +172,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 11', () => { const E = TemplateLiteralParser.Parse('A|(B)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -188,7 +188,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 12', () => { const E = TemplateLiteralParser.Parse('A(B)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -204,7 +204,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 13', () => { const E = TemplateLiteralParser.Parse('(A)B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -220,7 +220,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 14', () => { const E = TemplateLiteralParser.Parse('(A)|B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -236,35 +236,35 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 15', () => { const E = TemplateLiteralParser.Parse('|') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 16', () => { const E = TemplateLiteralParser.Parse('||') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 17', () => { const E = TemplateLiteralParser.Parse('||A') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: 'A', }) }) it('Expression 18', () => { const E = TemplateLiteralParser.Parse('A||') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'const', const: 'A', }) }) it('Expression 19', () => { const E = TemplateLiteralParser.Parse('A||B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -280,7 +280,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 20', () => { const E = TemplateLiteralParser.Parse('A|()|B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -300,7 +300,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 21', () => { const E = TemplateLiteralParser.Parse('A|(|)|B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -320,7 +320,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 22', () => { const E = TemplateLiteralParser.Parse('A|(||)|B') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -340,7 +340,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 23', () => { const E = TemplateLiteralParser.Parse('|A(||)B|') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -360,7 +360,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 24', () => { const E = TemplateLiteralParser.Parse('A(B)(C)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -380,7 +380,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 25', () => { const E = TemplateLiteralParser.Parse('A(B)|(C)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -405,7 +405,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 26', () => { const E = TemplateLiteralParser.Parse('A(B|C)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -430,7 +430,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 27', () => { const E = TemplateLiteralParser.Parse('A|(B|C)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -455,7 +455,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 28', () => { const E = TemplateLiteralParser.Parse('((A)B)C') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -480,7 +480,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 29', () => { const E = TemplateLiteralParser.Parse('(0|1)(0|1)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { @@ -514,7 +514,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 30', () => { const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -548,7 +548,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 31', () => { const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'or', expr: [ { @@ -577,7 +577,7 @@ describe('type/TemplateLiteralParser', () => { }) it('Expression 32', () => { const E = TemplateLiteralParser.Parse('(0(1|0)1)') - Assert.isEqual(E, { + Assert.IsEqual(E, { type: 'and', expr: [ { diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template/pattern.ts index b87143069..68d30330a 100644 --- a/test/runtime/type/template/pattern.ts +++ b/test/runtime/type/template/pattern.ts @@ -4,26 +4,26 @@ import { Assert } from '../../assert/index' describe('type/TemplateLiteralPattern', () => { const Equal = (template: TTemplateLiteral, expect: string) => { const pattern = template.pattern.slice(1, template.pattern.length - 1) - Assert.isEqual(pattern, expect) + Assert.IsEqual(pattern, expect) } // --------------------------------------------------------------- // Escape // --------------------------------------------------------------- it('Escape 1', () => { const T = Type.TemplateLiteral([Type.Literal('.*')]) - Assert.isEqual(T.pattern, '^\\.\\*$') + Assert.IsEqual(T.pattern, '^\\.\\*$') }) it('Escape 2', () => { const T = Type.TemplateLiteral([Type.Literal('(')]) - Assert.isEqual(T.pattern, '^\\($') + Assert.IsEqual(T.pattern, '^\\($') }) it('Escape 3', () => { const T = Type.TemplateLiteral([Type.Literal(')')]) - Assert.isEqual(T.pattern, '^\\)$') + Assert.IsEqual(T.pattern, '^\\)$') }) it('Escape 4', () => { const T = Type.TemplateLiteral([Type.Literal('|')]) - Assert.isEqual(T.pattern, '^\\|$') + Assert.IsEqual(T.pattern, '^\\|$') }) // --------------------------------------------------------------- // Pattern diff --git a/test/runtime/type/value/guard.ts b/test/runtime/type/value/guard.ts new file mode 100644 index 000000000..51652350b --- /dev/null +++ b/test/runtime/type/value/guard.ts @@ -0,0 +1,109 @@ +import { Assert } from '../../assert/index' +import { ValueGuard } from '@sinclair/typebox' + +describe('type/ValueGuard', () => { + // ----------------------------------------------------- + // IsNull + // ----------------------------------------------------- + it('Should guard null 1', () => { + const R = ValueGuard.IsNull(null) + Assert.IsTrue(R) + }) + it('Should guard null 2', () => { + const R = ValueGuard.IsNull({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsUndefined + // ----------------------------------------------------- + it('Should guard undefined 1', () => { + const R = ValueGuard.IsUndefined(undefined) + Assert.IsTrue(R) + }) + it('Should guard undefined 2', () => { + const R = ValueGuard.IsUndefined({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBigInt + // ----------------------------------------------------- + it('Should guard bigint 1', () => { + const R = ValueGuard.IsBigInt(1n) + Assert.IsTrue(R) + }) + it('Should guard bigint 2', () => { + const R = ValueGuard.IsBigInt(1) + Assert.IsFalse(R) + }) + it('Should guard bigint 3', () => { + const R = ValueGuard.IsBigInt('123') + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsNumber + // ----------------------------------------------------- + it('Should guard number 1', () => { + const R = ValueGuard.IsNumber(1) + Assert.IsTrue(R) + }) + it('Should guard number 2', () => { + const R = ValueGuard.IsNumber(3.14) + Assert.IsTrue(R) + }) + it('Should guard number 3', () => { + const R = ValueGuard.IsNumber('') + Assert.IsFalse(R) + }) + it('Should guard number 4', () => { + const R = ValueGuard.IsNumber(NaN) + Assert.IsTrue(R) + }) + // ----------------------------------------------------- + // IsString + // ----------------------------------------------------- + it('Should guard string 1', () => { + const R = ValueGuard.IsString('') + Assert.IsTrue(R) + }) + it('Should guard string 2', () => { + const R = ValueGuard.IsString(true) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBoolean + // ----------------------------------------------------- + it('Should guard boolean 1', () => { + const R = ValueGuard.IsBoolean(true) + Assert.IsTrue(R) + }) + it('Should guard boolean 2', () => { + const R = ValueGuard.IsBoolean(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsObject + // ----------------------------------------------------- + it('Should guard object 1', () => { + const R = ValueGuard.IsObject({}) + Assert.IsTrue(R) + }) + it('Should guard object 2', () => { + const R = ValueGuard.IsObject(1) + Assert.IsFalse(R) + }) + it('Should guard object 3', () => { + const R = ValueGuard.IsObject([]) + Assert.IsTrue(R) + }) + // ----------------------------------------------------- + // IsArray + // ----------------------------------------------------- + it('Should guard array 1', () => { + const R = ValueGuard.IsArray([]) + Assert.IsTrue(R) + }) + it('Should guard array 2', () => { + const R = ValueGuard.IsArray({}) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/value/index.ts b/test/runtime/type/value/index.ts new file mode 100644 index 000000000..4f2ca894f --- /dev/null +++ b/test/runtime/type/value/index.ts @@ -0,0 +1 @@ +import './guard' diff --git a/test/runtime/value/cast/any.ts b/test/runtime/value/cast/any.ts index b5beef61a..832caf5f6 100644 --- a/test/runtime/value/cast/any.ts +++ b/test/runtime/value/cast/any.ts @@ -7,46 +7,46 @@ describe('value/cast/Any', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result.getTime(100), 100) + Assert.IsEqual(result.getTime(100), 100) }) it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) - Assert.isEqual(result, { a: 1, b: 2 }) + Assert.IsEqual(result, { a: 1, b: 2 }) }) }) diff --git a/test/runtime/value/cast/array.ts b/test/runtime/value/cast/array.ts index 904c51811..83ad7f6c7 100644 --- a/test/runtime/value/cast/array.ts +++ b/test/runtime/value/cast/array.ts @@ -8,91 +8,91 @@ describe('value/cast/Array', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, [1]) + Assert.IsEqual(result, [1]) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = [6, 7, 8] const result = Value.Cast(T, value) - Assert.isEqual(result, [6, 7, 8]) + Assert.IsEqual(result, [6, 7, 8]) }) it('Should preserve with invalid element set to default', () => { const value = [6, 7, 8, 'hello', 9] const result = Value.Cast(T, value) - Assert.isEqual(result, [6, 7, 8, 0, 9]) + Assert.IsEqual(result, [6, 7, 8, 0, 9]) }) // ----------------------------------------------------------------- // Constraints: Ranges // ----------------------------------------------------------------- it('Should cast array and truncate to maxItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3 }), [0, 1, 2, 4, 5, 6]) - Assert.isEqual(result, [0, 1, 2]) + Assert.IsEqual(result, [0, 1, 2]) }) it('Should cast arrays and append array to minItems from value', () => { const result = Value.Cast(Type.Array(Type.Number(), { minItems: 6 }), [0, 1, 2]) - Assert.isEqual(result, [0, 1, 2, 0, 0, 0]) + Assert.IsEqual(result, [0, 1, 2, 0, 0, 0]) }) it('Should cast array and truncate to maxItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { maxItems: 3, default: [0, 1, 2, 4, 5, 6] }), null) - Assert.isEqual(result, [0, 1, 2]) + Assert.IsEqual(result, [0, 1, 2]) }) it('Should cast arrays and append array to minItems from default value', () => { const result = Value.Cast(Type.Array(Type.Number({ default: 1 }), { minItems: 6, default: [0, 1, 2] }), null) - Assert.isEqual(result, [0, 1, 2, 1, 1, 1]) + Assert.IsEqual(result, [0, 1, 2, 1, 1, 1]) }) // ----------------------------------------------------------------- // Constraints: Unique // ----------------------------------------------------------------- it('Should cast arrays with uniqueItems with unique default value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true, default: [0, 1, 2] }), null) - Assert.isEqual(result, [0, 1, 2]) + Assert.IsEqual(result, [0, 1, 2]) }) it('Should cast arrays with uniqueItems with unique value', () => { const result = Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), [0, 1, 2]) - Assert.isEqual(result, [0, 1, 2]) + Assert.IsEqual(result, [0, 1, 2]) }) it('Should throw when casting arrays with uniqueItems and no value or default value', () => { - Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), null)) + Assert.Throws(() => Value.Cast(Type.Array(Type.Number(), { uniqueItems: true }), null)) }) it('Should throw when casting arrays with uniqueItems and not enough values to populate set', () => { - Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true }), [0, 1])) + Assert.Throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true }), [0, 1])) }) it('Should throw when casting arrays with uniqueItems and not enough default values to populate set', () => { - Assert.throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true, default: [0, 1] }), null)) + Assert.Throws(() => Value.Cast(Type.Array(Type.Number(), { minItems: 3, uniqueItems: true, default: [0, 1] }), null)) }) // ----------------------------------------------------------------- // Suggestion: https://github.com/sinclairzx81/typebox/issues/239 @@ -101,18 +101,18 @@ describe('value/cast/Array', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const value = [1, 1, 2, 2] const result = Value.Cast(T, value) - Assert.isEqual(result, [1, 2]) + Assert.IsEqual(result, [1, 2]) }) it('Should should fill up with defaults to minItems', () => { const T = Type.Array(Type.Number(), { minItems: 3 }) const value = [1, 2] const result = Value.Cast(T, value) - Assert.isEqual(result, [1, 2, 0]) + Assert.IsEqual(result, [1, 2, 0]) }) it('Should should truncate to maxItems', () => { const T = Type.Array(Type.Number(), { maxItems: 3 }) const value = [1, 2, 3, 4] const result = Value.Cast(T, value) - Assert.isEqual(result, [1, 2, 3]) + Assert.IsEqual(result, [1, 2, 3]) }) }) diff --git a/test/runtime/value/cast/async-iterator.ts b/test/runtime/value/cast/async-iterator.ts new file mode 100644 index 000000000..ac27c0971 --- /dev/null +++ b/test/runtime/value/cast/async-iterator.ts @@ -0,0 +1,37 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/AsyncIterator', () => { + const T = Type.AsyncIterator(Type.Any()) + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.asyncIterator in result) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.asyncIterator in result) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.asyncIterator in result) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.asyncIterator in result) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.asyncIterator in result) + }) + it('Should preseve', () => { + const value = (async function* () {})() + const result = Value.Cast(T, value) + Assert.IsTrue(value === result) + }) +}) diff --git a/test/runtime/value/cast/bigint.ts b/test/runtime/value/cast/bigint.ts index 57589c93e..873fa73db 100644 --- a/test/runtime/value/cast/bigint.ts +++ b/test/runtime/value/cast/bigint.ts @@ -8,46 +8,46 @@ describe('value/cast/BigInt', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = BigInt(100) const result = Value.Cast(T, value) - Assert.isEqual(result, BigInt(100)) + Assert.IsEqual(result, BigInt(100)) }) }) diff --git a/test/runtime/value/cast/boolean.ts b/test/runtime/value/cast/boolean.ts index 6c14b5603..cbb976134 100644 --- a/test/runtime/value/cast/boolean.ts +++ b/test/runtime/value/cast/boolean.ts @@ -8,46 +8,46 @@ describe('value/cast/Boolean', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/cast/composite.ts b/test/runtime/value/cast/composite.ts index 8be304b8e..1db6d804e 100644 --- a/test/runtime/value/cast/composite.ts +++ b/test/runtime/value/cast/composite.ts @@ -25,47 +25,47 @@ describe('value/cast/Composite', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast and preserve object', () => { const value = { x: 7, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 7, y: 8, z: 9, @@ -77,7 +77,7 @@ describe('value/cast/Composite', () => { it('Should upcast and preserve from incorrect properties', () => { const value = { x: {}, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 0, y: 8, z: 9, diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/custom.ts index e29b21a7d..3bfb8959f 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/custom.ts @@ -14,37 +14,37 @@ describe('value/cast/Custom', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = { a: 'hello', b: 'world' } @@ -55,6 +55,6 @@ describe('value/cast/Custom', () => { }), value, ) - Assert.isEqual(result, { a: 'hello', b: 'world' }) + Assert.IsEqual(result, { a: 'hello', b: 'world' }) }) }) diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts index ec681d908..4e6fc6016 100644 --- a/test/runtime/value/cast/date.ts +++ b/test/runtime/value/cast/date.ts @@ -8,31 +8,31 @@ describe('value/cast/Date', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result.getTime(), 100) + Assert.IsEqual(result.getTime(), 100) }) }) diff --git a/test/runtime/value/cast/enum.ts b/test/runtime/value/cast/enum.ts index 1b0d1d12b..0013e8119 100644 --- a/test/runtime/value/cast/enum.ts +++ b/test/runtime/value/cast/enum.ts @@ -12,51 +12,51 @@ describe('value/cast/Boolean', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 123 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from enum A', () => { const value = Foo.A const result = Value.Cast(T, value) - Assert.isEqual(result, Foo.A) + Assert.IsEqual(result, Foo.A) }) it('Should upcast from enum B', () => { const value = Foo.B const result = Value.Cast(T, value) - Assert.isEqual(result, Foo.B) + Assert.IsEqual(result, Foo.B) }) }) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 8db2c8711..be2225807 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -1,5 +1,6 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './composite' @@ -8,6 +9,7 @@ import './date' import './enum' import './integer' import './intersect' +import './iterator' import './keyof' import './literal' import './never' @@ -17,7 +19,7 @@ import './number' import './object' import './recursive' import './record' -import './regex' +import './regexp' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/value/cast/integer.ts b/test/runtime/value/cast/integer.ts index 534913a83..3a5b375c1 100644 --- a/test/runtime/value/cast/integer.ts +++ b/test/runtime/value/cast/integer.ts @@ -8,36 +8,36 @@ describe('value/cast/Integer', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, 1) + Assert.IsEqual(result, 1) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) }) diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index 596ecb2da..82044975f 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -10,7 +10,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number() }) ]) const V = Value.Cast(T, 1) - Assert.isEqual(V, { x: 0, y: 0 }) + Assert.IsEqual(V, { x: 0, y: 0 }) }) it('Should cast from an partial object and preserve', () => { // prettier-ignore @@ -19,7 +19,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number() }) ]) const V = Value.Cast(T, { x: 1 }) - Assert.isEqual(V, { x: 1, y: 0 }) + Assert.IsEqual(V, { x: 1, y: 0 }) }) it('Should cast and use default values', () => { // prettier-ignore @@ -28,7 +28,7 @@ describe('value/cast/Intersect', () => { Type.Object({ y: Type.Number({ default: 42 }) }) ]) const V = Value.Cast(T, { x: 1 }) - Assert.isEqual(V, { x: 1, y: 42 }) + Assert.IsEqual(V, { x: 1, y: 42 }) }) it('Should throw with an illogical intersect', () => { // prettier-ignore @@ -36,7 +36,7 @@ describe('value/cast/Intersect', () => { Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() }) ]) - Assert.throws(() => Value.Cast(T, { x: 1 })) + Assert.Throws(() => Value.Cast(T, { x: 1 })) }) it('Should throw with an illogical intersect (primative)', () => { // prettier-ignore @@ -44,7 +44,7 @@ describe('value/cast/Intersect', () => { Type.Number(), Type.String() ]) - Assert.throws(() => Value.Cast(T, { x: 1 })) + Assert.Throws(() => Value.Cast(T, { x: 1 })) }) it('Should use last intersected default for equivalent sub schemas', () => { // prettier-ignore @@ -53,7 +53,7 @@ describe('value/cast/Intersect', () => { Type.Object({ x: Type.Number({ default: 1000 }) }) ]) const V = Value.Cast(T, null) - Assert.isEqual(V, { x: 1000 }) + Assert.IsEqual(V, { x: 1000 }) }) it('Should use last intersected default for equivalent sub schemas (primitives)', () => { // prettier-ignore @@ -62,7 +62,7 @@ describe('value/cast/Intersect', () => { Type.Number({ default: 1000 }) ]) const V = Value.Cast(T, null) - Assert.isEqual(V, 1000) + Assert.IsEqual(V, 1000) }) it('Should preserve if default is specified', () => { // prettier-ignore @@ -71,6 +71,6 @@ describe('value/cast/Intersect', () => { Type.Number({ default: 1000 }) ]) const V = Value.Cast(T, 2000) - Assert.isEqual(V, 2000) + Assert.IsEqual(V, 2000) }) }) diff --git a/test/runtime/value/cast/iterator.ts b/test/runtime/value/cast/iterator.ts new file mode 100644 index 000000000..14863180c --- /dev/null +++ b/test/runtime/value/cast/iterator.ts @@ -0,0 +1,37 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Iterator', () => { + const T = Type.Iterator(Type.Any()) + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.iterator in result) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.iterator in result) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.iterator in result) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.iterator in result) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.IsTrue(Symbol.iterator in result) + }) + it('Should preseve', () => { + const value = (function* () {})() + const result = Value.Cast(T, value) + Assert.IsTrue(value === result) + }) +}) diff --git a/test/runtime/value/cast/keyof.ts b/test/runtime/value/cast/keyof.ts index 81cd828d3..d0f41daf7 100644 --- a/test/runtime/value/cast/keyof.ts +++ b/test/runtime/value/cast/keyof.ts @@ -14,46 +14,46 @@ describe('value/cast/KeyOf', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = 'y' const result = Value.Cast(T, value) - Assert.isEqual(result, 'y') + Assert.IsEqual(result, 'y') }) }) diff --git a/test/runtime/value/cast/literal.ts b/test/runtime/value/cast/literal.ts index 71ca7dff8..59b356908 100644 --- a/test/runtime/value/cast/literal.ts +++ b/test/runtime/value/cast/literal.ts @@ -8,46 +8,46 @@ describe('value/cast/Literal', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) }) diff --git a/test/runtime/value/cast/never.ts b/test/runtime/value/cast/never.ts index c493f32f2..eeb782ed8 100644 --- a/test/runtime/value/cast/never.ts +++ b/test/runtime/value/cast/never.ts @@ -6,34 +6,34 @@ describe('value/cast/Never', () => { const T = Type.Never() it('Should throw from string', () => { const value = 'hello' - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from number', () => { const value = 1 - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from boolean', () => { const value = false - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from object', () => { const value = {} - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from array', () => { const value = [1] - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from undefined', () => { const value = undefined - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should throw from null', () => { const value = null - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) it('Should upcast from date', () => { const value = null - Assert.throws(() => Value.Cast(T, value)) + Assert.Throws(() => Value.Cast(T, value)) }) }) diff --git a/test/runtime/value/cast/not.ts b/test/runtime/value/cast/not.ts index e0713d473..d5802e646 100644 --- a/test/runtime/value/cast/not.ts +++ b/test/runtime/value/cast/not.ts @@ -7,46 +7,46 @@ describe('value/cast/Not', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, 0) // default + Assert.IsEqual(result, 0) // default }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should preserve', () => { const value = 100 const result = Value.Cast(T, value) - Assert.isEqual(result, 100) + Assert.IsEqual(result, 100) }) }) diff --git a/test/runtime/value/cast/null.ts b/test/runtime/value/cast/null.ts index b4d019bd4..de1d5a737 100644 --- a/test/runtime/value/cast/null.ts +++ b/test/runtime/value/cast/null.ts @@ -8,46 +8,46 @@ describe('value/cast/Null', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, null) + Assert.IsEqual(result, null) }) }) diff --git a/test/runtime/value/cast/number.ts b/test/runtime/value/cast/number.ts index 66d999c91..882ad6474 100644 --- a/test/runtime/value/cast/number.ts +++ b/test/runtime/value/cast/number.ts @@ -8,41 +8,41 @@ describe('value/cast/Number', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, 1) + Assert.IsEqual(result, 1) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = 123 const result = Value.Cast(T, value) - Assert.isEqual(result, 123) + Assert.IsEqual(result, 123) }) }) diff --git a/test/runtime/value/cast/object.ts b/test/runtime/value/cast/object.ts index 3fa460100..56a0668de 100644 --- a/test/runtime/value/cast/object.ts +++ b/test/runtime/value/cast/object.ts @@ -22,47 +22,47 @@ describe('value/cast/Object', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = { x: 7, y: 8, z: 9, a: 10, b: 11, c: 12 } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 7, y: 8, z: 9, @@ -74,7 +74,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object', () => { const value = { x: 7, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 7, y: 8, z: 9, @@ -86,7 +86,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object with incorrect properties', () => { const value = { x: {}, y: 8, z: 9 } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 0, y: 8, z: 9, @@ -98,7 +98,7 @@ describe('value/cast/Object', () => { it('Should upcast and preserve partial object and omit unknown properties', () => { const value = { x: 7, y: 8, z: 9, unknown: 'foo' } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 7, y: 8, z: 9, @@ -127,7 +127,7 @@ describe('value/cast/Object', () => { z: true, }, ) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 1, y: 2, z: { a: 0, b: 0 }, @@ -153,7 +153,7 @@ describe('value/cast/Object', () => { z: { b: 1 }, }, ) - Assert.isEqual(result, { + Assert.IsEqual(result, { x: 1, y: 2, z: { a: 0, b: 1 }, diff --git a/test/runtime/value/cast/record.ts b/test/runtime/value/cast/record.ts index 1ef6828af..fa6ea9d72 100644 --- a/test/runtime/value/cast/record.ts +++ b/test/runtime/value/cast/record.ts @@ -12,46 +12,45 @@ describe('value/cast/Record', () => { }), ) const E = {} - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = { @@ -59,7 +58,7 @@ describe('value/cast/Record', () => { b: { x: 4, y: 5, z: 6 }, } const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should preserve and patch invalid records', () => { const value = { @@ -70,7 +69,7 @@ describe('value/cast/Record', () => { e: { x: 1, y: 2, w: 9000 }, } const result = Value.Cast(T, value) - Assert.isEqual(result, { + Assert.IsEqual(result, { a: { x: 1, y: 2, z: 3 }, b: { x: 4, y: 5, z: 0 }, c: { x: 0, y: 0, z: 0 }, diff --git a/test/runtime/value/cast/recursive.ts b/test/runtime/value/cast/recursive.ts index cf4316ba4..cb2616043 100644 --- a/test/runtime/value/cast/recursive.ts +++ b/test/runtime/value/cast/recursive.ts @@ -13,42 +13,42 @@ describe('value/cast/Recursive', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = E const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = { @@ -60,7 +60,7 @@ describe('value/cast/Recursive', () => { ], } const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from varying types', () => { const TypeA = Type.Recursive((This) => diff --git a/test/runtime/value/cast/regex.ts b/test/runtime/value/cast/regexp.ts similarity index 78% rename from test/runtime/value/cast/regex.ts rename to test/runtime/value/cast/regexp.ts index d23bef49c..4bfc94c62 100644 --- a/test/runtime/value/cast/regex.ts +++ b/test/runtime/value/cast/regexp.ts @@ -3,51 +3,51 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/cast/RegEx', () => { - const T = Type.RegEx(/foo/, { default: 'foo' }) + const T = Type.RegExp(/foo/, { default: 'foo' }) const E = 'foo' it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, 'foo') + Assert.IsEqual(result, 'foo') }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) }) diff --git a/test/runtime/value/cast/string.ts b/test/runtime/value/cast/string.ts index ff8f5c11d..24d3fd6ab 100644 --- a/test/runtime/value/cast/string.ts +++ b/test/runtime/value/cast/string.ts @@ -8,36 +8,36 @@ describe('value/cast/String', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = 'foo' const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) }) diff --git a/test/runtime/value/cast/symbol.ts b/test/runtime/value/cast/symbol.ts index 05fd88049..18598e809 100644 --- a/test/runtime/value/cast/symbol.ts +++ b/test/runtime/value/cast/symbol.ts @@ -7,46 +7,46 @@ describe('value/cast/Symbol', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from number', () => { const value = 0 const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isTypeOf(result, 'symbol') + Assert.IsTypeOf(result, 'symbol') }) it('Should preserve', () => { const value = Symbol('hello') const result = Value.Cast(T, value) - Assert.isEqual(result.description, value.description) + Assert.IsEqual(result.description, value.description) }) }) diff --git a/test/runtime/value/cast/template-literal.ts b/test/runtime/value/cast/template-literal.ts index 322928ba5..dc199e17a 100644 --- a/test/runtime/value/cast/template-literal.ts +++ b/test/runtime/value/cast/template-literal.ts @@ -8,46 +8,46 @@ describe('value/cast/TemplateLiteral', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = 'helloworld' const result = Value.Cast(T, value) - Assert.isEqual(result, 'helloworld') + Assert.IsEqual(result, 'helloworld') }) }) diff --git a/test/runtime/value/cast/tuple.ts b/test/runtime/value/cast/tuple.ts index cb805cf6e..8d30bbed5 100644 --- a/test/runtime/value/cast/tuple.ts +++ b/test/runtime/value/cast/tuple.ts @@ -8,66 +8,66 @@ describe('value/cast/Tuple', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, [1, '']) + Assert.IsEqual(result, [1, '']) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = [42, 'world'] const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast with empty', () => { const value = [] as any[] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should append with less than tuple length', () => { const value = [42] const result = Value.Cast(T, value) - Assert.isEqual(result, [42, '']) + Assert.IsEqual(result, [42, '']) }) it('Should truncate with greater than tuple length', () => { const value = [42, '', true] const result = Value.Cast(T, value) - Assert.isEqual(result, [42, '']) + Assert.IsEqual(result, [42, '']) }) it('Should preserve and patch invalid element', () => { const value = [{}, 'hello'] const result = Value.Cast(T, value) - Assert.isEqual(result, [0, 'hello']) + Assert.IsEqual(result, [0, 'hello']) }) }) diff --git a/test/runtime/value/cast/uint8array.ts b/test/runtime/value/cast/uint8array.ts index 4adf0eb44..6678cc715 100644 --- a/test/runtime/value/cast/uint8array.ts +++ b/test/runtime/value/cast/uint8array.ts @@ -8,46 +8,46 @@ describe('value/cast/Uint8Array', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = new Uint8Array([6, 7, 8, 9, 10]) const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) }) diff --git a/test/runtime/value/cast/undefined.ts b/test/runtime/value/cast/undefined.ts index 9821ee947..8bb23a4fe 100644 --- a/test/runtime/value/cast/undefined.ts +++ b/test/runtime/value/cast/undefined.ts @@ -8,46 +8,46 @@ describe('value/cast/Undefined', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preseve', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, undefined) + Assert.IsEqual(result, undefined) }) }) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 4ca73b41e..33d77b41d 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -28,91 +28,90 @@ describe('value/cast/Union', () => { y: 0, z: 0, } - it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve A', () => { const value = { type: 'A', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should preserve B', () => { const value = { type: 'B', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should infer through heuristics #1', () => { const value = { type: 'A', a: 'a', b: 'b', c: 'c' } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + Assert.IsEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) it('Should infer through heuristics #2', () => { const value = { type: 'B', x: 1, y: 2, z: 3 } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.IsEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #3', () => { const value = { type: 'A', a: 'a', b: 'b', c: null } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) + Assert.IsEqual(result, { type: 'A', x: 0, y: 0, z: 0 }) }) it('Should infer through heuristics #4', () => { const value = { type: 'B', x: 1, y: 2, z: {} } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.IsEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #5', () => { const value = { type: 'B', x: 1, y: 2, z: null } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.IsEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should infer through heuristics #6', () => { const value = { x: 1 } const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) + Assert.IsEqual(result, { type: 'A', x: 1, y: 0, z: 0 }) }) it('Should infer through heuristics #7', () => { const value = { a: null } // property existing should contribute const result = Value.Cast(T, value) - Assert.isEqual(result, { type: 'B', a: '', b: '', c: '' }) + Assert.IsEqual(result, { type: 'B', a: '', b: '', c: '' }) }) it('Should cast with default value (create)', () => { const result = Value.Cast( @@ -125,7 +124,7 @@ describe('value/cast/Union', () => { value: 'D', }, ) - Assert.isEqual(result, { + Assert.IsEqual(result, { id: 42, value: 'C', }) @@ -141,7 +140,7 @@ describe('value/cast/Union', () => { value: 'B', }, ) - Assert.isEqual(result, { + Assert.IsEqual(result, { id: 42, value: 'B', }) diff --git a/test/runtime/value/cast/unknown.ts b/test/runtime/value/cast/unknown.ts index f6193fb7c..10058ef2e 100644 --- a/test/runtime/value/cast/unknown.ts +++ b/test/runtime/value/cast/unknown.ts @@ -7,46 +7,46 @@ describe('value/cast/Unknown', () => { it('Should upcast from string', () => { const value = 'hello' const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from boolean', () => { const value = false const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, value) + Assert.IsEqual(result, value) }) it('Should upcast from date', () => { const value = new Date(100) const result: any = Value.Cast(T, value) - Assert.isEqual(result.getTime(), 100) + Assert.IsEqual(result.getTime(), 100) }) it('Should preserve', () => { const value = { a: 1, b: 2 } const result = Value.Cast(T, value) - Assert.isEqual(result, { a: 1, b: 2 }) + Assert.IsEqual(result, { a: 1, b: 2 }) }) }) diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts index 6996f3bf0..41adb69e2 100644 --- a/test/runtime/value/cast/void.ts +++ b/test/runtime/value/cast/void.ts @@ -8,46 +8,46 @@ describe('value/cast/Void', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from number', () => { const value = 1 const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from boolean', () => { const value = true const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should upcast from date', () => { const value = new Date(100) const result = Value.Cast(T, value) - Assert.isEqual(result, E) + Assert.IsEqual(result, E) }) it('Should preserve', () => { const value = null const result = Value.Cast(T, value) - Assert.isEqual(result, null) + Assert.IsEqual(result, null) }) }) diff --git a/test/runtime/value/check/any.ts b/test/runtime/value/check/any.ts index 1f46bb09a..302412c36 100644 --- a/test/runtime/value/check/any.ts +++ b/test/runtime/value/check/any.ts @@ -7,41 +7,41 @@ describe('value/check/Any', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index c0ef023b8..421a3e19a 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -7,48 +7,41 @@ describe('value/check/Array', () => { const T = Type.Array(Type.Number()) const value = [1, 2, 3] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail number array', () => { const T = Type.Array(Type.Number()) const value = ['a', 'b', 'c'] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should pass object array', () => { const T = Type.Array(Type.Object({ x: Type.Number() })) const value = [{ x: 1 }, { x: 1 }, { x: 1 }] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail object array', () => { const T = Type.Array(Type.Object({ x: Type.Number() })) const value = [{ x: 1 }, { x: 1 }, 1] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail Date', () => { const value = new Date() const result = Value.Check(Type.Array(Type.Any()), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should validate array with unique primitive items', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const result = Value.Check(T, [0, 1, 2]) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should not validate array with non-unique primitive items', () => { const T = Type.Array(Type.Number(), { uniqueItems: true }) const result = Value.Check(T, [0, 0, 2]) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should validate array with unique object items', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) const result = Value.Check(T, [ @@ -56,9 +49,8 @@ describe('value/check/Array', () => { { x: 2, y: 2 }, { x: 3, y: 3 }, ]) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should not validate array with non-unique object items', () => { const T = Type.Array(Type.Object({ x: Type.Number(), y: Type.Number() }), { uniqueItems: true }) const result = Value.Check(T, [ @@ -66,6 +58,56 @@ describe('value/check/Array', () => { { x: 1, y: 1 }, { x: 3, y: 3 }, ]) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) + }) + // --------------------------------------------------------- + // Contains + // --------------------------------------------------------- + it('Should validate for contains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1) }) + Assert.IsTrue(Value.Check(T, [1])) + Assert.IsTrue(Value.Check(T, [1, 2])) + Assert.IsFalse(Value.Check(T, [])) + Assert.IsFalse(Value.Check(T, [2])) + }) + it('Should validate for minContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3 }) + Assert.IsTrue(Value.Check(T, [1, 1, 1, 2])) + Assert.IsTrue(Value.Check(T, [2, 1, 1, 1, 2])) + Assert.IsTrue(Value.Check(T, [1, 1, 1])) + Assert.IsFalse(Value.Check(T, [])) + Assert.IsFalse(Value.Check(T, [1, 1])) + Assert.IsFalse(Value.Check(T, [2])) + }) + it('Should validate for maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), maxContains: 3 }) + Assert.IsTrue(Value.Check(T, [1, 1, 1])) + Assert.IsTrue(Value.Check(T, [1, 1])) + Assert.IsTrue(Value.Check(T, [2, 2, 2, 2, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1])) + }) + it('Should validate for minContains and maxContains', () => { + const T = Type.Array(Type.Number(), { contains: Type.Literal(1), minContains: 3, maxContains: 5 }) + Assert.IsFalse(Value.Check(T, [1, 1])) + Assert.IsTrue(Value.Check(T, [1, 1, 1])) + Assert.IsTrue(Value.Check(T, [1, 1, 1, 1])) + Assert.IsTrue(Value.Check(T, [1, 1, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1, 1])) + }) + it('Should not validate minContains and maxContains when contains is unspecified', () => { + const T = Type.Array(Type.Number(), { minContains: 3, maxContains: 5 }) + Assert.IsFalse(Value.Check(T, [1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1, 1])) + }) + it('Should produce illogical schema when contains is not sub type of items', () => { + const T = Type.Array(Type.Number(), { contains: Type.String(), minContains: 3, maxContains: 5 }) + Assert.IsFalse(Value.Check(T, [1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1])) + Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1, 1])) }) }) diff --git a/test/runtime/value/check/async-iterator.ts b/test/runtime/value/check/async-iterator.ts new file mode 100644 index 000000000..881f68c1c --- /dev/null +++ b/test/runtime/value/check/async-iterator.ts @@ -0,0 +1,24 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/AsyncIterator', () => { + it('Should pass async iterator 1', () => { + async function* f() {} + const T = Type.AsyncIterator(Type.Any()) + const result = Value.Check(T, f()) + Assert.IsEqual(result, true) + }) + it('Should pass async iterator 2', () => { + const T = Type.AsyncIterator(Type.Any()) + const result = Value.Check(T, { + [Symbol.asyncIterator]: () => {}, + }) + Assert.IsEqual(result, true) + }) + it('Should pass async iterator', () => { + const T = Type.AsyncIterator(Type.Any()) + const result = Value.Check(T, {}) + Assert.IsEqual(result, false) + }) +}) diff --git a/test/runtime/value/check/bigint.ts b/test/runtime/value/check/bigint.ts index 2f663c384..b253926f5 100644 --- a/test/runtime/value/check/bigint.ts +++ b/test/runtime/value/check/bigint.ts @@ -7,38 +7,35 @@ describe('value/check/BigInt', () => { it('Should not validate NaN', () => { const T = Type.BigInt() const result = Value.Check(T, NaN) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.BigInt() const result = Value.Check(T, Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.BigInt() const result = Value.Check(T, -Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail integer', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail integer', () => { const value = 3.14 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should pass bigint', () => { const result = Value.Check(T, BigInt(0)) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/check/boolean.ts b/test/runtime/value/check/boolean.ts index d15d8b7d4..3eafcf878 100644 --- a/test/runtime/value/check/boolean.ts +++ b/test/runtime/value/check/boolean.ts @@ -7,41 +7,41 @@ describe('value/check/Boolean', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/composite.ts b/test/runtime/value/check/composite.ts index fe4afa39f..7c12e76c8 100644 --- a/test/runtime/value/check/composite.ts +++ b/test/runtime/value/check/composite.ts @@ -14,7 +14,6 @@ describe('value/check/Composite', () => { c: Type.String(), }) const T = Type.Composite([A, B]) - it('Should pass composite', () => { const value = { x: 1, @@ -25,9 +24,8 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail intersect with invalid property', () => { const value = { x: true, @@ -38,9 +36,8 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail intersect with missing property', () => { const value = { y: 1, @@ -50,15 +47,13 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail intersect with primitive value', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should pass intersect with optional properties', () => { const A = Type.Object({ x: Type.Optional(Type.Number()), @@ -77,6 +72,6 @@ describe('value/check/Composite', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts index 45e3dbba1..3072822f9 100644 --- a/test/runtime/value/check/custom.ts +++ b/test/runtime/value/check/custom.ts @@ -3,29 +3,27 @@ import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/check/Custom', () => { - TypeRegistry.Set('BigInt', (schema, value) => typeof value === 'bigint') - - it('Should validate bigint', () => { - const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Assert.isEqual(Value.Check(T, 1n), true) + const FooBar = Type.Unsafe({ [Kind]: 'FooBar' }) + before(() => { + TypeRegistry.Set('FooBar', (schema, value) => value === 'foobar') }) - - it('Should not validate bigint', () => { - const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Assert.isEqual(Value.Check(T, 1), false) + after(() => { + TypeRegistry.Delete('FooBar') }) - - it('Should validate bigint nested', () => { - const T = Type.Object({ - x: Type.Unsafe({ [Kind]: 'BigInt' }), - }) - Assert.isEqual(Value.Check(T, { x: 1n }), true) + it('Should validate foobar', () => { + Assert.IsEqual(Value.Check(FooBar, 'foobar'), true) }) - - it('Should not validate bigint nested', () => { - const T = Type.Object({ - x: Type.Unsafe({ [Kind]: 'BigInt' }), - }) - Assert.isEqual(Value.Check(T, { x: 1 }), false) + it('Should not validate foobar', () => { + Assert.IsEqual(Value.Check(FooBar, 1), false) + }) + it('Should validate foobar nested', () => { + // prettier-ignore + const T = Type.Object({ x: FooBar }) + Assert.IsEqual(Value.Check(T, { x: 'foobar' }), true) + }) + it('Should not validate foobar nested', () => { + // prettier-ignore + const T = Type.Object({ x: FooBar }) + Assert.IsEqual(Value.Check(T, { x: 1 }), false) }) }) diff --git a/test/runtime/value/check/date.ts b/test/runtime/value/check/date.ts index 2d4e74f8c..800c764b4 100644 --- a/test/runtime/value/check/date.ts +++ b/test/runtime/value/check/date.ts @@ -7,46 +7,46 @@ describe('value/check/Date', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should not validate Date if is invalid', () => { const value = new Date('not-a-valid-date') const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/enum.ts b/test/runtime/value/check/enum.ts index ec1395cdc..34d9612cf 100644 --- a/test/runtime/value/check/enum.ts +++ b/test/runtime/value/check/enum.ts @@ -8,27 +8,24 @@ describe('value/check/Enum', () => { B = 2, } const T = Type.Enum(Foo) - it('Should pass enum option A', () => { const value = Foo.A const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should pass enum option B', () => { const value = Foo.A const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail unknown value', () => { const value = 'unknown' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index f5cc52770..9dac82003 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -1,5 +1,6 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './composite' @@ -8,6 +9,7 @@ import './date' import './enum' import './integer' import './intersect' +import './iterator' import './keyof' import './literal' import './never' @@ -18,7 +20,7 @@ import './object' import './recursive' import './ref' import './record' -import './regex' +import './regexp' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/value/check/integer.ts b/test/runtime/value/check/integer.ts index 2db4fe7a7..9344a2937 100644 --- a/test/runtime/value/check/integer.ts +++ b/test/runtime/value/check/integer.ts @@ -4,38 +4,34 @@ import { Assert } from '../../assert/index' describe('value/check/Integer', () => { const T = Type.Integer() - it('Should not validate NaN', () => { const T = Type.Integer() const result = Value.Check(T, NaN) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.Integer() const result = Value.Check(T, Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.Integer() const result = Value.Check(T, -Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should pass integer', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail integer', () => { const value = 3.14 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/intersect.ts b/test/runtime/value/check/intersect.ts index e41a9d0ad..e727f2bdb 100644 --- a/test/runtime/value/check/intersect.ts +++ b/test/runtime/value/check/intersect.ts @@ -7,79 +7,79 @@ describe('value/check/Intersect', () => { const A = Type.Number() const B = Type.Number() const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, 1), true) + Assert.IsEqual(Value.Check(T, 1), true) }) it('Should not intersect string and number', () => { const A = Type.String() const B = Type.Number() const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, 1), false) - Assert.isEqual(Value.Check(T, '1'), false) + Assert.IsEqual(Value.Check(T, 1), false) + Assert.IsEqual(Value.Check(T, '1'), false) }) it('Should intersect two objects', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1 }), true) }) it('Should not intersect two objects with internal additionalProperties false', () => { const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1 }), false) }) it('Should intersect two objects and mandate required properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) - Assert.isEqual(Value.Check(T, { x: 1 }), false) - Assert.isEqual(Value.Check(T, { x: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1 }), false) }) it('Should intersect two objects with unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], {}) - Assert.isEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), true) }) it('Should intersect two objects and restrict unevaluated properties', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], { unevaluatedProperties: false }) - Assert.isEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1, z: 1 }), false) }) it('Should intersect two objects and allow unevaluated properties of number', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ y: Type.Number() }) const T = Type.Intersect([A, B], { unevaluatedProperties: Type.Number() }) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: 3 }), true) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: '1' }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, z: 3 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, z: '1' }), false) }) it('Should intersect two union objects with overlapping properties of the same type', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ x: Type.Number() })]) const T = Type.Intersect([A, B]) - Assert.isEqual(Value.Check(T, { x: 1 }), true) - Assert.isEqual(Value.Check(T, { x: '1' }), false) + Assert.IsEqual(Value.Check(T, { x: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: '1' }), false) }) it('Should not intersect two union objects with overlapping properties of varying types', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ x: Type.String() })]) const T = Type.Intersect([A, B]) - Assert.isEqual(Value.Check(T, { x: 1 }), false) - Assert.isEqual(Value.Check(T, { x: '1' }), false) + Assert.IsEqual(Value.Check(T, { x: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: '1' }), false) }) it('Should intersect two union objects with non-overlapping properties', () => { const A = Type.Union([Type.Object({ x: Type.Number() })]) const B = Type.Union([Type.Object({ y: Type.Number() })]) const T = Type.Intersect([A, B]) - Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1 }), true) }) it('Should not intersect two union objects with non-overlapping properties for additionalProperties false', () => { const A = Type.Union([Type.Object({ x: Type.Number() }, { additionalProperties: false })]) const B = Type.Union([Type.Object({ y: Type.Number() }, { additionalProperties: false })]) const T = Type.Intersect([A, B]) - Assert.isEqual(Value.Check(T, { x: 1, y: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 1 }), false) }) it('unevaluatedProperties with Record 1', () => { const T = Type.Intersect( @@ -94,7 +94,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2 }), true) }) it('unevaluatedProperties with Record 2', () => { const T = Type.Intersect( @@ -109,7 +109,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: 'hello' }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, 0: 'hello' }), true) }) it('unevaluatedProperties with Record 3', () => { const T = Type.Intersect( @@ -124,7 +124,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: false, }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, 0: 1 }), false) }) it('unevaluatedProperties with Record 4', () => { const T = Type.Intersect( @@ -139,7 +139,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2 }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2 }), true) }) it('unevaluatedProperties with Record 5', () => { const T = Type.Intersect( @@ -154,7 +154,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: true }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, z: true }), true) }) it('unevaluatedProperties with Record 6', () => { const T = Type.Intersect( @@ -169,7 +169,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, z: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, z: 1 }), false) }) it('unevaluatedProperties with Record 7', () => { const T = Type.Intersect( @@ -184,7 +184,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '' }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, 0: '' }), true) }) it('unevaluatedProperties with Record 8', () => { const T = Type.Intersect( @@ -199,7 +199,7 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: true }), true) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: true }), true) }) it('unevaluatedProperties with Record 9', () => { const T = Type.Intersect( @@ -214,6 +214,6 @@ describe('value/check/Intersect', () => { unevaluatedProperties: Type.Boolean(), }, ) - Assert.isEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: 1 }), false) + Assert.IsEqual(Value.Check(T, { x: 1, y: 2, 0: '', z: 1 }), false) }) }) diff --git a/test/runtime/value/check/iterator.ts b/test/runtime/value/check/iterator.ts new file mode 100644 index 000000000..69274d107 --- /dev/null +++ b/test/runtime/value/check/iterator.ts @@ -0,0 +1,24 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Iterator', () => { + it('Should pass iterator 1', () => { + function* f() {} + const T = Type.Iterator(Type.Any()) + const result = Value.Check(T, f()) + Assert.IsEqual(result, true) + }) + it('Should pass iterator 2', () => { + const T = Type.Iterator(Type.Any()) + const result = Value.Check(T, { + [Symbol.iterator]: () => {}, + }) + Assert.IsEqual(result, true) + }) + it('Should pass iterator', () => { + const T = Type.Iterator(Type.Any()) + const result = Value.Check(T, {}) + Assert.IsEqual(result, false) + }) +}) diff --git a/test/runtime/value/check/keyof.ts b/test/runtime/value/check/keyof.ts index ff9234f27..a3407c95d 100644 --- a/test/runtime/value/check/keyof.ts +++ b/test/runtime/value/check/keyof.ts @@ -10,28 +10,24 @@ describe('value/check/KeyOf', () => { z: Type.Number(), }), ) - it('Should pass keyof', () => { const value = 'x' const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail keyof', () => { const value = 'w' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail keyof with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail keyof with null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/literal.ts b/test/runtime/value/check/literal.ts index ae1e28dcb..4acca4557 100644 --- a/test/runtime/value/check/literal.ts +++ b/test/runtime/value/check/literal.ts @@ -7,21 +7,21 @@ describe('value/check/Literal', () => { it('Should pass literal', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail literal', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail literal with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail literal with null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/never.ts b/test/runtime/value/check/never.ts index 979e74474..325f189f3 100644 --- a/test/runtime/value/check/never.ts +++ b/test/runtime/value/check/never.ts @@ -7,36 +7,36 @@ describe('value/check/Never', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/not.ts b/test/runtime/value/check/not.ts index 780d08c24..ba5471bf8 100644 --- a/test/runtime/value/check/not.ts +++ b/test/runtime/value/check/not.ts @@ -5,8 +5,8 @@ import { Assert } from '../../assert/index' describe('value/check/Not', () => { it('Should validate with not number', () => { const T = Type.Not(Type.Number(), Type.String()) - Assert.isEqual(Value.Check(T, 1), false) - Assert.isEqual(Value.Check(T, 'A'), true) + Assert.IsEqual(Value.Check(T, 1), false) + Assert.IsEqual(Value.Check(T, 'A'), true) }) it('Should validate with union left', () => { // prettier-ignore @@ -15,10 +15,10 @@ describe('value/check/Not', () => { Type.Literal('B'), Type.Literal('C') ]), Type.String()) - Assert.isEqual(Value.Check(T, 'A'), false) - Assert.isEqual(Value.Check(T, 'B'), false) - Assert.isEqual(Value.Check(T, 'C'), false) - Assert.isEqual(Value.Check(T, 'D'), true) + Assert.IsEqual(Value.Check(T, 'A'), false) + Assert.IsEqual(Value.Check(T, 'B'), false) + Assert.IsEqual(Value.Check(T, 'C'), false) + Assert.IsEqual(Value.Check(T, 'D'), true) }) it('Should validate with union right', () => { // prettier-ignore @@ -26,8 +26,8 @@ describe('value/check/Not', () => { Type.String(), Type.Boolean() ])) - Assert.isEqual(Value.Check(T, 1), false) - Assert.isEqual(Value.Check(T, 'A'), true) - Assert.isEqual(Value.Check(T, true), true) + Assert.IsEqual(Value.Check(T, 1), false) + Assert.IsEqual(Value.Check(T, 'A'), true) + Assert.IsEqual(Value.Check(T, true), true) }) }) diff --git a/test/runtime/value/check/null.ts b/test/runtime/value/check/null.ts index fea54f9ec..2a92ec855 100644 --- a/test/runtime/value/check/null.ts +++ b/test/runtime/value/check/null.ts @@ -7,41 +7,41 @@ describe('value/check/Null', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/number.ts b/test/runtime/value/check/number.ts index 3b60578ed..a38909989 100644 --- a/test/runtime/value/check/number.ts +++ b/test/runtime/value/check/number.ts @@ -7,61 +7,61 @@ describe('value/check/Number', () => { it('Should not validate NaN', () => { const T = Type.Number() const result = Value.Check(T, NaN) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate +Infinity', () => { const T = Type.Number() const result = Value.Check(T, Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should not validate -Infinity', () => { const T = Type.Number() const result = Value.Check(T, -Infinity) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail NaN', () => { const result = Value.Check(Type.Number(), NaN) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/object.ts b/test/runtime/value/check/object.ts index 65485881b..fe6b5b718 100644 --- a/test/runtime/value/check/object.ts +++ b/test/runtime/value/check/object.ts @@ -11,7 +11,6 @@ describe('value/check/Object', () => { b: Type.String(), c: Type.String(), }) - it('Should pass object', () => { const value = { x: 1, @@ -22,9 +21,8 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail object with additional properties', () => { const T = Type.Object( { @@ -41,9 +39,8 @@ describe('value/check/Object', () => { a: 1, } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail object with invalid property', () => { const value = { x: true, @@ -54,9 +51,8 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail object with missing property', () => { const value = { y: 1, @@ -66,9 +62,8 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should pass object with optional properties', () => { const T = Type.Object({ x: Type.Optional(Type.Number()), @@ -84,21 +79,18 @@ describe('value/check/Object', () => { c: '1', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) - it('Should fail object with null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should fail object with undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) - it('Should validate schema additional properties of string', () => { const T = Type.Object( { @@ -109,8 +101,7 @@ describe('value/check/Object', () => { additionalProperties: Type.String(), }, ) - - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -119,7 +110,7 @@ describe('value/check/Object', () => { true, ) - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -128,7 +119,6 @@ describe('value/check/Object', () => { false, ) }) - it('Should validate schema additional properties of array', () => { const T = Type.Object( { @@ -139,8 +129,7 @@ describe('value/check/Object', () => { additionalProperties: Type.Array(Type.Number()), }, ) - - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -148,8 +137,7 @@ describe('value/check/Object', () => { }), true, ) - - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -158,7 +146,6 @@ describe('value/check/Object', () => { false, ) }) - it('Should validate schema additional properties of object', () => { const T = Type.Object( { @@ -171,8 +158,7 @@ describe('value/check/Object', () => { }), }, ) - - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -180,8 +166,7 @@ describe('value/check/Object', () => { }), true, ) - - Assert.isEqual( + Assert.IsEqual( Value.Check(T, { x: 1, y: 2, @@ -192,36 +177,36 @@ describe('value/check/Object', () => { }) it('Should check for property key if property type is undefined', () => { const T = Type.Object({ x: Type.Undefined() }) - Assert.isEqual(Value.Check(T, { x: undefined }), true) - Assert.isEqual(Value.Check(T, {}), false) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) + Assert.IsEqual(Value.Check(T, {}), false) }) it('Should check for property key if property type extends undefined', () => { const T = Type.Object({ x: Type.Union([Type.Number(), Type.Undefined()]) }) - Assert.isEqual(Value.Check(T, { x: 1 }), true) - Assert.isEqual(Value.Check(T, { x: undefined }), true) - Assert.isEqual(Value.Check(T, {}), false) + Assert.IsEqual(Value.Check(T, { x: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) + Assert.IsEqual(Value.Check(T, {}), false) }) it('Should not check for property key if property type is undefined and optional', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) - Assert.isEqual(Value.Check(T, { x: undefined }), true) - Assert.isEqual(Value.Check(T, {}), true) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) + Assert.IsEqual(Value.Check(T, {}), true) }) it('Should not check for property key if property type extends undefined and optional', () => { const T = Type.Object({ x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])) }) - Assert.isEqual(Value.Check(T, { x: 1 }), true) - Assert.isEqual(Value.Check(T, { x: undefined }), true) - Assert.isEqual(Value.Check(T, {}), true) + Assert.IsEqual(Value.Check(T, { x: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) + Assert.IsEqual(Value.Check(T, {}), true) }) it('Should check undefined for optional property of number', () => { const T = Type.Object({ x: Type.Optional(Type.Number()) }) - Assert.isEqual(Value.Check(T, { x: 1 }), true) - Assert.isEqual(Value.Check(T, { x: undefined }), true) // allowed by default - Assert.isEqual(Value.Check(T, {}), true) + Assert.IsEqual(Value.Check(T, { x: 1 }), true) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) // allowed by default + Assert.IsEqual(Value.Check(T, {}), true) }) it('Should check undefined for optional property of undefined', () => { const T = Type.Object({ x: Type.Optional(Type.Undefined()) }) - Assert.isEqual(Value.Check(T, { x: 1 }), false) - Assert.isEqual(Value.Check(T, {}), true) - Assert.isEqual(Value.Check(T, { x: undefined }), true) + Assert.IsEqual(Value.Check(T, { x: 1 }), false) + Assert.IsEqual(Value.Check(T, {}), true) + Assert.IsEqual(Value.Check(T, { x: undefined }), true) }) }) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 1e36e37b1..c6e04ce8d 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -20,33 +20,33 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail when below minProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { minProperties: 4 }) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) }) it('Should fail when above maxProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { maxProperties: 4 }) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), true) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) }) it('Should fail with illogical minProperties | maxProperties', () => { const T = Type.Record(Type.String(), Type.Number(), { minProperties: 5, maxProperties: 4 }) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), false) - Assert.isEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3 }), false) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4 }), false) + Assert.IsEqual(Value.Check(T, { a: 1, b: 2, c: 3, d: 4, e: 5 }), false) }) it('Should fail record with Date', () => { const T = Type.Record(Type.String(), Type.String()) const result = Value.Check(T, new Date()) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail record with Uint8Array', () => { const T = Type.Record(Type.String(), Type.String()) const result = Value.Check(T, new Uint8Array()) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail record with missing property', () => { const T = Type.Record( @@ -64,7 +64,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail record with invalid property', () => { const T = Type.Record( @@ -83,7 +83,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass record with optional property', () => { const T = Type.Record( @@ -101,7 +101,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass record with optional property', () => { const T = Type.Record( @@ -119,7 +119,7 @@ describe('value/check/Record', () => { }, } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) // ------------------------------------------------- // Number Key @@ -132,7 +132,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should not pass record with invalid number key', () => { @@ -143,7 +143,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) // ------------------------------------------------- // Integer Key @@ -156,7 +156,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should not pass record with invalid integer key', () => { const T = Type.Record(Type.Integer(), Type.String(), { additionalProperties: false }) @@ -166,7 +166,7 @@ describe('value/check/Record', () => { 2: 'a', } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) // ------------------------------------------------------------ // AdditionalProperties @@ -174,26 +174,26 @@ describe('value/check/Record', () => { it('AdditionalProperties 1', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: true }) const R = Value.Check(T, { 1: '', 2: '', x: 1, y: 2, z: 3 }) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('AdditionalProperties 2', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) const R = Value.Check(T, { 1: '', 2: '', 3: '' }) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('AdditionalProperties 3', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: false }) const R = Value.Check(T, { 1: '', 2: '', x: '' }) - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) it('AdditionalProperties 4', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) const R = Value.Check(T, { 1: '', 2: '', x: '' }) - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) it('AdditionalProperties 5', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) const R = Value.Check(T, { 1: '', 2: '', x: true }) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) }) diff --git a/test/runtime/value/check/recursive.ts b/test/runtime/value/check/recursive.ts index f6c7b94be..1b6c60bb9 100644 --- a/test/runtime/value/check/recursive.ts +++ b/test/runtime/value/check/recursive.ts @@ -20,7 +20,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail recursive with invalid id', () => { @@ -33,7 +33,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail rec with invalid nodes', () => { @@ -46,7 +46,7 @@ describe('value/check/Recursive', () => { ], } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail recursive with missing id', () => { @@ -55,7 +55,7 @@ describe('value/check/Recursive', () => { nodes: [{ nodes: [] }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail rec with missing nodes', () => { @@ -64,6 +64,6 @@ describe('value/check/Recursive', () => { nodes: [{ id: 'B' }, { id: 'C', nodes: [] }, { id: 'D', nodes: [] }], } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/ref.ts b/test/runtime/value/check/ref.ts index e838a383a..cc541149d 100644 --- a/test/runtime/value/check/ref.ts +++ b/test/runtime/value/check/ref.ts @@ -10,10 +10,10 @@ describe('value/check/Ref', () => { y: Type.Number(), z: Type.Number(), }, - { $id: Assert.nextId() }, + { $id: Assert.NextId() }, ) const R = Type.Ref(T) - Assert.isEqual( + Assert.IsEqual( Value.Check(R, [T], { x: 1, y: 2, @@ -30,10 +30,10 @@ describe('value/check/Ref', () => { y: Type.Number(), z: Type.Number(), }, - { $id: Assert.nextId() }, + { $id: Assert.NextId() }, ) const R = Type.Ref(T) - Assert.isEqual( + Assert.IsEqual( Value.Check(R, [T], { x: 1, y: 2, @@ -59,10 +59,10 @@ describe('value/check/Ref', () => { }, { $id: 'T' }, ) - Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3 }), true) - Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 'hello' } }), true) - Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 1 } }), false) - Assert.isEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: {} }), false) + Assert.IsEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3 }), true) + Assert.IsEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 'hello' } }), true) + Assert.IsEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: { name: 1 } }), false) + Assert.IsEqual(Value.Check(R, [T], { x: 1, y: 2, z: 3, r: {} }), false) // Ok(R, { x: 1, y: 2, z: 3 }, [T]) // Ok(R, , [T]) // Fail(R, , [T]) @@ -77,7 +77,7 @@ describe('value/check/Ref', () => { }), ) const R = Type.Ref(T) - Assert.isEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) - Assert.isEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) + Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) + Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) }) }) diff --git a/test/runtime/value/check/regex.ts b/test/runtime/value/check/regex.ts deleted file mode 100644 index b56076ded..000000000 --- a/test/runtime/value/check/regex.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('value/check/RegEx', () => { - it('Should pass regex', () => { - const T = Type.RegEx(/foo/) - const value = 'foo' - const result = Value.Check(T, value) - Assert.isEqual(result, true) - }) - - it('Should fail regex', () => { - const T = Type.RegEx(/foo/) - const value = 'bar' - const result = Value.Check(T, value) - Assert.isEqual(result, false) - }) -}) diff --git a/test/runtime/value/check/regexp.ts b/test/runtime/value/check/regexp.ts new file mode 100644 index 000000000..ce843038e --- /dev/null +++ b/test/runtime/value/check/regexp.ts @@ -0,0 +1,36 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/RegExp', () => { + // ------------------------------------------------- + // Regular Expression + // ------------------------------------------------- + it('Should pass regular expression 1', () => { + const T = Type.RegExp(/foo/) + const value = 'foo' + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass regular expression 2', () => { + const T = Type.RegExp(/foo/) + const value = 'bar' + const result = Value.Check(T, value) + Assert.IsEqual(result, false) + }) + // ------------------------------------------------- + // Pattern + // ------------------------------------------------- + it('Should pass pattern 1', () => { + const T = Type.RegExp('foo') + const value = 'foo' + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass pattern 2', () => { + const T = Type.RegExp('foo') + const value = 'bar' + const result = Value.Check(T, value) + Assert.IsEqual(result, false) + }) +}) diff --git a/test/runtime/value/check/string.ts b/test/runtime/value/check/string.ts index 1dbc26690..48a0eb6a9 100644 --- a/test/runtime/value/check/string.ts +++ b/test/runtime/value/check/string.ts @@ -7,41 +7,41 @@ describe('value/check/String', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/symbol.ts b/test/runtime/value/check/symbol.ts index 12b343d15..ad342813d 100644 --- a/test/runtime/value/check/symbol.ts +++ b/test/runtime/value/check/symbol.ts @@ -7,46 +7,46 @@ describe('value/check/Symbol', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass symbol', () => { const value = Symbol(1) const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/check/template-literal.ts b/test/runtime/value/check/template-literal.ts index 44fbbb66b..0cffa6f86 100644 --- a/test/runtime/value/check/template-literal.ts +++ b/test/runtime/value/check/template-literal.ts @@ -9,23 +9,23 @@ describe('value/check/TemplateLiteral', () => { it('Should validate finite pattern 1', () => { // prettier-ignore const T = Type.TemplateLiteral([]) - Assert.isEqual(Value.Check(T, ''), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, ''), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 1', () => { // prettier-ignore const T = Type.TemplateLiteral([Type.Boolean()]) - Assert.isEqual(Value.Check(T, 'true'), true) - Assert.isEqual(Value.Check(T, 'false'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'true'), true) + Assert.IsEqual(Value.Check(T, 'false'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 2', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.Literal('A') ]) - Assert.isEqual(Value.Check(T, 'A'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'A'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 3', () => { // prettier-ignore @@ -33,8 +33,8 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Literal('B') ]) - Assert.isEqual(Value.Check(T, 'AB'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'AB'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 4', () => { // prettier-ignore @@ -45,9 +45,9 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('C') ]), ]) - Assert.isEqual(Value.Check(T, 'AB'), true) - Assert.isEqual(Value.Check(T, 'AC'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'AB'), true) + Assert.IsEqual(Value.Check(T, 'AC'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 5', () => { // prettier-ignore @@ -59,9 +59,9 @@ describe('value/check/TemplateLiteral', () => { ]), Type.Literal('D'), ]) - Assert.isEqual(Value.Check(T, 'ABD'), true) - Assert.isEqual(Value.Check(T, 'ACD'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'ABD'), true) + Assert.IsEqual(Value.Check(T, 'ACD'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate finite pattern 6', () => { // prettier-ignore @@ -75,11 +75,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('1') ]), ]) - Assert.isEqual(Value.Check(T, '00'), true) - Assert.isEqual(Value.Check(T, '01'), true) - Assert.isEqual(Value.Check(T, '10'), true) - Assert.isEqual(Value.Check(T, '11'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, '00'), true) + Assert.IsEqual(Value.Check(T, '01'), true) + Assert.IsEqual(Value.Check(T, '10'), true) + Assert.IsEqual(Value.Check(T, '11'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) // -------------------------------------------------------- // Infinite @@ -89,47 +89,47 @@ describe('value/check/TemplateLiteral', () => { const T = Type.TemplateLiteral([ Type.Number() ]) - Assert.isEqual(Value.Check(T, '1'), true) - Assert.isEqual(Value.Check(T, '22'), true) - Assert.isEqual(Value.Check(T, '333'), true) - Assert.isEqual(Value.Check(T, '4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, '1'), true) + Assert.IsEqual(Value.Check(T, '22'), true) + Assert.IsEqual(Value.Check(T, '333'), true) + Assert.IsEqual(Value.Check(T, '4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 2', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.Integer() ]) - Assert.isEqual(Value.Check(T, '1'), true) - Assert.isEqual(Value.Check(T, '22'), true) - Assert.isEqual(Value.Check(T, '333'), true) - Assert.isEqual(Value.Check(T, '4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, '1'), true) + Assert.IsEqual(Value.Check(T, '22'), true) + Assert.IsEqual(Value.Check(T, '333'), true) + Assert.IsEqual(Value.Check(T, '4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 3', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.BigInt() ]) - Assert.isEqual(Value.Check(T, '1'), true) - Assert.isEqual(Value.Check(T, '22'), true) - Assert.isEqual(Value.Check(T, '333'), true) - Assert.isEqual(Value.Check(T, '4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, '1'), true) + Assert.IsEqual(Value.Check(T, '22'), true) + Assert.IsEqual(Value.Check(T, '333'), true) + Assert.IsEqual(Value.Check(T, '4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 4', () => { // prettier-ignore const T = Type.TemplateLiteral([ Type.String() ]) - Assert.isEqual(Value.Check(T, '1'), true) - Assert.isEqual(Value.Check(T, '22'), true) - Assert.isEqual(Value.Check(T, '333'), true) - Assert.isEqual(Value.Check(T, '4444'), true) - Assert.isEqual(Value.Check(T, 'a'), true) - Assert.isEqual(Value.Check(T, 'bb'), true) - Assert.isEqual(Value.Check(T, 'ccc'), true) - Assert.isEqual(Value.Check(T, 'dddd'), true) + Assert.IsEqual(Value.Check(T, '1'), true) + Assert.IsEqual(Value.Check(T, '22'), true) + Assert.IsEqual(Value.Check(T, '333'), true) + Assert.IsEqual(Value.Check(T, '4444'), true) + Assert.IsEqual(Value.Check(T, 'a'), true) + Assert.IsEqual(Value.Check(T, 'bb'), true) + Assert.IsEqual(Value.Check(T, 'ccc'), true) + Assert.IsEqual(Value.Check(T, 'dddd'), true) }) it('Should validate infinite pattern 5', () => { @@ -138,11 +138,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Number() ]) - Assert.isEqual(Value.Check(T, 'A1'), true) - Assert.isEqual(Value.Check(T, 'A22'), true) - Assert.isEqual(Value.Check(T, 'A333'), true) - Assert.isEqual(Value.Check(T, 'A4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'A1'), true) + Assert.IsEqual(Value.Check(T, 'A22'), true) + Assert.IsEqual(Value.Check(T, 'A333'), true) + Assert.IsEqual(Value.Check(T, 'A4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 6', () => { // prettier-ignore @@ -150,11 +150,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.Integer() ]) - Assert.isEqual(Value.Check(T, 'A1'), true) - Assert.isEqual(Value.Check(T, 'A22'), true) - Assert.isEqual(Value.Check(T, 'A333'), true) - Assert.isEqual(Value.Check(T, 'A4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'A1'), true) + Assert.IsEqual(Value.Check(T, 'A22'), true) + Assert.IsEqual(Value.Check(T, 'A333'), true) + Assert.IsEqual(Value.Check(T, 'A4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 7', () => { // prettier-ignore @@ -162,11 +162,11 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.BigInt() ]) - Assert.isEqual(Value.Check(T, 'A1'), true) - Assert.isEqual(Value.Check(T, 'A22'), true) - Assert.isEqual(Value.Check(T, 'A333'), true) - Assert.isEqual(Value.Check(T, 'A4444'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'A1'), true) + Assert.IsEqual(Value.Check(T, 'A22'), true) + Assert.IsEqual(Value.Check(T, 'A333'), true) + Assert.IsEqual(Value.Check(T, 'A4444'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) it('Should validate infinite pattern 8', () => { // prettier-ignore @@ -174,14 +174,14 @@ describe('value/check/TemplateLiteral', () => { Type.Literal('A'), Type.String() ]) - Assert.isEqual(Value.Check(T, 'A1'), true) - Assert.isEqual(Value.Check(T, 'A22'), true) - Assert.isEqual(Value.Check(T, 'A333'), true) - Assert.isEqual(Value.Check(T, 'A4444'), true) - Assert.isEqual(Value.Check(T, 'Aa'), true) - Assert.isEqual(Value.Check(T, 'Abb'), true) - Assert.isEqual(Value.Check(T, 'Accc'), true) - Assert.isEqual(Value.Check(T, 'Adddd'), true) - Assert.isEqual(Value.Check(T, 'X'), false) + Assert.IsEqual(Value.Check(T, 'A1'), true) + Assert.IsEqual(Value.Check(T, 'A22'), true) + Assert.IsEqual(Value.Check(T, 'A333'), true) + Assert.IsEqual(Value.Check(T, 'A4444'), true) + Assert.IsEqual(Value.Check(T, 'Aa'), true) + Assert.IsEqual(Value.Check(T, 'Abb'), true) + Assert.IsEqual(Value.Check(T, 'Accc'), true) + Assert.IsEqual(Value.Check(T, 'Adddd'), true) + Assert.IsEqual(Value.Check(T, 'X'), false) }) }) diff --git a/test/runtime/value/check/tuple.ts b/test/runtime/value/check/tuple.ts index 2fc512f98..1a9f039c8 100644 --- a/test/runtime/value/check/tuple.ts +++ b/test/runtime/value/check/tuple.ts @@ -7,34 +7,34 @@ describe('value/check/Tuple', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail when tuple is less than length', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail when tuple is greater than length', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const value = [1, 1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass empty tuple', () => { const T = Type.Tuple([]) const value = [] as any[] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail empty tuple', () => { const T = Type.Tuple([]) const value = [1] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/uint8array.ts b/test/runtime/value/check/uint8array.ts index 21f526a79..c989fa966 100644 --- a/test/runtime/value/check/uint8array.ts +++ b/test/runtime/value/check/uint8array.ts @@ -7,27 +7,27 @@ describe('value/check/Uint8Array', () => { const T = Type.Uint8Array() const value = new Uint8Array(4) const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail Uint8Array', () => { const T = Type.Uint8Array() const value = [0, 1, 2, 3] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Uint8Array with undefined', () => { const T = Type.Uint8Array() const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Uint8Array with null', () => { const T = Type.Uint8Array() const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/undefined.ts b/test/runtime/value/check/undefined.ts index d25ec8303..bdc0099c0 100644 --- a/test/runtime/value/check/undefined.ts +++ b/test/runtime/value/check/undefined.ts @@ -7,41 +7,41 @@ describe('value/check/Undefined', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/union.ts b/test/runtime/value/check/union.ts index bbd29951e..c5a31b653 100644 --- a/test/runtime/value/check/union.ts +++ b/test/runtime/value/check/union.ts @@ -20,25 +20,25 @@ describe('value/check/Union', () => { it('Should pass union A', () => { const value = { type: 'A', x: 1, y: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass union B', () => { const value = { type: 'B', x: true, y: false } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail union A', () => { const value = { type: 'A', x: true, y: false } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail union B', () => { const value = { type: 'B', x: 1, y: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass union A with optional properties', () => { @@ -55,7 +55,7 @@ describe('value/check/Union', () => { const T = Type.Union([A, B]) const value = { type: 'A' } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail union A with invalid optional properties', () => { @@ -72,6 +72,6 @@ describe('value/check/Union', () => { const T = Type.Union([A, B]) const value = { type: 'A', x: true, y: false } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/check/unknown.ts b/test/runtime/value/check/unknown.ts index bb9943933..7026f3dcc 100644 --- a/test/runtime/value/check/unknown.ts +++ b/test/runtime/value/check/unknown.ts @@ -7,41 +7,41 @@ describe('value/check/Unknown', () => { it('Should pass string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) }) diff --git a/test/runtime/value/check/void.ts b/test/runtime/value/check/void.ts index 0c1b26eb9..92ff30b74 100644 --- a/test/runtime/value/check/void.ts +++ b/test/runtime/value/check/void.ts @@ -7,41 +7,41 @@ describe('value/check/Void', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass null', () => { const value = null const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should pass undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should fail Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) }) diff --git a/test/runtime/value/clone/clone.ts b/test/runtime/value/clone/clone.ts index 8655dd1ba..15ecb1f57 100644 --- a/test/runtime/value/clone/clone.ts +++ b/test/runtime/value/clone/clone.ts @@ -7,44 +7,36 @@ describe('value/clone/Clone', () => { // -------------------------------------------- it('Should clone null', () => { const R = Value.Clone(null) - Assert.isEqual(R, null) + Assert.IsEqual(R, null) }) - it('Should clone undefined', () => { const R = Value.Clone(undefined) - Assert.isEqual(R, undefined) + Assert.IsEqual(R, undefined) }) - it('Should clone number', () => { const R = Value.Clone(1) - Assert.isEqual(R, 1) + Assert.IsEqual(R, 1) }) - it('Should clone bigint', () => { const R = Value.Clone(1n) - Assert.isEqual(R, 1n) + Assert.IsEqual(R, 1n) }) - it('Should clone boolean', () => { const R = Value.Clone(true) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) - it('Should clone string', () => { const R = Value.Clone('hello') - Assert.isEqual(R, 'hello') + Assert.IsEqual(R, 'hello') }) - it('Should clone symbol', () => { const S = Symbol('hello') const R = Value.Clone(S) - Assert.isEqual(R, S) + Assert.IsEqual(R, S) }) - // -------------------------------------------- // ObjectType // -------------------------------------------- - it('Should clone object #1', () => { const V = { x: 1, @@ -52,9 +44,8 @@ describe('value/clone/Clone', () => { z: 3, } const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone object #2', () => { const V = { x: 1, @@ -67,9 +58,8 @@ describe('value/clone/Clone', () => { }, } const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone object #3', () => { const V = { x: 1, @@ -78,7 +68,7 @@ describe('value/clone/Clone', () => { w: [0, 1, 2, 3, 4], } const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) // -------------------------------------------- // ArrayType @@ -86,7 +76,7 @@ describe('value/clone/Clone', () => { it('Should clone array #1', () => { const V = [1, 2, 3, 4] const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should clone array #2', () => { const V = [ @@ -96,7 +86,7 @@ describe('value/clone/Clone', () => { [1, 2, 3], ] const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should clone array #3', () => { const V = [ @@ -106,72 +96,61 @@ describe('value/clone/Clone', () => { { x: 1, y: 2, z: 3 }, ] const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Int8Array', () => { const V = new Int8Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Uint8Array', () => { const V = new Uint8Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Uint8ClampedArray', () => { const V = new Uint8ClampedArray([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Int16Array', () => { const V = new Int16Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Uint16Array', () => { const V = new Uint16Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Int32Array', () => { const V = new Int32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Uint32Array', () => { const V = new Int32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Float32Array', () => { const V = new Float32Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone Float64Array', () => { const V = new Float64Array([1, 2, 3, 4]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone BigInt64Array', () => { const V = new BigInt64Array([1n, 2n, 3n, 4n]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) - it('Should clone BigUint64Array', () => { const V = new BigUint64Array([1n, 2n, 3n, 4n]) const R = Value.Clone(V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) }) diff --git a/test/runtime/value/convert/any.ts b/test/runtime/value/convert/any.ts index 81f57de58..96a6d5841 100644 --- a/test/runtime/value/convert/any.ts +++ b/test/runtime/value/convert/any.ts @@ -7,36 +7,36 @@ describe('value/convert/Any', () => { it('Should convert null', () => { const V = null const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert undefined', () => { const V = undefined const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert string', () => { const V = 'hello' const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert number', () => { const V = 42 const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert boolean', () => { const V = true const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert object', () => { const V = { x: 1 } const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert array', () => { const V = [1, 2, 3] const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) }) diff --git a/test/runtime/value/convert/array.ts b/test/runtime/value/convert/array.ts index be8ed6af6..c8a040f3d 100644 --- a/test/runtime/value/convert/array.ts +++ b/test/runtime/value/convert/array.ts @@ -6,27 +6,27 @@ describe('value/convert/Array', () => { it('Should convert array of number', () => { const T = Type.Array(Type.Number()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.isEqual(R, [1, 3.14, 1, 3.14, 1, 0, 1, 0, 'hello']) + Assert.IsEqual(R, [1, 3.14, 1, 3.14, 1, 0, 1, 0, 'hello']) }) it('Should convert array of boolean', () => { const T = Type.Array(Type.Boolean()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.isEqual(R, [true, 3.14, true, '3.14', true, false, true, false, 'hello']) + Assert.IsEqual(R, [true, 3.14, true, '3.14', true, false, true, false, 'hello']) }) it('Should convert array of string', () => { const T = Type.Array(Type.String()) const R = Value.Convert(T, [1, 3.14, '1', '3.14', true, false, 'true', 'false', 'hello']) - Assert.isEqual(R, ['1', '3.14', '1', '3.14', 'true', 'false', 'true', 'false', 'hello']) + Assert.IsEqual(R, ['1', '3.14', '1', '3.14', 'true', 'false', 'true', 'false', 'hello']) }) it('Should convert array of date', () => { const T = Type.Array(Type.Date()) const R = Value.Convert(T, [1, '1', true, false, 'true', 'false', 'hello']) as any[] - Assert.isEqual(R[0].getTime(), 1) - Assert.isEqual(R[1].getTime(), 1) - Assert.isEqual(R[2].getTime(), 1) - Assert.isEqual(R[3].getTime(), 0) - Assert.isEqual(R[4].getTime(), 1) - Assert.isEqual(R[5].getTime(), 0) - Assert.isEqual(R[6], 'hello') + Assert.IsEqual(R[0].getTime(), 1) + Assert.IsEqual(R[1].getTime(), 1) + Assert.IsEqual(R[2].getTime(), 1) + Assert.IsEqual(R[3].getTime(), 0) + Assert.IsEqual(R[4].getTime(), 1) + Assert.IsEqual(R[5].getTime(), 0) + Assert.IsEqual(R[6], 'hello') }) }) diff --git a/test/runtime/value/convert/async-iterator.ts b/test/runtime/value/convert/async-iterator.ts new file mode 100644 index 000000000..2429faf93 --- /dev/null +++ b/test/runtime/value/convert/async-iterator.ts @@ -0,0 +1,20 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// -------------------------------------------------------- +// non-convertable pass through +// -------------------------------------------------------- +describe('value/convert/AsyncIterator', () => { + const T = Type.AsyncIterator(Type.Any()) + it('Should passthrough 1', () => { + const V = (async function* () {})() + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) + it('Should passthrough 2', () => { + const V = 1 + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts index 83a069632..c5057ab00 100644 --- a/test/runtime/value/convert/bigint.ts +++ b/test/runtime/value/convert/bigint.ts @@ -6,41 +6,41 @@ describe('value/convert/BigInt', () => { it('Should convert bitint from string 1', () => { const T = Type.BigInt() const R = Value.Convert(T, '1') - Assert.isEqual(R, BigInt(1)) + Assert.IsEqual(R, BigInt(1)) }) it('Should convert bigint from string 2', () => { const T = Type.BigInt() const R = Value.Convert(T, '3.14') - Assert.isEqual(R, BigInt(3)) + Assert.IsEqual(R, BigInt(3)) }) it('Should convert bitint from string 3', () => { const T = Type.BigInt() const R = Value.Convert(T, 'true') - Assert.isEqual(R, BigInt(1)) + Assert.IsEqual(R, BigInt(1)) }) it('Should convert bigint from string 4', () => { const T = Type.BigInt() const R = Value.Convert(T, 'false') - Assert.isEqual(R, BigInt(0)) + Assert.IsEqual(R, BigInt(0)) }) it('Should convert bitint from number 1', () => { const T = Type.BigInt() const R = Value.Convert(T, 1) - Assert.isEqual(R, BigInt(1)) + Assert.IsEqual(R, BigInt(1)) }) it('Should convert bigint from number 2', () => { const T = Type.BigInt() const R = Value.Convert(T, 3.14) - Assert.isEqual(R, BigInt(3)) + Assert.IsEqual(R, BigInt(3)) }) it('Should convert bitint from boolean 1', () => { const T = Type.BigInt() const R = Value.Convert(T, true) - Assert.isEqual(R, BigInt(1)) + Assert.IsEqual(R, BigInt(1)) }) it('Should convert bigint from boolean 2', () => { const T = Type.BigInt() const R = Value.Convert(T, false) - Assert.isEqual(R, BigInt(0)) + Assert.IsEqual(R, BigInt(0)) }) }) diff --git a/test/runtime/value/convert/boolean.ts b/test/runtime/value/convert/boolean.ts index 2a72c08ee..8e91f6d5f 100644 --- a/test/runtime/value/convert/boolean.ts +++ b/test/runtime/value/convert/boolean.ts @@ -6,47 +6,47 @@ describe('value/convert/Boolean', () => { it('Should convert from string 1', () => { const T = Type.Boolean() const R = Value.Convert(T, '1') - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from string 2', () => { const T = Type.Boolean() const R = Value.Convert(T, '3.14') - Assert.isEqual(R, '3.14') + Assert.IsEqual(R, '3.14') }) it('Should convert from string 3', () => { const T = Type.Boolean() const R = Value.Convert(T, 'true') - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from string 4', () => { const T = Type.Boolean() const R = Value.Convert(T, 'false') - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) it('Should convert from number 1', () => { const T = Type.Boolean() const R = Value.Convert(T, 1) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from number 2', () => { const T = Type.Boolean() const R = Value.Convert(T, 3.14) - Assert.isEqual(R, 3.14) + Assert.IsEqual(R, 3.14) }) it('Should convert from number 3', () => { const T = Type.Boolean() const R = Value.Convert(T, 1.1) - Assert.isEqual(R, 1.1) + Assert.IsEqual(R, 1.1) }) it('Should convert from boolean 1', () => { const T = Type.Boolean() const R = Value.Convert(T, true) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from boolean 2', () => { const T = Type.Boolean() const R = Value.Convert(T, false) - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) // ---------------------------------------------------------- // Casts @@ -54,101 +54,101 @@ describe('value/convert/Boolean', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) it('Should convert string #2', () => { const value = 'true' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert string #3', () => { const value = 'TRUE' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert string #4', () => { const value = 'false' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert string #5', () => { const value = '0' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert string #6', () => { const value = '1' const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert string #7', () => { const value = '0' const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert string #8', () => { const value = '1' const result = Value.Convert(Type.Boolean({ default: false }), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert string #8', () => { const value = '2' const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.isEqual(result, '2') + Assert.IsEqual(result, '2') }) it('Should convert number #1', () => { const value = 0 const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert number #2', () => { const value = 1n const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert number #3', () => { const value = 1 const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert number #4', () => { const value = 2 const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, 2) + Assert.IsEqual(result, 2) }) it('Should convert number #5', () => { const value = 0 const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert number #6', () => { const value = 1 const result = Value.Convert(Type.Boolean({ default: false }), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert number #7', () => { const value = 2 const result = Value.Convert(Type.Boolean({ default: true }), value) - Assert.isEqual(result, 2) + Assert.IsEqual(result, 2) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, true) + Assert.IsEqual(result, true) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, false) + Assert.IsEqual(result, false) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, {}) + Assert.IsEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.Boolean(), value) - Assert.isEqual(result, []) + Assert.IsEqual(result, []) }) }) diff --git a/test/runtime/value/convert/composite.ts b/test/runtime/value/convert/composite.ts index 5c7678984..7114db8bd 100644 --- a/test/runtime/value/convert/composite.ts +++ b/test/runtime/value/convert/composite.ts @@ -11,6 +11,6 @@ describe('value/convert/Composite', () => { Type.Object({ z: Type.Boolean() }) ]) const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) - Assert.isEqual(R, { x: 42, y: true, z: 'hello' }) + Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/constructor.ts b/test/runtime/value/convert/constructor.ts new file mode 100644 index 000000000..f1769c245 --- /dev/null +++ b/test/runtime/value/convert/constructor.ts @@ -0,0 +1,20 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// -------------------------------------------------------- +// non-convertable pass through +// -------------------------------------------------------- +describe('value/convert/Constructor', () => { + const T = Type.Constructor([], Type.Any()) + it('Should passthrough 1', () => { + const V = function () {} + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) + it('Should passthrough 2', () => { + const V = 1 + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/custom.ts b/test/runtime/value/convert/custom.ts index 5bc20d59c..e284fbe82 100644 --- a/test/runtime/value/convert/custom.ts +++ b/test/runtime/value/convert/custom.ts @@ -7,18 +7,18 @@ describe('value/convert/Custom', () => { const Custom = TypeSystem.Type('type/convert/Custom/1', () => true) const T = Custom() const R = Value.Convert(T, true) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should not convert 2', () => { const Custom = TypeSystem.Type('type/convert/Custom/2', () => true) const T = Custom() const R = Value.Convert(T, 42) - Assert.isEqual(R, 42) + Assert.IsEqual(R, 42) }) it('Should not convert 3', () => { const Custom = TypeSystem.Type('type/convert/Custom/3', () => true) const T = Custom() const R = Value.Convert(T, 'hello') - Assert.isEqual(R, 'hello') + Assert.IsEqual(R, 'hello') }) }) diff --git a/test/runtime/value/convert/date.ts b/test/runtime/value/convert/date.ts index 171b5fb4c..abe6066a8 100644 --- a/test/runtime/value/convert/date.ts +++ b/test/runtime/value/convert/date.ts @@ -5,38 +5,38 @@ import { Assert } from '../../assert/index' describe('value/convert/Date', () => { it('Should convert from number', () => { const result = Value.Convert(Type.Date(), 123) as Date - Assert.isEqual(result.getTime(), 123) + Assert.IsEqual(result.getTime(), 123) }) it('Should convert from numeric string', () => { const result = Value.Convert(Type.Date(), '123') as Date - Assert.isEqual(result.getTime(), 123) + Assert.IsEqual(result.getTime(), 123) }) it('Should convert from boolean true (interpretted as numeric 1)', () => { const result = Value.Convert(Type.Date(), true) as Date - Assert.isEqual(result.getTime(), 1) + Assert.IsEqual(result.getTime(), 1) }) it('Should convert from datetime string', () => { const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03.000Z') as Date - Assert.isEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + Assert.IsEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') }) it('Should convert from datetime string without timezone', () => { const result = Value.Convert(Type.Date(), '1980-02-03T01:02:03') as Date - Assert.isEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') + Assert.IsEqual(result.toISOString(), '1980-02-03T01:02:03.000Z') }) it('Should convert from time with timezone', () => { const result = Value.Convert(Type.Date(), '01:02:03.000Z') as Date - Assert.isEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + Assert.IsEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') }) it('Should convert from time without timezone', () => { const result = Value.Convert(Type.Date(), '01:02:03') as Date - Assert.isEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') + Assert.IsEqual(result.toISOString(), '1970-01-01T01:02:03.000Z') }) it('Should convert from date string', () => { const result = Value.Convert(Type.Date(), '1980-02-03') as Date - Assert.isEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') + Assert.IsEqual(result.toISOString(), '1980-02-03T00:00:00.000Z') }) it('Should convert invalid strings to unix epoch 0', () => { const result = Value.Convert(Type.Date(), 'invalid-date') as Date - Assert.isEqual(result, 'invalid-date') + Assert.IsEqual(result, 'invalid-date') }) }) diff --git a/test/runtime/value/convert/function.ts b/test/runtime/value/convert/function.ts new file mode 100644 index 000000000..004aaa6be --- /dev/null +++ b/test/runtime/value/convert/function.ts @@ -0,0 +1,20 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// -------------------------------------------------------- +// non-convertable pass through +// -------------------------------------------------------- +describe('value/convert/Function', () => { + const T = Type.Function([], Type.Any()) + it('Should passthrough 1', () => { + const V = function () {} + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) + it('Should passthrough 2', () => { + const V = 1 + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/index.ts b/test/runtime/value/convert/index.ts index a14997a97..8514f77cc 100644 --- a/test/runtime/value/convert/index.ts +++ b/test/runtime/value/convert/index.ts @@ -1,21 +1,26 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './composite' +import './constructor' import './custom' import './date' import './enum' +import './function' import './integer' +import './iterator' import './keyof' import './literal' import './never' import './null' import './number' import './object' +import './promise' import './recursive' import './record' -import './regex' +import './regexp' import './string' import './symbol' import './tuple' diff --git a/test/runtime/value/convert/integer.ts b/test/runtime/value/convert/integer.ts index 727bddf0c..1cecc93eb 100644 --- a/test/runtime/value/convert/integer.ts +++ b/test/runtime/value/convert/integer.ts @@ -6,27 +6,27 @@ describe('value/convert/Integer', () => { it('Should convert from string 1', () => { const T = Type.Integer() const R = Value.Convert(T, '3.14') - Assert.isEqual(R, 3) + Assert.IsEqual(R, 3) }) it('Should convert from string 2', () => { const T = Type.Integer() const R = Value.Convert(T, '42') - Assert.isEqual(R, 42) + Assert.IsEqual(R, 42) }) it('Should convert from boolean 1', () => { const T = Type.Integer() const R = Value.Convert(T, true) - Assert.isEqual(R, 1) + Assert.IsEqual(R, 1) }) it('Should convert from boolean 2', () => { const T = Type.Integer() const R = Value.Convert(T, false) - Assert.isEqual(R, 0) + Assert.IsEqual(R, 0) }) it('Should convert from number 1', () => { const T = Type.Integer() const R = Value.Convert(T, 3.14) - Assert.isEqual(R, 3) + Assert.IsEqual(R, 3) }) // ---------------------------------------------------------- // Casts @@ -34,46 +34,46 @@ describe('value/convert/Integer', () => { it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) it('Should convert string #2', () => { const value = '3.14' const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 3) + Assert.IsEqual(result, 3) }) it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 0) + Assert.IsEqual(result, 0) }) it('Should convert string #4', () => { const value = '-100' const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, -100) + Assert.IsEqual(result, -100) }) it('Should convert number', () => { const value = 42 const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 42) + Assert.IsEqual(result, 42) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 1) + Assert.IsEqual(result, 1) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, 0) + Assert.IsEqual(result, 0) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, {}) + Assert.IsEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.Integer(), value) - Assert.isEqual(result, []) + Assert.IsEqual(result, []) }) }) diff --git a/test/runtime/value/convert/iterator.ts b/test/runtime/value/convert/iterator.ts new file mode 100644 index 000000000..f9045d664 --- /dev/null +++ b/test/runtime/value/convert/iterator.ts @@ -0,0 +1,20 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// -------------------------------------------------------- +// non-convertable pass through +// -------------------------------------------------------- +describe('value/convert/Iterator', () => { + const T = Type.Iterator(Type.Any()) + it('Should passthrough 1', () => { + const V = (function* () {})() + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) + it('Should passthrough 2', () => { + const V = 1 + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/literal.ts b/test/runtime/value/convert/literal.ts index c46ba3dc6..f161b1720 100644 --- a/test/runtime/value/convert/literal.ts +++ b/test/runtime/value/convert/literal.ts @@ -6,80 +6,80 @@ describe('value/convert/Literal:String', () => { it('Should convert from number 1', () => { const T = Type.Literal('1') const R = Value.Convert(T, 1) - Assert.isEqual(R, '1') + Assert.IsEqual(R, '1') }) it('Should convert from number 2', () => { const T = Type.Literal('1') const R = Value.Convert(T, 2) - Assert.isEqual(R, 2) + Assert.IsEqual(R, 2) }) it('Should convert from boolean', () => { const T = Type.Literal('true') const R = Value.Convert(T, true) - Assert.isEqual(R, 'true') + Assert.IsEqual(R, 'true') }) }) describe('value/convert/Literal:Number', () => { it('Should convert from number 1', () => { const T = Type.Literal(3.14) const R = Value.Convert(T, '3.14') - Assert.isEqual(R, 3.14) + Assert.IsEqual(R, 3.14) }) it('Should convert from number 2', () => { const T = Type.Literal(3.14) const R = Value.Convert(T, '3.15') - Assert.isEqual(R, '3.15') + Assert.IsEqual(R, '3.15') }) it('Should convert from boolean 1', () => { const T = Type.Literal(1) const R = Value.Convert(T, true) - Assert.isEqual(R, 1) + Assert.IsEqual(R, 1) }) it('Should convert from boolean 2', () => { const T = Type.Literal(0) const R = Value.Convert(T, false) - Assert.isEqual(R, 0) + Assert.IsEqual(R, 0) }) it('Should convert from boolean 3', () => { const T = Type.Literal(2) const R = Value.Convert(T, true) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) }) describe('value/convert/Literal:Boolean', () => { it('Should convert from number 1', () => { const T = Type.Literal(true) const R = Value.Convert(T, 3.14) - Assert.isEqual(R, 3.14) + Assert.IsEqual(R, 3.14) }) it('Should convert from number 2', () => { const T = Type.Literal(true) const R = Value.Convert(T, 1) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from string 1', () => { const T = Type.Literal(true) const R = Value.Convert(T, 'true') - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from string 2', () => { const T = Type.Literal(false) const R = Value.Convert(T, 'false') - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) it('Should convert from string 3', () => { const T = Type.Literal(true) const R = Value.Convert(T, '1') - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should convert from string 4', () => { const T = Type.Literal(false) const R = Value.Convert(T, '0') - Assert.isEqual(R, false) + Assert.IsEqual(R, false) }) it('Should convert from string 5', () => { const T = Type.Literal(false) const R = Value.Convert(T, '2') - Assert.isEqual(R, '2') + Assert.IsEqual(R, '2') }) }) diff --git a/test/runtime/value/convert/never.ts b/test/runtime/value/convert/never.ts index 5f6d0c4a6..f0cea44bd 100644 --- a/test/runtime/value/convert/never.ts +++ b/test/runtime/value/convert/never.ts @@ -6,16 +6,16 @@ describe('value/convert/Never', () => { it('Should not convert 1', () => { const T = Type.Never() const R = Value.Convert(T, true) - Assert.isEqual(R, true) + Assert.IsEqual(R, true) }) it('Should not convert 2', () => { const T = Type.Never() const R = Value.Convert(T, 42) - Assert.isEqual(R, 42) + Assert.IsEqual(R, 42) }) it('Should not convert 3', () => { const T = Type.Never() const R = Value.Convert(T, 'true') - Assert.isEqual(R, 'true') + Assert.IsEqual(R, 'true') }) }) diff --git a/test/runtime/value/convert/null.ts b/test/runtime/value/convert/null.ts index 12d5ab1ee..f490e8bec 100644 --- a/test/runtime/value/convert/null.ts +++ b/test/runtime/value/convert/null.ts @@ -6,14 +6,14 @@ describe('value/convert/Null', () => { const T = Type.Null() it('Should convert from string 1', () => { const R = Value.Convert(T, 'null') - Assert.isEqual(R, null) + Assert.IsEqual(R, null) }) it('Should convert from string 2', () => { const R = Value.Convert(T, 'NULL') - Assert.isEqual(R, null) + Assert.IsEqual(R, null) }) it('Should convert from string 3', () => { const R = Value.Convert(T, 'nil') - Assert.isEqual(R, 'nil') + Assert.IsEqual(R, 'nil') }) }) diff --git a/test/runtime/value/convert/number.ts b/test/runtime/value/convert/number.ts index 05636653c..1f1f62c4d 100644 --- a/test/runtime/value/convert/number.ts +++ b/test/runtime/value/convert/number.ts @@ -6,71 +6,70 @@ describe('value/convert/Number', () => { const T = Type.Number() it('Should convert from string 1', () => { const R = Value.Convert(T, '3.14') - Assert.isEqual(R, 3.14) + Assert.IsEqual(R, 3.14) }) it('Should convert from string 2', () => { const R = Value.Convert(T, '42') - Assert.isEqual(R, 42) + Assert.IsEqual(R, 42) }) it('Should convert from boolean 1', () => { const R = Value.Convert(T, true) - Assert.isEqual(R, 1) + Assert.IsEqual(R, 1) }) it('Should convert from boolean 2', () => { const R = Value.Convert(T, false) - Assert.isEqual(R, 0) + Assert.IsEqual(R, 0) }) it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.isEqual(R, 3.14) + Assert.IsEqual(R, 3.14) }) - // ---------------------------------------------------------- // Casts // ---------------------------------------------------------- it('Should convert string #1', () => { const value = 'hello' const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) it('Should convert string #2', () => { const value = '3.14' const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 3.14) + Assert.IsEqual(result, 3.14) }) it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 0) + Assert.IsEqual(result, 0) }) it('Should convert string #4', () => { const value = '-100' const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, -100) + Assert.IsEqual(result, -100) }) it('Should convert number', () => { const value = 42 const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 42) + Assert.IsEqual(result, 42) }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 1) + Assert.IsEqual(result, 1) }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.Number(), value) - Assert.isEqual(result, 0) + Assert.IsEqual(result, 0) }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, {}) + Assert.IsEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, []) + Assert.IsEqual(result, []) }) }) diff --git a/test/runtime/value/convert/object.ts b/test/runtime/value/convert/object.ts index 27dd6e824..fa5e4ed70 100644 --- a/test/runtime/value/convert/object.ts +++ b/test/runtime/value/convert/object.ts @@ -11,6 +11,6 @@ describe('value/convert/Object', () => { z: Type.Boolean() }) const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) - Assert.isEqual(R, { x: 42, y: true, z: 'hello' }) + Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/promise.ts b/test/runtime/value/convert/promise.ts new file mode 100644 index 000000000..254301489 --- /dev/null +++ b/test/runtime/value/convert/promise.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// -------------------------------------------------------- +// non-convertable pass through +// -------------------------------------------------------- +describe('value/convert/Promise', () => { + const T = Type.Promise(Type.Any()) + it('Should pass-through 1', () => { + const V = Promise.resolve(1) + const R = Value.Convert(T, V) + console.log(R) + Assert.IsEqual(R, V) + }) + it('Should passthrough 2', () => { + const V = 1 + const R = Value.Convert(T, V) + Assert.IsEqual(R, V) + }) +}) diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts index e773e8f25..961684000 100644 --- a/test/runtime/value/convert/record.ts +++ b/test/runtime/value/convert/record.ts @@ -6,6 +6,6 @@ describe('value/convert/Record', () => { it('Should convert record value to numeric', () => { const T = Type.Record(Type.String(), Type.Number()) const V = Value.Convert(T, { x: '42', y: '24', z: 'hello' }) - Assert.isEqual(V, { x: 42, y: '24', z: 'hello' }) + Assert.IsEqual(V, { x: 42, y: '24', z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/regex.ts b/test/runtime/value/convert/regexp.ts similarity index 100% rename from test/runtime/value/convert/regex.ts rename to test/runtime/value/convert/regexp.ts diff --git a/test/runtime/value/convert/string.ts b/test/runtime/value/convert/string.ts index 3580bbda7..5ff0a8726 100644 --- a/test/runtime/value/convert/string.ts +++ b/test/runtime/value/convert/string.ts @@ -6,27 +6,27 @@ describe('value/convert/String', () => { const T = Type.String() it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.isEqual(R, '3.14') + Assert.IsEqual(R, '3.14') }) it('Should convert from number 2', () => { const R = Value.Convert(T, 3) - Assert.isEqual(R, '3') + Assert.IsEqual(R, '3') }) it('Should convert from boolean 1', () => { const R = Value.Convert(T, true) - Assert.isEqual(R, 'true') + Assert.IsEqual(R, 'true') }) it('Should convert from boolean 2', () => { const R = Value.Convert(T, false) - Assert.isEqual(R, 'false') + Assert.IsEqual(R, 'false') }) it('Should convert from bigint', () => { const R = Value.Convert(T, BigInt(12345)) - Assert.isEqual(R, '12345') + Assert.IsEqual(R, '12345') }) it('Should convert from symbol', () => { const R = Value.Convert(T, Symbol(12345)) - Assert.isEqual(R, '12345') + Assert.IsEqual(R, '12345') }) // ---------------------------------------------------------- // Casts @@ -34,36 +34,36 @@ describe('value/convert/String', () => { it('Should convert string', () => { const value = 'hello' const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, 'hello') + Assert.IsEqual(result, 'hello') }) it('Should convert number #1', () => { const value = 42 const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, '42') + Assert.IsEqual(result, '42') }) it('Should convert number #2', () => { const value = 42n const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, '42') + Assert.IsEqual(result, '42') }) it('Should convert true', () => { const value = true const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, 'true') + Assert.IsEqual(result, 'true') }) it('Should convert false', () => { const value = false const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, 'false') + Assert.IsEqual(result, 'false') }) it('Should convert object', () => { const value = {} const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, {}) + Assert.IsEqual(result, {}) }) it('Should convert array', () => { const value = [] as any[] const result = Value.Convert(Type.String(), value) - Assert.isEqual(result, []) + Assert.IsEqual(result, []) }) }) diff --git a/test/runtime/value/convert/symbol.ts b/test/runtime/value/convert/symbol.ts index 096b9c3cd..ce453f6a9 100644 --- a/test/runtime/value/convert/symbol.ts +++ b/test/runtime/value/convert/symbol.ts @@ -6,10 +6,10 @@ describe('value/convert/Symbol', () => { const T = Type.Symbol() it('Should convert from number 1', () => { const R = Value.Convert(T, 3.14) - Assert.isEqual(R, '3.14') + Assert.IsEqual(R, '3.14') }) it('Should convert from number 2', () => { const R = Value.Convert(T, 3) - Assert.isEqual(R, '3') + Assert.IsEqual(R, '3') }) }) diff --git a/test/runtime/value/convert/tuple.ts b/test/runtime/value/convert/tuple.ts index 6047f3fc1..366ef8838 100644 --- a/test/runtime/value/convert/tuple.ts +++ b/test/runtime/value/convert/tuple.ts @@ -6,16 +6,16 @@ describe('value/convert/Tuple', () => { it('Should convert from Array 1', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1', 'true']) - Assert.isEqual(R, [1, true]) + Assert.IsEqual(R, [1, true]) }) it('Should convert from Array 2', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1']) - Assert.isEqual(R, [1]) + Assert.IsEqual(R, [1]) }) it('Should convert from Array 3', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1', '2', '3']) - Assert.isEqual(R, [1, 2, '3']) + Assert.IsEqual(R, [1, 2, '3']) }) }) diff --git a/test/runtime/value/convert/undefined.ts b/test/runtime/value/convert/undefined.ts index 90e9201a1..3feab7e40 100644 --- a/test/runtime/value/convert/undefined.ts +++ b/test/runtime/value/convert/undefined.ts @@ -6,10 +6,10 @@ describe('value/convert/Undefined', () => { const T = Type.Undefined() it('Should convert from string 1', () => { const R = Value.Convert(T, 'undefined') - Assert.isEqual(R, undefined) + Assert.IsEqual(R, undefined) }) it('Should convert from string 2', () => { const R = Value.Convert(T, 'hello') - Assert.isEqual(R, 'hello') + Assert.IsEqual(R, 'hello') }) }) diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index 96c79b4dd..12f731b08 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -10,15 +10,15 @@ describe('value/convert/Union', () => { const V1 = Value.Convert(T, { x: '42' }) const V2 = Value.Convert(T, { x: 'null' }) const V3 = Value.Convert(T, { x: 'hello' }) - Assert.isEqual(V1, { x: 42 }) - Assert.isEqual(V2, { x: null }) - Assert.isEqual(V3, { x: 'hello' }) + Assert.IsEqual(V1, { x: 42 }) + Assert.IsEqual(V2, { x: null }) + Assert.IsEqual(V3, { x: 'hello' }) }) it('Should convert first variant in ambiguous conversion', () => { const T = Type.Object({ x: Type.Union([Type.Boolean(), Type.Number()]), }) const V1 = Value.Convert(T, { x: '1' }) - Assert.isEqual(V1, { x: true }) + Assert.IsEqual(V1, { x: true }) }) }) diff --git a/test/runtime/value/convert/unknown.ts b/test/runtime/value/convert/unknown.ts index 7f43ce7f5..d3ca48479 100644 --- a/test/runtime/value/convert/unknown.ts +++ b/test/runtime/value/convert/unknown.ts @@ -7,36 +7,36 @@ describe('value/convert/Unknown', () => { it('Should convert null', () => { const V = null const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert undefined', () => { const V = undefined const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert string', () => { const V = 'hello' const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert number', () => { const V = 42 const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert boolean', () => { const V = true const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert object', () => { const V = { x: 1 } const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) it('Should convert array', () => { const V = [1, 2, 3] const R = Value.Convert(T, V) - Assert.isEqual(R, V) + Assert.IsEqual(R, V) }) }) diff --git a/test/runtime/value/create/any.ts b/test/runtime/value/create/any.ts index 3dc08a4af..44809c7d1 100644 --- a/test/runtime/value/create/any.ts +++ b/test/runtime/value/create/any.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Any', () => { it('Should create value', () => { const T = Type.Any() - Assert.isEqual(Value.Create(T), {}) + Assert.IsEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Any({ default: 1 }) - Assert.isEqual(Value.Create(T), 1) + Assert.IsEqual(Value.Create(T), 1) }) }) diff --git a/test/runtime/value/create/array.ts b/test/runtime/value/create/array.ts index e61a1a4f3..34377a245 100644 --- a/test/runtime/value/create/array.ts +++ b/test/runtime/value/create/array.ts @@ -5,14 +5,14 @@ import { Assert } from '../../assert/index' describe('value/create/Any', () => { it('Should create value', () => { const T = Type.Array(Type.String()) - Assert.isEqual(Value.Create(T), []) + Assert.IsEqual(Value.Create(T), []) }) it('Should create default', () => { const T = Type.Array(Type.String(), { default: ['1'] }) - Assert.isEqual(Value.Create(T), ['1']) + Assert.IsEqual(Value.Create(T), ['1']) }) it('Should create with minItems', () => { const T = Type.Array(Type.String(), { minItems: 4 }) - Assert.isEqual(Value.Create(T), ['', '', '', '']) + Assert.IsEqual(Value.Create(T), ['', '', '', '']) }) }) diff --git a/test/runtime/value/create/async-iterator.ts b/test/runtime/value/create/async-iterator.ts new file mode 100644 index 000000000..da2516908 --- /dev/null +++ b/test/runtime/value/create/async-iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/AsyncIterator', () => { + it('Should create value', () => { + const T = Type.AsyncIterator(Type.Any()) + const R = Value.Create(T) + Assert.IsTrue(Symbol.asyncIterator in R) + }) + it('Should create default', () => { + const T = Type.AsyncIterator(Type.Any(), { default: 1 }) + const R = Value.Create(T) + Assert.IsEqual(R, 1) + }) +}) diff --git a/test/runtime/value/create/bigint.ts b/test/runtime/value/create/bigint.ts index 2409d73ec..9f53207c5 100644 --- a/test/runtime/value/create/bigint.ts +++ b/test/runtime/value/create/bigint.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/BigInt', () => { it('Should create value', () => { const T = Type.BigInt() - Assert.isEqual(Value.Create(T), BigInt(0)) + Assert.IsEqual(Value.Create(T), BigInt(0)) }) it('Should create default', () => { const T = Type.BigInt({ default: true }) - Assert.isEqual(Value.Create(T), true) + Assert.IsEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/boolean.ts b/test/runtime/value/create/boolean.ts index 853dd9ac4..28c1c4ea2 100644 --- a/test/runtime/value/create/boolean.ts +++ b/test/runtime/value/create/boolean.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Boolean', () => { it('Should create value', () => { const T = Type.Boolean() - Assert.isEqual(Value.Create(T), false) + Assert.IsEqual(Value.Create(T), false) }) it('Should create default', () => { const T = Type.Boolean({ default: true }) - Assert.isEqual(Value.Create(T), true) + Assert.IsEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/composite.ts b/test/runtime/value/create/composite.ts index 40421aa6e..a19f506bc 100644 --- a/test/runtime/value/create/composite.ts +++ b/test/runtime/value/create/composite.ts @@ -15,7 +15,7 @@ describe('value/create/Composite', () => { c: Type.Number(), }) const T = Type.Composite([A, B]) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 0, y: 0, z: 0, @@ -36,7 +36,7 @@ describe('value/create/Composite', () => { c: Type.Number({ default: 6 }), }) const T = Type.Composite([A, B]) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 1, y: 2, z: 3, @@ -57,7 +57,7 @@ describe('value/create/Composite', () => { c: Type.Optional(Type.Number()), }) const T = Type.Composite([A, B]) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 0, y: 0, z: 0, diff --git a/test/runtime/value/create/constructor.ts b/test/runtime/value/create/constructor.ts index 7bf4d7777..39c54bdd1 100644 --- a/test/runtime/value/create/constructor.ts +++ b/test/runtime/value/create/constructor.ts @@ -13,9 +13,8 @@ describe('value/create/Constructor', () => { const C = Value.Create(T) const I = new C() const R = I.test() - Assert.isEqual(R, 123) + Assert.IsEqual(R, 123) }) - it('Should create default', () => { const T = Type.Constructor( [], @@ -33,6 +32,6 @@ describe('value/create/Constructor', () => { const C = Value.Create(T) const I = new C() const R = I.test() - Assert.isEqual(R, 321) + Assert.IsEqual(R, 321) }) }) diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts index 3c03cb110..8756a9c0a 100644 --- a/test/runtime/value/create/custom.ts +++ b/test/runtime/value/create/custom.ts @@ -6,12 +6,12 @@ describe('value/create/Custom', () => { it('Should create custom value with default', () => { TypeRegistry.Set('CustomCreate1', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) it('Should throw when no default value is specified', () => { TypeRegistry.Set('CustomCreate2', () => true) const T = Type.Unsafe({ [Kind]: 'CustomCreate2' }) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) }) diff --git a/test/runtime/value/create/enum.ts b/test/runtime/value/create/enum.ts index 6a2cf1aa6..81acdc6f0 100644 --- a/test/runtime/value/create/enum.ts +++ b/test/runtime/value/create/enum.ts @@ -9,7 +9,7 @@ describe('value/create/Boolean', () => { B, } const T = Type.Enum(Foo) - Assert.isEqual(Value.Create(T), Foo.A) + Assert.IsEqual(Value.Create(T), Foo.A) }) it('Should create default', () => { enum Foo { @@ -17,6 +17,6 @@ describe('value/create/Boolean', () => { B, } const T = Type.Enum(Foo, { default: Foo.B }) - Assert.isEqual(Value.Create(T), Foo.B) + Assert.IsEqual(Value.Create(T), Foo.B) }) }) diff --git a/test/runtime/value/create/function.ts b/test/runtime/value/create/function.ts index e69514260..0d1c2f1fb 100644 --- a/test/runtime/value/create/function.ts +++ b/test/runtime/value/create/function.ts @@ -7,13 +7,12 @@ describe('value/create/Function', () => { const T = Type.Function([], Type.Number({ default: 123 })) const F = Value.Create(T) const R = F() - Assert.isEqual(R, 123) + Assert.IsEqual(R, 123) }) - it('Should create default', () => { const T = Type.Function([], Type.Number({ default: 123 }), { default: () => 321 }) const F = Value.Create(T) const R = F() - Assert.isEqual(R, 321) + Assert.IsEqual(R, 321) }) }) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 26caee56d..385f53f1e 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -1,5 +1,6 @@ import './any' import './array' +import './async-iterator' import './bigint' import './boolean' import './composite' @@ -9,6 +10,7 @@ import './enum' import './function' import './integer' import './intersect' +import './iterator' import './keyof' import './literal' import './never' @@ -19,7 +21,7 @@ import './object' import './recursive' import './ref' import './record' -import './regex' +import './regexp' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/value/create/integer.ts b/test/runtime/value/create/integer.ts index 8354172a0..f6dc35b39 100644 --- a/test/runtime/value/create/integer.ts +++ b/test/runtime/value/create/integer.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Integer', () => { it('Should create value', () => { const T = Type.Integer() - Assert.isEqual(Value.Create(T), 0) + Assert.IsEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.Integer({ default: 7 }) - Assert.isEqual(Value.Create(T), 7) + Assert.IsEqual(Value.Create(T), 7) }) }) diff --git a/test/runtime/value/create/intersect.ts b/test/runtime/value/create/intersect.ts index 195f2364d..771e07e2b 100644 --- a/test/runtime/value/create/intersect.ts +++ b/test/runtime/value/create/intersect.ts @@ -6,36 +6,35 @@ describe('value/create/Intersect', () => { it('Should create value', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) const R = Value.Create(T) - - Assert.isEqual(R, { x: 0, y: 0 }) + Assert.IsEqual(R, { x: 0, y: 0 }) }) it('Should create value with default', () => { const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 100 }) }), Type.Object({ y: Type.Number({ default: 200 }) })]) const R = Value.Create(T) - Assert.isEqual(R, { x: 100, y: 200 }) + Assert.IsEqual(R, { x: 100, y: 200 }) }) it('Should create for overlapping intersection', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number(), y: Type.Number() })]) const R = Value.Create(T) - Assert.isEqual(R, { x: 0, y: 0 }) + Assert.IsEqual(R, { x: 0, y: 0 }) }) it('Should create with last intersected overlapping default', () => { const T = Type.Intersect([Type.Object({ x: Type.Number({ default: 1 }) }), Type.Object({ x: Type.Number({ default: 2 }), y: Type.Number() })]) const R = Value.Create(T) - Assert.isEqual(R, { x: 2, y: 0 }) + Assert.IsEqual(R, { x: 2, y: 0 }) }) it('Should throw for non-constructable intersection 1', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) it('Should throw for non-constructable intersection 2', () => { const T = Type.Intersect([Type.String(), Type.Number()]) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) it('Should not throw for non-constructable intersection with default', () => { const T = Type.Intersect([Type.String(), Type.Number()], { default: 'hello' }) const R = Value.Create(T) - Assert.isEqual(R, 'hello') + Assert.IsEqual(R, 'hello') }) it('Should create from nested intersection', () => { const T = Type.Intersect([ @@ -52,11 +51,11 @@ describe('value/create/Intersect', () => { ]), ]) const R = Value.Create(T) - Assert.isEqual(R, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) }) it('Should create non varying primitive', () => { const T = Type.Intersect([Type.Number(), Type.Number(), Type.Number()]) const R = Value.Create(T) - Assert.isEqual(R, 0) + Assert.IsEqual(R, 0) }) }) diff --git a/test/runtime/value/create/iterator.ts b/test/runtime/value/create/iterator.ts new file mode 100644 index 000000000..02ea1c991 --- /dev/null +++ b/test/runtime/value/create/iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Iterator', () => { + it('Should create value', () => { + const T = Type.Iterator(Type.Any()) + const R = Value.Create(T) + Assert.IsTrue(Symbol.iterator in R) + }) + it('Should create default', () => { + const T = Type.Iterator(Type.Any(), { default: 1 }) + const R = Value.Create(T) + Assert.IsEqual(R, 1) + }) +}) diff --git a/test/runtime/value/create/keyof.ts b/test/runtime/value/create/keyof.ts index 4f8aca015..0f4c33e1a 100644 --- a/test/runtime/value/create/keyof.ts +++ b/test/runtime/value/create/keyof.ts @@ -10,7 +10,7 @@ describe('value/create/KeyOf', () => { y: Type.Number(), }), ) - Assert.isEqual(Value.Create(T), 'x') + Assert.IsEqual(Value.Create(T), 'x') }) it('Should create default', () => { const T = Type.KeyOf( @@ -20,6 +20,6 @@ describe('value/create/KeyOf', () => { }), { default: 'y' }, ) - Assert.isEqual(Value.Create(T), 'y') + Assert.IsEqual(Value.Create(T), 'y') }) }) diff --git a/test/runtime/value/create/literal.ts b/test/runtime/value/create/literal.ts index 8bdb7db2c..3004dcd97 100644 --- a/test/runtime/value/create/literal.ts +++ b/test/runtime/value/create/literal.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('value/create/Literal', () => { it('Should create literal string', () => { const T = Type.Literal('hello') - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) it('Should create literal number', () => { const T = Type.Literal(1) - Assert.isEqual(Value.Create(T), 1) + Assert.IsEqual(Value.Create(T), 1) }) it('Should create literal boolean', () => { const T = Type.Literal(true) - Assert.isEqual(Value.Create(T), true) + Assert.IsEqual(Value.Create(T), true) }) it('Should create literal from default value', () => { const T = Type.Literal(true, { default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/never.ts b/test/runtime/value/create/never.ts index 437ff3ef3..55da1ea13 100644 --- a/test/runtime/value/create/never.ts +++ b/test/runtime/value/create/never.ts @@ -5,6 +5,6 @@ import { Assert } from '../../assert/index' describe('value/create/Never', () => { it('Should create value', () => { const T = Type.Never() - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) }) diff --git a/test/runtime/value/create/not.ts b/test/runtime/value/create/not.ts index 9890372c6..70f1ec2cb 100644 --- a/test/runtime/value/create/not.ts +++ b/test/runtime/value/create/not.ts @@ -5,16 +5,16 @@ import { Assert } from '../../assert/index' describe('value/create/Not', () => { it('Should throw without default value', () => { const T = Type.Not(Type.String()) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) it('Should create value with default inner', () => { const T = Type.Not(Type.String(), { default: 100 }) const R = Value.Create(T) - Assert.isEqual(R, 100) + Assert.IsEqual(R, 100) }) it('Should create value with default outer', () => { const T = Type.Not(Type.String(), { default: 100 }) const R = Value.Create(T) - Assert.isEqual(R, 100) + Assert.IsEqual(R, 100) }) }) diff --git a/test/runtime/value/create/null.ts b/test/runtime/value/create/null.ts index 3fc129a50..1c13270a7 100644 --- a/test/runtime/value/create/null.ts +++ b/test/runtime/value/create/null.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Null', () => { it('Should create value', () => { const T = Type.Null() - Assert.isEqual(Value.Create(T), null) + Assert.IsEqual(Value.Create(T), null) }) it('Should create null from default value', () => { const T = Type.Null({ default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/number.ts b/test/runtime/value/create/number.ts index db68e95a5..756c3aa59 100644 --- a/test/runtime/value/create/number.ts +++ b/test/runtime/value/create/number.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Number', () => { it('Should create value', () => { const T = Type.Number() - Assert.isEqual(Value.Create(T), 0) + Assert.IsEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.Number({ default: 7 }) - Assert.isEqual(Value.Create(T), 7) + Assert.IsEqual(Value.Create(T), 7) }) }) diff --git a/test/runtime/value/create/object.ts b/test/runtime/value/create/object.ts index 8f0ff10b4..0d2ed1cad 100644 --- a/test/runtime/value/create/object.ts +++ b/test/runtime/value/create/object.ts @@ -9,35 +9,32 @@ describe('value/create/Object', () => { y: Type.Number(), z: Type.Number(), }) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 0, y: 0, z: 0, }) }) - it('Should create value with optional properties', () => { const T = Type.Object({ x: Type.Optional(Type.Number()), y: Type.Optional(Type.Number()), z: Type.Optional(Type.Number()), }) - Assert.isEqual(Value.Create(T), {}) + Assert.IsEqual(Value.Create(T), {}) }) - it('Should create default with default properties', () => { const T = Type.Object({ x: Type.Number({ default: 1 }), y: Type.Number({ default: 2 }), z: Type.Number({ default: 3 }), }) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 1, y: 2, z: 3, }) }) - it('Should create nested object', () => { const T = Type.Object({ x: Type.Number(), @@ -49,14 +46,13 @@ describe('value/create/Object', () => { z: Type.Number(), }), }) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 0, y: 0, z: 0, w: { x: 7, y: 0, z: 0 }, }) }) - it('Should create with default', () => { const T = Type.Object( { @@ -66,7 +62,7 @@ describe('value/create/Object', () => { }, { default: { x: 1, y: 2, z: 3 } }, ) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { x: 1, y: 2, z: 3, diff --git a/test/runtime/value/create/record.ts b/test/runtime/value/create/record.ts index 05ebfd482..1a66fcc04 100644 --- a/test/runtime/value/create/record.ts +++ b/test/runtime/value/create/record.ts @@ -5,7 +5,7 @@ import { Assert } from '../../assert/index' describe('value/create/Record', () => { it('Should create value', () => { const T = Type.Record(Type.String(), Type.Object({})) - Assert.isEqual(Value.Create(T), {}) + Assert.IsEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Record(Type.String(), Type.Object({}), { @@ -13,6 +13,6 @@ describe('value/create/Record', () => { x: {}, }, }) - Assert.isEqual(Value.Create(T), { x: {} }) + Assert.IsEqual(Value.Create(T), { x: {} }) }) }) diff --git a/test/runtime/value/create/recursive.ts b/test/runtime/value/create/recursive.ts index 3f78233f2..2fb57e9df 100644 --- a/test/runtime/value/create/recursive.ts +++ b/test/runtime/value/create/recursive.ts @@ -10,7 +10,7 @@ describe('value/create/Recursive', () => { nodes: Type.Array(This), }), ) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { id: '', nodes: [], }) @@ -24,7 +24,7 @@ describe('value/create/Recursive', () => { }), { default: 7 }, ) - Assert.isEqual(Value.Create(T), 7) + Assert.IsEqual(Value.Create(T), 7) }) it('Should throw on infinite type', () => { const T = Type.Recursive((This) => @@ -32,7 +32,7 @@ describe('value/create/Recursive', () => { x: This, }), ) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) it('Should not throw on recursive type when terminating sub type proceeds self', () => { const T = Type.Recursive((This) => @@ -40,7 +40,7 @@ describe('value/create/Recursive', () => { x: Type.Union([Type.Null(), This]), }), ) - Assert.isEqual(Value.Create(T), { x: null }) + Assert.IsEqual(Value.Create(T), { x: null }) }) it('Should not throw on recursive type when self is optional', () => { const T = Type.Recursive((This) => @@ -48,6 +48,6 @@ describe('value/create/Recursive', () => { x: Type.Optional(This), }), ) - Assert.isEqual(Value.Create(T), {}) + Assert.IsEqual(Value.Create(T), {}) }) }) diff --git a/test/runtime/value/create/ref.ts b/test/runtime/value/create/ref.ts index b296f0bb2..ce3d9d155 100644 --- a/test/runtime/value/create/ref.ts +++ b/test/runtime/value/create/ref.ts @@ -13,7 +13,7 @@ describe('value/create/Ref', () => { { $id: 'T', default: 'target' }, ) const R = Type.Ref(T) - Assert.throws(() => Value.Create(R)) + Assert.Throws(() => Value.Create(R)) }) it('Should create ref default if ref default is defined', () => { const T = Type.Object( @@ -25,11 +25,11 @@ describe('value/create/Ref', () => { { $id: 'T', default: 'target' }, ) const R = Type.Ref(T, { default: 'override' }) - Assert.isEqual(Value.Create(R), 'override') // terminated at R default value + Assert.IsEqual(Value.Create(R), 'override') // terminated at R default value }) it('Should dereference remote schema via $ref', () => { const R = Type.Number({ $id: 'S' }) const T = Type.Object({ x: Type.Ref(R) }) - Assert.isEqual(Value.Create(T, [R]), { x: 0 }) + Assert.IsEqual(Value.Create(T, [R]), { x: 0 }) }) }) diff --git a/test/runtime/value/create/regex.ts b/test/runtime/value/create/regexp.ts similarity index 66% rename from test/runtime/value/create/regex.ts rename to test/runtime/value/create/regexp.ts index 8afa9fb8e..f1415fd6a 100644 --- a/test/runtime/value/create/regex.ts +++ b/test/runtime/value/create/regexp.ts @@ -4,13 +4,13 @@ import { Assert } from '../../assert/index' describe('value/create/RegEx', () => { it('Should throw without a default value', () => { - Assert.throws(() => { - const T = Type.RegEx(/foo/) + Assert.Throws(() => { + const T = Type.RegExp(/foo/) Value.Create(T) }) }) it('Should create default', () => { - const T = Type.RegEx(/foo/, { default: 'foo' }) - Assert.isEqual(Value.Create(T), 'foo') + const T = Type.RegExp(/foo/, { default: 'foo' }) + Assert.IsEqual(Value.Create(T), 'foo') }) }) diff --git a/test/runtime/value/create/string.ts b/test/runtime/value/create/string.ts index 41a0035ef..83ebe90f4 100644 --- a/test/runtime/value/create/string.ts +++ b/test/runtime/value/create/string.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/String', () => { it('Should create value', () => { const T = Type.String() - Assert.isEqual(Value.Create(T), 0) + Assert.IsEqual(Value.Create(T), 0) }) it('Should create default', () => { const T = Type.String({ default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/symbol.ts b/test/runtime/value/create/symbol.ts index 2c78809c2..90af86fa5 100644 --- a/test/runtime/value/create/symbol.ts +++ b/test/runtime/value/create/symbol.ts @@ -6,10 +6,10 @@ describe('value/create/Symbol', () => { it('Should create value', () => { const T = Type.Symbol() const V = Value.Create(T) - Assert.isEqual(typeof V, 'symbol') + Assert.IsEqual(typeof V, 'symbol') }) it('Should create default', () => { const T = Type.Symbol({ default: true }) - Assert.isEqual(Value.Create(T), true) + Assert.IsEqual(Value.Create(T), true) }) }) diff --git a/test/runtime/value/create/template-literal.ts b/test/runtime/value/create/template-literal.ts index 7103f1272..ccd9a84c3 100644 --- a/test/runtime/value/create/template-literal.ts +++ b/test/runtime/value/create/template-literal.ts @@ -6,30 +6,30 @@ describe('value/create/TemplateLiteral', () => { it('Should create pattern 1', () => { const T = Type.TemplateLiteral([Type.Literal('A')]) const V = Value.Create(T) - Assert.isEqual(V, 'A') + Assert.IsEqual(V, 'A') }) it('Should create pattern 2', () => { const T = Type.TemplateLiteral([Type.Literal('A'), Type.Literal('B')]) const V = Value.Create(T) - Assert.isEqual(V, 'AB') + Assert.IsEqual(V, 'AB') }) it('Should create pattern 3 (first only)', () => { const T = Type.TemplateLiteral([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) const V = Value.Create(T) - Assert.isEqual(V, 'AB') + Assert.IsEqual(V, 'AB') }) it('Should create pattern 4 (first only)', () => { const T = Type.TemplateLiteral([Type.Boolean()]) const V = Value.Create(T) - Assert.isEqual(V, 'true') + Assert.IsEqual(V, 'true') }) it('Should throw on infinite pattern', () => { const T = Type.TemplateLiteral([Type.Number()]) - Assert.throws(() => Value.Create(T)) + Assert.Throws(() => Value.Create(T)) }) it('Should create on infinite pattern with default', () => { const T = Type.TemplateLiteral([Type.Number()], { default: 42 }) const V = Value.Create(T) - Assert.isEqual(V, 42) + Assert.IsEqual(V, 42) }) }) diff --git a/test/runtime/value/create/tuple.ts b/test/runtime/value/create/tuple.ts index 2edb53bd6..085296bba 100644 --- a/test/runtime/value/create/tuple.ts +++ b/test/runtime/value/create/tuple.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('value/create/Tuple', () => { it('Should create value', () => { const T = Type.Tuple([Type.Number(), Type.String()]) - Assert.isEqual(Value.Create(T), [0, '']) + Assert.IsEqual(Value.Create(T), [0, '']) }) it('Should create default', () => { const T = Type.Tuple([Type.Number(), Type.String()], { default: [7, 'hello'] }) - Assert.isEqual(Value.Create(T), [7, 'hello']) + Assert.IsEqual(Value.Create(T), [7, 'hello']) }) it('Should create default elements', () => { const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })]) - Assert.isEqual(Value.Create(T), [7, 'hello']) + Assert.IsEqual(Value.Create(T), [7, 'hello']) }) it('Should create default by overriding elements', () => { const T = Type.Tuple([Type.Number({ default: 7 }), Type.String({ default: 'hello' })], { default: [32, 'world'] }) - Assert.isEqual(Value.Create(T), [32, 'world']) + Assert.IsEqual(Value.Create(T), [32, 'world']) }) }) diff --git a/test/runtime/value/create/uint8array.ts b/test/runtime/value/create/uint8array.ts index 1284f29f7..0dac37786 100644 --- a/test/runtime/value/create/uint8array.ts +++ b/test/runtime/value/create/uint8array.ts @@ -6,23 +6,21 @@ describe('value/create/Uint8Array', () => { it('Should create value', () => { const T = Type.Uint8Array() const value = Value.Create(T) - Assert.isInstanceOf(value, Uint8Array) - Assert.isEqual(value.length, 0) + Assert.IsInstanceOf(value, Uint8Array) + Assert.IsEqual(value.length, 0) }) - it('Should create default', () => { const T = Type.Uint8Array({ default: new Uint8Array([0, 1, 2, 3]) }) const value = Value.Create(T) - Assert.isInstanceOf(value, Uint8Array) - Assert.isEqual(value.length, 4) - Assert.isEqual([value[0], value[1], value[2], value[3]], [0, 1, 2, 3]) + Assert.IsInstanceOf(value, Uint8Array) + Assert.IsEqual(value.length, 4) + Assert.IsEqual([value[0], value[1], value[2], value[3]], [0, 1, 2, 3]) }) - it('Should create with minByteLength', () => { const T = Type.Uint8Array({ minByteLength: 4 }) const value = Value.Create(T) - Assert.isInstanceOf(value, Uint8Array) - Assert.isEqual(value.length, 4) - Assert.isEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) + Assert.IsInstanceOf(value, Uint8Array) + Assert.IsEqual(value.length, 4) + Assert.IsEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) }) }) diff --git a/test/runtime/value/create/undefined.ts b/test/runtime/value/create/undefined.ts index d9e3ed509..e2f530ee7 100644 --- a/test/runtime/value/create/undefined.ts +++ b/test/runtime/value/create/undefined.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Undefined', () => { it('Should create value', () => { const T = Type.Undefined() - Assert.isEqual(Value.Create(T), undefined) + Assert.IsEqual(Value.Create(T), undefined) }) it('Should create value from default value', () => { const T = Type.Undefined({ default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/create/union.ts b/test/runtime/value/create/union.ts index 1d3c79677..37da0abaa 100644 --- a/test/runtime/value/create/union.ts +++ b/test/runtime/value/create/union.ts @@ -17,14 +17,13 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.isEqual(Value.Create(T), { + Assert.IsEqual(Value.Create(T), { type: 'A', x: 0, y: 0, z: 0, }) }) - it('Should create union Null', () => { const A = Type.Null() const B = Type.Object({ @@ -34,9 +33,8 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.isEqual(Value.Create(T), null) + Assert.IsEqual(Value.Create(T), null) }) - it('Should create union Array', () => { const A = Type.Array(Type.String()) const B = Type.Object({ @@ -46,6 +44,6 @@ describe('value/create/Union', () => { z: Type.String(), }) const T = Type.Union([A, B]) - Assert.isEqual(Value.Create(T), []) + Assert.IsEqual(Value.Create(T), []) }) }) diff --git a/test/runtime/value/create/unknown.ts b/test/runtime/value/create/unknown.ts index d75a062c8..a67b3ec8f 100644 --- a/test/runtime/value/create/unknown.ts +++ b/test/runtime/value/create/unknown.ts @@ -5,10 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Unknown', () => { it('Should create value', () => { const T = Type.Unknown() - Assert.isEqual(Value.Create(T), {}) + Assert.IsEqual(Value.Create(T), {}) }) it('Should create default', () => { const T = Type.Unknown({ default: 1 }) - Assert.isEqual(Value.Create(T), 1) + Assert.IsEqual(Value.Create(T), 1) }) }) diff --git a/test/runtime/value/create/void.ts b/test/runtime/value/create/void.ts index 56c8a9a66..eb32ae1b9 100644 --- a/test/runtime/value/create/void.ts +++ b/test/runtime/value/create/void.ts @@ -5,11 +5,10 @@ import { Assert } from '../../assert/index' describe('value/create/Void', () => { it('Should create value', () => { const T = Type.Void() - Assert.isEqual(Value.Create(T), null) + Assert.IsEqual(Value.Create(T), null) }) - it('Should create value from default value', () => { const T = Type.Void({ default: 'hello' }) - Assert.isEqual(Value.Create(T), 'hello') + Assert.IsEqual(Value.Create(T), 'hello') }) }) diff --git a/test/runtime/value/delta/diff.ts b/test/runtime/value/delta/diff.ts index bda3d0a1d..2943e157f 100644 --- a/test/runtime/value/delta/diff.ts +++ b/test/runtime/value/delta/diff.ts @@ -4,19 +4,15 @@ import { Assert } from '../../assert/index' // ----------------------------------------------------------------- // Diff Factory // ----------------------------------------------------------------- - -function Update(path: string, value: unknown): Edit { +function Update(path: string, value: unknown): Edit { return { type: 'update', path, value } as any } - -function Insert(path: string, value: unknown): Edit { +function Insert(path: string, value: unknown): Edit { return { type: 'insert', path, value } as any } - -function Delete(path: string): Edit { +function Delete(path: string): Edit { return { type: 'delete', path } as any } - describe('value/delta/Diff', () => { // ---------------------------------------------------- // Null @@ -26,35 +22,35 @@ describe('value/delta/Diff', () => { const B = null const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL undefined to undefined', () => { const A = undefined const B = undefined const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL string to string', () => { const A = 'hello' const B = 'hello' const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL number to number', () => { const A = 1 const B = 1 const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL boolean to boolean', () => { const A = true const B = true const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL symbol to symbol', () => { const S = Symbol('A') @@ -62,363 +58,328 @@ describe('value/delta/Diff', () => { const B = S const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL object to object', () => { const A = { x: 1, y: 2, z: 3 } const B = { x: 1, y: 2, z: 3 } const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NULL array to array', () => { const A = [1, 2, 3] const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [] as Edit[] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - // ---------------------------------------------------- // Type Change Root // ---------------------------------------------------- - it('Should diff TYPE change number to null', () => { const A = 1 const B = null const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff TYPE change null to undefined', () => { const A = null const B = undefined const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to number', () => { const A = null const B = 1 const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to boolean', () => { const A = null const B = true const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to string', () => { const A = null const B = 'hello' const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to symbol', () => { const A = null const B = Symbol('A') const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to object', () => { const A = null const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff TYPE change null to array', () => { const A = null const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) // ---------------------------------------------------- // Value Change Root // ---------------------------------------------------- - it('Should diff VALUE change number', () => { const A = 1 const B = 2 const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff VALUE change boolean', () => { const A = false const B = true const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff VALUE change string', () => { const A = 'hello' const B = 'world' const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff VALUE change symbol', () => { const A = Symbol('A') const B = Symbol('B') - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('', B)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - // ---------------------------------------------------- // Array // ---------------------------------------------------- - it('Should diff ELEMENT update', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 9] const D = Value.Diff(A, B) const E = [Update('/3', 9)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT push', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 4, 5] const D = Value.Diff(A, B) const E = [Insert('/4', 5)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT push twice', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 4, 5, 6] const D = Value.Diff(A, B) const E = [Insert('/4', 5), Insert('/5', 6)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT pop', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3] const D = Value.Diff(A, B) const E = [Delete('/3')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT pop twice', () => { const A = [1, 2, 3, 4] const B = [1, 2] const D = Value.Diff(A, B) const E = [Delete('/3'), Delete('/2')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT unshift', () => { const A = [1, 2, 3, 4] const B = [2, 3, 4] const D = Value.Diff(A, B) const E = [Update('/0', 2), Update('/1', 3), Update('/2', 4), Delete('/3')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff ELEMENT unshift twice', () => { const A = [1, 2, 3, 4] const B = [3, 4] const D = Value.Diff(A, B) const E = [Update('/0', 3), Update('/1', 4), Delete('/3'), Delete('/2')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - // ---------------------------------------------------- // Object // ---------------------------------------------------- - it('Should diff PROPERTY insert', () => { const A = { x: 1, y: 1 } const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const E = [Insert('/z', 1)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff PROPERTY delete', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1 } const D = Value.Diff(A, B) const E = [Delete('/z')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff PROPERTY update', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1, z: 2 } const D = Value.Diff(A, B) const E = [Update('/z', 2)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff PROPERTY all values', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 2, y: 2, z: 2 } const D = Value.Diff(A, B) const E = [Update('/x', 2), Update('/y', 2), Update('/z', 2)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff PROPERTY all delete, all insert', () => { const A = { x: 1, y: 1, z: 1 } const B = { a: 2, b: 2, c: 2 } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff PROPERTY update, insert and delete order preserved', () => { const A = { x: 1, y: 1, z: 1, w: 1 } const B = { a: 2, b: 2, c: 2, w: 2 } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('/w', 2), Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - // ---------------------------------------------------- // Object Nested // ---------------------------------------------------- - it('Should diff NESTED OBJECT diff type change update', () => { const A = { v: 1 } const B = { v: { x: 1, y: 1, z: 1 } } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('/v', B.v)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED OBJECT diff value change update', () => { const A = { v: 1 } const B = { v: 2 } const D = Value.Diff(A, B) const E = [Update('/v', B.v)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED OBJECT diff partial property update', () => { const A = { v: { x: 1, y: 1, z: 1 } } const B = { v: { x: 2, y: 2, z: 2 } } const D = Value.Diff(A, B) const E = [Update('/v/x', B.v.x), Update('/v/y', B.v.y), Update('/v/z', B.v.z)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED OBJECT diff partial property insert', () => { const A = { v: { x: 1, y: 1, z: 1 } } const B = { v: { x: 1, y: 1, z: 1, w: 1 } } const D = Value.Diff(A, B) const E = [Insert('/v/w', B.v.w)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED OBJECT diff partial property delete', () => { const A = { v: { x: 1, y: 1, z: 1 } } const B = { v: { x: 1, y: 1 } } const D = Value.Diff(A, B) const E = [Delete('/v/z')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED OBJECT ordered diff - update, insert and delete', () => { const A = { v: { x: 1, y: 1 } } const B = { v: { x: 2, w: 2 } } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('/v/x', B.v.x), Insert('/v/w', B.v.w), Delete('/v/y')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - // ---------------------------------------------------- // Array Nested // ---------------------------------------------------- - it('Should diff NESTED ARRAY object diff type change update', () => { const A = [{ v: 1 }] const B = [{ v: { x: 1, y: 1, z: 1 } }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('/0/v', B[0].v)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED ARRAY object diff value change update', () => { const A = [{ v: 1 }] const B = [{ v: 2 }] const D = Value.Diff(A, B) const E = [Update('/0/v', B[0].v)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NESTED ARRAY object diff partial property update', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 2, y: 2, z: 2 } }] const D = Value.Diff(A, B) const E = [Update('/0/v/x', B[0].v.x), Update('/0/v/y', B[0].v.y), Update('/0/v/z', B[0].v.z)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) it('Should diff NESTED ARRAY object diff partial property insert', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] const D = Value.Diff(A, B) const E = [Insert('/0/v/w', B[0].v.w)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED ARRAY object diff partial property delete', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1 } }] const D = Value.Diff(A, B) const E = [Delete('/0/v/z')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff NESTED ARRAY update, insert and delete order preserved', () => { const A = [{ v: { x: 1, y: 1 } }] const B = [{ v: { x: 2, w: 2 } }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const E = [Update('/0/v/x', B[0].v.x), Insert('/0/v/w', B[0].v.w), Delete('/0/v/y')] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should throw if attempting to diff a current value with symbol key', () => { const A = [{ [Symbol('A')]: 1, v: { x: 1, y: 1 } }] const B = [{ v: { x: 2, w: 2 } }] - Assert.throws(() => Value.Diff(A, B)) + Assert.Throws(() => Value.Diff(A, B)) }) - it('Should throw if attempting to diff a next value with symbol key', () => { const A = [{ v: { x: 1, y: 1 } }] const B = [{ [Symbol('A')]: 1, v: { x: 2, w: 2 } }] - Assert.throws(() => Value.Diff(A, B)) + Assert.Throws(() => Value.Diff(A, B)) }) - it('Should diff a Uint8Array (same size)', () => { const A = new Uint8Array([0, 1, 2, 3]) const B = new Uint8Array([0, 9, 2, 9]) const D = Value.Diff(A, B) const E = [Update('/1', 9), Update('/3', 9)] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff a Uint8Array (less than requires full update)', () => { const A = new Uint8Array([0, 1, 2, 3]) const B = new Uint8Array([0, 9, 2]) const D = Value.Diff(A, B) const E = [Update('', new Uint8Array([0, 9, 2]))] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) - it('Should diff a Uint8Array (greater than requires full update)', () => { const A = new Uint8Array([0, 1, 2, 3]) const B = new Uint8Array([0, 9, 2, 3, 4]) const D = Value.Diff(A, B) const E = [Update('', new Uint8Array([0, 9, 2, 3, 4]))] - Assert.isEqual(D, E) + Assert.IsEqual(D, E) }) }) diff --git a/test/runtime/value/delta/patch.ts b/test/runtime/value/delta/patch.ts index cb9ac37f3..7e308c42a 100644 --- a/test/runtime/value/delta/patch.ts +++ b/test/runtime/value/delta/patch.ts @@ -10,35 +10,35 @@ describe('value/delta/Patch', () => { const B = null const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NULL undefined to undefined', () => { const A = undefined const B = undefined const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NULL string to string', () => { const A = 'hello' const B = 'hello' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NULL number to number', () => { const A = 1 const B = 1 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NULL boolean to boolean', () => { const A = true const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NULL symbol to symbol', () => { const S = Symbol('A') @@ -46,356 +46,323 @@ describe('value/delta/Patch', () => { const B = S const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NULL object to object', () => { const A = { x: 1, y: 2, z: 3 } const B = { x: 1, y: 2, z: 3 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NULL array to array', () => { const A = [1, 2, 3] const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - // ---------------------------------------------------- // Type Change Root // ---------------------------------------------------- - it('Should patch TYPE change number to null', () => { const A = 1 const B = null const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch TYPE change null to undefined', () => { const A = null const B = undefined const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to number', () => { const A = null const B = 1 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to boolean', () => { const A = null const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to string', () => { const A = null const B = 'hello' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to symbol', () => { const A = null const B = Symbol('A') const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to object', () => { const A = null const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change null to array', () => { const A = null const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch TYPE change object to array', () => { const A = { x: 1, y: 2 } const B = [1, 2, 3] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch TYPE change array to object', () => { const A = [1, 2, 3] const B = { x: 1, y: 2 } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - // ---------------------------------------------------- // Value Change Root // ---------------------------------------------------- - it('Should patch VALUE change number', () => { const A = 1 const B = 2 const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch VALUE change boolean', () => { const A = false const B = true const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch VALUE change string', () => { const A = 'hello' const B = 'world' const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch VALUE change symbol', () => { const A = Symbol('A') const B = Symbol('B') - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - // ---------------------------------------------------- // Array // ---------------------------------------------------- - it('Should patch ELEMENT update', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 9] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT push', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 4, 5] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT push twice', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3, 4, 5, 6] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT pop', () => { const A = [1, 2, 3, 4] const B = [1, 2, 3] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT pop twice', () => { const A = [1, 2, 3, 4] const B = [1, 2] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT unshift', () => { const A = [1, 2, 3, 4] const B = [2, 3, 4] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch ELEMENT unshift twice', () => { const A = [1, 2, 3, 4] const B = [3, 4] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - // ---------------------------------------------------- // Object // ---------------------------------------------------- - it('Should patch PROPERTY insert', () => { const A = { x: 1, y: 1 } const B = { x: 1, y: 1, z: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch PROPERTY delete', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch PROPERTY update', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 1, y: 1, z: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch PROPERTY all values', () => { const A = { x: 1, y: 1, z: 1 } const B = { x: 2, y: 2, z: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch PROPERTY all delete, all insert', () => { const A = { x: 1, y: 1, z: 1 } const B = { a: 2, b: 2, c: 2 } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch PROPERTY update, insert and delete order preserved', () => { const A = { x: 1, y: 1, z: 1, w: 1 } const B = { a: 2, b: 2, c: 2, w: 2 } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) // ---------------------------------------------------- // Object Nested // ---------------------------------------------------- - it('Should patch NESTED OBJECT diff type change update', () => { const A = { v: 1 } const B = { v: { x: 1, y: 1, z: 1 } } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NESTED OBJECT diff value change update', () => { const A = { v: 1 } const B = { v: 2 } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NESTED OBJECT diff partial property update', () => { const A = { v: { x: 1, y: 1, z: 1 } } const B = { v: { x: 2, y: 2, z: 2 } } const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NESTED OBJECT update, insert and delete order preserved', () => { const A = { v: { x: 1, y: 1 } } const B = { v: { x: 2, w: 2 } } - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - // ---------------------------------------------------- // Array Nested // ---------------------------------------------------- - it('Should patch NESTED ARRAY object diff type change update', () => { const A = [{ v: 1 }] const B = [{ v: { x: 1, y: 1, z: 1 } }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NESTED ARRAY object diff value change update', () => { const A = [{ v: 1 }] const B = [{ v: 2 }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NESTED ARRAY object diff partial property update', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 2, y: 2, z: 2 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) it('Should patch NESTED ARRAY object diff partial property insert', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1, z: 1, w: 1 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NESTED ARRAY object diff partial property delete', () => { const A = [{ v: { x: 1, y: 1, z: 1 } }] const B = [{ v: { x: 1, y: 1 } }] const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch NESTED ARRAY object ordered diff - update, insert and delete', () => { const A = [{ v: { x: 1, y: 1 } }] const B = [{ v: { x: 2, w: 2 } }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch Uint8Array (same size)', () => { const A = [{ v: new Uint8Array([0, 1, 3]) }] const B = [{ v: new Uint8Array([0, 1, 2]) }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch Uint8Array (less than size)', () => { const A = [{ v: new Uint8Array([0, 1, 3]) }] const B = [{ v: new Uint8Array([0, 1]) }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) - it('Should patch Uint8Array (greater than size)', () => { const A = [{ v: new Uint8Array([0, 1, 3]) }] const B = [{ v: new Uint8Array([0, 1, 2, 4]) }] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) // ---------------------------------------------------- // Mega Values // ---------------------------------------------------- - it('Should patch MEGA value', () => { const A = [ { a: { x: 1, y: 1 } }, @@ -440,8 +407,8 @@ describe('value/delta/Patch', () => { ], }, ] - const D = Value.Diff(A, B) + const D = Value.Diff(A, B) const P = Value.Patch(A, D) - Assert.isEqual(B, P) + Assert.IsEqual(B, P) }) }) diff --git a/test/runtime/value/equal/equal.ts b/test/runtime/value/equal/equal.ts index dda3c6b18..57382d800 100644 --- a/test/runtime/value/equal/equal.ts +++ b/test/runtime/value/equal/equal.ts @@ -4,108 +4,90 @@ import { Assert } from '../../assert/index' describe('value/equal/Equal', () => { it('Should equal null value', () => { const R = Value.Equal(null, null) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal null value', () => { const R = Value.Equal(null, 'null') - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal undefined value', () => { const R = Value.Equal(undefined, undefined) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal undefined value', () => { const R = Value.Equal(undefined, 'undefined') - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal symbol value', () => { const S1 = Symbol.for('test') const S2 = Symbol.for('test') const R = Value.Equal(S1, S2) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal symbol value', () => { const S1 = Symbol('test') const S2 = Symbol('test') const R = Value.Equal(S1, S2) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal string value', () => { const R = Value.Equal('hello', 'hello') - Assert.isEqual(R, true) + Assert.IsTrue(R) }) it('Should not equal string value', () => { const R = Value.Equal('hello', 'world') - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal number value', () => { const R = Value.Equal(1, 1) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal number value', () => { const R = Value.Equal(1, 2) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal boolean value', () => { const R = Value.Equal(true, true) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal boolean value', () => { const R = Value.Equal(true, false) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal array value', () => { const R = Value.Equal([0, 1, 2], [0, 1, 2]) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal array value', () => { const R = Value.Equal([0, 1, 2], [0, 1, 3]) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not equal array value with additional elements', () => { const R = Value.Equal([0, 1, 2], [0, 1, 2, 3]) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal object value', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3 }) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal object value', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 4 }) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should not equal object value with additional properties', () => { const R = Value.Equal({ x: 1, y: 2, z: 3 }, { x: 1, y: 2, z: 3, w: 1 }) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) - it('Should equal typed array value', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 2])) - Assert.isEqual(R, true) + Assert.IsTrue(R) }) - it('Should not equal typed array value', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1, 3])) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) it('Should not equal typed array value with varying type', () => { const R = Value.Equal(new Uint8Array([0, 1, 2]), new Int8Array([0, 1, 2])) - Assert.isEqual(R, false) + Assert.IsFalse(R) }) }) diff --git a/test/runtime/value/guard/guard.ts b/test/runtime/value/guard/guard.ts new file mode 100644 index 000000000..d51511184 --- /dev/null +++ b/test/runtime/value/guard/guard.ts @@ -0,0 +1,302 @@ +import { Assert } from '../../assert/index' +import * as ValueGuard from '@sinclair/typebox/value/guard' + +describe('value/guard/ValueGuard', () => { + // ----------------------------------------------------- + // IsNull + // ----------------------------------------------------- + it('Should guard null 1', () => { + const R = ValueGuard.IsNull(null) + Assert.IsTrue(R) + }) + it('Should guard null 2', () => { + const R = ValueGuard.IsNull({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsUndefined + // ----------------------------------------------------- + it('Should guard undefined 1', () => { + const R = ValueGuard.IsUndefined(undefined) + Assert.IsTrue(R) + }) + it('Should guard undefined 2', () => { + const R = ValueGuard.IsUndefined({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBigInt + // ----------------------------------------------------- + it('Should guard bigint 1', () => { + const R = ValueGuard.IsBigInt(1n) + Assert.IsTrue(R) + }) + it('Should guard bigint 2', () => { + const R = ValueGuard.IsBigInt(1) + Assert.IsFalse(R) + }) + it('Should guard bigint 3', () => { + const R = ValueGuard.IsBigInt('123') + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsNumber + // ----------------------------------------------------- + it('Should guard number 1', () => { + const R = ValueGuard.IsNumber(1) + Assert.IsTrue(R) + }) + it('Should guard number 2', () => { + const R = ValueGuard.IsNumber(3.14) + Assert.IsTrue(R) + }) + it('Should guard number 3', () => { + const R = ValueGuard.IsNumber('') + Assert.IsFalse(R) + }) + it('Should guard number 4', () => { + const R = ValueGuard.IsNumber(NaN) + Assert.IsTrue(R) + }) + // ----------------------------------------------------- + // IsString + // ----------------------------------------------------- + it('Should guard string 1', () => { + const R = ValueGuard.IsString('') + Assert.IsTrue(R) + }) + it('Should guard string 2', () => { + const R = ValueGuard.IsString(true) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBoolean + // ----------------------------------------------------- + it('Should guard boolean 1', () => { + const R = ValueGuard.IsBoolean(true) + Assert.IsTrue(R) + }) + it('Should guard boolean 2', () => { + const R = ValueGuard.IsBoolean(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsObject + // ----------------------------------------------------- + it('Should guard object 1', () => { + const R = ValueGuard.IsObject({}) + Assert.IsTrue(R) + }) + it('Should guard object 2', () => { + const R = ValueGuard.IsObject(1) + Assert.IsFalse(R) + }) + it('Should guard object 3', () => { + const R = ValueGuard.IsObject([]) + Assert.IsTrue(R) + }) + // ----------------------------------------------------- + // IsArray + // ----------------------------------------------------- + it('Should guard array 1', () => { + const R = ValueGuard.IsArray([]) + Assert.IsTrue(R) + }) + it('Should guard array 2', () => { + const R = ValueGuard.IsArray({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // Specialized (Values Only) + // ----------------------------------------------------- + // ----------------------------------------------------- + // IsAsyncIterator + // ----------------------------------------------------- + it('Should guard async iterator 1', () => { + const R = ValueGuard.IsAsyncIterator((async function* (): any {})()) + Assert.IsTrue(R) + }) + it('Should guard async iterator 2', () => { + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // HasPropertyKey + // ----------------------------------------------------- + it('Should guard property key 1', () => { + const O = { x: 10 } + const R = ValueGuard.HasPropertyKey(O, 'x') + Assert.IsTrue(R) + }) + it('Should guard property key 2', () => { + const O = { x: 10 } + const R = ValueGuard.HasPropertyKey(O, 'y') + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsDate + // ----------------------------------------------------- + it('Should guard date 1', () => { + const R = ValueGuard.IsDate(new Date()) + Assert.IsTrue(R) + }) + it('Should guard date 2', () => { + const R = ValueGuard.IsDate({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsFunction + // ----------------------------------------------------- + it('Should guard function 1', () => { + const R = ValueGuard.IsFunction(function () {}) + Assert.IsTrue(R) + }) + it('Should guard function 2', () => { + const R = ValueGuard.IsFunction({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsInteger + // ----------------------------------------------------- + it('Should guard integer 1', () => { + const R = ValueGuard.IsInteger(1) + Assert.IsTrue(R) + }) + it('Should guard integer 2', () => { + const R = ValueGuard.IsInteger(3.14) + Assert.IsFalse(R) + }) + it('Should guard integer 3', () => { + const R = ValueGuard.IsInteger(NaN) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsIterator + // ----------------------------------------------------- + it('Should guard iterator 1', () => { + const R = ValueGuard.IsIterator((function* () {})()) + Assert.IsTrue(R) + }) + it('Should guard iterator 2', () => { + const R = ValueGuard.IsIterator({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsPlainObject + // ----------------------------------------------------- + it('Should guard plain object 1', () => { + const R = ValueGuard.IsPlainObject({}) + Assert.IsTrue(R) + }) + it('Should guard plain object 2', () => { + const R = ValueGuard.IsPlainObject(new (class {})()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsPromise + // ----------------------------------------------------- + it('Should guard promise 1', () => { + const R = ValueGuard.IsPromise(Promise.resolve(1)) + Assert.IsTrue(R) + }) + it('Should guard promise 2', () => { + const R = ValueGuard.IsPromise(new (class {})()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsSymbol + // ----------------------------------------------------- + it('Should guard symbol 1', () => { + const R = ValueGuard.IsSymbol(Symbol(1)) + Assert.IsTrue(R) + }) + it('Should guard symbol 2', () => { + const R = ValueGuard.IsSymbol(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsTypedArray + // ----------------------------------------------------- + it('Should guard typed array 1', () => { + const R = ValueGuard.IsTypedArray(new Uint8Array(1)) + Assert.IsTrue(R) + }) + it('Should guard typed array 2', () => { + const R = ValueGuard.IsTypedArray(new Float32Array(1)) + Assert.IsTrue(R) + }) + it('Should guard typed array 3', () => { + const R = ValueGuard.IsTypedArray(new ArrayBuffer(1)) + Assert.IsFalse(R) + }) + it('Should guard typed array 4', () => { + const R = ValueGuard.IsTypedArray(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsUint8Array + // ----------------------------------------------------- + it('Should guard uint8array 1', () => { + const R = ValueGuard.IsUint8Array(new Uint8Array(1)) + Assert.IsTrue(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsValueType + // ----------------------------------------------------- + it('Should guard value type 1', () => { + const R = ValueGuard.IsValueType(1) + Assert.IsTrue(R) + }) + it('Should guard value type 2', () => { + const R = ValueGuard.IsValueType(true) + Assert.IsTrue(R) + }) + it('Should guard value type 3', () => { + const R = ValueGuard.IsValueType(false) + Assert.IsTrue(R) + }) + it('Should guard value type 4', () => { + const R = ValueGuard.IsValueType('hello') + Assert.IsTrue(R) + }) + it('Should guard value type 5', () => { + const R = ValueGuard.IsValueType(1n) + Assert.IsTrue(R) + }) + it('Should guard value type 6', () => { + const R = ValueGuard.IsValueType(null) + Assert.IsTrue(R) + }) + it('Should guard value type 7', () => { + const R = ValueGuard.IsValueType(undefined) + Assert.IsTrue(R) + }) + it('Should guard value type 8', () => { + const R = ValueGuard.IsValueType(function () {}) + Assert.IsFalse(R) + }) + it('Should guard value type 9', () => { + const R = ValueGuard.IsValueType({}) + Assert.IsFalse(R) + }) + it('Should guard value type 10', () => { + const R = ValueGuard.IsValueType([]) + Assert.IsFalse(R) + }) + it('Should guard value type 11', () => { + const R = ValueGuard.IsValueType(class {}) + Assert.IsFalse(R) + }) + it('Should guard value type 12', () => { + const R = ValueGuard.IsValueType(new (class {})()) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/value/guard/index.ts b/test/runtime/value/guard/index.ts new file mode 100644 index 000000000..4f2ca894f --- /dev/null +++ b/test/runtime/value/guard/index.ts @@ -0,0 +1 @@ +import './guard' diff --git a/test/runtime/value/hash/hash.ts b/test/runtime/value/hash/hash.ts index eac4765e8..b93b676f1 100644 --- a/test/runtime/value/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -1,111 +1,111 @@ -import { ValueHash } from '@sinclair/typebox/value' +import * as ValueHash from '@sinclair/typebox/value/hash' import { Assert } from '../../assert/index' describe('value/hash/Hash', () => { it('Should hash number', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(1)) - const A = ValueHash.Create(1) - const B = ValueHash.Create(2) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(1)) + const A = ValueHash.Hash(1) + const B = ValueHash.Hash(2) + Assert.NotEqual(A, B) }) it('Should hash string', () => { - Assert.isEqual('bigint', typeof ValueHash.Create('hello')) - const A = ValueHash.Create('hello') - const B = ValueHash.Create('world') - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash('hello')) + const A = ValueHash.Hash('hello') + const B = ValueHash.Hash('world') + Assert.NotEqual(A, B) }) it('Should hash boolean', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(true)) - Assert.isEqual('bigint', typeof ValueHash.Create(false)) - const A = ValueHash.Create(true) - const B = ValueHash.Create(false) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(true)) + Assert.IsEqual('bigint', typeof ValueHash.Hash(false)) + const A = ValueHash.Hash(true) + const B = ValueHash.Hash(false) + Assert.NotEqual(A, B) }) it('Should hash null', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(null)) - const A = ValueHash.Create(null) - const B = ValueHash.Create(undefined) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(null)) + const A = ValueHash.Hash(null) + const B = ValueHash.Hash(undefined) + Assert.NotEqual(A, B) }) it('Should hash array', () => { - Assert.isEqual('bigint', typeof ValueHash.Create([0, 1, 2, 3])) - const A = ValueHash.Create([0, 1, 2, 3]) - const B = ValueHash.Create([0, 2, 2, 3]) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash([0, 1, 2, 3])) + const A = ValueHash.Hash([0, 1, 2, 3]) + const B = ValueHash.Hash([0, 2, 2, 3]) + Assert.NotEqual(A, B) }) it('Should hash object 1', () => { // prettier-ignore - Assert.isEqual('bigint', typeof ValueHash.Create({ x: 1, y: 2 })) - const A = ValueHash.Create({ x: 1, y: 2 }) - const B = ValueHash.Create({ x: 2, y: 2 }) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash({ x: 1, y: 2 })) + const A = ValueHash.Hash({ x: 1, y: 2 }) + const B = ValueHash.Hash({ x: 2, y: 2 }) + Assert.NotEqual(A, B) }) it('Should hash object 2', () => { - const A = ValueHash.Create({ x: 1, y: [1, 2] }) - const B = ValueHash.Create({ x: 1, y: [1, 3] }) - Assert.notEqual(A, B) + const A = ValueHash.Hash({ x: 1, y: [1, 2] }) + const B = ValueHash.Hash({ x: 1, y: [1, 3] }) + Assert.NotEqual(A, B) }) it('Should hash object 3', () => { - const A = ValueHash.Create({ x: 1, y: undefined }) - const B = ValueHash.Create({ x: 1 }) - Assert.notEqual(A, B) + const A = ValueHash.Hash({ x: 1, y: undefined }) + const B = ValueHash.Hash({ x: 1 }) + Assert.NotEqual(A, B) }) it('Should hash object 4', () => { - const A = ValueHash.Create({ x: 1, y: new Uint8Array([0, 1, 2]) }) - const B = ValueHash.Create({ x: 1, y: [0, 1, 2] }) - Assert.notEqual(A, B) + const A = ValueHash.Hash({ x: 1, y: new Uint8Array([0, 1, 2]) }) + const B = ValueHash.Hash({ x: 1, y: [0, 1, 2] }) + Assert.NotEqual(A, B) }) it('Should hash object 5', () => { - const A = ValueHash.Create({ x: 1, y: undefined }) - const B = ValueHash.Create({ x: 2, y: undefined }) - Assert.notEqual(A, B) + const A = ValueHash.Hash({ x: 1, y: undefined }) + const B = ValueHash.Hash({ x: 2, y: undefined }) + Assert.NotEqual(A, B) }) it('Should hash Date', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(new Date())) - const A = ValueHash.Create(new Date(1)) - const B = ValueHash.Create(new Date(2)) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(new Date())) + const A = ValueHash.Hash(new Date(1)) + const B = ValueHash.Hash(new Date(2)) + Assert.NotEqual(A, B) }) it('Should hash Uint8Array', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(new Uint8Array([0, 1, 2, 3]))) - const A = ValueHash.Create(new Uint8Array([0, 1, 2, 3])) - const B = ValueHash.Create(new Uint8Array([0, 2, 2, 3])) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(new Uint8Array([0, 1, 2, 3]))) + const A = ValueHash.Hash(new Uint8Array([0, 1, 2, 3])) + const B = ValueHash.Hash(new Uint8Array([0, 2, 2, 3])) + Assert.NotEqual(A, B) }) it('Should hash undefined', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(undefined)) - const A = ValueHash.Create(undefined) - const B = ValueHash.Create(null) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(undefined)) + const A = ValueHash.Hash(undefined) + const B = ValueHash.Hash(null) + Assert.NotEqual(A, B) }) it('Should hash symbol 1', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) - const A = ValueHash.Create(Symbol(1)) - const B = ValueHash.Create(Symbol()) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) + const A = ValueHash.Hash(Symbol(1)) + const B = ValueHash.Hash(Symbol()) + Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) - const A = ValueHash.Create(Symbol(1)) - const B = ValueHash.Create(Symbol(2)) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) + const A = ValueHash.Hash(Symbol(1)) + const B = ValueHash.Hash(Symbol(2)) + Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(Symbol())) - const A = ValueHash.Create(Symbol(1)) - const B = ValueHash.Create(Symbol(1)) - Assert.isEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) + const A = ValueHash.Hash(Symbol(1)) + const B = ValueHash.Hash(Symbol(1)) + Assert.IsEqual(A, B) }) it('Should hash bigint 1', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(BigInt(1))) - const A = ValueHash.Create(BigInt(1)) - const B = ValueHash.Create(BigInt(2)) - Assert.notEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) + const A = ValueHash.Hash(BigInt(1)) + const B = ValueHash.Hash(BigInt(2)) + Assert.NotEqual(A, B) }) it('Should hash bigint 2', () => { - Assert.isEqual('bigint', typeof ValueHash.Create(BigInt(1))) - const A = ValueHash.Create(BigInt(1)) - const B = ValueHash.Create(BigInt(1)) - Assert.isEqual(A, B) + Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) + const A = ValueHash.Hash(BigInt(1)) + const B = ValueHash.Hash(BigInt(1)) + Assert.IsEqual(A, B) }) }) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index 57403d7b8..e318d92b4 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -5,6 +5,7 @@ import './convert' import './create' import './delta' import './equal' +import './guard' import './hash' import './mutate' import './pointer' diff --git a/test/runtime/value/mutate/mutate.ts b/test/runtime/value/mutate/mutate.ts index fe5b5d053..f90fcd8f5 100644 --- a/test/runtime/value/mutate/mutate.ts +++ b/test/runtime/value/mutate/mutate.ts @@ -7,23 +7,23 @@ describe('value/mutate/Mutate', () => { // -------------------------------------------- it('should throw 1', () => { // @ts-ignore - Assert.throws(() => Value.Mutate(1, 1)) + Assert.Throws(() => Value.Mutate(1, 1)) }) it('should throw 2', () => { // @ts-ignore - Assert.throws(() => Value.Mutate({}, 1)) + Assert.Throws(() => Value.Mutate({}, 1)) }) it('should throw 3', () => { // @ts-ignore - Assert.throws(() => Value.Mutate([], 1)) + Assert.Throws(() => Value.Mutate([], 1)) }) it('should throw 4', () => { // @ts-ignore - Assert.throws(() => Value.Mutate({}, [])) + Assert.Throws(() => Value.Mutate({}, [])) }) it('should throw 5', () => { // @ts-ignore - Assert.throws(() => Value.Mutate([], {})) + Assert.Throws(() => Value.Mutate([], {})) }) // -------------------------------------------- // Mutate @@ -33,56 +33,56 @@ describe('value/mutate/Mutate', () => { const X = { y: Y } const A = { x: X } Value.Mutate(A, {}) - Assert.isEqual(A, {}) + Assert.IsEqual(A, {}) }) it('Should mutate 1', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: { z: 2 } } }) - Assert.isEqual(A.x.y.z, 2) - Assert.isEqual(A.x.y, Y) - Assert.isEqual(A.x, X) + Assert.IsEqual(A.x.y.z, 2) + Assert.IsEqual(A.x.y, Y) + Assert.IsEqual(A.x, X) }) it('Should mutate 2', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: { z: [1, 2, 3] } } }) - Assert.isEqual(A.x.y.z, [1, 2, 3]) - Assert.isEqual(A.x.y, Y) - Assert.isEqual(A.x, X) + Assert.IsEqual(A.x.y.z, [1, 2, 3]) + Assert.IsEqual(A.x.y, Y) + Assert.IsEqual(A.x, X) }) it('Should mutate 3', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: {} }) - Assert.isEqual(A.x.y, undefined) - Assert.isEqual(A.x, X) + Assert.IsEqual(A.x.y, undefined) + Assert.IsEqual(A.x, X) }) it('Should mutate 4', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: 1 } }) - Assert.isEqual(A.x.y, 1) - Assert.isEqual(A.x, X) + Assert.IsEqual(A.x.y, 1) + Assert.IsEqual(A.x, X) }) it('Should mutate 5', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: { y: [1, 2, 3] } }) - Assert.isEqual(A.x.y, [1, 2, 3]) - Assert.isEqual(A.x, X) + Assert.IsEqual(A.x.y, [1, 2, 3]) + Assert.IsEqual(A.x, X) }) it('Should mutate 6', () => { const Y = { z: 1 } const X = { y: Y } const A = { x: X } Value.Mutate(A, { x: [1, 2, 3] }) - Assert.notEqual(A.x, X) - Assert.isEqual(A.x, [1, 2, 3]) + Assert.NotEqual(A.x, X) + Assert.IsEqual(A.x, [1, 2, 3]) }) }) diff --git a/test/runtime/value/pointer/pointer.ts b/test/runtime/value/pointer/pointer.ts index 6c1ee1652..d359716eb 100644 --- a/test/runtime/value/pointer/pointer.ts +++ b/test/runtime/value/pointer/pointer.ts @@ -5,207 +5,172 @@ describe('value/pointer/Pointer', () => { //----------------------------------------------- // Format //----------------------------------------------- - it('Should produce correct format #1', () => { const R = [...ValuePointer.Format('')] - Assert.isEqual(R, []) + Assert.IsEqual(R, []) }) - it('Should produce correct format #2', () => { const R = [...ValuePointer.Format('a')] - Assert.isEqual(R, ['a']) + Assert.IsEqual(R, ['a']) }) - it('Should produce correct format #3', () => { const R = [...ValuePointer.Format('/')] - Assert.isEqual(R, ['']) + Assert.IsEqual(R, ['']) }) - it('Should produce correct format #4', () => { const R = [...ValuePointer.Format('/x')] - Assert.isEqual(R, ['x']) + Assert.IsEqual(R, ['x']) }) - it('Should produce correct format #5', () => { const R = [...ValuePointer.Format('/x/')] - Assert.isEqual(R, ['x', '']) + Assert.IsEqual(R, ['x', '']) }) - it('Should produce correct format #6', () => { const R = [...ValuePointer.Format('/x//')] - Assert.isEqual(R, ['x', '', '']) + Assert.IsEqual(R, ['x', '', '']) }) - it('Should produce correct format #7', () => { const R = [...ValuePointer.Format('/x//y')] - Assert.isEqual(R, ['x', '', 'y']) + Assert.IsEqual(R, ['x', '', 'y']) }) - it('Should produce correct format #8', () => { const R = [...ValuePointer.Format('/x//y/')] - Assert.isEqual(R, ['x', '', 'y', '']) + Assert.IsEqual(R, ['x', '', 'y', '']) }) - it('Should produce correct format #9', () => { const R = [...ValuePointer.Format('/x/~0')] - Assert.isEqual(R, ['x', '~']) + Assert.IsEqual(R, ['x', '~']) }) - it('Should produce correct format #10', () => { const R = [...ValuePointer.Format('/x/~1')] - Assert.isEqual(R, ['x', '/']) + Assert.IsEqual(R, ['x', '/']) }) - it('Should produce correct format #11', () => { const R = [...ValuePointer.Format('/x/~0/')] - Assert.isEqual(R, ['x', '~', '']) + Assert.IsEqual(R, ['x', '~', '']) }) - it('Should produce correct format #12', () => { const R = [...ValuePointer.Format('/x/~1/')] - Assert.isEqual(R, ['x', '/', '']) + Assert.IsEqual(R, ['x', '/', '']) }) - it('Should produce correct format #13', () => { const R = [...ValuePointer.Format('/x/a~0b')] - Assert.isEqual(R, ['x', 'a~b']) + Assert.IsEqual(R, ['x', 'a~b']) }) - it('Should produce correct format #14', () => { const R = [...ValuePointer.Format('/x/a~1b')] - Assert.isEqual(R, ['x', 'a/b']) + Assert.IsEqual(R, ['x', 'a/b']) }) - it('Should produce correct format #15', () => { const R = [...ValuePointer.Format('/x/a~0b/')] - Assert.isEqual(R, ['x', 'a~b', '']) + Assert.IsEqual(R, ['x', 'a~b', '']) }) - it('Should produce correct format #16', () => { const R = [...ValuePointer.Format('/x/a~1b/')] - Assert.isEqual(R, ['x', 'a/b', '']) + Assert.IsEqual(R, ['x', 'a/b', '']) }) - it('Should produce correct format #17', () => { const R = [...ValuePointer.Format('/x/a~0b///y')] - Assert.isEqual(R, ['x', 'a~b', '', '', 'y']) + Assert.IsEqual(R, ['x', 'a~b', '', '', 'y']) }) - it('Should produce correct format #18', () => { const R = [...ValuePointer.Format('/x/a~1b///y')] - Assert.isEqual(R, ['x', 'a/b', '', '', 'y']) + Assert.IsEqual(R, ['x', 'a/b', '', '', 'y']) }) - it('Should produce correct format #19', () => { const R = [...ValuePointer.Format('/x/a~0b///')] - Assert.isEqual(R, ['x', 'a~b', '', '', '']) + Assert.IsEqual(R, ['x', 'a~b', '', '', '']) }) - it('Should produce correct format #20', () => { const R = [...ValuePointer.Format('/x/a~1b///')] - Assert.isEqual(R, ['x', 'a/b', '', '', '']) + Assert.IsEqual(R, ['x', 'a/b', '', '', '']) }) - //----------------------------------------------- // Get //----------------------------------------------- - it('Should get array #1', () => { const V = [0, 1, 2, 3] - Assert.isEqual(ValuePointer.Get(V, ''), [0, 1, 2, 3]) - Assert.isEqual(ValuePointer.Get(V, '/'), undefined) - Assert.isEqual(ValuePointer.Get(V, '/0'), 0) - Assert.isEqual(ValuePointer.Get(V, '/1'), 1) - Assert.isEqual(ValuePointer.Get(V, '/2'), 2) - Assert.isEqual(ValuePointer.Get(V, '/3'), 3) + Assert.IsEqual(ValuePointer.Get(V, ''), [0, 1, 2, 3]) + Assert.IsEqual(ValuePointer.Get(V, '/'), undefined) + Assert.IsEqual(ValuePointer.Get(V, '/0'), 0) + Assert.IsEqual(ValuePointer.Get(V, '/1'), 1) + Assert.IsEqual(ValuePointer.Get(V, '/2'), 2) + Assert.IsEqual(ValuePointer.Get(V, '/3'), 3) }) - it('Should get array #2', () => { const V = [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }] - Assert.isEqual(ValuePointer.Get(V, ''), [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]) - Assert.isEqual(ValuePointer.Get(V, '/'), undefined) - Assert.isEqual(ValuePointer.Get(V, '/0'), { x: 0 }) - Assert.isEqual(ValuePointer.Get(V, '/1'), { x: 1 }) - Assert.isEqual(ValuePointer.Get(V, '/2'), { x: 2 }) - Assert.isEqual(ValuePointer.Get(V, '/3'), { x: 3 }) - Assert.isEqual(ValuePointer.Get(V, '/0/x'), 0) - Assert.isEqual(ValuePointer.Get(V, '/1/x'), 1) - Assert.isEqual(ValuePointer.Get(V, '/2/x'), 2) - Assert.isEqual(ValuePointer.Get(V, '/3/x'), 3) + Assert.IsEqual(ValuePointer.Get(V, ''), [{ x: 0 }, { x: 1 }, { x: 2 }, { x: 3 }]) + Assert.IsEqual(ValuePointer.Get(V, '/'), undefined) + Assert.IsEqual(ValuePointer.Get(V, '/0'), { x: 0 }) + Assert.IsEqual(ValuePointer.Get(V, '/1'), { x: 1 }) + Assert.IsEqual(ValuePointer.Get(V, '/2'), { x: 2 }) + Assert.IsEqual(ValuePointer.Get(V, '/3'), { x: 3 }) + Assert.IsEqual(ValuePointer.Get(V, '/0/x'), 0) + Assert.IsEqual(ValuePointer.Get(V, '/1/x'), 1) + Assert.IsEqual(ValuePointer.Get(V, '/2/x'), 2) + Assert.IsEqual(ValuePointer.Get(V, '/3/x'), 3) }) - it('Should get object #1', () => { const V = { x: 0, y: 1, z: 2 } - Assert.isEqual(ValuePointer.Get(V, ''), { x: 0, y: 1, z: 2 }) - Assert.isEqual(ValuePointer.Get(V, '/'), undefined) - Assert.isEqual(ValuePointer.Get(V, '/x'), 0) - Assert.isEqual(ValuePointer.Get(V, '/y'), 1) - Assert.isEqual(ValuePointer.Get(V, '/z'), 2) + Assert.IsEqual(ValuePointer.Get(V, ''), { x: 0, y: 1, z: 2 }) + Assert.IsEqual(ValuePointer.Get(V, '/'), undefined) + Assert.IsEqual(ValuePointer.Get(V, '/x'), 0) + Assert.IsEqual(ValuePointer.Get(V, '/y'), 1) + Assert.IsEqual(ValuePointer.Get(V, '/z'), 2) }) - it('Should get object #2', () => { const V = { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } } - Assert.isEqual(ValuePointer.Get(V, ''), { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } }) - Assert.isEqual(ValuePointer.Get(V, '/'), undefined) - Assert.isEqual(ValuePointer.Get(V, '/x'), { x: 0 }) - Assert.isEqual(ValuePointer.Get(V, '/y'), { x: 1 }) - Assert.isEqual(ValuePointer.Get(V, '/z'), { x: 2 }) + Assert.IsEqual(ValuePointer.Get(V, ''), { x: { x: 0 }, y: { x: 1 }, z: { x: 2 } }) + Assert.IsEqual(ValuePointer.Get(V, '/'), undefined) + Assert.IsEqual(ValuePointer.Get(V, '/x'), { x: 0 }) + Assert.IsEqual(ValuePointer.Get(V, '/y'), { x: 1 }) + Assert.IsEqual(ValuePointer.Get(V, '/z'), { x: 2 }) }) - it('Should get object #3', () => { const V = { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } } - Assert.isEqual(ValuePointer.Get(V, ''), { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } }) - Assert.isEqual(ValuePointer.Get(V, '/'), { x: -1 }) - Assert.isEqual(ValuePointer.Get(V, '/x'), { '': { x: 1 } }) - Assert.isEqual(ValuePointer.Get(V, '/y'), { '': { x: 2 } }) - Assert.isEqual(ValuePointer.Get(V, '/z'), { '': { x: 3 } }) - Assert.isEqual(ValuePointer.Get(V, '/x/'), { x: 1 }) - Assert.isEqual(ValuePointer.Get(V, '/y/'), { x: 2 }) - Assert.isEqual(ValuePointer.Get(V, '/z/'), { x: 3 }) - Assert.isEqual(ValuePointer.Get(V, '/x//x'), 1) - Assert.isEqual(ValuePointer.Get(V, '/y//x'), 2) - Assert.isEqual(ValuePointer.Get(V, '/z//x'), 3) + Assert.IsEqual(ValuePointer.Get(V, ''), { '': { x: -1 }, x: { '': { x: 1 } }, y: { '': { x: 2 } }, z: { '': { x: 3 } } }) + Assert.IsEqual(ValuePointer.Get(V, '/'), { x: -1 }) + Assert.IsEqual(ValuePointer.Get(V, '/x'), { '': { x: 1 } }) + Assert.IsEqual(ValuePointer.Get(V, '/y'), { '': { x: 2 } }) + Assert.IsEqual(ValuePointer.Get(V, '/z'), { '': { x: 3 } }) + Assert.IsEqual(ValuePointer.Get(V, '/x/'), { x: 1 }) + Assert.IsEqual(ValuePointer.Get(V, '/y/'), { x: 2 }) + Assert.IsEqual(ValuePointer.Get(V, '/z/'), { x: 3 }) + Assert.IsEqual(ValuePointer.Get(V, '/x//x'), 1) + Assert.IsEqual(ValuePointer.Get(V, '/y//x'), 2) + Assert.IsEqual(ValuePointer.Get(V, '/z//x'), 3) }) - //----------------------------------------------- // Has //----------------------------------------------- - it('Should return has true for undefined', () => { const V = undefined - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for null', () => { const V = null - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for object', () => { const V = {} - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for array', () => { const V: any[] = [] - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for string', () => { const V = 'hello' - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for number', () => { const V = 42 - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for boolean', () => { const V = false - Assert.isEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) }) - it('Should return has true for deeply nested', () => { const V = { '': { x: { y: { z: 1 } } }, @@ -216,58 +181,52 @@ describe('value/pointer/Pointer', () => { n: null, } // exists - Assert.isEqual(ValuePointer.Has(V, ''), true) - Assert.isEqual(ValuePointer.Has(V, '/'), true) - Assert.isEqual(ValuePointer.Has(V, '//x'), true) - Assert.isEqual(ValuePointer.Has(V, '//x/y'), true) - Assert.isEqual(ValuePointer.Has(V, '//x/y/z'), true) - Assert.isEqual(ValuePointer.Has(V, '/x'), true) - Assert.isEqual(ValuePointer.Has(V, '/y/x'), true) - Assert.isEqual(ValuePointer.Has(V, '/z'), true) - Assert.isEqual(ValuePointer.Has(V, '/z/0'), true) - Assert.isEqual(ValuePointer.Has(V, '/z/0/x'), true) - Assert.isEqual(ValuePointer.Has(V, '/z/1'), true) - Assert.isEqual(ValuePointer.Has(V, '/z/1/y'), true) - Assert.isEqual(ValuePointer.Has(V, '/x'), true) - Assert.isEqual(ValuePointer.Has(V, '/n'), true) + Assert.IsEqual(ValuePointer.Has(V, ''), true) + Assert.IsEqual(ValuePointer.Has(V, '/'), true) + Assert.IsEqual(ValuePointer.Has(V, '//x'), true) + Assert.IsEqual(ValuePointer.Has(V, '//x/y'), true) + Assert.IsEqual(ValuePointer.Has(V, '//x/y/z'), true) + Assert.IsEqual(ValuePointer.Has(V, '/x'), true) + Assert.IsEqual(ValuePointer.Has(V, '/y/x'), true) + Assert.IsEqual(ValuePointer.Has(V, '/z'), true) + Assert.IsEqual(ValuePointer.Has(V, '/z/0'), true) + Assert.IsEqual(ValuePointer.Has(V, '/z/0/x'), true) + Assert.IsEqual(ValuePointer.Has(V, '/z/1'), true) + Assert.IsEqual(ValuePointer.Has(V, '/z/1/y'), true) + Assert.IsEqual(ValuePointer.Has(V, '/x'), true) + Assert.IsEqual(ValuePointer.Has(V, '/n'), true) }) - //----------------------------------------------- // Set //----------------------------------------------- - it('Should throw when setting root', () => { const V = {} - Assert.throws(() => ValuePointer.Set(V, '', { x: 1 })) + Assert.Throws(() => ValuePointer.Set(V, '', { x: 1 })) }) - it('Should set array values', () => { const V = [0, 1, 2] ValuePointer.Set(V, '/0', 3) ValuePointer.Set(V, '/1', 4) ValuePointer.Set(V, '/2', 5) - Assert.isEqual(V, [3, 4, 5]) + Assert.IsEqual(V, [3, 4, 5]) }) - it('Should set object values', () => { const V = { x: 0, y: 1, z: 2 } ValuePointer.Set(V, '/x', 3) ValuePointer.Set(V, '/y', 4) ValuePointer.Set(V, '/z', 5) - Assert.isEqual(V, { x: 3, y: 4, z: 5 }) + Assert.IsEqual(V, { x: 3, y: 4, z: 5 }) }) - it('Should set object values recursively #1', () => { const V = {} ValuePointer.Set(V, '/x/y/z', 1) - Assert.isEqual(V, { x: { y: { z: 1 } } }) + Assert.IsEqual(V, { x: { y: { z: 1 } } }) }) - it('Should set object values recursively #2', () => { const V = {} ValuePointer.Set(V, '/x/0/y/z/', 1) ValuePointer.Set(V, '/x/1/y/z/', 2) - Assert.isEqual(V, { + Assert.IsEqual(V, { x: { 0: { y: { @@ -286,16 +245,13 @@ describe('value/pointer/Pointer', () => { }, }) }) - //----------------------------------------------- // Delete //----------------------------------------------- - it('Should throw when deleting root', () => { const V = {} - Assert.throws(() => ValuePointer.Delete(V, '')) + Assert.Throws(() => ValuePointer.Delete(V, '')) }) - it('Should delete object properties', () => { const V = { x: { x: 1, y: 2, z: 3 }, @@ -303,9 +259,8 @@ describe('value/pointer/Pointer', () => { } ValuePointer.Delete(V, '/x/y') ValuePointer.Delete(V, '/y') - Assert.isEqual(V, { x: { x: 1, z: 3 } }) + Assert.IsEqual(V, { x: { x: 1, z: 3 } }) }) - it('Should be a noop if property does not exist', () => { const V = { x: { x: 1, y: 2, z: 3 }, @@ -313,84 +268,74 @@ describe('value/pointer/Pointer', () => { } ValuePointer.Delete(V, '/x/w') ValuePointer.Delete(V, '/w') - Assert.isEqual(V, { + Assert.IsEqual(V, { x: { x: 1, y: 2, z: 3 }, y: { x: 3, y: 4, z: 5 }, }) }) - it('Should not delete owner', () => { const V = { x: { y: { z: 1 } } } ValuePointer.Delete(V, '/x/y/z') - Assert.isEqual(V, { x: { y: {} } }) + Assert.IsEqual(V, { x: { y: {} } }) }) - it('Should delete owner', () => { const V = { x: { y: { z: 1 } } } ValuePointer.Delete(V, '/x/y') - Assert.isEqual(V, { x: {} }) + Assert.IsEqual(V, { x: {} }) }) - it('Should not throw if deleting null property', () => { const V = { x: { y: null } } ValuePointer.Delete(V, '/x/y/z') - Assert.isEqual(V, { x: { y: null } }) + Assert.IsEqual(V, { x: { y: null } }) }) - //----------------------------------------------- // Escapes //----------------------------------------------- - it('Should support get ~0 pointer escape', () => { const V = { x: { '~': { x: 1 } }, } - Assert.isEqual(ValuePointer.Get(V, '/x/~0'), { x: 1 }) + Assert.IsEqual(ValuePointer.Get(V, '/x/~0'), { x: 1 }) }) - it('Should support get ~1 pointer escape', () => { const V = { x: { '/': { x: 1 } }, } - Assert.isEqual(ValuePointer.Get(V, '/x/~1'), { x: 1 }) + Assert.IsEqual(ValuePointer.Get(V, '/x/~1'), { x: 1 }) }) - it('Should support set ~0 pointer escape', () => { const V = { x: { '~': { x: 1 } }, } ValuePointer.Set(V, '/x/~0', { x: 2 }) - Assert.isEqual(V, { + Assert.IsEqual(V, { x: { '~': { x: 2 } }, }) }) - it('Should support set ~1 pointer escape', () => { const V = { x: { '/': { x: 1 } }, } ValuePointer.Set(V, '/x/~1', { x: 2 }) - Assert.isEqual(V, { + Assert.IsEqual(V, { x: { '/': { x: 2 } }, }) }) - it('Should support delete ~0 pointer escape', () => { const V = { x: { '~': { x: 1 } }, } ValuePointer.Delete(V, '/x/~0') - Assert.isEqual(V, { + Assert.IsEqual(V, { x: {}, }) }) - it('Should support delete ~1 pointer escape', () => { const V = { x: { '/': { x: 1 } }, } ValuePointer.Delete(V, '/x/~1') - Assert.isEqual(V, { + Assert.IsEqual(V, { x: {}, }) }) diff --git a/test/static/async-iterator.ts b/test/static/async-iterator.ts new file mode 100644 index 000000000..3d8d8fd1a --- /dev/null +++ b/test/static/async-iterator.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.AsyncIterator(Type.String())).ToInfer>() diff --git a/test/static/awaited.ts b/test/static/awaited.ts new file mode 100644 index 000000000..f8b0f8228 --- /dev/null +++ b/test/static/awaited.ts @@ -0,0 +1,20 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Awaited(Type.String())).ToInfer() + +Expect(Type.Awaited(Type.Promise(Type.String()))).ToInfer() + +Expect(Type.Awaited(Type.Promise(Type.Promise(Type.String())))).ToInfer() + +// One Level + +Expect(Type.Awaited(Type.Union([Type.Promise(Type.String()), Type.Number()]))).ToInfer() + +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.String()), Type.Number()]))).ToInfer() + +// Two Levels + +Expect(Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToInfer() + +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToInfer() diff --git a/test/static/capitalize.ts b/test/static/capitalize.ts new file mode 100644 index 000000000..a72fa79cc --- /dev/null +++ b/test/static/capitalize.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Capitalize(Type.Literal('hello'))).ToInfer<'Hello'>() diff --git a/test/static/index.ts b/test/static/index.ts index 7cb57c759..be70f8a5c 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -1,7 +1,10 @@ import './any' import './array' +import './async-iterator' +import './awaited' import './bigint' import './boolean' +import './capitalize' import './composite' import './date' import './constructor-parameters' @@ -13,8 +16,10 @@ import './function' import './indexed' import './instance-type' import './intersect' +import './iterator' import './keyof' import './literal' +import './lowercase' import './modifier' import './namespace' import './never' @@ -32,7 +37,7 @@ import './readonly' import './recursive' import './record' import './ref' -import './regex' +import './regexp' import './required' import './rest' import './return-type' @@ -41,5 +46,7 @@ import './string' import './symbol' import './template-literal' import './tuple' +import './uncapitalize' import './union' import './unknown' +import './uppercase' diff --git a/test/static/iterator.ts b/test/static/iterator.ts new file mode 100644 index 000000000..4728ad035 --- /dev/null +++ b/test/static/iterator.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Iterator(Type.String())).ToInfer>() diff --git a/test/static/lowercase.ts b/test/static/lowercase.ts new file mode 100644 index 000000000..56f9c2732 --- /dev/null +++ b/test/static/lowercase.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Lowercase(Type.Literal('HELLO'))).ToInfer<'hello'>() diff --git a/test/static/modifier.ts b/test/static/modifier.ts index c0950bdf2..07186f6d2 100644 --- a/test/static/modifier.ts +++ b/test/static/modifier.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type } from '@sinclair/typebox' +import { Type, TSchema } from '@sinclair/typebox' // Asserts combinatory modifiers { diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts index 622e809e2..377cab0cf 100644 --- a/test/static/readonly-optional.ts +++ b/test/static/readonly-optional.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' +import { Type, TSchema } from '@sinclair/typebox' { const T = Type.Object({ diff --git a/test/static/record.ts b/test/static/record.ts index 207e30d3f..7338d5f26 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -10,7 +10,7 @@ import { Type, Static } from '@sinclair/typebox' } { // type K = string - const K = Type.RegEx(/foo|bar/) + const K = Type.RegExp(/foo|bar/) const T = Type.Record(K, Type.Number()) type T = Static Expect(T).ToInfer>() diff --git a/test/static/regex.ts b/test/static/regexp.ts similarity index 62% rename from test/static/regex.ts rename to test/static/regexp.ts index ea2769da7..60feac2af 100644 --- a/test/static/regex.ts +++ b/test/static/regexp.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.RegEx(/foo/)).ToInfer() +Expect(Type.RegExp(/foo/)).ToInfer() diff --git a/test/static/required.ts b/test/static/required.ts index 30aa9ca1d..d688584b1 100644 --- a/test/static/required.ts +++ b/test/static/required.ts @@ -1,8 +1,5 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' - -Expect(Type.RegEx(/foo/)).ToInfer() - { const T = Type.Required( Type.Object({ diff --git a/test/static/uncapitalize.ts b/test/static/uncapitalize.ts new file mode 100644 index 000000000..4d7cb5afe --- /dev/null +++ b/test/static/uncapitalize.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Uncapitalize(Type.Literal('HELLO'))).ToInfer<'hELLO'>() diff --git a/test/static/uppercase.ts b/test/static/uppercase.ts new file mode 100644 index 000000000..a8fcd1821 --- /dev/null +++ b/test/static/uppercase.ts @@ -0,0 +1,4 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +Expect(Type.Uppercase(Type.Literal('hello'))).ToInfer<'HELLO'>() diff --git a/tsconfig.json b/tsconfig.json index 0932a6a80..41a696de3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,39 @@ "@sinclair/typebox/system": [ "src/system/index.ts" ], + "@sinclair/typebox/value/cast": [ + "src/value/cast.ts" + ], + "@sinclair/typebox/value/check": [ + "src/value/check.ts" + ], + "@sinclair/typebox/value/clone": [ + "src/value/clone.ts" + ], + "@sinclair/typebox/value/convert": [ + "src/value/convert.ts" + ], + "@sinclair/typebox/value/create": [ + "src/value/create.ts" + ], + "@sinclair/typebox/value/delta": [ + "src/value/delta.ts" + ], + "@sinclair/typebox/value/equal": [ + "src/value/equal.ts" + ], + "@sinclair/typebox/value/guard": [ + "src/value/guard.ts" + ], + "@sinclair/typebox/value/hash": [ + "src/value/hash.ts" + ], + "@sinclair/typebox/value/mutate": [ + "src/value/mutate.ts" + ], + "@sinclair/typebox/value/pointer": [ + "src/value/pointer.ts" + ], "@sinclair/typebox/value": [ "src/value/index.ts" ], From 61efbc17f6de6de462cca012ae979292ee450ad1 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 1 Aug 2023 15:55:43 +0900 Subject: [PATCH 164/369] Revision 0.30.0 --- examples/transform/transform.ts | 10 +++++++--- readme.md | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/transform/transform.ts b/examples/transform/transform.ts index 31d20f26c..32b9a15c2 100644 --- a/examples/transform/transform.ts +++ b/examples/transform/transform.ts @@ -133,9 +133,13 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any, return Apply(schema, value, mode) } function TObject(schema: Types.TObject, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Object.keys(schema.properties).reduce((acc, key) => { - return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key], mode) } : { ...acc } - }, value) + const transformed = Apply(schema, value, mode) + const properties = Object.keys(transformed).reduce((acc, key) => { + return key in schema.properties + ? { ...acc, [key]: Visit(schema.properties[key], references, transformed[key], mode) } + : { ...acc, [key]: transformed[key] } + }, {}) + return { ...properties } } function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode) { return Apply(schema, value, mode) diff --git a/readme.md b/readme.md index 2e8f6e443..527bdd325 100644 --- a/readme.md +++ b/readme.md @@ -110,7 +110,7 @@ License MIT - [Types](#typesystem-types) - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) -- [Transform](#Transform) +- [Transform](#transform) - [Ecosystem](#ecosystem) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) From 72ffd1abca6337a1dc11e304d1c6428ab93ca973 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 2 Aug 2023 19:24:22 +0900 Subject: [PATCH 165/369] Intrinsic String Mapping (#516) --- package-lock.json | 4 +- package.json | 2 +- readme.md | 63 +++++++++- src/typebox.ts | 121 ++++++++++++++++--- test/runtime/compiler/partial.ts | 10 +- test/runtime/type/guard/capitalize.ts | 23 +++- test/runtime/type/guard/lowercase.ts | 23 +++- test/runtime/type/guard/uncapitalize.ts | 24 +++- test/runtime/type/guard/uppercase.ts | 24 +++- test/runtime/type/index.ts | 1 + test/runtime/type/intrinsic/index.ts | 1 + test/runtime/type/intrinsic/intrinsic.ts | 143 +++++++++++++++++++++++ test/static/capitalize.ts | 7 ++ test/static/lowercase.ts | 7 ++ test/static/uncapitalize.ts | 7 ++ test/static/uppercase.ts | 7 ++ 16 files changed, 426 insertions(+), 41 deletions(-) create mode 100644 test/runtime/type/intrinsic/index.ts create mode 100644 test/runtime/type/intrinsic/intrinsic.ts diff --git a/package-lock.json b/package-lock.json index 2c3f6f9a9..96f1ad970 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.30.0", + "version": "0.30.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.30.0", + "version": "0.30.1", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7f372b309..bd16d7149 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.30.0", + "version": "0.30.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 527bdd325..8a071cfc4 100644 --- a/readme.md +++ b/readme.md @@ -84,6 +84,7 @@ License MIT - [Recursive](#types-recursive) - [Conditional](#types-conditional) - [Template Literal](#types-template-literal) + - [Intrinsic String](#types-intrinsic-string) - [Indexed](#types-indexed) - [Negated](#types-negated) - [Rest](#types-rest) @@ -486,6 +487,30 @@ The following table lists the Standard TypeBox types. These types are fully comp │ ]) │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uncapitalize( │ type T = Uncapitalize< │ const T = { │ +│ Type.Literal('Hello') │ 'Hello' │ type: 'string', │ +│ ) │ > │ const: 'hello' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Capitalize( │ type T = Capitalize< │ const T = { │ +│ Type.Literal('hello') │ 'hello' │ type: 'string', │ +│ ) │ > │ const: 'Hello' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Uppercase( │ type T = Uppercase< │ const T = { │ +│ Type.Literal('hello') │ 'hello' │ type: 'string', │ +│ ) │ > │ const: 'HELLO' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Lowercase( │ type T = Lowercase< │ const T = { │ +│ Type.Literal('HELLO') │ 'HELLO' │ type: 'string', │ +│ ) │ > │ const: 'hello' │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const R = { │ │ x: Type.Number(), │ x: number, │ $ref: 'T' │ │ y: Type.Number() │ y: number │ } │ @@ -851,6 +876,34 @@ const R = Type.Record(T, Type.String()) // const R = { // } ``` + + +### Intrinsic String Types + +TypeBox supports a set of intrinsic string mapping functions which can be used on string literals. These functions match the TypeScript string intrinsic types `Uppercase`, `Lowercase`, `Capitalize` and `Uncapitalize`. These functions are supported for literal strings, template literals and union types. The following shows the literal string usage. + +```typescript +// TypeScript + +type A = Uncapitalize<'HELLO'> // type A = 'hELLO' + +type B = Capitalize<'hello'> // type B = 'Hello' + +type C = Uppercase<'hello'> // type C = 'HELLO' + +type D = Lowercase<'HELLO'> // type D = 'hello' + +// TypeBox + +const A = Type.Uncapitalize(Type.Literal('HELLO')) // const A: TLiteral<'hELLO'> + +const B = Type.Capitalize(Type.Literal('hello')) // const B: TLiteral<'Hello'> + +const C = Type.Uppercase(Type.Literal('hello')) // const C: TLiteral<'HELLO'> + +const D = Type.Lowercase(Type.Literal('HELLO')) // const D: TLiteral<'hello'> +``` + ### Indexed Access Types @@ -1586,11 +1639,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '129.4 kb' │ ' 58.6 kb' │ '2.21 x' │ -│ typebox/errors │ '111.6 kb' │ ' 50.1 kb' │ '2.23 x' │ -│ typebox/system │ ' 76.5 kb' │ ' 31.7 kb' │ '2.41 x' │ -│ typebox/value │ '180.7 kb' │ ' 79.3 kb' │ '2.28 x' │ -│ typebox │ ' 75.4 kb' │ ' 31.3 kb' │ '2.41 x' │ +│ typebox/compiler │ '131.4 kb' │ ' 59.4 kb' │ '2.21 x' │ +│ typebox/errors │ '113.6 kb' │ ' 50.9 kb' │ '2.23 x' │ +│ typebox/system │ ' 78.5 kb' │ ' 32.5 kb' │ '2.42 x' │ +│ typebox/value │ '182.8 kb' │ ' 80.0 kb' │ '2.28 x' │ +│ typebox │ ' 77.4 kb' │ ' 32.0 kb' │ '2.42 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/typebox.ts b/src/typebox.ts index 2e7c407cf..8211fece1 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -248,7 +248,7 @@ export interface TAsyncIterator extends TSchema { items: T } // ------------------------------------------------------------------------------- -// Awaited +// TAwaited // ------------------------------------------------------------------------------- // prettier-ignore export type TAwaitedRest = T extends [infer L, ...infer R] @@ -394,7 +394,6 @@ export interface TFunction = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] export type TIndexProperty = K extends keyof T ? [T[K]] : [] export type TIndexTuple = K extends keyof T ? [T[K]] : [] @@ -419,6 +418,34 @@ export type TIndex = T extends TTuple ? UnionType>> : TNever // -------------------------------------------------------------------------- +// TIntrinsic +// -------------------------------------------------------------------------- +export type TIntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' +// prettier-ignore +export type TIntrinsicTemplateLiteral = + M extends ('Lowercase' | 'Uppercase') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...TIntrinsicTemplateLiteral, M>] : T : + M extends ('Capitalize' | 'Uncapitalize') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...R] : T : + T +// prettier-ignore +export type TIntrinsicLiteral = + T extends string ? + M extends 'Uncapitalize' ? Uncapitalize : + M extends 'Capitalize' ? Capitalize : + M extends 'Uppercase' ? Uppercase : + M extends 'Lowercase' ? Lowercase : + string + : '' +// prettier-ignore +export type TIntrinsicRest = T extends [infer L, ...infer R] + ? [TIntrinsic, M>, ...TIntrinsicRest, M>] + : [] +// prettier-ignore +export type TIntrinsic = + T extends TTemplateLiteral ? TTemplateLiteral> : + T extends TUnion ? TUnion> : + T extends TLiteral ? TLiteral> : + T +// -------------------------------------------------------------------------- // TInteger // -------------------------------------------------------------------------- export interface TInteger extends TSchema, NumericOptions { @@ -1306,8 +1333,15 @@ export namespace TypeGuard { } /** Returns true if the given schema is TString */ export function TString(schema: unknown): schema is TString { + // prettier-ignore return ( - TKindOf(schema, 'String') && schema.type === 'string' && IsOptionalString(schema.$id) && IsOptionalNumber(schema.minLength) && IsOptionalNumber(schema.maxLength) && IsOptionalPattern(schema.pattern) && IsOptionalFormat(schema.format) + TKindOf(schema, 'String') && + schema.type === 'string' && + IsOptionalString(schema.$id) && + IsOptionalNumber(schema.minLength) && + IsOptionalNumber(schema.maxLength) && + IsOptionalPattern(schema.pattern) && + IsOptionalFormat(schema.format) ) } /** Returns true if the given schema is TSymbol */ @@ -2106,6 +2140,61 @@ export namespace IndexedAccessor { } } // -------------------------------------------------------------------------- +// Intrinsic +// -------------------------------------------------------------------------- +export namespace Intrinsic { + function Uncapitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return `${first.toLowerCase()}${rest}` + } + function Capitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return `${first.toUpperCase()}${rest}` + } + function Uppercase(value: string): string { + return value.toUpperCase() + } + function Lowercase(value: string): string { + return value.toLowerCase() + } + function IntrinsicTemplateLiteral(schema: TTemplateLiteral, mode: TIntrinsicMode) { + // note: template literals require special runtime handling as they are encoded in string patterns. + // This diverges from the mapped type which would otherwise map on the template literal kind. + const expression = TemplateLiteralParser.ParseExact(schema.pattern) + const finite = TemplateLiteralFinite.Check(expression) + if (!finite) return { ...schema, pattern: IntrinsicLiteral(schema.pattern, mode) } as any + const strings = [...TemplateLiteralGenerator.Generate(expression)] + const literals = strings.map((value) => Type.Literal(value)) + const mapped = IntrinsicRest(literals as any, mode) + const union = Type.Union(mapped) + return Type.TemplateLiteral([union]) + } + function IntrinsicLiteral(value: TLiteralValue, mode: TIntrinsicMode) { + // prettier-ignore + return typeof value === 'string' ? ( + mode === 'Uncapitalize' ? Uncapitalize(value) : + mode === 'Capitalize' ? Capitalize(value) : + mode === 'Uppercase' ? Uppercase(value) : + mode === 'Lowercase' ? Lowercase(value) : + value) : '' + } + function IntrinsicRest(schema: TSchema[], mode: TIntrinsicMode): TSchema[] { + if (schema.length === 0) return [] + const [L, ...R] = schema + return [Map(L, mode), ...IntrinsicRest(R, mode)] + } + function Visit(schema: TSchema, mode: TIntrinsicMode) { + if (TypeGuard.TTemplateLiteral(schema)) return IntrinsicTemplateLiteral(schema, mode) + if (TypeGuard.TUnion(schema)) return Type.Union(IntrinsicRest(schema.anyOf, mode)) + if (TypeGuard.TLiteral(schema)) return Type.Literal(IntrinsicLiteral(schema.const, mode)) + return schema + } + /** Applies an intrinsic string manipulation to the given type. */ + export function Map(schema: T, mode: M): TIntrinsic { + return Visit(schema, mode) + } +} +// -------------------------------------------------------------------------- // ObjectMap // -------------------------------------------------------------------------- export namespace ObjectMap { @@ -2538,10 +2627,9 @@ export class StandardTypeBuilder extends TypeBuilder { public Boolean(options: SchemaOptions = {}): TBoolean { return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) } - /** `[Standard]` Capitalize a LiteralString type */ - public Capitalize>(schema: T, options: SchemaOptions = {}): TLiteral> { - const [first, rest] = [schema.const.slice(0, 1), schema.const.slice(1)] - return Type.Literal(`${first.toUpperCase()}${rest}` as Capitalize, options) + /** `[Standard]` Intrinsic function to Capitalize LiteralString types */ + public Capitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { + return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Capitalize'), ...options } } /** `[Standard]` Creates a Composite object type */ public Composite(objects: [...T], options?: ObjectOptions): TComposite { @@ -2665,9 +2753,9 @@ export class StandardTypeBuilder extends TypeBuilder { public Literal(value: T, options: SchemaOptions = {}): TLiteral { return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) } - /** `[Standard]` Lowercase a LiteralString type */ - public Lowercase>(schema: T, options: SchemaOptions = {}): TLiteral> { - return Type.Literal(schema.const.toLowerCase() as Lowercase, options) + /** `[Standard]` Intrinsic function to Lowercase LiteralString types */ + public Lowercase(schema: T, options: SchemaOptions = {}): TIntrinsic { + return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Lowercase'), ...options } } /** `[Standard]` Creates a Never type */ public Never(options: SchemaOptions = {}): TNever { @@ -2857,10 +2945,9 @@ export class StandardTypeBuilder extends TypeBuilder { { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any return this.Create(schema) } - /** `[Standard]` Uncapitalize a LiteralString type */ - public Uncapitalize>(schema: T, options: SchemaOptions = {}): TLiteral> { - const [first, rest] = [schema.const.slice(0, 1), schema.const.slice(1)] - return Type.Literal(`${first.toLocaleLowerCase()}${rest}` as Uncapitalize, options) + /** `[Standard]` Intrinsic function to Uncapitalize LiteralString types */ + public Uncapitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { + return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Uncapitalize'), ...options } } /** `[Standard]` Creates a Union type */ public Union(anyOf: [], options?: SchemaOptions): TNever @@ -2890,9 +2977,9 @@ export class StandardTypeBuilder extends TypeBuilder { public Unsafe(options: UnsafeOptions = {}): TUnsafe { return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) } - /** `[Standard]` Uppercase a LiteralString type */ - public Uppercase>(schema: T, options: SchemaOptions = {}): TLiteral> { - return Type.Literal(schema.const.toUpperCase() as Uppercase, options) + /** `[Standard]` Intrinsic function to Uppercase LiteralString types */ + public Uppercase(schema: T, options: SchemaOptions = {}): TIntrinsic { + return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Uppercase'), ...options } } } // -------------------------------------------------------------------------- diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 72ab48c9e..51a1d93fc 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -14,11 +14,10 @@ describe('type/compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - console.log(T) - //Ok(T, { x: 1, y: 1, z: 1 }) - //Ok(T, { x: 1, y: 1 }) - //Ok(T, { x: 1 }) - //Ok(T, {}) + Ok(T, { x: 1, y: 1, z: 1 }) + Ok(T, { x: 1, y: 1 }) + Ok(T, { x: 1 }) + Ok(T, {}) }) it('Should update modifier types correctly when converting to partial', () => { const A = Type.Object( @@ -38,7 +37,6 @@ describe('type/compiler/Partial', () => { strictEqual(T.properties.z[Optional], 'Optional') strictEqual(T.properties.w[Optional], 'Optional') }) - it('Should inherit options from the source object', () => { const A = Type.Object( { diff --git a/test/runtime/type/guard/capitalize.ts b/test/runtime/type/guard/capitalize.ts index 9e60fa035..77720f423 100644 --- a/test/runtime/type/guard/capitalize.ts +++ b/test/runtime/type/guard/capitalize.ts @@ -1,9 +1,28 @@ -import { Type } from '@sinclair/typebox' +import { TypeGuard, Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/Capitalize', () => { - it('Should guard for TCapitalize', () => { + it('Should guard for Capitalize 1', () => { + const T = Type.Capitalize(Type.Literal('hello'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'Hello') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Capitalize 2', () => { const T = Type.Capitalize(Type.Literal('hello')) + Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'Hello') }) + it('Should guard for Capitalize 3', () => { + const T = Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')])) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'Hello') + Assert.IsEqual(T.anyOf[1].const, 'World') + }) + it('Should guard for Capitalize 4', () => { + const T = Type.Capitalize(Type.TemplateLiteral('hello${0|1}')) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') + }) }) diff --git a/test/runtime/type/guard/lowercase.ts b/test/runtime/type/guard/lowercase.ts index 3a269cd2e..7eaecf962 100644 --- a/test/runtime/type/guard/lowercase.ts +++ b/test/runtime/type/guard/lowercase.ts @@ -1,9 +1,28 @@ -import { Type } from '@sinclair/typebox' +import { TypeGuard, Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/Lowercase', () => { - it('Should guard for Lowercase', () => { + it('Should guard for Lowercase 1', () => { + const T = Type.Lowercase(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'hello') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Lowercase 2', () => { const T = Type.Lowercase(Type.Literal('HELLO')) + Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hello') }) + it('Should guard for Lowercase 3', () => { + const T = Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hello') + Assert.IsEqual(T.anyOf[1].const, 'world') + }) + it('Should guard for Lowercase 4', () => { + const T = Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}')) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello0|hello1)$') + }) }) diff --git a/test/runtime/type/guard/uncapitalize.ts b/test/runtime/type/guard/uncapitalize.ts index ece6195a3..a0fbd880b 100644 --- a/test/runtime/type/guard/uncapitalize.ts +++ b/test/runtime/type/guard/uncapitalize.ts @@ -1,10 +1,28 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { TypeGuard, Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/Uncapitalize', () => { - it('Should guard for Uncapitalize', () => { + it('Should guard for Uncapitalize 1', () => { + const T = Type.Uncapitalize(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'hELLO') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Uncapitalize 2', () => { const T = Type.Uncapitalize(Type.Literal('HELLO')) + Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hELLO') }) + it('Should guard for Uncapitalize 3', () => { + const T = Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hELLO') + Assert.IsEqual(T.anyOf[1].const, 'wORLD') + }) + it('Should guard for Uncapitalize 4', () => { + const T = Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}')) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') + }) }) diff --git a/test/runtime/type/guard/uppercase.ts b/test/runtime/type/guard/uppercase.ts index f85bdad43..241ead739 100644 --- a/test/runtime/type/guard/uppercase.ts +++ b/test/runtime/type/guard/uppercase.ts @@ -1,10 +1,28 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { TypeGuard, Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/Uppercase', () => { - it('Should guard for Uppercase', () => { + it('Should guard for Uppercase 1', () => { + const T = Type.Uppercase(Type.Literal('hello'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'HELLO') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Uppercase 2', () => { const T = Type.Uppercase(Type.Literal('hello')) + Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'HELLO') }) + it('Should guard for Uppercase 3', () => { + const T = Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')])) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'HELLO') + Assert.IsEqual(T.anyOf[1].const, 'WORLD') + }) + it('Should guard for Uppercase 4', () => { + const T = Type.Uppercase(Type.TemplateLiteral('hello${0|1}')) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') + }) }) diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index dd13431a2..bc394a539 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -1,6 +1,7 @@ import './clone/index' import './extends/index' import './guard/index' +import './intrinsic/index' import './normalize/index' import './registry/index' import './template/index' diff --git a/test/runtime/type/intrinsic/index.ts b/test/runtime/type/intrinsic/index.ts new file mode 100644 index 000000000..8bb9e079c --- /dev/null +++ b/test/runtime/type/intrinsic/index.ts @@ -0,0 +1 @@ +import './intrinsic' diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts new file mode 100644 index 000000000..074223c22 --- /dev/null +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -0,0 +1,143 @@ +import { TypeGuard, Intrinsic } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/intrinsic/IntrinsicString', () => { + // ---------------------------------------------------- + // Passthrough + // ---------------------------------------------------- + it('Should passthrough 1', () => { + const T = Intrinsic.Map(Type.String(), 'Capitalize') + Assert.IsTrue(TypeGuard.TString(T)) + }) + it('Should passthrough 2', () => { + const T = Intrinsic.Map(Type.Number(), 'Capitalize') + Assert.IsTrue(TypeGuard.TNumber(T)) + }) + it('Should passthrough 3', () => { + const T = Intrinsic.Map( + Type.Object({ + x: Type.Number(), + }), + 'Capitalize', + ) + Assert.IsTrue(TypeGuard.TObject(T)) + }) + // ---------------------------------------------------- + // Partial Passthrough + // ---------------------------------------------------- + it('Should partial passthrough 3', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Literal('hello') + const U = Type.Union([A, B]) + const T = Intrinsic.Map(U, 'Capitalize') + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.TObject(T.anyOf[0])) + Assert.IsTrue(TypeGuard.TLiteral(T.anyOf[1])) + Assert.IsEqual(T.anyOf[1].const, 'Hello') + }) + // ---------------------------------------------------- + // Mode: Literal + // ---------------------------------------------------- + it('Should map literal: Capitalize', () => { + const T = Intrinsic.Map(Type.Literal('hello'), 'Capitalize') + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'Hello') + }) + it('Should map literal: Uncapitalize', () => { + const T = Intrinsic.Map(Type.Literal('HELLO'), 'Uncapitalize') + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'hELLO') + }) + it('Should map literal: Uppercase', () => { + const T = Intrinsic.Map(Type.Literal('hello'), 'Uppercase') + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'HELLO') + }) + it('Should map literal: Lowercase', () => { + const T = Intrinsic.Map(Type.Literal('HELLO'), 'Lowercase') + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + // ---------------------------------------------------- + // Mode: Literal Union + // ---------------------------------------------------- + it('Should map literal union: Capitalize', () => { + const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'Hello') + Assert.IsEqual(T.anyOf[1].const, 'World') + }) + it('Should map literal union: Uncapitalize', () => { + const T = Intrinsic.Map(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hello') + Assert.IsEqual(T.anyOf[1].const, 'world') + }) + it('Should map literal union: Uppercase', () => { + const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'HELLO') + Assert.IsEqual(T.anyOf[1].const, 'WORLD') + }) + it('Should map literal union: Lowercase', () => { + const T = Intrinsic.Map(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hello') + Assert.IsEqual(T.anyOf[1].const, 'world') + }) + // ---------------------------------------------------- + // Mode: TemplateLiteral + // ---------------------------------------------------- + it('Should map template literal: Capitalize', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello1world|Hello2world)$') + }) + it('Should map template literal: Uncapitalize', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO1WORLD|hELLO2WORLD)$') + }) + it('Should map template literal: Uppercase', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO1WORLD|HELLO2WORLD)$') + }) + it('Should map template literal: Lowercase', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello1world|hello2world)$') + }) + // ---------------------------------------------------- + // Mode: TemplateLiteral Patterns + // ---------------------------------------------------- + it('Should map template literal patterns 1', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^hello(.*)world$') + console.log(T.pattern) + }) + it('Should map template literal patterns 2', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^hello(0|[1-9][0-9]*)world$') + console.log(T.pattern) + }) + it('Should map template literal patterns 3', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('${number}${string}'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)(.*)$') + console.log(T.pattern) + }) + it('Should map template literal patterns 3', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)hello(.*)$') + }) + it('Should map template literal patterns 3', () => { + const T = Intrinsic.Map(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(stringhello|numberhello)$') + }) +}) diff --git a/test/static/capitalize.ts b/test/static/capitalize.ts index a72fa79cc..826075277 100644 --- a/test/static/capitalize.ts +++ b/test/static/capitalize.ts @@ -2,3 +2,10 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' Expect(Type.Capitalize(Type.Literal('hello'))).ToInfer<'Hello'>() + +Expect(Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToInfer<'Hello' | 'World'>() + +Expect(Type.Capitalize(Type.TemplateLiteral('hello${0|1}'))).ToInfer<'Hello0' | 'Hello1'>() + +// passthrough +Expect(Type.Capitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/lowercase.ts b/test/static/lowercase.ts index 56f9c2732..5a4f23cb8 100644 --- a/test/static/lowercase.ts +++ b/test/static/lowercase.ts @@ -2,3 +2,10 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' Expect(Type.Lowercase(Type.Literal('HELLO'))).ToInfer<'hello'>() + +Expect(Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToInfer<'hello' | 'world'>() + +Expect(Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hello0' | 'hello1'>() + +// passthrough +Expect(Type.Lowercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/uncapitalize.ts b/test/static/uncapitalize.ts index 4d7cb5afe..c44caa542 100644 --- a/test/static/uncapitalize.ts +++ b/test/static/uncapitalize.ts @@ -2,3 +2,10 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' Expect(Type.Uncapitalize(Type.Literal('HELLO'))).ToInfer<'hELLO'>() + +Expect(Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToInfer<'hELLO' | 'wORLD'>() + +Expect(Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hELLO0' | 'hELLO1'>() + +// passthrough +Expect(Type.Uncapitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/uppercase.ts b/test/static/uppercase.ts index a8fcd1821..2c8e90ce5 100644 --- a/test/static/uppercase.ts +++ b/test/static/uppercase.ts @@ -2,3 +2,10 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' Expect(Type.Uppercase(Type.Literal('hello'))).ToInfer<'HELLO'>() + +Expect(Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToInfer<'HELLO' | 'WORLD'>() + +Expect(Type.Uppercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'HELLO0' | 'HELLO1'>() + +// passthrough +Expect(Type.Uppercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() From 3a543656d6862ef4bc9eead582f1783d41b8bfc6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 2 Aug 2023 19:59:39 +0900 Subject: [PATCH 166/369] Intrinsic String Mapping (#517) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 4 ++-- test/runtime/type/guard/capitalize.ts | 5 +++++ test/runtime/type/guard/lowercase.ts | 5 +++++ test/runtime/type/guard/uncapitalize.ts | 5 +++++ test/runtime/type/guard/uppercase.ts | 5 +++++ test/runtime/type/intrinsic/intrinsic.ts | 23 +++++++++++++++++++++++ test/static/capitalize.ts | 3 +++ test/static/lowercase.ts | 3 +++ test/static/uncapitalize.ts | 3 +++ test/static/uppercase.ts | 3 +++ 12 files changed, 60 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96f1ad970..3c774d74c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.30.1", + "version": "0.30.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.30.1", + "version": "0.30.2", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index bd16d7149..e2e0cb959 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.30.1", + "version": "0.30.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 8211fece1..8f37e240e 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -434,7 +434,7 @@ export type TIntrinsicLiteral = M extends 'Uppercase' ? Uppercase : M extends 'Lowercase' ? Lowercase : string - : '' + : T // prettier-ignore export type TIntrinsicRest = T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...TIntrinsicRest, M>] @@ -2176,7 +2176,7 @@ export namespace Intrinsic { mode === 'Capitalize' ? Capitalize(value) : mode === 'Uppercase' ? Uppercase(value) : mode === 'Lowercase' ? Lowercase(value) : - value) : '' + value) : value.toString() } function IntrinsicRest(schema: TSchema[], mode: TIntrinsicMode): TSchema[] { if (schema.length === 0) return [] diff --git a/test/runtime/type/guard/capitalize.ts b/test/runtime/type/guard/capitalize.ts index 77720f423..33ed2e9a8 100644 --- a/test/runtime/type/guard/capitalize.ts +++ b/test/runtime/type/guard/capitalize.ts @@ -25,4 +25,9 @@ describe('type/guard/Capitalize', () => { Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') }) + it('Should guard for Capitalize 5', () => { + const T = Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') + }) }) diff --git a/test/runtime/type/guard/lowercase.ts b/test/runtime/type/guard/lowercase.ts index 7eaecf962..5a07e5d41 100644 --- a/test/runtime/type/guard/lowercase.ts +++ b/test/runtime/type/guard/lowercase.ts @@ -25,4 +25,9 @@ describe('type/guard/Lowercase', () => { Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello0|hello1)$') }) + it('Should guard for Lowercase 5', () => { + const T = Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello0|hello1)$') + }) }) diff --git a/test/runtime/type/guard/uncapitalize.ts b/test/runtime/type/guard/uncapitalize.ts index a0fbd880b..d54b4e584 100644 --- a/test/runtime/type/guard/uncapitalize.ts +++ b/test/runtime/type/guard/uncapitalize.ts @@ -25,4 +25,9 @@ describe('type/guard/Uncapitalize', () => { Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') }) + it('Should guard for Uncapitalize 5', () => { + const T = Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') + }) }) diff --git a/test/runtime/type/guard/uppercase.ts b/test/runtime/type/guard/uppercase.ts index 241ead739..602ddf400 100644 --- a/test/runtime/type/guard/uppercase.ts +++ b/test/runtime/type/guard/uppercase.ts @@ -25,4 +25,9 @@ describe('type/guard/Uppercase', () => { Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') }) + it('Should guard for Uppercase 5', () => { + const T = Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') + }) }) diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts index 074223c22..966f0df50 100644 --- a/test/runtime/type/intrinsic/intrinsic.ts +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -110,6 +110,29 @@ describe('type/intrinsic/IntrinsicString', () => { Assert.IsEqual(T.pattern, '^(hello1world|hello2world)$') }) // ---------------------------------------------------- + // Mode: TemplateLiteral Numeric + // ---------------------------------------------------- + it('Should map template literal numeric: Capitalize', () => { + const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello1|Hello2)$') + }) + it('Should map template literal numeric: Uncapitalize', () => { + const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO1|hELLO2)$') + }) + it('Should map template literal numeric: Uppercase', () => { + const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO1|HELLO2)$') + }) + it('Should map template literal numeric: Lowercase', () => { + const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') + Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello1|hello2)$') + }) + // ---------------------------------------------------- // Mode: TemplateLiteral Patterns // ---------------------------------------------------- it('Should map template literal patterns 1', () => { diff --git a/test/static/capitalize.ts b/test/static/capitalize.ts index 826075277..ca02c7398 100644 --- a/test/static/capitalize.ts +++ b/test/static/capitalize.ts @@ -7,5 +7,8 @@ Expect(Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')] Expect(Type.Capitalize(Type.TemplateLiteral('hello${0|1}'))).ToInfer<'Hello0' | 'Hello1'>() +// prettier-ignore +Expect(Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'Hello1' | 'Hello2'>() + // passthrough Expect(Type.Capitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/lowercase.ts b/test/static/lowercase.ts index 5a4f23cb8..78c7f7a84 100644 --- a/test/static/lowercase.ts +++ b/test/static/lowercase.ts @@ -7,5 +7,8 @@ Expect(Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]) Expect(Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hello0' | 'hello1'>() +// prettier-ignore +Expect(Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'hello1' | 'hello2'>() + // passthrough Expect(Type.Lowercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/uncapitalize.ts b/test/static/uncapitalize.ts index c44caa542..a52d0c023 100644 --- a/test/static/uncapitalize.ts +++ b/test/static/uncapitalize.ts @@ -7,5 +7,8 @@ Expect(Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD' Expect(Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hELLO0' | 'hELLO1'>() +// prettier-ignore +Expect(Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'hELLO1' | 'hELLO2'>() + // passthrough Expect(Type.Uncapitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() diff --git a/test/static/uppercase.ts b/test/static/uppercase.ts index 2c8e90ce5..dafde13c6 100644 --- a/test/static/uppercase.ts +++ b/test/static/uppercase.ts @@ -7,5 +7,8 @@ Expect(Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')]) Expect(Type.Uppercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'HELLO0' | 'HELLO1'>() +// prettier-ignore +Expect(Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'HELLO1' | 'HELLO2'>() + // passthrough Expect(Type.Uppercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() From 461883d20a13658904784d0daff031a89b67bb78 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 2 Aug 2023 21:36:00 +0900 Subject: [PATCH 167/369] Revision 0.30.0 --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index 8a071cfc4..b89ddfe69 100644 --- a/readme.md +++ b/readme.md @@ -72,7 +72,6 @@ License MIT ## Contents - [Install](#install) - [Overview](#overview) -- [Features](#features) - [Usage](#usage) - [Types](#types) - [Standard](#types-standard) From 93547e50deb3d17eb47a07dfdfafb13e0febf52f Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 3 Aug 2023 06:47:07 +0900 Subject: [PATCH 168/369] Intrinsic String Mapping --- readme.md | 65 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/readme.md b/readme.md index b89ddfe69..f3f6c2c3d 100644 --- a/readme.md +++ b/readme.md @@ -83,9 +83,9 @@ License MIT - [Recursive](#types-recursive) - [Conditional](#types-conditional) - [Template Literal](#types-template-literal) - - [Intrinsic String](#types-intrinsic-string) - [Indexed](#types-indexed) - [Negated](#types-negated) + - [Intrinsic](#types-intrinsic) - [Rest](#types-rest) - [Guards](#types-guards) - [Unsafe](#types-unsafe) @@ -875,34 +875,6 @@ const R = Type.Record(T, Type.String()) // const R = { // } ``` - - -### Intrinsic String Types - -TypeBox supports a set of intrinsic string mapping functions which can be used on string literals. These functions match the TypeScript string intrinsic types `Uppercase`, `Lowercase`, `Capitalize` and `Uncapitalize`. These functions are supported for literal strings, template literals and union types. The following shows the literal string usage. - -```typescript -// TypeScript - -type A = Uncapitalize<'HELLO'> // type A = 'hELLO' - -type B = Capitalize<'hello'> // type B = 'Hello' - -type C = Uppercase<'hello'> // type C = 'HELLO' - -type D = Lowercase<'HELLO'> // type D = 'hello' - -// TypeBox - -const A = Type.Uncapitalize(Type.Literal('HELLO')) // const A: TLiteral<'hELLO'> - -const B = Type.Capitalize(Type.Literal('hello')) // const B: TLiteral<'Hello'> - -const C = Type.Uppercase(Type.Literal('hello')) // const C: TLiteral<'HELLO'> - -const D = Type.Lowercase(Type.Literal('HELLO')) // const D: TLiteral<'hello'> -``` - ### Indexed Access Types @@ -985,6 +957,41 @@ const Even = Type.Number({ multipleOf: 2 }) const Odd = Type.Intersect([Type.Number(), Type.Not(Even)]) ``` + + + +### Intrinsic String Types + +TypeBox supports TypeScript intrinsic string manipulation types `Uppercase`, `Lowercase`, `Capitalize` and `Uncapitalize`. These can be applied to string literals, template literals and unions. The following shows general usage. + +```typescript +// TypeScript + +type A = Capitalize<'hello'> // type A = 'Hello' + +type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' + +type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' + +// TypeBox + +const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> + +const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ + Type.Literal('hello'), // TLiteral<'Hello'>, + Type.Literal('world') // TLiteral<'World'> +])) // ]> + +const C = Type.Capitalize( // const C: TTemplateLiteral<[ + Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, +) // TUnion<[ + // TLiteral<'1'>, + // TLiteral<'2'>, + // TLiteral<'3'> + // ]> + // ]> +``` + ### Rest Types From 934dc9a4ae3bde14825465b30e3cc4db1e6de754 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 5 Aug 2023 17:31:12 +0900 Subject: [PATCH 169/369] Send Kind To TypeRegistry Function (#522) --- package-lock.json | 4 +- package.json | 2 +- src/compiler/compiler.ts | 11 ++- test/runtime/compiler/custom.ts | 26 ------- test/runtime/compiler/index.ts | 2 +- test/runtime/compiler/kind.ts | 72 +++++++++++++++++++ test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/kind.ts | 13 ++++ test/runtime/value/cast/index.ts | 2 +- .../runtime/value/cast/{custom.ts => kind.ts} | 18 ++--- test/runtime/value/check/custom.ts | 29 -------- test/runtime/value/check/index.ts | 2 +- test/runtime/value/check/kind.ts | 70 ++++++++++++++++++ test/runtime/value/convert/custom.ts | 24 ------- test/runtime/value/convert/index.ts | 2 +- test/runtime/value/convert/kind.ts | 39 ++++++++++ test/runtime/value/create/custom.ts | 17 ----- test/runtime/value/create/index.ts | 2 +- test/runtime/value/create/kind.ts | 23 ++++++ 19 files changed, 244 insertions(+), 115 deletions(-) delete mode 100644 test/runtime/compiler/custom.ts create mode 100644 test/runtime/compiler/kind.ts create mode 100644 test/runtime/type/guard/kind.ts rename test/runtime/value/cast/{custom.ts => kind.ts} (73%) delete mode 100644 test/runtime/value/check/custom.ts create mode 100644 test/runtime/value/check/kind.ts delete mode 100644 test/runtime/value/convert/custom.ts create mode 100644 test/runtime/value/convert/kind.ts delete mode 100644 test/runtime/value/create/custom.ts create mode 100644 test/runtime/value/create/kind.ts diff --git a/package-lock.json b/package-lock.json index 3c774d74c..e71d5adf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.30.2", + "version": "0.30.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.30.2", + "version": "0.30.3", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index e2e0cb959..bee3ce854 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.30.2", + "version": "0.30.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9509e0d29..c0bac349e 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -375,7 +375,9 @@ export namespace TypeCompiler { yield IsVoidCheck(value) } function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { - yield `kind('${schema[Types.Kind]}', ${value})` + const instance = state.instances.size + state.instances.set(instance, schema) + yield `kind('${schema[Types.Kind]}', ${instance}, ${value})` } function* Visit(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator { const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references @@ -470,6 +472,7 @@ export namespace TypeCompiler { language: 'javascript', // target language functions: new Map(), // local functions variables: new Map(), // local variables + instances: new Map() // exterior kind instances } // ------------------------------------------------------------------- // Compiler Factory @@ -533,6 +536,7 @@ export namespace TypeCompiler { state.language = options.language state.variables.clear() state.functions.clear() + state.instances.clear() if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) return Build(schema, references, options) @@ -541,8 +545,9 @@ export namespace TypeCompiler { export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { const generatedCode = Code(schema, references, { language: 'javascript' }) const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) - function typeRegistryFunction(kind: string, value: unknown) { - if (!Types.TypeRegistry.Has(kind)) return false + function typeRegistryFunction(kind: string, instance: number, value: unknown) { + if (!Types.TypeRegistry.Has(kind) || !state.instances.has(instance)) return false + const schema = state.instances.get(instance) const checkFunc = Types.TypeRegistry.Get(kind)! return checkFunc(schema, value) } diff --git a/test/runtime/compiler/custom.ts b/test/runtime/compiler/custom.ts deleted file mode 100644 index 4380a9e82..000000000 --- a/test/runtime/compiler/custom.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Type, Kind, TypeRegistry } from '@sinclair/typebox' -import { Ok, Fail } from './validate' - -describe('type/compiler/Custom', () => { - TypeRegistry.Set('BigInt', (schema, value) => typeof value === 'bigint') - it('Should validate bigint', () => { - const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Ok(T, 1n) - }) - it('Should not validate bigint', () => { - const T = Type.Unsafe({ [Kind]: 'BigInt' }) - Fail(T, 1) - }) - it('Should validate bigint nested', () => { - const T = Type.Object({ - x: Type.Unsafe({ [Kind]: 'BigInt' }), - }) - Ok(T, { x: 1n }) - }) - it('Should not validate bigint nested', () => { - const T = Type.Object({ - x: Type.Unsafe({ [Kind]: 'BigInt' }), - }) - Fail(T, { x: 1 }) - }) -}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index d0120684b..e47c1ce9b 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -4,7 +4,6 @@ import './async-iterator' import './bigint' import './boolean' import './composite' -import './custom' import './date' import './unicode' import './enum' @@ -12,6 +11,7 @@ import './integer' import './intersect' import './iterator' import './keyof' +import './kind' import './literal' import './modifier' import './never' diff --git a/test/runtime/compiler/kind.ts b/test/runtime/compiler/kind.ts new file mode 100644 index 000000000..972c5def2 --- /dev/null +++ b/test/runtime/compiler/kind.ts @@ -0,0 +1,72 @@ +import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Ok, Fail } from './validate' +import { Assert } from '../assert' + +describe('type/compiler/Kind', () => { + // ------------------------------------------------------------ + // Fixtures + // ------------------------------------------------------------ + beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI)) + afterEach(() => TypeRegistry.Delete('PI')) + // ------------------------------------------------------------ + // Tests + // ------------------------------------------------------------ + it('Should validate', () => { + const T = Type.Unsafe({ [Kind]: 'PI' }) + Ok(T, Math.PI) + }) + it('Should not validate', () => { + const T = Type.Unsafe({ [Kind]: 'PI' }) + Fail(T, Math.PI * 2) + }) + it('Should validate in object', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'PI' }), + }) + Ok(T, { x: Math.PI }) + }) + it('Should not validate in object', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'PI' }), + }) + Fail(T, { x: Math.PI * 2 }) + }) + it('Should validate in array', () => { + const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' })) + Ok(T, [Math.PI]) + }) + it('Should not validate in array', () => { + const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' })) + Fail(T, [Math.PI * 2]) + }) + it('Should validate in tuple', () => { + const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })]) + Ok(T, [Math.PI]) + }) + it('Should not validate in tuple', () => { + const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })]) + Fail(T, [Math.PI * 2]) + }) + // ------------------------------------------------------------ + // Instances + // ------------------------------------------------------------ + it('Should receive kind instance on registry callback', () => { + const stack: string[] = [] + TypeRegistry.Set('Kind', (schema: unknown) => { + // prettier-ignore + return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string') + ? (() => { stack.push(schema.$id); return true })() + : false + }) + const A = { [Kind]: 'Kind', $id: 'A' } as TSchema + const B = { [Kind]: 'Kind', $id: 'B' } as TSchema + const T = Type.Object({ a: A, b: B }) + const C = TypeCompiler.Compile(T) + const R = C.Check({ a: null, b: null }) + Assert.IsTrue(R) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + TypeRegistry.Delete('Kind') + }) +}) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 33ee2fb61..c1cb4c70d 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -16,6 +16,7 @@ import './integer' import './intersect' import './iterator' import './keyof' +import './kind' import './literal' import './lowercase' import './not' diff --git a/test/runtime/type/guard/kind.ts b/test/runtime/type/guard/kind.ts new file mode 100644 index 000000000..52c87f883 --- /dev/null +++ b/test/runtime/type/guard/kind.ts @@ -0,0 +1,13 @@ +import { TypeGuard, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TKind', () => { + it('Should guard 1', () => { + const T = { [Kind]: 'Kind' } + Assert.IsTrue(TypeGuard.TKind(T)) + }) + it('Should guard 2', () => { + const T = {} + Assert.IsFalse(TypeGuard.TKind(T)) + }) +}) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index be2225807..911bd9f17 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -4,13 +4,13 @@ import './async-iterator' import './bigint' import './boolean' import './composite' -import './custom' import './date' import './enum' import './integer' import './intersect' import './iterator' import './keyof' +import './kind' import './literal' import './never' import './not' diff --git a/test/runtime/value/cast/custom.ts b/test/runtime/value/cast/kind.ts similarity index 73% rename from test/runtime/value/cast/custom.ts rename to test/runtime/value/cast/kind.ts index 3bfb8959f..42cffc618 100644 --- a/test/runtime/value/cast/custom.ts +++ b/test/runtime/value/cast/kind.ts @@ -2,14 +2,16 @@ import { Value } from '@sinclair/typebox/value' import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/cast/Custom', () => { - before(() => { - TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world') - }) - after(() => { - TypeRegistry.Clear() - }) - const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' }) +describe('value/cast/Kind', () => { + // --------------------------------------------------------- + // Fixtures + // --------------------------------------------------------- + before(() => TypeRegistry.Set('Kind', (schema, value) => value === 'hello' || value === 'world')) + after(() => TypeRegistry.Clear()) + // --------------------------------------------------------- + // Tests + // --------------------------------------------------------- + const T = Type.Unsafe({ [Kind]: 'Kind', default: 'hello' }) const E = 'hello' it('Should upcast from string', () => { const value = 'hello' diff --git a/test/runtime/value/check/custom.ts b/test/runtime/value/check/custom.ts deleted file mode 100644 index 3072822f9..000000000 --- a/test/runtime/value/check/custom.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Value } from '@sinclair/typebox/value' -import { Type, Kind, TypeRegistry } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('type/check/Custom', () => { - const FooBar = Type.Unsafe({ [Kind]: 'FooBar' }) - before(() => { - TypeRegistry.Set('FooBar', (schema, value) => value === 'foobar') - }) - after(() => { - TypeRegistry.Delete('FooBar') - }) - it('Should validate foobar', () => { - Assert.IsEqual(Value.Check(FooBar, 'foobar'), true) - }) - it('Should not validate foobar', () => { - Assert.IsEqual(Value.Check(FooBar, 1), false) - }) - it('Should validate foobar nested', () => { - // prettier-ignore - const T = Type.Object({ x: FooBar }) - Assert.IsEqual(Value.Check(T, { x: 'foobar' }), true) - }) - it('Should not validate foobar nested', () => { - // prettier-ignore - const T = Type.Object({ x: FooBar }) - Assert.IsEqual(Value.Check(T, { x: 1 }), false) - }) -}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 9dac82003..a55d53a0b 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -4,13 +4,13 @@ import './async-iterator' import './bigint' import './boolean' import './composite' -import './custom' import './date' import './enum' import './integer' import './intersect' import './iterator' import './keyof' +import './kind' import './literal' import './never' import './not' diff --git a/test/runtime/value/check/kind.ts b/test/runtime/value/check/kind.ts new file mode 100644 index 000000000..91ad48a15 --- /dev/null +++ b/test/runtime/value/check/kind.ts @@ -0,0 +1,70 @@ +import { Value } from '@sinclair/typebox/value' +import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox' +import { Assert } from '../../assert' + +describe('value/check/Kind', () => { + // ------------------------------------------------------------ + // Fixtures + // ------------------------------------------------------------ + beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI)) + afterEach(() => TypeRegistry.Delete('PI')) + // ------------------------------------------------------------ + // Tests + // ------------------------------------------------------------ + it('Should validate', () => { + const T = Type.Unsafe({ [Kind]: 'PI' }) + Assert.IsTrue(Value.Check(T, Math.PI)) + }) + it('Should not validate', () => { + const T = Type.Unsafe({ [Kind]: 'PI' }) + Assert.IsFalse(Value.Check(T, Math.PI * 2)) + }) + it('Should validate in object', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'PI' }), + }) + Assert.IsTrue(Value.Check(T, { x: Math.PI })) + }) + it('Should not validate in object', () => { + const T = Type.Object({ + x: Type.Unsafe({ [Kind]: 'PI' }), + }) + Assert.IsFalse(Value.Check(T, { x: Math.PI * 2 })) + }) + it('Should validate in array', () => { + const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' })) + Assert.IsTrue(Value.Check(T, [Math.PI])) + }) + it('Should not validate in array', () => { + const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' })) + Assert.IsFalse(Value.Check(T, [Math.PI * 2])) + }) + it('Should validate in tuple', () => { + const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })]) + Assert.IsTrue(Value.Check(T, [Math.PI])) + }) + it('Should not validate in tuple', () => { + const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })]) + Assert.IsFalse(Value.Check(T, [Math.PI * 2])) + }) + // ------------------------------------------------------------ + // Instances + // ------------------------------------------------------------ + it('Should receive kind instance on registry callback', () => { + const stack: string[] = [] + TypeRegistry.Set('Kind', (schema: unknown) => { + // prettier-ignore + return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string') + ? (() => { stack.push(schema.$id); return true })() + : false + }) + const A = { [Kind]: 'Kind', $id: 'A' } as TSchema + const B = { [Kind]: 'Kind', $id: 'B' } as TSchema + const T = Type.Object({ a: A, b: B }) + const R = Value.Check(T, { a: null, b: null }) + Assert.IsTrue(R) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + TypeRegistry.Delete('Kind') + }) +}) diff --git a/test/runtime/value/convert/custom.ts b/test/runtime/value/convert/custom.ts deleted file mode 100644 index e284fbe82..000000000 --- a/test/runtime/value/convert/custom.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Value } from '@sinclair/typebox/value' -import { TypeSystem } from '@sinclair/typebox/system' -import { Assert } from '../../assert/index' - -describe('value/convert/Custom', () => { - it('Should not convert 1', () => { - const Custom = TypeSystem.Type('type/convert/Custom/1', () => true) - const T = Custom() - const R = Value.Convert(T, true) - Assert.IsEqual(R, true) - }) - it('Should not convert 2', () => { - const Custom = TypeSystem.Type('type/convert/Custom/2', () => true) - const T = Custom() - const R = Value.Convert(T, 42) - Assert.IsEqual(R, 42) - }) - it('Should not convert 3', () => { - const Custom = TypeSystem.Type('type/convert/Custom/3', () => true) - const T = Custom() - const R = Value.Convert(T, 'hello') - Assert.IsEqual(R, 'hello') - }) -}) diff --git a/test/runtime/value/convert/index.ts b/test/runtime/value/convert/index.ts index 8514f77cc..09f006f09 100644 --- a/test/runtime/value/convert/index.ts +++ b/test/runtime/value/convert/index.ts @@ -5,7 +5,7 @@ import './bigint' import './boolean' import './composite' import './constructor' -import './custom' +import './kind' import './date' import './enum' import './function' diff --git a/test/runtime/value/convert/kind.ts b/test/runtime/value/convert/kind.ts new file mode 100644 index 000000000..116178c9b --- /dev/null +++ b/test/runtime/value/convert/kind.ts @@ -0,0 +1,39 @@ +import { Value } from '@sinclair/typebox/value' +import { TypeRegistry, Kind, TSchema } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Kind', () => { + // --------------------------------------------------------- + // Fixtures + // --------------------------------------------------------- + beforeEach(() => TypeRegistry.Set('Kind', () => true)) + afterEach(() => TypeRegistry.Delete('Kind')) + // --------------------------------------------------------- + // Test + // --------------------------------------------------------- + it('Should not convert value 1', () => { + const T = { [Kind]: 'Kind' } as TSchema + const R = Value.Convert(T, true) + Assert.IsEqual(R, true) + }) + it('Should not convert value 2', () => { + const T = { [Kind]: 'Kind' } as TSchema + const R = Value.Convert(T, 42) + Assert.IsEqual(R, 42) + }) + it('Should not convert value 3', () => { + const T = { [Kind]: 'Kind' } as TSchema + const R = Value.Convert(T, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should not convert value 4', () => { + const T = { [Kind]: 'Kind' } as TSchema + const R = Value.Convert(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should not convert value 5', () => { + const T = { [Kind]: 'Kind' } as TSchema + const R = Value.Convert(T, [0, 1]) + Assert.IsEqual(R, [0, 1]) + }) +}) diff --git a/test/runtime/value/create/custom.ts b/test/runtime/value/create/custom.ts deleted file mode 100644 index 8756a9c0a..000000000 --- a/test/runtime/value/create/custom.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Value } from '@sinclair/typebox/value' -import { Type, Kind, TypeRegistry } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('value/create/Custom', () => { - it('Should create custom value with default', () => { - TypeRegistry.Set('CustomCreate1', () => true) - const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' }) - Assert.IsEqual(Value.Create(T), 'hello') - }) - - it('Should throw when no default value is specified', () => { - TypeRegistry.Set('CustomCreate2', () => true) - const T = Type.Unsafe({ [Kind]: 'CustomCreate2' }) - Assert.Throws(() => Value.Create(T)) - }) -}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 385f53f1e..9bb482979 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -4,7 +4,6 @@ import './async-iterator' import './bigint' import './boolean' import './composite' -import './custom' import './constructor' import './enum' import './function' @@ -12,6 +11,7 @@ import './integer' import './intersect' import './iterator' import './keyof' +import './kind' import './literal' import './never' import './not' diff --git a/test/runtime/value/create/kind.ts b/test/runtime/value/create/kind.ts new file mode 100644 index 000000000..3bb42d242 --- /dev/null +++ b/test/runtime/value/create/kind.ts @@ -0,0 +1,23 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Kind', () => { + // --------------------------------------------------------- + // Fixtures + // --------------------------------------------------------- + beforeEach(() => TypeRegistry.Set('Kind', () => true)) + afterEach(() => TypeRegistry.Delete('Kind')) + // --------------------------------------------------------- + // Tests + // --------------------------------------------------------- + it('Should create custom value with default', () => { + const T = Type.Unsafe({ [Kind]: 'Kind', default: 'hello' }) + Assert.IsEqual(Value.Create(T), 'hello') + }) + it('Should throw when no default value is specified', () => { + TypeRegistry.Set('Kind', () => true) + const T = Type.Unsafe({ [Kind]: 'Kind' }) + Assert.Throws(() => Value.Create(T)) + }) +}) From 8af419e37544ceb2916cff4dd3e15d45874768da Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 5 Aug 2023 21:39:31 +0900 Subject: [PATCH 170/369] Retain Registry Kinds on Compile (#523) --- package-lock.json | 4 +-- package.json | 2 +- src/compiler/compiler.ts | 5 ++-- test/runtime/compiler/kind.ts | 51 ++++++++++++++++++++++++++++++++ test/runtime/value/check/kind.ts | 37 +++++++++++++++++++++++ 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e71d5adf3..45c146d60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.30.3", + "version": "0.30.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.30.3", + "version": "0.30.4", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index bee3ce854..2b88f0e00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.30.3", + "version": "0.30.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index c0bac349e..1fd7f1def 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -545,9 +545,10 @@ export namespace TypeCompiler { export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { const generatedCode = Code(schema, references, { language: 'javascript' }) const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) + const instances = new Map(state.instances) function typeRegistryFunction(kind: string, instance: number, value: unknown) { - if (!Types.TypeRegistry.Has(kind) || !state.instances.has(instance)) return false - const schema = state.instances.get(instance) + if (!Types.TypeRegistry.Has(kind) || !instances.has(instance)) return false + const schema = instances.get(instance) const checkFunc = Types.TypeRegistry.Get(kind)! return checkFunc(schema, value) } diff --git a/test/runtime/compiler/kind.ts b/test/runtime/compiler/kind.ts index 972c5def2..2b500cf5d 100644 --- a/test/runtime/compiler/kind.ts +++ b/test/runtime/compiler/kind.ts @@ -69,4 +69,55 @@ describe('type/compiler/Kind', () => { Assert.IsEqual(stack[1], 'B') TypeRegistry.Delete('Kind') }) + // ------------------------------------------------------------ + // Instances Retain + // ------------------------------------------------------------ + it('Should retain kind instances on subsequent compile', () => { + let stack: string[] = [] + TypeRegistry.Set('Kind', (schema: unknown) => { + // prettier-ignore + return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string') + ? (() => { stack.push(schema.$id); return true })() + : false + }) + const A = { [Kind]: 'Kind', $id: 'A' } as TSchema + const B = { [Kind]: 'Kind', $id: 'B' } as TSchema + const C = { [Kind]: 'Kind', $id: 'C' } as TSchema + const D = { [Kind]: 'Kind', $id: 'D' } as TSchema + const T1 = Type.Object({ a: A, b: B }) + const T2 = Type.Object({ a: C, b: D }) + + // Compile T1 and run check, expect A and B + const C1 = TypeCompiler.Compile(T1) + const R1 = C1.Check({ a: null, b: null }) + Assert.IsTrue(R1) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + stack = [] + // compile T2 and force instance.clear() + const C2 = TypeCompiler.Compile(T2) + // run T1 check + const R2 = C1.Check({ a: null, b: null }) + Assert.IsTrue(R2) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + stack = [] + // run T2 check + const R3 = C2.Check({ a: null, b: null }) + Assert.IsTrue(R3) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'C') + Assert.IsEqual(stack[1], 'D') + stack = [] + // run T1 check + const R4 = C1.Check({ a: null, b: null }) + Assert.IsTrue(R4) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + stack = [] + TypeRegistry.Delete('Kind') + }) }) diff --git a/test/runtime/value/check/kind.ts b/test/runtime/value/check/kind.ts index 91ad48a15..9a16eed65 100644 --- a/test/runtime/value/check/kind.ts +++ b/test/runtime/value/check/kind.ts @@ -67,4 +67,41 @@ describe('value/check/Kind', () => { Assert.IsEqual(stack[1], 'B') TypeRegistry.Delete('Kind') }) + it('Should retain kind instances on subsequent check', () => { + let stack: string[] = [] + TypeRegistry.Set('Kind', (schema: unknown) => { + // prettier-ignore + return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string') + ? (() => { stack.push(schema.$id); return true })() + : false + }) + const A = { [Kind]: 'Kind', $id: 'A' } as TSchema + const B = { [Kind]: 'Kind', $id: 'B' } as TSchema + const C = { [Kind]: 'Kind', $id: 'C' } as TSchema + const D = { [Kind]: 'Kind', $id: 'D' } as TSchema + const T1 = Type.Object({ a: A, b: B }) + const T2 = Type.Object({ a: C, b: D }) + // run T1 check + const R2 = Value.Check(T1, { a: null, b: null }) + Assert.IsTrue(R2) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + stack = [] + // run T2 check + const R3 = Value.Check(T2, { a: null, b: null }) + Assert.IsTrue(R3) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'C') + Assert.IsEqual(stack[1], 'D') + stack = [] + // run T1 check + const R4 = Value.Check(T1, { a: null, b: null }) + Assert.IsTrue(R4) + Assert.IsEqual(stack.length, 2) + Assert.IsEqual(stack[0], 'A') + Assert.IsEqual(stack[1], 'B') + stack = [] + TypeRegistry.Delete('Kind') + }) }) From 920343cbc7e987b432595088d1b002c11a52a9cb Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 5 Aug 2023 21:41:56 +0900 Subject: [PATCH 171/369] Documentation --- readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index f3f6c2c3d..3cec6819c 100644 --- a/readme.md +++ b/readme.md @@ -110,7 +110,7 @@ License MIT - [Types](#typesystem-types) - [Formats](#typesystem-formats) - [Policies](#typesystem-policies) -- [Transform](#transform) +- [Workbench](#workbench) - [Ecosystem](#ecosystem) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) @@ -1516,13 +1516,13 @@ TypeSystem.AllowArrayObjects = true TypeSystem.AllowNaN = true ``` - + -## TypeBox Transform +## TypeBox Workbench TypeBox offers a small web based code generation tool that can be used to convert TypeScript types into TypeBox types as well as a variety of other runtime type representations. -[TypeBox Transform Link Here](https://sinclairzx81.github.io/typebox-transform/) +[TypeBox Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) From 18d1cf7ac57c8b5f0292cb02ddbd1707e86c8c4c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 12 Aug 2023 06:31:50 +0900 Subject: [PATCH 172/369] Revision 0.31.0 (#525) --- .../compression/module/typebox-errors.ts | 4 +- changelog/0.31.0.md | 331 +++ examples/collections/array.ts | 6 +- examples/collections/map.ts | 6 +- examples/collections/set.ts | 4 +- examples/formats/date-time.ts | 39 + examples/formats/date.ts | 44 + examples/formats/email.ts | 35 + examples/formats/index.ts | 37 +- examples/formats/ipv4.ts | 35 + examples/formats/ipv6.ts | 36 + examples/formats/readme.md | 19 - examples/formats/standard.ts | 74 - examples/formats/time.ts | 48 + examples/formats/url.ts | 36 + .../{transform/index.ts => formats/uuid.ts} | 14 +- examples/transform/readme.md | 83 - examples/transform/transform.ts | 350 ---- examples/typedef/typedef.ts | 26 +- hammer.mjs | 8 +- package-lock.json | 604 +++--- package.json | 8 +- readme.md | 724 ++++--- src/compiler/compiler.ts | 203 +- src/compiler/index.ts | 2 +- src/errors/errors.ts | 430 ++-- src/system/system.ts | 219 +- src/typebox.ts | 1798 ++++++++++------- src/value/cast.ts | 224 +- src/value/check.ts | 160 +- src/value/clone.ts | 40 +- src/value/convert.ts | 154 +- src/value/create.ts | 116 +- src/value/delta.ts | 53 +- src/value/deref.ts | 41 + src/value/equal.ts | 29 +- src/value/guard.ts | 2 +- src/value/hash.ts | 28 +- src/value/mutate.ts | 36 +- src/value/pointer.ts | 4 +- src/value/transform.ts | 473 +++++ src/value/value.ts | 22 + test/runtime/assert/assert.ts | 4 +- test/runtime/compiler/any.ts | 2 +- test/runtime/compiler/array.ts | 2 +- test/runtime/compiler/async-iterator.ts | 2 +- test/runtime/compiler/bigint.ts | 2 +- test/runtime/compiler/boolean.ts | 2 +- test/runtime/compiler/composite.ts | 2 +- test/runtime/compiler/date.ts | 11 +- test/runtime/compiler/enum.ts | 2 +- test/runtime/compiler/index.ts | 1 - test/runtime/compiler/integer.ts | 2 +- test/runtime/compiler/intersect.ts | 4 +- test/runtime/compiler/iterator.ts | 2 +- test/runtime/compiler/keyof.ts | 3 +- test/runtime/compiler/kind.ts | 2 +- test/runtime/compiler/literal.ts | 2 +- test/runtime/compiler/modifier.ts | 3 - test/runtime/compiler/never.ts | 2 +- test/runtime/compiler/not.ts | 2 +- test/runtime/compiler/null.ts | 2 +- test/runtime/compiler/number.ts | 2 +- test/runtime/compiler/object.ts | 2 +- test/runtime/compiler/omit.ts | 2 +- test/runtime/compiler/optional.ts | 2 +- test/runtime/compiler/partial.ts | 2 +- test/runtime/compiler/pick.ts | 2 +- test/runtime/compiler/readonly-optional.ts | 2 +- test/runtime/compiler/readonly.ts | 2 +- test/runtime/compiler/record.ts | 2 +- test/runtime/compiler/recursive.ts | 2 +- test/runtime/compiler/ref.ts | 2 +- test/runtime/compiler/regexp.ts | 2 +- test/runtime/compiler/required.ts | 2 +- test/runtime/compiler/string.ts | 2 +- test/runtime/compiler/symbol.ts | 2 +- test/runtime/compiler/template-literal.ts | 2 +- test/runtime/compiler/tuple.ts | 3 +- test/runtime/compiler/uint8array.ts | 2 +- test/runtime/compiler/unicode.ts | 2 +- test/runtime/compiler/union.ts | 2 +- test/runtime/compiler/unknown.ts | 4 +- test/runtime/compiler/void.ts | 2 +- test/runtime/errors/index.ts | 3 +- test/runtime/errors/iterator/index.ts | 1 + .../runtime/errors/{ => iterator}/iterator.ts | 2 +- test/runtime/errors/types/array-contains.ts | 17 + .../errors/types/array-max-contains.ts | 36 + test/runtime/errors/types/array-max-items.ts | 22 + .../errors/types/array-min-contains.ts | 28 + test/runtime/errors/types/array-min-items.ts | 22 + .../errors/types/array-unique-items.ts | 21 + test/runtime/errors/types/array.ts | 27 + test/runtime/errors/types/async-iterator.ts | 17 + .../errors/types/bigint-exclusive-maximum.ts | 17 + .../errors/types/bigint-exclusive-minimum.ts | 17 + test/runtime/errors/types/bigint-maximum.ts | 17 + test/runtime/errors/types/bigint-minimum.ts | 17 + .../errors/types/bigint-multiple-of.ts | 17 + test/runtime/errors/types/bigint.ts | 17 + test/runtime/errors/types/boolean.ts | 17 + .../types/date-exclusive-maximum-timestamp.ts | 17 + .../types/date-exclusive-minimum-timestamp.ts | 17 + .../errors/types/date-maximum-timestamp.ts | 17 + .../errors/types/date-minimum-timestamp.ts | 17 + .../types/date-multiple-of-timestamp.ts | 17 + test/runtime/errors/types/date.ts | 17 + test/runtime/errors/types/function.ts | 17 + test/runtime/errors/types/index.ts | 63 + .../errors/types/integer-exclusive-maximum.ts | 17 + .../errors/types/integer-exclusive-minimum.ts | 17 + test/runtime/errors/types/integer-maximum.ts | 17 + test/runtime/errors/types/integer-minimum.ts | 17 + .../errors/types/integer-multiple-of.ts | 17 + test/runtime/errors/types/integer.ts | 17 + .../types/intersect-unevaluated-properties.ts | 36 + test/runtime/errors/types/intersect.ts | 24 + test/runtime/errors/types/iterator.ts | 17 + test/runtime/errors/types/kind.ts | 25 + test/runtime/errors/types/literal.ts | 46 + test/runtime/errors/types/never.ts | 13 + test/runtime/errors/types/not.ts | 17 + test/runtime/errors/types/null.ts | 17 + .../errors/types/number-exclusive-maximum.ts | 17 + .../errors/types/number-exclusive-minimum.ts | 17 + test/runtime/errors/types/number-maximum.ts | 17 + test/runtime/errors/types/number-minimum.ts | 17 + .../errors/types/number-multiple-of.ts | 17 + test/runtime/errors/types/number.ts | 17 + .../types/object-additional-properties.ts | 48 + .../errors/types/object-max-properties.ts | 17 + .../errors/types/object-min-properties.ts | 17 + .../errors/types/object-required-property.ts | 22 + test/runtime/errors/types/object.ts | 65 + test/runtime/errors/types/promise.ts | 17 + test/runtime/errors/types/resolve.ts | 7 + .../errors/types/string-format-unknown.ts | 13 + test/runtime/errors/types/string-format.ts | 25 + .../runtime/errors/types/string-max-length.ts | 17 + .../runtime/errors/types/string-min-length.ts | 17 + test/runtime/errors/types/string-pattern.ts | 17 + test/runtime/errors/types/string.ts | 17 + test/runtime/errors/types/symbol.ts | 17 + test/runtime/errors/types/tuple-length.ts | 56 + test/runtime/errors/types/tuple.ts | 17 + .../types/uint8array-max-byte-length.ts | 17 + .../types/uint8array-min-byte-length.ts | 17 + test/runtime/errors/types/uint8array.ts | 17 + test/runtime/errors/types/undefined.ts | 17 + test/runtime/errors/types/union.ts | 21 + test/runtime/errors/types/void.ts | 32 + test/runtime/system/index.ts | 3 +- .../system/policy/allow-array-object.ts | 65 + test/runtime/system/policy/allow-nan.ts | 62 + test/runtime/system/policy/allow-null-void.ts | 31 + .../policy/exact-optional-property-types.ts | 39 + test/runtime/system/policy/index.ts | 4 + test/runtime/system/system.ts | 205 -- test/runtime/system/type/format.ts | 19 + test/runtime/system/type/index.ts | 2 + test/runtime/system/type/type.ts | 22 + test/runtime/type/guard/date.ts | 23 +- test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/intersect.ts | 9 +- test/runtime/type/guard/omit.ts | 17 +- test/runtime/type/guard/partial.ts | 16 +- test/runtime/type/guard/pick.ts | 17 +- test/runtime/type/guard/record.ts | 24 +- test/runtime/type/guard/recursive.ts | 16 + test/runtime/type/guard/required.ts | 16 +- test/runtime/type/guard/rest.ts | 67 +- test/runtime/type/intrinsic/intrinsic.ts | 5 +- test/runtime/type/normalize/exclude.ts | 2 +- test/runtime/type/normalize/extract.ts | 2 +- test/runtime/type/normalize/indexed.ts | 2 +- test/runtime/type/normalize/intersect.ts | 2 +- test/runtime/type/normalize/record.ts | 2 +- test/runtime/type/normalize/union.ts | 2 +- test/runtime/type/registry/format.ts | 2 +- test/runtime/type/registry/type.ts | 2 +- test/runtime/type/template/finite.ts | 2 +- test/runtime/type/template/generate.ts | 2 +- test/runtime/type/template/parser.ts | 2 +- test/runtime/type/template/pattern.ts | 2 +- test/runtime/value/cast/regexp.ts | 2 +- test/runtime/value/cast/void.ts | 6 +- test/runtime/value/check/date.ts | 53 +- test/runtime/value/convert/async-iterator.ts | 3 - test/runtime/value/convert/constructor.ts | 5 +- test/runtime/value/convert/function.ts | 3 - test/runtime/value/convert/integer.ts | 2 +- test/runtime/value/convert/iterator.ts | 3 - test/runtime/value/convert/literal.ts | 6 +- test/runtime/value/convert/number.ts | 2 +- test/runtime/value/convert/promise.ts | 6 +- test/runtime/value/convert/record.ts | 2 +- test/runtime/value/convert/regexp.ts | 2 +- test/runtime/value/convert/symbol.ts | 8 +- test/runtime/value/convert/tuple.ts | 2 +- test/runtime/value/create/array.ts | 2 +- test/runtime/value/create/string.ts | 2 +- test/runtime/value/create/void.ts | 2 +- test/runtime/value/index.ts | 1 + test/runtime/value/transform/_nested.ts | 52 + test/runtime/value/transform/any.ts | 34 + test/runtime/value/transform/array.ts | 97 + .../runtime/value/transform/async-iterator.ts | 40 + test/runtime/value/transform/bigint.ts | 40 + test/runtime/value/transform/boolean.ts | 40 + test/runtime/value/transform/constructor.ts | 40 + test/runtime/value/transform/date.ts | 40 + test/runtime/value/transform/function.ts | 40 + test/runtime/value/transform/index.ts | 31 + test/runtime/value/transform/integer.ts | 40 + test/runtime/value/transform/intersect.ts | 122 ++ test/runtime/value/transform/iterator.ts | 40 + test/runtime/value/transform/literal.ts | 71 + test/runtime/value/transform/never.ts | 33 + test/runtime/value/transform/not.ts | 40 + test/runtime/value/transform/null.ts | 40 + test/runtime/value/transform/number.ts | 40 + test/runtime/value/transform/object.ts | 153 ++ test/runtime/value/transform/promise.ts | 40 + test/runtime/value/transform/record.ts | 123 ++ test/runtime/value/transform/recursive.ts | 131 ++ test/runtime/value/transform/ref.ts | 62 + test/runtime/value/transform/string.ts | 40 + test/runtime/value/transform/symbol.ts | 40 + .../value/transform/template-literal.ts | 40 + test/runtime/value/transform/tuple.ts | 98 + test/runtime/value/transform/undefined.ts | 40 + test/runtime/value/transform/union.ts | 170 ++ test/runtime/value/transform/unknown.ts | 34 + test/runtime/value/transform/unsafe.ts | 46 + test/runtime/value/transform/void.ts | 40 + test/static/any.ts | 2 +- test/static/array.ts | 8 +- test/static/assert.ts | 9 +- test/static/async-iterator.ts | 2 +- test/static/awaited.ts | 14 +- test/static/bigint.ts | 2 +- test/static/boolean.ts | 2 +- test/static/capitalize.ts | 10 +- test/static/composite.ts | 14 +- test/static/constructor-parameters.ts | 2 +- test/static/constructor.ts | 2 +- test/static/date.ts | 2 +- test/static/enum.ts | 2 +- test/static/exclude.ts | 24 +- test/static/extract.ts | 24 +- test/static/function.ts | 2 +- test/static/index.ts | 1 + test/static/indexed.ts | 51 +- test/static/intersect.ts | 8 +- test/static/iterator.ts | 2 +- test/static/keyof.ts | 16 +- test/static/literal.ts | 6 +- test/static/lowercase.ts | 10 +- test/static/modifier.ts | 2 +- test/static/never.ts | 2 +- test/static/not.ts | 6 +- test/static/null.ts | 2 +- test/static/number.ts | 2 +- test/static/object.ts | 6 +- test/static/omit.ts | 12 +- test/static/optional.ts | 2 +- test/static/parameters.ts | 2 +- test/static/partial.ts | 6 +- test/static/pick.ts | 14 +- test/static/readonly-optional.ts | 2 +- test/static/readonly.ts | 2 +- test/static/record.ts | 18 +- test/static/recursive.ts | 12 +- test/static/ref.ts | 4 +- test/static/regexp.ts | 2 +- test/static/required.ts | 6 +- test/static/rest.ts | 56 +- test/static/return-type.ts | 4 +- test/static/strict.ts | 2 +- test/static/string.ts | 2 +- test/static/symbol.ts | 2 +- test/static/template-literal.ts | 22 +- test/static/transform.ts | 184 ++ test/static/tuple.ts | 2 +- test/static/uncapitalize.ts | 10 +- test/static/union.ts | 8 +- test/static/unknown.ts | 2 +- test/static/uppercase.ts | 10 +- tsconfig.json | 3 + 290 files changed, 8014 insertions(+), 3458 deletions(-) create mode 100644 changelog/0.31.0.md create mode 100644 examples/formats/date-time.ts create mode 100644 examples/formats/date.ts create mode 100644 examples/formats/email.ts create mode 100644 examples/formats/ipv4.ts create mode 100644 examples/formats/ipv6.ts delete mode 100644 examples/formats/readme.md delete mode 100644 examples/formats/standard.ts create mode 100644 examples/formats/time.ts create mode 100644 examples/formats/url.ts rename examples/{transform/index.ts => formats/uuid.ts} (76%) delete mode 100644 examples/transform/readme.md delete mode 100644 examples/transform/transform.ts create mode 100644 src/value/deref.ts create mode 100644 src/value/transform.ts delete mode 100644 test/runtime/compiler/modifier.ts create mode 100644 test/runtime/errors/iterator/index.ts rename test/runtime/errors/{ => iterator}/iterator.ts (96%) create mode 100644 test/runtime/errors/types/array-contains.ts create mode 100644 test/runtime/errors/types/array-max-contains.ts create mode 100644 test/runtime/errors/types/array-max-items.ts create mode 100644 test/runtime/errors/types/array-min-contains.ts create mode 100644 test/runtime/errors/types/array-min-items.ts create mode 100644 test/runtime/errors/types/array-unique-items.ts create mode 100644 test/runtime/errors/types/array.ts create mode 100644 test/runtime/errors/types/async-iterator.ts create mode 100644 test/runtime/errors/types/bigint-exclusive-maximum.ts create mode 100644 test/runtime/errors/types/bigint-exclusive-minimum.ts create mode 100644 test/runtime/errors/types/bigint-maximum.ts create mode 100644 test/runtime/errors/types/bigint-minimum.ts create mode 100644 test/runtime/errors/types/bigint-multiple-of.ts create mode 100644 test/runtime/errors/types/bigint.ts create mode 100644 test/runtime/errors/types/boolean.ts create mode 100644 test/runtime/errors/types/date-exclusive-maximum-timestamp.ts create mode 100644 test/runtime/errors/types/date-exclusive-minimum-timestamp.ts create mode 100644 test/runtime/errors/types/date-maximum-timestamp.ts create mode 100644 test/runtime/errors/types/date-minimum-timestamp.ts create mode 100644 test/runtime/errors/types/date-multiple-of-timestamp.ts create mode 100644 test/runtime/errors/types/date.ts create mode 100644 test/runtime/errors/types/function.ts create mode 100644 test/runtime/errors/types/index.ts create mode 100644 test/runtime/errors/types/integer-exclusive-maximum.ts create mode 100644 test/runtime/errors/types/integer-exclusive-minimum.ts create mode 100644 test/runtime/errors/types/integer-maximum.ts create mode 100644 test/runtime/errors/types/integer-minimum.ts create mode 100644 test/runtime/errors/types/integer-multiple-of.ts create mode 100644 test/runtime/errors/types/integer.ts create mode 100644 test/runtime/errors/types/intersect-unevaluated-properties.ts create mode 100644 test/runtime/errors/types/intersect.ts create mode 100644 test/runtime/errors/types/iterator.ts create mode 100644 test/runtime/errors/types/kind.ts create mode 100644 test/runtime/errors/types/literal.ts create mode 100644 test/runtime/errors/types/never.ts create mode 100644 test/runtime/errors/types/not.ts create mode 100644 test/runtime/errors/types/null.ts create mode 100644 test/runtime/errors/types/number-exclusive-maximum.ts create mode 100644 test/runtime/errors/types/number-exclusive-minimum.ts create mode 100644 test/runtime/errors/types/number-maximum.ts create mode 100644 test/runtime/errors/types/number-minimum.ts create mode 100644 test/runtime/errors/types/number-multiple-of.ts create mode 100644 test/runtime/errors/types/number.ts create mode 100644 test/runtime/errors/types/object-additional-properties.ts create mode 100644 test/runtime/errors/types/object-max-properties.ts create mode 100644 test/runtime/errors/types/object-min-properties.ts create mode 100644 test/runtime/errors/types/object-required-property.ts create mode 100644 test/runtime/errors/types/object.ts create mode 100644 test/runtime/errors/types/promise.ts create mode 100644 test/runtime/errors/types/resolve.ts create mode 100644 test/runtime/errors/types/string-format-unknown.ts create mode 100644 test/runtime/errors/types/string-format.ts create mode 100644 test/runtime/errors/types/string-max-length.ts create mode 100644 test/runtime/errors/types/string-min-length.ts create mode 100644 test/runtime/errors/types/string-pattern.ts create mode 100644 test/runtime/errors/types/string.ts create mode 100644 test/runtime/errors/types/symbol.ts create mode 100644 test/runtime/errors/types/tuple-length.ts create mode 100644 test/runtime/errors/types/tuple.ts create mode 100644 test/runtime/errors/types/uint8array-max-byte-length.ts create mode 100644 test/runtime/errors/types/uint8array-min-byte-length.ts create mode 100644 test/runtime/errors/types/uint8array.ts create mode 100644 test/runtime/errors/types/undefined.ts create mode 100644 test/runtime/errors/types/union.ts create mode 100644 test/runtime/errors/types/void.ts create mode 100644 test/runtime/system/policy/allow-array-object.ts create mode 100644 test/runtime/system/policy/allow-nan.ts create mode 100644 test/runtime/system/policy/allow-null-void.ts create mode 100644 test/runtime/system/policy/exact-optional-property-types.ts create mode 100644 test/runtime/system/policy/index.ts delete mode 100644 test/runtime/system/system.ts create mode 100644 test/runtime/system/type/format.ts create mode 100644 test/runtime/system/type/index.ts create mode 100644 test/runtime/system/type/type.ts create mode 100644 test/runtime/type/guard/recursive.ts create mode 100644 test/runtime/value/transform/_nested.ts create mode 100644 test/runtime/value/transform/any.ts create mode 100644 test/runtime/value/transform/array.ts create mode 100644 test/runtime/value/transform/async-iterator.ts create mode 100644 test/runtime/value/transform/bigint.ts create mode 100644 test/runtime/value/transform/boolean.ts create mode 100644 test/runtime/value/transform/constructor.ts create mode 100644 test/runtime/value/transform/date.ts create mode 100644 test/runtime/value/transform/function.ts create mode 100644 test/runtime/value/transform/index.ts create mode 100644 test/runtime/value/transform/integer.ts create mode 100644 test/runtime/value/transform/intersect.ts create mode 100644 test/runtime/value/transform/iterator.ts create mode 100644 test/runtime/value/transform/literal.ts create mode 100644 test/runtime/value/transform/never.ts create mode 100644 test/runtime/value/transform/not.ts create mode 100644 test/runtime/value/transform/null.ts create mode 100644 test/runtime/value/transform/number.ts create mode 100644 test/runtime/value/transform/object.ts create mode 100644 test/runtime/value/transform/promise.ts create mode 100644 test/runtime/value/transform/record.ts create mode 100644 test/runtime/value/transform/recursive.ts create mode 100644 test/runtime/value/transform/ref.ts create mode 100644 test/runtime/value/transform/string.ts create mode 100644 test/runtime/value/transform/symbol.ts create mode 100644 test/runtime/value/transform/template-literal.ts create mode 100644 test/runtime/value/transform/tuple.ts create mode 100644 test/runtime/value/transform/undefined.ts create mode 100644 test/runtime/value/transform/union.ts create mode 100644 test/runtime/value/transform/unknown.ts create mode 100644 test/runtime/value/transform/unsafe.ts create mode 100644 test/runtime/value/transform/void.ts create mode 100644 test/static/transform.ts diff --git a/benchmark/compression/module/typebox-errors.ts b/benchmark/compression/module/typebox-errors.ts index 4a6324bbb..f1de81528 100644 --- a/benchmark/compression/module/typebox-errors.ts +++ b/benchmark/compression/module/typebox-errors.ts @@ -1,3 +1,3 @@ -import * as ValueErrors from '@sinclair/typebox/errors' +import * as Errors from '@sinclair/typebox/errors' -console.log(ValueErrors) +console.log(Errors) diff --git a/changelog/0.31.0.md b/changelog/0.31.0.md new file mode 100644 index 000000000..4dc83aa48 --- /dev/null +++ b/changelog/0.31.0.md @@ -0,0 +1,331 @@ +## [0.31.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.31.0) + +## Overview + +Revision 0.31.0 is a subsequent milestone revision for the TypeBox library and a direct continuation of the work carried out in 0.30.0 to optimize and prepare TypeBox for a 1.0 release candidate. This revision implements a new codec system with Transform types, provides configurable error message generation for i18n support, adds a library wide exception type named TypeBoxError and generalizes the Rest type to enable richer composition. This revision also finalizes optimization work to reduce the TypeBox package size. + +This revision contains relatively minor breaking changes due to internal type renaming. A minor semver revision is required. + +## Contents + +- Enhancements + - [Transform Types](#Transform-Types) + - [Encode and Decode](#Encode-and-Decode) + - [StaticEncode and StaticDecode](#StaticEncode-and-StaticDecode) + - [Rest Types](#Rest-Types) + - [Record Key](#Record-Key) + - [TypeBoxError](#TypeBoxError) + - [TypeSystemErrorFunction](#TypeSystemErrorFunction) + - [Reduce Package Size](#Reduce-Package-Size) +- Breaking + - [JsonTypeBuilder and JavaScriptTypeBuilder](#JsonTypeBuilder-and-JavaScriptTypeBuilder) + - [TypeSystemPolicy](#TypeSystemPolicy) + + + +## Transform Types + +Revision 0.31.0 includes a new codec system referred to as Transform types. A Transform type is used to augment a regular TypeBox type with Encode and Decode functions. These functions are invoked via the new Encode and Decode functions available on both Value and TypeCompiler modules. + +The following shows a Transform type which increments and decrements a number. + +```typescript +import { Value } from '@sinclair/typebox/value' + +const T = Type.Transform(Type.Number()) // const T = { + .Decode(value => value + 1) // type: 'number', + .Encode(value => value - 1) // [Symbol(TypeBox.Kind)]: 'Number', + // [Symbol(TypeBox.Transform)]: { + // Decode: [Function: Decode], + // Encode: [Function: Encode] + // } + // } + +const A = Value.Decode(T, 0) // const A: number = 1 + +const B = Value.Encode(T, 1) // const B: number = 0 +``` + + + +## Encode and Decode + +Revision 0.31.0 includes new functions to Decode and Encode values. These functions are written in service to Transform types, but can be used equally well without them. These functions return a typed value that matches the type being transformed. TypeBox will infer decode and encode differently, yielding the correct type as derived from the codec implementation. + +The following shows decoding and encoding between number to Date. Note these functions will throw if the value is invalid. + +```typescript +const T = Type.Transform(Type.Number()) + .Decode(value => new Date(value)) // number to Date + .Encode(value => value.getTime()) // Date to number + +// Ok +// +const A = Value.Decode(T, 42) // const A = new Date(42) + +const B = Value.Encode(T, new Date(42)) // const B = 42 + +// Error +// +const C = Value.Decode(T, true) // Error: Expected number + +const D = Value.Encode(T, 'not a date') // Error: getTime is not a function +``` +The Decode function is extremely fast when decoding regular TypeBox types; and TypeBox will by pass codec execution if the type being decoded contains no interior Transforms (and will only use Check). When using Transforms however, these functions may incur a performance penelty due to codecs operating structurally on values using dynamic techniques (as would be the case for applications manually decoding values). As such the Decode design is built to be general and opt in, but not necessarily high performance. + + + +## StaticEncode and StaticDecode + +Revision 0.31.0 includes new inference types `StaticEncode` and `StaticDecode`. These types can be used to infer the encoded and decoded states of a Transform as well as regular TypeBox types. These types can be used to replace `Static` for `Request` and `Response` inference pipelines. + +The following shows an example `Route` function that uses Transform inference via `StaticDecode`. + +```typescript +// Route +// +export type RouteCallback = + (request: StaticDecode) => StaticDecode // replace Static with StaticDecode + +export function Route( + path: TPath, + requestType: TRequest, + responseType: TResponse, + callback: RouteCallback +) { + // route handling here ... + + const input = null // receive input + const request = Value.Decode(requestType, input) + const response = callback(request) + const output = Value.Encode(responseType, response) + // send output +} + +// Route: Without Transform +// +const Timestamp = Type.Number() + +Route('/exampleA', Timestamp, Timestamp, (value) => { + return value // value observed as number +}) + +// Route: With Transform +// +const Timestamp = Type.Transform(Type.Number()) + .Decode(value => new Date(value)) + .Encode(value => value.getTime()) + +Route('/exampleB', Timestamp, Timestamp, (value) => { + return value // value observed as Date +}) +``` + + + +## Rest Types + +Revision 0.31.0 updates the Rest type to support variadic tuple extraction from Union, Intersection and Tuple types. Previously the Rest type was limited to Tuple types only, but has been extended to other types to allow uniform remapping without having to extract types from specific schema representations. + +The following remaps a Tuple into a Union. + +```typescript +const T = Type.Tuple([ // const T = { + Type.String(), // type: 'array', + Type.Number() // items: [ +]) // { type: 'string' }, + // { type: 'number' } + // ], + // additionalItems: false, + // minItems: 2, + // maxItems: 2, + // } + +const R = Type.Rest(T) // const R = [ + // { type: 'string' }, + // { type: 'number' } + // ] + +const U = Type.Union(R) // const U = { + // anyOf: [ + // { type: 'string' }, + // { type: 'number' } + // ] + // } +``` +This type can be used to remap Intersect a Composite + +```typescript +const I = Type.Intersect([ // const I = { + Type.Object({ x: Type.Number() }), // allOf: [{ + Type.Object({ y: Type.Number() }) // type: 'object', +]) // required: ['x'], + // properties: { + // x: { type: 'number' } + // } + // }, { + // type: 'object', + // required: ['y'], + // properties: { + // y: { type: 'number' } + // } + // }] + // } + +const C = Type.Composite(Type.Rest(I)) // const C = { + // type: 'object', + // required: ['x', 'y'], + // properties: { + // 'x': { type: 'number' }, + // 'y': { type: 'number' } + // } + // } +``` + + + +## Record Key + +Revision 0.31.0 updates the inference strategy for Record types and generalizes RecordKey to TSchema. This update aims to help Record types compose better when used with generic functions. The update also removes the overloaded Record factory methods, opting for a full conditional inference path. It also removes the `RecordKey` type which would type error when used with Record overloads. The return type of Record will be TNever if passing an invalid key. Valid Record key types include TNumber, TString, TInteger, TTemplateLiteral, TLiteralString, TLiteralNumber and TUnion. + +```typescript +// 0.30.0 +// +import { RecordKey, TSchema } from '@sinclair/typebox' + +function StrictRecord(K: K, T: T) { + return Type.Record(K, T, { additionalProperties: false }) // Error: RecordKey unresolvable to overload +} +// 0.31.0 +// +import { TSchema } from '@sinclair/typebox' + +function StrictRecord(K: K, T: T) { + return Type.Record(K, T, { additionalProperties: false }) // Ok: dynamically mapped +} + +const A = StrictRecord(Type.String(), Type.Null()) // const A: TRecord + +const B = StrictRecord(Type.Literal('A'), Type.Null()) // const B: TObject<{ A: TNull }> + +const C = StrictRecord(Type.BigInt(), Type.Null()) // const C: TNever +``` + + + +## TypeBoxError + +Revision 0.31.0 updates all errors thrown by TypeBox to extend the sub type `TypeBoxError`. This can be used to help narrow down the source of errors in `try/catch` blocks. + +```typescript +import { Type, TypeBoxError } from '@sinclair/typebox' +import { Value } from '@sinclair/typebox/value' + +try { + const A = Value.Decode(Type.Number(), 'hello') +} catch(error) { + if(error instanceof TypeBoxError) { + // typebox threw this error + } +} +``` + + + +## TypeSystemErrorFunction + +Revision 0.31.0 adds functionality to remap error messages with the TypeSystemErrorFunction. This function is invoked whenever a validation error is generated in TypeBox. The following is an example of a custom TypeSystemErrorFunction using some of the messages TypeBox generates by default. TypeBox also provides the DefaultErrorFunction which can be used for fallthrough cases. + +```typescript +import { TypeSystemErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/system' + +// Example CustomErrorFunction +export function CustomErrorFunction(schema: Types.TSchema, errorType: ValueErrorType) { + switch (errorType) { + case ValueErrorType.ArrayContains: + return 'Expected array to contain at least one matching value' + case ValueErrorType.ArrayMaxContains: + return `Expected array to contain no more than ${schema.maxContains} matching values` + case ValueErrorType.ArrayMinContains: + return `Expected array to contain at least ${schema.minContains} matching values` + ... + default: return DefaultErrorFunction(schema, errorType) + } +} +// Sets the CustomErrorFunction +TypeSystemErrorFunction.Set(CustomErrorFunction) +``` +It is possible to call `.Set()` on the TypeSystemErrorFunction module prior to each call to `.Errors()`. This can be useful for applications that require i18n support in their validation pipelines. + + + +## Reduce Package Size + +Revision 0.31.0 completes a full sweep of code optimizations and modularization to reduce package bundle size. The following table shows the bundle sizes inclusive of the new 0.31.0 functionality against 0.30.0. + +```typescript +// Revision 0.30.0 +// +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '131.4 kb' │ ' 59.4 kb' │ '2.21 x' │ +│ typebox/errors │ '113.6 kb' │ ' 50.9 kb' │ '2.23 x' │ +│ typebox/system │ ' 78.5 kb' │ ' 32.5 kb' │ '2.42 x' │ +│ typebox/value │ '182.8 kb' │ ' 80.0 kb' │ '2.28 x' │ +│ typebox │ ' 77.4 kb' │ ' 32.0 kb' │ '2.42 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ + +// Revision 0.31.0 +// +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '149.5 kb' │ ' 66.1 kb' │ '2.26 x' │ +│ typebox/errors │ '112.1 kb' │ ' 49.4 kb' │ '2.27 x' │ +│ typebox/system │ ' 83.2 kb' │ ' 37.1 kb' │ '2.24 x' │ +│ typebox/value │ '191.1 kb' │ ' 82.7 kb' │ '2.31 x' │ +│ typebox │ ' 73.0 kb' │ ' 31.9 kb' │ '2.29 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ +``` + +Additional code reductions may not be possible without implicating code maintainability. The `typebox` module may however be broken down into sub modules in later revisions to further bolster modularity, but is retained as a single file on this revision for historical reasons (not necessarily technical ones). + + + +## JsonTypeBuilder and JavaScriptTypeBuilder + +Revision 0.31.0 renames the `StandardTypeBuilder` and `ExtendedTypeBuilder` to `JsonTypeBuilder` and `JavaScriptTypeBuilder` respectively. Applications that extend TypeBox's TypeBuilders will need to update to these names. + +```typescript +// 0.30.0 +// +export class ApplicationTypeBuilder extends ExtendedTypeBuilder {} + +// 0.31.0 +// +export class ApplicationTypeBuilder extends JavaScriptTypeBuilder {} +``` +These builders also update the jsdoc comment to `[Json]` and `[JavaScript]` inline with this new naming convention. + + + +## TypeSystemPolicy + +Revision 0.31.0 moves the `TypeSystem.Policy` configurations into a new type named `TypeSystemPolicy`. This change was done to unify internal policy checks used by the Value and Error modules during bundle size optimization; as well as to keep policy configurations contextually separate from the Type and Format API on the TypeSystem module. + +```typescript +// Revision 0.30.0 +// +import { TypeSystem } from '@sinclair/typebox/system' + +TypeSystem.AllowNaN = true + +// Revision 0.31.0 +// +import { TypeSystemPolicy } from '@sinclair/typebox/system' + +TypeSystemPolicy.AllowNaN = true + +TypeSystemPolicy.IsNumberLike(NaN) // true +``` \ No newline at end of file diff --git a/examples/collections/array.ts b/examples/collections/array.ts index 1a7a7550b..90f5d73b4 100644 --- a/examples/collections/array.ts +++ b/examples/collections/array.ts @@ -27,18 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static } from '@sinclair/typebox' +import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeArrayError // ---------------------------------------------------------------- -export class TypeArrayError extends Error { +export class TypeArrayError extends TypeBoxError { constructor(message: string) { super(`${message}`) } } -export class TypeArrayLengthError extends Error { +export class TypeArrayLengthError extends TypeBoxError { constructor() { super('arrayLength not a number') } diff --git a/examples/collections/map.ts b/examples/collections/map.ts index 2fa3dffed..796140fe6 100644 --- a/examples/collections/map.ts +++ b/examples/collections/map.ts @@ -27,18 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static } from '@sinclair/typebox' +import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeMapKeyError // ---------------------------------------------------------------- -export class TypeMapKeyError extends Error { +export class TypeMapKeyError extends TypeBoxError { constructor(message: string) { super(`${message} for key`) } } -export class TypeMapValueError extends Error { +export class TypeMapValueError extends TypeBoxError { constructor(key: unknown, message: string) { super(`${message} for key ${JSON.stringify(key)}`) } diff --git a/examples/collections/set.ts b/examples/collections/set.ts index 4e8d31213..8cf312a3e 100644 --- a/examples/collections/set.ts +++ b/examples/collections/set.ts @@ -27,13 +27,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static } from '@sinclair/typebox' +import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // Errors // ---------------------------------------------------------------- -export class TypeSetError extends Error { +export class TypeSetError extends TypeBoxError { constructor(message: string) { super(`${message}`) } diff --git a/examples/formats/date-time.ts b/examples/formats/date-time.ts new file mode 100644 index 000000000..ed8ac29ac --- /dev/null +++ b/examples/formats/date-time.ts @@ -0,0 +1,39 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IsDate } from './date' +import { IsTime } from './time' + +const DATE_TIME_SEPARATOR = /t|\s/i + +/** + * `[ajv-formats]` ISO8601 DateTime + * @example `2020-12-12T20:20:40+00:00` + */ +export function IsDateTime(value: string, strictTimeZone?: boolean): boolean { + const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) + return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) +} diff --git a/examples/formats/date.ts b/examples/formats/date.ts new file mode 100644 index 000000000..fc006341b --- /dev/null +++ b/examples/formats/date.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] +const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ + +function IsLeapYear(year: number): boolean { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) +} +/** + * `[ajv-formats]` ISO8601 Date component + * @example `2020-12-12` + */ +export function IsDate(value: string): boolean { + const matches: string[] | null = DATE.exec(value) + if (!matches) return false + const year: number = +matches[1] + const month: number = +matches[2] + const day: number = +matches[3] + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) +} diff --git a/examples/formats/email.ts b/examples/formats/email.ts new file mode 100644 index 000000000..430b7e470 --- /dev/null +++ b/examples/formats/email.ts @@ -0,0 +1,35 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const Email = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i + +/** + * `[ajv-formats]` Internet Email Address [RFC 5321, section 4.1.2.](http://tools.ietf.org/html/rfc5321#section-4.1.2) + * @example `user@domain.com` + */ +export function IsEmail(value: string): boolean { + return Email.test(value) +} diff --git a/examples/formats/index.ts b/examples/formats/index.ts index 4251323c5..0b20821fe 100644 --- a/examples/formats/index.ts +++ b/examples/formats/index.ts @@ -1 +1,36 @@ -export * from './standard' +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './date-time' +export * from './date' +export * from './email' +export * from './ipv4' +export * from './ipv6' +export * from './time' +export * from './url' +export * from './uuid' diff --git a/examples/formats/ipv4.ts b/examples/formats/ipv4.ts new file mode 100644 index 000000000..1cf506988 --- /dev/null +++ b/examples/formats/ipv4.ts @@ -0,0 +1,35 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const IPv4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ + +/** + * `[ajv-formats]` IPv4 address according to dotted-quad ABNF syntax as defined in [RFC 2673, section 3.2](http://tools.ietf.org/html/rfc2673#section-3.2) + * @example `192.168.0.1` + */ +export function IsIPv4(value: string): boolean { + return IPv4.test(value) +} diff --git a/examples/formats/ipv6.ts b/examples/formats/ipv6.ts new file mode 100644 index 000000000..819be3ff2 --- /dev/null +++ b/examples/formats/ipv6.ts @@ -0,0 +1,36 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const IPv6 = + /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i + +/** + * `[ajv-formats]` IPv6 address as defined in [RFC 2373, section 2.2](http://tools.ietf.org/html/rfc2373#section-2.2). + * @example `2001:0db8:85a3:0000:0000:8a2e:0370:7334` + */ +export function IsIPv6(value: string): boolean { + return IPv6.test(value) +} diff --git a/examples/formats/readme.md b/examples/formats/readme.md deleted file mode 100644 index 02d7b0c28..000000000 --- a/examples/formats/readme.md +++ /dev/null @@ -1,19 +0,0 @@ -# Formats - -This example provides TypeCompiler supported versions of the `ajv-formats` package. - -## Standard - -The `standard.ts` file provided with this example implements several standard string formats. - -| Format | Description | -| --- | --- | -| `email` | Internet email address, see [RFC 5321, section 4.1.2.](http://tools.ietf.org/html/rfc5321#section-4.1.2) | -| `date-time` | ISO8601 DateTime. example, `2018-11-13T20:20:39+00:00` | -| `date` | ISO8601 Date component, for example, `2018-11-13` | -| `time` | ISO8601 Time component, for example, `20:20:39+00:00` | -| `uuid` | A Universally Unique Identifier as defined by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122). | -| `url` | A uniform resource locator as defined in [RFC 1738](https://www.rfc-editor.org/rfc/rfc1738) -| `ipv4` | IPv4 address, according to dotted-quad ABNF syntax as defined in [RFC 2673, section 3.2](http://tools.ietf.org/html/rfc2673#section-3.2). | -| `ipv6` | IPv6 address, as defined in [RFC 2373, section 2.2](http://tools.ietf.org/html/rfc2373#section-2.2). | - diff --git a/examples/formats/standard.ts b/examples/formats/standard.ts deleted file mode 100644 index 7a0d9b06e..000000000 --- a/examples/formats/standard.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { FormatRegistry } from '@sinclair/typebox' - -// ------------------------------------------------------------------------------------------- -// Format Registration -// ------------------------------------------------------------------------------------------- -FormatRegistry.Set('date-time', (value) => IsDateTime(value, true)) -FormatRegistry.Set('date', (value) => IsDate(value)) -FormatRegistry.Set('time', (value) => IsTime(value)) -FormatRegistry.Set('email', (value) => IsEmail(value)) -FormatRegistry.Set('uuid', (value) => IsUuid(value)) -FormatRegistry.Set('url', (value) => IsUrl(value)) -FormatRegistry.Set('ipv6', (value) => IsIPv6(value)) -FormatRegistry.Set('ipv4', (value) => IsIPv4(value)) - -// ------------------------------------------------------------------------------------------- -// https://github.com/ajv-validator/ajv-formats/blob/master/src/formats.ts -// ------------------------------------------------------------------------------------------- - -const UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i -const DATE_TIME_SEPARATOR = /t|\s/i -const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i -const DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ -const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] -const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/ -const IPV6 = /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i -const URL = /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu -const EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i -function IsLeapYear(year: number): boolean { - return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) -} -function IsDate(str: string): boolean { - const matches: string[] | null = DATE.exec(str) - if (!matches) return false - const year: number = +matches[1] - const month: number = +matches[2] - const day: number = +matches[3] - return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && IsLeapYear(year) ? 29 : DAYS[month]) -} -function IsTime(str: string, strictTimeZone?: boolean): boolean { - const matches: string[] | null = TIME.exec(str) - if (!matches) return false - const hr: number = +matches[1] - const min: number = +matches[2] - const sec: number = +matches[3] - const tz: string | undefined = matches[4] - const tzSign: number = matches[5] === '-' ? -1 : 1 - const tzH: number = +(matches[6] || 0) - const tzM: number = +(matches[7] || 0) - if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false - if (hr <= 23 && min <= 59 && sec < 60) return true - const utcMin = min - tzM * tzSign - const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) - return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 -} -function IsDateTime(value: string, strictTimeZone?: boolean): boolean { - const dateTime: string[] = value.split(DATE_TIME_SEPARATOR) - return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone) -} -function IsEmail(value: string) { - return EMAIL.test(value) -} -function IsUuid(value: string) { - return UUID.test(value) -} -function IsUrl(value: string) { - return URL.test(value) -} -function IsIPv6(value: string) { - return IPV6.test(value) -} -function IsIPv4(value: string) { - return IPV4.test(value) -} - diff --git a/examples/formats/time.ts b/examples/formats/time.ts new file mode 100644 index 000000000..9002f6908 --- /dev/null +++ b/examples/formats/time.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i + +/** + * `[ajv-formats]` ISO8601 Time component + * @example `20:20:39+00:00` + */ +export function IsTime(value: string, strictTimeZone?: boolean): boolean { + const matches: string[] | null = TIME.exec(value) + if (!matches) return false + const hr: number = +matches[1] + const min: number = +matches[2] + const sec: number = +matches[3] + const tz: string | undefined = matches[4] + const tzSign: number = matches[5] === '-' ? -1 : 1 + const tzH: number = +(matches[6] || 0) + const tzM: number = +(matches[7] || 0) + if (tzH > 23 || tzM > 59 || (strictTimeZone && !tz)) return false + if (hr <= 23 && min <= 59 && sec < 60) return true + const utcMin = min - tzM * tzSign + const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0) + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61 +} diff --git a/examples/formats/url.ts b/examples/formats/url.ts new file mode 100644 index 000000000..c01091eda --- /dev/null +++ b/examples/formats/url.ts @@ -0,0 +1,36 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/format + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +const Url = + /^(?:https?|wss?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu + +/** + * `[ajv-formats:deprecated]` A uniform resource locator as defined in [RFC 1738](https://www.rfc-editor.org/rfc/rfc1738) + * @example `http://domain.com` + */ +export function IsUrl(value: string) { + return Url.test(value) +} diff --git a/examples/transform/index.ts b/examples/formats/uuid.ts similarity index 76% rename from examples/transform/index.ts rename to examples/formats/uuid.ts index d0bddc5f1..6213fba69 100644 --- a/examples/transform/index.ts +++ b/examples/formats/uuid.ts @@ -1,11 +1,9 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/transform +@sinclair/typebox/format The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -26,4 +24,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './transform' \ No newline at end of file +const Uuid = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i + +/** + * `[ajv-formats]` A Universally Unique Identifier as defined by [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122). + * @example `9aa8a673-8590-4db2-9830-01755844f7c1` + */ +export function IsUuid(value: string): boolean { + return Uuid.test(value) +} diff --git a/examples/transform/readme.md b/examples/transform/readme.md deleted file mode 100644 index bb02bc888..000000000 --- a/examples/transform/readme.md +++ /dev/null @@ -1,83 +0,0 @@ -# Transform Types - -Transform Types are an experimental codec system that supports deferred encode and decode of typed data structures received over IO interfaces. This system is undergoing consideration for inclusion in the TypeBox library. - -### Design - -The following is the high level design for Transform types. It consists of three functions, `Transform`, `Decode` and `Encode`. The following is the general usage. - -```typescript -import { Transform, Encode, Decode } from './transform' - -const Timestamp = Transform(Type.Number(), { // The Transform function wraps a TypeBox type with two codec - decode: (value) => new Date(value), // functions which implement logic to decode a received value - encode: (value) => value.getTime(), // (i.e. number) into a application type (Date). The encode -}) // function handles the reverse mapping. - -type N = Static // type N = { timestamp: number } - // -const N = Type.Object({ // Transform types are to be used like any other type and will - timestamp: Timestamp // infer as the original TypeBox type. For example, the type `N` -}) // above will infer as { timestamp: number } (as derived from - // the TB type) - - - -const D = Decode(N, { timestamp: 123 }) // const D = { timestamp: Date(123) } - // - // The Decode function accepts any type plus a value. The Decode - // function return type will be that of the transforms decode() - // return type (which is Date), with the second argument statically - // typed as N. This function acts as a kind of parse() that returns - // the decoded type or throws on validation error. - - -const E = Encode(N, { timestamp: new Date(123) }) // const E = { timestamp: 123 } - // - // The encode function performs the inverse, accepting the - // decoded type { timestamp: Date } and re-encoding to the - // target type { timestamp: number }. This function will - // also throw on validation error. -``` -### Integration - -A practical use case for Transform types is the automatic encode and decode of values received over the network. The following is an example implementation using the Fastify web framework with the TypeBox Type Provider (used for auto type inference). Note that this usage wouldn't be exclusive to Fastify. The implementation below uses explicit Decode and Encode within a route handler. - -```typescript -import { Type, TypeBoxTypeProvider } from '@fastify/type-provider-typebox' -import Fastify from 'fastify' - -const Data = Type.Object({ - timestamp: Transform(Type.Number(), { - decode: (value) => new Date(value), - encode: (value) => value.getTime(), - }) -}) - -Fastify().withTypeProvider().post('/', { - schema: { - body: Data - response: { 200: Data } - } -}, (req, res) => { - const decoded = Decode(Data, req.body) // decode { timestamp: number } - // as { timestamp: Date } - - // ... some operations on decoded value - // - // i.e. decoded.timestamp.getTime() - - const encoded = Encode(Data, decoded) // encode { timetamp: Date } - // as { timestamp: number } - - res.status(200).send(encoded) // send! -}) -``` - -### Current Status - -From the example above, the current design of Transform types would require explicit Encode and Decode within a route handler (or other function in receipt of data); and it would be very challenging for general purpose frameworks to integrate this functionality in a more concise fashion (currently). - -Possible options to make this more cohesive may be to provide route wrapping functions to handle codec execution and type inference. More integrated solutions would involve framework maintainers offering codec supportive type definitions to act as hooks for auto static inference (on both decode and encode phases). The latter option is the preference but would require a fair amount of engineering in downstream frameworks. - -Given the many unknowns and general complexity surrounding this feature, TypeBox offers the Transform implementation to the community (as a single `transform.ts` module) for experimentation and general feedback. TypeBox welcomes third party packages, example user integrations, code enhancements or general insight into end user usage patterns. \ No newline at end of file diff --git a/examples/transform/transform.ts b/examples/transform/transform.ts deleted file mode 100644 index 32b9a15c2..000000000 --- a/examples/transform/transform.ts +++ /dev/null @@ -1,350 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/transform - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '@sinclair/typebox' -import * as ValueGuard from '@sinclair/typebox/value/guard' -import * as ValueCheck from '@sinclair/typebox/value/check' - -// -------------------------------------------------------------------------- -// Symbols -// -------------------------------------------------------------------------- -export const TransformSymbol = Symbol.for('TypeBox.Transform') - -// ---------------------------------------------------------------------------------------------- -// Errors -// ---------------------------------------------------------------------------------------------- -export class TransformDecodeError extends Error { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { - super('ValueTransform: The value passed to decode was invalid') - } -} -export class TransformEncodeError extends Error { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { - super('ValueTransform: The encode function returned an invalid value') - } -} -export class ValueTransformUnknownTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('Unknown type') - } -} -export class ValueTransformDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`Unable to dereference type with $id '${schema.$ref}'`) - } -} -export class ValueTransformFallthroughError extends Error { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public mode: ValueTransformMode) { - super('Unexpected transform error') - } -} -export class ValueTransformCodecError extends Error { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public mode: ValueTransformMode, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown'}`) - } -} -export type ValueTransformMode = 'encode' | 'decode' - -// ---------------------------------------------------------------------------------------------- -// Apply -// ---------------------------------------------------------------------------------------------- -function Apply(schema: Types.TSchema, value: any, mode: ValueTransformMode) { - try { - if (!HasTransform(schema)) return value - const transform = schema[TransformSymbol] as unknown as TransformCodec - if (mode === 'decode') return transform.decode(value) - if (mode === 'encode') return transform.encode(value) - } catch (error) { - throw new ValueTransformCodecError(schema, value, mode, error) - } -} -// -------------------------------------------------------------------------- -// Guards -// -------------------------------------------------------------------------- -function HasTransform(schema: Types.TSchema): schema is Types.TSchema & { [TransformSymbol]: TransformCodec } { - return ValueGuard.HasPropertyKey(schema, TransformSymbol) -} -// ---------------------------------------------------------------------------------------------- -// Transform -// ---------------------------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { - return Apply(schema, value, mode) -} -function TArray(schema: Types.TArray, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { - const transformed = Apply(schema, value, mode) - return transformed.map((value: any) => Visit(schema.items, references, value, mode)) -} -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TDate(schema: Types.TDate, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TNever(schema: Types.TNever, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TNull(schema: Types.TNull, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const transformed = Apply(schema, value, mode) - const properties = Object.keys(transformed).reduce((acc, key) => { - return key in schema.properties - ? { ...acc, [key]: Visit(schema.properties[key], references, transformed[key], mode) } - : { ...acc, [key]: transformed[key] } - }, {}) - return { ...properties } -} -function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const transformed = Apply(schema, value, mode) - const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[propertyKey] - const result = {} as Record - for (const [propKey, propValue] of Object.entries(transformed)) { - result[propKey] = Visit(property, references, propValue, mode) - } - return result -} -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueTransformDereferenceError(schema) - const target = references[index] - const resolved = Visit(target, references, value, mode) - return Apply(schema, resolved, mode) -} -function TString(schema: Types.TString, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TThis(schema: Types.TThis, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueTransformDereferenceError(schema) - const target = references[index] - const resolved = Visit(target, references, value, mode) - return Apply(schema, resolved, mode) -} -function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const transformed = Apply(schema, value, mode) - // prettier-ignore - return Apply(schema, transformed.map((value: any, index: any) => { - return index < schema.items!.length ? Visit(schema.items![index], references, value, mode) : value - }), mode) -} -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - const transformed = Apply(schema, value, mode) - for (const subschema of schema.anyOf) { - const inner = Visit(subschema, references, transformed, mode) - if (ValueCheck.Check(subschema, references, inner)) { - return Apply(schema, inner, mode) - } - } - return transformed -} -function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode) { - return Apply(schema, value, mode) -} -function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any, mode: ValueTransformMode): any { - const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Any': - return TAny(schema_, references_, value, mode) - case 'Array': - return TArray(schema_, references_, value, mode) - case 'BigInt': - return TBigInt(schema_, references_, value, mode) - case 'Boolean': - return TBoolean(schema_, references_, value, mode) - case 'Constructor': - return TConstructor(schema_, references_, value, mode) - case 'Date': - return TDate(schema_, references_, value, mode) - case 'Function': - return TFunction(schema_, references_, value, mode) - case 'Integer': - return TInteger(schema_, references_, value, mode) - case 'Intersect': - return TIntersect(schema_, references_, value, mode) - case 'Literal': - return TLiteral(schema_, references_, value, mode) - case 'Never': - return TNever(schema_, references_, value, mode) - case 'Null': - return TNull(schema_, references_, value, mode) - case 'Number': - return TNumber(schema_, references_, value, mode) - case 'Object': - return TObject(schema_, references_, value, mode) - case 'Promise': - return TPromise(schema_, references_, value, mode) - case 'Record': - return TRecord(schema_, references_, value, mode) - case 'Ref': - return TRef(schema_, references_, value, mode) - case 'String': - return TString(schema_, references_, value, mode) - case 'Symbol': - return TSymbol(schema_, references_, value, mode) - case 'TemplateLiteral': - return TTemplateLiteral(schema_, references_, value, mode) - case 'This': - return TThis(schema_, references_, value, mode) - case 'Tuple': - return TTuple(schema_, references_, value, mode) - case 'Undefined': - return TUndefined(schema_, references_, value, mode) - case 'Union': - return TUnion(schema_, references_, value, mode) - case 'Uint8Array': - return TUint8Array(schema_, references_, value, mode) - case 'Unknown': - return TUnknown(schema_, references_, value, mode) - case 'Void': - return TVoid(schema_, references_, value, mode) - default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueTransformUnknownTypeError(schema_) - return TKind(schema_, references_, value, mode) - } -} -// -------------------------------------------------------------------------- -// Transform Unwrap -// -------------------------------------------------------------------------- -// prettier-ignore -export type TransformUnwrapProperties = { - [K in keyof T]: TransformUnwrap -} -// prettier-ignore -export type TransformUnwrapRest = T extends [infer L, ...infer R] - ? [TransformUnwrap>, ...TransformUnwrapRest>] - : [] -// prettier-ignore -export type TransformUnwrap = - T extends TTransform ? Types.TUnsafe : - T extends Types.TConstructor ? Types.TConstructor>, TransformUnwrap> : - T extends Types.TFunction ? Types.TFunction>, TransformUnwrap> : - T extends Types.TIntersect ? Types.TIntersect>> : - T extends Types.TUnion ? Types.TUnion>> : - T extends Types.TNot ? Types.TNot> : - T extends Types.TTuple ? Types.TTuple>> : - T extends Types.TAsyncIterator ? Types.TAsyncIterator> : - T extends Types.TIterator ? Types.TIterator> : - T extends Types.TObject ? Types.TObject>> : - T extends Types.TRecord ? Types.TRecord> : - T extends Types.TArray ? Types.TArray> : - T extends Types.TPromise ? Types.TPromise> : - T -// -------------------------------------------------------------------------- -// Transform Types -// -------------------------------------------------------------------------- -export type TransformFunction = (value: T) => U - -export interface TransformCodec { - decode: TransformFunction, Output> - encode: TransformFunction> -} -export interface TTransform extends Types.TSchema { - static: Types.Static - [TransformSymbol]: TransformCodec - [key: string]: any -} -// -------------------------------------------------------------------------- -// Transform Functions -// -------------------------------------------------------------------------- -/** Creates a transform type by applying transform codec */ -export function Transform(schema: Input, codec: TransformCodec): TTransform { - if (!HasTransform(schema)) return { ...schema, [TransformSymbol]: codec } as TTransform - const mapped_encode = (value: unknown) => schema[TransformSymbol].encode(codec.encode(value as any)) - const mapped_decode = (value: unknown) => codec.decode(schema[TransformSymbol].decode(value)) - const mapped_codec = { encode: mapped_encode, decode: mapped_decode } - return { ...schema, [TransformSymbol]: mapped_codec } as TTransform -} -/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ -export function Decode(schema: T, references: Types.TSchema[], value: Types.Static): Types.Static> -/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ -export function Decode(schema: T, value: Types.Static): Types.Static> -/** Decodes a value using the given type. Will apply an transform codecs found for any sub type */ -export function Decode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const check = ValueCheck.Check(schema, references, value) - if (!check) throw new TransformDecodeError(schema, value) - const decoded = Visit(schema, references, value, 'decode') - return decoded -} -/** Encodes a value using the given type. Will apply an transforms found for any sub type */ -export function Encode(schema: T, references: Types.TSchema[], value: Types.Static>): Types.Static -/** Encodes a value using the given type. Will apply an transforms found for any sub type */ -export function Encode(schema: T, value: Types.Static>): Types.Static -/** Encodes a value using the given type. Will apply an transforms found for any sub type */ -export function Encode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = Visit(schema, references, value, 'encode') - const check = ValueCheck.Check(schema, references, encoded) - if (!check) throw new TransformEncodeError(schema, value) - return encoded -} diff --git a/examples/typedef/typedef.ts b/examples/typedef/typedef.ts index 9c959318d..5eb12a372 100644 --- a/examples/typedef/typedef.ts +++ b/examples/typedef/typedef.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { TypeSystemErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/system' import * as Types from '@sinclair/typebox' // -------------------------------------------------------------------------- @@ -239,9 +240,9 @@ export namespace TimestampFormat { // -------------------------------------------------------------------------- // ValueCheck // -------------------------------------------------------------------------- -export class ValueCheckError extends Error { +export class ValueCheckError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCheck: Unknown type') + super('Unknown type') } } export namespace ValueCheck { @@ -484,6 +485,27 @@ Types.TypeRegistry.Set('TypeDef:String', (schema, value) => ValueCheck. Types.TypeRegistry.Set('TypeDef:Struct', (schema, value) => ValueCheck.Check(schema, value)) Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => ValueCheck.Check(schema, value)) // -------------------------------------------------------------------------- +// TypeSystemErrorFunction +// -------------------------------------------------------------------------- +TypeSystemErrorFunction.Set((schema, type) => { + switch(schema[Types.Kind]) { + case 'TypeDef:Array': return 'Expected Array' + case 'TypeDef:Boolean': return 'Expected Boolean' + case 'TypeDef:Union': return 'Expected Union' + case 'TypeDef:Int8': return 'Expected Int8' + case 'TypeDef:Int16': return 'Expected Int16' + case 'TypeDef:Int32': return 'Expected Int32' + case 'TypeDef:Uint8': return 'Expected Uint8' + case 'TypeDef:Uint16': return 'Expected Uint16' + case 'TypeDef:Uint32': return 'Expected Uint32' + case 'TypeDef:Record': return 'Expected Record' + case 'TypeDef:String': return 'Expected String' + case 'TypeDef:Struct': return 'Expected Struct' + case 'TypeDef:Timestamp': return 'Expected Timestamp' + } + return DefaultErrorFunction(schema, type) +}) +// -------------------------------------------------------------------------- // TypeDefBuilder // -------------------------------------------------------------------------- export class TypeDefBuilder { diff --git a/hammer.mjs b/hammer.mjs index 3bf3406f6..c99da14f9 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -22,10 +22,16 @@ export async function start() { // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- -export async function benchmark() { +export async function benchmark_compression() { await compression() +} +export async function benchmark_measurement() { await measurement() } +export async function benchmark() { + await benchmark_compression() + await benchmark_measurement() +} // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index 45c146d60..efe7b622e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,44 @@ { "name": "@sinclair/typebox", - "version": "0.30.4", + "version": "0.31.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.30.4", + "version": "0.31.0", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", - "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", "typescript": "^5.1.6" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/linux-loong64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz", - "integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", "cpu": [ "loong64" ], @@ -38,24 +52,17 @@ } }, "node_modules/@sinclair/hammer": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.1.tgz", - "integrity": "sha512-/M7KXwCaXdDmBJXQHpn1NWSTUg7Rmx6vzseVOax7IXhsBYj3vEGU5yt/Xh3XdRLVOEcyl4vGWgJ0pL9cmyrTJA==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.2.tgz", + "integrity": "sha512-Nnzq4JuC3VB0JNEfxU4U9W65yCe4+/ft0+FGf6/HBn7BoxW3aigusstFdrzVdhjJ6NVABCwUBMgtYbD9X7Z94g==", "dev": true, "dependencies": { - "@sinclair/hammer": "^0.17.0", - "esbuild": "^0.14.53" + "esbuild": "^0.15.7" }, "bin": { "hammer": "hammer" } }, - "node_modules/@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", - "dev": true - }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -63,9 +70,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "version": "18.17.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.2.tgz", + "integrity": "sha512-wBo3KqP/PBqje5TI9UTiuL3yWfP6sdPtjtygSOqcYZWT232dfDeDOnkDps5wqZBP9NgGgYrNejinl0faAuE+HQ==", "dev": true }, "node_modules/@ungap/promise-all-settled": { @@ -141,9 +148,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -159,15 +166,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -223,24 +221,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -269,15 +249,6 @@ "node": ">=8" } }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -375,18 +346,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -403,9 +362,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz", - "integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, "hasInstallScript": true, "bin": { @@ -415,33 +374,34 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/linux-loong64": "0.14.53", - "esbuild-android-64": "0.14.53", - "esbuild-android-arm64": "0.14.53", - "esbuild-darwin-64": "0.14.53", - "esbuild-darwin-arm64": "0.14.53", - "esbuild-freebsd-64": "0.14.53", - "esbuild-freebsd-arm64": "0.14.53", - "esbuild-linux-32": "0.14.53", - "esbuild-linux-64": "0.14.53", - "esbuild-linux-arm": "0.14.53", - "esbuild-linux-arm64": "0.14.53", - "esbuild-linux-mips64le": "0.14.53", - "esbuild-linux-ppc64le": "0.14.53", - "esbuild-linux-riscv64": "0.14.53", - "esbuild-linux-s390x": "0.14.53", - "esbuild-netbsd-64": "0.14.53", - "esbuild-openbsd-64": "0.14.53", - "esbuild-sunos-64": "0.14.53", - "esbuild-windows-32": "0.14.53", - "esbuild-windows-64": "0.14.53", - "esbuild-windows-arm64": "0.14.53" + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, "node_modules/esbuild-android-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz", - "integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", "cpu": [ "x64" ], @@ -455,9 +415,9 @@ } }, "node_modules/esbuild-android-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz", - "integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", "cpu": [ "arm64" ], @@ -471,9 +431,9 @@ } }, "node_modules/esbuild-darwin-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz", - "integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", "cpu": [ "x64" ], @@ -487,9 +447,9 @@ } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz", - "integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", "cpu": [ "arm64" ], @@ -503,9 +463,9 @@ } }, "node_modules/esbuild-freebsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz", - "integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", "cpu": [ "x64" ], @@ -519,9 +479,9 @@ } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz", - "integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", "cpu": [ "arm64" ], @@ -535,9 +495,9 @@ } }, "node_modules/esbuild-linux-32": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz", - "integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", "cpu": [ "ia32" ], @@ -551,9 +511,9 @@ } }, "node_modules/esbuild-linux-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz", - "integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", "cpu": [ "x64" ], @@ -567,9 +527,9 @@ } }, "node_modules/esbuild-linux-arm": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz", - "integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", "cpu": [ "arm" ], @@ -583,9 +543,9 @@ } }, "node_modules/esbuild-linux-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz", - "integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", "cpu": [ "arm64" ], @@ -599,9 +559,9 @@ } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz", - "integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", "cpu": [ "mips64el" ], @@ -615,9 +575,9 @@ } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz", - "integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", "cpu": [ "ppc64" ], @@ -631,9 +591,9 @@ } }, "node_modules/esbuild-linux-riscv64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz", - "integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", "cpu": [ "riscv64" ], @@ -647,9 +607,9 @@ } }, "node_modules/esbuild-linux-s390x": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz", - "integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", "cpu": [ "s390x" ], @@ -663,9 +623,9 @@ } }, "node_modules/esbuild-netbsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz", - "integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", "cpu": [ "x64" ], @@ -679,9 +639,9 @@ } }, "node_modules/esbuild-openbsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz", - "integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", "cpu": [ "x64" ], @@ -695,9 +655,9 @@ } }, "node_modules/esbuild-sunos-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz", - "integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", "cpu": [ "x64" ], @@ -711,9 +671,9 @@ } }, "node_modules/esbuild-windows-32": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz", - "integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", "cpu": [ "ia32" ], @@ -727,9 +687,9 @@ } }, "node_modules/esbuild-windows-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz", - "integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", "cpu": [ "x64" ], @@ -743,9 +703,9 @@ } }, "node_modules/esbuild-windows-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz", - "integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", "cpu": [ "arm64" ], @@ -851,15 +811,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1074,15 +1025,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.0" - } - }, "node_modules/minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", @@ -1222,15 +1164,6 @@ "node": ">=0.10.0" } }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1244,9 +1177,9 @@ } }, "node_modules/prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -1259,9 +1192,9 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -1400,15 +1333,6 @@ "node": ">=8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -1540,29 +1464,29 @@ } }, "dependencies": { + "@esbuild/android-arm": { + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", + "dev": true, + "optional": true + }, "@esbuild/linux-loong64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.53.tgz", - "integrity": "sha512-W2dAL6Bnyn4xa/QRSU3ilIK4EzD5wgYXKXJiS1HDF5vU3675qc2bvFyLwbUcdmssDveyndy7FbitrCoiV/eMLg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", "dev": true, "optional": true }, "@sinclair/hammer": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.1.tgz", - "integrity": "sha512-/M7KXwCaXdDmBJXQHpn1NWSTUg7Rmx6vzseVOax7IXhsBYj3vEGU5yt/Xh3XdRLVOEcyl4vGWgJ0pL9cmyrTJA==", + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.2.tgz", + "integrity": "sha512-Nnzq4JuC3VB0JNEfxU4U9W65yCe4+/ft0+FGf6/HBn7BoxW3aigusstFdrzVdhjJ6NVABCwUBMgtYbD9X7Z94g==", "dev": true, "requires": { - "@sinclair/hammer": "^0.17.0", - "esbuild": "^0.14.53" + "esbuild": "^0.15.7" } }, - "@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", - "dev": true - }, "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -1570,9 +1494,9 @@ "dev": true }, "@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "version": "18.17.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.2.tgz", + "integrity": "sha512-wBo3KqP/PBqje5TI9UTiuL3yWfP6sdPtjtygSOqcYZWT232dfDeDOnkDps5wqZBP9NgGgYrNejinl0faAuE+HQ==", "dev": true }, "@ungap/promise-all-settled": { @@ -1624,9 +1548,9 @@ } }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1639,12 +1563,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1688,21 +1606,6 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, - "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1724,12 +1627,6 @@ } } }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", - "dev": true - }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1801,15 +1698,6 @@ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -1823,171 +1711,172 @@ "dev": true }, "esbuild": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.53.tgz", - "integrity": "sha512-ohO33pUBQ64q6mmheX1mZ8mIXj8ivQY/L4oVuAshr+aJI+zLl+amrp3EodrUNDNYVrKJXGPfIHFGhO8slGRjuw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, "requires": { - "@esbuild/linux-loong64": "0.14.53", - "esbuild-android-64": "0.14.53", - "esbuild-android-arm64": "0.14.53", - "esbuild-darwin-64": "0.14.53", - "esbuild-darwin-arm64": "0.14.53", - "esbuild-freebsd-64": "0.14.53", - "esbuild-freebsd-arm64": "0.14.53", - "esbuild-linux-32": "0.14.53", - "esbuild-linux-64": "0.14.53", - "esbuild-linux-arm": "0.14.53", - "esbuild-linux-arm64": "0.14.53", - "esbuild-linux-mips64le": "0.14.53", - "esbuild-linux-ppc64le": "0.14.53", - "esbuild-linux-riscv64": "0.14.53", - "esbuild-linux-s390x": "0.14.53", - "esbuild-netbsd-64": "0.14.53", - "esbuild-openbsd-64": "0.14.53", - "esbuild-sunos-64": "0.14.53", - "esbuild-windows-32": "0.14.53", - "esbuild-windows-64": "0.14.53", - "esbuild-windows-arm64": "0.14.53" + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, "esbuild-android-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.53.tgz", - "integrity": "sha512-fIL93sOTnEU+NrTAVMIKiAw0YH22HWCAgg4N4Z6zov2t0kY9RAJ50zY9ZMCQ+RT6bnOfDt8gCTnt/RaSNA2yRA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", "dev": true, "optional": true }, "esbuild-android-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.53.tgz", - "integrity": "sha512-PC7KaF1v0h/nWpvlU1UMN7dzB54cBH8qSsm7S9mkwFA1BXpaEOufCg8hdoEI1jep0KeO/rjZVWrsH8+q28T77A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.53.tgz", - "integrity": "sha512-gE7P5wlnkX4d4PKvLBUgmhZXvL7lzGRLri17/+CmmCzfncIgq8lOBvxGMiQ4xazplhxq+72TEohyFMZLFxuWvg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.53.tgz", - "integrity": "sha512-otJwDU3hnI15Q98PX4MJbknSZ/WSR1I45il7gcxcECXzfN4Mrpft5hBDHXNRnCh+5858uPXBXA1Vaz2jVWLaIA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.53.tgz", - "integrity": "sha512-WkdJa8iyrGHyKiPF4lk0MiOF87Q2SkE+i+8D4Cazq3/iqmGPJ6u49je300MFi5I2eUsQCkaOWhpCVQMTKGww2w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.53.tgz", - "integrity": "sha512-9T7WwCuV30NAx0SyQpw8edbKvbKELnnm1FHg7gbSYaatH+c8WJW10g/OdM7JYnv7qkimw2ZTtSA+NokOLd2ydQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.53.tgz", - "integrity": "sha512-VGanLBg5en2LfGDgLEUxQko2lqsOS7MTEWUi8x91YmsHNyzJVT/WApbFFx3MQGhkf+XdimVhpyo5/G0PBY91zg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.53.tgz", - "integrity": "sha512-pP/FA55j/fzAV7N9DF31meAyjOH6Bjuo3aSKPh26+RW85ZEtbJv9nhoxmGTd9FOqjx59Tc1ZbrJabuiXlMwuZQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.53.tgz", - "integrity": "sha512-/u81NGAVZMopbmzd21Nu/wvnKQK3pT4CrvQ8BTje1STXcQAGnfyKgQlj3m0j2BzYbvQxSy+TMck4TNV2onvoPA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.53.tgz", - "integrity": "sha512-GDmWITT+PMsjCA6/lByYk7NyFssW4Q6in32iPkpjZ/ytSyH+xeEx8q7HG3AhWH6heemEYEWpTll/eui3jwlSnw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.53.tgz", - "integrity": "sha512-d6/XHIQW714gSSp6tOOX2UscedVobELvQlPMkInhx1NPz4ThZI9uNLQ4qQJHGBGKGfu+rtJsxM4NVHLhnNRdWQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.53.tgz", - "integrity": "sha512-ndnJmniKPCB52m+r6BtHHLAOXw+xBCWIxNnedbIpuREOcbSU/AlyM/2dA3BmUQhsHdb4w3amD5U2s91TJ3MzzA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", "dev": true, "optional": true }, "esbuild-linux-riscv64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.53.tgz", - "integrity": "sha512-yG2sVH+QSix6ct4lIzJj329iJF3MhloLE6/vKMQAAd26UVPVkhMFqFopY+9kCgYsdeWvXdPgmyOuKa48Y7+/EQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", "dev": true, "optional": true }, "esbuild-linux-s390x": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.53.tgz", - "integrity": "sha512-OCJlgdkB+XPYndHmw6uZT7jcYgzmx9K+28PVdOa/eLjdoYkeAFvH5hTwX4AXGLZLH09tpl4bVsEtvuyUldaNCg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.53.tgz", - "integrity": "sha512-gp2SB+Efc7MhMdWV2+pmIs/Ja/Mi5rjw+wlDmmbIn68VGXBleNgiEZG+eV2SRS0kJEUyHNedDtwRIMzaohWedQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.53.tgz", - "integrity": "sha512-eKQ30ZWe+WTZmteDYg8S+YjHV5s4iTxeSGhJKJajFfQx9TLZJvsJX0/paqwP51GicOUruFpSUAs2NCc0a4ivQQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.53.tgz", - "integrity": "sha512-OWLpS7a2FrIRukQqcgQqR1XKn0jSJoOdT+RlhAxUoEQM/IpytS3FXzCJM6xjUYtpO5GMY0EdZJp+ur2pYdm39g==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.53.tgz", - "integrity": "sha512-m14XyWQP5rwGW0tbEfp95U6A0wY0DYPInWBB7D69FAXUpBpBObRoGTKRv36lf2RWOdE4YO3TNvj37zhXjVL5xg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.53.tgz", - "integrity": "sha512-s9skQFF0I7zqnQ2K8S1xdLSfZFsPLuOGmSx57h2btSEswv0N0YodYvqLcJMrNMXh6EynOmWD7rz+0rWWbFpIHQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.14.53", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.53.tgz", - "integrity": "sha512-E+5Gvb+ZWts+00T9II6wp2L3KG2r3iGxByqd/a1RmLmYWVsSVUjkvIxZuJ3hYTIbhLkH5PRwpldGTKYqVz0nzQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", "dev": true, "optional": true }, @@ -2053,12 +1942,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true - }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -2215,15 +2098,6 @@ "is-unicode-supported": "^0.1.0" } }, - "loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", - "dev": true, - "requires": { - "get-func-name": "^2.0.0" - } - }, "minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", @@ -2322,12 +2196,6 @@ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, - "pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2335,15 +2203,15 @@ "dev": true }, "prettier": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", - "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "randombytes": { @@ -2435,12 +2303,6 @@ "is-number": "^7.0.0" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, "typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", diff --git a/package.json b/package.json index 2b88f0e00..295d4384e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.30.4", + "version": "0.31.0", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -22,11 +22,13 @@ "./value/convert": "./value/convert.js", "./value/create": "./value/create.js", "./value/delta": "./value/delta.js", + "./value/deref": "./value/deref.js", "./value/equal": "./value/equal.js", "./value/guard": "./value/guard.js", "./value/hash": "./value/hash.js", "./value/mutate": "./value/mutate.js", "./value/pointer": "./value/pointer.js", + "./value/transform": "./value/transform.js", "./value": "./value/index.js", ".": "./typebox.js" }, @@ -42,6 +44,8 @@ "clean": "hammer task clean", "format": "hammer task format", "start": "hammer task start", + "benchmark:compression": "hammer task benchmark_compression", + "benchmark:measurement": "hammer task benchmark_measurement", "benchmark": "hammer task benchmark", "build": "hammer task build", "publish": "hammer task publish", @@ -49,12 +53,10 @@ }, "devDependencies": { "@sinclair/hammer": "^0.17.1", - "@types/chai": "^4.3.3", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "chai": "^4.3.6", "mocha": "^9.2.2", "prettier": "^2.7.1", "typescript": "^5.1.6" diff --git a/readme.md b/readme.md index 3cec6819c..4b80a0671 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@

      TypeBox

      -

      JSON Schema Type Builder with Static Type Resolution for TypeScript

      +

      Json Schema Type Builder with Static Type Resolution for TypeScript

      @@ -25,21 +25,16 @@ $ npm install @sinclair/typebox --save ``` -#### Deno -```typescript -import { Static, Type } from 'npm:@sinclair/typebox' -``` - -#### Esm +#### Esm + Deno ```typescript -import { Static, Type } from 'https://esm.sh/@sinclair/typebox' +import { Type, Static } from 'https://esm.sh/@sinclair/typebox' ``` ## Example ```typescript -import { Static, Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' const T = Type.Object({ // const T = { x: Type.Number(), // type: 'object', @@ -63,9 +58,11 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder 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 assertion rules of the TypeScript language. TypeBox allows one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. +TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime checked using standard Json Schema validation. -This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. +TypeBox is designed to be a runtime type system based on industry standard specifications for use in distributed systems. It offers serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, a high performance runtime validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. + +TypeBox can be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. License MIT @@ -74,20 +71,20 @@ License MIT - [Overview](#overview) - [Usage](#usage) - [Types](#types) - - [Standard](#types-standard) - - [Extended](#types-extended) + - [Json](#types-json) + - [JavaScript](#types-javascript) - [Options](#types-options) - [Properties](#types-properties) - [Generics](#types-generics) - [References](#types-references) - [Recursive](#types-recursive) - [Conditional](#types-conditional) - - [Template Literal](#types-template-literal) + - [Template Literal](#types-templateliteral) - [Indexed](#types-indexed) - - [Negated](#types-negated) - - [Intrinsic](#types-intrinsic) - [Rest](#types-rest) - - [Guards](#types-guards) + - [Transform](#types-transform) + - [Intrinsic](#types-intrinsic) + - [Guard](#types-guard) - [Unsafe](#types-unsafe) - [Strict](#types-strict) - [Values](#values) @@ -96,6 +93,8 @@ License MIT - [Check](#values-check) - [Convert](#values-convert) - [Cast](#values-cast) + - [Decode](#values-decode) + - [Encode](#values-decode) - [Equal](#values-equal) - [Hash](#values-hash) - [Diff](#values-diff) @@ -103,14 +102,19 @@ License MIT - [Errors](#values-errors) - [Mutate](#values-mutate) - [Pointer](#values-pointer) +- [TypeRegistry](#typeregistry) + - [Type](#typeregistry-type) + - [Format](#typeregistry-format) - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) - [TypeSystem](#typesystem) - [Types](#typesystem-types) - [Formats](#typesystem-formats) + - [Errors](#typesystem-errors) - [Policies](#typesystem-policies) - [Workbench](#workbench) +- [Codegen](#codegen) - [Ecosystem](#ecosystem) - [Benchmark](#benchmark) - [Compile](#benchmark-compile) @@ -125,7 +129,7 @@ License MIT The following shows general usage. ```typescript -import { Static, Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- // @@ -179,7 +183,7 @@ type T = Static // type T = { //-------------------------------------------------------------------------------------------- // -// ... then use the type both as JSON schema and as a TypeScript type. +// ... then use the type both as Json Schema and as a TypeScript type. // //-------------------------------------------------------------------------------------------- @@ -187,7 +191,7 @@ import { Value } from '@sinclair/typebox/value' function receive(value: T) { // ... as a Static Type - if(Value.Check(T, value)) { // ... as a JSON Schema + if(Value.Check(T, value)) { // ... as a Json Schema // ok... } @@ -198,17 +202,17 @@ function receive(value: T) { // ... as a Static Type ## Types -TypeBox types are JSON schema fragments that compose into complex types. Each fragment is structured such that a JSON schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox provides a set of Standard types which are used create JSON schema compliant schematics as well as an Extended type set used to create schematics for constructs native to JavaScript. +TypeBox types are Json Schema fragments that compose into more complex types. Each fragment is structured such that any Json Schema compliant validator can runtime assert a value the same way TypeScript will statically assert a type. TypeBox offers a set of Json Types which are used to create Json Schema compliant schematics as well as a JavaScript type set used to create schematics for constructs native to JavaScript. - + -### Standard Types +### Json Types -The following table lists the Standard TypeBox types. These types are fully compatible with the JSON Schema Draft 7 specification. +The following table lists the supported Json types. These types are fully compatible with the Json Schema Draft 7 specification. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ -│ TypeBox │ TypeScript │ JSON Schema │ +│ TypeBox │ TypeScript │ Json Schema │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Any() │ type T = any │ const T = { } │ @@ -387,12 +391,6 @@ The following table lists the Standard TypeBox types. These types are fully comp │ ) │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Pattern('^xy$') │ type T = string │ const T = { │ -│ │ │ type: 'string', │ -│ │ │ pattern: '^xy$' │ -│ │ │ } │ -│ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const U = Type.Union([ │ type U = 'open' | 'close' │ const T = { │ │ Type.Literal('open'), │ │ type: 'string', │ │ Type.Literal('close') │ type T = `on${U}` │ pattern: '^on(open|close)$' │ @@ -523,11 +521,11 @@ The following table lists the Standard TypeBox types. These types are fully comp └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - + -### Extended Types +### JavaScript Types -TypeBox provides several extended types that can be used to produce schematics for common JavaScript constructs. These types can not be used with standard JSON schema validators; but are useful to help frame schematics for RPC interfaces that may receive JSON validated data. Extended types are prefixed with the `[Extended]` doc comment for convenience. The following table lists the supported types. +TypeBox provides an extended type set that can be used to create schematics for common JavaScript constructs. These types can not be used with any standard Json Schema validator; but can be used to frame schematics for interfaces that may receive Json validated data. JavaScript types are prefixed with the `[JavaScript]` jsdoc comment for convenience. The following table lists the supported types. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -568,6 +566,14 @@ TypeBox provides several extended types that can be used to produce schematics f │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = │ type T = │ const T = { │ +│ Type.AsyncIterator( │ AsyncIterableIterator< │ type: 'AsyncIterator', │ +│ Type.String() │ string │ items: { │ +│ ) │ > │ type: 'string' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Iterator( │ type T = │ const T = { │ │ Type.String() │ IterableIterator │ type: 'Iterator', │ │ ) │ │ items: { │ @@ -576,11 +582,9 @@ TypeBox provides several extended types that can be used to produce schematics f │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = │ type T = │ const T = { │ -│ Type.AsyncIterator( │ AsyncIterableIterator< │ type: 'AsyncIterator', │ -│ Type.String() │ string │ items: { │ -│ ) │ > │ type: 'string' │ -│ │ │ } │ +│ const T = Type.RegExp(/abc/) │ type T = string │ const T = { │ +│ │ │ type: 'string' │ +│ │ │ pattern: 'abc' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -620,7 +624,7 @@ TypeBox provides several extended types that can be used to produce schematics f ### Options -You can pass JSON Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. +You can pass Json Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. ```typescript // String must be an email @@ -649,11 +653,11 @@ const T = Type.Array(Type.Integer(), { // const T = { ### Properties -Object properties can be modified with `readonly` or `optional`. The following table shows how these modifiers map between TypeScript and JSON Schema. +Object properties can be modified with Readonly and Optional. The following table shows how these modifiers map between TypeScript and Json Schema. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ -│ TypeBox │ TypeScript │ JSON Schema │ +│ TypeBox │ TypeScript │ Json Schema │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Object({ │ type T = { │ const T = { │ @@ -692,7 +696,7 @@ Object properties can be modified with `readonly` or `optional`. The following t ### Generic Types -Generic types are created with generic functions. All TypeBox types extend the sub type `TSchema` so it is common to constrain function arguments to this type. The following creates a generic `Vector` type. +Generic types can be created with generic functions. All types extend the base type TSchema. It is common to constrain generic function arguments to this type. The following creates a generic Vector type. ```typescript import { Type, Static, TSchema } from '@sinclair/typebox' @@ -716,32 +720,32 @@ type NumberVector = Static // type NumberVector = { // } ``` -Generic types can be used to create aliases for more complex types. The following creates a `Nullable` type. +Generic types are often used to create aliases for more complex types. The following creates a Nullable generic type. ```typescript const Nullable = (schema: T) => Type.Union([schema, Type.Null()]) -const T = Nullable(Type.String()) // const T = { - // anyOf: [ - // { type: 'string' }, - // { type: 'null' } - // ] - // } +const T = Nullable(Type.String()) // const T = { + // anyOf: [ + // { type: 'string' }, + // { type: 'null' } + // ] + // } -type T = Static // type T = string | null +type T = Static // type T = string | null ``` ### Reference Types -Reference types are supported with `Ref`. +Reference types are supported with Ref. ```typescript const T = Type.String({ $id: 'T' }) // const T = { // $id: 'T', // type: 'string' - // } + // } const R = Type.Ref('T') // const R = { // $ref: 'T' @@ -754,7 +758,7 @@ type R = Static // type R = string ### Recursive Types -Recursive types are supported with `Recursive`. Recursive type inference is also supported. +TypeBox supports singular recursive data structures. Recursive type inference is also supported. The following creates a recursive Node data structure. ```typescript const Node = Type.Recursive(This => Type.Object({ // const Node = { @@ -791,7 +795,7 @@ function test(node: Node) { ### Conditional Types -TypeBox supports conditional types with `Extends`. This type performs a structural assignment check against the first two parameters and returns either the `true` or `false` type as given from the second two parameters. The conditional types `Exclude` and `Extract` are also supported. +TypeBox supports runtime conditional types with Extends. This type will perform a structural assignability check for the first two arguments and returns one of the second two arguments based on the result. The Extends type is modelled after TypeScript conditional types. The Exclude and Extract conditional types are also supported. ```typescript // TypeScript @@ -820,25 +824,25 @@ const T1 = Type.Extract( // const T1: TLiteral<1> = Type.Literal(1) ) -const T2 = Type.Exclude( // const T2: TUnion<[ - Type.Union([ // TLiteral<2>, - Type.Literal(1), // TLiteral<3> - Type.Literal(2), // ]> = { - Type.Literal(3) // anyOf: [{ - ]), // type: 'number', - Type.Literal(1) // const: 2 -) // }, { - // type: 'number', - // const: 3 - // }] - // } +const T2 = Type.Exclude( // const T2: TUnion<[ + Type.Union([ // TLiteral<2>, + Type.Literal(1), // TLiteral<3> + Type.Literal(2), // ]> = { + Type.Literal(3) // anyOf: [{ + ]), // type: 'number', + Type.Literal(1) // const: 2 +) // }, { + // type: 'number', + // const: 3 + // }] + // } ``` - + ### Template Literal Types -TypeBox supports template literal types with `TemplateLiteral`. This type provides an embedded DSL syntax that is similar to the TypeScript template literal syntax. These type can also be composed by passing a tuple of exterior union and literal types. The following example shows the DSL syntax. +TypeBox supports template literal types with TemplateLiteral. This type can be created using a string syntax that is similar to the TypeScript template literal syntax. This type can also be constructed by passing an array of Union and Literal types in sequence. The following example shows the string syntax. ```typescript // TypeScript @@ -879,7 +883,7 @@ const R = Type.Record(T, Type.String()) // const R = { ### Indexed Access Types -TypeBox supports indexed access types using `Index`. This type provides a consistent way of accessing interior property and array element types without having to extract them from the underlying schema representation. Indexed access types are supported for object, array, tuple, union and intersect types. +TypeBox supports Indexed Access Types with Index. This type enables uniform access to interior property and array element types without having to extract them from the underlying schema representation. This type is supported for Object, Array, Tuple, Union and Intersect types. ```typescript const T = Type.Object({ // const T = { @@ -910,150 +914,117 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { // } ``` - - -### Negated Types - -TypeBox has support for type negation with `Not`. This type will always infer as `unknown`. + -```typescript -const T = Type.Not(Type.String()) // const T = { - // not: { type: 'string' } - // } +### Rest Types -type T = Static // type T = unknown - // - // where T could be any type other than string -``` -Type negation can be useful for certain forms of type narrowing. For example, consider a type that represents a `number` but not the numbers `1, 2, 3`. The example below shows an imaginary TypeScript syntax to express such a type followed by the TypeBox representation. +TypeBox provides the Rest type to uniformly extract variadic tuples from Intersect, Union and Tuple types. This type can be useful to remap variadic types into different forms. The following uses Rest to remap a Tuple into a Union. ```typescript -// TypeScript - -type T = number & not (1 | 2 | 3) // not actual syntax +const T = Type.Tuple([ // const T = { + Type.String(), // type: 'array', + Type.Number() // items: [ +]) // { type: 'string' }, + // { type: 'number' } + // ], + // additionalItems: false, + // minItems: 2, + // maxItems: 2, + // } -// TypeBox +const R = Type.Rest(T) // const R = [ + // { type: 'string' }, + // { type: 'number' } + // ] -const T = Type.Intersect([ // const T = { - Type.Number(), // allOf: [ - Type.Not(Type.Union([ // { type: "number" }, - Type.Literal(1), // { - Type.Literal(2), // not: { - Type.Literal(3) // anyOf: [ - ])) // { const: 1, type: "number" }, -]) // { const: 2, type: "number" }, - // { const: 3, type: "number" } - // ] - // } - // } +const U = Type.Union(R) // const U = { + // anyOf: [ + // { type: 'string' }, + // { type: 'number' } // ] // } - -type T = Static // type T = number -``` -This type can be used with constraints to create schematics that would otherwise be difficult to express. -```typescript -const Even = Type.Number({ multipleOf: 2 }) - -const Odd = Type.Intersect([Type.Number(), Type.Not(Even)]) ``` - + -### Intrinsic String Types +### Transform Types -TypeBox supports TypeScript intrinsic string manipulation types `Uppercase`, `Lowercase`, `Capitalize` and `Uncapitalize`. These can be applied to string literals, template literals and unions. The following shows general usage. +TypeBox supports bi-directional decode and encode with Transform types. These types are designed to operate with the Value and TypeCompiler Encode and Decode functions. Transform types are useful to convert Json encoded values into constructs more natural to JavaScript. The following creates a Transform type to convert between Date and number using the Value module. ```typescript -// TypeScript - -type A = Capitalize<'hello'> // type A = 'Hello' +import { Value } from '@sinclair/typebox/value' -type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' +const T = Type.Transform(Type.Number()) + .Decode(value => new Date(value)) // required: number to Date + .Encode(value => value.getTime()) // required: Date to number -type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' +const decoded = Value.Decode(T, 0) // const decoded = Date(1970-01-01T00:00:00.000Z) +const encoded = Value.Encode(T, decoded) // const encoded = 0 +``` +Use the StaticEncode or StaticDecode types to infer a Transform type. +```typescript +import { Static, StaticDecode, StaticEncode } from '@sinclair/typebox' -// TypeBox +const T = Type.Transform(Type.Array(Type.Number(), { uniqueItems: true })) + .Decode(value => new Set(value)) + .Encode(value => [...value]) -const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> - -const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ - Type.Literal('hello'), // TLiteral<'Hello'>, - Type.Literal('world') // TLiteral<'World'> -])) // ]> - -const C = Type.Capitalize( // const C: TTemplateLiteral<[ - Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, -) // TUnion<[ - // TLiteral<'1'>, - // TLiteral<'2'>, - // TLiteral<'3'> - // ]> - // ]> +type D = StaticDecode // type D = Set +type E = StaticEncode // type E = Array +type T = Static // type T = Array ``` - + -### Rest Types +### Intrinsic Types -Rest parameters are supported with `Rest`. This function is used to extract interior type elements from tuples which enables them to compose with the JavaScript spread operator `...`. This type can be used for tuple concatenation as well function parameter assignment. +TypeBox supports the TypeScript Intrinsic String Manipulation types Uppercase, Lowercase, Capitalize and Uncapitalize. These types can be used to remap String Literal, TemplateLiteral and Union types. ```typescript // TypeScript -type T = [number, number] // type T = [number, number] - -type C = [...T, number] // type C = [number, number, number] - -type F = (...param: C) => void // type F = ( - // param0: number, - // param1: number, - // param2: number - // ) => void +type A = Capitalize<'hello'> // type A = 'Hello' +type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' +type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' // TypeBox -const T = Type.Tuple([ // const T: TTuple<[ - Type.Number(), // TNumber, - Type.Number() // TNumber -]) // ]> +const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> -const C = Type.Tuple([ // const C: TTuple<[ - ...Type.Rest(T), // TNumber, - Type.Number() // TNumber, -]) // TNumber - // ]> +const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ + Type.Literal('hello'), // TLiteral<'Hello'>, + Type.Literal('world') // TLiteral<'World'> +])) // ]> -const F = Type.Function(Type.Rest(C), Type.Void()) // const F: TFunction<[ - // TNumber, - // TNumber, - // TNumber - // ], TVoid> +const C = Type.Capitalize( // const C: TTemplateLiteral<[ + Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, +) // TUnion<[ + // TLiteral<'1'>, + // TLiteral<'2'>, + // TLiteral<'3'> + // ]> + // ]> ``` + ### Unsafe Types -TypeBox supports the creation of user defined schematics with user defined inference rules using the Unsafe type. +TypeBox supports user defined types with Unsafe. This type allows you to specify both schema representation and inference type. The following creates an Unsafe type with a number schema that infers as string. ```typescript const T = Type.Unsafe({ type: 'number' }) // const T = { // type: 'number' // } -type T = Static // type T = string +type T = Static // type T = string - ? ``` - -This type can be useful to create various extended schematics, such as those used by OpenAPI. - +The Unsafe type is often used to create schematics for extended specifications like OpenAPI ```typescript -import { Type, Static, TSchema } from '@sinclair/typebox' -// Nullable - -function Nullable(schema: T) { - return Type.Unsafe | null>({ ...schema, nullable: true }) -} +const Nullable = (schema: T) => Type.Unsafe | null>({ + ...schema, nullable: true +}) const T = Nullable(Type.String()) // const T = { // type: 'string', @@ -1062,33 +1033,29 @@ const T = Nullable(Type.String()) // const T = { type T = Static // type T = string | null -// StringEnum - -function StringEnum(values: [...T]) { - return Type.Unsafe({ type: 'string', enum: values }) -} - -const T = StringEnum(['A', 'B', 'C']) // const T = { +const StringEnum = (values: [...T]) => Type.Unsafe({ + type: 'string', enum: values +}) +const S = StringEnum(['A', 'B', 'C']) // const S = { // enum: ['A', 'B', 'C'] // } -type T = Static // type T = 'A' | 'B' | 'C' +type S = Static // type S = 'A' | 'B' | 'C' ``` + - +### Type Guard -### Type Guards - -TypeBox provides a TypeGuard module to assert JavaScript values are valid TypeBox types. +TypeBox can type check its own types with the TypeGuard module. This module is written for reflection and provides structural tests for every TypeBox type. Functions of this module return `is` guards which can be used with TypeScript control flow assertions to obtain schema inference. The following guards that the value A is TString. ```typescript import { Type, Kind, TypeGuard } from '@sinclair/typebox' -const T = { [Kind]: 'String', type: 'string' } +const A: unknown = { ... } -if(TypeGuard.TString(T)) { +if(TypeGuard.TString(A)) { - // T is TString + A.type // A.type = 'string' } ``` @@ -1096,7 +1063,7 @@ if(TypeGuard.TString(T)) { ### Strict -TypeBox types contain various symbol properties that are used for reflection, composition and compilation. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Strict` function that will omit these properties if necessary. +TypeBox types contain various symbol properties that are used for reflection, composition and compilation. These properties are not strictly valid Json Schema; so in some cases it may be desirable to omit them. TypeBox provides a `Strict` function that will omit these properties if necessary. ```typescript const T = Type.Object({ // const T = { @@ -1125,7 +1092,7 @@ const U = Type.Strict(T) // const U = { ## Values -TypeBox provides an optional utility module that can be used to perform common operations on JavaScript values. This module includes functionality to create, check and cast values from types as well as check equality, clone, diff and patch JavaScript values. This module is provided via optional import. +TypeBox provides an optional utility module that can be used to perform structural operations on JavaScript values. This module includes functionality to create, check and cast values from types as well as check equality, clone, diff and patch JavaScript values. This module is provided via optional import. ```typescript import { Value } from '@sinclair/typebox/value' @@ -1147,7 +1114,7 @@ const A = Value.Create(T) // const A = { x: 0, y: 42 ### Clone -Use the Clone function to deeply clone a value +Use the Clone function to deeply clone a value. ```typescript const A = Value.Clone({ x: 1, y: 2, z: 3 }) // const A = { x: 1, y: 2, z: 3 } @@ -1157,7 +1124,7 @@ const A = Value.Clone({ x: 1, y: 2, z: 3 }) // const A = { x: 1, y: 2, ### Check -Use the Check function to type check a value +Use the Check function to type check a value. ```typescript const T = Type.Object({ x: Type.Number() }) @@ -1169,21 +1136,21 @@ const R = Value.Check(T, { x: 1 }) // const R = true ### Convert -Use the Convert function to convert a value into its target type if a reasonable conversion is possible. This function may return an invalid value and should be checked before use. It's return type is `unknown`. +Use the Convert function to convert a value into its target type if a reasonable conversion is possible. This function may return an invalid value and should be checked before use. Its return type is `unknown`. ```typescript const T = Type.Object({ x: Type.Number() }) -const R1 = Value.Convert(T, { x: '3.14' }) // const R1 = { x: 3.14 } +const R1 = Value.Convert(T, { x: '3.14' }) // const R1 = { x: 3.14 } -const R2 = Value.Convert(T, { x: 'not a number' }) // const R2 = { x: 'not a number' } +const R2 = Value.Convert(T, { x: 'not a number' }) // const R2 = { x: 'not a number' } ``` ### Cast -Use the Cast function to cast a value into a type. The cast function will retain as much information as possible from the original value. +Use the Cast function to cast a value with a type. The cast function will retain as much information as possible from the original value. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) @@ -1195,6 +1162,29 @@ const Y = Value.Cast(T, { x: 1 }) // const Y = { x: 1, y: 0 } const Z = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const Z = { x: 1, y: 2 } ``` + + +### Decode + +Use the Decode function to decode a value from a type, or throw if the value is invalid. The return value will infer as the decoded type. This function will run Transform codecs if available. + +```typescript +const A = Type.Decode(Type.String(), 'hello') // const A = 'hello' + +const B = Type.Decode(Type.String(), 42) // throw +``` + + +### Encode + +Use the Encode function to encode a value to a type, or throw if the value is invalid. The return value will infer as the encoded type. This function will run Transform codecs if available. + +```typescript +const A = Type.Encode(Type.String(), 'hello') // const A = 'hello' + +const B = Type.Encode(Type.String(), 42) // throw +``` + ### Equal @@ -1215,31 +1205,31 @@ const R = Value.Equal( // const R = true Use the Hash function to create a [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) non cryptographic hash of a value. ```typescript -const A = Value.Hash({ x: 1, y: 2, z: 3 }) // const A = 2910466848807138541n +const A = Value.Hash({ x: 1, y: 2, z: 3 }) // const A = 2910466848807138541n -const B = Value.Hash({ x: 1, y: 4, z: 3 }) // const B = 1418369778807423581n +const B = Value.Hash({ x: 1, y: 4, z: 3 }) // const B = 1418369778807423581n ``` ### Diff -Use the Diff function to produce a sequence of edits to transform one value into another. +Use the Diff function to generate a sequence of edits that will transform one value into another. ```typescript -const E = Value.Diff( // const E = [ - { x: 1, y: 2, z: 3 }, // { type: 'update', path: '/y', value: 4 }, - { y: 4, z: 5, w: 6 } // { type: 'update', path: '/z', value: 5 }, -) // { type: 'insert', path: '/w', value: 6 }, - // { type: 'delete', path: '/x' } - // ] +const E = Value.Diff( // const E = [ + { x: 1, y: 2, z: 3 }, // { type: 'update', path: '/y', value: 4 }, + { y: 4, z: 5, w: 6 } // { type: 'update', path: '/z', value: 5 }, +) // { type: 'insert', path: '/w', value: 6 }, + // { type: 'delete', path: '/x' } + // ] ``` ### Patch -Use the Patch function to apply edits +Use the Patch function to apply a sequence of edits. ```typescript const A = { x: 1, y: 2 } @@ -1258,7 +1248,7 @@ const C = Value.Patch(A, E) // const C = { x: 3 } ### Errors -Use the Errors function enumerate validation errors. +Use the Errors function to enumerate validation errors. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number() }) @@ -1284,18 +1274,13 @@ Use the Mutate function to perform a deep mutable value assignment while retaini ```typescript const Y = { z: 1 } // const Y = { z: 1 } - const X = { y: Y } // const X = { y: { z: 1 } } - const A = { x: X } // const A = { x: { y: { z: 1 } } } - Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } const R0 = A.x.y.z === 2 // const R0 = true - const R1 = A.x.y === Y // const R1 = true - const R2 = A.x === X // const R2 = true ``` @@ -1303,7 +1288,7 @@ const R2 = A.x === X // const R2 = true ### Pointer -Use ValuePointer to perform mutable updates on existing values using [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) JSON Pointers. +Use ValuePointer to perform mutable updates on existing values using [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) Json Pointers. ```typescript import { ValuePointer } from '@sinclair/typebox/value' @@ -1311,19 +1296,55 @@ import { ValuePointer } from '@sinclair/typebox/value' const A = { x: 0, y: 0, z: 0 } ValuePointer.Set(A, '/x', 1) // const A' = { x: 1, y: 0, z: 0 } - ValuePointer.Set(A, '/y', 1) // const A' = { x: 1, y: 1, z: 0 } - ValuePointer.Set(A, '/z', 1) // const A' = { x: 1, y: 1, z: 1 } ``` + + +## TypeRegistry + +The TypeBox type system can be extended with additional types and formats using the TypeRegistry and FormatRegistry modules. These modules integrate deeply with TypeBox's internal type checking infrastructure and can be used to create application specific types, or register schematics for alternative specifications. + + + +### TypeRegistry + +Use the TypeRegistry to register a new type. The Kind must match the registered type name. + +```typescript +import { TypeRegistry, Kind } from '@sinclair/typebox' + +TypeRegistry.Set('Foo', (schema, value) => value === 'foo') + +const A = Value.Check({ [Kind]: 'Foo' }, 'foo') // const A = true +const B = Value.Check({ [Kind]: 'Foo' }, 'bar') // const B = false +``` + + + +### FormatRegistry + +Use the FormatRegistry to register a string format. + +```typescript +import { FormatRegistry } from '@sinclair/typebox' + +FormatRegistry.Set('foo', (value) => value === 'foo') + +const T = Type.String({ format: 'foo' }) + +const A = Value.Check(T, 'foo') // const A = true +const B = Value.Check(T, 'bar') // const B = false +``` + ## TypeCheck -TypeBox types target JSON Schema draft 7 so are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for high performance compilation and value assertion. +TypeBox types target Json Schema Draft 7 and are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for TypeBox types that offers high performance compilation and value checking. -The following sections detail using Ajv and TypeBox's compiler infrastructure. +The following sections detail using Ajv and the TypeBox compiler infrastructure. @@ -1357,20 +1378,20 @@ const ajv = addFormats(new Ajv({}), [ 'regex' ]) -const C = ajv.compile(Type.Object({ +const validate = ajv.compile(Type.Object({ x: Type.Number(), y: Type.Number(), z: Type.Number() })) -const R = C({ x: 1, y: 2, z: 3 }) // const R = true +const R = validate({ x: 1, y: 2, z: 3 }) // const R = true ``` ### TypeCompiler -The TypeBox TypeCompiler is a high performance JIT compiler that transforms TypeBox types into optimized JavaScript validation routines. The compiler is tuned for fast compilation as well as fast value assertion. It is designed to serve as a validation backend that can be integrated into larger applications; but can also be used as a general purpose validator. +The TypeBox TypeCompiler is a high performance JIT validation compiler that transforms TypeBox types into optimized JavaScript validation routines. The compiler is tuned for fast compilation as well as fast value assertion. It is built to serve as a validation backend that can be integrated into larger applications. It can also be used for code generation. The TypeCompiler is provided as an optional import. @@ -1378,7 +1399,7 @@ The TypeCompiler is provided as an optional import. import { TypeCompiler } from '@sinclair/typebox/compiler' ``` -Use the `Compile(...)` function to compile a type. Note that compilation is an expensive operation that should typically be performed once per type during application start up. TypeBox does not cache previously compiled types, so applications are expected to hold references to each compiled type for the lifetime of the application. +Use the Compile function to JIT compile a type. Note that compilation is generally an expensive operation and should only be performed once per type during application start up. TypeBox does not cache previously compiled types, and applications are expected to hold references to each compiled type for the lifetime of the application. ```typescript const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck - -console.log(C.Code()) // return function check(value) { +const C = TypeCompiler.Code(Type.String()) // const C = `return function check(value) { // return ( // (typeof value === 'string') // ) - // } + // }` ``` ## TypeSystem -The TypeBox TypeSystem module provides functionality to define types above and beyond the Standard and Extended type sets as well as control various assertion policies. Configurations made to the TypeSystem module are observed by both `TypeCompiler` and `Value` modules. - -The TypeSystem module is provided as an optional import. - -```typescript -import { TypeSystem } from '@sinclair/typebox/system' -``` +The TypeBox TypeSystem module provides functionality to define types above and beyond the built-in Json and JavaScript type sets. They also manage TypeBox's localization options (i18n) for error message generation and can control various assertion policies used when type checking. Configurations made to the TypeSystem module are observed by the TypeCompiler, Value and Error modules. ### Types -Use the `Type(...)` function to create custom types. This function lets you specify custom value assertion logic and will return a type factory function which is used to instance the type. This function accepts two generic arguments, the first is the inference type, the second is options used to constrain the type. The following creates a Vector type. +Use the TypeSystem Type function to register a user defined type. ```typescript -type VectorOptions = { abs: boolean } - -type Vector = { x: number, y: number } +import { TypeSystem } from '@sinclair/typebox/system' -const Vector = TypeSystem.Type('Vector', (options, value) => { - return ( - typeof value === 'object' && value !== null && - 'x' in value && typeof value.x === 'number' && - 'y' in value && typeof value.y === 'number' && - (options.abs ? (value.x === Math.abs(value.x) && value.y === Math.abs(value.y)) : true) - ) +const StringSet = TypeSystem.Type>('StringSet', (options, value) => { + return value instanceof Set && [...value].every(value => typeof value === 'string') }) -const T = Vector({ abs: true }) - -type T = Static // type T = Vector +const T = StringSet({}) // Pass options if any -const R1 = Value.Check(T, { x: 1, y: 1 }) // const R1 = true +const A = Value.Check(T, new Set()) // const A = true +const B = Value.Check(T, new Set(['hello'])) // const B = true +const C = Value.Check(T, new Set([1])) // const C = false -const R2 = Value.Check(T, { x: 1, y: '1' }) // const R2 = false - -const R3 = Value.Check(T, { x: 1, y: -1 }) // const R3 = false ``` ### Formats -Use the `Format(...)` function to create a custom string format. The following creates a format that checks for lowercase strings. +Use the TypeSystem Format function to register a string format. ```typescript -TypeSystem.Format('lowercase', value => value === value.toLowerCase()) // format should be lowercase +import { TypeSystem } from '@sinclair/typebox/system' + +const F = TypeSystem.Format('foo', value => value === 'Foo') + +const T = Type.String({ format: F }) + +const A = Value.Check(T, 'foo') // const A = true +const B = Value.Check(T, 'bar') // const B = false +``` + + -const T = Type.String({ format: 'lowercase' }) +### Errors -const A = Value.Check(T, 'Hello') // const A = false +Use the TypeSystemErrorFunction to override validation error messages. This can be used to localize errors or create error messages for user defined types. -const B = Value.Check(T, 'hello') // const B = true +```typescript +import { TypeSystemErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/system' + +TypeSystemErrorFunction.Set((schema, errorType) => { // i18n override + switch(errorType) { + /* en-US */ case ValueErrorType.String: return 'Expected string' + /* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu' + /* ko-KR */ case ValueErrorType.Boolean: return '예상 부울' + /* en-US */ default: return DefaultErrorFunction(schema, errorType) + } +}) +const T = Type.Object({ // const T = { ... } + x: Type.String(), + y: Type.Number(), + z: Type.Boolean() +}) +const E = [...Value.Errors(T, { // const E = [{ + x: null, // type: 48, + y: null, // schema: { ... }, + z: null // path: '/x', +})] // value: null, + // message: 'Expected string' + // }, { + // type: 34, + // schema: { ... }, + // path: '/y', + // value: null, + // message: 'Nombre attendu' + // }, { + // type: 14, + // schema: { ... }, + // path: '/z', + // value: null, + // message: '예상 부울' + // }] ``` ### Policies -TypeBox validates using standard JSON Schema assertion policies by default. It is possible to override some of these policies to have TypeBox assert inline with TypeScript static assertion rules. The following policy overrides are available. +TypeBox validates using standard Json Schema assertion policies by default. The TypeSystemPolicy module can override some of these to have TypeBox check values inline with TypeScript static assertions. It also provides overrides for certain checking rules related to non-serializable values (such as void) which can be useful in Json based protocols such as JsonRpc-2. + +The following overrides are available. ```typescript +import { TypeSystemPolicy } from '@sinclair/typebox/system' + // Disallow undefined values for optional properties (default is false) // // const A: { x?: number } = { x: undefined } - disallowed when enabled -TypeSystem.ExactOptionalPropertyTypes = true +TypeSystemPolicy.ExactOptionalPropertyTypes = true // Allow arrays to validate as object types (default is false) // // const A: {} = [] - allowed in TS -TypeSystem.AllowArrayObjects = true +TypeSystemPolicy.AllowArrayObject = true // Allow numeric values to be NaN or + or - Infinity (default is false) // // const A: number = NaN - allowed in TS -TypeSystem.AllowNaN = true +TypeSystemPolicy.AllowNaN = true + +// Allow void types to check with undefined and null (default is false) +// +// Used to signal void return on Json-RPC 2.0 protocol + +TypeSystemPolicy.AllowNullVoid = true ``` ## TypeBox Workbench -TypeBox offers a small web based code generation tool that can be used to convert TypeScript types into TypeBox types as well as a variety of other runtime type representations. +TypeBox offers a web based code generation tool that can convert TypeScript types into TypeBox types as well as several other ecosystem libraries. [TypeBox Workbench Link Here](https://sinclairzx81.github.io/typebox-workbench/) + + +## TypeBox Codegen + +TypeBox provides a code generation library that can be used to automate type translation between TypeScript and TypeBox. This library also includes functionality to transform TypeScript types to other ecosystem libraries. + +[TypeBox Codegen Link Here](https://github.com/sinclairzx81/typebox-codegen) + ## Ecosystem -The following is a list of community packages that provide general tooling and framework integration support for TypeBox. +The following is a list of community packages that offer general tooling, extended functionality and framework integration support for TypeBox. | Package | Description | | ------------- | ------------- | @@ -1536,7 +1608,7 @@ The following is a list of community packages that provide general tooling and f | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | -| [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from JSON schemas | +| [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | @@ -1559,35 +1631,35 @@ This benchmark measures compilation performance for varying types. You can revie ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 232 ms' │ ' 8 ms' │ ' 29.00 x' │ -│ Literal_Number │ 1000 │ ' 179 ms' │ ' 6 ms' │ ' 29.83 x' │ -│ Literal_Boolean │ 1000 │ ' 154 ms' │ ' 3 ms' │ ' 51.33 x' │ -│ Primitive_Number │ 1000 │ ' 160 ms' │ ' 7 ms' │ ' 22.86 x' │ -│ Primitive_String │ 1000 │ ' 149 ms' │ ' 6 ms' │ ' 24.83 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 191 ms' │ ' 9 ms' │ ' 21.22 x' │ -│ Primitive_Boolean │ 1000 │ ' 135 ms' │ ' 4 ms' │ ' 33.75 x' │ -│ Primitive_Null │ 1000 │ ' 144 ms' │ ' 6 ms' │ ' 24.00 x' │ -│ Object_Unconstrained │ 1000 │ ' 1144 ms' │ ' 30 ms' │ ' 38.13 x' │ -│ Object_Constrained │ 1000 │ ' 1228 ms' │ ' 24 ms' │ ' 51.17 x' │ -│ Object_Vector3 │ 1000 │ ' 380 ms' │ ' 9 ms' │ ' 42.22 x' │ -│ Object_Box3D │ 1000 │ ' 1771 ms' │ ' 30 ms' │ ' 59.03 x' │ -│ Tuple_Primitive │ 1000 │ ' 471 ms' │ ' 11 ms' │ ' 42.82 x' │ -│ Tuple_Object │ 1000 │ ' 1272 ms' │ ' 15 ms' │ ' 84.80 x' │ -│ Composite_Intersect │ 1000 │ ' 606 ms' │ ' 17 ms' │ ' 35.65 x' │ -│ Composite_Union │ 1000 │ ' 560 ms' │ ' 22 ms' │ ' 25.45 x' │ -│ Math_Vector4 │ 1000 │ ' 824 ms' │ ' 14 ms' │ ' 58.86 x' │ -│ Math_Matrix4 │ 1000 │ ' 419 ms' │ ' 9 ms' │ ' 46.56 x' │ -│ Array_Primitive_Number │ 1000 │ ' 382 ms' │ ' 6 ms' │ ' 63.67 x' │ -│ Array_Primitive_String │ 1000 │ ' 324 ms' │ ' 6 ms' │ ' 54.00 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 301 ms' │ ' 4 ms' │ ' 75.25 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1734 ms' │ ' 21 ms' │ ' 82.57 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1509 ms' │ ' 20 ms' │ ' 75.45 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 824 ms' │ ' 14 ms' │ ' 58.86 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1619 ms' │ ' 16 ms' │ ' 101.19 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 773 ms' │ ' 16 ms' │ ' 48.31 x' │ -│ Array_Composite_Union │ 1000 │ ' 822 ms' │ ' 17 ms' │ ' 48.35 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1131 ms' │ ' 13 ms' │ ' 87.00 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 661 ms' │ ' 10 ms' │ ' 66.10 x' │ +│ Literal_String │ 1000 │ ' 216 ms' │ ' 9 ms' │ ' 24.00 x' │ +│ Literal_Number │ 1000 │ ' 169 ms' │ ' 7 ms' │ ' 24.14 x' │ +│ Literal_Boolean │ 1000 │ ' 150 ms' │ ' 5 ms' │ ' 30.00 x' │ +│ Primitive_Number │ 1000 │ ' 161 ms' │ ' 7 ms' │ ' 23.00 x' │ +│ Primitive_String │ 1000 │ ' 148 ms' │ ' 6 ms' │ ' 24.67 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 185 ms' │ ' 9 ms' │ ' 20.56 x' │ +│ Primitive_Boolean │ 1000 │ ' 132 ms' │ ' 4 ms' │ ' 33.00 x' │ +│ Primitive_Null │ 1000 │ ' 141 ms' │ ' 3 ms' │ ' 47.00 x' │ +│ Object_Unconstrained │ 1000 │ ' 1109 ms' │ ' 30 ms' │ ' 36.97 x' │ +│ Object_Constrained │ 1000 │ ' 1200 ms' │ ' 24 ms' │ ' 50.00 x' │ +│ Object_Vector3 │ 1000 │ ' 379 ms' │ ' 9 ms' │ ' 42.11 x' │ +│ Object_Box3D │ 1000 │ ' 1709 ms' │ ' 30 ms' │ ' 56.97 x' │ +│ Tuple_Primitive │ 1000 │ ' 456 ms' │ ' 14 ms' │ ' 32.57 x' │ +│ Tuple_Object │ 1000 │ ' 1229 ms' │ ' 17 ms' │ ' 72.29 x' │ +│ Composite_Intersect │ 1000 │ ' 570 ms' │ ' 17 ms' │ ' 33.53 x' │ +│ Composite_Union │ 1000 │ ' 513 ms' │ ' 19 ms' │ ' 27.00 x' │ +│ Math_Vector4 │ 1000 │ ' 782 ms' │ ' 13 ms' │ ' 60.15 x' │ +│ Math_Matrix4 │ 1000 │ ' 393 ms' │ ' 12 ms' │ ' 32.75 x' │ +│ Array_Primitive_Number │ 1000 │ ' 361 ms' │ ' 12 ms' │ ' 30.08 x' │ +│ Array_Primitive_String │ 1000 │ ' 296 ms' │ ' 5 ms' │ ' 59.20 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 315 ms' │ ' 4 ms' │ ' 78.75 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1721 ms' │ ' 22 ms' │ ' 78.23 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1450 ms' │ ' 21 ms' │ ' 69.05 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 813 ms' │ ' 13 ms' │ ' 62.54 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1537 ms' │ ' 17 ms' │ ' 90.41 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 753 ms' │ ' 17 ms' │ ' 44.29 x' │ +│ Array_Composite_Union │ 1000 │ ' 808 ms' │ ' 16 ms' │ ' 50.50 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1118 ms' │ ' 16 ms' │ ' 69.88 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 690 ms' │ ' 9 ms' │ ' 76.67 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1601,37 +1673,37 @@ This benchmark measures validation performance for varying types. You can review ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 18 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 15 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Literal_Boolean │ 1000000 │ ' 13 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ -│ Primitive_Number │ 1000000 │ ' 21 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ -│ Primitive_String │ 1000000 │ ' 19 ms' │ ' 16 ms' │ ' 10 ms' │ ' 1.60 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 150 ms' │ ' 41 ms' │ ' 35 ms' │ ' 1.17 x' │ -│ Primitive_Boolean │ 1000000 │ ' 17 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Null │ 1000000 │ ' 18 ms' │ ' 16 ms' │ ' 9 ms' │ ' 1.78 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1001 ms' │ ' 31 ms' │ ' 24 ms' │ ' 1.29 x' │ -│ Object_Constrained │ 1000000 │ ' 1288 ms' │ ' 50 ms' │ ' 36 ms' │ ' 1.39 x' │ -│ Object_Vector3 │ 1000000 │ ' 439 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Object_Box3D │ 1000000 │ ' 2109 ms' │ ' 52 ms' │ ' 45 ms' │ ' 1.16 x' │ -│ Object_Recursive │ 1000000 │ ' 5337 ms' │ ' 356 ms' │ ' 162 ms' │ ' 2.20 x' │ -│ Tuple_Primitive │ 1000000 │ ' 164 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Tuple_Object │ 1000000 │ ' 744 ms' │ ' 29 ms' │ ' 18 ms' │ ' 1.61 x' │ -│ Composite_Intersect │ 1000000 │ ' 764 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Composite_Union │ 1000000 │ ' 516 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Math_Vector4 │ 1000000 │ ' 262 ms' │ ' 20 ms' │ ' 11 ms' │ ' 1.82 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1089 ms' │ ' 37 ms' │ ' 27 ms' │ ' 1.37 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 276 ms' │ ' 21 ms' │ ' 11 ms' │ ' 1.91 x' │ -│ Array_Primitive_String │ 1000000 │ ' 228 ms' │ ' 21 ms' │ ' 14 ms' │ ' 1.50 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 159 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 5695 ms' │ ' 77 ms' │ ' 69 ms' │ ' 1.12 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5701 ms' │ ' 127 ms' │ ' 110 ms' │ ' 1.15 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 21267 ms' │ ' 1664 ms' │ ' 573 ms' │ ' 2.90 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 702 ms' │ ' 40 ms' │ ' 32 ms' │ ' 1.25 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3141 ms' │ ' 68 ms' │ ' 51 ms' │ ' 1.33 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3145 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2134 ms' │ ' 68 ms' │ ' 31 ms' │ ' 2.19 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1197 ms' │ ' 37 ms' │ ' 25 ms' │ ' 1.48 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 5323 ms' │ ' 111 ms' │ ' 96 ms' │ ' 1.16 x' │ +│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 15 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ +│ Literal_Boolean │ 1000000 │ ' 14 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ +│ Primitive_Number │ 1000000 │ ' 25 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Primitive_String │ 1000000 │ ' 21 ms' │ ' 24 ms' │ ' 9 ms' │ ' 2.67 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 156 ms' │ ' 43 ms' │ ' 38 ms' │ ' 1.13 x' │ +│ Primitive_Boolean │ 1000000 │ ' 18 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Primitive_Null │ 1000000 │ ' 20 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1055 ms' │ ' 32 ms' │ ' 24 ms' │ ' 1.33 x' │ +│ Object_Constrained │ 1000000 │ ' 1232 ms' │ ' 49 ms' │ ' 43 ms' │ ' 1.14 x' │ +│ Object_Vector3 │ 1000000 │ ' 432 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Object_Box3D │ 1000000 │ ' 1993 ms' │ ' 54 ms' │ ' 46 ms' │ ' 1.17 x' │ +│ Object_Recursive │ 1000000 │ ' 5115 ms' │ ' 342 ms' │ ' 159 ms' │ ' 2.15 x' │ +│ Tuple_Primitive │ 1000000 │ ' 156 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Tuple_Object │ 1000000 │ ' 740 ms' │ ' 29 ms' │ ' 18 ms' │ ' 1.61 x' │ +│ Composite_Intersect │ 1000000 │ ' 797 ms' │ ' 26 ms' │ ' 14 ms' │ ' 1.86 x' │ +│ Composite_Union │ 1000000 │ ' 530 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ +│ Math_Vector4 │ 1000000 │ ' 240 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1036 ms' │ ' 39 ms' │ ' 27 ms' │ ' 1.44 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 248 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Array_Primitive_String │ 1000000 │ ' 227 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 138 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 5540 ms' │ ' 66 ms' │ ' 59 ms' │ ' 1.12 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 5750 ms' │ ' 123 ms' │ ' 108 ms' │ ' 1.14 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 21842 ms' │ ' 1771 ms' │ ' 599 ms' │ ' 2.96 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 715 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3131 ms' │ ' 63 ms' │ ' 50 ms' │ ' 1.26 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3064 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2172 ms' │ ' 65 ms' │ ' 31 ms' │ ' 2.10 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1032 ms' │ ' 37 ms' │ ' 24 ms' │ ' 1.54 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 4859 ms' │ ' 114 ms' │ ' 86 ms' │ ' 1.33 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1645,11 +1717,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '131.4 kb' │ ' 59.4 kb' │ '2.21 x' │ -│ typebox/errors │ '113.6 kb' │ ' 50.9 kb' │ '2.23 x' │ -│ typebox/system │ ' 78.5 kb' │ ' 32.5 kb' │ '2.42 x' │ -│ typebox/value │ '182.8 kb' │ ' 80.0 kb' │ '2.28 x' │ -│ typebox │ ' 77.4 kb' │ ' 32.0 kb' │ '2.42 x' │ +│ typebox/compiler │ '148.9 kb' │ ' 65.8 kb' │ '2.26 x' │ +│ typebox/errors │ '111.5 kb' │ ' 49.1 kb' │ '2.27 x' │ +│ typebox/system │ ' 82.6 kb' │ ' 36.8 kb' │ '2.24 x' │ +│ typebox/value │ '190.5 kb' │ ' 82.4 kb' │ '2.31 x' │ +│ typebox │ ' 72.4 kb' │ ' 31.6 kb' │ '2.29 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 1fd7f1def..d469ccde3 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -26,11 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeSystem } from '../system/index' +import { EncodeTransform, DecodeTransform, HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform' +import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard' +import { Errors, ValueErrorIterator } from '../errors/errors' +import { TypeSystemPolicy } from '../system/index' +import { Deref } from '../value/deref' +import { Hash } from '../value/hash' import * as Types from '../typebox' -import * as ValueErrors from '../errors/index' -import * as ValueHash from '../value/hash' -import * as ValueGuard from '../value/guard' // ------------------------------------------------------------------- // CheckFunction @@ -40,19 +42,33 @@ export type CheckFunction = (value: unknown) => boolean // TypeCheck // ------------------------------------------------------------------- export class TypeCheck { - constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) {} + private readonly hasTransform: boolean + constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) { + this.hasTransform = HasTransform.Has(schema, references) + } /** Returns the generated assertion code used to validate this type. */ public Code(): string { return this.code } /** Returns an iterator for each error in this value. */ - public Errors(value: unknown): ValueErrors.ValueErrorIterator { - return ValueErrors.Errors(this.schema, this.references, value) + public Errors(value: unknown): ValueErrorIterator { + return Errors(this.schema, this.references, value) } /** Returns true if the value matches the compiled type. */ public Check(value: unknown): value is Types.Static { return this.checkFunc(value) } + /** Decodes a value or throws if error */ + public Decode(value: unknown): Types.StaticDecode { + if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) + return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value + } + /** Encodes a value or throws if error */ + public Encode(value: unknown): Types.StaticEncode { + const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value + if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) + return encoded + } } // ------------------------------------------------------------------- // Character @@ -115,26 +131,43 @@ namespace Identifier { // ------------------------------------------------------------------- // Errors // ------------------------------------------------------------------- -export class TypeCompilerUnknownTypeError extends Error { +export class TypeCompilerUnknownTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('TypeCompiler: Unknown type') + super('Unknown type') } } -export class TypeCompilerDereferenceError extends Error { - constructor(public readonly schema: Types.TRef) { - super(`TypeCompiler: Unable to dereference type with $id '${schema.$ref}'`) +export class TypeCompilerTypeGuardError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema) { + super('Preflight validation check failed to guard for the given schema') } } -export class TypeCompilerTypeGuardError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('TypeCompiler: Preflight validation check failed to guard for the given schema') +// ------------------------------------------------------------------- +// Policy +// ------------------------------------------------------------------- +export namespace Policy { + export function IsExactOptionalProperty(value: string, key: string, expression: string) { + return TypeSystemPolicy.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)` + } + export function IsObjectLike(value: string): string { + return !TypeSystemPolicy.AllowArrayObject ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)` + } + export function IsRecordLike(value: string): string { + return !TypeSystemPolicy.AllowArrayObject + ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` + : `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` + } + export function IsNumberLike(value: string): string { + return !TypeSystemPolicy.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` + } + export function IsVoidLike(value: string): string { + return TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` } } // ------------------------------------------------------------------- // TypeCompiler // ------------------------------------------------------------------- export type TypeCompilerLanguageOption = 'typescript' | 'javascript' -export interface TypeCompilerOptions { +export interface TypeCompilerCodegenOptions { language?: TypeCompilerLanguageOption } /** Compiles Types for Runtime Type Checking */ @@ -146,26 +179,6 @@ export namespace TypeCompiler { return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' } // ------------------------------------------------------------------- - // Polices - // ------------------------------------------------------------------- - function IsExactOptionalProperty(value: string, key: string, expression: string) { - return TypeSystem.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)` - } - function IsObjectCheck(value: string): string { - return !TypeSystem.AllowArrayObjects ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)` - } - function IsRecordCheck(value: string): string { - return !TypeSystem.AllowArrayObjects - ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` - : `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` - } - function IsNumberCheck(value: string): string { - return !TypeSystem.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` - } - function IsVoidCheck(value: string): string { - return TypeSystem.AllowVoidNull ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` - } - // ------------------------------------------------------------------- // Types // ------------------------------------------------------------------- function* TAny(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { @@ -174,15 +187,15 @@ export namespace TypeCompiler { function* TArray(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')] - if (ValueGuard.IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` - if (ValueGuard.IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` + if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` + if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` const elementExpression = CreateExpression(schema.items, references, 'value') yield `${value}.every((${parameter}) => ${elementExpression})` - if (Types.TypeGuard.TSchema(schema.contains) || ValueGuard.IsNumber(schema.minContains) || ValueGuard.IsNumber(schema.maxContains)) { + if (Types.TypeGuard.TSchema(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { const containsSchema = Types.TypeGuard.TSchema(schema.contains) ? schema.contains : Types.Type.Never() const checkExpression = CreateExpression(containsSchema, references, 'value') - const checkMinContains = ValueGuard.IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] - const checkMaxContains = ValueGuard.IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] + const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] + const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] const checkCount = `const count = ${value}.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ') yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})` @@ -198,11 +211,11 @@ export namespace TypeCompiler { } function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` - if (ValueGuard.IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` - if (ValueGuard.IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` - if (ValueGuard.IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` - if (ValueGuard.IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` - if (ValueGuard.IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` + if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` + if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` + if (IsBigInt(schema.maximum)) yield `${value} <= BigInt(${schema.maximum})` + if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` + if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` } function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'boolean')` @@ -212,21 +225,22 @@ export namespace TypeCompiler { } function* TDate(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())` - if (ValueGuard.IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` - if (ValueGuard.IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` - if (ValueGuard.IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` - if (ValueGuard.IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` + if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` + if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` + if (IsNumber(schema.maximumTimestamp)) yield `${value}.getTime() <= ${schema.maximumTimestamp}` + if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` + if (IsNumber(schema.multipleOfTimestamp)) yield `(${value}.getTime() % ${schema.multipleOfTimestamp}) === 0` } function* TFunction(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } function* TInteger(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` - if (ValueGuard.IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` - if (ValueGuard.IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` - if (ValueGuard.IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` - if (ValueGuard.IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` - if (ValueGuard.IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ') @@ -263,17 +277,17 @@ export namespace TypeCompiler { yield `(${value} === null)` } function* TNumber(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { - yield IsNumberCheck(value) - if (ValueGuard.IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` - if (ValueGuard.IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` - if (ValueGuard.IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` - if (ValueGuard.IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` - if (ValueGuard.IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + yield Policy.IsNumberLike(value) + if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` + if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` + if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` + if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` + if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } function* TObject(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { - yield IsObjectCheck(value) - if (ValueGuard.IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` - if (ValueGuard.IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` + yield Policy.IsObjectLike(value) + if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const knownKeys = Object.getOwnPropertyNames(schema.properties) for (const knownKey of knownKeys) { const memberExpression = MemberExpression.Encode(value, knownKey) @@ -283,7 +297,7 @@ export namespace TypeCompiler { if (Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` } else { const expression = CreateExpression(property, references, memberExpression) - yield IsExactOptionalProperty(value, knownKey, expression) + yield Policy.IsExactOptionalProperty(value, knownKey, expression) } } if (schema.additionalProperties === false) { @@ -304,9 +318,9 @@ export namespace TypeCompiler { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } function* TRecord(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { - yield IsRecordCheck(value) - if (ValueGuard.IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` - if (ValueGuard.IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` + yield Policy.IsRecordLike(value) + if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` + if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] const variable = CreateVariable(`new RegExp(/${patternKey}/)`) const check1 = CreateExpression(patternSchema, references, 'value') @@ -315,9 +329,7 @@ export namespace TypeCompiler { yield `(Object.entries(${value}).every(([key, value]) => ${expression}))` } function* TRef(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new TypeCompilerDereferenceError(schema) - const target = references[index] + const target = Deref(schema, references) // Reference: If we have seen this reference before we can just yield and return the function call. // If this isn't the case we defer to visit to generate and set the function for subsequent passes. if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` @@ -325,8 +337,8 @@ export namespace TypeCompiler { } function* TString(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` - if (ValueGuard.IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` - if (ValueGuard.IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` + if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` + if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` if (schema.pattern !== undefined) { const variable = CreateVariable(`${new RegExp(schema.pattern)};`) yield `${variable}.test(${value})` @@ -344,8 +356,8 @@ export namespace TypeCompiler { yield `${variable}.test(${value})` } function* TThis(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { - const func = CreateFunctionName(schema.$ref) - yield `${func}(${value})` + // Note: This types are assured to be hoisted prior to this call. Just yield the function. + yield `${CreateFunctionName(schema.$ref)}(${value})` } function* TTuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` @@ -365,14 +377,14 @@ export namespace TypeCompiler { } function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { yield `${value} instanceof Uint8Array` - if (ValueGuard.IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` - if (ValueGuard.IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` + if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` + if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { yield 'true' } function* TVoid(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { - yield IsVoidCheck(value) + yield Policy.IsVoidLike(value) } function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { const instance = state.instances.size @@ -380,12 +392,12 @@ export namespace TypeCompiler { yield `kind('${schema[Types.Kind]}', ${instance}, ${value})` } function* Visit(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator { - const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any // ---------------------------------------------------------------------------------- // Hoisting // ---------------------------------------------------------------------------------- - if (useHoisting && ValueGuard.IsString(schema.$id)) { + if (useHoisting && IsString(schema.$id)) { const functionName = CreateFunctionName(schema.$id) if (state.functions.has(functionName)) { return yield `${functionName}(${value})` @@ -395,9 +407,6 @@ export namespace TypeCompiler { return yield `${functionName}(${value})` } } - // ---------------------------------------------------------------------------------- - // Types - // ---------------------------------------------------------------------------------- switch (schema_[Types.Kind]) { case 'Any': return yield* TAny(schema_, references_, value) @@ -505,29 +514,29 @@ export namespace TypeCompiler { // ------------------------------------------------------------------- // Compile // ------------------------------------------------------------------- - function Build(schema: T, references: Types.TSchema[], options: TypeCompilerOptions): string { + function Build(schema: T, references: Types.TSchema[], options: TypeCompilerCodegenOptions): string { const functionCode = CreateFunction('check', schema, references, 'value') // will populate functions and variables const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') const functions = [...state.functions.values()] const variables = [...state.variables.values()] // prettier-ignore - const checkFunction = ValueGuard.IsString(schema.$id) // ensure top level schemas with $id's are hoisted + const checkFunction = IsString(schema.$id) // ensure top level schemas with $id's are hoisted ? `return function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}` : `return ${functionCode}` return [...variables, ...functions, checkFunction].join('\n') } - /** Returns the generated assertion code used to validate this type. */ - export function Code(schema: T, references: Types.TSchema[], options?: TypeCompilerOptions): string - /** Returns the generated assertion code used to validate this type. */ - export function Code(schema: T, options?: TypeCompilerOptions): string - /** Returns the generated assertion code used to validate this type. */ + /** Generates the code used to assert this type and returns it as a string */ + export function Code(schema: T, references: Types.TSchema[], options?: TypeCompilerCodegenOptions): string + /** Generates the code used to assert this type and returns it as a string */ + export function Code(schema: T, options?: TypeCompilerCodegenOptions): string + /** Generates the code used to assert this type and returns it as a string */ export function Code(...args: any[]) { const defaults = { language: 'javascript' } // prettier-ignore const [schema, references, options] = ( - args.length === 2 && ValueGuard.IsArray(args[1]) ? [args[0], args[1], defaults] : - args.length === 2 && !ValueGuard.IsArray(args[1]) ? [args[0], [], args[1]] : + args.length === 2 && IsArray(args[1]) ? [args[0], args[1], defaults] : + args.length === 2 && !IsArray(args[1]) ? [args[0], [], args[1]] : args.length === 3 ? [args[0], args[1], args[2]] : args.length === 1 ? [args[0], [], defaults] : [null, [], defaults] @@ -541,15 +550,15 @@ export namespace TypeCompiler { for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) return Build(schema, references, options) } - /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */ + /** Compiles a TypeBox type for optimal runtime type checking. Types must be valid TypeBox types of TSchema */ export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { const generatedCode = Code(schema, references, { language: 'javascript' }) const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) const instances = new Map(state.instances) function typeRegistryFunction(kind: string, instance: number, value: unknown) { if (!Types.TypeRegistry.Has(kind) || !instances.has(instance)) return false - const schema = instances.get(instance) const checkFunc = Types.TypeRegistry.Get(kind)! + const schema = instances.get(instance)! return checkFunc(schema, value) } function formatRegistryFunction(format: string, value: string) { @@ -557,10 +566,10 @@ export namespace TypeCompiler { const checkFunc = Types.FormatRegistry.Get(format)! return checkFunc(value) } - function valueHashFunction(value: unknown) { - return ValueHash.Hash(value) + function hashFunction(value: unknown) { + return Hash(value) } - const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, valueHashFunction) + const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, hashFunction) return new TypeCheck(schema, references, checkFunction, generatedCode) } } diff --git a/src/compiler/index.ts b/src/compiler/index.ts index e747dd76a..cc91cb40b 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -26,5 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { ValueError, ValueErrorType } from '../errors/index' +export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' export * from './compiler' diff --git a/src/errors/errors.ts b/src/errors/errors.ts index f7ee21a19..f9b4b4edc 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -26,79 +26,79 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeSystem } from '../system/index' +import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../value/guard' +import { TypeSystemPolicy, TypeSystemErrorFunction } from '../system/system' +import { Deref } from '../value/deref' +import { Hash } from '../value/hash' import * as Types from '../typebox' -import * as ValueHash from '../value/hash' -import * as ValueGuard from '../value/guard' // -------------------------------------------------------------------------- // ValueErrorType // -------------------------------------------------------------------------- export enum ValueErrorType { - Array, - ArrayMinItems, - ArrayMaxItems, ArrayContains, - ArrayMinContains, ArrayMaxContains, + ArrayMaxItems, + ArrayMinContains, + ArrayMinItems, ArrayUniqueItems, + Array, AsyncIterator, - BigInt, - BigIntMultipleOf, - BigIntExclusiveMinimum, BigIntExclusiveMaximum, - BigIntMinimum, + BigIntExclusiveMinimum, BigIntMaximum, + BigIntMinimum, + BigIntMultipleOf, + BigInt, Boolean, - Date, - DateExclusiveMinimumTimestamp, DateExclusiveMaximumTimestamp, - DateMinimumTimestamp, + DateExclusiveMinimumTimestamp, DateMaximumTimestamp, + DateMinimumTimestamp, + DateMultipleOfTimestamp, + Date, Function, - Integer, - IntegerMultipleOf, - IntegerExclusiveMinimum, IntegerExclusiveMaximum, - IntegerMinimum, + IntegerExclusiveMinimum, IntegerMaximum, - Intersect, + IntegerMinimum, + IntegerMultipleOf, + Integer, IntersectUnevaluatedProperties, + Intersect, Iterator, + Kind, Literal, Never, Not, Null, - Number, - NumberMultipleOf, - NumberExclusiveMinimum, NumberExclusiveMaximum, - NumberMinimum, + NumberExclusiveMinimum, NumberMaximum, - Object, - ObjectMinProperties, - ObjectMaxProperties, + NumberMinimum, + NumberMultipleOf, + Number, ObjectAdditionalProperties, - ObjectRequiredProperties, + ObjectMaxProperties, + ObjectMinProperties, + ObjectRequiredProperty, + Object, Promise, - RecordKeyNumeric, - RecordKeyString, - String, - StringMinLength, - StringMaxLength, - StringPattern, StringFormatUnknown, StringFormat, + StringMaxLength, + StringMinLength, + StringPattern, + String, Symbol, - TupleZeroLength, TupleLength, + Tuple, + Uint8ArrayMaxByteLength, + Uint8ArrayMinByteLength, + Uint8Array, Undefined, Union, - Uint8Array, - Uint8ArrayMinByteLength, - Uint8ArrayMaxByteLength, Void, - Kind, } // -------------------------------------------------------------------------- // ValueError @@ -111,6 +111,20 @@ export interface ValueError { message: string } // -------------------------------------------------------------------------- +// ValueErrors +// -------------------------------------------------------------------------- +export class ValueErrorsUnknownTypeError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema) { + super('Unknown type') + } +} +// -------------------------------------------------------------------------- +// Guards +// -------------------------------------------------------------------------- +function IsDefined(value: unknown): value is T { + return value !== undefined +} +// -------------------------------------------------------------------------- // ValueErrorIterator // -------------------------------------------------------------------------- export class ValueErrorIterator { @@ -125,175 +139,127 @@ export class ValueErrorIterator { } } // -------------------------------------------------------------------------- -// ValueErrors +// Create // -------------------------------------------------------------------------- -export class ValueErrorsUnknownTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { - super('ValueErrors: Unknown type') - } -} -export class ValueErrorsDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueErrors: Unable to dereference type with $id '${schema.$ref}'`) - } -} -// -------------------------------------------------------------------------- -// Guards -// -------------------------------------------------------------------------- -function IsDefined(value: unknown): value is T { - return value !== undefined -} -// -------------------------------------------------------------------------- -// Policies -// -------------------------------------------------------------------------- -function IsExactOptionalProperty(value: Record, key: string) { - return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined -} -function IsObject(value: unknown): value is Record { - const isObject = ValueGuard.IsObject(value) - return TypeSystem.AllowArrayObjects ? isObject : isObject && !ValueGuard.IsArray(value) -} -function IsRecordObject(value: unknown): value is Record { - return IsObject(value) && !(value instanceof Date) && !(value instanceof Uint8Array) -} -function IsNumber(value: unknown): value is number { - const isNumber = ValueGuard.IsNumber(value) - return TypeSystem.AllowNaN ? isNumber : isNumber && Number.isFinite(value) -} -function IsVoid(value: unknown): value is void { - const isUndefined = ValueGuard.IsUndefined(value) - return TypeSystem.AllowVoidNull ? isUndefined || value === null : isUndefined +function Create(type: ValueErrorType, schema: Types.TSchema, path: string, value: unknown): ValueError { + return { type, schema, path, value, message: TypeSystemErrorFunction.Get()(schema, type) } } // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- function* TAny(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsArray(value)) { - return yield { type: ValueErrorType.Array, schema, path, value, message: `Expected array` } + if (!IsArray(value)) { + return yield Create(ValueErrorType.Array, schema, path, value) } if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { - yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` } + yield Create(ValueErrorType.ArrayMinItems, schema, path, value) } if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) { - yield { type: ValueErrorType.ArrayMinItems, schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` } + yield Create(ValueErrorType.ArrayMaxItems, schema, path, value) } - for (let i = 0; i < value.length; i++) { yield* Visit(schema.items, references, `${path}/${i}`, value[i]) } // prettier-ignore - if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = ValueHash.Hash(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { - yield { type: ValueErrorType.ArrayUniqueItems, schema, path, value, message: `Expected array elements to be unique` } + if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) { const hashed = Hash(element); if (set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { + yield Create(ValueErrorType.ArrayUniqueItems, schema, path, value) } // contains - if (!(IsDefined(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains))) { + if (!(IsDefined(schema.contains) || IsDefined(schema.minContains) || IsDefined(schema.maxContains))) { return } const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() const containsCount = value.reduce((acc: number, value, index) => (Visit(containsSchema, references, `${path}${index}`, value).next().done === true ? acc + 1 : acc), 0) if (containsCount === 0) { - yield { type: ValueErrorType.ArrayContains, schema, path, value, message: `Expected array to contain at least one matching type` } + yield Create(ValueErrorType.ArrayContains, schema, path, value) } - if (ValueGuard.IsNumber(schema.minContains) && containsCount < schema.minContains) { - yield { type: ValueErrorType.ArrayMinContains, schema, path, value, message: `Expected array to contain at least ${schema.minContains} matching types` } + if (IsNumber(schema.minContains) && containsCount < schema.minContains) { + yield Create(ValueErrorType.ArrayMinContains, schema, path, value) } - if (ValueGuard.IsNumber(schema.maxContains) && containsCount > schema.maxContains) { - yield { type: ValueErrorType.ArrayMaxContains, schema, path, value, message: `Expected array to contain no more than ${schema.maxContains} matching types` } + if (IsNumber(schema.maxContains) && containsCount > schema.maxContains) { + yield Create(ValueErrorType.ArrayMaxContains, schema, path, value) } } function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsAsyncIterator(value)) { - yield { type: ValueErrorType.AsyncIterator, schema, path, value, message: `Expected value to be an async iterator` } - } + if (!IsAsyncIterator(value)) yield Create(ValueErrorType.AsyncIterator, schema, path, value) } function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsBigInt(value)) { - return yield { type: ValueErrorType.BigInt, schema, path, value, message: `Expected bigint` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { - yield { type: ValueErrorType.BigIntMultipleOf, schema, path, value, message: `Expected bigint to be a multiple of ${schema.multipleOf}` } + if (!IsBigInt(value)) return yield Create(ValueErrorType.BigInt, schema, path, value) + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield Create(ValueErrorType.BigIntExclusiveMaximum, schema, path, value) } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.BigIntExclusiveMinimum, schema, path, value, message: `Expected bigint to be greater than ${schema.exclusiveMinimum}` } + yield Create(ValueErrorType.BigIntExclusiveMinimum, schema, path, value) } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.BigIntExclusiveMaximum, schema, path, value, message: `Expected bigint to be less than ${schema.exclusiveMaximum}` } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield Create(ValueErrorType.BigIntMaximum, schema, path, value) } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.BigIntMinimum, schema, path, value, message: `Expected bigint to be greater or equal to ${schema.minimum}` } + yield Create(ValueErrorType.BigIntMinimum, schema, path, value) } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.BigIntMaximum, schema, path, value, message: `Expected bigint to be less or equal to ${schema.maximum}` } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { + yield Create(ValueErrorType.BigIntMultipleOf, schema, path, value) } } function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsBoolean(value)) { - return yield { type: ValueErrorType.Boolean, schema, path, value, message: `Expected boolean` } - } + if (!IsBoolean(value)) yield Create(ValueErrorType.Boolean, schema, path, value) } function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { yield* Visit(schema.returns, references, path, value.prototype) } function* TDate(schema: Types.TDate, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsDate(value)) { - return yield { type: ValueErrorType.Date, schema, path, value, message: `Expected Date object` } - } - if (!isFinite(value.getTime())) { - return yield { type: ValueErrorType.Date, schema, path, value, message: `Invalid Date` } + if (!IsDate(value)) return yield Create(ValueErrorType.Date, schema, path, value) + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + yield Create(ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value) } if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater than ${schema.exclusiveMinimum}` } + yield Create(ValueErrorType.DateExclusiveMinimumTimestamp, schema, path, value) } - if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { - yield { type: ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less than ${schema.exclusiveMaximum}` } + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + yield Create(ValueErrorType.DateMaximumTimestamp, schema, path, value) } if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { - yield { type: ValueErrorType.DateMinimumTimestamp, schema, path, value, message: `Expected Date timestamp to be greater or equal to ${schema.minimum}` } + yield Create(ValueErrorType.DateMinimumTimestamp, schema, path, value) } - if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { - yield { type: ValueErrorType.DateMaximumTimestamp, schema, path, value, message: `Expected Date timestamp to be less or equal to ${schema.maximum}` } + if (IsDefined(schema.multipleOfTimestamp) && !(value.getTime() % schema.multipleOfTimestamp === 0)) { + yield Create(ValueErrorType.DateMultipleOfTimestamp, schema, path, value) } } function* TFunction(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsFunction(value)) { - return yield { type: ValueErrorType.Function, schema, path, value, message: `Expected function` } - } + if (!IsFunction(value)) yield Create(ValueErrorType.Function, schema, path, value) } function* TInteger(schema: Types.TInteger, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsInteger(value)) { - return yield { type: ValueErrorType.Integer, schema, path, value, message: `Expected integer` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - yield { type: ValueErrorType.IntegerMultipleOf, schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` } + if (!IsInteger(value)) return yield Create(ValueErrorType.Integer, schema, path, value) + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield Create(ValueErrorType.IntegerExclusiveMaximum, schema, path, value) } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.IntegerExclusiveMinimum, schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` } + yield Create(ValueErrorType.IntegerExclusiveMinimum, schema, path, value) } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.IntegerExclusiveMaximum, schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield Create(ValueErrorType.IntegerMaximum, schema, path, value) } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.IntegerMinimum, schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` } + yield Create(ValueErrorType.IntegerMinimum, schema, path, value) } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.IntegerMaximum, schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + yield Create(ValueErrorType.IntegerMultipleOf, schema, path, value) } } function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { for (const inner of schema.allOf) { const next = Visit(inner, references, path, value).next() if (!next.done) { + yield Create(ValueErrorType.Intersect, schema, path, value) yield next.value - yield { type: ValueErrorType.Intersect, schema, path, value, message: `Expected all sub schemas to be valid` } - return } } if (schema.unevaluatedProperties === false) { const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) for (const valueKey of Object.getOwnPropertyNames(value)) { if (!keyCheck.test(valueKey)) { - yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Unexpected property` } + yield Create(ValueErrorType.IntersectUnevaluatedProperties, schema, `${path}/${valueKey}`, value) } } } @@ -302,93 +268,63 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path for (const valueKey of Object.getOwnPropertyNames(value)) { if (!keyCheck.test(valueKey)) { const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() - if (!next.done) { - yield next.value - yield { type: ValueErrorType.IntersectUnevaluatedProperties, schema, path: `${path}/${valueKey}`, value, message: `Invalid additional property` } - return - } + if (!next.done) yield next.value // yield interior } } } } function* TIterator(schema: Types.TIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(IsObject(value) && Symbol.iterator in value)) { - yield { type: ValueErrorType.Iterator, schema, path, value, message: `Expected value to be an iterator` } - } + if (!IsIterator(value)) yield Create(ValueErrorType.Iterator, schema, path, value) } function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === schema.const)) { - const error = typeof schema.const === 'string' ? `'${schema.const}'` : schema.const - return yield { type: ValueErrorType.Literal, schema, path, value, message: `Expected ${error}` } - } + if (!(value === schema.const)) yield Create(ValueErrorType.Literal, schema, path, value) } function* TNever(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { - yield { type: ValueErrorType.Never, schema, path, value, message: `Value cannot be validated` } + yield Create(ValueErrorType.Never, schema, path, value) } function* TNot(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (Visit(schema.not, references, path, value).next().done === true) { - yield { type: ValueErrorType.Not, schema, path, value, message: `Value should not validate` } - } + if (Visit(schema.not, references, path, value).next().done === true) yield Create(ValueErrorType.Not, schema, path, value) } function* TNull(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsNull(value)) { - return yield { type: ValueErrorType.Null, schema, path, value, message: `Expected null` } - } + if (!IsNull(value)) yield Create(ValueErrorType.Null, schema, path, value) } function* TNumber(schema: Types.TNumber, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsNumber(value)) { - return yield { type: ValueErrorType.Number, schema, path, value, message: `Expected number` } - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { - yield { type: ValueErrorType.NumberMultipleOf, schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` } + if (!TypeSystemPolicy.IsNumberLike(value)) return yield Create(ValueErrorType.Number, schema, path, value) + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + yield Create(ValueErrorType.NumberExclusiveMaximum, schema, path, value) } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { - yield { type: ValueErrorType.NumberExclusiveMinimum, schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` } + yield Create(ValueErrorType.NumberExclusiveMinimum, schema, path, value) } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - yield { type: ValueErrorType.NumberExclusiveMaximum, schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` } + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + yield Create(ValueErrorType.NumberMaximum, schema, path, value) } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { - yield { type: ValueErrorType.NumberMinimum, schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` } + yield Create(ValueErrorType.NumberMinimum, schema, path, value) } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { - yield { type: ValueErrorType.NumberMaximum, schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + yield Create(ValueErrorType.NumberMultipleOf, schema, path, value) } } function* TObject(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsObject(value)) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected object` } - } + if (!TypeSystemPolicy.IsObjectLike(value)) return yield Create(ValueErrorType.Object, schema, path, value) if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + yield Create(ValueErrorType.ObjectMinProperties, schema, path, value) } if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } + yield Create(ValueErrorType.ObjectMaxProperties, schema, path, value) } const requiredKeys = Array.isArray(schema.required) ? schema.required : ([] as string[]) const knownKeys = Object.getOwnPropertyNames(schema.properties) const unknownKeys = Object.getOwnPropertyNames(value) - for (const knownKey of knownKeys) { - const property = schema.properties[knownKey] - if (schema.required && schema.required.includes(knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) - if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { - yield { type: ValueErrorType.ObjectRequiredProperties, schema: property, path: `${path}/${knownKey}`, value: undefined, message: `Expected required property` } - } - } else { - if (IsExactOptionalProperty(value, knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) - } - } - } for (const requiredKey of requiredKeys) { if (unknownKeys.includes(requiredKey)) continue - yield { type: ValueErrorType.ObjectRequiredProperties, schema: schema.properties[requiredKey], path: `${path}/${requiredKey}`, value: undefined, message: `Expected required property` } + yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${requiredKey}`, undefined) } if (schema.additionalProperties === false) { for (const valueKey of unknownKeys) { if (!knownKeys.includes(valueKey)) { - yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: `${path}/${valueKey}`, value: value[valueKey], message: `Unexpected property` } + yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${valueKey}`, value[valueKey]) } } } @@ -398,101 +334,96 @@ function* TObject(schema: Types.TObject, references: Types.TSchema[], path: stri yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey]) } } + for (const knownKey of knownKeys) { + const property = schema.properties[knownKey] + if (schema.required && schema.required.includes(knownKey)) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { + yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${knownKey}`, undefined) + } + } else { + if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey)) { + yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + } + } + } } function* TPromise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsPromise(value)) { - yield { type: ValueErrorType.Promise, schema, path, value, message: `Expected Promise` } - } + if (!IsPromise(value)) yield Create(ValueErrorType.Promise, schema, path, value) } function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsRecordObject(value)) { - return yield { type: ValueErrorType.Object, schema, path, value, message: `Expected record object` } - } + if (!TypeSystemPolicy.IsRecordLike(value)) return yield Create(ValueErrorType.Object, schema, path, value) if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { - yield { type: ValueErrorType.ObjectMinProperties, schema, path, value, message: `Expected object to have at least ${schema.minProperties} properties` } + yield Create(ValueErrorType.ObjectMinProperties, schema, path, value) } if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) { - yield { type: ValueErrorType.ObjectMaxProperties, schema, path, value, message: `Expected object to have no more than ${schema.maxProperties} properties` } + yield Create(ValueErrorType.ObjectMaxProperties, schema, path, value) } const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] const regex = new RegExp(patternKey) for (const [propertyKey, propertyValue] of Object.entries(value)) { - if (regex.test(propertyKey)) { - yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) - continue - } - if (typeof schema.additionalProperties === 'object') { - yield* Visit(schema.additionalProperties, references, `${path}/${propertyKey}`, propertyValue) + if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) + } + if (typeof schema.additionalProperties === 'object') { + for (const [propertyKey, propertyValue] of Object.entries(value)) { + if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${propertyKey}`, propertyValue) } - if (schema.additionalProperties === false) { - const propertyPath = `${path}/${propertyKey}` - const message = `Unexpected property '${propertyPath}'` - return yield { type: ValueErrorType.ObjectAdditionalProperties, schema, path: propertyPath, value: propertyValue, message } + } + if (schema.additionalProperties === false) { + for (const [propertyKey, propertyValue] of Object.entries(value)) { + if (regex.test(propertyKey)) continue + return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${propertyKey}`, propertyValue) } } } function* TRef(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueErrorsDereferenceError(schema) - const target = references[index] - yield* Visit(target, references, path, value) + yield* Visit(Deref(schema, references), references, path, value) } function* TString(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsString(value)) { - return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } - } + if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value) if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { - yield { type: ValueErrorType.StringMinLength, schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` } + yield Create(ValueErrorType.StringMinLength, schema, path, value) } if (IsDefined(schema.maxLength) && !(value.length <= schema.maxLength)) { - yield { type: ValueErrorType.StringMaxLength, schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` } + yield Create(ValueErrorType.StringMaxLength, schema, path, value) } - if (ValueGuard.IsString(schema.pattern)) { + if (IsString(schema.pattern)) { const regex = new RegExp(schema.pattern) if (!regex.test(value)) { - yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } + yield Create(ValueErrorType.StringPattern, schema, path, value) } } - if (ValueGuard.IsString(schema.format)) { + if (IsString(schema.format)) { if (!Types.FormatRegistry.Has(schema.format)) { - yield { type: ValueErrorType.StringFormatUnknown, schema, path, value, message: `Unknown string format '${schema.format}'` } + yield Create(ValueErrorType.StringFormatUnknown, schema, path, value) } else { const format = Types.FormatRegistry.Get(schema.format)! if (!format(value)) { - yield { type: ValueErrorType.StringFormat, schema, path, value, message: `Expected string to match format '${schema.format}'` } + yield Create(ValueErrorType.StringFormat, schema, path, value) } } } } function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsSymbol(value)) { - return yield { type: ValueErrorType.Symbol, schema, path, value, message: 'Expected symbol' } - } + if (!IsSymbol(value)) yield Create(ValueErrorType.Symbol, schema, path, value) } function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsString(value)) { - return yield { type: ValueErrorType.String, schema, path, value, message: 'Expected string' } - } + if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value) const regex = new RegExp(schema.pattern) if (!regex.test(value)) { - yield { type: ValueErrorType.StringPattern, schema, path, value, message: `Expected string to match pattern ${schema.pattern}` } + yield Create(ValueErrorType.StringPattern, schema, path, value) } } function* TThis(schema: Types.TThis, references: Types.TSchema[], path: string, value: any): IterableIterator { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueErrorsDereferenceError(schema) - const target = references[index] - yield* Visit(target, references, path, value) + yield* Visit(Deref(schema, references), references, path, value) } function* TTuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsArray(value)) { - return yield { type: ValueErrorType.Array, schema, path, value, message: 'Expected Array' } - } + if (!IsArray(value)) return yield Create(ValueErrorType.Tuple, schema, path, value) if (schema.items === undefined && !(value.length === 0)) { - return yield { type: ValueErrorType.TupleZeroLength, schema, path, value, message: 'Expected tuple to have 0 elements' } + return yield Create(ValueErrorType.TupleLength, schema, path, value) } if (!(value.length === schema.maxItems)) { - yield { type: ValueErrorType.TupleLength, schema, path, value, message: `Expected tuple to have ${schema.maxItems} elements` } + return yield Create(ValueErrorType.TupleLength, schema, path, value) } if (!schema.items) { return @@ -502,46 +433,35 @@ function* TTuple(schema: Types.TTuple, references: Types.TSchema[], path: } } function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!(value === undefined)) { - yield { type: ValueErrorType.Undefined, schema, path, value, message: `Expected undefined` } - } + if (!IsUndefined(value)) yield Create(ValueErrorType.Undefined, schema, path, value) } function* TUnion(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { - const errors: ValueError[] = [] - for (const inner of schema.anyOf) { - const variantErrors = [...Visit(inner, references, path, value)] - if (variantErrors.length === 0) return - errors.push(...variantErrors) - } - if (errors.length > 0) { - yield { type: ValueErrorType.Union, schema, path, value, message: 'Expected value of union' } + let count = 0 + for (const subschema of schema.anyOf) { + const errors = [...Visit(subschema, references, path, value)] + if (errors.length === 0) return // matched + count += errors.length } - for (const error of errors) { - yield error + if (count > 0) { + yield Create(ValueErrorType.Union, schema, path, value) } } function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!ValueGuard.IsUint8Array(value)) { - return yield { type: ValueErrorType.Uint8Array, schema, path, value, message: `Expected Uint8Array` } - } + if (!IsUint8Array(value)) return yield Create(ValueErrorType.Uint8Array, schema, path, value) if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { - yield { type: ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length less or equal to ${schema.maxByteLength}` } + yield Create(ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value) } if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) { - yield { type: ValueErrorType.Uint8ArrayMinByteLength, schema, path, value, message: `Expected Uint8Array to have a byte length greater or equal to ${schema.maxByteLength}` } + yield Create(ValueErrorType.Uint8ArrayMinByteLength, schema, path, value) } } function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} function* TVoid(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { - if (!IsVoid(value)) { - return yield { type: ValueErrorType.Void, schema, path, value, message: `Expected void` } - } + if (!TypeSystemPolicy.IsVoidLike(value)) yield Create(ValueErrorType.Void, schema, path, value) } function* TKind(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { const check = Types.TypeRegistry.Get(schema[Types.Kind])! - if (!check(schema, value)) { - return yield { type: ValueErrorType.Kind, schema, path, value, message: `Expected kind ${schema[Types.Kind]}` } - } + if (!check(schema, value)) yield Create(ValueErrorType.Kind, schema, path, value) } function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { const references_ = IsDefined(schema.$id) ? [...references, schema] : references diff --git a/src/system/system.ts b/src/system/system.ts index 4b5291fd2..528286259 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,31 +26,30 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard' +import { ValueErrorType } from '../errors/errors' import * as Types from '../typebox' -export class TypeSystemDuplicateTypeKind extends Error { +// -------------------------------------------------------------------------- +// Errors +// -------------------------------------------------------------------------- +export class TypeSystemDuplicateTypeKind extends Types.TypeBoxError { constructor(kind: string) { super(`Duplicate type kind '${kind}' detected`) } } -export class TypeSystemDuplicateFormat extends Error { +export class TypeSystemDuplicateFormat extends Types.TypeBoxError { constructor(kind: string) { super(`Duplicate string format '${kind}' detected`) } } +// ------------------------------------------------------------------------------------------- +// TypeSystem +// ------------------------------------------------------------------------------------------- /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { - /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ - export let ExactOptionalPropertyTypes: boolean = false - /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ - export let AllowArrayObjects: boolean = false - /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ - export let AllowNaN: boolean = false - /** Sets whether `null` should validate for void types. The default is `false` */ - export let AllowVoidNull: boolean = false - /** Creates a new type */ - export function Type(kind: string, check: (options: Options, value: unknown) => boolean) { + export function Type>(kind: string, check: (options: Options, value: unknown) => boolean) { if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) Types.TypeRegistry.Set(kind, check) return (options: Partial = {}) => Types.Type.Unsafe({ ...options, [Types.Kind]: kind }) @@ -62,3 +61,199 @@ export namespace TypeSystem { return format } } +// -------------------------------------------------------------------------- +// TypeSystemErrorFunction +// -------------------------------------------------------------------------- +/** Manages error message providers */ +export namespace TypeSystemErrorFunction { + let errorMessageFunction: ErrorFunction = DefaultErrorFunction + /** Resets the error message function to en-us */ + export function Reset() { + errorMessageFunction = DefaultErrorFunction + } + /** Sets the error message function used to generate error messages */ + export function Set(callback: ErrorFunction) { + errorMessageFunction = callback + } + /** Gets the error message function */ + export function Get(): ErrorFunction { + return errorMessageFunction + } +} +// -------------------------------------------------------------------------- +// TypeSystemPolicy +// -------------------------------------------------------------------------- +/** Shared assertion routines used by the value and errors modules */ +export namespace TypeSystemPolicy { + /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ + export let ExactOptionalPropertyTypes: boolean = false + /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ + export let AllowArrayObject: boolean = false + /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ + export let AllowNaN: boolean = false + /** Sets whether `null` should validate for void types. The default is `false` */ + export let AllowNullVoid: boolean = false + /** Asserts this value using the ExactOptionalPropertyTypes policy */ + export function IsExactOptionalProperty(value: Record, key: string) { + return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + } + /** Asserts this value using the AllowArrayObjects policy */ + export function IsObjectLike(value: unknown): value is Record { + const isObject = IsObject(value) + return AllowArrayObject ? isObject : isObject && !IsArray(value) + } + /** Asserts this value as a record using the AllowArrayObjects policy */ + export function IsRecordLike(value: unknown): value is Record { + return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array) + } + /** Asserts this value using the AllowNaN policy */ + export function IsNumberLike(value: unknown): value is number { + const isNumber = IsNumber(value) + return AllowNaN ? isNumber : isNumber && Number.isFinite(value) + } + /** Asserts this value using the AllowVoidNull policy */ + export function IsVoidLike(value: unknown): value is void { + const isUndefined = IsUndefined(value) + return AllowNullVoid ? isUndefined || value === null : isUndefined + } +} +// -------------------------------------------------------------------------- +// ErrorFunction +// -------------------------------------------------------------------------- +export type ErrorFunction = (schema: Types.TSchema, type: ValueErrorType) => string +// -------------------------------------------------------------------------- +// DefaultErrorFunction +// -------------------------------------------------------------------------- +/** Creates an error message using en-US as the default locale */ +export function DefaultErrorFunction(schema: Types.TSchema, errorType: ValueErrorType) { + switch (errorType) { + case ValueErrorType.ArrayContains: + return 'Expected array to contain at least one matching value' + case ValueErrorType.ArrayMaxContains: + return `Expected array to contain no more than ${schema.maxContains} matching values` + case ValueErrorType.ArrayMinContains: + return `Expected array to contain at least ${schema.minContains} matching values` + case ValueErrorType.ArrayMaxItems: + return `Expected array length to be less or equal to ${schema.maxItems}` + case ValueErrorType.ArrayMinItems: + return `Expected array length to be greater or equal to ${schema.minItems}` + case ValueErrorType.ArrayUniqueItems: + return 'Expected array elements to be unique' + case ValueErrorType.Array: + return 'Expected array' + case ValueErrorType.AsyncIterator: + return 'Expected AsyncIterator' + case ValueErrorType.BigIntExclusiveMaximum: + return `Expected bigint to be less than ${schema.exclusiveMaximum}` + case ValueErrorType.BigIntExclusiveMinimum: + return `Expected bigint to be greater than ${schema.exclusiveMinimum}` + case ValueErrorType.BigIntMaximum: + return `Expected bigint to be less or equal to ${schema.maximum}` + case ValueErrorType.BigIntMinimum: + return `Expected bigint to be greater or equal to ${schema.minimum}` + case ValueErrorType.BigIntMultipleOf: + return `Expected bigint to be a multiple of ${schema.multipleOf}` + case ValueErrorType.BigInt: + return 'Expected bigint' + case ValueErrorType.Boolean: + return 'Expected boolean' + case ValueErrorType.DateExclusiveMinimumTimestamp: + return `Expected Date timestamp to be greater than ${schema.exclusiveMinimumTimestamp}` + case ValueErrorType.DateExclusiveMaximumTimestamp: + return `Expected Date timestamp to be less than ${schema.exclusiveMaximumTimestamp}` + case ValueErrorType.DateMinimumTimestamp: + return `Expected Date timestamp to be greater or equal to ${schema.minimumTimestamp}` + case ValueErrorType.DateMaximumTimestamp: + return `Expected Date timestamp to be less or equal to ${schema.maximumTimestamp}` + case ValueErrorType.DateMultipleOfTimestamp: + return `Expected Date timestamp to be a multiple of ${schema.multipleOfTimestamp}` + case ValueErrorType.Date: + return 'Expected Date' + case ValueErrorType.Function: + return 'Expected function' + case ValueErrorType.IntegerExclusiveMaximum: + return `Expected integer to be less than ${schema.exclusiveMaximum}` + case ValueErrorType.IntegerExclusiveMinimum: + return `Expected integer to be greater than ${schema.exclusiveMinimum}` + case ValueErrorType.IntegerMaximum: + return `Expected integer to be less or equal to ${schema.maximum}` + case ValueErrorType.IntegerMinimum: + return `Expected integer to be greater or equal to ${schema.minimum}` + case ValueErrorType.IntegerMultipleOf: + return `Expected integer to be a multiple of ${schema.multipleOf}` + case ValueErrorType.Integer: + return 'Expected integer' + case ValueErrorType.IntersectUnevaluatedProperties: + return 'Unexpected property' + case ValueErrorType.Intersect: + return 'Expected all values to match' + case ValueErrorType.Iterator: + return 'Expected Iterator' + case ValueErrorType.Literal: + return `Expected ${typeof schema.const === 'string' ? `'${schema.const}'` : schema.const}` + case ValueErrorType.Never: + return 'Never' + case ValueErrorType.Not: + return 'Value should not match' + case ValueErrorType.Null: + return 'Expected null' + case ValueErrorType.NumberExclusiveMaximum: + return `Expected number to be less than ${schema.exclusiveMaximum}` + case ValueErrorType.NumberExclusiveMinimum: + return `Expected number to be greater than ${schema.exclusiveMinimum}` + case ValueErrorType.NumberMaximum: + return `Expected number to be less or equal to ${schema.maximum}` + case ValueErrorType.NumberMinimum: + return `Expected number to be greater or equal to ${schema.minimum}` + case ValueErrorType.NumberMultipleOf: + return `Expected number to be a multiple of ${schema.multipleOf}` + case ValueErrorType.Number: + return 'Expected number' + case ValueErrorType.Object: + return 'Expected object' + case ValueErrorType.ObjectAdditionalProperties: + return 'Unexpected property' + case ValueErrorType.ObjectMaxProperties: + return `Expected object to have no more than ${schema.maxProperties} properties` + case ValueErrorType.ObjectMinProperties: + return `Expected object to have at least ${schema.minProperties} properties` + case ValueErrorType.ObjectRequiredProperty: + return 'Required property' + case ValueErrorType.Promise: + return 'Expected Promise' + case ValueErrorType.StringFormatUnknown: + return `Unknown format '${schema.format}'` + case ValueErrorType.StringFormat: + return `Expected string to match '${schema.format}' format` + case ValueErrorType.StringMaxLength: + return `Expected string length less or equal to ${schema.maxLength}` + case ValueErrorType.StringMinLength: + return `Expected string length greater or equal to ${schema.minLength}` + case ValueErrorType.StringPattern: + return `Expected string to match '${schema.pattern}'` + case ValueErrorType.String: + return 'Expected string' + case ValueErrorType.Symbol: + return 'Expected symbol' + case ValueErrorType.TupleLength: + return `Expected tuple to have ${schema.maxItems || 0} elements` + case ValueErrorType.Tuple: + return 'Expected tuple' + case ValueErrorType.Uint8ArrayMaxByteLength: + return `Expected byte length less or equal to ${schema.maxByteLength}` + case ValueErrorType.Uint8ArrayMinByteLength: + return `Expected byte length greater or equal to ${schema.minByteLength}` + case ValueErrorType.Uint8Array: + return 'Expected Uint8Array' + case ValueErrorType.Undefined: + return 'Expected undefined' + case ValueErrorType.Union: + return 'Expected union value' + case ValueErrorType.Void: + return 'Expected void' + case ValueErrorType.Kind: + return `Expected kind '${schema[Types.Kind]}'` + default: + return 'Unknown error type' + } +} diff --git a/src/typebox.ts b/src/typebox.ts index 8f37e240e..412d6412d 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -29,6 +29,7 @@ THE SOFTWARE. // -------------------------------------------------------------------------- // Symbols // -------------------------------------------------------------------------- +export const Transform = Symbol.for('TypeBox.Transform') export const Readonly = Symbol.for('TypeBox.Readonly') export const Optional = Symbol.for('TypeBox.Optional') export const Hint = Symbol.for('TypeBox.Hint') @@ -326,6 +327,8 @@ export interface DateOptions extends SchemaOptions { maximumTimestamp?: number /** The minimum timestamp value */ minimumTimestamp?: number + /** The multiple of timestamp value */ + multipleOfTimestamp?: number } export interface TDate extends TSchema, DateOptions { [Kind]: 'Date' @@ -567,8 +570,11 @@ export type ObjectProperties = T extends TObject ? U : never export type ObjectPropertyKeys = T extends TObject ? keyof U : never export type TAdditionalProperties = undefined | TSchema | boolean export interface ObjectOptions extends SchemaOptions { + /** Additional property constraints for this object */ additionalProperties?: TAdditionalProperties + /** The minimum number of properties allowed on this object */ minProperties?: number + /** The maximum number of properties allowed on this object */ maxProperties?: number } export interface TObject extends TSchema, ObjectOptions { @@ -641,16 +647,39 @@ export interface TPromise extends TSchema { // -------------------------------------------------------------------------- // TRecord // -------------------------------------------------------------------------- -export type RecordTemplateLiteralObjectType = Ensure]: T }>>> -export type RecordTemplateLiteralType = IsTemplateLiteralFinite extends true ? RecordTemplateLiteralObjectType : TRecord -export type RecordUnionLiteralType = Static extends string ? Ensure]: T }>> : never -export type RecordLiteralType, T extends TSchema> = Ensure> -export type RecordNumberType = Ensure> -export type RecordStringType = Ensure> -export type RecordKey = TUnion[]> | TLiteral | TTemplateLiteral | TInteger | TNumber | TString -export interface TRecord extends TSchema { +export type TRecordFromUnionLiteralString = { [_ in K['const']]: T } +export type TRecordFromUnionLiteralNumber = { [_ in K['const']]: T } +// prettier-ignore +export type TRecordFromUnionRest = K extends [infer L, ...infer R] ? ( + L extends TUnion ? TRecordFromUnionRest & TRecordFromUnionRest, T> : + L extends TLiteralString ? TRecordFromUnionLiteralString & TRecordFromUnionRest, T> : + L extends TLiteralNumber ? TRecordFromUnionLiteralNumber & TRecordFromUnionRest, T> : +{}) : {} +export type TRecordFromUnion = Ensure>>>> +export type TRecordFromTemplateLiteralKeyInfinite = Ensure> +export type TRecordFromTemplateLiteralKeyFinite> = Ensure]: T }>>> +// prettier-ignore +export type TRecordFromTemplateLiteralKey = IsTemplateLiteralFinite extends false + ? TRecordFromTemplateLiteralKeyInfinite + : TRecordFromTemplateLiteralKeyFinite +export type TRecordFromLiteralStringKey = Ensure> +export type TRecordFromLiteralNumberKey = Ensure> +export type TRecordFromStringKey = Ensure> +export type TRecordFromNumberKey = Ensure> +export type TRecordFromIntegerKey = Ensure> +// prettier-ignore +export type TRecordResolve = + K extends TUnion ? TRecordFromUnion : + K extends TTemplateLiteral ? TRecordFromTemplateLiteralKey : + K extends TLiteralString ? TRecordFromLiteralStringKey : + K extends TLiteralNumber ? TRecordFromLiteralNumberKey : + K extends TString ? TRecordFromStringKey : + K extends TNumber ? TRecordFromNumberKey : + K extends TInteger ? TRecordFromIntegerKey : + TNever +export interface TRecord extends TSchema { [Kind]: 'Record' - static: Record, Static> + static: Record, string | number>, Static> type: 'object' patternProperties: { [pattern: string]: T } additionalProperties: TAdditionalProperties @@ -679,7 +708,7 @@ export interface TRef extends TSchema { // -------------------------------------------------------------------------- // TRest // -------------------------------------------------------------------------- -export type TRest = T extends TTuple ? Assert : Assert<[T], TSchema[]> +export type TRest = T extends TIntersect ? R : T extends TUnion ? R : T extends TTuple ? R : [] // -------------------------------------------------------------------------- // TReturnType // -------------------------------------------------------------------------- @@ -823,12 +852,52 @@ export interface TTemplateLiteral = { + [K in keyof T]: DecodeStaticType +} +// prettier-ignore +export type DecodeStaticRest = T extends [infer L, ...infer R] + ? [DecodeStaticType>, ...DecodeStaticRest>] + : [] +// prettier-ignore +export type DecodeStaticType = + T extends TTransform ? TUnsafe : + T extends TArray ? TArray> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TConstructor ? TConstructor>, DecodeStaticType> : + T extends TFunction ? TFunction>, DecodeStaticType> : + T extends TIntersect ? TIntersect>> : + T extends TIterator ? TIterator> : + T extends TNot ? TNot> : + T extends TObject ? TObject>> : + T extends TPromise ? TPromise> : + T extends TRecord ? TRecord> : + T extends TRecursive ? TRecursive> : + T extends TRef ? TRef> : + T extends TTuple ? TTuple>> : + T extends TUnion ? TUnion>> : + T +export type TransformFunction = (value: T) => U +export interface TransformOptions { + Decode: TransformFunction, O> + Encode: TransformFunction> +} +export type TTransformResolve = T extends TTransform ? S : Static +export interface TTransform extends TSchema { + static: TTransformResolve + [Transform]: TransformOptions + [key: string]: any +} +// -------------------------------------------------------------------------- // TTuple // -------------------------------------------------------------------------- -export type TTupleInfer = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleInfer, P>] : [] +export type TTupleRest = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleRest, P>] : [] export interface TTuple extends TSchema { [Kind]: 'Tuple' - static: TTupleInfer // { [K in keyof T]: T[K] extends TSchema ? Static : T[K] } + static: TTupleRest type: 'array' items?: T additionalItems?: false @@ -904,7 +973,11 @@ export interface TVoid extends TSchema { // -------------------------------------------------------------------------- // Static // -------------------------------------------------------------------------- -/** Infers a static type from a TypeBox type */ +/** Creates the decoded static form for a TypeBox type */ +export type StaticDecode = Static, P> +/** Creates the encoded static form for a TypeBox type */ +export type StaticEncode = Static +/** Creates the static type for a TypeBox type */ export type Static = (T & { params: P })['static'] // -------------------------------------------------------------------------- // TypeRegistry @@ -939,6 +1012,14 @@ export namespace TypeRegistry { } } // -------------------------------------------------------------------------- +// TypeBoxError +// -------------------------------------------------------------------------- +export class TypeBoxError extends Error { + constructor(message: string) { + super(message) + } +} +// -------------------------------------------------------------------------- // TypeRegistry // -------------------------------------------------------------------------- export type FormatRegistryValidationFunction = (value: string) => boolean @@ -973,40 +1054,45 @@ export namespace FormatRegistry { // -------------------------------------------------------------------------- // ValueGuard // -------------------------------------------------------------------------- +/** Provides functions to type guard raw JavaScript values */ export namespace ValueGuard { - export function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null - } + /** Returns true if this value is an array */ export function IsArray(value: unknown): value is unknown[] { return Array.isArray(value) } + /** Returns true if this value is bigint */ + export function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' + } + /** Returns true if this value is a boolean */ export function IsBoolean(value: unknown): value is boolean { return typeof value === 'boolean' } + /** Returns true if this value is null */ export function IsNull(value: unknown): value is null { return value === null } - export function IsUndefined(value: unknown): value is undefined { - return value === undefined - } - export function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } + /** Returns true if this value is number */ export function IsNumber(value: unknown): value is number { return typeof value === 'number' } + /** Returns true if this value is an object */ + export function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null + } + /** Returns true if this value is string */ export function IsString(value: unknown): value is string { return typeof value === 'string' } + /** Returns true if this value is undefined */ + export function IsUndefined(value: unknown): value is undefined { + return value === undefined + } } // -------------------------------------------------------------------------- // TypeGuard // -------------------------------------------------------------------------- -export class TypeGuardUnknownTypeError extends Error { - constructor(public readonly schema: unknown) { - super('TypeGuard: Unknown type') - } -} +export class TypeGuardUnknownTypeError extends TypeBoxError {} /** Provides functions to test if JavaScript values are TypeBox types */ export namespace TypeGuard { function IsPattern(value: unknown): value is string { @@ -1051,7 +1137,7 @@ export namespace TypeGuard { function IsOptionalSchema(value: unknown): value is boolean | undefined { return ValueGuard.IsUndefined(value) || TSchema(value) } - /** Returns true if the given schema is TAny */ + /** Returns true if the given value is TAny */ export function TAny(schema: unknown): schema is TAny { // prettier-ignore return ( @@ -1059,7 +1145,7 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is TArray */ + /** Returns true if the given value is TArray */ export function TArray(schema: unknown): schema is TArray { return ( TKindOf(schema, 'Array') && @@ -1074,7 +1160,7 @@ export namespace TypeGuard { IsOptionalNumber(schema.maxContains) ) } - /** Returns true if the given schema is TAsyncIterator */ + /** Returns true if the given value is TAsyncIterator */ export function TAsyncIterator(schema: unknown): schema is TAsyncIterator { // prettier-ignore return ( @@ -1084,21 +1170,21 @@ export namespace TypeGuard { TSchema(schema.items) ) } - /** Returns true if the given schema is TBigInt */ + /** Returns true if the given value is TBigInt */ export function TBigInt(schema: unknown): schema is TBigInt { // prettier-ignore return ( TKindOf(schema, 'BigInt') && schema.type === 'bigint' && IsOptionalString(schema.$id) && - IsOptionalBigInt(schema.multipleOf) && - IsOptionalBigInt(schema.minimum) && - IsOptionalBigInt(schema.maximum) && + IsOptionalBigInt(schema.exclusiveMaximum) && IsOptionalBigInt(schema.exclusiveMinimum) && - IsOptionalBigInt(schema.exclusiveMaximum) + IsOptionalBigInt(schema.maximum) && + IsOptionalBigInt(schema.minimum) && + IsOptionalBigInt(schema.multipleOf) ) } - /** Returns true if the given schema is TBoolean */ + /** Returns true if the given value is TBoolean */ export function TBoolean(schema: unknown): schema is TBoolean { // prettier-ignore return ( @@ -1107,86 +1193,70 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is TConstructor */ + /** Returns true if the given value is TConstructor */ export function TConstructor(schema: unknown): schema is TConstructor { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Constructor') && schema.type === 'constructor' && IsOptionalString(schema.$id) && - ValueGuard.IsArray(schema.parameters) && - TSchema(schema.returns)) - ) { - return false - } - for (const parameter of schema.parameters) { - if (!TSchema(parameter)) return false - } - return true + ValueGuard.IsArray(schema.parameters) && + schema.parameters.every(schema => TSchema(schema)) && + TSchema(schema.returns) + ) } - /** Returns true if the given schema is TDate */ + /** Returns true if the given value is TDate */ export function TDate(schema: unknown): schema is TDate { return ( TKindOf(schema, 'Date') && schema.type === 'Date' && IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minimumTimestamp) && - IsOptionalNumber(schema.maximumTimestamp) && + IsOptionalNumber(schema.exclusiveMaximumTimestamp) && IsOptionalNumber(schema.exclusiveMinimumTimestamp) && - IsOptionalNumber(schema.exclusiveMaximumTimestamp) + IsOptionalNumber(schema.maximumTimestamp) && + IsOptionalNumber(schema.minimumTimestamp) && + IsOptionalNumber(schema.multipleOfTimestamp) ) } - /** Returns true if the given schema is TFunction */ + /** Returns true if the given value is TFunction */ export function TFunction(schema: unknown): schema is TFunction { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Function') && schema.type === 'function' && IsOptionalString(schema.$id) && ValueGuard.IsArray(schema.parameters) && - TSchema(schema.returns)) - ) { - return false - } - for (const parameter of schema.parameters) { - if (!TSchema(parameter)) return false - } - return true + schema.parameters.every(schema => TSchema(schema)) && + TSchema(schema.returns) + ) } - /** Returns true if the given schema is TInteger */ + /** Returns true if the given value is TInteger */ export function TInteger(schema: unknown): schema is TInteger { return ( TKindOf(schema, 'Integer') && schema.type === 'integer' && IsOptionalString(schema.$id) && - IsOptionalNumber(schema.multipleOf) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMaximum) && IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.exclusiveMaximum) + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.multipleOf) ) } - /** Returns true if the given schema is TIntersect */ + /** Returns true if the given value is TIntersect */ export function TIntersect(schema: unknown): schema is TIntersect { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Intersect') && + (ValueGuard.IsString(schema.type) && schema.type !== 'object' ? false : true) && ValueGuard.IsArray(schema.allOf) && + schema.allOf.every(schema => TSchema(schema) && !TTransform(schema)) && IsOptionalString(schema.type) && (IsOptionalBoolean(schema.unevaluatedProperties) || IsOptionalSchema(schema.unevaluatedProperties)) && - IsOptionalString(schema.$id)) - ) { - return false - } - if ('type' in schema && schema.type !== 'object') { - return false - } - for (const inner of schema.allOf) { - if (!TSchema(inner)) return false - } - return true + IsOptionalString(schema.$id) + ) } - /** Returns true if the given schema is TIterator */ + /** Returns true if the given value is TIterator */ export function TIterator(schema: unknown): schema is TIterator { // prettier-ignore return ( @@ -1196,27 +1266,27 @@ export namespace TypeGuard { TSchema(schema.items) ) } - /** Returns true if the given schema is a TKind with the given name. */ + /** Returns true if the given value is a TKind with the given name. */ export function TKindOf(schema: unknown, kind: T): schema is Record & { [Kind]: T } { return TKind(schema) && schema[Kind] === kind } - /** Returns true if the given schema is TKind */ + /** Returns true if the given value is TKind */ export function TKind(schema: unknown): schema is Record & { [Kind]: string } { return ValueGuard.IsObject(schema) && Kind in schema && ValueGuard.IsString(schema[Kind]) } - /** Returns true if the given schema is TLiteral */ + /** Returns true if the given value is TLiteral */ export function TLiteralString(schema: unknown): schema is TLiteral { return TLiteral(schema) && ValueGuard.IsString(schema.const) } - /** Returns true if the given schema is TLiteral */ + /** Returns true if the given value is TLiteral */ export function TLiteralNumber(schema: unknown): schema is TLiteral { return TLiteral(schema) && ValueGuard.IsNumber(schema.const) } - /** Returns true if the given schema is TLiteral */ + /** Returns true if the given value is TLiteral */ export function TLiteralBoolean(schema: unknown): schema is TLiteral { return TLiteral(schema) && ValueGuard.IsBoolean(schema.const) } - /** Returns true if the given schema is TLiteral */ + /** Returns true if the given value is TLiteral */ export function TLiteral(schema: unknown): schema is TLiteral { // prettier-ignore return ( @@ -1228,7 +1298,7 @@ export namespace TypeGuard { ) ) } - /** Returns true if the given schema is TNever */ + /** Returns true if the given value is TNever */ export function TNever(schema: unknown): schema is TNever { // prettier-ignore return ( @@ -1237,7 +1307,7 @@ export namespace TypeGuard { Object.getOwnPropertyNames(schema.not).length === 0 ) } - /** Returns true if the given schema is TNot */ + /** Returns true if the given value is TNot */ export function TNot(schema: unknown): schema is TNot { // prettier-ignore return ( @@ -1245,7 +1315,7 @@ export namespace TypeGuard { TSchema(schema.not) ) } - /** Returns true if the given schema is TNull */ + /** Returns true if the given value is TNull */ export function TNull(schema: unknown): schema is TNull { // prettier-ignore return ( @@ -1254,41 +1324,34 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is TNumber */ + /** Returns true if the given value is TNumber */ export function TNumber(schema: unknown): schema is TNumber { return ( TKindOf(schema, 'Number') && schema.type === 'number' && IsOptionalString(schema.$id) && - IsOptionalNumber(schema.multipleOf) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.exclusiveMaximum) && IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.exclusiveMaximum) + IsOptionalNumber(schema.maximum) && + IsOptionalNumber(schema.minimum) && + IsOptionalNumber(schema.multipleOf) ) } - /** Returns true if the given schema is TObject */ + /** Returns true if the given value is TObject */ export function TObject(schema: unknown): schema is TObject { - if ( - !( - TKindOf(schema, 'Object') && - schema.type === 'object' && - IsOptionalString(schema.$id) && - ValueGuard.IsObject(schema.properties) && - IsAdditionalProperties(schema.additionalProperties) && - IsOptionalNumber(schema.minProperties) && - IsOptionalNumber(schema.maxProperties) - ) - ) { - return false - } - for (const [key, value] of Object.entries(schema.properties)) { - if (!IsControlCharacterFree(key)) return false - if (!TSchema(value)) return false - } - return true + // prettier-ignore + return ( + TKindOf(schema, 'Object') && + schema.type === 'object' && + IsOptionalString(schema.$id) && + ValueGuard.IsObject(schema.properties) && + IsAdditionalProperties(schema.additionalProperties) && + IsOptionalNumber(schema.minProperties) && + IsOptionalNumber(schema.maxProperties) && + Object.entries(schema.properties).every(([key, schema]) => IsControlCharacterFree(key) && TSchema(schema)) + ) } - /** Returns true if the given schema is TPromise */ + /** Returns true if the given value is TPromise */ export function TPromise(schema: unknown): schema is TPromise { // prettier-ignore return ( @@ -1298,31 +1361,31 @@ export namespace TypeGuard { TSchema(schema.item) ) } - /** Returns true if the given schema is TRecord */ + /** Returns true if the given value is TRecord */ export function TRecord(schema: unknown): schema is TRecord { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Record') && schema.type === 'object' && IsOptionalString(schema.$id) && IsAdditionalProperties(schema.additionalProperties) && - ValueGuard.IsObject(schema.patternProperties)) - ) { - return false - } - const keys = Object.getOwnPropertyNames(schema.patternProperties) - if (keys.length !== 1) { - return false - } - if (!IsPattern(keys[0])) { - return false - } - if (!TSchema(schema.patternProperties[keys[0]])) { - return false - } - return true + ValueGuard.IsObject(schema.patternProperties) && + ((schema: Record) => { + const keys = Object.getOwnPropertyNames(schema.patternProperties) + return ( + keys.length === 1 && + IsPattern(keys[0]) && + ValueGuard.IsObject(schema.patternProperties) && + TSchema(schema.patternProperties[keys[0]]) + ) + })(schema) + ) + } + /** Returns true if this value is TRecursive */ + export function TRecursive(schema: unknown): schema is { [Hint]: 'Recursive' } { + return ValueGuard.IsObject(schema) && Hint in schema && schema[Hint] === 'Recursive' } - /** Returns true if the given schema is TRef */ + /** Returns true if the given value is TRef */ export function TRef(schema: unknown): schema is TRef { // prettier-ignore return ( @@ -1331,7 +1394,7 @@ export namespace TypeGuard { ValueGuard.IsString(schema.$ref) ) } - /** Returns true if the given schema is TString */ + /** Returns true if the given value is TString */ export function TString(schema: unknown): schema is TString { // prettier-ignore return ( @@ -1344,7 +1407,7 @@ export namespace TypeGuard { IsOptionalFormat(schema.format) ) } - /** Returns true if the given schema is TSymbol */ + /** Returns true if the given value is TSymbol */ export function TSymbol(schema: unknown): schema is TSymbol { // prettier-ignore return ( @@ -1353,7 +1416,7 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is TTemplateLiteral */ + /** Returns true if the given value is TTemplateLiteral */ export function TTemplateLiteral(schema: unknown): schema is TTemplateLiteral { // prettier-ignore return ( @@ -1364,7 +1427,7 @@ export namespace TypeGuard { schema.pattern[schema.pattern.length - 1] === '$' ) } - /** Returns true if the given schema is TThis */ + /** Returns true if the given value is TThis */ export function TThis(schema: unknown): schema is TThis { // prettier-ignore return ( @@ -1373,31 +1436,31 @@ export namespace TypeGuard { ValueGuard.IsString(schema.$ref) ) } - /** Returns true if the given schema is TTuple */ + /** Returns true of this value is TTransform */ + export function TTransform(schema: unknown): schema is { [Transform]: TransformOptions } { + return ValueGuard.IsObject(schema) && Transform in schema + } + /** Returns true if the given value is TTuple */ export function TTuple(schema: unknown): schema is TTuple { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Tuple') && schema.type === 'array' && IsOptionalString(schema.$id) && ValueGuard.IsNumber(schema.minItems) && ValueGuard.IsNumber(schema.maxItems) && - schema.minItems === schema.maxItems) - ) { - return false - } - if (ValueGuard.IsUndefined(schema.items) && ValueGuard.IsUndefined(schema.additionalItems) && schema.minItems === 0) { - return true - } - if (!ValueGuard.IsArray(schema.items)) { - return false - } - for (const inner of schema.items) { - if (!TSchema(inner)) return false - } - return true + schema.minItems === schema.maxItems && + (( // empty + ValueGuard.IsUndefined(schema.items) && + ValueGuard.IsUndefined(schema.additionalItems) && + schema.minItems === 0 + ) || ( + ValueGuard.IsArray(schema.items) && + schema.items.every(schema => TSchema(schema)) + )) + ) } - /** Returns true if the given schema is TUndefined */ + /** Returns true if the given value is TUndefined */ export function TUndefined(schema: unknown): schema is TUndefined { // prettier-ignore return ( @@ -1406,26 +1469,22 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is TUnion[]> */ + /** Returns true if the given value is TUnion[]> */ export function TUnionLiteral(schema: unknown): schema is TUnion { return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) } - /** Returns true if the given schema is TUnion */ + /** Returns true if the given value is TUnion */ export function TUnion(schema: unknown): schema is TUnion { // prettier-ignore - if (!( + return ( TKindOf(schema, 'Union') && - ValueGuard.IsArray(schema.anyOf) && - IsOptionalString(schema.$id)) - ) { - return false - } - for (const inner of schema.anyOf) { - if (!TSchema(inner)) return false - } - return true + IsOptionalString(schema.$id) && + ValueGuard.IsObject(schema) && + ValueGuard.IsArray(schema.anyOf) && + schema.anyOf.every(schema => TSchema(schema)) + ) } - /** Returns true if the given schema is TUint8Array */ + /** Returns true if the given value is TUint8Array */ export function TUint8Array(schema: unknown): schema is TUint8Array { // prettier-ignore return ( @@ -1436,7 +1495,7 @@ export namespace TypeGuard { IsOptionalNumber(schema.maxByteLength) ) } - /** Returns true if the given schema is TUnknown */ + /** Returns true if the given value is TUnknown */ export function TUnknown(schema: unknown): schema is TUnknown { // prettier-ignore return ( @@ -1444,11 +1503,11 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if the given schema is a raw TUnsafe */ + /** Returns true if the given value is a raw TUnsafe */ export function TUnsafe(schema: unknown): schema is TUnsafe { return TKindOf(schema, 'Unsafe') } - /** Returns true if the given schema is TVoid */ + /** Returns true if the given value is TVoid */ export function TVoid(schema: unknown): schema is TVoid { // prettier-ignore return ( @@ -1457,15 +1516,15 @@ export namespace TypeGuard { IsOptionalString(schema.$id) ) } - /** Returns true if this schema has the Readonly modifier */ + /** Returns true if this value has a Readonly symbol */ export function TReadonly(schema: T): schema is TReadonly { return ValueGuard.IsObject(schema) && schema[Readonly] === 'Readonly' } - /** Returns true if this schema has the Optional modifier */ + /** Returns true if this value has a Optional symbol */ export function TOptional(schema: T): schema is TOptional { return ValueGuard.IsObject(schema) && schema[Optional] === 'Optional' } - /** Returns true if the given schema is TSchema */ + /** Returns true if the given value is TSchema */ export function TSchema(schema: unknown): schema is TSchema { return ( ValueGuard.IsObject(schema) && @@ -1510,24 +1569,21 @@ export namespace TypeGuard { /** Fast undefined check used for properties of type undefined */ export namespace ExtendsUndefined { export function Check(schema: TSchema): boolean { - if (schema[Kind] === 'Undefined') return true - if (schema[Kind] === 'Not') { - return !Check(schema.not) - } - if (schema[Kind] === 'Intersect') { - const intersect = schema as TIntersect - return intersect.allOf.every((schema) => Check(schema)) - } - if (schema[Kind] === 'Union') { - const union = schema as TUnion - return union.anyOf.some((schema) => Check(schema)) - } - return false + return schema[Kind] === 'Intersect' + ? (schema as TIntersect).allOf.every((schema) => Check(schema)) + : schema[Kind] === 'Union' + ? (schema as TUnion).anyOf.some((schema) => Check(schema)) + : schema[Kind] === 'Undefined' + ? true + : schema[Kind] === 'Not' + ? !Check(schema.not) + : false } } // -------------------------------------------------------------------------- // TypeExtends // -------------------------------------------------------------------------- +export class TypeExtendsError extends TypeBoxError {} export enum TypeExtendsResult { Union, True, @@ -1538,7 +1594,13 @@ export namespace TypeExtends { // IntoBooleanResult // -------------------------------------------------------------------------- function IntoBooleanResult(result: TypeExtendsResult) { - return result === TypeExtendsResult.False ? TypeExtendsResult.False : TypeExtendsResult.True + return result === TypeExtendsResult.False ? result : TypeExtendsResult.True + } + // -------------------------------------------------------------------------- + // Throw + // -------------------------------------------------------------------------- + function Throw(message: string): never { + throw new TypeExtendsError(message) } // -------------------------------------------------------------------------- // StructuralRight @@ -1554,12 +1616,15 @@ export namespace TypeExtends { ) } function StructuralRight(left: TSchema, right: TSchema) { - if (TypeGuard.TNever(right)) return TNeverRight(left, right) - if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) - if (TypeGuard.TUnion(right)) return TUnionRight(left, right) - if (TypeGuard.TUnknown(right)) return TUnknownRight(left, right) - if (TypeGuard.TAny(right)) return TAnyRight(left, right) - throw Error('TypeExtends: StructuralRight') + // prettier-ignore + return ( + TypeGuard.TNever(right) ? TNeverRight(left, right) : + TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : + TypeGuard.TUnion(right) ? TUnionRight(left, right) : + TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : + TypeGuard.TAny(right) ? TAnyRight(left, right) : + Throw('StructuralRight') + ) } // -------------------------------------------------------------------------- // Any @@ -1568,135 +1633,181 @@ export namespace TypeExtends { return TypeExtendsResult.True } function TAny(left: TAny, right: TSchema) { - if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) - if (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) return TypeExtendsResult.True - if (TypeGuard.TUnion(right)) return TypeExtendsResult.Union - if (TypeGuard.TUnknown(right)) return TypeExtendsResult.True - if (TypeGuard.TAny(right)) return TypeExtendsResult.True - return TypeExtendsResult.Union + // prettier-ignore + return ( + TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : + (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) ? TypeExtendsResult.True : + TypeGuard.TUnion(right) ? TypeExtendsResult.Union : + TypeGuard.TUnknown(right) ? TypeExtendsResult.True : + TypeGuard.TAny(right) ? TypeExtendsResult.True : + TypeExtendsResult.Union + ) } // -------------------------------------------------------------------------- // Array // -------------------------------------------------------------------------- function TArrayRight(left: TSchema, right: TArray) { - if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False - if (TypeGuard.TAny(left)) return TypeExtendsResult.Union - if (TypeGuard.TNever(left)) return TypeExtendsResult.True - return TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TUnknown(left) ? TypeExtendsResult.False : + TypeGuard.TAny(left) ?TypeExtendsResult.Union : + TypeGuard.TNever(left) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } function TArray(left: TArray, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True - if (!TypeGuard.TArray(right)) return TypeExtendsResult.False - return IntoBooleanResult(Visit(left.items, right.items)) + // prettier-ignore + return ( + TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TArray(right) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) } // -------------------------------------------------------------------------- // AsyncIterator // -------------------------------------------------------------------------- function TAsyncIterator(left: TAsyncIterator, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (!TypeGuard.TAsyncIterator(right)) return TypeExtendsResult.False - return IntoBooleanResult(Visit(left.items, right.items)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TAsyncIterator(right) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) } // -------------------------------------------------------------------------- // BigInt // -------------------------------------------------------------------------- function TBigInt(left: TBigInt, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TBigInt(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TBigInt(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Boolean // -------------------------------------------------------------------------- function TBooleanRight(left: TSchema, right: TBoolean) { - if (TypeGuard.TLiteral(left) && ValueGuard.IsBoolean(left.const)) return TypeExtendsResult.True - return TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False + return TypeGuard.TLiteral(left) && ValueGuard.IsBoolean(left.const) ? TypeExtendsResult.True : TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False } function TBoolean(left: TBoolean, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TBoolean(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TBoolean(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Constructor // -------------------------------------------------------------------------- function TConstructor(left: TConstructor, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (!TypeGuard.TConstructor(right)) return TypeExtendsResult.False - if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False - if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { - return TypeExtendsResult.False - } - return IntoBooleanResult(Visit(left.returns, right.returns)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TConstructor(right) ? TypeExtendsResult.False : + left.parameters.length > right.parameters.length ? TypeExtendsResult.False : + (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.returns, right.returns)) + ) } // -------------------------------------------------------------------------- // Date // -------------------------------------------------------------------------- function TDate(left: TDate, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TDate(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TDate(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Function // -------------------------------------------------------------------------- function TFunction(left: TFunction, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (!TypeGuard.TFunction(right)) return TypeExtendsResult.False - if (left.parameters.length > right.parameters.length) return TypeExtendsResult.False - if (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) { - return TypeExtendsResult.False - } - return IntoBooleanResult(Visit(left.returns, right.returns)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TFunction(right) ? TypeExtendsResult.False : + left.parameters.length > right.parameters.length ? TypeExtendsResult.False : + (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.returns, right.returns)) + ) } // -------------------------------------------------------------------------- // Integer // -------------------------------------------------------------------------- function TIntegerRight(left: TSchema, right: TInteger) { - if (TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const)) return TypeExtendsResult.True - return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const) ? TypeExtendsResult.True : + TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } function TInteger(left: TInteger, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Intersect // -------------------------------------------------------------------------- function TIntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { - return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) + ? TypeExtendsResult.True + : TypeExtendsResult.False } function TIntersect(left: TIntersect, right: TSchema) { - return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) + ? TypeExtendsResult.True + : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Iterator // -------------------------------------------------------------------------- function TIterator(left: TIterator, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (!TypeGuard.TIterator(right)) return TypeExtendsResult.False - return IntoBooleanResult(Visit(left.items, right.items)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TIterator(right) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) } // -------------------------------------------------------------------------- // Literal // -------------------------------------------------------------------------- function TLiteral(left: TLiteral, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - if (TypeGuard.TString(right)) return TStringRight(left, right) - if (TypeGuard.TNumber(right)) return TNumberRight(left, right) - if (TypeGuard.TInteger(right)) return TIntegerRight(left, right) - if (TypeGuard.TBoolean(right)) return TBooleanRight(left, right) - return TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TString(right) ? TStringRight(left, right) : + TypeGuard.TNumber(right) ? TNumberRight(left, right) : + TypeGuard.TInteger(right) ? TIntegerRight(left, right) : + TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Never @@ -1723,31 +1834,46 @@ export namespace TypeExtends { // TypeScript has no concept of negated types, and attempts to correctly check the negated // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer // the type. Instead we unwrap to either unknown or T and continue evaluating. - if (TypeGuard.TNot(left)) return Visit(UnwrapTNot(left), right) - if (TypeGuard.TNot(right)) return Visit(left, UnwrapTNot(right)) - throw new Error(`TypeExtends: Invalid fallthrough for Not`) + // prettier-ignore + return ( + TypeGuard.TNot(left) ? Visit(UnwrapTNot(left), right) : + TypeGuard.TNot(right) ? Visit(left, UnwrapTNot(right)) : + Throw('Invalid fallthrough for Not') + ) } // -------------------------------------------------------------------------- // Null // -------------------------------------------------------------------------- function TNull(left: TNull, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TNull(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TNull(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Number // -------------------------------------------------------------------------- function TNumberRight(left: TSchema, right: TNumber) { - if (TypeGuard.TLiteralNumber(left)) return TypeExtendsResult.True - return TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TLiteralNumber(left) ? TypeExtendsResult.True : + TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } function TNumber(left: TNumber, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Object @@ -1804,117 +1930,156 @@ export namespace TypeExtends { // Property // -------------------------------------------------------------------------- function Property(left: TSchema, right: TSchema) { - if (Visit(left, right) === TypeExtendsResult.False) return TypeExtendsResult.False - if (TypeGuard.TOptional(left) && !TypeGuard.TOptional(right)) return TypeExtendsResult.False - return TypeExtendsResult.True + // prettier-ignore + return ( + Visit(left, right) === TypeExtendsResult.False ? TypeExtendsResult.False : + TypeGuard.TOptional(left) && !TypeGuard.TOptional(right) ? TypeExtendsResult.False : + TypeExtendsResult.True + ) } function TObjectRight(left: TSchema, right: TObject) { - if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False - if (TypeGuard.TAny(left)) return TypeExtendsResult.Union - if (TypeGuard.TNever(left)) return TypeExtendsResult.True - if (TypeGuard.TLiteralString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True - if (TypeGuard.TLiteralNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True - if (TypeGuard.TLiteralBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True - if (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) return TypeExtendsResult.True - if (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) return TypeExtendsResult.True - if (TypeGuard.TString(left) && IsObjectStringLike(right)) return TypeExtendsResult.True - if (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) return TypeExtendsResult.True - if (TypeGuard.TNumber(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True - if (TypeGuard.TInteger(left) && IsObjectNumberLike(right)) return TypeExtendsResult.True - if (TypeGuard.TBoolean(left) && IsObjectBooleanLike(right)) return TypeExtendsResult.True - if (TypeGuard.TUint8Array(left) && IsObjectUint8ArrayLike(right)) return TypeExtendsResult.True - if (TypeGuard.TDate(left) && IsObjectDateLike(right)) return TypeExtendsResult.True - if (TypeGuard.TConstructor(left) && IsObjectConstructorLike(right)) return TypeExtendsResult.True - if (TypeGuard.TFunction(left) && IsObjectFunctionLike(right)) return TypeExtendsResult.True - if (TypeGuard.TRecord(left) && TypeGuard.TString(RecordKey(left))) { - // When expressing a Record with literal key values, the Record is converted into a Object with - // the Hint assigned as `Record`. This is used to invert the extends logic. - return right[Hint] === 'Record' ? TypeExtendsResult.True : TypeExtendsResult.False - } - if (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) { - return IsObjectPropertyCount(right, 0) ? TypeExtendsResult.True : TypeExtendsResult.False - } - return TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TUnknown(left) ? TypeExtendsResult.False : + TypeGuard.TAny(left) ? TypeExtendsResult.Union : ( + TypeGuard.TNever(left) || + (TypeGuard.TLiteralString(left) && IsObjectStringLike(right)) || + (TypeGuard.TLiteralNumber(left) && IsObjectNumberLike(right)) || + (TypeGuard.TLiteralBoolean(left) && IsObjectBooleanLike(right)) || + (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || + (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) || + (TypeGuard.TString(left) && IsObjectStringLike(right)) || + (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || + (TypeGuard.TNumber(left) && IsObjectNumberLike(right)) || + (TypeGuard.TInteger(left) && IsObjectNumberLike(right)) || + (TypeGuard.TBoolean(left) && IsObjectBooleanLike(right)) || + (TypeGuard.TUint8Array(left) && IsObjectUint8ArrayLike(right)) || + (TypeGuard.TDate(left) && IsObjectDateLike(right)) || + (TypeGuard.TConstructor(left) && IsObjectConstructorLike(right)) || + (TypeGuard.TFunction(left) && IsObjectFunctionLike(right)) + ) ? TypeExtendsResult.True : + (TypeGuard.TRecord(left) && TypeGuard.TString(RecordKey(left))) ? (() => { + // When expressing a Record with literal key values, the Record is converted into a Object with + // the Hint assigned as `Record`. This is used to invert the extends logic. + return right[Hint] === 'Record' ? TypeExtendsResult.True : TypeExtendsResult.False + })() : + (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) ? (() => { + return IsObjectPropertyCount(right, 0) + ? TypeExtendsResult.True + : TypeExtendsResult.False + })() : + TypeExtendsResult.False + ) } function TObject(left: TObject, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - if (!TypeGuard.TObject(right)) return TypeExtendsResult.False - for (const key of Object.getOwnPropertyNames(right.properties)) { - if (!(key in left.properties)) return TypeExtendsResult.False - if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False - } - } - return TypeExtendsResult.True + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + !TypeGuard.TObject(right) ? TypeExtendsResult.False : + (() => { + for (const key of Object.getOwnPropertyNames(right.properties)) { + if (!(key in left.properties)) return TypeExtendsResult.False + if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { + return TypeExtendsResult.False + } + } + return TypeExtendsResult.True + })() + ) } // -------------------------------------------------------------------------- // Promise // -------------------------------------------------------------------------- function TPromise(left: TPromise, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right) && IsObjectPromiseLike(right)) return TypeExtendsResult.True - if (!TypeGuard.TPromise(right)) return TypeExtendsResult.False - return IntoBooleanResult(Visit(left.item, right.item)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) && IsObjectPromiseLike(right) ? TypeExtendsResult.True : + !TypeGuard.TPromise(right) ? TypeExtendsResult.False : + IntoBooleanResult(Visit(left.item, right.item)) + ) } // -------------------------------------------------------------------------- // Record // -------------------------------------------------------------------------- function RecordKey(schema: TRecord) { - if (PatternNumberExact in schema.patternProperties) return Type.Number() - if (PatternStringExact in schema.patternProperties) return Type.String() - throw Error('TypeExtends: Cannot get record key') + // prettier-ignore + return ( + PatternNumberExact in schema.patternProperties ? Type.Number() : + PatternStringExact in schema.patternProperties ? Type.String() : + Throw('Unknown record key pattern') + ) } function RecordValue(schema: TRecord) { - if (PatternNumberExact in schema.patternProperties) return schema.patternProperties[PatternNumberExact] - if (PatternStringExact in schema.patternProperties) return schema.patternProperties[PatternStringExact] - throw Error('TypeExtends: Cannot get record value') + // prettier-ignore + return ( + PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : + PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : + Throw('Unable to get record value schema') + ) } function TRecordRight(left: TSchema, right: TRecord) { - const Key = RecordKey(right) - const Value = RecordValue(right) - if (TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) return TypeExtendsResult.True - if (TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) - if (TypeGuard.TString(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) - if (TypeGuard.TArray(left) && TypeGuard.TNumber(Key)) return Visit(left, Value) - if (TypeGuard.TObject(left)) { - for (const key of Object.getOwnPropertyNames(left.properties)) { - if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False + const [Key, Value] = [RecordKey(right), RecordValue(right)] + // prettier-ignore + return ( + (TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) ? TypeExtendsResult.True : + TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : + TypeGuard.TString(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : + TypeGuard.TArray(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : + TypeGuard.TObject(left) ? (() => { + for (const key of Object.getOwnPropertyNames(left.properties)) { + if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { + return TypeExtendsResult.False + } } - } - return TypeExtendsResult.True - } - return TypeExtendsResult.False + return TypeExtendsResult.True + })() : + TypeExtendsResult.False + ) } function TRecord(left: TRecord, right: TSchema) { - const Value = RecordValue(left) - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (!TypeGuard.TRecord(right)) return TypeExtendsResult.False - return Visit(Value, RecordValue(right)) + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TRecord(right) ? TypeExtendsResult.False : + Visit(RecordValue(left), RecordValue(right)) + ) } // -------------------------------------------------------------------------- // String // -------------------------------------------------------------------------- function TStringRight(left: TSchema, right: TString) { - if (TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const)) return TypeExtendsResult.True - return TypeGuard.TString(left) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const) ? TypeExtendsResult.True : + TypeGuard.TString(left) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } function TString(left: TString, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TString(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TString(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Symbol // -------------------------------------------------------------------------- function TSymbol(left: TSymbol, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TSymbol(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TSymbol(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // TemplateLiteral @@ -1923,9 +2088,12 @@ export namespace TypeExtends { // TemplateLiteral types are resolved to either unions for finite expressions or string // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for // either type and continue evaluating. - if (TypeGuard.TTemplateLiteral(left)) return Visit(TemplateLiteralResolver.Resolve(left), right) - if (TypeGuard.TTemplateLiteral(right)) return Visit(left, TemplateLiteralResolver.Resolve(right)) - throw new Error(`TypeExtends: Invalid fallthrough for TemplateLiteral`) + // prettier-ignore + return ( + TypeGuard.TTemplateLiteral(left) ? Visit(TemplateLiteralResolver.Resolve(left), right) : + TypeGuard.TTemplateLiteral(right) ? Visit(left, TemplateLiteralResolver.Resolve(right)) : + Throw('Invalid fallthrough for TemplateLiteral') + ) } // -------------------------------------------------------------------------- // Tuple @@ -1939,47 +2107,68 @@ export namespace TypeExtends { ) } function TTupleRight(left: TSchema, right: TTuple) { - if (TypeGuard.TNever(left)) return TypeExtendsResult.True - if (TypeGuard.TUnknown(left)) return TypeExtendsResult.False - if (TypeGuard.TAny(left)) return TypeExtendsResult.Union - return TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TNever(left) ? TypeExtendsResult.True : + TypeGuard.TUnknown(left) ? TypeExtendsResult.False : + TypeGuard.TAny(left) ? TypeExtendsResult.Union : + TypeExtendsResult.False + ) } function TTuple(left: TTuple, right: TSchema): TypeExtendsResult { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right) && IsObjectArrayLike(right)) return TypeExtendsResult.True - if (TypeGuard.TArray(right) && IsArrayOfTuple(left, right)) return TypeExtendsResult.True - if (!TypeGuard.TTuple(right)) return TypeExtendsResult.False - if ((ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items))) return TypeExtendsResult.False - if (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) return TypeExtendsResult.True - return left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : + TypeGuard.TArray(right) && IsArrayOfTuple(left, right) ? TypeExtendsResult.True : + !TypeGuard.TTuple(right) ? TypeExtendsResult.False : + (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.False : + (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.True : + left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Uint8Array // -------------------------------------------------------------------------- function TUint8Array(left: TUint8Array, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - return TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Undefined // -------------------------------------------------------------------------- function TUndefined(left: TUndefined, right: TSchema) { - if (IsStructuralRight(right)) return StructuralRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - if (TypeGuard.TRecord(right)) return TRecordRight(left, right) - if (TypeGuard.TVoid(right)) return VoidRight(left, right) - return TypeGuard.TUndefined(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TVoid(right) ? VoidRight(left, right) : + TypeGuard.TUndefined(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Union // -------------------------------------------------------------------------- function TUnionRight(left: TSchema, right: TUnion): TypeExtendsResult { - return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) + ? TypeExtendsResult.True + : TypeExtendsResult.False } function TUnion(left: TUnion, right: TSchema): TypeExtendsResult { - return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) + ? TypeExtendsResult.True + : TypeExtendsResult.False } // -------------------------------------------------------------------------- // Unknown @@ -1988,66 +2177,77 @@ export namespace TypeExtends { return TypeExtendsResult.True } function TUnknown(left: TUnknown, right: TSchema) { - if (TypeGuard.TNever(right)) return TNeverRight(left, right) - if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) - if (TypeGuard.TUnion(right)) return TUnionRight(left, right) - if (TypeGuard.TAny(right)) return TAnyRight(left, right) - if (TypeGuard.TString(right)) return TStringRight(left, right) - if (TypeGuard.TNumber(right)) return TNumberRight(left, right) - if (TypeGuard.TInteger(right)) return TIntegerRight(left, right) - if (TypeGuard.TBoolean(right)) return TBooleanRight(left, right) - if (TypeGuard.TArray(right)) return TArrayRight(left, right) - if (TypeGuard.TTuple(right)) return TTupleRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - return TypeGuard.TUnknown(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return ( + TypeGuard.TNever(right) ? TNeverRight(left, right) : + TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : + TypeGuard.TUnion(right) ? TUnionRight(left, right) : + TypeGuard.TAny(right) ? TAnyRight(left, right) : + TypeGuard.TString(right) ? TStringRight(left, right) : + TypeGuard.TNumber(right) ? TNumberRight(left, right) : + TypeGuard.TInteger(right) ? TIntegerRight(left, right) : + TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : + TypeGuard.TArray(right) ? TArrayRight(left, right) : + TypeGuard.TTuple(right) ? TTupleRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TUnknown(right) ? TypeExtendsResult.True : + TypeExtendsResult.False + ) } // -------------------------------------------------------------------------- // Void // -------------------------------------------------------------------------- function VoidRight(left: TSchema, right: TVoid) { - if (TypeGuard.TUndefined(left)) return TypeExtendsResult.True - return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : + TypeGuard.TUndefined(left) ? TypeExtendsResult.True : + TypeExtendsResult.False } function TVoid(left: TVoid, right: TSchema) { - if (TypeGuard.TIntersect(right)) return TIntersectRight(left, right) - if (TypeGuard.TUnion(right)) return TUnionRight(left, right) - if (TypeGuard.TUnknown(right)) return TUnknownRight(left, right) - if (TypeGuard.TAny(right)) return TAnyRight(left, right) - if (TypeGuard.TObject(right)) return TObjectRight(left, right) - return TypeGuard.TVoid(right) ? TypeExtendsResult.True : TypeExtendsResult.False + // prettier-ignore + return TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : + TypeGuard.TUnion(right) ? TUnionRight(left, right) : + TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : + TypeGuard.TAny(right) ? TAnyRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TVoid(right) ? TypeExtendsResult.True : + TypeExtendsResult.False } function Visit(left: TSchema, right: TSchema): TypeExtendsResult { - // Resolvable Types - if (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) return TTemplateLiteral(left, right) - if (TypeGuard.TNot(left) || TypeGuard.TNot(right)) return TNot(left, right) - // Standard Types - if (TypeGuard.TAny(left)) return TAny(left, right) - if (TypeGuard.TArray(left)) return TArray(left, right) - if (TypeGuard.TBigInt(left)) return TBigInt(left, right) - if (TypeGuard.TBoolean(left)) return TBoolean(left, right) - if (TypeGuard.TAsyncIterator(left)) return TAsyncIterator(left, right) - if (TypeGuard.TConstructor(left)) return TConstructor(left, right) - if (TypeGuard.TDate(left)) return TDate(left, right) - if (TypeGuard.TFunction(left)) return TFunction(left, right) - if (TypeGuard.TInteger(left)) return TInteger(left, right) - if (TypeGuard.TIntersect(left)) return TIntersect(left, right) - if (TypeGuard.TIterator(left)) return TIterator(left, right) - if (TypeGuard.TLiteral(left)) return TLiteral(left, right) - if (TypeGuard.TNever(left)) return TNever(left, right) - if (TypeGuard.TNull(left)) return TNull(left, right) - if (TypeGuard.TNumber(left)) return TNumber(left, right) - if (TypeGuard.TObject(left)) return TObject(left, right) - if (TypeGuard.TRecord(left)) return TRecord(left, right) - if (TypeGuard.TString(left)) return TString(left, right) - if (TypeGuard.TSymbol(left)) return TSymbol(left, right) - if (TypeGuard.TTuple(left)) return TTuple(left, right) - if (TypeGuard.TPromise(left)) return TPromise(left, right) - if (TypeGuard.TUint8Array(left)) return TUint8Array(left, right) - if (TypeGuard.TUndefined(left)) return TUndefined(left, right) - if (TypeGuard.TUnion(left)) return TUnion(left, right) - if (TypeGuard.TUnknown(left)) return TUnknown(left, right) - if (TypeGuard.TVoid(left)) return TVoid(left, right) - throw Error(`TypeExtends: Unknown left type operand '${left[Kind]}'`) + // prettier-ignore + return ( + // resolvable + (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) ? TTemplateLiteral(left, right) : + (TypeGuard.TNot(left) || TypeGuard.TNot(right)) ? TNot(left, right) : + // standard + TypeGuard.TAny(left) ? TAny(left, right) : + TypeGuard.TArray(left) ? TArray(left, right) : + TypeGuard.TBigInt(left) ? TBigInt(left, right) : + TypeGuard.TBoolean(left) ? TBoolean(left, right) : + TypeGuard.TAsyncIterator(left) ? TAsyncIterator(left, right) : + TypeGuard.TConstructor(left) ? TConstructor(left, right) : + TypeGuard.TDate(left) ? TDate(left, right) : + TypeGuard.TFunction(left) ? TFunction(left, right) : + TypeGuard.TInteger(left) ? TInteger(left, right) : + TypeGuard.TIntersect(left) ? TIntersect(left, right) : + TypeGuard.TIterator(left) ? TIterator(left, right) : + TypeGuard.TLiteral(left) ? TLiteral(left, right) : + TypeGuard.TNever(left) ? TNever(left, right) : + TypeGuard.TNull(left) ? TNull(left, right) : + TypeGuard.TNumber(left) ? TNumber(left, right) : + TypeGuard.TObject(left) ? TObject(left, right) : + TypeGuard.TRecord(left) ? TRecord(left, right) : + TypeGuard.TString(left) ? TString(left, right) : + TypeGuard.TSymbol(left) ? TSymbol(left, right) : + TypeGuard.TTuple(left) ? TTuple(left, right) : + TypeGuard.TPromise(left) ? TPromise(left, right) : + TypeGuard.TUint8Array(left) ? TUint8Array(left, right) : + TypeGuard.TUndefined(left) ? TUndefined(left, right) : + TypeGuard.TUnion(left) ? TUnion(left, right) : + TypeGuard.TUnknown(left) ? TUnknown(left, right) : + TypeGuard.TVoid(left) ? TVoid(left, right) : + Throw(`Unknown left type operand '${left[Kind]}'`) + ) } export function Extends(left: TSchema, right: TSchema): TypeExtendsResult { return Visit(left, right) @@ -2067,12 +2267,17 @@ export namespace TypeClone { return (value as any).map((value: unknown) => Visit(value as any)) } function Visit(value: unknown): any { - if (ValueGuard.IsArray(value)) return ArrayType(value) - if (ValueGuard.IsObject(value)) return ObjectType(value) - return value + // prettier-ignore + return ValueGuard.IsArray(value) ? ArrayType(value) : + ValueGuard.IsObject(value) ? ObjectType(value) : + value + } + /** Clones a Rest */ + export function Rest(schemas: [...T]): T { + return schemas.map((schema) => Type(schema)) as T } - /** Clones a type. */ - export function Clone(schema: T, options: SchemaOptions = {}): T { + /** Clones a Type */ + export function Type(schema: T, options: SchemaOptions = {}): T { return { ...Visit(schema), ...options } } } @@ -2082,7 +2287,7 @@ export namespace TypeClone { export namespace IndexedAccessor { function OptionalUnwrap(schema: TSchema[]): TSchema[] { return schema.map((schema) => { - const { [Optional]: _, ...clone } = TypeClone.Clone(schema) + const { [Optional]: _, ...clone } = TypeClone.Type(schema) return clone }) } @@ -2093,17 +2298,16 @@ export namespace IndexedAccessor { return schema.some((schema) => TypeGuard.TOptional(schema)) } function ResolveIntersect(schema: TIntersect): TSchema { - const optional = IsIntersectOptional(schema.allOf) - return optional ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema + return IsIntersectOptional(schema.allOf) ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema } function ResolveUnion(schema: TUnion): TSchema { - const optional = IsUnionOptional(schema.anyOf) - return optional ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema + return IsUnionOptional(schema.anyOf) ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema } function ResolveOptional(schema: TSchema) { - if (schema[Kind] === 'Intersect') return ResolveIntersect(schema as TIntersect) - if (schema[Kind] === 'Union') return ResolveUnion(schema as TUnion) - return schema + // prettier-ignore + return schema[Kind] === 'Intersect' ? ResolveIntersect(schema as TIntersect) : + schema[Kind] === 'Union' ? ResolveUnion(schema as TUnion) : + schema } function TIntersect(schema: TIntersect, key: string): TSchema { const resolved = schema.allOf.reduce((acc, schema) => { @@ -2128,11 +2332,12 @@ export namespace IndexedAccessor { return element } function Visit(schema: TSchema, key: string): TSchema { - if (schema[Kind] === 'Intersect') return TIntersect(schema as TIntersect, key) - if (schema[Kind] === 'Union') return TUnion(schema as TUnion, key) - if (schema[Kind] === 'Object') return TObject(schema as TObject, key) - if (schema[Kind] === 'Tuple') return TTuple(schema as TTuple, key) - return Type.Never() + // prettier-ignore + return schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, key) : + schema[Kind] === 'Union' ? TUnion(schema as TUnion, key) : + schema[Kind] === 'Object' ? TObject(schema as TObject, key) : + schema[Kind] === 'Tuple' ? TTuple(schema as TTuple, key) : + Type.Never() } export function Resolve(schema: TSchema, keys: TPropertyKey[], options: SchemaOptions = {}): TSchema { const resolved = keys.map((key) => Visit(schema, key.toString())) @@ -2184,10 +2389,11 @@ export namespace Intrinsic { return [Map(L, mode), ...IntrinsicRest(R, mode)] } function Visit(schema: TSchema, mode: TIntrinsicMode) { - if (TypeGuard.TTemplateLiteral(schema)) return IntrinsicTemplateLiteral(schema, mode) - if (TypeGuard.TUnion(schema)) return Type.Union(IntrinsicRest(schema.anyOf, mode)) - if (TypeGuard.TLiteral(schema)) return Type.Literal(IntrinsicLiteral(schema.const, mode)) - return schema + // prettier-ignore + return TypeGuard.TTemplateLiteral(schema) ? IntrinsicTemplateLiteral(schema, mode) : + TypeGuard.TUnion(schema) ? Type.Union(IntrinsicRest(schema.anyOf, mode)) : + TypeGuard.TLiteral(schema) ? Type.Literal(IntrinsicLiteral(schema.const, mode)) : + schema } /** Applies an intrinsic string manipulation to the given type. */ export function Map(schema: T, mode: M): TIntrinsic { @@ -2214,13 +2420,16 @@ export namespace ObjectMap { // prevent sub schema mapping as unregistered kinds will not pass TSchema checks. This is notable in the // case of TObject where unregistered property kinds cause the TObject check to fail. As mapping is only // used for composition, we use explicit checks instead. - if (schema[Kind] === 'Intersect') return TIntersect(schema as TIntersect, callback) - if (schema[Kind] === 'Union') return TUnion(schema as TUnion, callback) - if (schema[Kind] === 'Object') return TObject(schema as TObject, callback) - return schema + // prettier-ignore + return ( + schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, callback) : + schema[Kind] === 'Union' ? TUnion(schema as TUnion, callback) : + schema[Kind] === 'Object' ? TObject(schema as TObject, callback) : + schema + ) } export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { - return { ...Visit(TypeClone.Clone(schema), callback), ...options } as unknown as T + return { ...Visit(TypeClone.Type(schema), callback), ...options } as unknown as T } } // -------------------------------------------------------------------------- @@ -2247,11 +2456,14 @@ export namespace KeyResolver { return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] } function Visit(schema: TSchema, options: KeyResolverOptions): string[] { - if (TypeGuard.TIntersect(schema)) return TIntersect(schema, options) - if (TypeGuard.TUnion(schema)) return TUnion(schema, options) - if (TypeGuard.TObject(schema)) return TObject(schema, options) - if (TypeGuard.TRecord(schema)) return TRecord(schema, options) - return [] + // prettier-ignore + return ( + TypeGuard.TIntersect(schema) ? TIntersect(schema, options) : + TypeGuard.TUnion(schema) ? TUnion(schema, options) : + TypeGuard.TObject(schema) ? TObject(schema, options) : + TypeGuard.TRecord(schema) ? TRecord(schema, options) : + [] + ) } /** Resolves an array of keys in this schema */ export function ResolveKeys(schema: TSchema, options: KeyResolverOptions): string[] { @@ -2267,18 +2479,19 @@ export namespace KeyResolver { // -------------------------------------------------------------------------- // KeyArrayResolver // -------------------------------------------------------------------------- +export class KeyArrayResolverError extends TypeBoxError {} export namespace KeyArrayResolver { /** Resolves an array of string[] keys from the given schema or array type. */ export function Resolve(schema: TSchema | string[]): string[] { - if (Array.isArray(schema)) return schema - if (TypeGuard.TUnionLiteral(schema)) return schema.anyOf.map((schema) => schema.const.toString()) - if (TypeGuard.TLiteral(schema)) return [schema.const as string] - if (TypeGuard.TTemplateLiteral(schema)) { - const expression = TemplateLiteralParser.ParseExact(schema.pattern) - if (!TemplateLiteralFinite.Check(expression)) throw Error('KeyArrayResolver: Cannot resolve keys from infinite template expression') - return [...TemplateLiteralGenerator.Generate(expression)] - } - return [] + // prettier-ignore + return Array.isArray(schema) ? schema : + TypeGuard.TUnionLiteral(schema) ? schema.anyOf.map((schema) => schema.const.toString()) : + TypeGuard.TLiteral(schema) ? [schema.const as string] : + TypeGuard.TTemplateLiteral(schema) ? (() => { + const expression = TemplateLiteralParser.ParseExact(schema.pattern) + if (!TemplateLiteralFinite.Check(expression)) throw new KeyArrayResolverError('Cannot resolve keys from infinite template expression') + return [...TemplateLiteralGenerator.Generate(expression)] + })() : [] } } // -------------------------------------------------------------------------- @@ -2302,32 +2515,27 @@ export namespace UnionResolver { // -------------------------------------------------------------------------- // TemplateLiteralPattern // -------------------------------------------------------------------------- +export class TemplateLiteralPatternError extends TypeBoxError {} export namespace TemplateLiteralPattern { + function Throw(message: string): never { + throw new TemplateLiteralPatternError(message) + } function Escape(value: string) { return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } function Visit(schema: TSchema, acc: string): string { - if (TypeGuard.TTemplateLiteral(schema)) { - return schema.pattern.slice(1, schema.pattern.length - 1) - } else if (TypeGuard.TUnion(schema)) { - return `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` - } else if (TypeGuard.TNumber(schema)) { - return `${acc}${PatternNumber}` - } else if (TypeGuard.TInteger(schema)) { - return `${acc}${PatternNumber}` - } else if (TypeGuard.TBigInt(schema)) { - return `${acc}${PatternNumber}` - } else if (TypeGuard.TString(schema)) { - return `${acc}${PatternString}` - } else if (TypeGuard.TLiteral(schema)) { - return `${acc}${Escape(schema.const.toString())}` - } else if (TypeGuard.TBoolean(schema)) { - return `${acc}${PatternBoolean}` - } else if (TypeGuard.TNever(schema)) { - throw Error('TemplateLiteralPattern: TemplateLiteral cannot operate on types of TNever') - } else { - throw Error(`TemplateLiteralPattern: Unexpected Kind '${schema[Kind]}'`) - } + // prettier-ignore + return ( + TypeGuard.TTemplateLiteral(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : + TypeGuard.TUnion(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : + TypeGuard.TNumber(schema) ? `${acc}${PatternNumber}` : + TypeGuard.TInteger(schema) ? `${acc}${PatternNumber}` : + TypeGuard.TBigInt(schema) ? `${acc}${PatternNumber}` : + TypeGuard.TString(schema) ? `${acc}${PatternString}` : + TypeGuard.TLiteral(schema) ? `${acc}${Escape(schema.const.toString())}` : + TypeGuard.TBoolean(schema) ? `${acc}${PatternBoolean}` : + Throw(`Unexpected Kind '${schema[Kind]}'`) + ) } export function Create(kinds: TTemplateLiteralKind[]): string { return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` @@ -2348,11 +2556,7 @@ export namespace TemplateLiteralResolver { // -------------------------------------------------------------------------------------- // TemplateLiteralParser // -------------------------------------------------------------------------------------- -export class TemplateLiteralParserError extends Error { - constructor(message: string) { - super(message) - } -} +export class TemplateLiteralParserError extends TypeBoxError {} export namespace TemplateLiteralParser { export type Expression = And | Or | Const export type Const = { type: 'const'; const: string } @@ -2447,16 +2651,18 @@ export namespace TemplateLiteralParser { index = end - 1 } } - if (expressions.length === 0) return { type: 'const', const: '' } - if (expressions.length === 1) return expressions[0] - return { type: 'and', expr: expressions } + // prettier-ignore + return (expressions.length === 0) ? { type: 'const', const: '' } : + (expressions.length === 1) ? expressions[0] : + { type: 'and', expr: expressions } } /** Parses a pattern and returns an expression tree */ export function Parse(pattern: string): Expression { - if (IsGroup(pattern)) return Parse(InGroup(pattern)) - if (IsPrecedenceOr(pattern)) return Or(pattern) - if (IsPrecedenceAnd(pattern)) return And(pattern) - return { type: 'const', const: pattern } + // prettier-ignore + return IsGroup(pattern) ? Parse(InGroup(pattern)) : + IsPrecedenceOr(pattern) ? Or(pattern) : + IsPrecedenceAnd(pattern) ? And(pattern) : + { type: 'const', const: pattern } } /** Parses a pattern and strips forward and trailing ^ and $ */ export function ParseExact(pattern: string): Expression { @@ -2466,7 +2672,11 @@ export namespace TemplateLiteralParser { // -------------------------------------------------------------------------------------- // TemplateLiteralFinite // -------------------------------------------------------------------------------------- +export class TemplateLiteralFiniteError extends TypeBoxError {} export namespace TemplateLiteralFinite { + function Throw(message: string): never { + throw new TemplateLiteralFiniteError(message) + } function IsNumber(expression: TemplateLiteralParser.Expression): boolean { // prettier-ignore return ( @@ -2493,17 +2703,19 @@ export namespace TemplateLiteralFinite { return expression.type === 'const' && expression.const === '.*' } export function Check(expression: TemplateLiteralParser.Expression): boolean { - if (IsBoolean(expression)) return true - if (IsNumber(expression) || IsString(expression)) return false - if (expression.type === 'and') return expression.expr.every((expr) => Check(expr)) - if (expression.type === 'or') return expression.expr.every((expr) => Check(expr)) - if (expression.type === 'const') return true - throw Error(`TemplateLiteralFinite: Unknown expression type`) + // prettier-ignore + return IsBoolean(expression) ? true : + IsNumber(expression) || IsString(expression) ? false : + (expression.type === 'and') ? expression.expr.every((expr) => Check(expr)) : + (expression.type === 'or') ? expression.expr.every((expr) => Check(expr)) : + (expression.type === 'const') ? true : + Throw(`Unknown expression type`) } } // -------------------------------------------------------------------------------------- // TemplateLiteralGenerator // -------------------------------------------------------------------------------------- +export class TemplateLiteralGeneratorError extends TypeBoxError {} export namespace TemplateLiteralGenerator { function* Reduce(buffer: string[][]): IterableIterator { if (buffer.length === 1) return yield* buffer[0] @@ -2523,10 +2735,13 @@ export namespace TemplateLiteralGenerator { return yield expression.const } export function* Generate(expression: TemplateLiteralParser.Expression): IterableIterator { - if (expression.type === 'and') return yield* And(expression) - if (expression.type === 'or') return yield* Or(expression) - if (expression.type === 'const') return yield* Const(expression) - throw Error('TemplateLiteralGenerator: Unknown expression') + // prettier-ignore + return ( + expression.type === 'and' ? yield* And(expression) : + expression.type === 'or' ? yield* Or(expression) : + expression.type === 'const' ? yield* Const(expression) : + (() => { throw new TemplateLiteralGeneratorError('Unknown expression') })() + ) } } // --------------------------------------------------------------------- @@ -2535,12 +2750,21 @@ export namespace TemplateLiteralGenerator { export namespace TemplateLiteralDslParser { function* ParseUnion(template: string): IterableIterator { const trim = template.trim().replace(/"|'/g, '') - if (trim === 'boolean') return yield Type.Boolean() - if (trim === 'number') return yield Type.Number() - if (trim === 'bigint') return yield Type.BigInt() - if (trim === 'string') return yield Type.String() - const literals = trim.split('|').map((literal) => Type.Literal(literal.trim())) - return yield literals.length === 0 ? Type.Never() : literals.length === 1 ? literals[0] : Type.Union(literals) + // prettier-ignore + return ( + trim === 'boolean' ? yield Type.Boolean() : + trim === 'number' ? yield Type.Number() : + trim === 'bigint' ? yield Type.BigInt() : + trim === 'string' ? yield Type.String() : + yield (() => { + const literals = trim.split('|').map((literal) => Type.Literal(literal.trim())) + return ( + literals.length === 0 ? Type.Never() : + literals.length === 1 ? literals[0] : + Type.Union(literals) + ) + })() + ) } function* ParseTerminal(template: string): IterableIterator { if (template[1] !== '{') { @@ -2571,6 +2795,33 @@ export namespace TemplateLiteralDslParser { return [...ParseLiteral(template_dsl)] } } +// --------------------------------------------------------------------- +// TransformBuilder +// --------------------------------------------------------------------- +export class TransformDecodeBuilder { + constructor(private readonly schema: T) {} + public Decode, U>>(decode: D): TransformEncodeBuilder { + return new TransformEncodeBuilder(this.schema, decode) + } +} +export class TransformEncodeBuilder { + constructor(private readonly schema: T, private readonly decode: D) {} + public Encode, StaticDecode>>(encode: E): TTransform> { + const schema = TypeClone.Type(this.schema) + // prettier-ignore + return ( + TypeGuard.TTransform(schema) ? (() => { + const Encode = (value: unknown) => schema[Transform].Encode(encode(value as any)) + const Decode = (value: unknown) => this.decode(schema[Transform].Decode(value)) + const Codec = { Encode: Encode, Decode: Decode } + return { ...schema, [Transform]: Codec } + })() : (() => { + const Codec = { Decode: this.decode, Encode: encode } + return { ...schema, [Transform]: Codec } + })() + ) as TTransform> + } +} // -------------------------------------------------------------------------- // TypeOrdinal: Used for auto $id generation // -------------------------------------------------------------------------- @@ -2578,229 +2829,257 @@ let TypeOrdinal = 0 // -------------------------------------------------------------------------- // TypeBuilder // -------------------------------------------------------------------------- +export class TypeBuilderError extends TypeBoxError {} export class TypeBuilder { - /** `[Utility]` Creates a schema without `static` and `params` types */ + /** `[Internal]` Creates a schema without `static` and `params` types */ protected Create(schema: Omit): T { return schema as any } - /** `[Utility]` Discards a property key from the given schema */ + /** `[Internal]` Throws a TypeBuilder error with the given message */ + protected Throw(message: string): never { + throw new TypeBuilderError(message) + } + /** `[Internal]` Discards a property key from the given schema */ protected Discard(schema: TSchema, key: PropertyKey): TSchema { const { [key as any]: _, ...rest } = schema return rest as TSchema } - /** `[Standard]` Omits compositing symbols from this schema */ + /** `[Json]` Omits compositing symbols from this schema */ public Strict(schema: T): T { return JSON.parse(JSON.stringify(schema)) } } // -------------------------------------------------------------------------- -// StandardTypeBuilder +// JsonTypeBuilder // -------------------------------------------------------------------------- -export class StandardTypeBuilder extends TypeBuilder { +export class JsonTypeBuilder extends TypeBuilder { // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ - /** `[Standard]` Creates a Readonly and Optional property */ + /** `[Json]` Creates a Readonly and Optional property */ public ReadonlyOptional(schema: T): TReadonly> { return this.Readonly(this.Optional(schema)) } - /** `[Standard]` Creates a Readonly property */ + /** `[Json]` Creates a Readonly property */ public Readonly(schema: T): TReadonly { - return { ...TypeClone.Clone(schema), [Readonly]: 'Readonly' } + return { ...TypeClone.Type(schema), [Readonly]: 'Readonly' } } - /** `[Standard]` Creates an Optional property */ + /** `[Json]` Creates an Optional property */ public Optional(schema: T): TOptional { - return { ...TypeClone.Clone(schema), [Optional]: 'Optional' } + return { ...TypeClone.Type(schema), [Optional]: 'Optional' } } // ------------------------------------------------------------------------ // Types // ------------------------------------------------------------------------ - /** `[Standard]` Creates an Any type */ + /** `[Json]` Creates an Any type */ public Any(options: SchemaOptions = {}): TAny { return this.Create({ ...options, [Kind]: 'Any' }) } - /** `[Standard]` Creates an Array type */ + /** `[Json]` Creates an Array type */ public Array(schema: T, options: ArrayOptions = {}): TArray { - return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Clone(schema) }) + return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Type(schema) }) } - /** `[Standard]` Creates a Boolean type */ + /** `[Json]` Creates a Boolean type */ public Boolean(options: SchemaOptions = {}): TBoolean { return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) } - /** `[Standard]` Intrinsic function to Capitalize LiteralString types */ + /** `[Json]` Intrinsic function to Capitalize LiteralString types */ public Capitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Capitalize'), ...options } + return { ...Intrinsic.Map(TypeClone.Type(schema), 'Capitalize'), ...options } } - /** `[Standard]` Creates a Composite object type */ + /** `[Json]` Creates a Composite object type */ public Composite(objects: [...T], options?: ObjectOptions): TComposite { const intersect: any = Type.Intersect(objects, {}) const keys = KeyResolver.ResolveKeys(intersect, { includePatterns: false }) const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Type.Index(intersect, [key]) }), {} as TProperties) return Type.Object(properties, options) as TComposite } - /** `[Standard]` Creates a Enum type */ + /** `[Json]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { // prettier-ignore const values = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const anyOf = values.map((value) => (ValueGuard.IsString(value) ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value })) + // prettier-ignore + const anyOf = values.map((value) => ValueGuard.IsString(value) + ? { [Kind]: 'Literal', type: 'string' as const, const: value } + : { [Kind]: 'Literal', type: 'number' as const, const: value } + ) return this.Create({ ...options, [Kind]: 'Union', anyOf }) } - /** `[Standard]` Creates a Conditional type */ + /** `[Json]` Creates a Conditional type */ public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { switch (TypeExtends.Extends(left, right)) { case TypeExtendsResult.Union: - return this.Union([TypeClone.Clone(trueType, options), TypeClone.Clone(falseType, options)]) as any as TExtends + return this.Union([TypeClone.Type(trueType, options), TypeClone.Type(falseType, options)]) as any as TExtends case TypeExtendsResult.True: - return TypeClone.Clone(trueType, options) as unknown as TExtends + return TypeClone.Type(trueType, options) as unknown as TExtends case TypeExtendsResult.False: - return TypeClone.Clone(falseType, options) as unknown as TExtends + return TypeClone.Type(falseType, options) as unknown as TExtends } } - /** `[Standard]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ public Exclude(unionType: L, excludedMembers: R, options: SchemaOptions = {}): TExclude { - if (TypeGuard.TTemplateLiteral(unionType)) return this.Exclude(TemplateLiteralResolver.Resolve(unionType), excludedMembers, options) as TExclude - if (TypeGuard.TTemplateLiteral(excludedMembers)) return this.Exclude(unionType, TemplateLiteralResolver.Resolve(excludedMembers), options) as any as TExclude - if (TypeGuard.TUnion(unionType)) { - const narrowed = unionType.anyOf.filter((inner) => TypeExtends.Extends(inner, excludedMembers) === TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExclude - } else { - return (TypeExtends.Extends(unionType, excludedMembers) !== TypeExtendsResult.False ? this.Never(options) : TypeClone.Clone(unionType, options)) as any - } - } - /** `[Standard]` Constructs a type by extracting from type all union members that are assignable to union */ + // prettier-ignore + return ( + TypeGuard.TTemplateLiteral(unionType) ? this.Exclude(TemplateLiteralResolver.Resolve(unionType), excludedMembers, options) : + TypeGuard.TTemplateLiteral(excludedMembers) ? this.Exclude(unionType, TemplateLiteralResolver.Resolve(excludedMembers), options) : + TypeGuard.TUnion(unionType) ? (() => { + const narrowed = unionType.anyOf.filter((inner) => TypeExtends.Extends(inner, excludedMembers) === TypeExtendsResult.False) + return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) as TExclude + })() : + TypeExtends.Extends(unionType, excludedMembers) !== TypeExtendsResult.False ? this.Never(options) : + TypeClone.Type(unionType, options) + ) as TExclude + } + /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ public Extract(type: L, union: R, options: SchemaOptions = {}): TExtract { - if (TypeGuard.TTemplateLiteral(type)) return this.Extract(TemplateLiteralResolver.Resolve(type), union, options) as TExtract - if (TypeGuard.TTemplateLiteral(union)) return this.Extract(type, TemplateLiteralResolver.Resolve(union), options) as any as TExtract - if (TypeGuard.TUnion(type)) { - const narrowed = type.anyOf.filter((inner) => TypeExtends.Extends(inner, union) !== TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Clone(narrowed[0], options) : this.Union(narrowed, options)) as TExtract - } else { - return (TypeExtends.Extends(type, union) !== TypeExtendsResult.False ? TypeClone.Clone(type, options) : this.Never(options)) as any - } - } - /** `[Standard]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> - /** `[Standard]` Returns an Indexed property type for the given keys */ + // prettier-ignore + return ( + TypeGuard.TTemplateLiteral(type) ? this.Extract(TemplateLiteralResolver.Resolve(type), union, options) : + TypeGuard.TTemplateLiteral(union) ? this.Extract(type, TemplateLiteralResolver.Resolve(union), options) : + TypeGuard.TUnion(type) ? (() => { + const narrowed = type.anyOf.filter((inner) => TypeExtends.Extends(inner, union) !== TypeExtendsResult.False) + return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) + })() : + TypeExtends.Extends(type, union) !== TypeExtendsResult.False ? TypeClone.Type(type, options) : + this.Never(options) + ) as TExtract + } + /** `[Json]` Returns an Indexed property type for the given keys */ public Index(schema: T, keys: K, options?: SchemaOptions): AssertType - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> + /** `[Json]` Returns an Indexed property type for the given keys */ public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ public Index(schema: T, key: K, options?: SchemaOptions): TSchema - /** `[Standard]` Returns an Indexed property type for the given keys */ + /** `[Json]` Returns an Indexed property type for the given keys */ public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - if (TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved)) { - return TypeClone.Clone(schema.items, options) - } else if (TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved)) { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const cloned = items.map((schema) => TypeClone.Clone(schema)) - return this.Union(cloned, options) - } else { - const keys = KeyArrayResolver.Resolve(unresolved) - const clone = TypeClone.Clone(schema) - return IndexedAccessor.Resolve(clone, keys, options) - } + // prettier-ignore + return ( + TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved) ? (() => { + return TypeClone.Type(schema.items, options) + })() : + TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved) ? (() => { + const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items + const cloned = items.map((schema) => TypeClone.Type(schema)) + return this.Union(cloned, options) + })() : (() => { + const keys = KeyArrayResolver.Resolve(unresolved) + const clone = TypeClone.Type(schema) + return IndexedAccessor.Resolve(clone, keys, options) + })() + ) } - /** `[Standard]` Creates an Integer type */ + /** `[Json]` Creates an Integer type */ public Integer(options: NumericOptions = {}): TInteger { return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) } - /** `[Standard]` Creates an Intersect type */ + /** `[Json]` Creates an Intersect type */ public Intersect(allOf: [], options?: SchemaOptions): TNever - /** `[Standard]` Creates an Intersect type */ + /** `[Json]` Creates an Intersect type */ public Intersect(allOf: [...T], options?: SchemaOptions): T[0] - /** `[Standard]` Creates an Intersect type */ + /** `[Json]` Creates an Intersect type */ public Intersect(allOf: [...T], options?: IntersectOptions): TIntersect - /** `[Standard]` Creates an Intersect type */ + /** `[Json]` Creates an Intersect type */ public Intersect(allOf: TSchema[], options: IntersectOptions = {}) { if (allOf.length === 0) return Type.Never() - if (allOf.length === 1) return TypeClone.Clone(allOf[0], options) + if (allOf.length === 1) return TypeClone.Type(allOf[0], options) + if (allOf.some((schema) => TypeGuard.TTransform(schema))) this.Throw('Cannot intersect transform types') const objects = allOf.every((schema) => TypeGuard.TObject(schema)) - const cloned = allOf.map((schema) => TypeClone.Clone(schema)) - const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) ? { unevaluatedProperties: TypeClone.Clone(options.unevaluatedProperties) } : {} - if (options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects) { - return this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) - } else { - return this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: cloned }) - } - } - /** `[Standard]` Creates a KeyOf type */ + const cloned = TypeClone.Rest(allOf) + // prettier-ignore + const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) + ? { unevaluatedProperties: TypeClone.Type(options.unevaluatedProperties) } + : {} + return options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects + ? this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) + : this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: cloned }) + } + /** `[Json]` Creates a KeyOf type */ public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { - if (TypeGuard.TRecord(schema)) { - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - if (pattern === PatternNumberExact) return this.Number(options) as unknown as TKeyOf - if (pattern === PatternStringExact) return this.String(options) as unknown as TKeyOf - throw Error('StandardTypeBuilder: Unable to resolve key type from Record key pattern') - } else if (TypeGuard.TTuple(schema)) { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const literals = items.map((_, index) => Type.Literal(index)) - return this.Union(literals, options) as unknown as TKeyOf - } else if (TypeGuard.TArray(schema)) { - return this.Number(options) as unknown as TKeyOf - } else { - const keys = KeyResolver.ResolveKeys(schema, { includePatterns: false }) - if (keys.length === 0) return this.Never(options) as TKeyOf - const literals = keys.map((key) => this.Literal(key)) - return this.Union(literals, options) as unknown as TKeyOf - } - } - /** `[Standard]` Creates a Literal type */ + // prettier-ignore + return ( + TypeGuard.TRecord(schema) ? (() => { + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + return ( + pattern === PatternNumberExact ? this.Number(options) : + pattern === PatternStringExact ? this.String(options) : + this.Throw('Unable to resolve key type from Record key pattern') + ) + })() : + TypeGuard.TTuple(schema) ? (() => { + const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items + const literals = items.map((_, index) => Type.Literal(index.toString())) + return this.Union(literals, options) + })() : + TypeGuard.TArray(schema) ? (() => { + return this.Number(options) + })() : (() => { + const keys = KeyResolver.ResolveKeys(schema, { includePatterns: false }) + if (keys.length === 0) return this.Never(options) as TKeyOf + const literals = keys.map((key) => this.Literal(key)) + return this.Union(literals, options) + })() + ) as unknown as TKeyOf + } + /** `[Json]` Creates a Literal type */ public Literal(value: T, options: SchemaOptions = {}): TLiteral { return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) } - /** `[Standard]` Intrinsic function to Lowercase LiteralString types */ + /** `[Json]` Intrinsic function to Lowercase LiteralString types */ public Lowercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Lowercase'), ...options } + return { ...Intrinsic.Map(TypeClone.Type(schema), 'Lowercase'), ...options } } - /** `[Standard]` Creates a Never type */ + /** `[Json]` Creates a Never type */ public Never(options: SchemaOptions = {}): TNever { return this.Create({ ...options, [Kind]: 'Never', not: {} }) } - /** `[Standard]` Creates a Not type */ + /** `[Json]` Creates a Not type */ public Not(schema: T, options?: SchemaOptions): TNot { - return this.Create({ ...options, [Kind]: 'Not', not: TypeClone.Clone(schema) }) + return this.Create({ ...options, [Kind]: 'Not', not: TypeClone.Type(schema) }) } - /** `[Standard]` Creates a Null type */ + /** `[Json]` Creates a Null type */ public Null(options: SchemaOptions = {}): TNull { return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) } - /** `[Standard]` Creates a Number type */ + /** `[Json]` Creates a Number type */ public Number(options: NumericOptions = {}): TNumber { return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) } - /** `[Standard]` Creates an Object type */ + /** `[Json]` Creates an Object type */ public Object(properties: T, options: ObjectOptions = {}): TObject { const propertyKeys = Object.getOwnPropertyNames(properties) const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key])) const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) - const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Clone(options.additionalProperties) } : {} - const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(properties[key]) }), {} as TProperties) - if (requiredKeys.length > 0) { - return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) - } else { - return this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) - } + const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Type(options.additionalProperties) } : {} + const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(properties[key]) }), {} as TProperties) + return requiredKeys.length > 0 + ? this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) + : this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) } - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Standard]` Constructs a type whose keys are omitted from the given type */ + /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(TypeClone.Clone(schema), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: string) => !keys.includes(key as any)) if (object.required.length === 0) delete object.required @@ -2811,31 +3090,31 @@ export class StandardTypeBuilder extends TypeBuilder { return this.Create(object) }, options) } - /** `[Standard]` Constructs a type where all properties are optional */ + /** `[Json]` Constructs a type where all properties are optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { // prettier-ignore - return ObjectMap.Map(schema, (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { return { ...acc, [key]: this.Optional(object.properties[key]) } }, {} as TProperties) return this.Object(properties, this.Discard(object, 'required') /* object used as options to retain other constraints */) }, options) } - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick>(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Standard]` Constructs a type whose keys are picked from the given type */ + /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(TypeClone.Clone(schema), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: any) => keys.includes(key)) if (object.required.length === 0) delete object.required @@ -2846,208 +3125,205 @@ export class StandardTypeBuilder extends TypeBuilder { return this.Create(object) }, options) } - /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): RecordUnionLiteralType - /** `[Standard]` Creates a Record type */ - public Record, T extends TSchema>(key: K, schema: T, options?: ObjectOptions): RecordLiteralType - /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): RecordTemplateLiteralType - /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): RecordNumberType - /** `[Standard]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): RecordStringType - /** `[Standard]` Creates a Record type */ - public Record(key: RecordKey, schema: TSchema, options: ObjectOptions = {}) { - if (TypeGuard.TTemplateLiteral(key)) { - const expression = TemplateLiteralParser.ParseExact(key.pattern) - // prettier-ignore - return TemplateLiteralFinite.Check(expression) - ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Clone(schema) }), {} as TProperties), options)) - : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Clone(schema) }}) - } else if (TypeGuard.TUnion(key)) { - const union = UnionResolver.Resolve(key) - if (TypeGuard.TUnionLiteral(union)) { - const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Clone(schema) }), {} as TProperties) - return this.Object(properties, { ...options, [Hint]: 'Record' }) - } else throw Error('StandardTypeBuilder: Record key of type union contains non-literal types') - } else if (TypeGuard.TLiteral(key)) { - if (ValueGuard.IsString(key.const) || ValueGuard.IsNumber(key.const)) { - return this.Object({ [key.const]: TypeClone.Clone(schema) }, options) - } else throw Error('StandardTypeBuilder: Record key of type literal is not of type string or number') - } else if (TypeGuard.TInteger(key) || TypeGuard.TNumber(key)) { - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [PatternNumberExact]: TypeClone.Clone(schema) } }) - } else if (TypeGuard.TString(key)) { - const pattern = ValueGuard.IsUndefined(key.pattern) ? PatternStringExact : key.pattern - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Clone(schema) } }) - } else { - throw Error(`StandardTypeBuilder: Record key is an invalid type`) - } + /** `[Json]` Creates a Record type */ + public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordResolve { + // prettier-ignore + return ( + TypeGuard.TTemplateLiteral(key) ? (() => { + const expression = TemplateLiteralParser.ParseExact(key.pattern) + // prettier-ignore + return TemplateLiteralFinite.Check(expression) + ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(schema) }), {} as TProperties), options)) + : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Type(schema) }}) + })() : + TypeGuard.TUnion(key) ? (() => { + const union = UnionResolver.Resolve(key) + if (TypeGuard.TUnionLiteral(union)) { + const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Type(schema) }), {} as TProperties) + return this.Object(properties, { ...options, [Hint]: 'Record' }) + } else this.Throw('Record key of type union contains non-literal types') + })() : + TypeGuard.TLiteral(key) ? (() => { + // prettier-ignore + return (ValueGuard.IsString(key.const) || ValueGuard.IsNumber(key.const)) + ? this.Object({ [key.const]: TypeClone.Type(schema) }, options) + : this.Throw('Record key of type literal is not of type string or number') + })() : + TypeGuard.TInteger(key) || TypeGuard.TNumber(key) ? (() => { + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [PatternNumberExact]: TypeClone.Type(schema) } }) + })() : + TypeGuard.TString(key) ? (() => { + const pattern = ValueGuard.IsUndefined(key.pattern) ? PatternStringExact : key.pattern + return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Type(schema) } }) + })() : + this.Never() + ) } - /** `[Standard]` Creates a Recursive type */ + /** `[Json]` Creates a Recursive type */ public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { if (ValueGuard.IsUndefined(options.$id)) (options as any).$id = `T${TypeOrdinal++}` const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) thisType.$id = options.$id return this.Create({ ...options, [Hint]: 'Recursive', ...thisType } as any) } - /** `[Standard]` Creates a Ref type. The referenced type must contain a $id */ + /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ public Ref(schema: T, options?: SchemaOptions): TRef - /** `[Standard]` Creates a Ref type. */ + /** `[Json]` Creates a Ref type. */ public Ref($ref: string, options?: SchemaOptions): TRef - /** `[Standard]` Creates a Ref type. */ + /** `[Json]` Creates a Ref type. */ public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { if (ValueGuard.IsString(unresolved)) return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved }) - if (ValueGuard.IsUndefined(unresolved.$id)) throw Error('StandardTypeBuilder.Ref: Target type must specify an $id') + if (ValueGuard.IsUndefined(unresolved.$id)) this.Throw('Reference target type must specify an $id') return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved.$id! }) } - /** `[Standard]` Constructs a type where all properties are required */ + /** `[Json]` Constructs a type where all properties are required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { // prettier-ignore - return ObjectMap.Map(schema, (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { return { ...acc, [key]: this.Discard(object.properties[key], Optional) as TSchema } }, {} as TProperties) return this.Object(properties, object /* object used as options to retain other constraints */) }, options) } - /** `[Standard]` Extracts the rest array from a Tuple */ + /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ public Rest(schema: T): TRest { - if (TypeGuard.TTuple(schema)) { - if (ValueGuard.IsUndefined(schema.items)) return [] as TSchema[] as TRest - return schema.items.map((schema) => TypeClone.Clone(schema)) as TRest - } else { - return [TypeClone.Clone(schema)] as TRest - } + return ( + TypeGuard.TTuple(schema) && !ValueGuard.IsUndefined(schema.items) ? TypeClone.Rest(schema.items) : TypeGuard.TIntersect(schema) ? TypeClone.Rest(schema.allOf) : TypeGuard.TUnion(schema) ? TypeClone.Rest(schema.anyOf) : [] + ) as TRest } - /** `[Standard]` Creates a String type */ + /** `[Json]` Creates a String type */ public String(options: StringOptions = {}): TString { return this.Create({ ...options, [Kind]: 'String', type: 'string' }) } - /** `[Standard]` Creates a TemplateLiteral type from template dsl string */ + /** `[Json]` Creates a TemplateLiteral type from template dsl string */ public TemplateLiteral(templateDsl: T, options?: SchemaOptions): TTemplateLiteralDslParser - /** `[Standard]` Creates a TemplateLiteral type */ + /** `[Json]` Creates a TemplateLiteral type */ public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral - /** `[Standard]` Creates a TemplateLiteral type */ + /** `[Json]` Creates a TemplateLiteral type */ public TemplateLiteral(unresolved: unknown, options: SchemaOptions = {}) { // prettier-ignore - const pattern = (ValueGuard.IsString(unresolved)) + const pattern = ValueGuard.IsString(unresolved) ? TemplateLiteralPattern.Create(TemplateLiteralDslParser.Parse(unresolved)) : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) } - /** `[Standard]` Creates a Tuple type */ + /** `[Json]` Creates a Transform type */ + public Transform(schema: I): TransformDecodeBuilder { + return new TransformDecodeBuilder(schema) + } + /** `[Json]` Creates a Tuple type */ public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { const [additionalItems, minItems, maxItems] = [false, items.length, items.length] - const clonedItems = items.map((item) => TypeClone.Clone(item)) + const clonedItems = TypeClone.Rest(items) // prettier-ignore const schema = (items.length > 0 ? { ...options, [Kind]: 'Tuple', type: 'array', items: clonedItems, additionalItems, minItems, maxItems } : { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any return this.Create(schema) } - /** `[Standard]` Intrinsic function to Uncapitalize LiteralString types */ + /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ public Uncapitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Uncapitalize'), ...options } + return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uncapitalize'), ...options } } - /** `[Standard]` Creates a Union type */ + /** `[Json]` Creates a Union type */ public Union(anyOf: [], options?: SchemaOptions): TNever - /** `[Standard]` Creates a Union type */ + /** `[Json]` Creates a Union type */ public Union(anyOf: [...T], options?: SchemaOptions): T[0] - /** `[Standard]` Creates a Union type */ + /** `[Json]` Creates a Union type */ public Union(anyOf: [...T], options?: SchemaOptions): TUnion - /** `[Experimental]` Converts a TemplateLiteral into a Union */ + /** `[Json-Experimental]` Converts a TemplateLiteral into a Union */ public Union(template: T): TUnionTemplateLiteral - /** `[Standard]` Creates a Union type */ + /** `[Json]` Creates a Union type */ public Union(union: TSchema[] | TTemplateLiteral, options: SchemaOptions = {}) { - if (TypeGuard.TTemplateLiteral(union)) { - return TemplateLiteralResolver.Resolve(union) - } else { - const anyOf = union - if (anyOf.length === 0) return this.Never(options) - if (anyOf.length === 1) return this.Create(TypeClone.Clone(anyOf[0], options)) - const clonedAnyOf = anyOf.map((schema) => TypeClone.Clone(schema)) - return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) - } - } - /** `[Standard]` Creates an Unknown type */ + // prettier-ignore + return TypeGuard.TTemplateLiteral(union) + ? TemplateLiteralResolver.Resolve(union) + : (() => { + const anyOf = union + if (anyOf.length === 0) return this.Never(options) + if (anyOf.length === 1) return this.Create(TypeClone.Type(anyOf[0], options)) + const clonedAnyOf = TypeClone.Rest(anyOf) + return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) + })() + } + /** `[Json]` Creates an Unknown type */ public Unknown(options: SchemaOptions = {}): TUnknown { return this.Create({ ...options, [Kind]: 'Unknown' }) } - /** `[Standard]` Creates a Unsafe type that will infers as the generic argument T */ + /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ public Unsafe(options: UnsafeOptions = {}): TUnsafe { return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) } - /** `[Standard]` Intrinsic function to Uppercase LiteralString types */ + /** `[Json]` Intrinsic function to Uppercase LiteralString types */ public Uppercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Clone(schema), 'Uppercase'), ...options } + return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uppercase'), ...options } } } // -------------------------------------------------------------------------- -// ExtendedTypeBuilder +// JavaScriptTypeBuilder // -------------------------------------------------------------------------- -export class ExtendedTypeBuilder extends StandardTypeBuilder { - /** `[Extended]` Creates a AsyncIterator type */ +export class JavaScriptTypeBuilder extends JsonTypeBuilder { + /** `[JavaScript]` Creates a AsyncIterator type */ public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { - return this.Create({ ...options, [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: TypeClone.Clone(items) }) + return this.Create({ ...options, [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: TypeClone.Type(items) }) } - /** `[Extended]` Constructs a type by recursively unwrapping Promise types */ + /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { - const AwaitedRest = (rest: TSchema[]): TSchema[] => { - if (rest.length === 0) return rest + // prettier-ignore + const Unwrap = (rest: TSchema[]): TSchema[] => rest.length > 0 ? (() => { const [L, ...R] = rest - return [this.Awaited(L), ...AwaitedRest(R)] - } + return [this.Awaited(L), ...Unwrap(R)] + })() : rest // prettier-ignore return ( - TypeGuard.TIntersect(schema) ? Type.Intersect(AwaitedRest(schema.allOf)) : - TypeGuard.TUnion(schema) ? Type.Union(AwaitedRest(schema.anyOf)) : + TypeGuard.TIntersect(schema) ? Type.Intersect(Unwrap(schema.allOf)) : + TypeGuard.TUnion(schema) ? Type.Union(Unwrap(schema.anyOf)) : TypeGuard.TPromise(schema) ? this.Awaited(schema.item) : - TypeClone.Clone(schema, options) + TypeClone.Type(schema, options) ) as TAwaited } - /** `[Extended]` Creates a BigInt type */ + /** `[JavaScript]` Creates a BigInt type */ public BigInt(options: NumericOptions = {}): TBigInt { return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) } - /** `[Extended]` Extracts the ConstructorParameters from the given Constructor type */ + /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { return this.Tuple([...schema.parameters], { ...options }) } - /** `[Extended]` Creates a Constructor type */ + /** `[JavaScript]` Creates a Constructor type */ public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - const clonedReturns = TypeClone.Clone(returns) - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter)) + const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters: clonedParameters, returns: clonedReturns }) } - /** `[Extended]` Creates a Date type */ + /** `[JavaScript]` Creates a Date type */ public Date(options: DateOptions = {}): TDate { return this.Create({ ...options, [Kind]: 'Date', type: 'Date' }) } - /** `[Extended]` Creates a Function type */ + /** `[JavaScript]` Creates a Function type */ public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { - const clonedReturns = TypeClone.Clone(returns, {}) - const clonedParameters = parameters.map((parameter) => TypeClone.Clone(parameter)) + const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters: clonedParameters, returns: clonedReturns }) } - /** `[Extended]` Extracts the InstanceType from the given Constructor type */ + /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { - return TypeClone.Clone(schema.returns, options) + return TypeClone.Type(schema.returns, options) } - /** `[Extended]` Creates an Iterator type */ + /** `[JavaScript]` Creates an Iterator type */ public Iterator(items: T, options: SchemaOptions = {}): TIterator { - return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Clone(items) }) + return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Type(items) }) } - /** `[Extended]` Extracts the Parameters from the given Function type */ + /** `[JavaScript]` Extracts the Parameters from the given Function type */ public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { return this.Tuple(schema.parameters, { ...options }) } - /** `[Extended]` Creates a Promise type */ + /** `[JavaScript]` Creates a Promise type */ public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'Promise', item: TypeClone.Clone(item) }) + return this.Create({ ...options, [Kind]: 'Promise', type: 'Promise', item: TypeClone.Type(item) }) } - /** `[Extended]` Creates a String type from a Regular Expression pattern */ + /** `[JavaScript]` Creates a String type from a Regular Expression pattern */ public RegExp(pattern: string, options?: SchemaOptions): TString - /** `[Extended]` Creates a String type from a Regular Expression */ + /** `[JavaScript]` Creates a String type from a Regular Expression */ public RegExp(regex: RegExp, options?: SchemaOptions): TString /** `[Extended]` Creates a String type */ public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { @@ -3060,28 +3336,28 @@ export class ExtendedTypeBuilder extends StandardTypeBuilder { public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { return this.RegExp(regex, options) } - /** `[Extended]` Extracts the ReturnType from the given Function type */ + /** `[JavaScript]` Extracts the ReturnType from the given Function type */ public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { - return TypeClone.Clone(schema.returns, options) + return TypeClone.Type(schema.returns, options) } - /** `[Extended]` Creates a Symbol type */ + /** `[JavaScript]` Creates a Symbol type */ public Symbol(options?: SchemaOptions): TSymbol { return this.Create({ ...options, [Kind]: 'Symbol', type: 'symbol' }) } - /** `[Extended]` Creates a Undefined type */ + /** `[JavaScript]` Creates a Undefined type */ public Undefined(options: SchemaOptions = {}): TUndefined { return this.Create({ ...options, [Kind]: 'Undefined', type: 'undefined' }) } - /** `[Extended]` Creates a Uint8Array type */ + /** `[JavaScript]` Creates a Uint8Array type */ public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' }) } - /** `[Extended]` Creates a Void type */ + /** `[JavaScript]` Creates a Void type */ public Void(options: SchemaOptions = {}): TVoid { return this.Create({ ...options, [Kind]: 'Void', type: 'void' }) } } -/** JSON Schema Type Builder with Static Resolution for TypeScript */ -export const StandardType = new StandardTypeBuilder() -/** JSON Schema Type Builder with Static Resolution for TypeScript */ -export const Type = new ExtendedTypeBuilder() +/** Json Type Builder with Static Resolution for TypeScript */ +export const JsonType = new JsonTypeBuilder() +/** JavaScript Type Builder with Static Resolution for TypeScript */ +export const Type = new JavaScriptTypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast.ts index 5b284dcbc..16e3ec9cf 100644 --- a/src/value/cast.ts +++ b/src/value/cast.ts @@ -26,43 +26,34 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from './guard' +import { Create } from './create' +import { Check } from './check' +import { Clone } from './clone' +import { Deref } from './deref' import * as Types from '../typebox' -import * as ValueCreate from './create' -import * as ValueCheck from './check' -import * as ValueClone from './clone' -import * as ValueGuard from './guard' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- -export class ValueCastReferenceTypeError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCast: Cannot locate referenced schema with $id '${schema.$ref}'`) - } -} -export class ValueCastArrayUniqueItemsTypeError extends Error { +export class ValueCastArrayUniqueItemsTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { - super('ValueCast: Array cast produced invalid data due to uniqueItems constraint') + super('Array cast produced invalid data due to uniqueItems constraint') } } -export class ValueCastNeverTypeError extends Error { +export class ValueCastNeverTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCast: Never types cannot be cast') + super('Never types cannot be cast') } } -export class ValueCastRecursiveTypeError extends Error { +export class ValueCastRecursiveTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCast.Recursive: Cannot cast recursive schemas') + super('Cannot cast recursive schemas') } } -export class ValueCastUnknownTypeError extends Error { +export class ValueCastUnknownTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCast: Unknown type') - } -} -export class ValueCastDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCast: Unable to dereference type with $id '${schema.$ref}'`) + super('Unknown type') } } // -------------------------------------------------------------------------- @@ -74,19 +65,19 @@ export class ValueCastDereferenceError extends Error { // -------------------------------------------------------------------------- namespace UnionCastCreate { function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { - if (schema[Types.Kind] === 'Object' && typeof value === 'object' && !ValueGuard.IsNull(value)) { + if (schema[Types.Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) { const object = schema as Types.TObject const keys = Object.getOwnPropertyNames(value) const entries = Object.entries(object.properties) const [point, max] = [1 / entries.length, entries.length] return entries.reduce((acc, [key, schema]) => { const literal = schema[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0 - const checks = ValueCheck.Check(schema, references, value[key]) ? point : 0 + const checks = Check(schema, references, value[key]) ? point : 0 const exists = keys.includes(key) ? point : 0 return acc + (literal + checks + exists) }, 0) } else { - return ValueCheck.Check(schema, references, value) ? 1 : 0 + return Check(schema, references, value) ? 1 : 0 } } function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { @@ -110,33 +101,30 @@ namespace UnionCastCreate { } } // -------------------------------------------------------------------------- -// Cast +// Default // -------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) +export function DefaultClone(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return Check(schema, references, value) ? Clone(value) : Create(schema, references) +} +export function Default(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + return Check(schema, references, value) ? value : Create(schema, references) } +// -------------------------------------------------------------------------- +// Cast +// -------------------------------------------------------------------------- function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - const created = ValueGuard.IsArray(value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) - const minimum = ValueGuard.IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created - const maximum = ValueGuard.IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum + if (Check(schema, references, value)) return Clone(value) + const created = IsArray(value) ? Clone(value) : Create(schema, references) + const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created + const maximum = IsNumber(schema.maxItems) && minimum.length > schema.maxItems ? minimum.slice(0, schema.maxItems) : minimum const casted = maximum.map((value: unknown) => Visit(schema.items, references, value)) if (schema.uniqueItems !== true) return casted const unique = [...new Set(casted)] - if (!ValueCheck.Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) + if (!Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) return unique } -function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueCreate.Create(schema, references) + if (Check(schema, references, value)) return Create(schema, references) const required = new Set(schema.returns.required || []) const result = function () {} for (const [key, property] of Object.entries(schema.returns.properties)) { @@ -145,41 +133,17 @@ function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], v } return result } -function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} -function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any { - const created = ValueCreate.Create(schema, references) - const mapped = ValueGuard.IsPlainObject(created) && ValueGuard.IsPlainObject(value) ? { ...(created as any), ...value } : value - return ValueCheck.Check(schema, references, mapped) ? mapped : ValueCreate.Create(schema, references) -} -function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) + const created = Create(schema, references) + const mapped = IsPlainObject(created) && IsPlainObject(value) ? { ...(created as any), ...value } : value + return Check(schema, references, mapped) ? mapped : Create(schema, references) } function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): any { throw new ValueCastNeverTypeError(schema) } -function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return value - if (value === null || typeof value !== 'object') return ValueCreate.Create(schema, references) + if (Check(schema, references, value)) return value + if (value === null || typeof value !== 'object') return Create(schema, references) const required = new Set(schema.required || []) const result = {} as Record for (const [key, property] of Object.entries(schema.properties)) { @@ -196,12 +160,9 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) } return result } -function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return ValueCreate.Create(schema, references) + if (Check(schema, references, value)) return Clone(value) + if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return Create(schema, references) const subschemaPropertyName = Object.getOwnPropertyNames(schema.patternProperties)[0] const subschema = schema.patternProperties[subschemaPropertyName] const result = {} as Record @@ -211,117 +172,78 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v return result } function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): any { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCastDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) -} -function TString(schema: Types.TString, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? value : ValueCreate.Create(schema, references) -} -function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} -function TTemplateLiteral(schema: Types.TSymbol, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + return Visit(Deref(schema, references), references, value) } function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): any { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCastDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + return Visit(Deref(schema, references), references, value) } function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { - if (ValueCheck.Check(schema, references, value)) return ValueClone.Clone(value) - if (!ValueGuard.IsArray(value)) return ValueCreate.Create(schema, references) + if (Check(schema, references, value)) return Clone(value) + if (!IsArray(value)) return Create(schema, references) if (schema.items === undefined) return [] return schema.items.map((schema, index) => Visit(schema, references, value[index])) } -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : UnionCastCreate.Create(schema, references, value) -} -function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} -function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) -} -function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - return ValueCheck.Check(schema, references, value) ? ValueClone.Clone(value) : ValueCreate.Create(schema, references) + return Check(schema, references, value) ? Clone(value) : UnionCastCreate.Create(schema, references, value) } function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any switch (schema[Types.Kind]) { - case 'Any': - return TAny(schema_, references_, value) + // ------------------------------------------------------ + // Structural + // ------------------------------------------------------ case 'Array': return TArray(schema_, references_, value) - case 'AsyncIterator': - return TAsyncIterator(schema_, references_, value) - case 'BigInt': - return TBigInt(schema_, references_, value) - case 'Boolean': - return TBoolean(schema_, references_, value) case 'Constructor': return TConstructor(schema_, references_, value) - case 'Date': - return TDate(schema_, references_, value) - case 'Function': - return TFunction(schema_, references_, value) - case 'Integer': - return TInteger(schema_, references_, value) case 'Intersect': return TIntersect(schema_, references_, value) - case 'Iterator': - return TIterator(schema_, references_, value) - case 'Literal': - return TLiteral(schema_, references_, value) case 'Never': return TNever(schema_, references_, value) - case 'Not': - return TNot(schema_, references_, value) - case 'Null': - return TNull(schema_, references_, value) - case 'Number': - return TNumber(schema_, references_, value) case 'Object': return TObject(schema_, references_, value) - case 'Promise': - return TPromise(schema_, references_, value) case 'Record': return TRecord(schema_, references_, value) case 'Ref': return TRef(schema_, references_, value) - case 'String': - return TString(schema_, references_, value) - case 'Symbol': - return TSymbol(schema_, references_, value) - case 'TemplateLiteral': - return TTemplateLiteral(schema_, references_, value) case 'This': return TThis(schema_, references_, value) case 'Tuple': return TTuple(schema_, references_, value) - case 'Undefined': - return TUndefined(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) + // ------------------------------------------------------ + // DefaultClone + // ------------------------------------------------------ + case 'Date': + case 'Symbol': case 'Uint8Array': - return TUint8Array(schema_, references_, value) + return DefaultClone(schema, references, value) + // ------------------------------------------------------ + // Default + // ------------------------------------------------------ + case 'Any': + case 'AsyncIterator': + case 'BigInt': + case 'Boolean': + case 'Function': + case 'Integer': + case 'Iterator': + case 'Literal': + case 'Not': + case 'Null': + case 'Number': + case 'Promise': + case 'String': + case 'TemplateLiteral': + case 'Undefined': case 'Unknown': - return TUnknown(schema_, references_, value) case 'Void': - return TVoid(schema_, references_, value) + return Default(schema_, references_, value) default: if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) - return TKind(schema_, references_, value) + return Default(schema_, references_, value) } } // -------------------------------------------------------------------------- diff --git a/src/value/check.ts b/src/value/check.ts index 624e08eda..7e987708c 100644 --- a/src/value/check.ts +++ b/src/value/check.ts @@ -26,22 +26,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeSystem } from '../system/index' +import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from './guard' +import { TypeSystemPolicy } from '../system/index' +import { Deref } from './deref' +import { Hash } from './hash' import * as Types from '../typebox' -import * as ValueGuard from './guard' -import * as ValueHash from './hash' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- -export class ValueCheckUnknownTypeError extends Error { +export class ValueCheckUnknownTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super(`ValueCheck: ${schema[Types.Kind] ? `Unknown type '${schema[Types.Kind]}'` : 'Unknown type'}`) - } -} -export class ValueCheckDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCheck: Unable to dereference type with $id '${schema.$ref}'`) + super(`Unknown type`) } } // -------------------------------------------------------------------------- @@ -57,36 +53,13 @@ function IsDefined(value: unknown): value is T { return value !== undefined } // -------------------------------------------------------------------------- -// Policies -// -------------------------------------------------------------------------- -function IsExactOptionalProperty(value: Record, key: string) { - return TypeSystem.ExactOptionalPropertyTypes ? key in value : value[key] !== undefined -} -function IsObject(value: unknown): value is Record { - const isObject = ValueGuard.IsObject(value) - return TypeSystem.AllowArrayObjects ? isObject : isObject && !ValueGuard.IsArray(value) -} -function IsRecordObject(value: unknown): value is Record { - return IsObject(value) && !(value instanceof Date) && !(value instanceof Uint8Array) -} -function IsNumber(value: unknown): value is number { - const isNumber = ValueGuard.IsNumber(value) - return TypeSystem.AllowNaN ? isNumber : isNumber && Number.isFinite(value) -} -function IsVoid(value: unknown): value is void { - const isUndefined = ValueGuard.IsUndefined(value) - return TypeSystem.AllowVoidNull ? isUndefined || value === null : isUndefined -} -// -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { return true } function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { - if (!Array.isArray(value)) { - return false - } + if (!IsArray(value)) return false if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { return false } @@ -97,7 +70,7 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): return false } // prettier-ignore - if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = ValueHash.Hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { + if (schema.uniqueItems === true && !((function() { const set = new Set(); for(const element of value) { const hashed = Hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true })())) { return false } // contains @@ -105,7 +78,7 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): return true // exit } const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() - const containsCount = value.reduce((acc, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0) + const containsCount = value.reduce((acc: number, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0) if (containsCount === 0) { return false } @@ -118,76 +91,72 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): return true } function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): boolean { - return IsObject(value) && Symbol.asyncIterator in value + return IsAsyncIterator(value) } function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { - if (!ValueGuard.IsBigInt(value)) { - return false - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { + if (!IsBigInt(value)) return false + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { return false } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { return false } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) { return false } return true } function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { - return typeof value === 'boolean' + return IsBoolean(value) } function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { return Visit(schema.returns, references, value.prototype) } function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { - if (!(value instanceof Date)) { - return false - } - if (!IsNumber(value.getTime())) { + if (!IsDate(value)) return false + if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { return false } if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) { return false } - if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { + if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { return false } if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) { return false } - if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) { + if (IsDefined(schema.multipleOfTimestamp) && !(value.getTime() % schema.multipleOfTimestamp === 0)) { return false } return true } function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { - return typeof value === 'function' + return IsFunction(value) } function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { - if (!ValueGuard.IsInteger(value)) { + if (!IsInteger(value)) { return false } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { + if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { return false } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { return false } - if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { return false } return true @@ -207,7 +176,7 @@ function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value } } function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): boolean { - return IsObject(value) && Symbol.iterator in value + return IsIterator(value) } function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { return value === schema.const @@ -219,33 +188,29 @@ function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): bool return !Visit(schema.not, references, value) } function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { - return value === null + return IsNull(value) } function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { - if (!IsNumber(value)) { - return false - } - if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + if (!TypeSystemPolicy.IsNumberLike(value)) return false + if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false } if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) { return false } - if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { - return false - } if (IsDefined(schema.minimum) && !(value >= schema.minimum)) { return false } if (IsDefined(schema.maximum) && !(value <= schema.maximum)) { return false } + if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) { + return false + } return true } function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { - if (!IsObject(value)) { - return false - } + if (!TypeSystemPolicy.IsObjectLike(value)) return false if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { return false } @@ -263,7 +228,7 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) return false } } else { - if (IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { + if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) { return false } } @@ -284,10 +249,10 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) } } function TPromise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { - return typeof value === 'object' && typeof value.then === 'function' + return IsPromise(value) } function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { - if (!IsRecordObject(value)) { + if (!TypeSystemPolicy.IsRecordLike(value)) { return false } if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { @@ -298,27 +263,27 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v } const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] const regex = new RegExp(patternKey) - return Object.entries(value).every(([key, value]) => { - if (regex.test(key)) { - return Visit(patternSchema, references, value) - } - if (typeof schema.additionalProperties === 'object') { - return Visit(schema.additionalProperties, references, value) - } - if (schema.additionalProperties === false) { - return false - } - return true + // prettier-ignore + const check1 = Object.entries(value).every(([key, value]) => { + return (regex.test(key)) ? Visit(patternSchema, references, value) : true }) + // prettier-ignore + const check2 = typeof schema.additionalProperties === 'object' ? Object.entries(value).every(([key, value]) => { + return (!regex.test(key)) ? Visit(schema.additionalProperties as Types.TSchema, references, value) : true + }) : true + const check3 = + schema.additionalProperties === false + ? Object.getOwnPropertyNames(value).every((key) => { + return regex.test(key) + }) + : true + return check1 && check2 && check3 } function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCheckDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + return Visit(Deref(schema, references), references, value) } function TString(schema: Types.TString, references: Types.TSchema[], value: any): boolean { - if (!ValueGuard.IsString(value)) { + if (!IsString(value)) { return false } if (IsDefined(schema.minLength)) { @@ -339,25 +304,16 @@ function TString(schema: Types.TString, references: Types.TSchema[], value: any) return true } function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): boolean { - if (!(typeof value === 'symbol')) { - return false - } - return true + return IsSymbol(value) } function TTemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { - if (!ValueGuard.IsString(value)) { - return false - } - return new RegExp(schema.pattern).test(value) + return IsString(value) && new RegExp(schema.pattern).test(value) } function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): boolean { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCheckDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + return Visit(Deref(schema, references), references, value) } function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { - if (!ValueGuard.IsArray(value)) { + if (!IsArray(value)) { return false } if (schema.items === undefined && !(value.length === 0)) { @@ -375,13 +331,13 @@ function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: return true } function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { - return value === undefined + return IsUndefined(value) } function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { return schema.anyOf.some((inner) => Visit(inner, references, value)) } function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { - if (!(value instanceof Uint8Array)) { + if (!IsUint8Array(value)) { return false } if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { @@ -396,7 +352,7 @@ function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: an return true } function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { - return IsVoid(value) + return TypeSystemPolicy.IsVoidLike(value) } function TKind(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { if (!Types.TypeRegistry.Has(schema[Types.Kind])) return false diff --git a/src/value/clone.ts b/src/value/clone.ts index ef1ecb51d..a2e2d1f30 100644 --- a/src/value/clone.ts +++ b/src/value/clone.ts @@ -26,40 +26,26 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as ValueGuard from './guard' +import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from './guard' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' // -------------------------------------------------------------------------- // Clonable // -------------------------------------------------------------------------- -function ObjectType(value: ValueGuard.ObjectType): any { +function ObjectType(value: ObjectType): any { const keys = [...Object.getOwnPropertyNames(value), ...Object.getOwnPropertySymbols(value)] return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) } -function ArrayType(value: ValueGuard.ArrayType): any { +function ArrayType(value: ArrayType): any { return value.map((element: any) => Clone(element)) } -function TypedArrayType(value: ValueGuard.TypedArrayType): any { +function TypedArrayType(value: TypedArrayType): any { return value.slice() } function DateType(value: Date): any { return new Date(value.toISOString()) } -function ValueType(value: ValueGuard.ValueType): any { - return value -} -// -------------------------------------------------------------------------- -// Non-Clonable -// -------------------------------------------------------------------------- -function AsyncIteratorType(value: AsyncIterableIterator): any { - return value -} -function IteratorType(value: IterableIterator): any { - return value -} -function FunctionType(value: Function): any { - return value -} -function PromiseType(value: Promise): any { +function ValueType(value: ValueType): any { return value } // -------------------------------------------------------------------------- @@ -67,14 +53,10 @@ function PromiseType(value: Promise): any { // -------------------------------------------------------------------------- /** Returns a clone of the given value */ export function Clone(value: T): T { - if (ValueGuard.IsArray(value)) return ArrayType(value) - if (ValueGuard.IsAsyncIterator(value)) return AsyncIteratorType(value) - if (ValueGuard.IsFunction(value)) return FunctionType(value) - if (ValueGuard.IsIterator(value)) return IteratorType(value) - if (ValueGuard.IsPromise(value)) return PromiseType(value) - if (ValueGuard.IsDate(value)) return DateType(value) - if (ValueGuard.IsPlainObject(value)) return ObjectType(value) - if (ValueGuard.IsTypedArray(value)) return TypedArrayType(value) - if (ValueGuard.IsValueType(value)) return ValueType(value) + if (IsArray(value)) return ArrayType(value) + if (IsDate(value)) return DateType(value) + if (IsPlainObject(value)) return ObjectType(value) + if (IsTypedArray(value)) return TypedArrayType(value) + if (IsValueType(value)) return ValueType(value) throw new Error('ValueClone: Unable to clone value') } diff --git a/src/value/convert.ts b/src/value/convert.ts index c38be43e3..fd64cfacf 100644 --- a/src/value/convert.ts +++ b/src/value/convert.ts @@ -26,53 +26,49 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from './guard' +import { Clone } from './clone' +import { Check } from './check' +import { Deref } from './deref' import * as Types from '../typebox' -import * as ValueClone from './clone' -import * as ValueCheck from './check' -import * as ValueGuard from './guard' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- -export class ValueConvertUnknownTypeError extends Error { +export class ValueConvertUnknownTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueConvert: Unknown type') - } -} -export class ValueConvertDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueConvert: Unable to dereference type with $id '${schema.$ref}'`) + super('Unknown type') } } // -------------------------------------------------------------------------- // Conversions // -------------------------------------------------------------------------- function IsStringNumeric(value: unknown): value is string { - return ValueGuard.IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) + return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) } function IsValueToString(value: unknown): value is { toString: () => string } { - return ValueGuard.IsBigInt(value) || ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) + return IsBigInt(value) || IsBoolean(value) || IsNumber(value) } function IsValueTrue(value: unknown): value is true { - return value === true || (ValueGuard.IsNumber(value) && value === 1) || (ValueGuard.IsBigInt(value) && value === BigInt('1')) || (ValueGuard.IsString(value) && (value.toLowerCase() === 'true' || value === '1')) + return value === true || (IsNumber(value) && value === 1) || (IsBigInt(value) && value === BigInt('1')) || (IsString(value) && (value.toLowerCase() === 'true' || value === '1')) } function IsValueFalse(value: unknown): value is false { - return value === false || (ValueGuard.IsNumber(value) && value === 0) || (ValueGuard.IsBigInt(value) && value === BigInt('0')) || (ValueGuard.IsString(value) && (value.toLowerCase() === 'false' || value === '0')) + return value === false || (IsNumber(value) && (value === 0 || Object.is(value, -0))) || (IsBigInt(value) && value === BigInt('0')) || (IsString(value) && (value.toLowerCase() === 'false' || value === '0' || value === '-0')) } function IsTimeStringWithTimeZone(value: unknown): value is string { - return ValueGuard.IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) } function IsTimeStringWithoutTimeZone(value: unknown): value is string { - return ValueGuard.IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + return IsString(value) && /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) } function IsDateTimeStringWithTimeZone(value: unknown): value is string { - return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i.test(value) } function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { - return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)?$/i.test(value) } function IsDateString(value: unknown): value is string { - return ValueGuard.IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) + return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) } // -------------------------------------------------------------------------- // Convert @@ -97,29 +93,29 @@ function TryConvertLiteral(schema: Types.TLiteral, value: unknown) { } else if (typeof schema.const === 'boolean') { return TryConvertLiteralBoolean(value, schema.const) } else { - return ValueClone.Clone(value) + return Clone(value) } } function TryConvertBoolean(value: unknown) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value } function TryConvertBigInt(value: unknown) { - return IsStringNumeric(value) ? BigInt(parseInt(value)) : ValueGuard.IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? 0 : IsValueTrue(value) ? 1 : value + return IsStringNumeric(value) ? BigInt(parseInt(value)) : IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value } function TryConvertString(value: unknown) { - return IsValueToString(value) ? value.toString() : ValueGuard.IsSymbol(value) && value.description !== undefined ? value.description.toString() : value + return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value } function TryConvertNumber(value: unknown) { return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value } function TryConvertInteger(value: unknown) { - return IsStringNumeric(value) ? parseInt(value) : ValueGuard.IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value + return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value } function TryConvertNull(value: unknown) { - return ValueGuard.IsString(value) && value.toLowerCase() === 'null' ? null : value + return IsString(value) && value.toLowerCase() === 'null' ? null : value } function TryConvertUndefined(value: unknown) { - return ValueGuard.IsString(value) && value === 'undefined' ? undefined : value + return IsString(value) && value === 'undefined' ? undefined : value } function TryConvertDate(value: unknown) { // -------------------------------------------------------------------------- @@ -128,9 +124,9 @@ function TryConvertDate(value: unknown) { // and will return a epoch date if invalid. Consider better string parsing // for the iso dates in future revisions. // -------------------------------------------------------------------------- - return ValueGuard.IsDate(value) + return IsDate(value) ? value - : ValueGuard.IsNumber(value) + : IsNumber(value) ? new Date(value) : IsValueTrue(value) ? new Date(1) @@ -151,50 +147,35 @@ function TryConvertDate(value: unknown) { : value } // -------------------------------------------------------------------------- -// Cast +// Default // -------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): any { +export function Default(value: any) { return value } +// -------------------------------------------------------------------------- +// Convert +// -------------------------------------------------------------------------- function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - if (ValueGuard.IsArray(value)) { + if (IsArray(value)) { return value.map((value) => Visit(schema.items, references, value)) } return value } -function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): any { - return value -} function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): unknown { return TryConvertBigInt(value) } function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { return TryConvertBoolean(value) } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): unknown { - return ValueClone.Clone(value) -} function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { return TryConvertDate(value) } -function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): unknown { - return value -} function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { return TryConvertInteger(value) } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { - return value -} -function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): unknown { - return value -} function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { return TryConvertLiteral(schema, value) } -function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): unknown { - return value -} function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { return TryConvertNull(value) } @@ -202,15 +183,12 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any) return TryConvertNumber(value) } function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { - if (ValueGuard.IsObject(value)) + if (IsObject(value)) return Object.getOwnPropertyNames(schema.properties).reduce((acc, key) => { return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } }, value) return value } -function TPromise(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - return value -} function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] @@ -221,28 +199,19 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v return result } function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueConvertDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + return Visit(Deref(schema, references), references, value) } function TString(schema: Types.TString, references: Types.TSchema[], value: any): unknown { return TryConvertString(value) } function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { - return value -} -function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: any) { - return value + return IsString(value) || IsNumber(value) ? Symbol(value) : value } function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): unknown { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueConvertDereferenceError(schema) - const target = references[index] - return Visit(target, references, value) + return Visit(Deref(schema, references), references, value) } function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { - if (ValueGuard.IsArray(value) && !ValueGuard.IsUndefined(schema.items)) { + if (IsArray(value) && !IsUndefined(schema.items)) { return value.map((value, index) => { return index < schema.items!.length ? Visit(schema.items![index], references, value) : value }) @@ -255,62 +224,37 @@ function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { for (const subschema of schema.anyOf) { const converted = Visit(subschema, references, value) - if (ValueCheck.Check(subschema, references, converted)) { + if (Check(subschema, references, converted)) { return converted } } return value } -function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): unknown { - return value -} -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): unknown { - return value -} -function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): unknown { - return value -} -function TKind(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - return value -} function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { - const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any switch (schema[Types.Kind]) { - case 'Any': - return TAny(schema_, references_, value) + // ------------------------------------------------------ + // Structural + // ------------------------------------------------------ case 'Array': return TArray(schema_, references_, value) - case 'AsyncIterator': - return TAsyncIterator(schema_, references_, value) case 'BigInt': return TBigInt(schema_, references_, value) case 'Boolean': return TBoolean(schema_, references_, value) - case 'Constructor': - return TConstructor(schema_, references_, value) case 'Date': return TDate(schema_, references_, value) - case 'Function': - return TFunction(schema_, references_, value) case 'Integer': return TInteger(schema_, references_, value) - case 'Intersect': - return TIntersect(schema_, references_, value) - case 'Iterator': - return TIterator(schema_, references_, value) case 'Literal': return TLiteral(schema_, references_, value) - case 'Never': - return TNever(schema_, references_, value) case 'Null': return TNull(schema_, references_, value) case 'Number': return TNumber(schema_, references_, value) case 'Object': return TObject(schema_, references_, value) - case 'Promise': - return TPromise(schema_, references_, value) case 'Record': return TRecord(schema_, references_, value) case 'Ref': @@ -319,8 +263,6 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): return TString(schema_, references_, value) case 'Symbol': return TSymbol(schema_, references_, value) - case 'TemplateLiteral': - return TTemplateLiteral(schema_, references_, value) case 'This': return TThis(schema_, references_, value) case 'Tuple': @@ -329,15 +271,25 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): return TUndefined(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) + // ------------------------------------------------------ + // Default + // ------------------------------------------------------ + case 'Any': + case 'AsyncIterator': + case 'Constructor': + case 'Function': + case 'Intersect': + case 'Iterator': + case 'Never': + case 'Promise': + case 'TemplateLiteral': case 'Uint8Array': - return TUint8Array(schema_, references_, value) case 'Unknown': - return TUnknown(schema_, references_, value) case 'Void': - return TVoid(schema_, references_, value) + return Default(value) default: if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) - return TKind(schema_, references_, value) + return Default(value) } } // -------------------------------------------------------------------------- diff --git a/src/value/create.ts b/src/value/create.ts index a2387b6f1..1013bbbdb 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -26,62 +26,58 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { HasPropertyKey, IsString } from './guard' +import { Check } from './check' +import { Deref } from './deref' import * as Types from '../typebox' -import * as ValueCheck from './check' -import * as ValueGuard from './guard' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- -export class ValueCreateUnknownTypeError extends Error { +export class ValueCreateUnknownTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Unknown type') + super('Unknown type') } } -export class ValueCreateNeverTypeError extends Error { +export class ValueCreateNeverTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Never types cannot be created') + super('Never types cannot be created') } } -export class ValueCreateNotTypeError extends Error { +export class ValueCreateNotTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Not types must have a default value') + super('Not types must have a default value') } } -export class ValueCreateIntersectTypeError extends Error { +export class ValueCreateIntersectTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Intersect produced invalid value. Consider using a default value.') + super('Intersect produced invalid value. Consider using a default value.') } } -export class ValueCreateTempateLiteralTypeError extends Error { +export class ValueCreateTempateLiteralTypeError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema) { - super('ValueCreate: Can only create template literal values from patterns that produce finite sequences. Consider using a default value.') + super('Can only create template literal values from patterns that produce finite sequences. Consider using a default value.') } } -export class ValueCreateDereferenceError extends Error { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`ValueCreate: Unable to dereference type with $id '${schema.$ref}'`) - } -} -export class ValueCreateRecursiveInstantiationError extends Error { +export class ValueCreateRecursiveInstantiationError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema, public readonly recursiveMaxDepth: number) { - super('ValueCreate: Value cannot be created as recursive type may produce value of infinite size. Consider using a default.') + super('Value cannot be created as recursive type may produce value of infinite size. Consider using a default.') } } // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- function TAny(schema: Types.TAny, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } function TArray(schema: Types.TArray, references: Types.TSchema[]): any { - if (schema.uniqueItems === true && !ValueGuard.HasPropertyKey(schema, 'default')) { + if (schema.uniqueItems === true && !HasPropertyKey(schema, 'default')) { throw new Error('ValueCreate.Array: Array with the uniqueItems constraint requires a default value') - } else if ('contains' in schema && !ValueGuard.HasPropertyKey(schema, 'default')) { + } else if ('contains' in schema && !HasPropertyKey(schema, 'default')) { throw new Error('ValueCreate.Array: Array with the contains constraint requires a default value') } else if ('default' in schema) { return schema.default @@ -94,28 +90,28 @@ function TArray(schema: Types.TArray, references: Types.TSchema[]): any { } } function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]) { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return (async function* () {})() } } function TBigInt(schema: Types.TBigInt, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return BigInt(0) } } function TBoolean(schema: Types.TBoolean, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return false } } function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { const value = Visit(schema.returns, references) as any @@ -134,7 +130,7 @@ function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): } } function TDate(schema: Types.TDate, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimumTimestamp !== undefined) { return new Date(schema.minimumTimestamp) @@ -143,14 +139,14 @@ function TDate(schema: Types.TDate, references: Types.TSchema[]): any { } } function TFunction(schema: Types.TFunction, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return () => Visit(schema.returns, references) } } function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { return schema.minimum @@ -159,7 +155,7 @@ function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { } } function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { // Note: The best we can do here is attempt to instance each sub type and apply through object assign. For non-object @@ -169,19 +165,19 @@ function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any const next = Visit(schema, references) as any return typeof next === 'object' ? { ...acc, ...next } : next }, {}) - if (!ValueCheck.Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) + if (!Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) return value } } function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return (function* () {})() } } function TLiteral(schema: Types.TLiteral, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return schema.const @@ -191,21 +187,21 @@ function TNever(schema: Types.TNever, references: Types.TSchema[]): any { throw new ValueCreateNeverTypeError(schema) } function TNot(schema: Types.TNot, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { throw new ValueCreateNotTypeError(schema) } } function TNull(schema: Types.TNull, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return null } } function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { return schema.minimum @@ -214,7 +210,7 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { } } function TObject(schema: Types.TObject, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { const required = new Set(schema.required) @@ -227,7 +223,7 @@ function TObject(schema: Types.TObject, references: Types.TSchema[]): any { } } function TPromise(schema: Types.TPromise, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return Promise.resolve(Visit(schema.item, references)) @@ -235,7 +231,7 @@ function TPromise(schema: Types.TPromise, references: Types.TSchema[]): any } function TRecord(schema: Types.TRecord, references: Types.TSchema[]): any { const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0] - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') @@ -247,30 +243,27 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[]): } } function TRef(schema: Types.TRef, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCreateDereferenceError(schema) - const target = references[index] - return Visit(target, references) + return Visit(Deref(schema, references), references) } } function TString(schema: Types.TString, references: Types.TSchema[]): any { if (schema.pattern !== undefined) { - if (!ValueGuard.HasPropertyKey(schema, 'default')) { + if (!HasPropertyKey(schema, 'default')) { throw new Error('ValueCreate.String: String types with patterns must specify a default value') } else { return schema.default } } else if (schema.format !== undefined) { - if (!ValueGuard.HasPropertyKey(schema, 'default')) { + if (!HasPropertyKey(schema, 'default')) { throw new Error('ValueCreate.String: String types with formats must specify a default value') } else { return schema.default } } else { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minLength !== undefined) { return Array.from({ length: schema.minLength }) @@ -282,7 +275,7 @@ function TString(schema: Types.TString, references: Types.TSchema[]): any { } } function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if ('value' in schema) { return Symbol.for(schema.value) @@ -291,7 +284,7 @@ function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { } } function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } const expression = Types.TemplateLiteralParser.ParseExact(schema.pattern) @@ -301,17 +294,14 @@ function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSch } function TThis(schema: Types.TThis, references: Types.TSchema[]): any { if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { - const index = references.findIndex((foreign) => foreign.$id === schema.$ref) - if (index === -1) throw new ValueCreateDereferenceError(schema) - const target = references[index] - return Visit(target, references) + return Visit(Deref(schema, references), references) } } function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } if (schema.items === undefined) { @@ -321,14 +311,14 @@ function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { } } function TUndefined(schema: Types.TUndefined, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return undefined } } function TUnion(schema: Types.TUnion, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.anyOf.length === 0) { throw new Error('ValueCreate.Union: Cannot create Union with zero variants') @@ -337,7 +327,7 @@ function TUnion(schema: Types.TUnion, references: Types.TSchema[]): any { } } function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minByteLength !== undefined) { return new Uint8Array(schema.minByteLength) @@ -346,28 +336,28 @@ function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): an } } function TUnknown(schema: Types.TUnknown, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } function TVoid(schema: Types.TVoid, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { return void 0 } } function TKind(schema: Types.TSchema, references: Types.TSchema[]): any { - if (ValueGuard.HasPropertyKey(schema, 'default')) { + if (HasPropertyKey(schema, 'default')) { return schema.default } else { - throw new Error('ValueCreate: User defined types must specify a default value') + throw new Error('User defined types must specify a default value') } } function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { - const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any switch (schema_[Types.Kind]) { case 'Any': diff --git a/src/value/delta.ts b/src/value/delta.ts index 5a8074645..5a2092dc0 100644 --- a/src/value/delta.ts +++ b/src/value/delta.ts @@ -26,10 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { IsPlainObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from './guard' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' import { Type, Static } from '../typebox' import { ValuePointer } from './pointer' -import * as ValueGuard from './guard' -import * as ValueClone from './clone' +import { Clone } from './clone' // -------------------------------------------------------------------------- // Commands @@ -58,12 +59,12 @@ export const Edit = Type.Union([Insert, Update, Delete]) // -------------------------------------------------------------------------- export class ValueDeltaObjectWithSymbolKeyError extends Error { constructor(public readonly key: unknown) { - super('ValueDelta: Cannot diff objects with symbol keys') + super('Cannot diff objects with symbol keys') } } export class ValueDeltaUnableToDiffUnknownValue extends Error { constructor(public readonly value: unknown) { - super('ValueDelta: Unable to create diff edits for unknown value') + super('Unable to create diff edits for unknown value') } } // -------------------------------------------------------------------------- @@ -81,30 +82,30 @@ function CreateDelete(path: string): Edit { // -------------------------------------------------------------------------- // Diffing Generators // -------------------------------------------------------------------------- -function* ObjectType(path: string, current: ValueGuard.ObjectType, next: unknown): IterableIterator { - if (!ValueGuard.IsPlainObject(next)) return yield CreateUpdate(path, next) +function* ObjectType(path: string, current: ObjectType, next: unknown): IterableIterator { + if (!IsPlainObject(next)) return yield CreateUpdate(path, next) const currentKeys = [...Object.keys(current), ...Object.getOwnPropertySymbols(current)] const nextKeys = [...Object.keys(next), ...Object.getOwnPropertySymbols(next)] for (const key of currentKeys) { - if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (ValueGuard.IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${String(key)}`, undefined) + if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${String(key)}`, undefined) } for (const key of nextKeys) { - if (ValueGuard.IsUndefined(current[key]) || ValueGuard.IsUndefined(next[key])) continue - if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (IsUndefined(current[key]) || IsUndefined(next[key])) continue + if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) yield* Visit(`${path}/${String(key)}`, current[key], next[key]) } for (const key of nextKeys) { - if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (ValueGuard.IsUndefined(current[key])) yield CreateInsert(`${path}/${String(key)}`, next[key]) + if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (IsUndefined(current[key])) yield CreateInsert(`${path}/${String(key)}`, next[key]) } for (const key of currentKeys.reverse()) { - if (ValueGuard.IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (ValueGuard.IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${String(key)}`) + if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) + if (IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${String(key)}`) } } -function* ArrayType(path: string, current: ValueGuard.ArrayType, next: unknown): IterableIterator { - if (!ValueGuard.IsArray(next)) return yield CreateUpdate(path, next) +function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIterator { + if (!IsArray(next)) return yield CreateUpdate(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]) } @@ -117,21 +118,21 @@ function* ArrayType(path: string, current: ValueGuard.ArrayType, next: unknown): yield CreateDelete(`${path}/${i}`) } } -function* TypedArrayType(path: string, current: ValueGuard.TypedArrayType, next: unknown): IterableIterator { - if (!ValueGuard.IsTypedArray(next) || current.length !== next.length || Object.getPrototypeOf(current).constructor.name !== Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next) +function* TypedArrayType(path: string, current: TypedArrayType, next: unknown): IterableIterator { + if (!IsTypedArray(next) || current.length !== next.length || Object.getPrototypeOf(current).constructor.name !== Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]) } } -function* ValueType(path: string, current: ValueGuard.ValueType, next: unknown): IterableIterator { +function* ValueType(path: string, current: ValueType, next: unknown): IterableIterator { if (current === next) return yield CreateUpdate(path, next) } function* Visit(path: string, current: unknown, next: unknown): IterableIterator { - if (ValueGuard.IsPlainObject(current)) return yield* ObjectType(path, current, next) - if (ValueGuard.IsArray(current)) return yield* ArrayType(path, current, next) - if (ValueGuard.IsTypedArray(current)) return yield* TypedArrayType(path, current, next) - if (ValueGuard.IsValueType(current)) return yield* ValueType(path, current, next) + if (IsPlainObject(current)) return yield* ObjectType(path, current, next) + if (IsArray(current)) return yield* ArrayType(path, current, next) + if (IsTypedArray(current)) return yield* TypedArrayType(path, current, next) + if (IsValueType(current)) return yield* ValueType(path, current, next) throw new ValueDeltaUnableToDiffUnknownValue(current) } // --------------------------------------------------------------------- @@ -151,12 +152,12 @@ function IsIdentity(edits: Edit[]) { } export function Patch(current: unknown, edits: Edit[]): T { if (IsRootUpdate(edits)) { - return ValueClone.Clone(edits[0].value) as T + return Clone(edits[0].value) as T } if (IsIdentity(edits)) { - return ValueClone.Clone(current) as T + return Clone(current) as T } - const clone = ValueClone.Clone(current) + const clone = Clone(current) for (const edit of edits) { switch (edit.type) { case 'insert': { diff --git a/src/value/deref.ts b/src/value/deref.ts new file mode 100644 index 000000000..cf9ebd798 --- /dev/null +++ b/src/value/deref.ts @@ -0,0 +1,41 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeBoxError, TSchema, TRef, TThis } from '../typebox' + +export class TypeDereferenceError extends TypeBoxError { + constructor(public readonly schema: TRef | TThis) { + super(`Unable to dereference schema with $id '${schema.$id}'`) + } +} +/** Dereferences a schema from the references array or throws if not found */ +export function Deref(schema: TRef | TThis, references: TSchema[]): TSchema { + const index = references.findIndex((target) => target.$id === schema.$ref) + if (index === -1) throw new TypeDereferenceError(schema) + return references[index] +} diff --git a/src/value/equal.ts b/src/value/equal.ts index 48a5f9e5b..558d96f8b 100644 --- a/src/value/equal.ts +++ b/src/value/equal.ts @@ -26,30 +26,31 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as ValueGuard from './guard' +import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from './guard' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' // -------------------------------------------------------------------------- // Equality Checks // -------------------------------------------------------------------------- -function ObjectType(left: ValueGuard.ObjectType, right: unknown): boolean { - if (!ValueGuard.IsPlainObject(right)) return false +function ObjectType(left: ObjectType, right: unknown): boolean { + if (!IsPlainObject(right)) return false const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] const rightKeys = [...Object.keys(right), ...Object.getOwnPropertySymbols(right)] if (leftKeys.length !== rightKeys.length) return false return leftKeys.every((key) => Equal(left[key], right[key])) } function DateType(left: Date, right: unknown): any { - return ValueGuard.IsDate(right) && left.getTime() === right.getTime() + return IsDate(right) && left.getTime() === right.getTime() } -function ArrayType(left: ValueGuard.ArrayType, right: unknown): any { - if (!ValueGuard.IsArray(right) || left.length !== right.length) return false +function ArrayType(left: ArrayType, right: unknown): any { + if (!IsArray(right) || left.length !== right.length) return false return left.every((value, index) => Equal(value, right[index])) } -function TypedArrayType(left: ValueGuard.TypedArrayType, right: unknown): any { - if (!ValueGuard.IsTypedArray(right) || left.length !== right.length || Object.getPrototypeOf(left).constructor.name !== Object.getPrototypeOf(right).constructor.name) return false +function TypedArrayType(left: TypedArrayType, right: unknown): any { + if (!IsTypedArray(right) || left.length !== right.length || Object.getPrototypeOf(left).constructor.name !== Object.getPrototypeOf(right).constructor.name) return false return left.every((value, index) => Equal(value, right[index])) } -function ValueType(left: ValueGuard.ValueType, right: unknown): any { +function ValueType(left: ValueType, right: unknown): any { return left === right } // -------------------------------------------------------------------------- @@ -57,10 +58,10 @@ function ValueType(left: ValueGuard.ValueType, right: unknown): any { // -------------------------------------------------------------------------- /** Returns true if the left value deep-equals the right */ export function Equal(left: T, right: unknown): right is T { - if (ValueGuard.IsPlainObject(left)) return ObjectType(left, right) - if (ValueGuard.IsDate(left)) return DateType(left, right) - if (ValueGuard.IsTypedArray(left)) return TypedArrayType(left, right) - if (ValueGuard.IsArray(left)) return ArrayType(left, right) - if (ValueGuard.IsValueType(left)) return ValueType(left, right) + if (IsPlainObject(left)) return ObjectType(left, right) + if (IsDate(left)) return DateType(left, right) + if (IsTypedArray(left)) return TypedArrayType(left, right) + if (IsArray(left)) return ArrayType(left, right) + if (IsValueType(left)) return ValueType(left, right) throw new Error('ValueEquals: Unable to compare value') } diff --git a/src/value/guard.ts b/src/value/guard.ts index ce47fb7cd..5d5bb17ec 100644 --- a/src/value/guard.ts +++ b/src/value/guard.ts @@ -73,7 +73,7 @@ export function IsUint8Array(value: unknown): value is Uint8Array { } /** Returns true if this value is a Date */ export function IsDate(value: unknown): value is Date { - return value instanceof Date + return value instanceof Date && Number.isFinite(value.getTime()) } // -------------------------------------------------------------------------- // Standard diff --git a/src/value/hash.ts b/src/value/hash.ts index eeafffe07..fdeec07a5 100644 --- a/src/value/hash.ts +++ b/src/value/hash.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/hash +@sinclair/typebox/value The MIT License (MIT) @@ -26,14 +26,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as ValueGuard from './guard' +import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsPlainObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from './guard' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- export class ValueHashError extends Error { constructor(public readonly value: unknown) { - super(`Hash: Unable to hash value`) + super(`Unable to hash value`) } } // -------------------------------------------------------------------------- @@ -122,17 +122,17 @@ function UndefinedType(value: undefined) { return FNV1A64(ByteMarker.Undefined) } function Visit(value: any) { - if (ValueGuard.IsArray(value)) return ArrayType(value) - if (ValueGuard.IsBoolean(value)) return BooleanType(value) - if (ValueGuard.IsBigInt(value)) return BigIntType(value) - if (ValueGuard.IsDate(value)) return DateType(value) - if (ValueGuard.IsNull(value)) return NullType(value) - if (ValueGuard.IsNumber(value)) return NumberType(value) - if (ValueGuard.IsPlainObject(value)) return ObjectType(value) - if (ValueGuard.IsString(value)) return StringType(value) - if (ValueGuard.IsSymbol(value)) return SymbolType(value) - if (ValueGuard.IsUint8Array(value)) return Uint8ArrayType(value) - if (ValueGuard.IsUndefined(value)) return UndefinedType(value) + if (IsArray(value)) return ArrayType(value) + if (IsBoolean(value)) return BooleanType(value) + if (IsBigInt(value)) return BigIntType(value) + if (IsDate(value)) return DateType(value) + if (IsNull(value)) return NullType(value) + if (IsNumber(value)) return NumberType(value) + if (IsPlainObject(value)) return ObjectType(value) + if (IsString(value)) return StringType(value) + if (IsSymbol(value)) return SymbolType(value) + if (IsUint8Array(value)) return Uint8ArrayType(value) + if (IsUndefined(value)) return UndefinedType(value) throw new ValueHashError(value) } function FNV1A64(byte: number) { diff --git a/src/value/mutate.ts b/src/value/mutate.ts index bd4a952bf..42d563c4a 100644 --- a/src/value/mutate.ts +++ b/src/value/mutate.ts @@ -26,21 +26,21 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { IsPlainObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from './guard' import { ValuePointer } from './pointer' -import * as ValueClone from './clone' -import * as ValueGuard from './guard' +import { Clone } from './clone' // -------------------------------------------------------------------------- // Errors // -------------------------------------------------------------------------- export class ValueMutateTypeMismatchError extends Error { constructor() { - super('ValueMutate: Cannot assign due type mismatch of assignable values') + super('Cannot assign due type mismatch of assignable values') } } export class ValueMutateInvalidRootMutationError extends Error { constructor() { - super('ValueMutate: Only object and array types can be mutated at the root level') + super('Only object and array types can be mutated at the root level') } } // -------------------------------------------------------------------------- @@ -48,8 +48,8 @@ export class ValueMutateInvalidRootMutationError extends Error { // -------------------------------------------------------------------------- export type Mutable = { [key: string]: unknown } | unknown[] function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { - if (!ValueGuard.IsPlainObject(current)) { - ValuePointer.Set(root, path, ValueClone.Clone(next)) + if (!IsPlainObject(current)) { + ValuePointer.Set(root, path, Clone(next)) } else { const currentKeys = Object.keys(current) const nextKeys = Object.keys(next) @@ -69,8 +69,8 @@ function ObjectType(root: Mutable, path: string, current: unknown, next: Record< } } function ArrayType(root: Mutable, path: string, current: unknown, next: unknown[]) { - if (!ValueGuard.IsArray(current)) { - ValuePointer.Set(root, path, ValueClone.Clone(next)) + if (!IsArray(current)) { + ValuePointer.Set(root, path, Clone(next)) } else { for (let index = 0; index < next.length; index++) { Visit(root, `${path}/${index}`, current[index], next[index]) @@ -78,13 +78,13 @@ function ArrayType(root: Mutable, path: string, current: unknown, next: unknown[ current.splice(next.length) } } -function TypedArrayType(root: Mutable, path: string, current: unknown, next: ValueGuard.TypedArrayType) { - if (ValueGuard.IsTypedArray(current) && current.length === next.length) { +function TypedArrayType(root: Mutable, path: string, current: unknown, next: TypedArrayType) { + if (IsTypedArray(current) && current.length === next.length) { for (let i = 0; i < current.length; i++) { current[i] = next[i] } } else { - ValuePointer.Set(root, path, ValueClone.Clone(next)) + ValuePointer.Set(root, path, Clone(next)) } } function ValueType(root: Mutable, path: string, current: unknown, next: unknown) { @@ -92,22 +92,22 @@ function ValueType(root: Mutable, path: string, current: unknown, next: unknown) ValuePointer.Set(root, path, next) } function Visit(root: Mutable, path: string, current: unknown, next: unknown) { - if (ValueGuard.IsArray(next)) return ArrayType(root, path, current, next) - if (ValueGuard.IsTypedArray(next)) return TypedArrayType(root, path, current, next) - if (ValueGuard.IsPlainObject(next)) return ObjectType(root, path, current, next) - if (ValueGuard.IsValueType(next)) return ValueType(root, path, current, next) + if (IsArray(next)) return ArrayType(root, path, current, next) + if (IsTypedArray(next)) return TypedArrayType(root, path, current, next) + if (IsPlainObject(next)) return ObjectType(root, path, current, next) + if (IsValueType(next)) return ValueType(root, path, current, next) } // -------------------------------------------------------------------------- // Mutate // -------------------------------------------------------------------------- function IsNonMutableValue(value: unknown): value is Mutable { - return ValueGuard.IsTypedArray(value) || ValueGuard.IsValueType(value) + return IsTypedArray(value) || IsValueType(value) } function IsMismatchedValue(current: unknown, next: unknown) { // prettier-ignore return ( - (ValueGuard.IsPlainObject(current) && ValueGuard.IsArray(next)) || - (ValueGuard.IsArray(current) && ValueGuard.IsPlainObject(next)) + (IsPlainObject(current) && IsArray(next)) || + (IsArray(current) && IsPlainObject(next)) ) } // -------------------------------------------------------------------------- diff --git a/src/value/pointer.ts b/src/value/pointer.ts index 6b03c2f1e..995ce8129 100644 --- a/src/value/pointer.ts +++ b/src/value/pointer.ts @@ -31,12 +31,12 @@ THE SOFTWARE. // -------------------------------------------------------------------------- export class ValuePointerRootSetError extends Error { constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { - super('ValuePointer: Cannot set root value') + super('Cannot set root value') } } export class ValuePointerRootDeleteError extends Error { constructor(public readonly value: unknown, public readonly path: string) { - super('ValuePointer: Cannot delete root value') + super('Cannot delete root value') } } // -------------------------------------------------------------------------- diff --git a/src/value/transform.ts b/src/value/transform.ts new file mode 100644 index 000000000..e1608edab --- /dev/null +++ b/src/value/transform.ts @@ -0,0 +1,473 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IsString, IsPlainObject, IsArray, IsValueType } from './guard' +import { ValueError } from '../errors/errors' +import { Deref } from './deref' +import * as Types from '../typebox' + +// ------------------------------------------------------------------------- +// CheckFunction +// ------------------------------------------------------------------------- +export type CheckFunction = (schema: Types.TSchema, references: Types.TSchema[], value: unknown) => boolean + +// ------------------------------------------------------------------------- +// Errors +// ------------------------------------------------------------------------- +export class TransformUnknownTypeError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TRef | Types.TThis) { + super(`Unknown type`) + } +} +export class TransformDecodeCheckError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { + super(`Unable to decode due to invalid value`) + } +} +export class TransformEncodeCheckError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { + super(`Unable to encode due to invalid value`) + } +} +export class TransformDecodeError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { + super(`${error instanceof Error ? error.message : 'Unknown error'}`) + } +} +export class TransformEncodeError extends Types.TypeBoxError { + constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { + super(`${error instanceof Error ? error.message : 'Unknown error'}`) + } +} +// ------------------------------------------------------------------------- +// HasTransform +// ------------------------------------------------------------------------- +/** Recursively checks a schema for transform codecs */ +export namespace HasTransform { + function TArray(schema: Types.TArray, references: Types.TSchema[]): boolean { + return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) + } + function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]): boolean { + return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) + } + function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema.returns, references)) + } + function TFunction(schema: Types.TFunction, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema.returns, references)) + } + function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Types.TypeGuard.TTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) + } + function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) + } + function TNot(schema: Types.TNot, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Visit(schema.not, references) + } + function TObject(schema: Types.TObject, references: Types.TSchema[]) { + // prettier-ignore + return (Types.TypeGuard.TTransform(schema) || Object.values(schema.properties).some((schema) => Visit(schema, references)) || Types.TypeGuard.TSchema(schema.additionalProperties) && Visit(schema.additionalProperties, references) + ) + } + function TPromise(schema: Types.TPromise, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || Visit(schema.item, references) + } + function TRecord(schema: Types.TRecord, references: Types.TSchema[]) { + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[pattern] + return Types.TypeGuard.TTransform(schema) || Visit(property, references) || (Types.TypeGuard.TSchema(schema.additionalProperties) && Types.TypeGuard.TTransform(schema.additionalProperties)) + } + function TRef(schema: Types.TRef, references: Types.TSchema[]) { + if (Types.TypeGuard.TTransform(schema)) return true + return Visit(Deref(schema, references), references) + } + function TThis(schema: Types.TThis, references: Types.TSchema[]) { + if (Types.TypeGuard.TTransform(schema)) return true + return Visit(Deref(schema, references), references) + } + function TTuple(schema: Types.TTuple, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || (Types.TypeGuard.TSchema(schema.items) && schema.items.some((schema) => Visit(schema, references))) + } + function TUnion(schema: Types.TUnion, references: Types.TSchema[]) { + return Types.TypeGuard.TTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) + } + function Visit(schema: Types.TSchema, references: Types.TSchema[]): boolean { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + if (schema.$id && visited.has(schema.$id)) return false + if (schema.$id) visited.add(schema.$id) + switch (schema[Types.Kind]) { + // ------------------------------------------------------ + // Structural + // ------------------------------------------------------ + case 'Array': + return TArray(schema_, references_) + case 'AsyncIterator': + return TAsyncIterator(schema_, references_) + case 'Constructor': + return TConstructor(schema_, references_) + case 'Function': + return TFunction(schema_, references_) + case 'Intersect': + return TIntersect(schema_, references_) + case 'Iterator': + return TIterator(schema_, references_) + case 'Not': + return TNot(schema_, references_) + case 'Object': + return TObject(schema_, references_) + case 'Promise': + return TPromise(schema_, references_) + case 'Record': + return TRecord(schema_, references_) + case 'Ref': + return TRef(schema_, references_) + case 'This': + return TThis(schema_, references_) + case 'Tuple': + return TTuple(schema_, references_) + case 'Union': + return TUnion(schema_, references_) + // ------------------------------------------------------ + // Default + // ------------------------------------------------------ + case 'Any': + case 'BigInt': + case 'Boolean': + case 'Date': + case 'Integer': + case 'Literal': + case 'Never': + case 'Null': + case 'Number': + case 'String': + case 'Symbol': + case 'TemplateLiteral': + case 'Undefined': + case 'Uint8Array': + case 'Unknown': + case 'Void': + return Types.TypeGuard.TTransform(schema) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) + return Types.TypeGuard.TTransform(schema) + } + } + const visited = new Set() + /** Returns true if this schema contains a transform codec */ + export function Has(schema: Types.TSchema, references: Types.TSchema[]): boolean { + visited.clear() + return Visit(schema, references) + } +} +// ------------------------------------------------------------------------- +// DecodeTransform +// ------------------------------------------------------------------------- +/** Decodes a value using transform decoders if available. Does not ensure correct results. */ +export namespace DecodeTransform { + function Default(schema: Types.TSchema, value: any) { + try { + return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Decode(value) : value + } catch (error) { + throw new TransformDecodeError(schema, value, error) + } + } + function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { + const elements1 = value.map((value: any) => Visit(schema.items, references, value)) as unknown[] + return Default(schema, elements1) + } + function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { + if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) + const keys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const properties1 = Object.entries(value).reduce((acc, [key, value]) => { + return !keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(Types.IndexedAccessor.Resolve(schema, [key]), value) } + }, {} as Record) + if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) return Default(schema, properties1) + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(schema.unevaluatedProperties as Types.TSchema, value) } + }, {} as Record) + return Default(schema, properties2) + } + function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { + const value1 = Visit(schema.not, references, value) + return Default(schema, value1) + } + function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { + if (!IsPlainObject(value)) return Default(schema, value) + const properties1 = Object.entries(value).reduce((acc, [key, value]) => { + return !(key in schema.properties) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(schema.properties[key], references, value) } + }, {} as Record) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties1) + const additionalProperties = schema.additionalProperties as Types.TSchema + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return key in schema.properties ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } + }, {} as Record) + return Default(schema, properties2) + } + function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { + if (!IsPlainObject(value)) return Default(schema, value) + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[pattern] + const regex = new RegExp(pattern) + const properties1 = Object.entries(value).reduce((acc, [key, value]) => { + return !regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(property, references, value) } + }, {} as Record) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties1) + const additionalProperties = schema.additionalProperties as Types.TSchema + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } + }, {} as Record) + return Default(schema, properties2) + } + function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) + } + function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) + } + function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { + const value1 = IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value[index])) : [] + return Default(schema, value1) + } + function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { + const value1 = Default(schema, value) + for (const subschema of schema.anyOf) { + if (!checkFunction(subschema, references, value1)) continue + return Visit(subschema, references, value1) + } + return value1 + } + function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + // ------------------------------------------------------ + // Structural + // ------------------------------------------------------ + case 'Array': + return TArray(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) + case 'Not': + return TNot(schema_, references_, value) + case 'Object': + return TObject(schema_, references_, value) + case 'Record': + return TRecord(schema_, references_, value) + case 'Ref': + return TRef(schema_, references_, value) + case 'Symbol': + return Default(schema_, value) + case 'This': + return TThis(schema_, references_, value) + case 'Tuple': + return TTuple(schema_, references_, value) + case 'Union': + return TUnion(schema_, references_, value) + // ------------------------------------------------------ + // Default + // ------------------------------------------------------ + case 'Any': + case 'AsyncIterator': + case 'BigInt': + case 'Boolean': + case 'Constructor': + case 'Date': + case 'Function': + case 'Integer': + case 'Iterator': + case 'Literal': + case 'Never': + case 'Null': + case 'Number': + case 'Promise': + case 'String': + case 'TemplateLiteral': + case 'Undefined': + case 'Uint8Array': + case 'Unknown': + case 'Void': + return Default(schema_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) + return Default(schema_, value) + } + } + let checkFunction: CheckFunction = () => false + export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown { + checkFunction = check + return Visit(schema, references, value) + } +} +// ------------------------------------------------------------------------- +// DecodeTransform +// ------------------------------------------------------------------------- +/** Encodes a value using transform encoders if available. Does not ensure correct results. */ +export namespace EncodeTransform { + function Default(schema: Types.TSchema, value: any) { + try { + return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Encode(value) : value + } catch (error) { + throw new TransformEncodeError(schema, value, error) + } + } + function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { + const elements1 = Default(schema, value) + return elements1.map((value: any) => Visit(schema.items, references, value)) as unknown[] + } + function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { + const properties1 = Default(schema, value) + if (!IsPlainObject(value) || IsValueType(value)) return properties1 + const keys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return !keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(Types.IndexedAccessor.Resolve(schema, [key]), value) } + }, {} as Record) + if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) return Default(schema, properties2) + return Object.entries(properties2).reduce((acc, [key, value]) => { + return keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(schema.unevaluatedProperties as Types.TSchema, value) } + }, {} as Record) + } + function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { + const value1 = Default(schema, value) + return Default(schema.not, value1) + } + function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { + const properties1 = Default(schema, value) as Record + if (!IsPlainObject(value)) return properties1 + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return !(key in schema.properties) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(schema.properties[key], references, value) } + }, {} as Record) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return properties2 + const additionalProperties = schema.additionalProperties as Types.TSchema + return Object.entries(properties2).reduce((acc, [key, value]) => { + return key in schema.properties ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } + }, {} as Record) + } + function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { + const properties1 = Default(schema, value) as Record + if (!IsPlainObject(value)) return properties1 + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[pattern] + const regex = new RegExp(pattern) + const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { + return !regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(property, references, value) } + }, {} as Record) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties2) + const additionalProperties = schema.additionalProperties as Types.TSchema + return Object.entries(properties2).reduce((acc, [key, value]) => { + return regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } + }, {} as Record) + } + function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) + } + function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) + } + function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { + const value1 = Default(schema, value) + return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] + } + function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { + for (const subschema of schema.anyOf) { + if (!checkFunction(subschema, references, value)) continue + const value1 = Visit(subschema, references, value) + return Default(schema, value1) + } + return Default(schema, value) + } + function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { + const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Types.Kind]) { + // ------------------------------------------------------ + // Structural + // ------------------------------------------------------ + case 'Array': + return TArray(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) + case 'Not': + return TNot(schema_, references_, value) + case 'Object': + return TObject(schema_, references_, value) + case 'Record': + return TRecord(schema_, references_, value) + case 'Ref': + return TRef(schema_, references_, value) + case 'This': + return TThis(schema_, references_, value) + case 'Tuple': + return TTuple(schema_, references_, value) + case 'Union': + return TUnion(schema_, references_, value) + // ------------------------------------------------------ + // Apply + // ------------------------------------------------------ + case 'Any': + case 'AsyncIterator': + case 'BigInt': + case 'Boolean': + case 'Constructor': + case 'Date': + case 'Function': + case 'Integer': + case 'Iterator': + case 'Literal': + case 'Never': + case 'Null': + case 'Number': + case 'Promise': + case 'String': + case 'Symbol': + case 'TemplateLiteral': + case 'Undefined': + case 'Uint8Array': + case 'Unknown': + case 'Void': + return Default(schema_, value) + default: + if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) + return Default(schema_, value) + } + } + let checkFunction: CheckFunction = () => false + export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown { + checkFunction = check + return Visit(schema, references, value) + } +} diff --git a/src/value/value.ts b/src/value/value.ts index 242edc490..59b8dbe00 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -36,6 +36,7 @@ import * as ValueConvert from './convert' import * as ValueCreate from './create' import * as ValueCheck from './check' import * as ValueDelta from './delta' +import * as ValueTransform from './transform' import * as Types from '../typebox' /** Functions to perform structural operations on JavaScript values */ @@ -76,6 +77,27 @@ export namespace Value { export function Clone(value: T): T { return ValueClone.Clone(value) } + /** Decodes a value or throws if error */ + export function Decode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticDecode + /** Decodes a value or throws if error */ + export function Decode(schema: T, value: unknown): Types.StaticDecode + /** Decodes a value or throws if error */ + export function Decode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + if (!Check(schema, references, value)) throw new ValueTransform.TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) + return ValueTransform.DecodeTransform.Decode(schema, references, value, ValueCheck.Check) + } + /** Encodes a value or throws if error */ + export function Encode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticEncode + /** Encodes a value or throws if error */ + export function Encode(schema: T, value: unknown): Types.StaticEncode + /** Encodes a value or throws if error */ + export function Encode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value, ValueCheck.Check) + if (!Check(schema, references, encoded)) throw new ValueTransform.TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) + return encoded + } /** Returns an iterator for each error in this value. */ export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrors.ValueErrorIterator /** Returns an iterator for each error in this value. */ diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index bfb4f391d..65ca4aa10 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -12,7 +12,7 @@ export namespace Assert { assert.equal(actual.length, expect.length) for (let i = 0; i < actual.length; i++) assert.equal(actual[i], expect[i]) } - return assert.deepEqual(actual, expect) + return assert.deepStrictEqual(actual, expect) } export function NotEqual(actual: unknown, expect: unknown) { return assert.notEqual(actual, expect) @@ -37,7 +37,7 @@ export namespace Assert { } throw Error('Expected throw') } - export function IsInstanceOf(value: any, constructor: any) { + export function IsInstanceOf any>(value: any, constructor: T): asserts value is InstanceType { if (value instanceof constructor) return throw Error(`Value is not instance of ${constructor}`) } diff --git a/test/runtime/compiler/any.ts b/test/runtime/compiler/any.ts index 949147d7f..fb4c7de56 100644 --- a/test/runtime/compiler/any.ts +++ b/test/runtime/compiler/any.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok } from './validate' -describe('type/compiler/Any', () => { +describe('compiler/Any', () => { it('Should validate number', () => { const T = Type.Any() Ok(T, 1) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index 1b004d8d2..ae8fa4a6a 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Array', () => { +describe('compiler/Array', () => { it('Should validate an array of any', () => { const T = Type.Array(Type.Any()) Ok(T, [0, true, 'hello', {}]) diff --git a/test/runtime/compiler/async-iterator.ts b/test/runtime/compiler/async-iterator.ts index f2afa82f6..52dcf5e8d 100644 --- a/test/runtime/compiler/async-iterator.ts +++ b/test/runtime/compiler/async-iterator.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/AsyncIterator', () => { +describe('compiler/AsyncIterator', () => { it('Should validate a async iterator 1', () => { async function* f() {} const T = Type.AsyncIterator(Type.Any()) diff --git a/test/runtime/compiler/bigint.ts b/test/runtime/compiler/bigint.ts index 2a04cbc35..2e9c48a4e 100644 --- a/test/runtime/compiler/bigint.ts +++ b/test/runtime/compiler/bigint.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/BigInt', () => { +describe('compiler/BigInt', () => { it('Should not validate number', () => { const T = Type.BigInt() Fail(T, 3.14) diff --git a/test/runtime/compiler/boolean.ts b/test/runtime/compiler/boolean.ts index bbc54d942..e401585b1 100644 --- a/test/runtime/compiler/boolean.ts +++ b/test/runtime/compiler/boolean.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Boolean', () => { +describe('compiler/Boolean', () => { it('Should validate a boolean', () => { const T = Type.Boolean() Ok(T, true) diff --git a/test/runtime/compiler/composite.ts b/test/runtime/compiler/composite.ts index 48466589d..3186c8302 100644 --- a/test/runtime/compiler/composite.ts +++ b/test/runtime/compiler/composite.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Composite', () => { +describe('compiler/Composite', () => { it('Should compose two objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) diff --git a/test/runtime/compiler/date.ts b/test/runtime/compiler/date.ts index dfda22522..b4425f63c 100644 --- a/test/runtime/compiler/date.ts +++ b/test/runtime/compiler/date.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Date', () => { +describe('compiler/Date', () => { it('Should not validate number', () => { const T = Type.Date() Fail(T, 1) @@ -53,8 +53,8 @@ describe('type/compiler/Date', () => { }) it('Should validate Date maximumTimestamp', () => { const T = Type.Date({ maximumTimestamp: 10 }) - Ok(T, new Date(10)) Fail(T, new Date(11)) + Ok(T, new Date(10)) }) it('Should validate Date exclusiveMinimumTimestamp', () => { const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) @@ -63,7 +63,12 @@ describe('type/compiler/Date', () => { }) it('Should validate Date exclusiveMaximumTimestamp', () => { const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) - Ok(T, new Date(9)) Fail(T, new Date(10)) + Ok(T, new Date(9)) + }) + it('Should validate Date multipleOfTimestamp', () => { + const T = Type.Date({ multipleOfTimestamp: 2 }) + Fail(T, new Date(1)) + Ok(T, new Date(2)) }) }) diff --git a/test/runtime/compiler/enum.ts b/test/runtime/compiler/enum.ts index 268e2c8cb..679e4ac9c 100644 --- a/test/runtime/compiler/enum.ts +++ b/test/runtime/compiler/enum.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Enum', () => { +describe('compiler/Enum', () => { it('Should validate when emum uses default numeric values', () => { enum Kind { Foo, // = 0 diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index e47c1ce9b..0524426fc 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -13,7 +13,6 @@ import './iterator' import './keyof' import './kind' import './literal' -import './modifier' import './never' import './not' import './null' diff --git a/test/runtime/compiler/integer.ts b/test/runtime/compiler/integer.ts index 683842fbc..51720440e 100644 --- a/test/runtime/compiler/integer.ts +++ b/test/runtime/compiler/integer.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Integer', () => { +describe('compiler/Integer', () => { it('Should not validate number', () => { const T = Type.Integer() Fail(T, 3.14) diff --git a/test/runtime/compiler/intersect.ts b/test/runtime/compiler/intersect.ts index d7aae2e77..652e3be7f 100644 --- a/test/runtime/compiler/intersect.ts +++ b/test/runtime/compiler/intersect.ts @@ -1,7 +1,7 @@ -import { Type, Static } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Intersect', () => { +describe('compiler/Intersect', () => { it('Should intersect number and number', () => { const A = Type.Number() const B = Type.Number() diff --git a/test/runtime/compiler/iterator.ts b/test/runtime/compiler/iterator.ts index 630504929..e5c5e0bb1 100644 --- a/test/runtime/compiler/iterator.ts +++ b/test/runtime/compiler/iterator.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Iterator', () => { +describe('compiler/Iterator', () => { it('Should validate a iterator 1', () => { function* f() {} const T = Type.Iterator(Type.Any()) diff --git a/test/runtime/compiler/keyof.ts b/test/runtime/compiler/keyof.ts index 5acec21da..a1d5cc458 100644 --- a/test/runtime/compiler/keyof.ts +++ b/test/runtime/compiler/keyof.ts @@ -1,8 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' -describe('type/compiler/KeyOf', () => { +describe('compiler/KeyOf', () => { it('Should validate with all object keys as a kind of union', () => { const T = Type.KeyOf( Type.Object({ diff --git a/test/runtime/compiler/kind.ts b/test/runtime/compiler/kind.ts index 2b500cf5d..1f0274b00 100644 --- a/test/runtime/compiler/kind.ts +++ b/test/runtime/compiler/kind.ts @@ -3,7 +3,7 @@ import { TypeCompiler } from '@sinclair/typebox/compiler' import { Ok, Fail } from './validate' import { Assert } from '../assert' -describe('type/compiler/Kind', () => { +describe('compiler/Kind', () => { // ------------------------------------------------------------ // Fixtures // ------------------------------------------------------------ diff --git a/test/runtime/compiler/literal.ts b/test/runtime/compiler/literal.ts index ce81439d8..d2d17155a 100644 --- a/test/runtime/compiler/literal.ts +++ b/test/runtime/compiler/literal.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Literal', () => { +describe('compiler/Literal', () => { it('Should validate literal number', () => { const T = Type.Literal(42) Ok(T, 42) diff --git a/test/runtime/compiler/modifier.ts b/test/runtime/compiler/modifier.ts deleted file mode 100644 index 39f16c447..000000000 --- a/test/runtime/compiler/modifier.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Type } from '@sinclair/typebox' - -describe('type/compiler/Modifier', () => {}) diff --git a/test/runtime/compiler/never.ts b/test/runtime/compiler/never.ts index 6cc53bb93..e1a8c3afc 100644 --- a/test/runtime/compiler/never.ts +++ b/test/runtime/compiler/never.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Fail } from './validate' -describe('type/compiler/Never', () => { +describe('compiler/Never', () => { it('Should not validate number', () => { const T = Type.Never() Fail(T, 1) diff --git a/test/runtime/compiler/not.ts b/test/runtime/compiler/not.ts index 144b31de4..3621e91c4 100644 --- a/test/runtime/compiler/not.ts +++ b/test/runtime/compiler/not.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Not', () => { +describe('compiler/Not', () => { it('Should validate not number', () => { const T = Type.Not(Type.Number()) Fail(T, 1) diff --git a/test/runtime/compiler/null.ts b/test/runtime/compiler/null.ts index fcc8257fa..b23335f0b 100644 --- a/test/runtime/compiler/null.ts +++ b/test/runtime/compiler/null.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Null', () => { +describe('compiler/Null', () => { it('Should not validate number', () => { const T = Type.Null() Fail(T, 1) diff --git a/test/runtime/compiler/number.ts b/test/runtime/compiler/number.ts index 59081b8e3..7a97df5f5 100644 --- a/test/runtime/compiler/number.ts +++ b/test/runtime/compiler/number.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Number', () => { +describe('compiler/Number', () => { it('Should validate number', () => { const T = Type.Number() Ok(T, 3.14) diff --git a/test/runtime/compiler/object.ts b/test/runtime/compiler/object.ts index 9e526de49..10c154b4b 100644 --- a/test/runtime/compiler/object.ts +++ b/test/runtime/compiler/object.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Object', () => { +describe('compiler/Object', () => { // ----------------------------------------------------- // TypeCompiler Only // ----------------------------------------------------- diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts index 9c38ff7c3..8ff7b1509 100644 --- a/test/runtime/compiler/omit.ts +++ b/test/runtime/compiler/omit.ts @@ -2,7 +2,7 @@ import { Type, Kind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { deepEqual, strictEqual } from 'assert' -describe('type/compiler/Omit', () => { +describe('compiler/Omit', () => { it('Should omit properties on the source schema', () => { const A = Type.Object( { diff --git a/test/runtime/compiler/optional.ts b/test/runtime/compiler/optional.ts index a78384e67..5c24e175c 100644 --- a/test/runtime/compiler/optional.ts +++ b/test/runtime/compiler/optional.ts @@ -2,7 +2,7 @@ import { strictEqual } from 'assert' import { Type } from '@sinclair/typebox' import { Ok } from './validate' -describe('type/compiler/Optional', () => { +describe('compiler/Optional', () => { it('Should validate object with optional', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 51a1d93fc..870e6979b 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -3,7 +3,7 @@ import { Type, Kind, Optional, Readonly } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' -describe('type/compiler/Partial', () => { +describe('compiler/Partial', () => { it('Should convert a required object into a partial', () => { const A = Type.Object( { diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts index 556aa9859..227a55b27 100644 --- a/test/runtime/compiler/pick.ts +++ b/test/runtime/compiler/pick.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' -describe('type/compiler/Pick', () => { +describe('compiler/Pick', () => { it('Should pick properties from the source schema', () => { const Vector3 = Type.Object( { diff --git a/test/runtime/compiler/readonly-optional.ts b/test/runtime/compiler/readonly-optional.ts index fdaef8b4e..e4aeffc88 100644 --- a/test/runtime/compiler/readonly-optional.ts +++ b/test/runtime/compiler/readonly-optional.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' -describe('type/compiler/ReadonlyOptional', () => { +describe('compiler/ReadonlyOptional', () => { it('Should validate object with optional', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/readonly.ts b/test/runtime/compiler/readonly.ts index 1bc82d758..b852feb44 100644 --- a/test/runtime/compiler/readonly.ts +++ b/test/runtime/compiler/readonly.ts @@ -2,7 +2,7 @@ import { deepStrictEqual, strictEqual } from 'assert' import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Readonly', () => { +describe('compiler/Readonly', () => { it('Should validate object with readonly', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index ab8381fb4..cd9322b08 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Record', () => { +describe('compiler/Record', () => { // ------------------------------------------------------------- // Issues // ------------------------------------------------------------- diff --git a/test/runtime/compiler/recursive.ts b/test/runtime/compiler/recursive.ts index 513864ec6..0b537eca3 100644 --- a/test/runtime/compiler/recursive.ts +++ b/test/runtime/compiler/recursive.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert/index' import { Ok, Fail } from './validate' -describe('type/compiler/Recursive', () => { +describe('compiler/Recursive', () => { it('Should generate default ordinal $id if not specified', () => { const Node = Type.Recursive((Node) => Type.Object({ diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index 88d564e40..771a7b4f6 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { Assert } from '../assert/index' -describe('type/compiler/Ref', () => { +describe('compiler/Ref', () => { it('Should should validate when referencing a type', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/regexp.ts b/test/runtime/compiler/regexp.ts index 9a926893f..8d5d9ea39 100644 --- a/test/runtime/compiler/regexp.ts +++ b/test/runtime/compiler/regexp.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/RegExp', () => { +describe('compiler/RegExp', () => { //----------------------------------------------------- // Regular Expression //----------------------------------------------------- diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index 80360c949..14ca1bf5f 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -2,7 +2,7 @@ import { Type, Readonly, Optional } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' -describe('type/compiler/compiler/Required', () => { +describe('compiler/Required', () => { it('Should convert a partial object into a required object', () => { const A = Type.Object( { diff --git a/test/runtime/compiler/string.ts b/test/runtime/compiler/string.ts index 99beae9a0..2086bc3bd 100644 --- a/test/runtime/compiler/string.ts +++ b/test/runtime/compiler/string.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/String', () => { +describe('compiler/String', () => { it('Should not validate number', () => { const T = Type.String() Fail(T, 1) diff --git a/test/runtime/compiler/symbol.ts b/test/runtime/compiler/symbol.ts index fcca5a548..a35052612 100644 --- a/test/runtime/compiler/symbol.ts +++ b/test/runtime/compiler/symbol.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Symbol', () => { +describe('compiler/Symbol', () => { it('Should not validate a boolean', () => { const T = Type.Symbol() Fail(T, true) diff --git a/test/runtime/compiler/template-literal.ts b/test/runtime/compiler/template-literal.ts index c967cf9ee..7c0589cf0 100644 --- a/test/runtime/compiler/template-literal.ts +++ b/test/runtime/compiler/template-literal.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/TemplateLiteral', () => { +describe('compiler/TemplateLiteral', () => { // -------------------------------------------------------- // Finite // -------------------------------------------------------- diff --git a/test/runtime/compiler/tuple.ts b/test/runtime/compiler/tuple.ts index 9e1b82052..9271681a1 100644 --- a/test/runtime/compiler/tuple.ts +++ b/test/runtime/compiler/tuple.ts @@ -1,8 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { Assert } from '../assert' -describe('type/compiler/Tuple', () => { +describe('compiler/Tuple', () => { it('Should validate tuple of [string, number]', () => { const A = Type.String() const B = Type.Number() diff --git a/test/runtime/compiler/uint8array.ts b/test/runtime/compiler/uint8array.ts index 274975b23..306498726 100644 --- a/test/runtime/compiler/uint8array.ts +++ b/test/runtime/compiler/uint8array.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Uint8Array', () => { +describe('compiler/Uint8Array', () => { it('Should not validate number', () => { const T = Type.Uint8Array() Fail(T, 1) diff --git a/test/runtime/compiler/unicode.ts b/test/runtime/compiler/unicode.ts index 2e65bd6cf..0e3e569d4 100644 --- a/test/runtime/compiler/unicode.ts +++ b/test/runtime/compiler/unicode.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok } from './validate' -describe('type/compiler/Unicode', () => { +describe('compiler/Unicode', () => { // --------------------------------------------------------- // Identifiers // --------------------------------------------------------- diff --git a/test/runtime/compiler/union.ts b/test/runtime/compiler/union.ts index a1e1fe724..7cd710678 100644 --- a/test/runtime/compiler/union.ts +++ b/test/runtime/compiler/union.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Union', () => { +describe('compiler/Union', () => { it('Should validate union of string, number and boolean', () => { const A = Type.String() const B = Type.Number() diff --git a/test/runtime/compiler/unknown.ts b/test/runtime/compiler/unknown.ts index 30376d8e3..124a9f4a6 100644 --- a/test/runtime/compiler/unknown.ts +++ b/test/runtime/compiler/unknown.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' +import { Ok } from './validate' -describe('type/compiler/Unknown', () => { +describe('compiler/Unknown', () => { it('Should validate number', () => { const T = Type.Any() Ok(T, 1) diff --git a/test/runtime/compiler/void.ts b/test/runtime/compiler/void.ts index 4963c0e4e..4a203436c 100644 --- a/test/runtime/compiler/void.ts +++ b/test/runtime/compiler/void.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/compiler/Void', () => { +describe('compiler/Void', () => { it('Should not validate number', () => { const T = Type.Void() Fail(T, 1) diff --git a/test/runtime/errors/index.ts b/test/runtime/errors/index.ts index 41d3ac147..db746b1ff 100644 --- a/test/runtime/errors/index.ts +++ b/test/runtime/errors/index.ts @@ -1 +1,2 @@ -import './iterator' +import './iterator/index' +import './types/index' diff --git a/test/runtime/errors/iterator/index.ts b/test/runtime/errors/iterator/index.ts new file mode 100644 index 000000000..41d3ac147 --- /dev/null +++ b/test/runtime/errors/iterator/index.ts @@ -0,0 +1 @@ +import './iterator' diff --git a/test/runtime/errors/iterator.ts b/test/runtime/errors/iterator/iterator.ts similarity index 96% rename from test/runtime/errors/iterator.ts rename to test/runtime/errors/iterator/iterator.ts index 1f1ef8858..d32cd126d 100644 --- a/test/runtime/errors/iterator.ts +++ b/test/runtime/errors/iterator/iterator.ts @@ -1,6 +1,6 @@ import { Type } from '@sinclair/typebox' import { Errors } from '@sinclair/typebox/errors' -import { Assert } from '../assert' +import { Assert } from '../../assert' describe('errors/ValueErrorIterator', () => { it('Should return undefined for non error', () => { diff --git a/test/runtime/errors/types/array-contains.ts b/test/runtime/errors/types/array-contains.ts new file mode 100644 index 000000000..fe73e01f9 --- /dev/null +++ b/test/runtime/errors/types/array-contains.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayMaxContainsItems', () => { + const T = Type.Array(Type.Any(), { contains: Type.Literal(1) }) + it('Should pass 0', () => { + const R = Resolve(T, [1]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, [2]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains) + }) +}) diff --git a/test/runtime/errors/types/array-max-contains.ts b/test/runtime/errors/types/array-max-contains.ts new file mode 100644 index 000000000..df55b1e10 --- /dev/null +++ b/test/runtime/errors/types/array-max-contains.ts @@ -0,0 +1,36 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayMaxContainsItems', () => { + const T = Type.Array(Type.Any(), { contains: Type.Literal(1), maxContains: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, [1, 1, 1, 1]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 2', () => { + const R = Resolve(T, [1, 1, 1, 1, 1]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayMaxContains) + }) + it('Should pass 3', () => { + const R = Resolve(T, []) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains) + }) + it('Should pass 4', () => { + const R = Resolve(T, [1, 2, 3, 4]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 5', () => { + const R = Resolve(T, [2, 3, 4]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains) + }) +}) diff --git a/test/runtime/errors/types/array-max-items.ts b/test/runtime/errors/types/array-max-items.ts new file mode 100644 index 000000000..b24fdc86f --- /dev/null +++ b/test/runtime/errors/types/array-max-items.ts @@ -0,0 +1,22 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayMaxItems', () => { + const T = Type.Array(Type.Any(), { maxItems: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, [1, 2, 3, 4]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 2', () => { + const R = Resolve(T, [1, 2, 3, 4, 5]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayMaxItems) + }) +}) diff --git a/test/runtime/errors/types/array-min-contains.ts b/test/runtime/errors/types/array-min-contains.ts new file mode 100644 index 000000000..a8240684a --- /dev/null +++ b/test/runtime/errors/types/array-min-contains.ts @@ -0,0 +1,28 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayMinContainsItems', () => { + const T = Type.Array(Type.Any(), { contains: Type.Literal(1), minContains: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, [1, 1, 1, 1]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 2', () => { + const R = Resolve(T, [1]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayMinContains) + }) + it('Should pass 3', () => { + const R = Resolve(T, []) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayContains) + Assert.IsEqual(R[1].type, ValueErrorType.ArrayMinContains) + }) +}) diff --git a/test/runtime/errors/types/array-min-items.ts b/test/runtime/errors/types/array-min-items.ts new file mode 100644 index 000000000..fc1770a08 --- /dev/null +++ b/test/runtime/errors/types/array-min-items.ts @@ -0,0 +1,22 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayMinItems', () => { + const T = Type.Array(Type.Any(), { minItems: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, [1, 2, 3, 4]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 2', () => { + const R = Resolve(T, []) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayMinItems) + }) +}) diff --git a/test/runtime/errors/types/array-unique-items.ts b/test/runtime/errors/types/array-unique-items.ts new file mode 100644 index 000000000..d6f502cd6 --- /dev/null +++ b/test/runtime/errors/types/array-unique-items.ts @@ -0,0 +1,21 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ArrayUniqueItems', () => { + const T = Type.Array(Type.Any(), { uniqueItems: true }) + it('Should pass 0', () => { + const R = Resolve(T, [1, 2, 3, 4]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 2', () => { + const R = Resolve(T, []) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T, [1, 1, 3, 4]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ArrayUniqueItems) + }) +}) diff --git a/test/runtime/errors/types/array.ts b/test/runtime/errors/types/array.ts new file mode 100644 index 000000000..eb6904de2 --- /dev/null +++ b/test/runtime/errors/types/array.ts @@ -0,0 +1,27 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Array', () => { + const T = Type.Array(Type.Any()) + it('Should pass 0', () => { + const R = Resolve(T, [1, 2, 3]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 2', () => { + const R = Resolve(T, {}) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) + it('Should pass 3', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Array) + }) +}) diff --git a/test/runtime/errors/types/async-iterator.ts b/test/runtime/errors/types/async-iterator.ts new file mode 100644 index 000000000..55f6f17d8 --- /dev/null +++ b/test/runtime/errors/types/async-iterator.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/AsyncIterator', () => { + const T = Type.AsyncIterator(Type.Any()) + it('Should pass 0', () => { + const R = Resolve(T, (async function* (): any {})()) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.AsyncIterator) + }) +}) diff --git a/test/runtime/errors/types/bigint-exclusive-maximum.ts b/test/runtime/errors/types/bigint-exclusive-maximum.ts new file mode 100644 index 000000000..9acf831ef --- /dev/null +++ b/test/runtime/errors/types/bigint-exclusive-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigIntExclusiveMaximum', () => { + const T = Type.BigInt({ exclusiveMaximum: 4n }) + it('Should pass 0', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigIntExclusiveMaximum) + }) +}) diff --git a/test/runtime/errors/types/bigint-exclusive-minimum.ts b/test/runtime/errors/types/bigint-exclusive-minimum.ts new file mode 100644 index 000000000..80a19f4d2 --- /dev/null +++ b/test/runtime/errors/types/bigint-exclusive-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigIntExclusiveMinimum', () => { + const T = Type.BigInt({ exclusiveMinimum: 4n }) + it('Should pass 0', () => { + const R = Resolve(T, 5n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigIntExclusiveMinimum) + }) +}) diff --git a/test/runtime/errors/types/bigint-maximum.ts b/test/runtime/errors/types/bigint-maximum.ts new file mode 100644 index 000000000..fe5f2090b --- /dev/null +++ b/test/runtime/errors/types/bigint-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigIntMaximum', () => { + const T = Type.BigInt({ maximum: 4n }) + it('Should pass 0', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 5n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigIntMaximum) + }) +}) diff --git a/test/runtime/errors/types/bigint-minimum.ts b/test/runtime/errors/types/bigint-minimum.ts new file mode 100644 index 000000000..1f2177311 --- /dev/null +++ b/test/runtime/errors/types/bigint-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigIntMinimum', () => { + const T = Type.BigInt({ minimum: 4n }) + it('Should pass 0', () => { + const R = Resolve(T, 4n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 3n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigIntMinimum) + }) +}) diff --git a/test/runtime/errors/types/bigint-multiple-of.ts b/test/runtime/errors/types/bigint-multiple-of.ts new file mode 100644 index 000000000..f7fe277a1 --- /dev/null +++ b/test/runtime/errors/types/bigint-multiple-of.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigIntMultipleOf', () => { + const T = Type.BigInt({ multipleOf: 2n }) + it('Should pass 0', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigIntMultipleOf) + }) +}) diff --git a/test/runtime/errors/types/bigint.ts b/test/runtime/errors/types/bigint.ts new file mode 100644 index 000000000..4f7ed05b3 --- /dev/null +++ b/test/runtime/errors/types/bigint.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/BigInt', () => { + const T = Type.BigInt() + it('Should pass 0', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.BigInt) + }) +}) diff --git a/test/runtime/errors/types/boolean.ts b/test/runtime/errors/types/boolean.ts new file mode 100644 index 000000000..5ed8edce1 --- /dev/null +++ b/test/runtime/errors/types/boolean.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Boolean', () => { + const T = Type.Boolean() + it('Should pass 0', () => { + const R = Resolve(T, true) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Boolean) + }) +}) diff --git a/test/runtime/errors/types/date-exclusive-maximum-timestamp.ts b/test/runtime/errors/types/date-exclusive-maximum-timestamp.ts new file mode 100644 index 000000000..ab1c29197 --- /dev/null +++ b/test/runtime/errors/types/date-exclusive-maximum-timestamp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/DateExclusiveMaximumTimestamp', () => { + const T = Type.Date({ exclusiveMaximumTimestamp: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Date(0)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Date(4)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.DateExclusiveMaximumTimestamp) + }) +}) diff --git a/test/runtime/errors/types/date-exclusive-minimum-timestamp.ts b/test/runtime/errors/types/date-exclusive-minimum-timestamp.ts new file mode 100644 index 000000000..a9f439ef7 --- /dev/null +++ b/test/runtime/errors/types/date-exclusive-minimum-timestamp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/DateExclusiveMinimumTimestamp', () => { + const T = Type.Date({ exclusiveMinimumTimestamp: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Date(5)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Date(4)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.DateExclusiveMinimumTimestamp) + }) +}) diff --git a/test/runtime/errors/types/date-maximum-timestamp.ts b/test/runtime/errors/types/date-maximum-timestamp.ts new file mode 100644 index 000000000..11e8805a3 --- /dev/null +++ b/test/runtime/errors/types/date-maximum-timestamp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/DateMaximumTimestamp', () => { + const T = Type.Date({ maximumTimestamp: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Date(0)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Date(5)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.DateMaximumTimestamp) + }) +}) diff --git a/test/runtime/errors/types/date-minimum-timestamp.ts b/test/runtime/errors/types/date-minimum-timestamp.ts new file mode 100644 index 000000000..311fc474e --- /dev/null +++ b/test/runtime/errors/types/date-minimum-timestamp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/DateMinimumTimestamp', () => { + const T = Type.Date({ minimumTimestamp: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Date(4)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Date(3)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.DateMinimumTimestamp) + }) +}) diff --git a/test/runtime/errors/types/date-multiple-of-timestamp.ts b/test/runtime/errors/types/date-multiple-of-timestamp.ts new file mode 100644 index 000000000..8c3404ac1 --- /dev/null +++ b/test/runtime/errors/types/date-multiple-of-timestamp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/DateMultipleOfTimestamp', () => { + const T = Type.Date({ multipleOfTimestamp: 2 }) + it('Should pass 0', () => { + const R = Resolve(T, new Date(0)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Date(1)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.DateMultipleOfTimestamp) + }) +}) diff --git a/test/runtime/errors/types/date.ts b/test/runtime/errors/types/date.ts new file mode 100644 index 000000000..01a553ec2 --- /dev/null +++ b/test/runtime/errors/types/date.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Date', () => { + const T = Type.Date() + it('Should pass 0', () => { + const R = Resolve(T, new Date()) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Date) + }) +}) diff --git a/test/runtime/errors/types/function.ts b/test/runtime/errors/types/function.ts new file mode 100644 index 000000000..4fbb628a8 --- /dev/null +++ b/test/runtime/errors/types/function.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Function', () => { + const T = Type.Function([], Type.Any()) + it('Should pass 0', () => { + const R = Resolve(T, function (): any {}) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Function) + }) +}) diff --git a/test/runtime/errors/types/index.ts b/test/runtime/errors/types/index.ts new file mode 100644 index 000000000..077ccbe9e --- /dev/null +++ b/test/runtime/errors/types/index.ts @@ -0,0 +1,63 @@ +import './array-contains' +import './array-max-contains' +import './array-min-items' +import './array-min-contains' +import './array-max-items' +import './array-unique-items' +import './array' +import './async-iterator' +import './bigint-exclusive-maximum' +import './bigint-exclusive-minimum' +import './bigint-maximum' +import './bigint-minimum' +import './bigint-multiple-of' +import './bigint' +import './boolean' +import './date-exclusive-maximum-timestamp' +import './date-exclusive-minimum-timestamp' +import './date-maximum-timestamp' +import './date-minimum-timestamp' +import './date-multiple-of-timestamp' +import './date' +import './function' +import './integer-exclusive-maximum' +import './integer-exclusive-minimum' +import './integer-maximum' +import './integer-minimum' +import './integer-multiple-of' +import './integer' +import './intersect-unevaluated-properties' +import './intersect' +import './iterator' +import './kind' +import './literal' +import './never' +import './not' +import './null' +import './number-exclusive-maximum' +import './number-exclusive-minimum' +import './number-maximum' +import './number-minimum' +import './number' +import './number-multiple-of' +import './object-additional-properties' +import './object-max-properties' +import './object-min-properties' +import './object-required-property' +import './object' +import './promise' +import './string-format-unknown' +import './string-format' +import './string-max-length' +import './string-min-length' +import './string-pattern' +import './string' +import './symbol' +import './tuple-length' +import './tuple' +import './uint8array-max-byte-length' +import './uint8array-min-byte-length' +import './uint8array' +import './undefined' +import './union' +import './void' diff --git a/test/runtime/errors/types/integer-exclusive-maximum.ts b/test/runtime/errors/types/integer-exclusive-maximum.ts new file mode 100644 index 000000000..0667c1060 --- /dev/null +++ b/test/runtime/errors/types/integer-exclusive-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntegerExclusiveMaximum', () => { + const T = Type.Integer({ exclusiveMaximum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntegerExclusiveMaximum) + }) +}) diff --git a/test/runtime/errors/types/integer-exclusive-minimum.ts b/test/runtime/errors/types/integer-exclusive-minimum.ts new file mode 100644 index 000000000..e59556a93 --- /dev/null +++ b/test/runtime/errors/types/integer-exclusive-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntegerExclusiveMinimum', () => { + const T = Type.Integer({ exclusiveMinimum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 5) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntegerExclusiveMinimum) + }) +}) diff --git a/test/runtime/errors/types/integer-maximum.ts b/test/runtime/errors/types/integer-maximum.ts new file mode 100644 index 000000000..fbe028570 --- /dev/null +++ b/test/runtime/errors/types/integer-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntegerMaximum', () => { + const T = Type.Integer({ maximum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 5) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntegerMaximum) + }) +}) diff --git a/test/runtime/errors/types/integer-minimum.ts b/test/runtime/errors/types/integer-minimum.ts new file mode 100644 index 000000000..f7590e110 --- /dev/null +++ b/test/runtime/errors/types/integer-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntegerMinimum', () => { + const T = Type.Integer({ minimum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 3) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntegerMinimum) + }) +}) diff --git a/test/runtime/errors/types/integer-multiple-of.ts b/test/runtime/errors/types/integer-multiple-of.ts new file mode 100644 index 000000000..38a0e14ff --- /dev/null +++ b/test/runtime/errors/types/integer-multiple-of.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntegerMultipleOf', () => { + const T = Type.Integer({ multipleOf: 2 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntegerMultipleOf) + }) +}) diff --git a/test/runtime/errors/types/integer.ts b/test/runtime/errors/types/integer.ts new file mode 100644 index 000000000..5f671ed63 --- /dev/null +++ b/test/runtime/errors/types/integer.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Integer', () => { + const T = Type.Integer() + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Integer) + }) +}) diff --git a/test/runtime/errors/types/intersect-unevaluated-properties.ts b/test/runtime/errors/types/intersect-unevaluated-properties.ts new file mode 100644 index 000000000..58624b214 --- /dev/null +++ b/test/runtime/errors/types/intersect-unevaluated-properties.ts @@ -0,0 +1,36 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/IntersectUnevaluatedProperties', () => { + const T1 = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { + unevaluatedProperties: false, + }) + it('Should pass 0', () => { + const R = Resolve(T1, { x: 1, y: 2 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T1, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.IntersectUnevaluatedProperties) + }) + const T2 = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { + unevaluatedProperties: Type.String(), + }) + it('Should pass 3', () => { + const R = Resolve(T2, { x: 1, y: 2, z: '1' }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 4', () => { + const R = Resolve(T2, { x: 1, y: 2, a: 1, b: 2 }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.String) + Assert.IsEqual(R[0].path, '/a') + Assert.IsEqual(R[0].value, 1) + Assert.IsEqual(R[1].type, ValueErrorType.String) + Assert.IsEqual(R[1].path, '/b') + Assert.IsEqual(R[1].value, 2) + }) +}) diff --git a/test/runtime/errors/types/intersect.ts b/test/runtime/errors/types/intersect.ts new file mode 100644 index 000000000..e45b0dd19 --- /dev/null +++ b/test/runtime/errors/types/intersect.ts @@ -0,0 +1,24 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Intersect', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + it('Should pass 0', () => { + const R = Resolve(T, { x: 1, y: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, { x: 1 }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.Intersect) + Assert.IsEqual(R[1].type, ValueErrorType.ObjectRequiredProperty) + }) + it('Should pass 2', () => { + const R = Resolve(T, { y: 1 }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.Intersect) + Assert.IsEqual(R[1].type, ValueErrorType.ObjectRequiredProperty) + }) +}) diff --git a/test/runtime/errors/types/iterator.ts b/test/runtime/errors/types/iterator.ts new file mode 100644 index 000000000..f1ceee268 --- /dev/null +++ b/test/runtime/errors/types/iterator.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Iterator', () => { + const T = Type.Iterator(Type.Any()) + it('Should pass 0', () => { + const R = Resolve(T, (function* (): any {})()) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Iterator) + }) +}) diff --git a/test/runtime/errors/types/kind.ts b/test/runtime/errors/types/kind.ts new file mode 100644 index 000000000..c8865353e --- /dev/null +++ b/test/runtime/errors/types/kind.ts @@ -0,0 +1,25 @@ +import { TypeRegistry, Kind, TSchema } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Kind', () => { + // ---------------------------------------------------- + // Fixture + // ---------------------------------------------------- + beforeEach(() => TypeRegistry.Set('Foo', (schema, value) => value === 'foo')) + afterEach(() => TypeRegistry.Delete('Foo')) + // ---------------------------------------------------- + // Test + // ---------------------------------------------------- + const T = { [Kind]: 'Foo' } as TSchema + it('Should pass 0', () => { + const R = Resolve(T, 'foo') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Kind) + }) +}) diff --git a/test/runtime/errors/types/literal.ts b/test/runtime/errors/types/literal.ts new file mode 100644 index 000000000..2fe351133 --- /dev/null +++ b/test/runtime/errors/types/literal.ts @@ -0,0 +1,46 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Literal', () => { + // ----------------------------------------------------- + // LiteralString + // ----------------------------------------------------- + const T1 = Type.Literal('hello') + it('Should pass 0', () => { + const R = Resolve(T1, 'hello') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T1, 'world') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Literal) + }) + // ----------------------------------------------------- + // LiteralNumber + // ----------------------------------------------------- + const T2 = Type.Literal(0) + it('Should pass 2', () => { + const R = Resolve(T2, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T2, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Literal) + }) + // ----------------------------------------------------- + // LiteralBoolean + // ----------------------------------------------------- + const T3 = Type.Literal(true) + it('Should pass 4', () => { + const R = Resolve(T3, true) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 5', () => { + const R = Resolve(T3, false) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Literal) + }) +}) diff --git a/test/runtime/errors/types/never.ts b/test/runtime/errors/types/never.ts new file mode 100644 index 000000000..e85e808ff --- /dev/null +++ b/test/runtime/errors/types/never.ts @@ -0,0 +1,13 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Never', () => { + const T = Type.Never() + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Never) + }) +}) diff --git a/test/runtime/errors/types/not.ts b/test/runtime/errors/types/not.ts new file mode 100644 index 000000000..01d3605c5 --- /dev/null +++ b/test/runtime/errors/types/not.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Not', () => { + const T = Type.Not(Type.String()) + it('Should pass 0', () => { + const R = Resolve(T, true) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 'hello') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Not) + }) +}) diff --git a/test/runtime/errors/types/null.ts b/test/runtime/errors/types/null.ts new file mode 100644 index 000000000..0b897dc6a --- /dev/null +++ b/test/runtime/errors/types/null.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Null', () => { + const T = Type.Null() + it('Should pass 0', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Null) + }) +}) diff --git a/test/runtime/errors/types/number-exclusive-maximum.ts b/test/runtime/errors/types/number-exclusive-maximum.ts new file mode 100644 index 000000000..b80ce9417 --- /dev/null +++ b/test/runtime/errors/types/number-exclusive-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/NumberExclusiveMaximum', () => { + const T = Type.Number({ exclusiveMaximum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.NumberExclusiveMaximum) + }) +}) diff --git a/test/runtime/errors/types/number-exclusive-minimum.ts b/test/runtime/errors/types/number-exclusive-minimum.ts new file mode 100644 index 000000000..c474a8dd7 --- /dev/null +++ b/test/runtime/errors/types/number-exclusive-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/NumberExclusiveMinimum', () => { + const T = Type.Number({ exclusiveMinimum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 5) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.NumberExclusiveMinimum) + }) +}) diff --git a/test/runtime/errors/types/number-maximum.ts b/test/runtime/errors/types/number-maximum.ts new file mode 100644 index 000000000..fa4a3ec1d --- /dev/null +++ b/test/runtime/errors/types/number-maximum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/NumberMaximum', () => { + const T = Type.Number({ maximum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 5) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.NumberMaximum) + }) +}) diff --git a/test/runtime/errors/types/number-minimum.ts b/test/runtime/errors/types/number-minimum.ts new file mode 100644 index 000000000..8d8f4bfc2 --- /dev/null +++ b/test/runtime/errors/types/number-minimum.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/NumberMinimum', () => { + const T = Type.Number({ minimum: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, 4) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 3) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.NumberMinimum) + }) +}) diff --git a/test/runtime/errors/types/number-multiple-of.ts b/test/runtime/errors/types/number-multiple-of.ts new file mode 100644 index 000000000..921a2367d --- /dev/null +++ b/test/runtime/errors/types/number-multiple-of.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/NumberMultipleOf', () => { + const T = Type.Number({ multipleOf: 2 }) + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.NumberMultipleOf) + }) +}) diff --git a/test/runtime/errors/types/number.ts b/test/runtime/errors/types/number.ts new file mode 100644 index 000000000..0055c4dc1 --- /dev/null +++ b/test/runtime/errors/types/number.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Number', () => { + const T = Type.Number() + it('Should pass 0', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0n) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Number) + }) +}) diff --git a/test/runtime/errors/types/object-additional-properties.ts b/test/runtime/errors/types/object-additional-properties.ts new file mode 100644 index 000000000..9cad1bfc0 --- /dev/null +++ b/test/runtime/errors/types/object-additional-properties.ts @@ -0,0 +1,48 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ObjectAdditionalProperties', () => { + // ---------------------------------------------------------- + // additionalProperties: false + // ---------------------------------------------------------- + const T1 = Type.Object( + { + x: Type.Number(), + }, + { additionalProperties: false }, + ) + it('Should pass 0', () => { + const R = Resolve(T1, { x: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T1, { x: 1, y: 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectAdditionalProperties) + }) + // ---------------------------------------------------------- + // additionalProperties: TSchema + // ---------------------------------------------------------- + const T2 = Type.Object( + { + x: Type.Number(), + }, + { additionalProperties: Type.String() }, + ) + it('Should pass 2', () => { + const R = Resolve(T2, { x: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T2, { x: 1, a: 1, b: 2 }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.String) + Assert.IsEqual(R[0].path, '/a') + Assert.IsEqual(R[0].value, 1) + Assert.IsEqual(R[1].type, ValueErrorType.String) + Assert.IsEqual(R[1].path, '/b') + Assert.IsEqual(R[1].value, 2) + }) +}) diff --git a/test/runtime/errors/types/object-max-properties.ts b/test/runtime/errors/types/object-max-properties.ts new file mode 100644 index 000000000..ee042e3f0 --- /dev/null +++ b/test/runtime/errors/types/object-max-properties.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ObjectMaxProperties', () => { + const T = Type.Object({}, { maxProperties: 2 }) + it('Should pass 0', () => { + const R = Resolve(T, { x: 1, y: 2 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectMaxProperties) + }) +}) diff --git a/test/runtime/errors/types/object-min-properties.ts b/test/runtime/errors/types/object-min-properties.ts new file mode 100644 index 000000000..428a8b7e3 --- /dev/null +++ b/test/runtime/errors/types/object-min-properties.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ObjectMinProperties', () => { + const T = Type.Object({}, { minProperties: 2 }) + it('Should pass 0', () => { + const R = Resolve(T, { x: 1, y: 2 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, { x: 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectMinProperties) + }) +}) diff --git a/test/runtime/errors/types/object-required-property.ts b/test/runtime/errors/types/object-required-property.ts new file mode 100644 index 000000000..4019885a7 --- /dev/null +++ b/test/runtime/errors/types/object-required-property.ts @@ -0,0 +1,22 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ObjectRequiredProperty', () => { + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + it('Should pass 0', () => { + const R = Resolve(T, { x: 1, y: 2 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, { x: 1 }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[0].path, '/y') + Assert.IsEqual(R[0].value, undefined) + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[1].path, '/y') + Assert.IsEqual(R[1].value, undefined) + }) +}) diff --git a/test/runtime/errors/types/object.ts b/test/runtime/errors/types/object.ts new file mode 100644 index 000000000..3e9b86de8 --- /dev/null +++ b/test/runtime/errors/types/object.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Object', () => { + // ----------------------------------------------------------------- + // Object + // ----------------------------------------------------------------- + const T1 = Type.Object({ x: Type.Number(), y: Type.Number() }) + it('Should pass 0', () => { + const R = Resolve(T1, { x: 1, y: 2 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T1, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Object) + }) + // ----------------------------------------------------------------- + // Object: Optional + // ----------------------------------------------------------------- + const T2 = Type.Object({ x: Type.Optional(Type.Number()) }) + it('Should pass 2', () => { + const R = Resolve(T2, {}) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T2, { x: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 4', () => { + const R = Resolve(T2, { x: '' }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Number) + Assert.IsEqual(R[0].path, '/x') + Assert.IsEqual(R[0].value, '') + }) + // ----------------------------------------------------------------- + // Object: Optional Multiple + // ----------------------------------------------------------------- + const T3 = Type.Partial(Type.Object({ x: Type.Number(), y: Type.Number() })) + it('Should pass 5', () => { + const R = Resolve(T3, {}) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 6', () => { + const R = Resolve(T3, { x: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 7', () => { + const R = Resolve(T3, { y: 1 }) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 8', () => { + const R = Resolve(T3, { x: 'a', y: 'b' }) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.Number) + Assert.IsEqual(R[0].path, '/x') + Assert.IsEqual(R[0].value, 'a') + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[1].path, '/y') + Assert.IsEqual(R[1].value, 'b') + }) +}) diff --git a/test/runtime/errors/types/promise.ts b/test/runtime/errors/types/promise.ts new file mode 100644 index 000000000..81dbe426a --- /dev/null +++ b/test/runtime/errors/types/promise.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Promise', () => { + const T = Type.Promise(Type.Any()) + it('Should pass 0', () => { + const R = Resolve(T, Promise.resolve(0)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Promise) + }) +}) diff --git a/test/runtime/errors/types/resolve.ts b/test/runtime/errors/types/resolve.ts new file mode 100644 index 000000000..e636137be --- /dev/null +++ b/test/runtime/errors/types/resolve.ts @@ -0,0 +1,7 @@ +import { Errors } from '@sinclair/typebox/errors' +import { TSchema } from '@sinclair/typebox' + +/** Resolves errors */ +export function Resolve(schema: T, value: unknown) { + return [...Errors(schema, value)] +} diff --git a/test/runtime/errors/types/string-format-unknown.ts b/test/runtime/errors/types/string-format-unknown.ts new file mode 100644 index 000000000..b40e9622d --- /dev/null +++ b/test/runtime/errors/types/string-format-unknown.ts @@ -0,0 +1,13 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/StringFormatUnknown', () => { + const T = Type.String({ format: 'unknown' }) + it('Should pass 1', () => { + const R = Resolve(T, '') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringFormatUnknown) + }) +}) diff --git a/test/runtime/errors/types/string-format.ts b/test/runtime/errors/types/string-format.ts new file mode 100644 index 000000000..d4f03453c --- /dev/null +++ b/test/runtime/errors/types/string-format.ts @@ -0,0 +1,25 @@ +import { Type, FormatRegistry } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/StringFormat', () => { + // ---------------------------------------------- + // Fixture + // ---------------------------------------------- + beforeEach(() => FormatRegistry.Set('foo', (value) => value === 'foo')) + afterEach(() => FormatRegistry.Delete('foo')) + // ---------------------------------------------- + // Tests + // ---------------------------------------------- + const T = Type.String({ format: 'foo' }) + it('Should pass 0', () => { + const R = Resolve(T, 'foo') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 'bar') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringFormat) + }) +}) diff --git a/test/runtime/errors/types/string-max-length.ts b/test/runtime/errors/types/string-max-length.ts new file mode 100644 index 000000000..973ab2665 --- /dev/null +++ b/test/runtime/errors/types/string-max-length.ts @@ -0,0 +1,17 @@ +import { Type, FormatRegistry } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/StringMaxLength', () => { + const T = Type.String({ maxLength: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, '1234') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, '12345') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringMaxLength) + }) +}) diff --git a/test/runtime/errors/types/string-min-length.ts b/test/runtime/errors/types/string-min-length.ts new file mode 100644 index 000000000..aa96f9257 --- /dev/null +++ b/test/runtime/errors/types/string-min-length.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/StringMinLength', () => { + const T = Type.String({ minLength: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, '1234') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, '123') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringMinLength) + }) +}) diff --git a/test/runtime/errors/types/string-pattern.ts b/test/runtime/errors/types/string-pattern.ts new file mode 100644 index 000000000..07c2f0655 --- /dev/null +++ b/test/runtime/errors/types/string-pattern.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/StringPattern', () => { + const T = Type.String({ pattern: '123' }) + it('Should pass 0', () => { + const R = Resolve(T, '123') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, '321') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringPattern) + }) +}) diff --git a/test/runtime/errors/types/string.ts b/test/runtime/errors/types/string.ts new file mode 100644 index 000000000..79003ccfd --- /dev/null +++ b/test/runtime/errors/types/string.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/String', () => { + const T = Type.String() + it('Should pass 0', () => { + const R = Resolve(T, 'hello') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.String) + }) +}) diff --git a/test/runtime/errors/types/symbol.ts b/test/runtime/errors/types/symbol.ts new file mode 100644 index 000000000..ced5c216b --- /dev/null +++ b/test/runtime/errors/types/symbol.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Symbol', () => { + const T = Type.Symbol() + it('Should pass 0', () => { + const R = Resolve(T, Symbol(1)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Symbol) + }) +}) diff --git a/test/runtime/errors/types/tuple-length.ts b/test/runtime/errors/types/tuple-length.ts new file mode 100644 index 000000000..e8265e63d --- /dev/null +++ b/test/runtime/errors/types/tuple-length.ts @@ -0,0 +1,56 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/TupleLength', () => { + // ---------------------------------------------- + // Tuple: Empty + // ---------------------------------------------- + const T1 = Type.Tuple([]) + it('Should pass 0', () => { + const R = Resolve(T1, []) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T1, [1]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.TupleLength) + }) + // ---------------------------------------------- + // Tuple: One + // ---------------------------------------------- + const T2 = Type.Tuple([Type.Number()]) + it('Should pass 2', () => { + const R = Resolve(T2, [1]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T2, []) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.TupleLength) + }) + it('Should pass 4', () => { + const R = Resolve(T2, [1, 1]) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.TupleLength) + }) + // ---------------------------------------------- + // Tuple: Element + // ---------------------------------------------- + const T3 = Type.Tuple([Type.Number(), Type.Number()]) + it('Should pass 5', () => { + const R = Resolve(T3, [1, 1]) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 6', () => { + const R = Resolve(T3, ['a', 'b']) + Assert.IsEqual(R.length, 2) + Assert.IsEqual(R[0].type, ValueErrorType.Number) + Assert.IsEqual(R[0].path, '/0') + Assert.IsEqual(R[0].value, 'a') + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[1].path, '/1') + Assert.IsEqual(R[1].value, 'b') + }) +}) diff --git a/test/runtime/errors/types/tuple.ts b/test/runtime/errors/types/tuple.ts new file mode 100644 index 000000000..acfa15070 --- /dev/null +++ b/test/runtime/errors/types/tuple.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Tuple', () => { + const T = Type.Tuple([]) + it('Should pass 0', () => { + const R = Resolve(T, []) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Tuple) + }) +}) diff --git a/test/runtime/errors/types/uint8array-max-byte-length.ts b/test/runtime/errors/types/uint8array-max-byte-length.ts new file mode 100644 index 000000000..4e530e1aa --- /dev/null +++ b/test/runtime/errors/types/uint8array-max-byte-length.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Uint8ArrayMaxByteLength', () => { + const T = Type.Uint8Array({ maxByteLength: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Uint8Array(4)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Uint8Array(5)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Uint8ArrayMaxByteLength) + }) +}) diff --git a/test/runtime/errors/types/uint8array-min-byte-length.ts b/test/runtime/errors/types/uint8array-min-byte-length.ts new file mode 100644 index 000000000..0ecc7c1b7 --- /dev/null +++ b/test/runtime/errors/types/uint8array-min-byte-length.ts @@ -0,0 +1,17 @@ +import { Type, FormatRegistry } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Uint8ArrayMinByteLength', () => { + const T = Type.Uint8Array({ minByteLength: 4 }) + it('Should pass 0', () => { + const R = Resolve(T, new Uint8Array(4)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, new Uint8Array(3)) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Uint8ArrayMinByteLength) + }) +}) diff --git a/test/runtime/errors/types/uint8array.ts b/test/runtime/errors/types/uint8array.ts new file mode 100644 index 000000000..51b99dbc5 --- /dev/null +++ b/test/runtime/errors/types/uint8array.ts @@ -0,0 +1,17 @@ +import { Type, FormatRegistry } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Uint8Array', () => { + const T = Type.Uint8Array() + it('Should pass 0', () => { + const R = Resolve(T, new Uint8Array(4)) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Uint8Array) + }) +}) diff --git a/test/runtime/errors/types/undefined.ts b/test/runtime/errors/types/undefined.ts new file mode 100644 index 000000000..ac3be5d4f --- /dev/null +++ b/test/runtime/errors/types/undefined.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Undefined', () => { + const T = Type.Undefined() + it('Should pass 0', () => { + const R = Resolve(T, undefined) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 0) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Undefined) + }) +}) diff --git a/test/runtime/errors/types/union.ts b/test/runtime/errors/types/union.ts new file mode 100644 index 000000000..df110ed30 --- /dev/null +++ b/test/runtime/errors/types/union.ts @@ -0,0 +1,21 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/Union', () => { + const T = Type.Union([Type.String(), Type.Number()]) + it('Should pass 0', () => { + const R = Resolve(T, '1') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 1) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 2', () => { + const R = Resolve(T, true) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Union) + }) +}) diff --git a/test/runtime/errors/types/void.ts b/test/runtime/errors/types/void.ts new file mode 100644 index 000000000..cd5132f78 --- /dev/null +++ b/test/runtime/errors/types/void.ts @@ -0,0 +1,32 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' +import { TypeSystemPolicy } from '@sinclair/typebox/system' + +describe('errors/type/Void', () => { + const T = Type.Void() + it('Should pass 0', () => { + const R = Resolve(T, void 0) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, undefined) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 2', () => { + const R = Resolve(T, void 1) + Assert.IsEqual(R.length, 0) + }) + it('Should pass 3', () => { + const R = Resolve(T, null) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.Void) + }) + it('Should pass 4', () => { + TypeSystemPolicy.AllowNullVoid = true + const R = Resolve(T, null) + Assert.IsEqual(R.length, 0) + TypeSystemPolicy.AllowNullVoid = false + }) +}) diff --git a/test/runtime/system/index.ts b/test/runtime/system/index.ts index a3fc40210..8365111b5 100644 --- a/test/runtime/system/index.ts +++ b/test/runtime/system/index.ts @@ -1 +1,2 @@ -import './system' +import './policy/index' +import './type/index' diff --git a/test/runtime/system/policy/allow-array-object.ts b/test/runtime/system/policy/allow-array-object.ts new file mode 100644 index 000000000..7e23d5491 --- /dev/null +++ b/test/runtime/system/policy/allow-array-object.ts @@ -0,0 +1,65 @@ +import { Ok, Fail } from '../../compiler/validate' +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystemPolicy/AllowArrayObject', () => { + beforeEach(() => { + TypeSystemPolicy.AllowArrayObject = true + }) + afterEach(() => { + TypeSystemPolicy.AllowArrayObject = false + }) + // --------------------------------------------------------------- + // Object + // --------------------------------------------------------------- + it('Should validate arrays with empty objects', () => { + const T = Type.Object({}) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with length property', () => { + const T = Type.Object({ length: Type.Number() }) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays with objects with additionalProperties false when array has no elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Ok(T, []) + }) + it('Should not validate arrays with objects with additionalProperties false when array has elements', () => { + const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) + Fail(T, [0, 1, 2]) + }) + it('Should not validate arrays with objects when length property is string', () => { + const T = Type.Object({ length: Type.String() }) + Fail(T, [0, 1, 2]) + }) + // --------------------------------------------------------------- + // Record + // --------------------------------------------------------------- + it('Should validate arrays as Records with String Keys', () => { + const T = Type.Record(Type.String(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays as Records with Number Keys', () => { + const T = Type.Record(Type.Number(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should validate arrays as Records with Integer Keys', () => { + const T = Type.Record(Type.Integer(), Type.Number()) + Ok(T, [0, 1, 2]) + }) + it('Should not validate arrays as Records with Object Values', () => { + const T = Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Ok(T, [ + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + { x: 1, y: 1, z: 1 }, + ]) + }) +}) diff --git a/test/runtime/system/policy/allow-nan.ts b/test/runtime/system/policy/allow-nan.ts new file mode 100644 index 000000000..54ec151cf --- /dev/null +++ b/test/runtime/system/policy/allow-nan.ts @@ -0,0 +1,62 @@ +import { Ok, Fail } from '../../compiler/validate' +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystemPolicy/AllowNaN', () => { + beforeEach(() => { + TypeSystemPolicy.AllowNaN = true + }) + afterEach(() => { + TypeSystemPolicy.AllowNaN = false + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should validate number with NaN', () => { + const T = Type.Number() + Ok(T, NaN) + }) + it('Should validate number with +Infinity', () => { + const T = Type.Number() + Ok(T, Infinity) + }) + it('Should validate number with -Infinity', () => { + const T = Type.Number() + Ok(T, -Infinity) + }) + // --------------------------------------------------------------- + // Integer + // + // Note: The Number.isInteger() test will fail for NaN. Because + // of this we cannot reasonably override NaN handling for integers. + // --------------------------------------------------------------- + it('Should not validate integer with NaN', () => { + const T = Type.Integer() + Fail(T, NaN) + }) + it('Should not validate integer with +Infinity', () => { + const T = Type.Integer() + Fail(T, Infinity) + }) + it('Should not validate integer with -Infinity', () => { + const T = Type.Integer() + Fail(T, -Infinity) + }) + // --------------------------------------------------------------- + // BigInt + // + // Note: We expect failures here as bigint isn't IEEE754 + // --------------------------------------------------------------- + it('Should not validate bigint with NaN', () => { + const T = Type.BigInt() + Fail(T, NaN) + }) + it('Should not validate bigint with +Infinity', () => { + const T = Type.BigInt() + Fail(T, Infinity) + }) + it('Should not validate bigint with -Infinity', () => { + const T = Type.BigInt() + Fail(T, -Infinity) + }) +}) diff --git a/test/runtime/system/policy/allow-null-void.ts b/test/runtime/system/policy/allow-null-void.ts new file mode 100644 index 000000000..5903890fa --- /dev/null +++ b/test/runtime/system/policy/allow-null-void.ts @@ -0,0 +1,31 @@ +import { Ok } from '../../compiler/validate' +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystemPolicy/AllowNullVoid', () => { + beforeEach(() => { + TypeSystemPolicy.AllowNullVoid = true + }) + afterEach(() => { + TypeSystemPolicy.AllowNullVoid = false + }) + // --------------------------------------------------------------- + // Object + // --------------------------------------------------------------- + it('Should validate with null', () => { + const T = Type.Void() + Ok(T, null) + }) + it('Should validate with undefined', () => { + const T = Type.Void() + Ok(T, undefined) + }) + it('Should validate with void 0', () => { + const T = Type.Void() + Ok(T, void 0) + }) + it('Should validate with void 1', () => { + const T = Type.Void() + Ok(T, void 1) + }) +}) diff --git a/test/runtime/system/policy/exact-optional-property-types.ts b/test/runtime/system/policy/exact-optional-property-types.ts new file mode 100644 index 000000000..6c6e63bca --- /dev/null +++ b/test/runtime/system/policy/exact-optional-property-types.ts @@ -0,0 +1,39 @@ +import { Ok, Fail } from '../../compiler/validate' +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' + +describe('system/TypeSystemPolicy/ExactOptionalPropertyTypes', () => { + beforeEach(() => { + TypeSystemPolicy.ExactOptionalPropertyTypes = true + }) + afterEach(() => { + TypeSystemPolicy.ExactOptionalPropertyTypes = false + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should not validate optional number', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + }) + Ok(T, {}) + Ok(T, { x: 1 }) + Fail(T, { x: undefined }) + }) + it('Should not validate undefined', () => { + const T = Type.Object({ + x: Type.Optional(Type.Undefined()), + }) + Ok(T, {}) + Fail(T, { x: 1 }) + Ok(T, { x: undefined }) + }) + it('Should validate optional number | undefined', () => { + const T = Type.Object({ + x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])), + }) + Ok(T, {}) + Ok(T, { x: 1 }) + Ok(T, { x: undefined }) + }) +}) diff --git a/test/runtime/system/policy/index.ts b/test/runtime/system/policy/index.ts new file mode 100644 index 000000000..29bf29666 --- /dev/null +++ b/test/runtime/system/policy/index.ts @@ -0,0 +1,4 @@ +import './allow-array-object' +import './allow-nan' +import './allow-null-void' +import './exact-optional-property-types' diff --git a/test/runtime/system/system.ts b/test/runtime/system/system.ts deleted file mode 100644 index 9528e6dc8..000000000 --- a/test/runtime/system/system.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { Ok, Fail } from '../compiler/validate' -import { Assert } from '../assert/index' -import { TypeSystem } from '@sinclair/typebox/system' -import { Type } from '@sinclair/typebox' - -describe('system/TypeSystem/ExactOptionalPropertyTypes', () => { - before(() => { - TypeSystem.ExactOptionalPropertyTypes = true - }) - after(() => { - TypeSystem.ExactOptionalPropertyTypes = false - }) - // --------------------------------------------------------------- - // Number - // --------------------------------------------------------------- - it('Should not validate optional number', () => { - const T = Type.Object({ - x: Type.Optional(Type.Number()), - }) - Ok(T, {}) - Ok(T, { x: 1 }) - Fail(T, { x: undefined }) - }) - it('Should not validate undefined', () => { - const T = Type.Object({ - x: Type.Optional(Type.Undefined()), - }) - Ok(T, {}) - Fail(T, { x: 1 }) - Ok(T, { x: undefined }) - }) - it('Should validate optional number | undefined', () => { - const T = Type.Object({ - x: Type.Optional(Type.Union([Type.Number(), Type.Undefined()])), - }) - Ok(T, {}) - Ok(T, { x: 1 }) - Ok(T, { x: undefined }) - }) -}) -describe('system/TypeSystem/AllowNaN', () => { - before(() => { - TypeSystem.AllowNaN = true - }) - after(() => { - TypeSystem.AllowNaN = false - }) - // --------------------------------------------------------------- - // Number - // --------------------------------------------------------------- - it('Should validate number with NaN', () => { - const T = Type.Number() - Ok(T, NaN) - }) - it('Should validate number with +Infinity', () => { - const T = Type.Number() - Ok(T, Infinity) - }) - it('Should validate number with -Infinity', () => { - const T = Type.Number() - Ok(T, -Infinity) - }) - // --------------------------------------------------------------- - // Integer - // - // Note: The Number.isInteger() test will fail for NaN. Because - // of this we cannot reasonably override NaN handling for integers. - // --------------------------------------------------------------- - it('Should not validate integer with NaN', () => { - const T = Type.Integer() - Fail(T, NaN) - }) - it('Should not validate integer with +Infinity', () => { - const T = Type.Integer() - Fail(T, Infinity) - }) - it('Should not validate integer with -Infinity', () => { - const T = Type.Integer() - Fail(T, -Infinity) - }) - // --------------------------------------------------------------- - // BigInt - // - // Note: We expect failures here as bigint isn't IEEE754 - // --------------------------------------------------------------- - it('Should not validate bigint with NaN', () => { - const T = Type.BigInt() - Fail(T, NaN) - }) - it('Should not validate bigint with +Infinity', () => { - const T = Type.BigInt() - Fail(T, Infinity) - }) - it('Should not validate bigint with -Infinity', () => { - const T = Type.BigInt() - Fail(T, -Infinity) - }) -}) -describe('system/TypeSystem/AllowArrayObjects', () => { - before(() => { - TypeSystem.AllowArrayObjects = true - }) - after(() => { - TypeSystem.AllowArrayObjects = false - }) - // --------------------------------------------------------------- - // Object - // --------------------------------------------------------------- - it('Should validate arrays with empty objects', () => { - const T = Type.Object({}) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays with objects with length property', () => { - const T = Type.Object({ length: Type.Number() }) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays with objects with additionalProperties false when array has no elements', () => { - const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) - Ok(T, []) - }) - it('Should not validate arrays with objects with additionalProperties false when array has elements', () => { - const T = Type.Object({ length: Type.Number() }, { additionalProperties: false }) - Fail(T, [0, 1, 2]) - }) - it('Should not validate arrays with objects when length property is string', () => { - const T = Type.Object({ length: Type.String() }) - Fail(T, [0, 1, 2]) - }) - // --------------------------------------------------------------- - // Record - // --------------------------------------------------------------- - it('Should validate arrays as Records with String Keys', () => { - const T = Type.Record(Type.String(), Type.Number()) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays as Records with Number Keys', () => { - const T = Type.Record(Type.Number(), Type.Number()) - Ok(T, [0, 1, 2]) - }) - it('Should validate arrays as Records with Integer Keys', () => { - const T = Type.Record(Type.Integer(), Type.Number()) - Ok(T, [0, 1, 2]) - }) - it('Should not validate arrays as Records with Object Values', () => { - const T = Type.Record( - Type.String(), - Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }), - ) - Ok(T, [ - { x: 1, y: 1, z: 1 }, - { x: 1, y: 1, z: 1 }, - { x: 1, y: 1, z: 1 }, - ]) - }) -}) -describe('system/TypeSystem/AllowVoidNull', () => { - before(() => { - TypeSystem.AllowVoidNull = true - }) - after(() => { - TypeSystem.AllowVoidNull = false - }) - // --------------------------------------------------------------- - // Object - // --------------------------------------------------------------- - it('Should validate with null', () => { - const T = Type.Void() - Ok(T, null) - }) -}) -describe('system/TypeSystem/Format', () => { - it('Should create and validate a format', () => { - TypeSystem.Format('CreateFormat0', (value) => value === value.toLowerCase()) - const T = Type.String({ format: 'CreateFormat0' }) - Ok(T, 'action') - Fail(T, 'ACTION') - }) - it('Should throw if registering the same format twice', () => { - TypeSystem.Format('CreateFormat1', (value) => true) - Assert.Throws(() => TypeSystem.Format('CreateFormat1', (value) => true)) - }) -}) -describe('system/TypeSystem/Type', () => { - it('Should create and validate a type', () => { - type BigNumberOptions = { minimum?: bigint; maximum?: bigint } - const BigNumber = TypeSystem.Type('CreateType0', (options, value) => { - if (typeof value !== 'bigint') return false - if (options.maximum !== undefined && value > options.maximum) return false - if (options.minimum !== undefined && value < options.minimum) return false - return true - }) - const T = BigNumber({ minimum: 10n, maximum: 20n }) - Ok(T, 15n) - Fail(T, 5n) - Fail(T, 25n) - }) - it('Should throw if registering the same type twice', () => { - TypeSystem.Type('CreateType1', () => true) - Assert.Throws(() => TypeSystem.Type('CreateType1', () => true)) - }) -}) diff --git a/test/runtime/system/type/format.ts b/test/runtime/system/type/format.ts new file mode 100644 index 000000000..bac7c9160 --- /dev/null +++ b/test/runtime/system/type/format.ts @@ -0,0 +1,19 @@ +import { Ok, Fail } from '../../compiler/validate' +import { Assert } from '../../assert/index' +import { TypeSystem } from '@sinclair/typebox/system' +import { Type, FormatRegistry } from '@sinclair/typebox' + +describe('system/TypeSystem/Format', () => { + it('Should create and validate a format', () => { + const Foo = TypeSystem.Format('Foo', (value) => value === 'foo') + const T = Type.String({ format: Foo }) + Ok(T, 'foo') + Fail(T, 'bar') + FormatRegistry.Delete('Foo') + }) + it('Should throw if registering the same type twice', () => { + TypeSystem.Format('Foo', () => true) + Assert.Throws(() => TypeSystem.Format('Foo', () => true)) + FormatRegistry.Delete('Foo') + }) +}) diff --git a/test/runtime/system/type/index.ts b/test/runtime/system/type/index.ts new file mode 100644 index 000000000..1afccae94 --- /dev/null +++ b/test/runtime/system/type/index.ts @@ -0,0 +1,2 @@ +import './format' +import './type' diff --git a/test/runtime/system/type/type.ts b/test/runtime/system/type/type.ts new file mode 100644 index 000000000..86288f595 --- /dev/null +++ b/test/runtime/system/type/type.ts @@ -0,0 +1,22 @@ +import { Ok, Fail } from '../../compiler/validate' +import { TypeSystem } from '@sinclair/typebox/system' +import { TypeRegistry } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('system/TypeSystem/Type', () => { + it('Should create and validate a type', () => { + const Foo = TypeSystem.Type('Foo', (options, value) => { + Assert.IsEqual(options.option, 'test') + return value === 'foo' + }) + const T = Foo({ option: 'test' }) + Ok(T, 'foo') + Fail(T, 'bar') + TypeRegistry.Delete('Foo') + }) + it('Should throw if registering the same type twice', () => { + TypeSystem.Type('Foo', () => true) + Assert.Throws(() => TypeSystem.Type('Foo', () => true)) + TypeRegistry.Delete('Foo') + }) +}) diff --git a/test/runtime/type/guard/date.ts b/test/runtime/type/guard/date.ts index 2571c324a..a9534a4ab 100644 --- a/test/runtime/type/guard/date.ts +++ b/test/runtime/type/guard/date.ts @@ -13,27 +13,32 @@ describe('type/guard/TDate', () => { }) it('Should not guard for TDate with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Number({ $id: 1 })) + const R = TypeGuard.TDate(Type.Date({ $id: 1 })) Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid minimum', () => { + it('Should not guard for TDate with invalid exclusiveMaximumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Number({ minimum: '1' })) + const R = TypeGuard.TDate(Type.Date({ exclusiveMaximumTimestamp: '1' })) Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid maximum', () => { + it('Should not guard for TDate with invalid exclusiveMinimumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Number({ maximum: '1' })) + const R = TypeGuard.TDate(Type.Date({ exclusiveMinimumTimestamp: '1' })) Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid exclusiveMinimum', () => { + it('Should not guard for TDate with invalid maximumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Number({ exclusiveMinimum: '1' })) + const R = TypeGuard.TDate(Type.Date({ maximumTimestamp: '1' })) Assert.IsFalse(R) }) - it('Should not guard for TDate with invalid exclusiveMaximum', () => { + it('Should not guard for TDate with invalid minimumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Number({ exclusiveMaximum: '1' })) + const R = TypeGuard.TDate(Type.Date({ minimumTimestamp: '1' })) + Assert.IsFalse(R) + }) + it('Should not guard for TDate with invalid multipleOfTimestamp', () => { + // @ts-ignore + const R = TypeGuard.TDate(Type.Date({ multipleOfTimestamp: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index c1cb4c70d..534c10f17 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -28,6 +28,7 @@ import './partial' import './pick' import './promise' import './record' +import './recursive' import './ref' import './required' import './rest' diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/intersect.ts index 5cf94db7a..25c069c7b 100644 --- a/test/runtime/type/guard/intersect.ts +++ b/test/runtime/type/guard/intersect.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/guard/TUnion', () => { +describe('type/guard/TIntersect', () => { it('Should guard for TIntersect', () => { const R = TypeGuard.TIntersect( Type.Intersect([ @@ -29,4 +29,11 @@ describe('type/guard/TUnion', () => { ) Assert.IsFalse(R) }) + it('Should throw for intersected transform types', () => { + const N = Type.Transform(Type.Number()) + .Decode((value) => value) + .Encode((value) => value) + + Assert.Throws(() => Type.Intersect([N, N])) + }) }) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index b7d52a17f..1ceb07b1e 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -1,5 +1,4 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type, Kind } from '@sinclair/typebox' +import { TypeGuard, Type, Kind, Transform } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TOmit', () => { @@ -103,4 +102,18 @@ describe('type/guard/TOmit', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ad)) Assert.IsEqual(T.required, ['ad']) }) + it('Should discard transform', () => { + const S = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.String(), + }), + { + Decode: (value) => value, + Encode: (value) => value, + }, + ) + const T = Type.Omit(S, ['x']) + Assert.IsFalse(Transform in T) + }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index f7f996d59..8cb963bde 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -1,4 +1,4 @@ -import { TypeGuard, TypeRegistry, Type, Kind } from '@sinclair/typebox' +import { TypeGuard, TypeRegistry, Type, Kind, Transform } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TPartial', () => { @@ -40,4 +40,18 @@ describe('type/guard/TPartial', () => { Assert.IsEqual(T.anyOf[0].required, undefined) Assert.IsEqual(T.anyOf[1].required, undefined) }) + it('Should discard transform', () => { + const S = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.String(), + }), + { + Decode: (value) => value, + Encode: (value) => value, + }, + ) + const T = Type.Partial(S) + Assert.IsFalse(Transform in T) + }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 029160552..b25094ad5 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -1,5 +1,4 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type, Kind } from '@sinclair/typebox' +import { TypeGuard, Type, Kind, Transform } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TPick', () => { @@ -105,4 +104,18 @@ describe('type/guard/TPick', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ac)) Assert.IsEqual(T.required, ['ab', 'ac']) }) + it('Should discard transform', () => { + const S = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.String(), + }), + { + Decode: (value) => value, + Encode: (value) => value, + }, + ) + const T = Type.Pick(S, ['x']) + Assert.IsFalse(Transform in T) + }) }) diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 2703dd85d..6855175fa 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -21,15 +21,21 @@ describe('type/guard/TRecord', () => { }) it('Should guard overload 3', () => { // @ts-ignore - Assert.Throws(() => Type.Record(Type.Union([]), Type.String(), { extra: 1 })) + const T = Type.Record(Type.Union([]), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.TNever(T)) }) it('Should guard overload 4', () => { + // @ts-ignore + const T = Type.Record(Type.BigInt(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.TNever(T)) + }) + it('Should guard overload 5', () => { const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TObject(T)) Assert.IsTrue(TypeGuard.TString(T.properties.A)) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 5', () => { + it('Should guard overload 6', () => { const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Record(L, Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TObject(T)) @@ -37,32 +43,32 @@ describe('type/guard/TRecord', () => { Assert.IsTrue(TypeGuard.TString(T.properties.helloB)) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 6', () => { + it('Should guard overload 7', () => { const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TRecord(T)) Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 7', () => { + it('Should guard overload 8', () => { const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TRecord(T)) Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 8', () => { + it('Should guard overload 9', () => { const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TRecord(T)) Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternStringExact])) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 9', () => { + it('Should guard overload 10', () => { const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) const T = Type.Record(L, Type.String(), { extra: 1 }) Assert.IsTrue(TypeGuard.TRecord(T)) Assert.IsTrue(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`])) Assert.IsEqual(T.extra, 1) }) - it('Should guard overload 10', () => { + it('Should guard overload 11', () => { const L = Type.Union([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) const T = Type.Record(L, Type.String()) Assert.IsTrue(TypeGuard.TObject(T)) @@ -110,12 +116,12 @@ describe('type/guard/TRecord', () => { ) Assert.IsFalse(R) }) - it('Transform: Should should transform to TObject for single literal union value', () => { + it('Normalize: Should should normalize to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) - it('Transform: Should should transform to TObject for multi literal union value', () => { + it('Normalize: Should should normalize to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const R = TypeGuard.TObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/recursive.ts b/test/runtime/type/guard/recursive.ts new file mode 100644 index 000000000..8585e17b1 --- /dev/null +++ b/test/runtime/type/guard/recursive.ts @@ -0,0 +1,16 @@ +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TRecursive', () => { + it('Should guard 1', () => { + const T = Type.Recursive((This) => Type.Object({ nodes: This })) + Assert.IsTrue(TypeGuard.TRecursive(T)) + Assert.IsTrue(TypeGuard.TObject(T)) + }) + it('Should guard 2', () => { + const T = Type.Recursive((This) => Type.Tuple([This])) + Assert.IsTrue(TypeGuard.TRecursive(T)) + Assert.IsTrue(TypeGuard.TTuple(T)) + }) +}) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/required.ts index 4090a91fa..67c0b3858 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/required.ts @@ -1,4 +1,4 @@ -import { TypeGuard, TypeRegistry, Type, Kind } from '@sinclair/typebox' +import { TypeGuard, TypeRegistry, Type, Kind, Transform } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TRequired', () => { @@ -37,4 +37,18 @@ describe('type/guard/TRequired', () => { Assert.IsEqual(T.anyOf[0].required, ['x']) Assert.IsEqual(T.anyOf[1].required, ['y']) }) + it('Should discard transform', () => { + const S = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.String(), + }), + { + Decode: (value) => value, + Encode: (value) => value, + }, + ) + const T = Type.Required(S) + Assert.IsFalse(Transform in T) + }) }) diff --git a/test/runtime/type/guard/rest.ts b/test/runtime/type/guard/rest.ts index c911da85c..4c22803d5 100644 --- a/test/runtime/type/guard/rest.ts +++ b/test/runtime/type/guard/rest.ts @@ -1,22 +1,59 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, TypeGuard } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TRest', () => { - it('Should guard Rest 1', () => { - const T = Type.Tuple([Type.String(), Type.Number()]) - const R = Type.Rest(T) - Assert.IsTrue(TypeGuard.TString(R[0])) - Assert.IsTrue(TypeGuard.TNumber(R[1])) + it('Should guard 1', () => { + // union never + const A = Type.String() + const B = Type.Union(Type.Rest(A)) + Assert.IsTrue(TypeGuard.TNever(B)) }) - it('Should guard Rest 2', () => { - const T = Type.Tuple([]) - const R = Type.Rest(T) - Assert.IsEqual(R.length, 0) + it('Should guard 2', () => { + // intersect never + const A = Type.String() + const B = Type.Intersect(Type.Rest(A)) + Assert.IsTrue(TypeGuard.TNever(B)) }) - it('Should guard Rest 3', () => { - const T = Type.String() - const R = Type.Rest(T) - Assert.IsTrue(TypeGuard.TString(R[0])) + it('Should guard 3', () => { + // tuple + const A = Type.Tuple([Type.Number(), Type.String()]) + const B = Type.Union(Type.Rest(A)) + Assert.IsTrue(TypeGuard.TUnion(B)) + Assert.IsEqual(B.anyOf.length, 2) + Assert.IsTrue(TypeGuard.TNumber(B.anyOf[0])) + Assert.IsTrue(TypeGuard.TString(B.anyOf[1])) + }) + it('Should guard 4', () => { + // tuple spread + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Assert.IsTrue(TypeGuard.TTuple(C)) + Assert.IsEqual(C.items!.length, 4) + Assert.IsEqual(C.items![0].const, 1) + Assert.IsEqual(C.items![1].const, 2) + Assert.IsEqual(C.items![2].const, 3) + Assert.IsEqual(C.items![3].const, 4) + }) + it('Should guard 5', () => { + // union to intersect + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Union([A, B]) + const D = Type.Intersect(Type.Rest(C)) + Assert.IsTrue(TypeGuard.TIntersect(D)) + Assert.IsEqual(D.allOf.length, 2) + Assert.IsTrue(TypeGuard.TObject(D.allOf[0])) + Assert.IsTrue(TypeGuard.TObject(D.allOf[1])) + }) + it('Should guard 6', () => { + // intersect to composite + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Intersect([A, B]) + const D = Type.Composite(Type.Rest(C)) + Assert.IsTrue(TypeGuard.TObject(D)) + Assert.IsTrue(TypeGuard.TNumber(D.properties.x)) + Assert.IsTrue(TypeGuard.TString(D.properties.y)) }) }) diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts index 966f0df50..3ff191aee 100644 --- a/test/runtime/type/intrinsic/intrinsic.ts +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -2,7 +2,7 @@ import { TypeGuard, Intrinsic } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/intrinsic/IntrinsicString', () => { +describe('type/intrinsic/Map', () => { // ---------------------------------------------------- // Passthrough // ---------------------------------------------------- @@ -139,19 +139,16 @@ describe('type/intrinsic/IntrinsicString', () => { const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(.*)world$') - console.log(T.pattern) }) it('Should map template literal patterns 2', () => { const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(0|[1-9][0-9]*)world$') - console.log(T.pattern) }) it('Should map template literal patterns 3', () => { const T = Intrinsic.Map(Type.TemplateLiteral('${number}${string}'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)(.*)$') - console.log(T.pattern) }) it('Should map template literal patterns 3', () => { const T = Intrinsic.Map(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') diff --git a/test/runtime/type/normalize/exclude.ts b/test/runtime/type/normalize/exclude.ts index 299309d93..908f16145 100644 --- a/test/runtime/type/normalize/exclude.ts +++ b/test/runtime/type/normalize/exclude.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Exclude', () => { +describe('type/normalize/Exclude', () => { it('Normalize 1', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) const R = TypeGuard.TUnion(T) diff --git a/test/runtime/type/normalize/extract.ts b/test/runtime/type/normalize/extract.ts index 3e9c6f7e0..e1dc98195 100644 --- a/test/runtime/type/normalize/extract.ts +++ b/test/runtime/type/normalize/extract.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Extract', () => { +describe('type/normalize/Extract', () => { it('Normalize 1', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Number()])) const R = TypeGuard.TUnion(T) diff --git a/test/runtime/type/normalize/indexed.ts b/test/runtime/type/normalize/indexed.ts index 8e726db80..c149c66ae 100644 --- a/test/runtime/type/normalize/indexed.ts +++ b/test/runtime/type/normalize/indexed.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Index', () => { +describe('type/normalize/Index', () => { // --------------------------------------------------------- // Array // --------------------------------------------------------- diff --git a/test/runtime/type/normalize/intersect.ts b/test/runtime/type/normalize/intersect.ts index 3d449ad25..34837db79 100644 --- a/test/runtime/type/normalize/intersect.ts +++ b/test/runtime/type/normalize/intersect.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Intersect', () => { +describe('type/normalize/Intersect', () => { it('Normalize 1', () => { const T = Type.Intersect([Type.Number(), Type.String()]) const R = TypeGuard.TIntersect(T) diff --git a/test/runtime/type/normalize/record.ts b/test/runtime/type/normalize/record.ts index f7ad9334b..9f419429d 100644 --- a/test/runtime/type/normalize/record.ts +++ b/test/runtime/type/normalize/record.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Record', () => { +describe('type/normalize/Record', () => { it('Normalize 1', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Record(K, Type.String()) diff --git a/test/runtime/type/normalize/union.ts b/test/runtime/type/normalize/union.ts index 12e056fad..3c5d468a4 100644 --- a/test/runtime/type/normalize/union.ts +++ b/test/runtime/type/normalize/union.ts @@ -2,7 +2,7 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/normal/Union', () => { +describe('type/normalize/Union', () => { it('Normalize 1', () => { const T = Type.Union([Type.Number(), Type.String()]) const R = TypeGuard.TUnion(T) diff --git a/test/runtime/type/registry/format.ts b/test/runtime/type/registry/format.ts index ffa3d4c78..812a3de6f 100644 --- a/test/runtime/type/registry/format.ts +++ b/test/runtime/type/registry/format.ts @@ -1,7 +1,7 @@ import { FormatRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/FormatRegistry', () => { +describe('type/registry/Format', () => { it('Should set format', () => { FormatRegistry.Set('test#format1', () => true) }) diff --git a/test/runtime/type/registry/type.ts b/test/runtime/type/registry/type.ts index 60151ec9a..b36d2a8d4 100644 --- a/test/runtime/type/registry/type.ts +++ b/test/runtime/type/registry/type.ts @@ -1,7 +1,7 @@ import { TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/TypeRegistry', () => { +describe('type/registry/Type', () => { it('Should set type', () => { TypeRegistry.Set('test#type1', () => true) }) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts index 0df00de7d..53028dff9 100644 --- a/test/runtime/type/template/finite.ts +++ b/test/runtime/type/template/finite.ts @@ -1,7 +1,7 @@ import { PatternString, PatternBoolean, PatternNumber, TemplateLiteralParser, TemplateLiteralFinite } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/TemplateLiteralFinite', () => { +describe('type/templateliteral/TemplateLiteralFinite', () => { // --------------------------------------------------------------- // Finite // --------------------------------------------------------------- diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts index eef3b8101..73aa9cfc3 100644 --- a/test/runtime/type/template/generate.ts +++ b/test/runtime/type/template/generate.ts @@ -1,7 +1,7 @@ import { TemplateLiteralParser, TemplateLiteralGenerator } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/TemplateLiteralGenerator', () => { +describe('type/templateliteral/TemplateLiteralGenerator', () => { // --------------------------------------------------------------- // Exact (No Default Unwrap) // --------------------------------------------------------------- diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts index 1ed72176e..5ab50afbf 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template/parser.ts @@ -1,7 +1,7 @@ import { TemplateLiteralParser } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/TemplateLiteralParser', () => { +describe('type/templateliteral/TemplateLiteralParser', () => { // --------------------------------------------------------------- // Throws // --------------------------------------------------------------- diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template/pattern.ts index 68d30330a..6fdf85ca7 100644 --- a/test/runtime/type/template/pattern.ts +++ b/test/runtime/type/template/pattern.ts @@ -1,7 +1,7 @@ import { Type, TTemplateLiteral, PatternNumber, PatternString, PatternBoolean } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/TemplateLiteralPattern', () => { +describe('type/templateliteral/TemplateLiteralPattern', () => { const Equal = (template: TTemplateLiteral, expect: string) => { const pattern = template.pattern.slice(1, template.pattern.length - 1) Assert.IsEqual(pattern, expect) diff --git a/test/runtime/value/cast/regexp.ts b/test/runtime/value/cast/regexp.ts index 4bfc94c62..6b4e05950 100644 --- a/test/runtime/value/cast/regexp.ts +++ b/test/runtime/value/cast/regexp.ts @@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/cast/RegEx', () => { +describe('value/cast/RegExp', () => { const T = Type.RegExp(/foo/, { default: 'foo' }) const E = 'foo' it('Should upcast from string', () => { diff --git a/test/runtime/value/cast/void.ts b/test/runtime/value/cast/void.ts index 41adb69e2..3a0dd6f4f 100644 --- a/test/runtime/value/cast/void.ts +++ b/test/runtime/value/cast/void.ts @@ -4,7 +4,7 @@ import { Assert } from '../../assert/index' describe('value/cast/Void', () => { const T = Type.Void() - const E = null + const E = undefined it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) @@ -46,8 +46,8 @@ describe('value/cast/Void', () => { Assert.IsEqual(result, E) }) it('Should preserve', () => { - const value = null + const value = undefined const result = Value.Cast(T, value) - Assert.IsEqual(result, null) + Assert.IsEqual(result, undefined) }) }) diff --git a/test/runtime/value/check/date.ts b/test/runtime/value/check/date.ts index 800c764b4..fd3cca859 100644 --- a/test/runtime/value/check/date.ts +++ b/test/runtime/value/check/date.ts @@ -7,46 +7,81 @@ describe('value/check/Date', () => { it('Should fail string', () => { const value = 'hello' const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail number', () => { const value = 1 const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail boolean', () => { const value = true const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail null', () => { const value = null const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail undefined', () => { const value = undefined const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail object', () => { const value = { a: 1 } const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should fail array', () => { const value = [1, 2] const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) }) it('Should pass Date', () => { const value = new Date() const result = Value.Check(T, value) - Assert.IsEqual(result, true) + Assert.IsTrue(result) }) it('Should not validate Date if is invalid', () => { const value = new Date('not-a-valid-date') const result = Value.Check(T, value) - Assert.IsEqual(result, false) + Assert.IsFalse(result) + }) + it('Should validate Date minimumTimestamp', () => { + const T = Type.Date({ minimumTimestamp: 10 }) + const R1 = Value.Check(T, new Date(9)) + const R2 = Value.Check(T, new Date(10)) + Assert.IsFalse(R1) + Assert.IsTrue(R2) + }) + it('Should validate Date maximumTimestamp', () => { + const T = Type.Date({ maximumTimestamp: 10 }) + const R1 = Value.Check(T, new Date(11)) + const R2 = Value.Check(T, new Date(10)) + Assert.IsFalse(R1) + Assert.IsTrue(R2) + }) + it('Should validate Date exclusiveMinimumTimestamp', () => { + const T = Type.Date({ exclusiveMinimumTimestamp: 10 }) + const R1 = Value.Check(T, new Date(10)) + const R2 = Value.Check(T, new Date(11)) + Assert.IsFalse(R1) + Assert.IsTrue(R2) + }) + it('Should validate Date exclusiveMaximumTimestamp', () => { + const T = Type.Date({ exclusiveMaximumTimestamp: 10 }) + const R1 = Value.Check(T, new Date(10)) + const R2 = Value.Check(T, new Date(9)) + Assert.IsFalse(R1) + Assert.IsTrue(R2) + }) + it('Should validate Date multipleOfTimestamp', () => { + const T = Type.Date({ multipleOfTimestamp: 2 }) + const R1 = Value.Check(T, new Date(1)) + const R2 = Value.Check(T, new Date(2)) + Assert.IsFalse(R1) + Assert.IsTrue(R2) }) }) diff --git a/test/runtime/value/convert/async-iterator.ts b/test/runtime/value/convert/async-iterator.ts index 2429faf93..bec76a3d7 100644 --- a/test/runtime/value/convert/async-iterator.ts +++ b/test/runtime/value/convert/async-iterator.ts @@ -2,9 +2,6 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -// -------------------------------------------------------- -// non-convertable pass through -// -------------------------------------------------------- describe('value/convert/AsyncIterator', () => { const T = Type.AsyncIterator(Type.Any()) it('Should passthrough 1', () => { diff --git a/test/runtime/value/convert/constructor.ts b/test/runtime/value/convert/constructor.ts index f1769c245..dabd0a2fb 100644 --- a/test/runtime/value/convert/constructor.ts +++ b/test/runtime/value/convert/constructor.ts @@ -2,13 +2,10 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -// -------------------------------------------------------- -// non-convertable pass through -// -------------------------------------------------------- describe('value/convert/Constructor', () => { const T = Type.Constructor([], Type.Any()) it('Should passthrough 1', () => { - const V = function () {} + const V = class {} const R = Value.Convert(T, V) Assert.IsEqual(R, V) }) diff --git a/test/runtime/value/convert/function.ts b/test/runtime/value/convert/function.ts index 004aaa6be..74e72dd7f 100644 --- a/test/runtime/value/convert/function.ts +++ b/test/runtime/value/convert/function.ts @@ -2,9 +2,6 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -// -------------------------------------------------------- -// non-convertable pass through -// -------------------------------------------------------- describe('value/convert/Function', () => { const T = Type.Function([], Type.Any()) it('Should passthrough 1', () => { diff --git a/test/runtime/value/convert/integer.ts b/test/runtime/value/convert/integer.ts index 1cecc93eb..8f17ff4c1 100644 --- a/test/runtime/value/convert/integer.ts +++ b/test/runtime/value/convert/integer.ts @@ -44,7 +44,7 @@ describe('value/convert/Integer', () => { it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Integer(), value) - Assert.IsEqual(result, 0) + Assert.IsEqual(result, -0) }) it('Should convert string #4', () => { const value = '-100' diff --git a/test/runtime/value/convert/iterator.ts b/test/runtime/value/convert/iterator.ts index f9045d664..1dbf33702 100644 --- a/test/runtime/value/convert/iterator.ts +++ b/test/runtime/value/convert/iterator.ts @@ -2,9 +2,6 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -// -------------------------------------------------------- -// non-convertable pass through -// -------------------------------------------------------- describe('value/convert/Iterator', () => { const T = Type.Iterator(Type.Any()) it('Should passthrough 1', () => { diff --git a/test/runtime/value/convert/literal.ts b/test/runtime/value/convert/literal.ts index f161b1720..c85ac2441 100644 --- a/test/runtime/value/convert/literal.ts +++ b/test/runtime/value/convert/literal.ts @@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/convert/Literal:String', () => { +describe('value/convert/LiteralString', () => { it('Should convert from number 1', () => { const T = Type.Literal('1') const R = Value.Convert(T, 1) @@ -19,7 +19,7 @@ describe('value/convert/Literal:String', () => { Assert.IsEqual(R, 'true') }) }) -describe('value/convert/Literal:Number', () => { +describe('value/convert/LiteralNumber', () => { it('Should convert from number 1', () => { const T = Type.Literal(3.14) const R = Value.Convert(T, '3.14') @@ -46,7 +46,7 @@ describe('value/convert/Literal:Number', () => { Assert.IsEqual(R, true) }) }) -describe('value/convert/Literal:Boolean', () => { +describe('value/convert/LiteralBoolean', () => { it('Should convert from number 1', () => { const T = Type.Literal(true) const R = Value.Convert(T, 3.14) diff --git a/test/runtime/value/convert/number.ts b/test/runtime/value/convert/number.ts index 1f1f62c4d..8c151db12 100644 --- a/test/runtime/value/convert/number.ts +++ b/test/runtime/value/convert/number.ts @@ -40,7 +40,7 @@ describe('value/convert/Number', () => { it('Should convert string #3', () => { const value = '-0' const result = Value.Convert(Type.Number(), value) - Assert.IsEqual(result, 0) + Assert.IsEqual(result, -0) }) it('Should convert string #4', () => { const value = '-100' diff --git a/test/runtime/value/convert/promise.ts b/test/runtime/value/convert/promise.ts index 254301489..9d0439277 100644 --- a/test/runtime/value/convert/promise.ts +++ b/test/runtime/value/convert/promise.ts @@ -2,15 +2,11 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -// -------------------------------------------------------- -// non-convertable pass through -// -------------------------------------------------------- describe('value/convert/Promise', () => { const T = Type.Promise(Type.Any()) - it('Should pass-through 1', () => { + it('Should passthrough 1', () => { const V = Promise.resolve(1) const R = Value.Convert(T, V) - console.log(R) Assert.IsEqual(R, V) }) it('Should passthrough 2', () => { diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts index 961684000..90c24f440 100644 --- a/test/runtime/value/convert/record.ts +++ b/test/runtime/value/convert/record.ts @@ -6,6 +6,6 @@ describe('value/convert/Record', () => { it('Should convert record value to numeric', () => { const T = Type.Record(Type.String(), Type.Number()) const V = Value.Convert(T, { x: '42', y: '24', z: 'hello' }) - Assert.IsEqual(V, { x: 42, y: '24', z: 'hello' }) + Assert.IsEqual(V, { x: 42, y: 24, z: 'hello' }) }) }) diff --git a/test/runtime/value/convert/regexp.ts b/test/runtime/value/convert/regexp.ts index 8f4efad92..c2282b47b 100644 --- a/test/runtime/value/convert/regexp.ts +++ b/test/runtime/value/convert/regexp.ts @@ -2,6 +2,6 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/convert/RegEx', () => { +describe('value/convert/RegExp', () => { it('Should convert', () => {}) }) diff --git a/test/runtime/value/convert/symbol.ts b/test/runtime/value/convert/symbol.ts index ce453f6a9..6ef559da2 100644 --- a/test/runtime/value/convert/symbol.ts +++ b/test/runtime/value/convert/symbol.ts @@ -5,11 +5,11 @@ import { Assert } from '../../assert/index' describe('value/convert/Symbol', () => { const T = Type.Symbol() it('Should convert from number 1', () => { - const R = Value.Convert(T, 3.14) - Assert.IsEqual(R, '3.14') + const R = Value.Convert(T, 3.14) as Symbol + Assert.IsEqual(R.description, '3.14') }) it('Should convert from number 2', () => { - const R = Value.Convert(T, 3) - Assert.IsEqual(R, '3') + const R = Value.Convert(T, 3) as Symbol + Assert.IsEqual(R.description, '3') }) }) diff --git a/test/runtime/value/convert/tuple.ts b/test/runtime/value/convert/tuple.ts index 366ef8838..663caf0f8 100644 --- a/test/runtime/value/convert/tuple.ts +++ b/test/runtime/value/convert/tuple.ts @@ -6,7 +6,7 @@ describe('value/convert/Tuple', () => { it('Should convert from Array 1', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) const R = Value.Convert(T, ['1', 'true']) - Assert.IsEqual(R, [1, true]) + Assert.IsEqual(R, [1, 1]) }) it('Should convert from Array 2', () => { const T = Type.Tuple([Type.Number(), Type.Number()]) diff --git a/test/runtime/value/create/array.ts b/test/runtime/value/create/array.ts index 34377a245..ae042f8ce 100644 --- a/test/runtime/value/create/array.ts +++ b/test/runtime/value/create/array.ts @@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('value/create/Any', () => { +describe('value/create/Array', () => { it('Should create value', () => { const T = Type.Array(Type.String()) Assert.IsEqual(Value.Create(T), []) diff --git a/test/runtime/value/create/string.ts b/test/runtime/value/create/string.ts index 83ebe90f4..222f03180 100644 --- a/test/runtime/value/create/string.ts +++ b/test/runtime/value/create/string.ts @@ -5,7 +5,7 @@ import { Assert } from '../../assert/index' describe('value/create/String', () => { it('Should create value', () => { const T = Type.String() - Assert.IsEqual(Value.Create(T), 0) + Assert.IsEqual(Value.Create(T), '') }) it('Should create default', () => { const T = Type.String({ default: 'hello' }) diff --git a/test/runtime/value/create/void.ts b/test/runtime/value/create/void.ts index eb32ae1b9..1bef38828 100644 --- a/test/runtime/value/create/void.ts +++ b/test/runtime/value/create/void.ts @@ -5,7 +5,7 @@ import { Assert } from '../../assert/index' describe('value/create/Void', () => { it('Should create value', () => { const T = Type.Void() - Assert.IsEqual(Value.Create(T), null) + Assert.IsEqual(Value.Create(T), undefined) }) it('Should create value from default value', () => { const T = Type.Void({ default: 'hello' }) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index e318d92b4..de126d75d 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -9,3 +9,4 @@ import './guard' import './hash' import './mutate' import './pointer' +import './transform' diff --git a/test/runtime/value/transform/_nested.ts b/test/runtime/value/transform/_nested.ts new file mode 100644 index 000000000..700de2c38 --- /dev/null +++ b/test/runtime/value/transform/_nested.ts @@ -0,0 +1,52 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Nested', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T11 = Type.Transform(Type.Literal(1)) + .Decode((value) => value) + .Encode((value) => value) + const T12 = Type.Transform(T11) + .Decode((value) => value) + .Encode((value) => value) + const T13 = Type.Transform(T12) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T13, 1) + Assert.IsEqual(R, 1) + }) + it('Should encode identity', () => { + const R = Value.Encode(T13, 1) + Assert.IsEqual(R, 1) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T13, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T21 = Type.Transform(Type.Literal(1)) + .Decode((value) => 2 as const) + .Encode((value) => 1 as const) + const T22 = Type.Transform(T21) + .Decode((value) => 3 as const) + .Encode((value) => 2 as const) + const T23 = Type.Transform(T22) + .Decode((value) => 4 as const) + .Encode((value) => 3 as const) + it('Should decode mapped', () => { + const R = Value.Decode(T23, 1) + Assert.IsEqual(R, 4) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T23, 4) + Assert.IsEqual(R, 1) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T23, null)) + }) +}) diff --git a/test/runtime/value/transform/any.ts b/test/runtime/value/transform/any.ts new file mode 100644 index 000000000..91281c315 --- /dev/null +++ b/test/runtime/value/transform/any.ts @@ -0,0 +1,34 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Any', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Any()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped', () => { + const R = Value.Decode(T0, 123) + Assert.IsEqual(R, 123) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T0, 123) + Assert.IsEqual(R, 123) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Any()) + .Decode((value) => 1) + .Encode((value) => 2) + it('Should decode mapped', () => { + const R = Value.Decode(T1, null) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 2) + }) +}) diff --git a/test/runtime/value/transform/array.ts b/test/runtime/value/transform/array.ts new file mode 100644 index 000000000..96afb073e --- /dev/null +++ b/test/runtime/value/transform/array.ts @@ -0,0 +1,97 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Array', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Array(Type.Number())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped', () => { + const R = Value.Decode(T0, [0, 1, 2]) + Assert.IsEqual(R, [0, 1, 2]) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T0, [0, 1, 2]) + Assert.IsEqual(R, [0, 1, 2]) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Array(Type.Number())) + .Decode((value) => 1) + .Encode((value) => [0, 1, 2]) + it('Should decode mapped', () => { + const R = Value.Decode(T1, []) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, [0, 1, 2]) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // -------------------------------------------------------- + // Elements + // -------------------------------------------------------- + const B2 = Type.Transform(Type.Boolean()) + .Decode((value) => (value ? 'TRUE' : 'FALSE')) + .Encode((value) => (value === 'TRUE' ? true : false)) + const T2 = Type.Array(B2) + it('Should decode elements', () => { + const R = Value.Decode(T2, [true, false]) + Assert.IsEqual(R, ['TRUE', 'FALSE']) + }) + it('Should encode elements', () => { + const R = Value.Encode(T2, ['TRUE', 'FALSE']) + Assert.IsEqual(R, [true, false]) + }) + it('Should throw on elements decode', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) + // -------------------------------------------------------- + // Elements Contains (Not Supported) + // -------------------------------------------------------- + const N3 = Type.Transform(Type.Literal(1)) + .Decode((value) => 'hello') + .Encode((value) => 1 as const) + const T3 = Type.Array(Type.Number(), { + contains: N3, + }) + it('Should decode contains', () => { + const R = Value.Decode(T3, [1, 2, 3]) + Assert.IsEqual(R, [1, 2, 3]) + }) + it('Should throw on contains encode', () => { + Assert.Throws(() => Value.Encode(T3, ['hello', 2, 3])) + }) + it('Should throw on contains decode', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) + // ------------------------------------------------------------ + // Set + // ------------------------------------------------------------ + const T4 = Type.Transform(Type.Array(Type.Number())) + .Decode((value) => new Set(value)) + .Encode((value) => [...value]) + it('should decode set', () => { + const R = Value.Decode(T4, [1, 1, 2, 3]) + Assert.IsInstanceOf(R, Set) + Assert.IsTrue(R.has(1)) + Assert.IsTrue(R.has(2)) + Assert.IsTrue(R.has(3)) + }) + it('should encode set', () => { + const R = Value.Encode(T4, new Set([1, 2, 3])) + Assert.IsEqual(R, [1, 2, 3]) + }) + it('Should throw on set decode', () => { + Assert.Throws(() => Value.Decode(T4, {})) + }) +}) diff --git a/test/runtime/value/transform/async-iterator.ts b/test/runtime/value/transform/async-iterator.ts new file mode 100644 index 000000000..c418d723f --- /dev/null +++ b/test/runtime/value/transform/async-iterator.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/AsyncIterator', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.AsyncIterator(Type.Number())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, (async function* (): any {})()) + Assert.IsTrue(Symbol.asyncIterator in R) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, (async function* (): any {})()) + Assert.IsTrue(Symbol.asyncIterator in R) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.AsyncIterator(Type.Number())) + .Decode((value) => 1) + .Encode((value) => (async function* (): any {})()) + it('Should decode', () => { + const R = Value.Decode(T1, (async function* (): any {})()) + Assert.IsEqual(R, 1) + }) + it('Should encode', () => { + const R = Value.Encode(T1, null) + Assert.IsTrue(Symbol.asyncIterator in R) + }) + it('Should throw on decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/bigint.ts b/test/runtime/value/transform/bigint.ts new file mode 100644 index 000000000..ffc66b058 --- /dev/null +++ b/test/runtime/value/transform/bigint.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/BigInt', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.BigInt()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 5n) + Assert.IsEqual(R, 5n) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 5n) + Assert.IsEqual(R, 5n) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.BigInt()) + .Decode((value) => 1) + .Encode((value) => 2n) + it('Should decode mapped', () => { + const R = Value.Decode(T1, 1n) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 2n) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/boolean.ts b/test/runtime/value/transform/boolean.ts new file mode 100644 index 000000000..1421a1e8a --- /dev/null +++ b/test/runtime/value/transform/boolean.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Boolean', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Boolean()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, true) + Assert.IsEqual(R, true) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, false) + Assert.IsEqual(R, false) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Boolean()) + .Decode((value) => 1) + .Encode((value) => true) + it('Should decode mapped', () => { + const R = Value.Decode(T1, true) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, true) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/constructor.ts b/test/runtime/value/transform/constructor.ts new file mode 100644 index 000000000..0cfe28d43 --- /dev/null +++ b/test/runtime/value/transform/constructor.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Constructor', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Constructor([], Type.Any())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, class {}) + Assert.IsTypeOf(R, 'function') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, class {}) + Assert.IsTypeOf(R, 'function') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Constructor([], Type.Any())) + .Decode((value) => 1) + .Encode((value) => class {}) + it('Should decode mapped', () => { + const R = Value.Decode(T1, class {}) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsTypeOf(R, 'function') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/date.ts b/test/runtime/value/transform/date.ts new file mode 100644 index 000000000..1d83713e0 --- /dev/null +++ b/test/runtime/value/transform/date.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Date', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Date()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, new Date(123)) + Assert.IsEqual(R, new Date(123)) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, new Date(123)) + Assert.IsEqual(R, new Date(123)) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Date()) + .Decode((value) => 1) + .Encode((value) => new Date()) + it('Should decode mapped', () => { + const R = Value.Decode(T1, new Date()) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsInstanceOf(R, Date) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/function.ts b/test/runtime/value/transform/function.ts new file mode 100644 index 000000000..5d94b8d72 --- /dev/null +++ b/test/runtime/value/transform/function.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Function', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Function([], Type.Any())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, class {}) + Assert.IsTypeOf(R, 'function') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, class {}) + Assert.IsTypeOf(R, 'function') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Function([], Type.Any())) + .Decode((value) => 1) + .Encode((value) => function () {}) + it('Should decode mapped', () => { + const R = Value.Decode(T1, function () {}) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsTypeOf(R, 'function') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/index.ts b/test/runtime/value/transform/index.ts new file mode 100644 index 000000000..2dc90ff81 --- /dev/null +++ b/test/runtime/value/transform/index.ts @@ -0,0 +1,31 @@ +import './_nested' +import './any' +import './array' +import './async-iterator' +import './bigint' +import './boolean' +import './constructor' +import './date' +import './function' +import './integer' +import './intersect' +import './iterator' +import './literal' +import './never' +import './not' +import './null' +import './number' +import './object' +import './promise' +import './record' +import './recursive' +import './ref' +import './string' +import './symbol' +import './template-literal' +import './tuple' +import './undefined' +import './union' +import './unknown' +import './unsafe' +import './void' diff --git a/test/runtime/value/transform/integer.ts b/test/runtime/value/transform/integer.ts new file mode 100644 index 000000000..2f91f3f35 --- /dev/null +++ b/test/runtime/value/transform/integer.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Integer', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Integer()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 42) + Assert.IsEqual(R, 42) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 42) + Assert.IsEqual(R, 42) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Integer()) + .Decode((value) => 1) + .Encode((value) => 2) + it('Should decode mapped', () => { + const R = Value.Decode(T1, 1) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 2) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/intersect.ts b/test/runtime/value/transform/intersect.ts new file mode 100644 index 000000000..342f93abe --- /dev/null +++ b/test/runtime/value/transform/intersect.ts @@ -0,0 +1,122 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Intersect', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + // prettier-ignore + const T0 = Type.Transform(Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ])) + .Decode(value => value) + .Encode(value => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + // prettier-ignore + const T1 = Type.Transform(Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ])) + .Decode((value) => 1) + .Encode((value) => ({ x: 1, y: 2 })) + it('Should decode mapped', () => { + const R = Value.Decode(T1, { x: 1, y: 2 }) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // -------------------------------------------------------- + // Mapped Property + // -------------------------------------------------------- + const N2 = Type.Transform(Type.Number()) + .Decode((value) => value.toString()) + .Encode((value) => parseFloat(value)) + // prettier-ignore + const T2 = Type.Transform(Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: N2 }) + ])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode property', () => { + const R = Value.Decode(T2, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: '2' }) + }) + it('Should encode property', () => { + const R = Value.Encode(T2, { x: 1, y: '2' }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on property decode', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) + // -------------------------------------------------------- + // Unevaluated Property + // -------------------------------------------------------- + const N3 = Type.Transform(Type.Number()) + .Decode((value) => value.toString()) + .Encode((value) => parseFloat(value)) + // prettier-ignore + const T3 = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ], { + unevaluatedProperties: N3 + }) + it('Should decode unevaluated property', () => { + const R = Value.Decode(T3, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: '3' }) + }) + it('Should encode unevaluated property', () => { + const R = Value.Encode(T3, { x: 1, y: 2, z: '3' }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should throw on unevaluated property decode', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) + // -------------------------------------------------------- + // Transform Intersection Interior (Not Supported) + // -------------------------------------------------------- + it('Should throw on intersected interior transform types', () => { + const N4 = Type.Transform(Type.Number()) + .Decode((value) => value) + .Encode((value) => value) + Assert.Throws(() => Type.Intersect([N4, N4])) + }) + // -------------------------------------------------------- + // Transform Intersection Exterior + // -------------------------------------------------------- + const T4 = Type.Transform(Type.Intersect([Type.Number(), Type.Number()])) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + it('Should decode exterior value type', () => { + const R = Value.Decode(T4, 1) + Assert.IsEqual(R, 2) + }) + it('Should encode exterior value type', () => { + const R = Value.Encode(T4, 2) + Assert.IsEqual(R, 1) + }) + it('Should throw on exterior value type decode', () => { + Assert.Throws(() => Value.Decode(T4, null)) + }) +}) diff --git a/test/runtime/value/transform/iterator.ts b/test/runtime/value/transform/iterator.ts new file mode 100644 index 000000000..90bc03773 --- /dev/null +++ b/test/runtime/value/transform/iterator.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Iterator', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Iterator(Type.Number())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, (function* (): any {})()) + Assert.IsTrue(Symbol.iterator in R) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, (function* (): any {})()) + Assert.IsTrue(Symbol.iterator in R) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Iterator(Type.Number())) + .Decode((value) => 1) + .Encode((value) => (function* (): any {})()) + it('Should decode mapped', () => { + const R = Value.Decode(T1, (function* (): any {})()) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsTrue(Symbol.iterator in R) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/literal.ts b/test/runtime/value/transform/literal.ts new file mode 100644 index 000000000..9e0f9e41c --- /dev/null +++ b/test/runtime/value/transform/literal.ts @@ -0,0 +1,71 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Literal', () => { + // ----------------------------------------------- + // Identity + // ----------------------------------------------- + const T0 = Type.Transform(Type.Literal(123)) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped', () => { + const R = Value.Decode(T0, 123) + Assert.IsEqual(R, 123) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T0, 123) + Assert.IsEqual(R, 123) + }) + // ----------------------------------------------- + // LiteralString + // ----------------------------------------------- + const T1 = Type.Transform(Type.Literal('hello')) + .Decode((value) => 1) + .Encode((value) => 'hello' as const) + it('Should decode literal string', () => { + const R = Value.Decode(T1, 'hello') + Assert.IsEqual(R, 1) + }) + it('Should encode literal string', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 'hello') + }) + it('Should throw on decode literal string', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // ----------------------------------------------- + // LiteralNumber + // ----------------------------------------------- + const T2 = Type.Transform(Type.Literal(2)) + .Decode((value) => 1) + .Encode((value) => 2 as const) + it('Should decode literal number', () => { + const R = Value.Decode(T2, 2) + Assert.IsEqual(R, 1) + }) + it('Should encode literal number', () => { + const R = Value.Encode(T2, null) + Assert.IsEqual(R, 2) + }) + it('Should throw on decode literal number', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) + // ----------------------------------------------- + // LiteralBoolean + // ----------------------------------------------- + const T3 = Type.Transform(Type.Literal(true)) + .Decode((value) => 1) + .Encode((value) => true as const) + it('Should decode literal boolean', () => { + const R = Value.Decode(T3, true) + Assert.IsEqual(R, 1) + }) + it('Should encode literal boolean', () => { + const R = Value.Encode(T3, null) + Assert.IsEqual(R, true) + }) + it('Should throw on decode literal boolean', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) +}) diff --git a/test/runtime/value/transform/never.ts b/test/runtime/value/transform/never.ts new file mode 100644 index 000000000..3a5414821 --- /dev/null +++ b/test/runtime/value/transform/never.ts @@ -0,0 +1,33 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Never', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Never()) + .Decode((value) => value) + // @ts-ignore + .Encode((value) => value) + it('Should throw on identity encode', () => { + Assert.Throws(() => Value.Encode(T0, undefined)) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, undefined)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Never()) + // @ts-ignore + .Decode((value) => 1) + // @ts-ignore + .Encode((value) => 1) + it('Should throw on mapped encode', () => { + Assert.Throws(() => Value.Encode(T1, undefined)) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, undefined)) + }) +}) diff --git a/test/runtime/value/transform/not.ts b/test/runtime/value/transform/not.ts new file mode 100644 index 000000000..97277223e --- /dev/null +++ b/test/runtime/value/transform/not.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Not', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Not(Type.String())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 1) + Assert.IsEqual(R, 1) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 1) + Assert.IsEqual(R, 1) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, '')) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Not(Type.String())) + .Decode((value) => 1) + .Encode((value) => 2) + it('Should decode mapped', () => { + const R = Value.Decode(T1, null) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 2) + }) + it('Should throw on decode not', () => { + Assert.Throws(() => Value.Decode(T1, 'hello')) + }) +}) diff --git a/test/runtime/value/transform/null.ts b/test/runtime/value/transform/null.ts new file mode 100644 index 000000000..a113d1566 --- /dev/null +++ b/test/runtime/value/transform/null.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Null', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Null()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, null) + Assert.IsEqual(R, null) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, null) + Assert.IsEqual(R, null) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, undefined)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Null()) + .Decode((value) => 1) + .Encode((value) => null) + it('Should decode mapped', () => { + const R = Value.Decode(T1, null) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 123) + Assert.IsEqual(R, null) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, undefined)) + }) +}) diff --git a/test/runtime/value/transform/number.ts b/test/runtime/value/transform/number.ts new file mode 100644 index 000000000..dcd9328c7 --- /dev/null +++ b/test/runtime/value/transform/number.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Number', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Number()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 42) + Assert.IsEqual(R, 42) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 42) + Assert.IsEqual(R, 42) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Number()) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + it('Should decode mapped', () => { + const R = Value.Decode(T1, 1) + Assert.IsEqual(R, 2) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 2) + Assert.IsEqual(R, 1) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, undefined)) + }) +}) diff --git a/test/runtime/value/transform/object.ts b/test/runtime/value/transform/object.ts new file mode 100644 index 000000000..2c3c0b423 --- /dev/null +++ b/test/runtime/value/transform/object.ts @@ -0,0 +1,153 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Object', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, undefined)) + }) + // ---------------------------------------------------------- + // Object + // ---------------------------------------------------------- + const T1 = Type.Transform( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + .Decode((value) => 42) + .Encode((value) => ({ x: 1, y: 2 })) + it('Should decode mapped', () => { + const R = Value.Decode(T1, { x: 1, y: 2 }) + Assert.IsEqual(R, 42) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, undefined)) + }) + // ---------------------------------------------------------- + // Object: Transform Property + // ---------------------------------------------------------- + const N2 = Type.Transform(Type.Integer()) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T2 = Type.Object({ + x: N2, + y: N2, + }) + it('Should decode transform property', () => { + const R = Value.Decode(T2, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: '1', y: '2' }) + }) + it('Should encode transform property', () => { + const R = Value.Encode(T2, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on decode transform property', () => { + Assert.Throws(() => Value.Decode(T2, undefined)) + }) + // ---------------------------------------------------------- + // Object: Transform Property Nested (Twizzle) + // ---------------------------------------------------------- + const N3 = Type.Transform(Type.Integer()) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T3 = Type.Transform( + Type.Object({ + x: N3, + y: N3, + }), + ) + .Decode((value) => ({ x: value.y, y: value.x })) + .Encode((value) => ({ x: value.y, y: value.x })) + it('Should decode transform property nested', () => { + const R = Value.Decode(T3, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: '2', y: '1' }) + }) + it('Should encode transform property nested', () => { + const R = Value.Encode(T3, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 2, y: 1 }) + }) + it('Should throw on decode transform property nested', () => { + Assert.Throws(() => Value.Decode(T3, undefined)) + }) + // ---------------------------------------------------------- + // Object Additional Properties + // ---------------------------------------------------------- + const N4 = Type.Transform(Type.Integer()) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T4 = Type.Transform( + Type.Object( + { + x: Type.Number(), + }, + { + additionalProperties: N4, + }, + ), + ) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode additional property', () => { + const R = Value.Decode(T4, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: '2' }) + }) + it('Should encode additional property', () => { + const R = Value.Encode(T4, { x: 1, y: '5' }) + Assert.IsEqual(R, { x: 1, y: 5 }) + }) + it('Should throw on additional property 1', () => { + Assert.Throws(() => Value.Decode(T4, undefined)) + }) + it('Should throw on additional property 2', () => { + Assert.Throws(() => Value.Decode(T4, { x: 1, y: true })) + }) + // ------------------------------------------------------------ + // Map + // ------------------------------------------------------------ + const T5 = Type.Transform(Type.Object({ x: Type.String(), y: Type.String() })) + .Decode((value) => new Map(Object.entries(value))) + .Encode((value) => Object.fromEntries(value.entries()) as any) + it('should decode map', () => { + const R = Value.Decode(T5, { x: 'hello', y: 'world' }) + Assert.IsInstanceOf(R, Map) + Assert.IsEqual(R.get('x'), 'hello') + Assert.IsEqual(R.get('y'), 'world') + }) + it('should encode map', () => { + const R = Value.Encode( + T5, + new Map([ + ['x', 'hello'], + ['y', 'world'], + ]), + ) + Assert.IsEqual(R, { x: 'hello', y: 'world' }) + }) + it('Should throw on map decode', () => { + Assert.Throws(() => Value.Decode(T5, {})) + }) +}) diff --git a/test/runtime/value/transform/promise.ts b/test/runtime/value/transform/promise.ts new file mode 100644 index 000000000..9a4f33c45 --- /dev/null +++ b/test/runtime/value/transform/promise.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Promise', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Promise(Type.Number())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, Promise.resolve(1)) + Assert.IsTrue(R instanceof Promise) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, Promise.resolve(1)) + Assert.IsTrue(R instanceof Promise) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, undefined)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T = Type.Transform(Type.Promise(Type.Number())) + .Decode((value) => 1) + .Encode((value) => Promise.resolve(1)) + it('Should decode mapped', () => { + const R = Value.Decode(T, Promise.resolve(1)) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T, null) + Assert.IsTrue(R instanceof Promise) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T, null)) + }) +}) diff --git a/test/runtime/value/transform/record.ts b/test/runtime/value/transform/record.ts new file mode 100644 index 000000000..2b2bc3d82 --- /dev/null +++ b/test/runtime/value/transform/record.ts @@ -0,0 +1,123 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Record', () => { + // ------------------------------------------------------------ + // Identity + // ------------------------------------------------------------ + const T0 = Type.Transform(Type.Record(Type.String(), Type.Boolean())) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, { + a: true, + b: false, + }) + Assert.IsEqual(R, { + a: true, + b: false, + }) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, { + a: true, + b: false, + }) + Assert.IsEqual(R, { + a: true, + b: false, + }) + }) + it('Should throw on identity', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // ------------------------------------------------------------ + // Additional Properties True + // ------------------------------------------------------------ + const N1 = Type.Transform(Type.Number()) + .Decode((value) => `number-${value.toString()}`) + .Encode((value) => parseFloat(value.replace(/number-/g, ''))) + const T1 = Type.Record(Type.Number(), N1) + it('Should decode additional properties allowed', () => { + const R = Value.Decode(T1, { + 0: 1, + a: true, + }) + Assert.IsEqual(R, { + 0: 'number-1', + a: true, + }) + }) + it('Should encode additional properties allowed', () => { + const R = Value.Encode(T1, { + 0: 'number-1', + a: true, + }) + Assert.IsEqual(R, { + 0: 1, + a: true, + }) + }) + it('Should throw on additional properties allowed', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // ------------------------------------------------------------ + // Complex Transform + // ------------------------------------------------------------ + const N2 = Type.Transform(Type.Number()) + .Decode((value) => `number-${value.toString()}`) + .Encode((value) => parseFloat(value.replace(/number-/g, ''))) + const B2 = Type.Transform(Type.Boolean()) + .Decode((value) => (value ? 'TRUE' : 'FALSE')) + .Encode((value) => (value === 'TRUE' ? true : false)) + const T3 = Type.Record(Type.Number(), N2, { additionalProperties: B2 }) + it('Should decode complex', () => { + const R = Value.Decode(T3, { + 0: 1, + a: true, + }) + Assert.IsEqual(R, { + 0: 'number-1', + a: 'TRUE', + }) + }) + it('Should encode complex', () => { + const R = Value.Encode(T3, { + 0: 'number-1', + a: 'TRUE', + }) + Assert.IsEqual(R, { + 0: 1, + a: true, + }) + }) + it('Should throw on complex decode', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) + // ------------------------------------------------------------ + // Map + // ------------------------------------------------------------ + const T4 = Type.Transform(Type.Record(Type.String(), Type.String())) + .Decode((value) => new Map(Object.entries(value))) + .Encode((value) => Object.fromEntries(value.entries())) + it('should decode map', () => { + const R = Value.Decode(T4, { x: 'hello', y: 'world' }) + Assert.IsInstanceOf(R, Map) + Assert.IsEqual(R.get('x'), 'hello') + Assert.IsEqual(R.get('y'), 'world') + }) + it('should encode map', () => { + const R = Value.Encode( + T4, + new Map([ + ['x', 'hello'], + ['y', 'world'], + ]), + ) + Assert.IsEqual(R, { x: 'hello', y: 'world' }) + }) + it('Should throw on map decode', () => { + Assert.Throws(() => Value.Decode(T4, null)) + }) +}) diff --git a/test/runtime/value/transform/recursive.ts b/test/runtime/value/transform/recursive.ts new file mode 100644 index 000000000..2b22d42e1 --- /dev/null +++ b/test/runtime/value/transform/recursive.ts @@ -0,0 +1,131 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Recursive', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform( + Type.Recursive((This) => + Type.Object({ + value: Type.Number(), + nodes: Type.Array(This), + }), + ), + ) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + Assert.IsEqual(R, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + Assert.IsEqual(R, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, undefined)) + }) + // ----------------------------------------------- + // Mapped + // ----------------------------------------------- + const T1 = Type.Transform( + Type.Recursive((This) => + Type.Object({ + value: Type.Number(), + nodes: Type.Array(This), + }), + ), + ) + .Decode((value) => 1) + .Encode((value) => ({ value: 1, nodes: [] })) + it('Should decode mapped', () => { + const R = Value.Decode(T1, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, { value: 1, nodes: [] }) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // ----------------------------------------------- + // Recursive Property Remap + // ----------------------------------------------- + const N2 = Type.Transform(Type.Number()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T2 = Type.Recursive((This) => + Type.Object({ + value: N2, + nodes: Type.Array(This), + }), + ) + it('Should decode property', () => { + const R = Value.Decode(T2, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + Assert.IsEqual(R, { + value: new Date(1), + nodes: [ + { value: new Date(2), nodes: [] }, + { value: new Date(3), nodes: [] }, + ], + }) + }) + it('Should encode property', () => { + const R = Value.Encode(T2, { + value: new Date(1), + nodes: [ + { value: new Date(2), nodes: [] }, + { value: new Date(3), nodes: [] }, + ], + }) + Assert.IsEqual(R, { + value: 1, + nodes: [ + { value: 2, nodes: [] }, + { value: 3, nodes: [] }, + ], + }) + }) + it('Should throw on decode property', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) +}) diff --git a/test/runtime/value/transform/ref.ts b/test/runtime/value/transform/ref.ts new file mode 100644 index 000000000..20385f9bb --- /dev/null +++ b/test/runtime/value/transform/ref.ts @@ -0,0 +1,62 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Ref', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const N0 = Type.Number({ $id: 'N0' }) + const T0 = Type.Transform(Type.Ref(N0)) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped', () => { + const R = Value.Decode(T0, [N0], 0) + Assert.IsEqual(R, 0) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T0, [N0], 0) + Assert.IsEqual(R, 0) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const N1 = Type.Number({ $id: 'N1' }) + const T1 = Type.Transform(Type.Ref(N1)) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + it('Should decode mapped', () => { + const R = Value.Decode(T1, [N1], 0) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, [N1], 1) + Assert.IsEqual(R, 0) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // -------------------------------------------------------- + // Mapped Remote + // -------------------------------------------------------- + const N2 = Type.Transform(Type.Number({ $id: 'N2' })) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + const T2 = Type.Transform(Type.Ref(N2)) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + it('Should decode mapped remote', () => { + const R = Value.Decode(T2, [N2], 0) + Assert.IsEqual(R, 2) + }) + it('Should encode mapped remote', () => { + const R = Value.Encode(T2, [N2], 2) + Assert.IsEqual(R, 0) + }) + it('Should throw on mapped remote decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/string.ts b/test/runtime/value/transform/string.ts new file mode 100644 index 000000000..6c15c074f --- /dev/null +++ b/test/runtime/value/transform/string.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/String', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.String()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.String()) + .Decode((value) => value.split('').reverse().join('')) + .Encode((value) => value.split('').reverse().join('')) + it('Should decode mapped', () => { + const R = Value.Decode(T1, 'ABC') + Assert.IsEqual(R, 'CBA') + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 'CBA') + Assert.IsEqual(R, 'ABC') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/symbol.ts b/test/runtime/value/transform/symbol.ts new file mode 100644 index 000000000..5dfca318d --- /dev/null +++ b/test/runtime/value/transform/symbol.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/String', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Symbol()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, Symbol('hello')) + Assert.IsEqual(R.description, 'hello') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, Symbol('hello')) + Assert.IsEqual(R.description, 'hello') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Symbol()) + .Decode((value) => Symbol(value.description?.split('').reverse().join(''))) + .Encode((value) => Symbol(value.description?.split('').reverse().join(''))) + it('Should decode mapped', () => { + const R = Value.Decode(T1, Symbol('ABC')) + Assert.IsEqual(R.description, 'CBA') + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, Symbol('CBA')) + Assert.IsEqual(R.description, 'ABC') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/template-literal.ts b/test/runtime/value/transform/template-literal.ts new file mode 100644 index 000000000..174abaa2b --- /dev/null +++ b/test/runtime/value/transform/template-literal.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/TemplateLiteral', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.TemplateLiteral([Type.Literal('hello')])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.TemplateLiteral([Type.Literal('ABC')])) + .Decode((value) => value.split('').reverse().join('') as 'CBA') + .Encode((value) => value.split('').reverse().join('') as 'ABC') + it('Should decode mapped', () => { + const R = Value.Decode(T1, 'ABC') + Assert.IsEqual(R, 'CBA') + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 'CBA') + Assert.IsEqual(R, 'ABC') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/tuple.ts b/test/runtime/value/transform/tuple.ts new file mode 100644 index 000000000..cf6b9d51f --- /dev/null +++ b/test/runtime/value/transform/tuple.ts @@ -0,0 +1,98 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Tuple', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Tuple([Type.Number(), Type.Number()])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Tuple([Type.Number()])) + .Decode((value) => [value[0] + 1] as [number]) + .Encode((value) => [value[0] - 1] as [number]) + it('Should decode mapped', () => { + const R = Value.Decode(T1, [0]) + Assert.IsEqual(R, [1]) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, [1]) + Assert.IsEqual(R, [0]) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // -------------------------------------------------------- + // Mapped Element + // -------------------------------------------------------- + const N2 = Type.Transform(Type.Number()) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + const T2 = Type.Transform(Type.Tuple([N2])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped element', () => { + const R = Value.Decode(T2, [0]) + Assert.IsEqual(R, [1]) + }) + it('Should encode mapped element', () => { + const R = Value.Encode(T2, [1]) + Assert.IsEqual(R, [0]) + }) + it('Should throw on mapped element decode', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) + // -------------------------------------------------------- + // Mapped Element + // -------------------------------------------------------- + const N3 = Type.Transform(Type.Number()) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + const T3 = Type.Transform(Type.Tuple([N3])) + .Decode((value) => [value[0].toString()]) + .Encode((value) => [parseFloat(value[0])] as [number]) + it('Should decode mapped element', () => { + const R = Value.Decode(T3, [0]) + Assert.IsEqual(R, ['1']) + }) + it('Should encode mapped element', () => { + const R = Value.Encode(T3, ['1']) + Assert.IsEqual(R, [0]) + }) + it('Should throw on mapped element decode', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) + // ------------------------------------------------------------ + // Set + // ------------------------------------------------------------ + const T4 = Type.Transform(Type.Tuple([Type.Number()])) + .Decode((value) => new Set(value)) + .Encode((value) => [...value] as any) + it('should decode set', () => { + const R = Value.Decode(T4, [1]) + Assert.IsInstanceOf(R, Set) + Assert.IsTrue(R.has(1)) + }) + it('should encode set', () => { + const R = Value.Encode(T4, new Set([1])) + Assert.IsEqual(R, [1]) + }) + it('Should throw on set decode', () => { + Assert.Throws(() => Value.Decode(T4, {})) + }) +}) diff --git a/test/runtime/value/transform/undefined.ts b/test/runtime/value/transform/undefined.ts new file mode 100644 index 000000000..7f8582a9e --- /dev/null +++ b/test/runtime/value/transform/undefined.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Undefined', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Undefined()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Undefined()) + .Decode((value) => null) + .Encode((value) => undefined) + it('Should decode mapped', () => { + const R = Value.Decode(T1, undefined) + Assert.IsEqual(R, null) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, undefined) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts new file mode 100644 index 000000000..993c87002 --- /dev/null +++ b/test/runtime/value/transform/union.ts @@ -0,0 +1,170 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Union', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + // prettier-ignore + const T0 = Type.Transform(Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, { y: 2 }) + Assert.IsEqual(R, { y: 2 }) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + // prettier-ignore + const T1 = Type.Transform(Type.Union([ + Type.Object({ type: Type.Literal('hello') }), + Type.Object({ type: Type.Literal('world') }) + ])) + .Decode((value) => 'test' as const) + .Encode((value) => ({ type: 'hello' as const })) + it('Should decode mapped', () => { + const R = Value.Decode(T1, { type: 'hello' }) + Assert.IsEqual(R, 'test') + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 'test') + Assert.IsEqual(R, { type: 'hello' }) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) + // -------------------------------------------------------- + // Mapped ValueType + // -------------------------------------------------------- + const M21 = Type.Transform(Type.Number()) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + const M22 = Type.Transform(M21) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + // prettier-ignore + const T2 = Type.Transform(Type.Union([Type.String(), M22])) + .Decode((value) => value) + .Encode((value) => { + if (value === 'hello') return 'world' + return value + }) + it('Should decode value type 1', () => { + const R = Value.Decode(T2, 0) + Assert.IsEqual(R, 2) + }) + it('Should decode value type 2', () => { + const R = Value.Decode(T2, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should encode value type 1', () => { + const R = Value.Encode(T2, 'hello') + Assert.IsEqual(R, 'world') + }) + it('Should encode value type 2', () => { + const R = Value.Encode(T2, 2) + Assert.IsEqual(R, 0) + }) + it('Should throw on value type decode', () => { + Assert.Throws(() => Value.Decode(T2, null)) + }) + // -------------------------------------------------------- + // Mapped ObjectType + // -------------------------------------------------------- + const N31 = Type.Transform( + Type.Object({ + x: Type.Number(), + }), + ) + .Decode((value) => ({ x: value.x + 1 })) + .Encode((value) => ({ x: value.x - 1 })) + const N32 = Type.Transform( + Type.Object({ + x: Type.String(), + }), + ) + .Decode((value) => ({ x: value.x.split('').reverse().join('') })) + .Encode((value) => ({ x: value.x.split('').reverse().join('') })) + // prettier-ignore + const T3 = Type.Transform(Type.Union([N31, N32])) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode object types 1', () => { + const R = Value.Decode(T3, { x: 0 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should decode object types 2', () => { + const R = Value.Decode(T3, { x: 'abc' }) + Assert.IsEqual(R, { x: 'cba' }) + }) + it('Should encode object types 1', () => { + const R = Value.Encode(T3, { x: 1 }) + Assert.IsEqual(R, { x: 0 }) + }) + it('Should encode object types 2', () => { + const R = Value.Encode(T3, { x: 'cba' }) + Assert.IsEqual(R, { x: 'abc' }) + }) + it('Should throw on object types decode', () => { + Assert.Throws(() => Value.Decode(T3, null)) + }) + // -------------------------------------------------------- + // Mapped Mixed Types + // -------------------------------------------------------- + const N41 = Type.Transform(Type.Number()) + .Decode((value) => value + 1) + .Encode((value) => value - 1) + const N42 = Type.Transform( + Type.Object({ + x: Type.Number(), + }), + ) + .Decode((value) => ({ x: value.x + 1 })) + .Encode((value) => ({ x: value.x - 1 })) + const N43 = Type.Transform(Type.Tuple([Type.Number()])) + .Decode((value) => [value[0] + 1]) + .Encode((value) => [value[0] - 1] as [number]) + // prettier-ignore + const T4 = Type.Transform(Type.Union([N41, N42, N43])) + .Decode((value) => typeof value === 'number' ? value + 1 : value) + .Encode((value) => typeof value === 'number' ? value - 1 : value) + it('Should decode mixed types 1', () => { + const R = Value.Decode(T4, { x: 0 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should decode mixed types 2', () => { + const R = Value.Decode(T4, 0) + Assert.IsEqual(R, 2) + }) + it('Should decode mixed types 3', () => { + const R = Value.Decode(T4, [0]) + Assert.IsEqual(R, [1]) + }) + it('Should encode mixed types 1', () => { + const R = Value.Encode(T4, { x: 1 }) + Assert.IsEqual(R, { x: 0 }) + }) + it('Should encode mixed types 2', () => { + const R = Value.Encode(T4, 2) + Assert.IsEqual(R, 0) + }) + it('Should encode mixed types 3', () => { + const R = Value.Encode(T4, [1]) + Assert.IsEqual(R, [0]) + }) + it('Should throw on mixed types decode', () => { + Assert.Throws(() => Value.Decode(T4, null)) + }) +}) diff --git a/test/runtime/value/transform/unknown.ts b/test/runtime/value/transform/unknown.ts new file mode 100644 index 000000000..2fe673a17 --- /dev/null +++ b/test/runtime/value/transform/unknown.ts @@ -0,0 +1,34 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Unknown', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Unknown()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode mapped', () => { + const R = Value.Decode(T0, 123) + Assert.IsEqual(R, 123) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T0, 123) + Assert.IsEqual(R, 123) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Unknown()) + .Decode((value) => 1) + .Encode((value) => 2) + it('Should decode mapped', () => { + const R = Value.Decode(T1, null) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, 2) + }) +}) diff --git a/test/runtime/value/transform/unsafe.ts b/test/runtime/value/transform/unsafe.ts new file mode 100644 index 000000000..76d31cb4f --- /dev/null +++ b/test/runtime/value/transform/unsafe.ts @@ -0,0 +1,46 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' + +describe('value/transform/Unsafe', () => { + // -------------------------------------------------------- + // Fixtures + // -------------------------------------------------------- + beforeEach(() => TypeRegistry.Set('Foo', (schema, value) => value !== null)) // throw on null + afterEach(() => TypeRegistry.Delete('Foo')) + const Foo = Type.Unsafe({ [Kind]: 'Foo' }) + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Foo) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, 'hello') + Assert.IsEqual(R, 'hello') + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Foo) + .Decode((value) => value.split('').reverse().join('')) + .Encode((value) => value.split('').reverse().join('')) + it('Should decode mapped', () => { + const R = Value.Decode(T1, 'ABC') + Assert.IsEqual(R, 'CBA') + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, 'CBA') + Assert.IsEqual(R, 'ABC') + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/void.ts b/test/runtime/value/transform/void.ts new file mode 100644 index 000000000..16cd87248 --- /dev/null +++ b/test/runtime/value/transform/void.ts @@ -0,0 +1,40 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Void', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Void()) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Void()) + .Decode((value) => null) + .Encode((value) => undefined) + it('Should decode mapped', () => { + const R = Value.Decode(T1, undefined) + Assert.IsEqual(R, null) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, undefined) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/static/any.ts b/test/static/any.ts index abe16019e..90b417006 100644 --- a/test/static/any.ts +++ b/test/static/any.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Any()).ToInfer() +Expect(Type.Any()).ToStatic() diff --git a/test/static/array.ts b/test/static/array.ts index 1b52abc7c..2ec60f3cc 100644 --- a/test/static/array.ts +++ b/test/static/array.ts @@ -1,7 +1,7 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Array(Type.String())).ToInfer() +Expect(Type.Array(Type.String())).ToStatic() Expect( Type.Array( @@ -11,7 +11,7 @@ Expect( z: Type.String(), }), ), -).ToInfer< +).ToStatic< { x: number y: boolean @@ -19,6 +19,6 @@ Expect( }[] >() -Expect(Type.Array(Type.Array(Type.String()))).ToInfer() +Expect(Type.Array(Type.Array(Type.String()))).ToStatic() -Expect(Type.Array(Type.Tuple([Type.String(), Type.Number()]))).ToInfer<[string, number][]>() +Expect(Type.Array(Type.Tuple([Type.String(), Type.Number()]))).ToStatic<[string, number][]>() diff --git a/test/static/assert.ts b/test/static/assert.ts index 4b8763847..0e764d992 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -1,8 +1,9 @@ -import { Static, TSchema } from '@sinclair/typebox' +import { Static, StaticDecode, StaticEncode, TSchema } from '@sinclair/typebox' -export function Expect>(schema: T) { +export function Expect(schema: T) { return { - ToInfer: () => {}, - ToBe: () => {}, + ToStatic: >() => {}, + ToStaticDecode: >() => {}, + ToStaticEncode: >() => {}, } } diff --git a/test/static/async-iterator.ts b/test/static/async-iterator.ts index 3d8d8fd1a..75364f1b0 100644 --- a/test/static/async-iterator.ts +++ b/test/static/async-iterator.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.AsyncIterator(Type.String())).ToInfer>() +Expect(Type.AsyncIterator(Type.String())).ToStatic>() diff --git a/test/static/awaited.ts b/test/static/awaited.ts index f8b0f8228..303e30f89 100644 --- a/test/static/awaited.ts +++ b/test/static/awaited.ts @@ -1,20 +1,20 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Awaited(Type.String())).ToInfer() +Expect(Type.Awaited(Type.String())).ToStatic() -Expect(Type.Awaited(Type.Promise(Type.String()))).ToInfer() +Expect(Type.Awaited(Type.Promise(Type.String()))).ToStatic() -Expect(Type.Awaited(Type.Promise(Type.Promise(Type.String())))).ToInfer() +Expect(Type.Awaited(Type.Promise(Type.Promise(Type.String())))).ToStatic() // One Level -Expect(Type.Awaited(Type.Union([Type.Promise(Type.String()), Type.Number()]))).ToInfer() +Expect(Type.Awaited(Type.Union([Type.Promise(Type.String()), Type.Number()]))).ToStatic() -Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.String()), Type.Number()]))).ToInfer() +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.String()), Type.Number()]))).ToStatic() // Two Levels -Expect(Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToInfer() +Expect(Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToStatic() -Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToInfer() +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToStatic() diff --git a/test/static/bigint.ts b/test/static/bigint.ts index a18b437ba..40c5b90e0 100644 --- a/test/static/bigint.ts +++ b/test/static/bigint.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.BigInt()).ToInfer() +Expect(Type.BigInt()).ToStatic() diff --git a/test/static/boolean.ts b/test/static/boolean.ts index d1b9f4c79..9e1b01316 100644 --- a/test/static/boolean.ts +++ b/test/static/boolean.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Boolean()).ToInfer() +Expect(Type.Boolean()).ToStatic() diff --git a/test/static/capitalize.ts b/test/static/capitalize.ts index ca02c7398..b2982203e 100644 --- a/test/static/capitalize.ts +++ b/test/static/capitalize.ts @@ -1,14 +1,14 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Capitalize(Type.Literal('hello'))).ToInfer<'Hello'>() +Expect(Type.Capitalize(Type.Literal('hello'))).ToStatic<'Hello'>() -Expect(Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToInfer<'Hello' | 'World'>() +Expect(Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToStatic<'Hello' | 'World'>() -Expect(Type.Capitalize(Type.TemplateLiteral('hello${0|1}'))).ToInfer<'Hello0' | 'Hello1'>() +Expect(Type.Capitalize(Type.TemplateLiteral('hello${0|1}'))).ToStatic<'Hello0' | 'Hello1'>() // prettier-ignore -Expect(Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'Hello1' | 'Hello2'>() +Expect(Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'Hello1' | 'Hello2'>() // passthrough -Expect(Type.Capitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() +Expect(Type.Capitalize(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>() diff --git a/test/static/composite.ts b/test/static/composite.ts index cf9bb88a0..62528245c 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -13,7 +13,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number }>() } @@ -29,7 +29,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: never }>() } @@ -45,7 +45,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number }>() } @@ -62,7 +62,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' A: Type.Optional(Type.Number()), }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number | undefined }>() } @@ -74,7 +74,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' A: Type.Number(), }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number }>() } @@ -86,7 +86,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' A: Type.Number(), }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number }>() } @@ -102,7 +102,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' }) const T = Type.Composite([A, B]) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number B: number }>() diff --git a/test/static/constructor-parameters.ts b/test/static/constructor-parameters.ts index 68081e37f..43db97852 100644 --- a/test/static/constructor-parameters.ts +++ b/test/static/constructor-parameters.ts @@ -10,4 +10,4 @@ const C = Type.Constructor( const P = Type.ConstructorParameters(C) -Expect(P).ToInfer<[number, string]>() +Expect(P).ToStatic<[number, string]>() diff --git a/test/static/constructor.ts b/test/static/constructor.ts index 8e05d8c40..a05363991 100644 --- a/test/static/constructor.ts +++ b/test/static/constructor.ts @@ -8,4 +8,4 @@ const C = Type.Constructor( }), ) -Expect(C).ToInfer { method: (param_0: number, param_1: string) => boolean }>() +Expect(C).ToStatic { method: (param_0: number, param_1: string) => boolean }>() diff --git a/test/static/date.ts b/test/static/date.ts index 5907ac9eb..fe20220d5 100644 --- a/test/static/date.ts +++ b/test/static/date.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Date()).ToInfer() +Expect(Type.Date()).ToStatic() diff --git a/test/static/enum.ts b/test/static/enum.ts index e44ae72aa..2833716b7 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -10,5 +10,5 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Enum(E) - Expect(T).ToBe>() // ? + Expect(T).ToStatic>() // ? } diff --git a/test/static/exclude.ts b/test/static/exclude.ts index 526e7bc60..bca6d820a 100644 --- a/test/static/exclude.ts +++ b/test/static/exclude.ts @@ -3,15 +3,15 @@ import { Expect } from './assert' { const T = Type.Exclude(Type.String(), Type.String()) - Expect(T).ToBe() + Expect(T).ToStatic() } { const T = Type.Exclude(Type.String(), Type.Number()) - Expect(T).ToBe() + Expect(T).ToStatic() } { const T = Type.Exclude(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) - Expect(T).ToBe() + Expect(T).ToStatic() } // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -21,20 +21,20 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe() + Expect(T).ToStatic() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C'>() + Expect(T).ToStatic<'C'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C' | 'B'>() + Expect(T).ToStatic<'C' | 'B'>() } // ------------------------------------------------------------------------ // TemplateLiteral | Union @@ -44,20 +44,20 @@ import { Expect } from './assert' const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) - Expect(T).ToBe() + Expect(T).ToStatic() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C'>() + Expect(T).ToStatic<'C'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C' | 'B'>() + Expect(T).ToStatic<'C' | 'B'>() } // ------------------------------------------------------------------------ // Union | TemplateLiteral @@ -67,18 +67,18 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe() + Expect(T).ToStatic() } { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C'>() + Expect(T).ToStatic<'C'>() } { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) - Expect(T).ToBe<'C' | 'B'>() + Expect(T).ToStatic<'C' | 'B'>() } diff --git a/test/static/extract.ts b/test/static/extract.ts index 9e4073836..4bd3dc833 100644 --- a/test/static/extract.ts +++ b/test/static/extract.ts @@ -3,15 +3,15 @@ import { Expect } from './assert' { const T = Type.Extract(Type.String(), Type.String()) - Expect(T).ToBe() + Expect(T).ToStatic() } { const T = Type.Extract(Type.String(), Type.Number()) - Expect(T).ToBe() + Expect(T).ToStatic() } { const T = Type.Extract(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) - Expect(T).ToBe() + Expect(T).ToStatic() } // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -21,20 +21,20 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B' | 'C'>() + Expect(T).ToStatic<'A' | 'B' | 'C'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B'>() + Expect(T).ToStatic<'A' | 'B'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A'>() + Expect(T).ToStatic<'A'>() } // ------------------------------------------------------------------------ // TemplateLiteral | Union @@ -43,20 +43,20 @@ import { Expect } from './assert' const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B' | 'C'>() + Expect(T).ToStatic<'A' | 'B' | 'C'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B'>() + Expect(T).ToStatic<'A' | 'B'>() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A'>() + Expect(T).ToStatic<'A'>() } // ------------------------------------------------------------------------ // Union | TemplateLiteral @@ -66,18 +66,18 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B' | 'C'>() + Expect(T).ToStatic<'A' | 'B' | 'C'>() } { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A' | 'B'>() + Expect(T).ToStatic<'A' | 'B'>() } { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Extract(A, B) - Expect(T).ToBe<'A'>() + Expect(T).ToStatic<'A'>() } diff --git a/test/static/function.ts b/test/static/function.ts index 824075f7c..623aa911e 100644 --- a/test/static/function.ts +++ b/test/static/function.ts @@ -8,4 +8,4 @@ const C = Type.Function( }), ) -Expect(C).ToInfer<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() +Expect(C).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() diff --git a/test/static/index.ts b/test/static/index.ts index be70f8a5c..d1c757f10 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -45,6 +45,7 @@ import './strict' import './string' import './symbol' import './template-literal' +import './transform' import './tuple' import './uncapitalize' import './union' diff --git a/test/static/indexed.ts b/test/static/indexed.ts index c2cf5f9b6..cc0c9f24a 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -8,19 +8,19 @@ import { Type, Static } from '@sinclair/typebox' }) const R = Type.Index(T, ['x', 'y']) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) const R = Type.Index(T, Type.Union([Type.Literal('0'), Type.Literal('1')])) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const T = Type.Tuple([Type.Number(), Type.String(), Type.Boolean()]) const R = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const T = Type.Object({ @@ -30,37 +30,37 @@ import { Type, Static } from '@sinclair/typebox' const R = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const A = Type.Tuple([Type.String(), Type.Boolean()]) const R = Type.Index(A, Type.Number()) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const A = Type.Tuple([Type.String()]) const R = Type.Index(A, Type.Number()) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const A = Type.Tuple([]) const R = Type.Index(A, Type.Number()) type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const A = Type.Object({}) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } { const A = Type.Array(Type.Number()) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToInfer() + Expect(R).ToStatic() } // ------------------------------------------------------------------ // Intersections @@ -76,7 +76,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['y']) type O = Static - Expect(R).ToBe<1>() + Expect(R).ToStatic<1>() } { type A = { x: string; y: 1 } @@ -89,7 +89,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: 1 } @@ -102,7 +102,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x', 'y']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: number } @@ -115,7 +115,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: number } @@ -128,7 +128,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['y']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: number } @@ -141,7 +141,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x', 'y']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: 1 } @@ -158,7 +158,7 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: string; y: 1 } @@ -175,7 +175,7 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) type O = Static - Expect(R).ToBe<1>() + Expect(R).ToStatic<1>() } { type A = { x: string; y: 1 } @@ -192,7 +192,7 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { type A = { x: 'A'; y: 1 } @@ -209,7 +209,7 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Union([A, B, C, D]) const R = Type.Index(I, Type.Union([Type.Literal('x')])) type O = Static - Expect(R).ToBe<'A' | 'B' | 'C' | 'D'>() + Expect(R).ToStatic<'A' | 'B' | 'C' | 'D'>() } { type I = { @@ -227,7 +227,7 @@ import { Type, Static } from '@sinclair/typebox' ) const R = Type.Index(I, ['x', 'y', 'z']) // z unresolvable type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } // ------------------------------------------------ // Numeric | String Variants @@ -239,7 +239,7 @@ import { Type, Static } from '@sinclair/typebox' }) const R = Type.Index(T, [0, '1']) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() } { const T = Type.Object({ @@ -248,5 +248,12 @@ import { Type, Static } from '@sinclair/typebox' }) const R = Type.Index(T, Type.KeyOf(T)) type O = Static - Expect(R).ToBe() + Expect(R).ToStatic() +} +{ + const P = Type.Tuple([Type.Number(), Type.String()]) + const R = Type.Object({ + x: Type.Index(P, [0, 1]), + }) + Expect(R).ToStatic<{ x: number | string }>() } diff --git a/test/static/intersect.ts b/test/static/intersect.ts index b7cf181b6..8fc36160e 100644 --- a/test/static/intersect.ts +++ b/test/static/intersect.ts @@ -12,7 +12,7 @@ import { Type, TOptional, TString, Static } from '@sinclair/typebox' }) const T = Type.Intersect([A, B]) - Expect(T).ToInfer< + Expect(T).ToStatic< { A: string B: string @@ -32,8 +32,8 @@ import { Type, TOptional, TString, Static } from '@sinclair/typebox' }) const T = Type.Intersect([A, B]) - Expect(T.properties.A).ToBe>() - Expect(T.properties.B).ToBe() + Expect(T.properties.A).ToStatic>() + Expect(T.properties.B).ToStatic() } // https://github.com/sinclairzx81/typebox/issues/113 @@ -50,5 +50,5 @@ import { Type, TOptional, TString, Static } from '@sinclair/typebox' // invert equivelence (expect true both cases) type T1 = T extends { A: string } & ({ B: string } | { C: string }) ? true : false type T2 = { A: string } & ({ B: string } | { C: string }) extends T ? true : false - Expect(T).ToBe<{ A: string } & ({ B: string } | { C: string })>() // solved! + Expect(T).ToStatic<{ A: string } & ({ B: string } | { C: string })>() // solved! } diff --git a/test/static/iterator.ts b/test/static/iterator.ts index 4728ad035..5727f641f 100644 --- a/test/static/iterator.ts +++ b/test/static/iterator.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Iterator(Type.String())).ToInfer>() +Expect(Type.Iterator(Type.String())).ToStatic>() diff --git a/test/static/keyof.ts b/test/static/keyof.ts index e5ad39fed..10d21df36 100644 --- a/test/static/keyof.ts +++ b/test/static/keyof.ts @@ -9,7 +9,7 @@ import { Type } from '@sinclair/typebox' C: Type.Null(), }), ) - Expect(K).ToInfer<'A' | 'B' | 'C'>() + Expect(K).ToStatic<'A' | 'B' | 'C'>() } { @@ -24,7 +24,7 @@ import { Type } from '@sinclair/typebox' const K = Type.KeyOf(T) - Expect(K).ToInfer<'A' | 'B'>() + Expect(K).ToStatic<'A' | 'B'>() } { @@ -39,7 +39,7 @@ import { Type } from '@sinclair/typebox' const K = Type.KeyOf(T) - Expect(K).ToInfer<'C'>() + Expect(K).ToStatic<'C'>() } { @@ -53,7 +53,7 @@ import { Type } from '@sinclair/typebox' ['A', 'B'], ), ) - Expect(T).ToInfer<'C'>() + Expect(T).ToStatic<'C'>() } { { @@ -70,13 +70,13 @@ import { Type } from '@sinclair/typebox' const K1 = Type.KeyOf(T) - Expect(K1).ToInfer<'type' | 'x' | 'y' | 'z'>() + Expect(K1).ToStatic<'type' | 'x' | 'y' | 'z'>() const P = Type.Omit(T, ['type', 'x']) const K2 = Type.KeyOf(P) - Expect(K2).ToInfer<'y' | 'z'>() + Expect(K2).ToStatic<'y' | 'z'>() } } { @@ -89,7 +89,7 @@ import { Type } from '@sinclair/typebox' }), ) const K = Type.KeyOf(T) - Expect(K).ToInfer<'a' | 'b' | 'c' | 'd'>() + Expect(K).ToStatic<'a' | 'b' | 'c' | 'd'>() } { const T = Type.Object({ @@ -98,5 +98,5 @@ import { Type } from '@sinclair/typebox' c: Type.Optional(Type.String()), }) const K = Type.KeyOf(T) - Expect(K).ToInfer<'a' | 'b' | 'c'>() + Expect(K).ToStatic<'a' | 'b' | 'c'>() } diff --git a/test/static/literal.ts b/test/static/literal.ts index 461f43475..8a76ffd4f 100644 --- a/test/static/literal.ts +++ b/test/static/literal.ts @@ -1,8 +1,8 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Literal('hello')).ToInfer<'hello'>() +Expect(Type.Literal('hello')).ToStatic<'hello'>() -Expect(Type.Literal(true)).ToInfer() +Expect(Type.Literal(true)).ToStatic() -Expect(Type.Literal(42)).ToInfer<42>() +Expect(Type.Literal(42)).ToStatic<42>() diff --git a/test/static/lowercase.ts b/test/static/lowercase.ts index 78c7f7a84..f04ce5ece 100644 --- a/test/static/lowercase.ts +++ b/test/static/lowercase.ts @@ -1,14 +1,14 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Lowercase(Type.Literal('HELLO'))).ToInfer<'hello'>() +Expect(Type.Lowercase(Type.Literal('HELLO'))).ToStatic<'hello'>() -Expect(Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToInfer<'hello' | 'world'>() +Expect(Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToStatic<'hello' | 'world'>() -Expect(Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hello0' | 'hello1'>() +Expect(Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'hello0' | 'hello1'>() // prettier-ignore -Expect(Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'hello1' | 'hello2'>() +Expect(Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'hello1' | 'hello2'>() // passthrough -Expect(Type.Lowercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() +Expect(Type.Lowercase(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>() diff --git a/test/static/modifier.ts b/test/static/modifier.ts index 07186f6d2..cb108b6d3 100644 --- a/test/static/modifier.ts +++ b/test/static/modifier.ts @@ -9,7 +9,7 @@ import { Type, TSchema } from '@sinclair/typebox' C: Type.Optional(Type.String()), D: Type.String(), }) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ readonly A?: string readonly B: string C?: string diff --git a/test/static/never.ts b/test/static/never.ts index 5eae99bec..8261621e6 100644 --- a/test/static/never.ts +++ b/test/static/never.ts @@ -3,5 +3,5 @@ import { Type } from '@sinclair/typebox' { const T = Type.Never() - Expect(T).ToInfer() + Expect(T).ToStatic() } diff --git a/test/static/not.ts b/test/static/not.ts index de3abbf7d..5c95fa0ce 100644 --- a/test/static/not.ts +++ b/test/static/not.ts @@ -14,13 +14,13 @@ import { Type } from '@sinclair/typebox' const A = Type.Number() const B = Type.Not(Type.Number()) const T = Type.Extends(A, B, Type.Literal(true), Type.Literal(false)) - Expect(T).ToInfer() + Expect(T).ToStatic() } { const T = Type.Not(Type.Number()) - Expect(T).ToInfer() + Expect(T).ToStatic() } { const T = Type.Not(Type.Not(Type.Number())) - Expect(T).ToInfer() + Expect(T).ToStatic() } diff --git a/test/static/null.ts b/test/static/null.ts index 36993ffe3..50f474bec 100644 --- a/test/static/null.ts +++ b/test/static/null.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Null()).ToInfer() +Expect(Type.Null()).ToStatic() diff --git a/test/static/number.ts b/test/static/number.ts index 2b1717110..17a41ee5c 100644 --- a/test/static/number.ts +++ b/test/static/number.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Number()).ToInfer() +Expect(Type.Number()).ToStatic() diff --git a/test/static/object.ts b/test/static/object.ts index c9d57419c..d7f4b0ef5 100644 --- a/test/static/object.ts +++ b/test/static/object.ts @@ -8,7 +8,7 @@ import { Type } from '@sinclair/typebox' C: Type.String(), }) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string C: string @@ -32,7 +32,7 @@ import { Type } from '@sinclair/typebox' C: Type.String(), }), }) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: { A: string B: string @@ -62,7 +62,7 @@ import { Type } from '@sinclair/typebox' }, ) // note: Pending TypeScript support for negated types. - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: number B: number C: number diff --git a/test/static/omit.ts b/test/static/omit.ts index 98b93ba57..7b1bb5644 100644 --- a/test/static/omit.ts +++ b/test/static/omit.ts @@ -12,7 +12,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ C: string }>() } @@ -30,7 +30,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ C: string }>() } @@ -50,7 +50,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ C: string }>() } @@ -66,7 +66,7 @@ import { Type, Static } from '@sinclair/typebox' }) const T = Type.Intersect([Union, Extended]) - Expect(T).ToInfer< + Expect(T).ToStatic< ( | { type: 'A' @@ -86,7 +86,7 @@ import { Type, Static } from '@sinclair/typebox' const P = Type.Omit(T, ['type', 'x']) - Expect(P).ToInfer< + Expect(P).ToStatic< ({} | {} | {}) & { y: number z: number @@ -95,7 +95,7 @@ import { Type, Static } from '@sinclair/typebox' const O = Type.Partial(P) - Expect(O).ToInfer< + Expect(O).ToStatic< ({} | {} | {}) & { y?: number | undefined z?: number | undefined diff --git a/test/static/optional.ts b/test/static/optional.ts index aba6c4e8c..b3342692a 100644 --- a/test/static/optional.ts +++ b/test/static/optional.ts @@ -8,7 +8,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A?: string }>() } diff --git a/test/static/parameters.ts b/test/static/parameters.ts index 02ad9c989..439ee9efe 100644 --- a/test/static/parameters.ts +++ b/test/static/parameters.ts @@ -10,4 +10,4 @@ const C = Type.Function( const P = Type.Parameters(C) -Expect(P).ToInfer<[number, string]>() +Expect(P).ToStatic<[number, string]>() diff --git a/test/static/partial.ts b/test/static/partial.ts index a9e591f94..d0d075a84 100644 --- a/test/static/partial.ts +++ b/test/static/partial.ts @@ -11,7 +11,7 @@ import { Type, Static } from '@sinclair/typebox' ) type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A?: string B?: string C?: string @@ -31,7 +31,7 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Intersect([Union, Extended]) - Expect(T).ToInfer< + Expect(T).ToStatic< ( | { type: 'A' @@ -51,7 +51,7 @@ import { Type, Static } from '@sinclair/typebox' const P = Type.Partial(T) - Expect(P).ToInfer< + Expect(P).ToStatic< ( | { type?: 'A' | undefined diff --git a/test/static/pick.ts b/test/static/pick.ts index f9d825571..de29ba4bb 100644 --- a/test/static/pick.ts +++ b/test/static/pick.ts @@ -12,7 +12,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string }>() @@ -31,7 +31,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string }>() @@ -52,7 +52,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string }>() @@ -69,7 +69,7 @@ import { Type, Static } from '@sinclair/typebox' }) const T = Type.Intersect([Union, Extended]) - Expect(T).ToInfer< + Expect(T).ToStatic< ( | { type: 'A' @@ -89,11 +89,11 @@ import { Type, Static } from '@sinclair/typebox' const K = Type.KeyOf(T) - Expect(K).ToInfer<'type' | 'x' | 'y' | 'z'>() + Expect(K).ToStatic<'type' | 'x' | 'y' | 'z'>() const P = Type.Pick(T, ['type', 'x']) - Expect(P).ToInfer< + Expect(P).ToStatic< ( | { type: 'A' @@ -111,7 +111,7 @@ import { Type, Static } from '@sinclair/typebox' const O = Type.Partial(P) - Expect(O).ToInfer< + Expect(O).ToStatic< ( | { type?: 'A' | undefined diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts index 377cab0cf..3191dcd75 100644 --- a/test/static/readonly-optional.ts +++ b/test/static/readonly-optional.ts @@ -5,7 +5,7 @@ import { Type, TSchema } from '@sinclair/typebox' const T = Type.Object({ A: Type.ReadonlyOptional(Type.String()), }) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ readonly A?: string }>() } diff --git a/test/static/readonly.ts b/test/static/readonly.ts index 6cce5ebb3..4d1668346 100644 --- a/test/static/readonly.ts +++ b/test/static/readonly.ts @@ -8,7 +8,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ readonly A: string }>() } diff --git a/test/static/record.ts b/test/static/record.ts index 7338d5f26..a314c46ff 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -6,29 +6,29 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { // type K = string const K = Type.RegExp(/foo|bar/) const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { // type K = number const K = Type.Number() const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() - Expect(T).ToInfer>() + Expect(T).ToStatic>() + Expect(T).ToStatic>() } { // type K = 'A' | 'B' | 'C' const K = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { // type K = keyof { A: number, B: number, C: number } @@ -41,7 +41,7 @@ import { Type, Static } from '@sinclair/typebox' ) const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { // type K = keyof Omit<{ A: number, B: number, C: number }, 'C'> @@ -57,16 +57,16 @@ import { Type, Static } from '@sinclair/typebox' ) const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { const T = Type.Record(Type.Number(), Type.String()) - Expect(T).ToInfer>() + Expect(T).ToStatic>() } { const T = Type.Record(Type.Integer(), Type.String()) - Expect(T).ToInfer>() + Expect(T).ToStatic>() } diff --git a/test/static/recursive.ts b/test/static/recursive.ts index 06ca5d025..7f79ab6f5 100644 --- a/test/static/recursive.ts +++ b/test/static/recursive.ts @@ -10,7 +10,7 @@ import { Type, Static } from '@sinclair/typebox' }), ) type T = Static - Expect(R).ToInfer<{ id: string; nodes: T[] }>() + Expect(R).ToStatic<{ id: string; nodes: T[] }>() } { // keyof @@ -21,7 +21,7 @@ import { Type, Static } from '@sinclair/typebox' }), ) const T = Type.KeyOf(R) - Expect(T).ToInfer<'id' | 'nodes'>() + Expect(T).ToStatic<'id' | 'nodes'>() } { // partial @@ -32,7 +32,7 @@ import { Type, Static } from '@sinclair/typebox' }), ) const T = Type.Partial(R) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ id: string | undefined nodes: Static[] | undefined }>() @@ -47,7 +47,7 @@ import { Type, Static } from '@sinclair/typebox' ) const P = Type.Partial(R) const T = Type.Required(P) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ id: string nodes: Static[] }>() @@ -61,7 +61,7 @@ import { Type, Static } from '@sinclair/typebox' }), ) const T = Type.Pick(R, ['id']) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ id: string }>() } @@ -74,7 +74,7 @@ import { Type, Static } from '@sinclair/typebox' }), ) const T = Type.Omit(R, ['id']) - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ nodes: Static[] }>() } diff --git a/test/static/ref.ts b/test/static/ref.ts index c4abe3675..47ccd9b29 100644 --- a/test/static/ref.ts +++ b/test/static/ref.ts @@ -8,6 +8,6 @@ import { Type, Static } from '@sinclair/typebox' type T = Static type R = Static - Expect(T).ToInfer() - Expect(R).ToInfer() + Expect(T).ToStatic() + Expect(R).ToStatic() } diff --git a/test/static/regexp.ts b/test/static/regexp.ts index 60feac2af..7f9413071 100644 --- a/test/static/regexp.ts +++ b/test/static/regexp.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.RegExp(/foo/)).ToInfer() +Expect(Type.RegExp(/foo/)).ToStatic() diff --git a/test/static/required.ts b/test/static/required.ts index d688584b1..cbf9fd4e4 100644 --- a/test/static/required.ts +++ b/test/static/required.ts @@ -11,7 +11,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string C: string @@ -32,7 +32,7 @@ import { Type, Static } from '@sinclair/typebox' const P = Type.Partial(T) - Expect(P).ToInfer< + Expect(P).ToStatic< ( | { type?: 'A' | undefined @@ -52,7 +52,7 @@ import { Type, Static } from '@sinclair/typebox' const R = Type.Required(P) - Expect(R).ToInfer< + Expect(R).ToStatic< ( | { type: 'A' diff --git a/test/static/rest.ts b/test/static/rest.ts index 129e22aa4..4f2bba7f0 100644 --- a/test/static/rest.ts +++ b/test/static/rest.ts @@ -1,36 +1,52 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' - { - const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) - const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) - const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) - Expect(C).ToInfer<[1, 2, 3, 4]>() + // union never + const A = Type.String() + const B = Type.Union(Type.Rest(A)) + Expect(B).ToStatic() } { + // intersect never const A = Type.String() - const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) - const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) - Expect(C).ToInfer<[string, 3, 4]>() + const B = Type.Intersect(Type.Rest(A)) + Expect(B).ToStatic() } { - const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) - const B = Type.String() - const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) - Expect(C).ToInfer<[1, 2, string]>() + // tuple + const A = Type.Tuple([Type.Number(), Type.String()]) + const B = Type.Union(Type.Rest(A)) + Expect(B).ToStatic() } { + // tuple spread const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) - const B = Type.Union(Type.Rest(A)) - Expect(B).ToInfer<1 | 2>() + const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Expect(C).ToStatic<[1, 2, 3, 4]>() } { - const A = Type.Tuple([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - const B = Type.Intersect(Type.Rest(A)) - Expect(B).ToInfer<{ x: number } & { y: number }>() + // union to intersect + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Union([A, B]) + const D = Type.Intersect(Type.Rest(C)) + Expect(D).ToStatic< + { + x: number + } & { + y: string + } + >() } { - const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) - const B = Type.Function(Type.Rest(A), Type.Null()) - Expect(B).ToInfer<(a: 1, b: 2) => null>() + // intersect to composite + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Intersect([A, B]) + const D = Type.Composite(Type.Rest(C)) + Expect(D).ToStatic<{ + x: number + y: string + }>() } diff --git a/test/static/return-type.ts b/test/static/return-type.ts index d394484ee..aa463509b 100644 --- a/test/static/return-type.ts +++ b/test/static/return-type.ts @@ -6,7 +6,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer() + Expect(T).ToStatic() } { @@ -14,5 +14,5 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer() + Expect(T).ToStatic() } diff --git a/test/static/strict.ts b/test/static/strict.ts index a389c8963..a6a0ebcac 100644 --- a/test/static/strict.ts +++ b/test/static/strict.ts @@ -12,7 +12,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<{ + Expect(T).ToStatic<{ A: string B: string C: string diff --git a/test/static/string.ts b/test/static/string.ts index 5f21e625b..609dad5ad 100644 --- a/test/static/string.ts +++ b/test/static/string.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.String()).ToInfer() +Expect(Type.String()).ToStatic() diff --git a/test/static/symbol.ts b/test/static/symbol.ts index 633541438..13c8c6aeb 100644 --- a/test/static/symbol.ts +++ b/test/static/symbol.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Symbol()).ToInfer() +Expect(Type.Symbol()).ToStatic() diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts index 1b8ebf3b1..f0ec8444e 100644 --- a/test/static/template-literal.ts +++ b/test/static/template-literal.ts @@ -4,43 +4,43 @@ import { Type } from '@sinclair/typebox' { // Empty const T = Type.TemplateLiteral([]) - Expect(T).ToInfer<''>() + Expect(T).ToStatic<''>() } { // Literal const T = Type.TemplateLiteral([Type.Literal('hello')]) - Expect(T).ToInfer<'hello'>() + Expect(T).ToStatic<'hello'>() } { // And Sequence const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Literal('world')]) - Expect(T).ToInfer<'helloworld'>() + Expect(T).ToStatic<'helloworld'>() } { // And / Or Sequence const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('1'), Type.Literal('2')])]) - Expect(T).ToInfer<'hello1' | 'hello2'>() + Expect(T).ToStatic<'hello1' | 'hello2'>() } { // Auxiliary Template const A = Type.TemplateLiteral([Type.Union([Type.Literal('1'), Type.Literal('2')])]) const T = Type.TemplateLiteral([Type.Literal('hello'), A]) - Expect(T).ToInfer<'hello1' | 'hello2'>() + Expect(T).ToStatic<'hello1' | 'hello2'>() } { // String const T = Type.TemplateLiteral([Type.String()]) - Expect(T).ToInfer<`${string}`>() + Expect(T).ToStatic<`${string}`>() } { // Number const T = Type.TemplateLiteral([Type.Number()]) - Expect(T).ToInfer<`${number}`>() + Expect(T).ToStatic<`${number}`>() } { // Boolean const T = Type.TemplateLiteral([Type.Boolean()]) - Expect(T).ToInfer<`${boolean}`>() + Expect(T).ToStatic<`${boolean}`>() } { // Enum Implicit @@ -51,7 +51,7 @@ import { Type } from '@sinclair/typebox' } const A = Type.Enum(E) const T = Type.TemplateLiteral([Type.Literal('hello'), A]) - Expect(T).ToInfer<'hello0' | 'hello1' | 'hello2'>() + Expect(T).ToStatic<'hello0' | 'hello1' | 'hello2'>() } { // Enum Explicit @@ -62,11 +62,11 @@ import { Type } from '@sinclair/typebox' } const A = Type.Enum(E) const T = Type.TemplateLiteral([Type.Literal('hello'), A]) - Expect(T).ToInfer<'hello0' | 'helloB' | 'helloC'>() + Expect(T).ToStatic<'hello0' | 'helloB' | 'helloC'>() } { // Enum Object Explicit const A = Type.Enum(Object.freeze({ a: 'A', b: 'B' })) const T = Type.TemplateLiteral([Type.Literal('hello'), A]) - Expect(T).ToInfer<'helloA' | 'helloB'>() + Expect(T).ToStatic<'helloA' | 'helloB'>() } diff --git a/test/static/transform.ts b/test/static/transform.ts new file mode 100644 index 000000000..99e7e067a --- /dev/null +++ b/test/static/transform.ts @@ -0,0 +1,184 @@ +import { Type, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' +import { Expect } from './assert' +{ + // string > number + const T = Type.Transform(Type.Number()) + .Decode((value) => value.toString()) + .Encode((value) => parseFloat(value)) + + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode() + Expect(T).ToStatic() +} +{ + // boolean > union + const T = Type.Transform(Type.Boolean()) + .Decode((value) => (value ? (1 as const) : (2 as const))) + .Encode((value) => true) + + Expect(T).ToStatic() + Expect(T).ToStaticDecode<1 | 2>() + Expect(T).ToStaticEncode() +} +{ + // literal > union + const T = Type.Transform(Type.Union([Type.Literal(1), Type.Literal(2)])) + .Decode((value) => true as const) + .Encode((value) => (value ? (1 as const) : (2 as const))) + Expect(T).ToStatic<1 | 2>() + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode<1 | 2>() +} +{ + // nested: 1 > 2 > 3 + const T1 = Type.Transform(Type.Literal(1)) + .Decode((value) => 2 as const) + .Encode((value) => 1 as const) + + const T2 = Type.Transform(T1) + .Decode((value) => 3 as const) + .Encode((value) => 2 as const) + + Expect(T1).ToStatic<1>() + Expect(T1).ToStaticDecode<2>() + Expect(T1).ToStaticEncode<1>() + + Expect(T2).ToStatic<2>() // resolve to base + Expect(T2).ToStaticDecode<3>() + Expect(T2).ToStaticEncode<2>() +} +{ + // nested: 1 > 2 > 3 > 4 + const T1 = Type.Transform(Type.Literal(1)) + .Decode((value) => 2 as const) + .Encode((value) => 1 as const) + + const T2 = Type.Transform(T1) + .Decode((value) => 3 as const) + .Encode((value) => 2 as const) + + const T3 = Type.Transform(T2) + .Decode((value) => 4 as const) + .Encode((value) => 3 as const) + + Expect(T1).ToStatic<1>() + Expect(T1).ToStaticDecode<2>() + Expect(T1).ToStaticEncode<1>() + + Expect(T2).ToStatic<2>() + Expect(T2).ToStaticDecode<3>() + Expect(T2).ToStaticEncode<2>() + + Expect(T3).ToStatic<3>() + Expect(T3).ToStaticDecode<4>() + Expect(T3).ToStaticEncode<3>() +} +{ + // recursive > 1 + // prettier-ignore + const T = Type.Transform(Type.Recursive(This => Type.Object({ + id: Type.String(), + nodes: Type.Array(This) + }))) + .Decode((value) => 1 as const) + .Encode((value) => ({ + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] } + ] + })) + + interface N { + id: string + nodes: this[] + } + Expect(T).ToStatic() + Expect(T).ToStaticDecode<1>() + Expect(T).ToStaticEncode() +} +{ + // recursive > 1 > 2 + interface N { + id: string + nodes: this[] + } + // prettier-ignore + const T1 = Type.Transform(Type.Recursive(This => Type.Object({ + id: Type.String(), + nodes: Type.Array(This) + }))) + .Decode((value) => 1 as const) + .Encode((value) => ({ + id: 'A', + nodes: [ + { id: 'B', nodes: [] }, + { id: 'C', nodes: [] } + ] + })) + + const T2 = Type.Transform(T1) + .Decode((value) => 2 as const) + .Encode((value) => 1 as const) + + Expect(T1).ToStatic() + Expect(T1).ToStaticDecode<1>() + Expect(T1).ToStaticEncode() + + Expect(T2).ToStatic<1>() + Expect(T2).ToStaticDecode<2>() + Expect(T2).ToStaticEncode<1>() +} +{ + // deep-nesting + // prettier-ignore + const T = Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Transform(Type.Literal(1)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value)) + .Decode((value) => value) + .Encode((value) => value) + + Expect(T).ToStatic<1>() + Expect(T).ToStaticDecode<1>() + Expect(T).ToStaticEncode<1>() +} +{ + // null to typebox type + // prettier-ignore + const T = Type.Transform(Type.Null()) + .Decode(value => Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + })) + .Encode(value => null) + Expect(T).ToStatic() + Expect(T).ToStaticDecode< + TObject<{ + x: TNumber + y: TNumber + z: TNumber + }> + >() + Expect(T).ToStaticEncode() + type T = StaticDecode + type S = Static // type S = { + // x: number; + // y: number; + // z: number; + // } // lol +} diff --git a/test/static/tuple.ts b/test/static/tuple.ts index 2ced852bc..61fde9821 100644 --- a/test/static/tuple.ts +++ b/test/static/tuple.ts @@ -6,5 +6,5 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer<[number, string, boolean]>() + Expect(T).ToStatic<[number, string, boolean]>() } diff --git a/test/static/uncapitalize.ts b/test/static/uncapitalize.ts index a52d0c023..226504b94 100644 --- a/test/static/uncapitalize.ts +++ b/test/static/uncapitalize.ts @@ -1,14 +1,14 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Uncapitalize(Type.Literal('HELLO'))).ToInfer<'hELLO'>() +Expect(Type.Uncapitalize(Type.Literal('HELLO'))).ToStatic<'hELLO'>() -Expect(Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToInfer<'hELLO' | 'wORLD'>() +Expect(Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]))).ToStatic<'hELLO' | 'wORLD'>() -Expect(Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'hELLO0' | 'hELLO1'>() +Expect(Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'hELLO0' | 'hELLO1'>() // prettier-ignore -Expect(Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'hELLO1' | 'hELLO2'>() +Expect(Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'hELLO1' | 'hELLO2'>() // passthrough -Expect(Type.Uncapitalize(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() +Expect(Type.Uncapitalize(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>() diff --git a/test/static/union.ts b/test/static/union.ts index 114d685fc..3529e5465 100644 --- a/test/static/union.ts +++ b/test/static/union.ts @@ -8,7 +8,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer() + Expect(T).ToStatic() } { const A = Type.Object({ @@ -23,7 +23,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer< + Expect(T).ToStatic< | { A: string B: string @@ -48,7 +48,7 @@ import { Type, Static } from '@sinclair/typebox' type T = Static - Expect(T).ToInfer< + Expect(T).ToStatic< | { A: string B: string @@ -69,5 +69,5 @@ import { Type, Static } from '@sinclair/typebox' { const T = Type.Union([]) - Expect(T).ToInfer() + Expect(T).ToStatic() } diff --git a/test/static/unknown.ts b/test/static/unknown.ts index 28168a2a4..0ff2e5d81 100644 --- a/test/static/unknown.ts +++ b/test/static/unknown.ts @@ -1,4 +1,4 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Unknown()).ToInfer() +Expect(Type.Unknown()).ToStatic() diff --git a/test/static/uppercase.ts b/test/static/uppercase.ts index dafde13c6..2341feab6 100644 --- a/test/static/uppercase.ts +++ b/test/static/uppercase.ts @@ -1,14 +1,14 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -Expect(Type.Uppercase(Type.Literal('hello'))).ToInfer<'HELLO'>() +Expect(Type.Uppercase(Type.Literal('hello'))).ToStatic<'HELLO'>() -Expect(Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToInfer<'HELLO' | 'WORLD'>() +Expect(Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')]))).ToStatic<'HELLO' | 'WORLD'>() -Expect(Type.Uppercase(Type.TemplateLiteral('HELLO${0|1}'))).ToInfer<'HELLO0' | 'HELLO1'>() +Expect(Type.Uppercase(Type.TemplateLiteral('HELLO${0|1}'))).ToStatic<'HELLO0' | 'HELLO1'>() // prettier-ignore -Expect(Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToBe<'HELLO1' | 'HELLO2'>() +Expect(Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]))).ToStatic<'HELLO1' | 'HELLO2'>() // passthrough -Expect(Type.Uppercase(Type.Object({ x: Type.Number() }))).ToInfer<{ x: number }>() +Expect(Type.Uppercase(Type.Object({ x: Type.Number() }))).ToStatic<{ x: number }>() diff --git a/tsconfig.json b/tsconfig.json index 41a696de3..ec148af39 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -49,6 +49,9 @@ "@sinclair/typebox/value/pointer": [ "src/value/pointer.ts" ], + "@sinclair/typebox/value/transform": [ + "src/value/transform/index.ts" + ], "@sinclair/typebox/value": [ "src/value/index.ts" ], From 824bd8821a8934725b791abda40079c099f6f7da Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 14 Aug 2023 12:44:55 +0900 Subject: [PATCH 173/369] Revision 0.31.1 (#533) --- package.json | 2 +- readme.md | 6 +- src/value/transform.ts | 4 +- test/runtime/compiler/constructor.ts | 80 +++++++++++++++++++++++ test/runtime/compiler/function.ts | 25 +++++++ test/runtime/compiler/index.ts | 2 + test/runtime/value/check/constructor.ts | 87 +++++++++++++++++++++++++ test/runtime/value/check/function.ts | 28 ++++++++ test/runtime/value/check/index.ts | 2 + 9 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 test/runtime/compiler/constructor.ts create mode 100644 test/runtime/compiler/function.ts create mode 100644 test/runtime/value/check/constructor.ts create mode 100644 test/runtime/value/check/function.ts diff --git a/package.json b/package.json index 295d4384e..fbe8febe3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.0", + "version": "0.31.1", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 4b80a0671..d19ccfd62 100644 --- a/readme.md +++ b/readme.md @@ -58,11 +58,9 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime checked using standard Json Schema validation. +TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime checked using standard Json Schema validation. -TypeBox is designed to be a runtime type system based on industry standard specifications for use in distributed systems. It offers serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, a high performance runtime validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. - -TypeBox can be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. +TypeBox is designed to be a runtime type system based on industry standard specifications. It offers serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, a high performance runtime validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. It can either be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. License MIT diff --git a/src/value/transform.ts b/src/value/transform.ts index e1608edab..12faf5834 100644 --- a/src/value/transform.ts +++ b/src/value/transform.ts @@ -76,10 +76,10 @@ export namespace HasTransform { return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) } function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema.returns, references)) + return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) } function TFunction(schema: Types.TFunction, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema.returns, references)) + return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) } function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]) { return Types.TypeGuard.TTransform(schema) || Types.TypeGuard.TTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) diff --git a/test/runtime/compiler/constructor.ts b/test/runtime/compiler/constructor.ts new file mode 100644 index 000000000..324faf111 --- /dev/null +++ b/test/runtime/compiler/constructor.ts @@ -0,0 +1,80 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('compiler/Constructor', () => { + it('Should validate constructor 1', () => { + const T = Type.Constructor([], Type.Object({})) + Ok(T, class {}) + }) + it('Should validate constructor 2', () => { + const T = Type.Constructor([Type.Number()], Type.Object({})) + // note: constructor arguments are non-checkable + Ok(T, class {}) + }) + it('Should validate constructor 3', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + method: Type.Function([], Type.Void()), + }), + ) + Ok( + T, + class { + method() {} + }, + ) + }) + it('Should validate constructor 4', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Ok( + T, + class { + get x() { + return 1 + } + get y() { + return 1 + } + get z() { + return 1 + } + }, + ) + }) + it('Should not validate constructor 1', () => { + const T = Type.Constructor([Type.Number()], Type.Object({})) + Fail(T, 1) + }) + it('Should not validate constructor 2', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Fail( + T, + class { + get x() { + return null + } + get y() { + return null + } + get z() { + return null + } + }, + ) + }) +}) diff --git a/test/runtime/compiler/function.ts b/test/runtime/compiler/function.ts new file mode 100644 index 000000000..29a604b96 --- /dev/null +++ b/test/runtime/compiler/function.ts @@ -0,0 +1,25 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('compiler/Function', () => { + it('Should validate function 1', () => { + const T = Type.Function([Type.Number()], Type.Number()) + Ok(T, function () {}) + }) + it('Should validate function 2', () => { + const T = Type.Function([Type.Number()], Type.Number()) + // note: validation only checks typeof 'function' + Ok(T, function (a: string, b: string, c: string, d: string) {}) + }) + it('Should validate function 3', () => { + const T = Type.Function([Type.Number()], Type.Number()) + // note: validation only checks typeof 'function' + Ok(T, function () { + return 'not-a-number' + }) + }) + it('Should not validate function', () => { + const T = Type.Function([Type.Number()], Type.Number()) + Fail(T, 1) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index 0524426fc..db87a1e15 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -4,9 +4,11 @@ import './async-iterator' import './bigint' import './boolean' import './composite' +import './constructor' import './date' import './unicode' import './enum' +import './function' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/check/constructor.ts b/test/runtime/value/check/constructor.ts new file mode 100644 index 000000000..ba553e3d6 --- /dev/null +++ b/test/runtime/value/check/constructor.ts @@ -0,0 +1,87 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Constructor', () => { + it('Should validate constructor 1', () => { + const T = Type.Constructor([], Type.Object({})) + Assert.IsTrue(Value.Check(T, class {})) + }) + it('Should validate constructor 2', () => { + const T = Type.Constructor([Type.Number()], Type.Object({})) + // note: constructor arguments are non-checkable + Assert.IsTrue(Value.Check(T, class {})) + }) + it('Should validate constructor 3', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + method: Type.Function([], Type.Void()), + }), + ) + Assert.IsTrue( + Value.Check( + T, + class { + method() {} + }, + ), + ) + }) + it('Should validate constructor 4', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Assert.IsTrue( + Value.Check( + T, + class { + get x() { + return 1 + } + get y() { + return 1 + } + get z() { + return 1 + } + }, + ), + ) + }) + it('Should not validate constructor 1', () => { + const T = Type.Constructor([Type.Number()], Type.Object({})) + Assert.IsFalse(Value.Check(T, 1)) + }) + it('Should not validate constructor 2', () => { + const T = Type.Constructor( + [Type.Number()], + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + Assert.IsFalse( + Value.Check( + T, + class { + get x() { + return null + } + get y() { + return null + } + get z() { + return null + } + }, + ), + ) + }) +}) diff --git a/test/runtime/value/check/function.ts b/test/runtime/value/check/function.ts new file mode 100644 index 000000000..d7842f3c5 --- /dev/null +++ b/test/runtime/value/check/function.ts @@ -0,0 +1,28 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Function', () => { + it('Should validate function 1', () => { + const T = Type.Function([Type.Number()], Type.Number()) + Assert.IsTrue(Value.Check(T, function () {})) + }) + it('Should validate function 2', () => { + const T = Type.Function([Type.Number()], Type.Number()) + // note: validation only checks typeof 'function' + Assert.IsTrue(Value.Check(T, function (a: string, b: string, c: string, d: string) {})) + }) + it('Should validate function 3', () => { + const T = Type.Function([Type.Number()], Type.Number()) + // note: validation only checks typeof 'function' + Assert.IsTrue( + Value.Check(T, function () { + return 'not-a-number' + }), + ) + }) + it('Should not validate function', () => { + const T = Type.Function([Type.Number()], Type.Number()) + Assert.IsFalse(Value.Check(T, 1)) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index a55d53a0b..7b51cf3bf 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -4,8 +4,10 @@ import './async-iterator' import './bigint' import './boolean' import './composite' +import './constructor' import './date' import './enum' +import './function' import './integer' import './intersect' import './iterator' From 483a0f7093be6e970a1043653c1c819af02af31c Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 19 Aug 2023 15:41:58 +0900 Subject: [PATCH 174/369] Documentation --- readme.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index d19ccfd62..582456434 100644 --- a/readme.md +++ b/readme.md @@ -60,7 +60,9 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime checked using standard Json Schema validation. -TypeBox is designed to be a runtime type system based on industry standard specifications. It offers serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, a high performance runtime validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. It can either be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. +TypeBox is built upon industry standard specifications. It offers reflectable, serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, includes a high performance validation compiler, offers various tools for working with dynamic data and provides detailed structured error reporting. + +TypeBox can be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. License MIT @@ -1167,9 +1169,9 @@ const Z = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const Z = { x: 1, y: 2 } Use the Decode function to decode a value from a type, or throw if the value is invalid. The return value will infer as the decoded type. This function will run Transform codecs if available. ```typescript -const A = Type.Decode(Type.String(), 'hello') // const A = 'hello' +const A = Value.Decode(Type.String(), 'hello') // const A = 'hello' -const B = Type.Decode(Type.String(), 42) // throw +const B = Value.Decode(Type.String(), 42) // throw ``` @@ -1178,9 +1180,9 @@ const B = Type.Decode(Type.String(), 42) // throw Use the Encode function to encode a value to a type, or throw if the value is invalid. The return value will infer as the encoded type. This function will run Transform codecs if available. ```typescript -const A = Type.Encode(Type.String(), 'hello') // const A = 'hello' +const A = Value.Encode(Type.String(), 'hello') // const A = 'hello' -const B = Type.Encode(Type.String(), 42) // throw +const B = Value.Encode(Type.String(), 42) // throw ``` From d758fb5846a7b80f9b3f8281786c86f32f4a64d0 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 23 Aug 2023 04:32:17 +0900 Subject: [PATCH 175/369] Escape for Single Quote Literal Value (#540) --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 10 +++++++++- test/runtime/compiler/literal.ts | 11 +++++++++++ test/runtime/schema/literal.ts | 11 +++++++++++ 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index efe7b622e..2b0ccba85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.0", + "version": "0.31.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.0", + "version": "0.31.2", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index fbe8febe3..7b85f51eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.1", + "version": "0.31.2", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index d469ccde3..25c463a9f 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -129,6 +129,14 @@ namespace Identifier { } } // ------------------------------------------------------------------- +// LiteralString +// ------------------------------------------------------------------- +namespace LiteralString { + export function Escape(content: string) { + return content.replace(/'/g, "\\'") + } +} +// ------------------------------------------------------------------- // Errors // ------------------------------------------------------------------- export class TypeCompilerUnknownTypeError extends Types.TypeBoxError { @@ -263,7 +271,7 @@ export namespace TypeCompiler { if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { yield `(${value} === ${schema.const})` } else { - yield `(${value} === '${schema.const}')` + yield `(${value} === '${LiteralString.Escape(schema.const)}')` } } function* TNever(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { diff --git a/test/runtime/compiler/literal.ts b/test/runtime/compiler/literal.ts index d2d17155a..5cd3e289b 100644 --- a/test/runtime/compiler/literal.ts +++ b/test/runtime/compiler/literal.ts @@ -36,4 +36,15 @@ describe('compiler/Literal', () => { Fail(T, 43) Fail(T, 'world') }) + // reference: https://github.com/sinclairzx81/typebox/issues/539 + it('Should escape single quote literals', () => { + const T = Type.Literal("it's") + Ok(T, "it's") + Fail(T, "it''s") + }) + it('Should escape multiple single quote literals', () => { + const T = Type.Literal("'''''''''") + Ok(T, "'''''''''") + Fail(T, "''''''''") // minus 1 + }) }) diff --git a/test/runtime/schema/literal.ts b/test/runtime/schema/literal.ts index d517697d3..d14d5b784 100644 --- a/test/runtime/schema/literal.ts +++ b/test/runtime/schema/literal.ts @@ -36,4 +36,15 @@ describe('type/schema/Literal', () => { Fail(T, 43) Fail(T, 'world') }) + // reference: https://github.com/sinclairzx81/typebox/issues/539 + it('Should escape single quote literals', () => { + const T = Type.Literal("it's") + Ok(T, "it's") + Fail(T, "it''s") + }) + it('Should escape multiple single quote literals', () => { + const T = Type.Literal("'''''''''") + Ok(T, "'''''''''") + Fail(T, "''''''''") // minus 1 + }) }) From f005478de11dda8db5c274273c334c04398085cf Mon Sep 17 00:00:00 2001 From: Hamish Cox <20359560+HamishWHC@users.noreply.github.com> Date: Fri, 25 Aug 2023 15:23:24 +1000 Subject: [PATCH 176/369] Decode Modifiers (#544) * Handle TOptionals in DecodeStaticType Fixes #543 * Readonly and Optional --------- Co-authored-by: sinclair --- src/typebox.ts | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index 412d6412d..b4538363b 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -855,30 +855,36 @@ export interface TTemplateLiteral = { - [K in keyof T]: DecodeStaticType +export type DecodeModifier = + T extends TReadonly & TOptional ? TReadonly> : + T extends TReadonly ? TReadonly : + T extends TOptional ? TOptional : + D +// prettier-ignore +export type DecodeProperties = { + [K in keyof T]: DecodeType } // prettier-ignore -export type DecodeStaticRest = T extends [infer L, ...infer R] - ? [DecodeStaticType>, ...DecodeStaticRest>] +export type DecodeRest = T extends [infer L, ...infer R] + ? [DecodeType>, ...DecodeRest>] : [] // prettier-ignore -export type DecodeStaticType = - T extends TTransform ? TUnsafe : - T extends TArray ? TArray> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor>, DecodeStaticType> : - T extends TFunction ? TFunction>, DecodeStaticType> : - T extends TIntersect ? TIntersect>> : - T extends TIterator ? TIterator> : - T extends TNot ? TNot> : - T extends TObject ? TObject>> : - T extends TPromise ? TPromise> : - T extends TRecord ? TRecord> : - T extends TRecursive ? TRecursive> : - T extends TRef ? TRef> : - T extends TTuple ? TTuple>> : - T extends TUnion ? TUnion>> : +export type DecodeType = + T extends TTransform ? DecodeModifier, T> : + T extends TArray ? DecodeModifier>, T> : + T extends TAsyncIterator ? DecodeModifier>, T> : + T extends TConstructor ? DecodeModifier>, DecodeType>, T> : + T extends TFunction ? DecodeModifier>, DecodeType>, T> : + T extends TIntersect ? DecodeModifier>>, T> : + T extends TIterator ? DecodeModifier>, T> : + T extends TNot ? DecodeModifier>, T> : + T extends TObject ? DecodeModifier>>, T> : + T extends TPromise ? DecodeModifier>, T> : + T extends TRecord ? DecodeModifier>, T> : + T extends TRecursive ? DecodeModifier>, T> : + T extends TRef ? DecodeModifier>, T> : + T extends TTuple ? DecodeModifier>>, T> : + T extends TUnion ? DecodeModifier>>, T> : T export type TransformFunction = (value: T) => U export interface TransformOptions { @@ -974,7 +980,7 @@ export interface TVoid extends TSchema { // Static // -------------------------------------------------------------------------- /** Creates the decoded static form for a TypeBox type */ -export type StaticDecode = Static, P> +export type StaticDecode = Static, P> /** Creates the encoded static form for a TypeBox type */ export type StaticEncode = Static /** Creates the static type for a TypeBox type */ From 0762bd31a7e33c8702b2e8031d6ec6dffdae6d07 Mon Sep 17 00:00:00 2001 From: Sjors Smits Date: Fri, 25 Aug 2023 07:48:12 +0200 Subject: [PATCH 177/369] Exterior RegExp Encode (#542) --- src/compiler/compiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 25c463a9f..0c9d2b958 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -330,7 +330,7 @@ export namespace TypeCompiler { if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] - const variable = CreateVariable(`new RegExp(/${patternKey}/)`) + const variable = CreateVariable(`${new RegExp(patternKey)}`) const check1 = CreateExpression(patternSchema, references, 'value') const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' const expression = `(${variable}.test(key) ? ${check1} : ${check2})` From 6e0b99937e3d70448a86dd6c58de6cbf37598dd5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 25 Aug 2023 16:07:45 +0900 Subject: [PATCH 178/369] Revision 0.31.3 (#545) --- hammer.mjs | 2 +- package-lock.json | 18 +++++------ package.json | 4 +-- readme.md | 7 +++-- test/runtime/compiler/record.ts | 22 +++++++++++-- test/runtime/index.ts | 2 +- test/runtime/schema/any.ts | 2 +- test/runtime/schema/array.ts | 2 +- test/runtime/schema/boolean.ts | 2 +- test/runtime/schema/composite.ts | 2 +- test/runtime/schema/date.ts | 2 +- test/runtime/schema/enum.ts | 2 +- test/runtime/schema/integer.ts | 2 +- test/runtime/schema/intersect.ts | 2 +- test/runtime/schema/keyof.ts | 3 +- test/runtime/schema/literal.ts | 2 +- test/runtime/schema/never.ts | 2 +- test/runtime/schema/not.ts | 2 +- test/runtime/schema/null.ts | 2 +- test/runtime/schema/number.ts | 2 +- test/runtime/schema/object.ts | 2 +- test/runtime/schema/omit.ts | 2 +- test/runtime/schema/optional.ts | 2 +- test/runtime/schema/partial.ts | 22 ++++++------- test/runtime/schema/pick.ts | 12 +++---- test/runtime/schema/readonly-optional.ts | 8 ++--- test/runtime/schema/readonly.ts | 10 +++--- test/runtime/schema/record.ts | 24 ++++++++++++-- test/runtime/schema/recursive.ts | 6 ++-- test/runtime/schema/ref.ts | 3 +- test/runtime/schema/regexp.ts | 2 +- test/runtime/schema/required.ts | 16 +++++----- test/runtime/schema/string.ts | 2 +- test/runtime/schema/template-literal.ts | 2 +- test/runtime/schema/tuple.ts | 3 +- test/runtime/schema/uint8array.ts | 2 +- test/runtime/schema/union.ts | 2 +- test/runtime/schema/unknown.ts | 4 +-- test/runtime/schema/unsafe.ts | 2 +- test/runtime/schema/void.ts | 2 +- test/runtime/value/check/record.ts | 40 ++++++++++++++++++++++++ test/static/transform.ts | 32 +++++++++++++++++++ 42 files changed, 196 insertions(+), 88 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index c99da14f9..76e8ea38e 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -36,7 +36,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 2b0ccba85..283e9a681 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.2", + "version": "0.31.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.2", + "version": "0.31.3", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", @@ -16,7 +16,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.1.6" + "typescript": "^5.2.2" } }, "node_modules/@esbuild/android-arm": { @@ -1334,9 +1334,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2304,9 +2304,9 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 7b85f51eb..7ceb60964 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.2", + "version": "0.31.3", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -59,6 +59,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.1.6" + "typescript": "^5.2.2" } } diff --git a/readme.md b/readme.md index 582456434..05345fec0 100644 --- a/readme.md +++ b/readme.md @@ -60,7 +60,7 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime checked using standard Json Schema validation. -TypeBox is built upon industry standard specifications. It offers reflectable, serializable and publishable types as standard, a fully extensible type system capable of supporting multiple schema specifications, includes a high performance validation compiler, offers various tools for working with dynamic data and provides detailed structured error reporting. +TypeBox types are designed to express industry standard schematics as TypeScript types. All types are runtime reflectable, serializable and publishable by default. It includes an extensible type system able to represent type safe schematics for multiple schema specifications. It also provides a high performance validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. TypeBox can be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. @@ -949,7 +949,7 @@ const U = Type.Union(R) // const U = { ### Transform Types -TypeBox supports bi-directional decode and encode with Transform types. These types are designed to operate with the Value and TypeCompiler Encode and Decode functions. Transform types are useful to convert Json encoded values into constructs more natural to JavaScript. The following creates a Transform type to convert between Date and number using the Value module. +TypeBox supports value decoding and encoding with Transform types. These types work in tandem with the Encode and Decode functions available on the Value and TypeCompiler modules. Transform types can be used to convert Json encoded values into constructs more natural to JavaScript. The following creates a Transform type to decode numbers into Dates using the Value module. ```typescript import { Value } from '@sinclair/typebox/value' @@ -1604,13 +1604,16 @@ The following is a list of community packages that offer general tooling, extend | Package | Description | | ------------- | ------------- | +| [drizzle-typebox](https://www.npmjs.com/package/drizzle-typebox) | Generates TypeBox types from Drizzle ORM schemas | | [elysia](https://github.com/elysiajs/elysia) | Fast and friendly Bun web framework | | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | +| [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | +| [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index cd9322b08..89368d724 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -85,7 +85,7 @@ describe('compiler/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) - it('Should should validate when specifying regular expressions', () => { + it('Should validate when specifying regular expressions', () => { const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number()) Ok(T, { @@ -94,7 +94,7 @@ describe('compiler/Record', () => { op_c: 3, }) }) - it('Should should not validate when specifying regular expressions and passing invalid property', () => { + it('Should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { @@ -103,6 +103,24 @@ describe('compiler/Record', () => { aop_c: 3, }) }) + it('Should validate with quoted string pattern', () => { + const K = Type.String({ pattern: "'(a|b|c)" }) + const T = Type.Record(K, Type.Number()) + Ok(T, { + "'a": 1, + "'b": 2, + "'c": 3, + }) + }) + it('Should validate with forward-slash pattern', () => { + const K = Type.String({ pattern: '/(a|b|c)' }) + const T = Type.Record(K, Type.Number()) + Ok(T, { + '/a': 1, + '/b': 2, + '/c': 3, + }) + }) // ------------------------------------------------------------ // Integer Keys // ------------------------------------------------------------ diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 5a8bca7ab..8f53de837 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -1,6 +1,6 @@ import './compiler/index' -import './errors/index' import './schema/index' +import './errors/index' import './system/index' import './type/index' import './value/index' diff --git a/test/runtime/schema/any.ts b/test/runtime/schema/any.ts index 3e857c831..695113f09 100644 --- a/test/runtime/schema/any.ts +++ b/test/runtime/schema/any.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok } from './validate' -describe('type/schema/Any', () => { +describe('compiler-ajv/Any', () => { it('Should validate number', () => { const T = Type.Any() Ok(T, 1) diff --git a/test/runtime/schema/array.ts b/test/runtime/schema/array.ts index d20e504c4..dd9feaea4 100644 --- a/test/runtime/schema/array.ts +++ b/test/runtime/schema/array.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Array', () => { +describe('compiler-ajv/Array', () => { it('Should validate an array of any', () => { const T = Type.Array(Type.Any()) Ok(T, [0, true, 'hello', {}]) diff --git a/test/runtime/schema/boolean.ts b/test/runtime/schema/boolean.ts index f8f75a55e..6ff4f7861 100644 --- a/test/runtime/schema/boolean.ts +++ b/test/runtime/schema/boolean.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Boolean', () => { +describe('compiler-ajv/Boolean', () => { it('Should validate a boolean', () => { const T = Type.Boolean() Ok(T, true) diff --git a/test/runtime/schema/composite.ts b/test/runtime/schema/composite.ts index 23e5d1055..41e8a8ac9 100644 --- a/test/runtime/schema/composite.ts +++ b/test/runtime/schema/composite.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../assert' import { Ok, Fail } from './validate' -describe('type/schema/Composite', () => { +describe('compiler-ajv/Composite', () => { it('Should compose two objects', () => { const A = Type.Object({ a: Type.String() }) const B = Type.Object({ b: Type.Number() }) diff --git a/test/runtime/schema/date.ts b/test/runtime/schema/date.ts index 659c704b9..f5f8e12fc 100644 --- a/test/runtime/schema/date.ts +++ b/test/runtime/schema/date.ts @@ -10,7 +10,7 @@ // which are configured to use Value.Check() // ---------------------------------------------------- -// describe('type/schema/Date', () => { +// describe('compiler-ajv/Date', () => { // it('Should not validate number', () => { // const T = Type.Date() // Fail(T, 1) diff --git a/test/runtime/schema/enum.ts b/test/runtime/schema/enum.ts index 910e16e27..c4599be98 100644 --- a/test/runtime/schema/enum.ts +++ b/test/runtime/schema/enum.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Enum', () => { +describe('compiler-ajv/Enum', () => { it('Should validate when emum uses default numeric values', () => { enum Kind { Foo, // = 0 diff --git a/test/runtime/schema/integer.ts b/test/runtime/schema/integer.ts index 5d984691f..664eacf7b 100644 --- a/test/runtime/schema/integer.ts +++ b/test/runtime/schema/integer.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Integer', () => { +describe('compiler-ajv/Integer', () => { it('Should not validate number', () => { const T = Type.Integer() Fail(T, 3.14) diff --git a/test/runtime/schema/intersect.ts b/test/runtime/schema/intersect.ts index 5e4b64a4f..00fe0ac1a 100644 --- a/test/runtime/schema/intersect.ts +++ b/test/runtime/schema/intersect.ts @@ -1,7 +1,7 @@ import { Type, Static } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Intersect', () => { +describe('compiler-ajv/Intersect', () => { it('Should intersect number and number', () => { const A = Type.Number() const B = Type.Number() diff --git a/test/runtime/schema/keyof.ts b/test/runtime/schema/keyof.ts index 17b36914f..0734a2f93 100644 --- a/test/runtime/schema/keyof.ts +++ b/test/runtime/schema/keyof.ts @@ -1,8 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' -describe('type/schema/KeyOf', () => { +describe('compiler-ajv/KeyOf', () => { it('Should validate with all object keys as a kind of union', () => { const T = Type.KeyOf( Type.Object({ diff --git a/test/runtime/schema/literal.ts b/test/runtime/schema/literal.ts index d14d5b784..a41d69044 100644 --- a/test/runtime/schema/literal.ts +++ b/test/runtime/schema/literal.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Literal', () => { +describe('compiler-ajv/Literal', () => { it('Should validate literal number', () => { const T = Type.Literal(42) Ok(T, 42) diff --git a/test/runtime/schema/never.ts b/test/runtime/schema/never.ts index 8a3f6e386..49904f4eb 100644 --- a/test/runtime/schema/never.ts +++ b/test/runtime/schema/never.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Fail } from './validate' -describe('type/schema/Never', () => { +describe('compiler-ajv/Never', () => { it('Should not validate number', () => { const T = Type.Never() Fail(T, 1) diff --git a/test/runtime/schema/not.ts b/test/runtime/schema/not.ts index 491de05b4..013cf28a5 100644 --- a/test/runtime/schema/not.ts +++ b/test/runtime/schema/not.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Not', () => { +describe('compiler-ajv/Not', () => { it('Should validate not number', () => { const T = Type.Not(Type.Number()) Fail(T, 1) diff --git a/test/runtime/schema/null.ts b/test/runtime/schema/null.ts index b338d78eb..4947b0cf9 100644 --- a/test/runtime/schema/null.ts +++ b/test/runtime/schema/null.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Null', () => { +describe('compiler-ajv/Null', () => { it('Should not validate number', () => { const T = Type.Null() Fail(T, 1) diff --git a/test/runtime/schema/number.ts b/test/runtime/schema/number.ts index 880a85e4a..30dd7fb8a 100644 --- a/test/runtime/schema/number.ts +++ b/test/runtime/schema/number.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Number', () => { +describe('compiler-ajv/Number', () => { it('Should validate number', () => { const T = Type.Number() Ok(T, 3.14) diff --git a/test/runtime/schema/object.ts b/test/runtime/schema/object.ts index cabe95979..c096bc546 100644 --- a/test/runtime/schema/object.ts +++ b/test/runtime/schema/object.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Object', () => { +describe('compiler-ajv/Object', () => { it('Should not validate a number', () => { const T = Type.Object({}) Fail(T, 42) diff --git a/test/runtime/schema/omit.ts b/test/runtime/schema/omit.ts index 7a1d6e108..0053c6808 100644 --- a/test/runtime/schema/omit.ts +++ b/test/runtime/schema/omit.ts @@ -2,7 +2,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' -describe('type/schema/Omit', () => { +describe('compiler-ajv/Omit', () => { it('Should omit properties on the source schema', () => { const A = Type.Object( { diff --git a/test/runtime/schema/optional.ts b/test/runtime/schema/optional.ts index 298550f73..a5d39dd6f 100644 --- a/test/runtime/schema/optional.ts +++ b/test/runtime/schema/optional.ts @@ -2,7 +2,7 @@ import { strictEqual } from 'assert' import { Type } from '@sinclair/typebox' import { Ok } from './validate' -describe('type/schema/Optional', () => { +describe('compiler-ajv/Optional', () => { it('Should validate object with optional', () => { const T = Type.Object( { diff --git a/test/runtime/schema/partial.ts b/test/runtime/schema/partial.ts index c28f1368e..5e56d704a 100644 --- a/test/runtime/schema/partial.ts +++ b/test/runtime/schema/partial.ts @@ -1,8 +1,8 @@ import { Type, Readonly, Optional } from '@sinclair/typebox' -import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' +import { Ok } from './validate' +import { Assert } from '../assert' -describe('type/schema/Partial', () => { +describe('compiler-ajv/Partial', () => { it('Should convert a required object into a partial.', () => { const A = Type.Object( { @@ -29,12 +29,12 @@ describe('type/schema/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.x[Optional], 'Optional') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.y[Optional], 'Optional') - strictEqual(T.properties.z[Optional], 'Optional') - strictEqual(T.properties.w[Optional], 'Optional') + Assert.IsEqual(T.properties.x[Readonly], 'Readonly') + Assert.IsEqual(T.properties.x[Optional], 'Optional') + Assert.IsEqual(T.properties.y[Readonly], 'Readonly') + Assert.IsEqual(T.properties.y[Optional], 'Optional') + Assert.IsEqual(T.properties.z[Optional], 'Optional') + Assert.IsEqual(T.properties.w[Optional], 'Optional') }) it('Should inherit options from the source object', () => { const A = Type.Object( @@ -46,7 +46,7 @@ describe('type/schema/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(A.additionalProperties, false) - strictEqual(T.additionalProperties, false) + Assert.IsEqual(A.additionalProperties, false) + Assert.IsEqual(T.additionalProperties, false) }) }) diff --git a/test/runtime/schema/pick.ts b/test/runtime/schema/pick.ts index 39dde3a06..e9c09c700 100644 --- a/test/runtime/schema/pick.ts +++ b/test/runtime/schema/pick.ts @@ -1,8 +1,8 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' +import { Assert } from '../assert' -describe('type/schema/Pick', () => { +describe('compiler-ajv/Pick', () => { it('Should pick properties from the source schema', () => { const Vector3 = Type.Object( { @@ -25,7 +25,7 @@ describe('type/schema/Pick', () => { { additionalProperties: false }, ) const T = Type.Pick(A, ['x', 'y']) - strictEqual(T.required!.includes('z'), false) + Assert.IsEqual(T.required!.includes('z'), false) }) it('Should delete the required property if no required properties remain', () => { const A = Type.Object( @@ -37,7 +37,7 @@ describe('type/schema/Pick', () => { { additionalProperties: false }, ) const T = Type.Pick(A, ['x', 'y']) - strictEqual(T.required, undefined) + Assert.IsEqual(T.required, undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( @@ -49,8 +49,8 @@ describe('type/schema/Pick', () => { { additionalProperties: false }, ) const T = Type.Pick(A, ['x', 'y']) - strictEqual(A.additionalProperties, false) - strictEqual(T.additionalProperties, false) + Assert.IsEqual(A.additionalProperties, false) + Assert.IsEqual(T.additionalProperties, false) }) it('Should pick with keyof object', () => { const A = Type.Object({ diff --git a/test/runtime/schema/readonly-optional.ts b/test/runtime/schema/readonly-optional.ts index 6025c3cf1..cbdd883ff 100644 --- a/test/runtime/schema/readonly-optional.ts +++ b/test/runtime/schema/readonly-optional.ts @@ -1,8 +1,8 @@ import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' +import { Ok } from './validate' +import { Assert } from '../assert' -describe('type/schema/ReadonlyOptional', () => { +describe('compiler-ajv/ReadonlyOptional', () => { it('Should validate object with optional', () => { const T = Type.Object( { @@ -22,6 +22,6 @@ describe('type/schema/ReadonlyOptional', () => { }, { additionalProperties: false }, ) - strictEqual(T.required!.includes('a'), false) + Assert.IsEqual(T.required!.includes('a'), false) }) }) diff --git a/test/runtime/schema/readonly.ts b/test/runtime/schema/readonly.ts index a651d3af3..c26dd62f6 100644 --- a/test/runtime/schema/readonly.ts +++ b/test/runtime/schema/readonly.ts @@ -1,8 +1,8 @@ -import { deepStrictEqual, strictEqual } from 'assert' import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' +import { Ok } from './validate' +import { Assert } from '../assert' -describe('type/schema/Readonly', () => { +describe('compiler-ajv/Readonly', () => { it('Should validate object with readonly', () => { const T = Type.Object( { @@ -21,7 +21,7 @@ describe('type/schema/Readonly', () => { }, { additionalProperties: false }, ) - strictEqual(T.required!.includes('a'), true) - strictEqual(T.required!.includes('b'), true) + Assert.IsEqual(T.required!.includes('a'), true) + Assert.IsEqual(T.required!.includes('b'), true) }) }) diff --git a/test/runtime/schema/record.ts b/test/runtime/schema/record.ts index 6a6ba818c..082a58fb2 100644 --- a/test/runtime/schema/record.ts +++ b/test/runtime/schema/record.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Record', () => { +describe('compiler-ajv/Record', () => { it('Should validate when all property values are numbers', () => { const T = Type.Record(Type.String(), Type.Number()) Ok(T, { a: 1, b: 2, c: 3 }) @@ -54,7 +54,7 @@ describe('type/schema/Record', () => { const R = Type.Record(Type.KeyOf(T), Type.Number(), { additionalProperties: false }) Fail(R, { a: 1, b: 2, c: 3, d: 4 }) }) - it('Should should validate when specifying regular expressions', () => { + it('Should validate when specifying regular expressions', () => { const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number()) Ok(T, { @@ -63,7 +63,7 @@ describe('type/schema/Record', () => { op_c: 3, }) }) - it('Should should not validate when specifying regular expressions and passing invalid property', () => { + it('Should not validate when specifying regular expressions and passing invalid property', () => { const K = Type.RegExp(/^op_.*$/) const T = Type.Record(K, Type.Number(), { additionalProperties: false }) Fail(T, { @@ -72,6 +72,24 @@ describe('type/schema/Record', () => { aop_c: 3, }) }) + it('Should validate with quoted string pattern', () => { + const K = Type.String({ pattern: "'(a|b|c)" }) + const T = Type.Record(K, Type.Number()) + Ok(T, { + "'a": 1, + "'b": 2, + "'c": 3, + }) + }) + it('Should validate with forward-slash pattern', () => { + const K = Type.String({ pattern: '/(a|b|c)' }) + const T = Type.Record(K, Type.Number()) + Ok(T, { + '/a': 1, + '/b': 2, + '/c': 3, + }) + }) // ------------------------------------------------------------ // Integer Keys // ------------------------------------------------------------ diff --git a/test/runtime/schema/recursive.ts b/test/runtime/schema/recursive.ts index b2f90d756..0a04dc313 100644 --- a/test/runtime/schema/recursive.ts +++ b/test/runtime/schema/recursive.ts @@ -1,8 +1,8 @@ import { Type } from '@sinclair/typebox' -import { Assert } from '../assert/index' import { Ok, Fail } from './validate' +import { Assert } from '../assert/index' -describe('type/schema/Recursive', () => { +describe('compiler-ajv/Recursive', () => { it('Should generate default ordinal $id if not specified', () => { const Node = Type.Recursive((Node) => Type.Object({ @@ -10,7 +10,7 @@ describe('type/schema/Recursive', () => { nodes: Type.Array(Node), }), ) - Assert.IsEqual(Node.$id === undefined, false) + Assert.IsFalse(Node.$id === undefined) }) it('Should override default ordinal $id if specified', () => { const Node = Type.Recursive( diff --git a/test/runtime/schema/ref.ts b/test/runtime/schema/ref.ts index a3d522f89..d9bf5c954 100644 --- a/test/runtime/schema/ref.ts +++ b/test/runtime/schema/ref.ts @@ -1,8 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { Assert } from '../assert/index' -describe('type/schema/Ref', () => { +describe('compiler-ajv/Ref', () => { it('Should should validate when referencing a type', () => { const T = Type.Object( { diff --git a/test/runtime/schema/regexp.ts b/test/runtime/schema/regexp.ts index 643b43b81..dcfae6e8d 100644 --- a/test/runtime/schema/regexp.ts +++ b/test/runtime/schema/regexp.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/RegExp', () => { +describe('compiler-ajv/RegExp', () => { //----------------------------------------------------- // Regular Expression //----------------------------------------------------- diff --git a/test/runtime/schema/required.ts b/test/runtime/schema/required.ts index 19ab20f5c..7df4b1875 100644 --- a/test/runtime/schema/required.ts +++ b/test/runtime/schema/required.ts @@ -1,8 +1,8 @@ import { Type, Readonly, Optional } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { strictEqual } from 'assert' +import { Assert } from '../assert' -describe('type/schema/Required', () => { +describe('compiler-ajv/Required', () => { it('Should convert a partial object into a required object', () => { const A = Type.Object( { @@ -26,10 +26,10 @@ describe('type/schema/Required', () => { w: Type.Number(), }) const T = Type.Required(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.z[Optional], undefined) - strictEqual(T.properties.w[Optional], undefined) + Assert.IsEqual(T.properties.x[Readonly], 'Readonly') + Assert.IsEqual(T.properties.y[Readonly], 'Readonly') + Assert.IsEqual(T.properties.z[Optional], undefined) + Assert.IsEqual(T.properties.w[Optional], undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( @@ -41,8 +41,8 @@ describe('type/schema/Required', () => { { additionalPropeties: false }, ) const T = Type.Required(A) - strictEqual(A.additionalPropeties, false) - strictEqual(T.additionalPropeties, false) + Assert.IsEqual(A.additionalPropeties, false) + Assert.IsEqual(T.additionalPropeties, false) }) // it('Should construct new object when targetting reference', () => { // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) diff --git a/test/runtime/schema/string.ts b/test/runtime/schema/string.ts index 73b62efa2..33729175e 100644 --- a/test/runtime/schema/string.ts +++ b/test/runtime/schema/string.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/String', () => { +describe('compiler-ajv/String', () => { it('Should not validate number', () => { const T = Type.String() Fail(T, 1) diff --git a/test/runtime/schema/template-literal.ts b/test/runtime/schema/template-literal.ts index 80c862b58..ad083a50b 100644 --- a/test/runtime/schema/template-literal.ts +++ b/test/runtime/schema/template-literal.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/TemplateLiteral', () => { +describe('compiler-ajv/TemplateLiteral', () => { // -------------------------------------------------------- // Finite // -------------------------------------------------------- diff --git a/test/runtime/schema/tuple.ts b/test/runtime/schema/tuple.ts index 22f9999dc..96dfc1aa3 100644 --- a/test/runtime/schema/tuple.ts +++ b/test/runtime/schema/tuple.ts @@ -1,8 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -import { Assert } from '../assert' -describe('type/schema/Tuple', () => { +describe('compiler-ajv/Tuple', () => { it('Should validate tuple of [string, number]', () => { const A = Type.String() const B = Type.Number() diff --git a/test/runtime/schema/uint8array.ts b/test/runtime/schema/uint8array.ts index 96e2dd39c..b50979504 100644 --- a/test/runtime/schema/uint8array.ts +++ b/test/runtime/schema/uint8array.ts @@ -3,7 +3,7 @@ // --------------------------------------------------- // import { Type } from '@sinclair/typebox' // import { Ok, Fail } from './validate' -// describe('type/schema/Uint8Array', () => { +// describe('compiler-ajv/Uint8Array', () => { // it('Should not validate number', () => { // const T = Type.Uint8Array() // Fail(T, 1) diff --git a/test/runtime/schema/union.ts b/test/runtime/schema/union.ts index fc6493c91..ab18c1e18 100644 --- a/test/runtime/schema/union.ts +++ b/test/runtime/schema/union.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Union', () => { +describe('compiler-ajv/Union', () => { it('Should validate union of string, number and boolean', () => { const A = Type.String() const B = Type.Number() diff --git a/test/runtime/schema/unknown.ts b/test/runtime/schema/unknown.ts index 2bc681767..8ad7cfacf 100644 --- a/test/runtime/schema/unknown.ts +++ b/test/runtime/schema/unknown.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' -import { Ok, Fail } from './validate' +import { Ok } from './validate' -describe('type/schema/Unknown', () => { +describe('compiler-ajv/Unknown', () => { it('Should validate number', () => { const T = Type.Any() Ok(T, 1) diff --git a/test/runtime/schema/unsafe.ts b/test/runtime/schema/unsafe.ts index 509d2d4fb..6a53eea0a 100644 --- a/test/runtime/schema/unsafe.ts +++ b/test/runtime/schema/unsafe.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Unsafe', () => { +describe('compiler-ajv/Unsafe', () => { it('Should validate an unsafe type', () => { const T = Type.Unsafe({ type: 'object', diff --git a/test/runtime/schema/void.ts b/test/runtime/schema/void.ts index 1aab66be4..547ead1ae 100644 --- a/test/runtime/schema/void.ts +++ b/test/runtime/schema/void.ts @@ -1,7 +1,7 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('type/schema/Void', () => { +describe('compiler-ajv/Void', () => { it('Should not validate number', () => { const T = Type.Void() Fail(T, 1) diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index c6e04ce8d..2e4bd7362 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -121,6 +121,46 @@ describe('value/check/Record', () => { const result = Value.Check(T, value) Assert.IsEqual(result, true) }) + it('Should validate when specifying regular expressions', () => { + const K = Type.RegExp(/^op_.*$/) + const T = Type.Record(K, Type.Number()) + const R = Value.Check(T, { + op_a: 1, + op_b: 2, + op_c: 3, + }) + Assert.IsTrue(R) + }) + it('Should not validate when specifying regular expressions and passing invalid property', () => { + const K = Type.RegExp(/^op_.*$/) + const T = Type.Record(K, Type.Number(), { additionalProperties: false }) + const R = Value.Check(T, { + op_a: 1, + op_b: 2, + aop_c: 3, + }) + Assert.IsFalse(R) + }) + it('Should validate with quoted string pattern', () => { + const K = Type.String({ pattern: "'(a|b|c)" }) + const T = Type.Record(K, Type.Number()) + const R = Value.Check(T, { + "'a": 1, + "'b": 2, + "'c": 3, + }) + Assert.IsTrue(R) + }) + it('Should validate with forward-slash pattern', () => { + const K = Type.String({ pattern: '/(a|b|c)' }) + const T = Type.Record(K, Type.Number()) + const R = Value.Check(T, { + '/a': 1, + '/b': 2, + '/c': 3, + }) + Assert.IsTrue(R) + }) // ------------------------------------------------- // Number Key // ------------------------------------------------- diff --git a/test/static/transform.ts b/test/static/transform.ts index 99e7e067a..3e02df125 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -182,3 +182,35 @@ import { Expect } from './assert' // z: number; // } // lol } +{ + // ensure decode as optional + // prettier-ignore + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()) + }) + Expect(T).ToStaticDecode<{ x: undefined; y: undefined }>() + Expect(T).ToStaticDecode<{ x: 1; y: 1 }>() +} +{ + // ensure decode as readonly + // prettier-ignore + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Readonly(Type.Number()) + }) + Expect(T).ToStaticDecode<{ readonly x: 1; readonly y: 1 }>() +} +{ + // ensure decode as optional union + // prettier-ignore + const T = Type.Object({ + x: Type.Optional(Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Expect(T).ToStaticDecode<{ x: 1 }>() + Expect(T).ToStaticDecode<{ x: '1' }>() + Expect(T).ToStaticDecode<{ x: undefined }>() +} From 5a8a0af0d3ec6e00d63571f88951858dac4d494b Mon Sep 17 00:00:00 2001 From: Josh Michaels <31548851+joshmossas@users.noreply.github.com> Date: Sat, 26 Aug 2023 02:09:26 -0500 Subject: [PATCH 179/369] Type inference for Type.Enum (#551) --- src/typebox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typebox.ts b/src/typebox.ts index b4538363b..d33380c12 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2901,7 +2901,7 @@ export class JsonTypeBuilder extends TypeBuilder { return Type.Object(properties, options) as TComposite } /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { + public Enum>(item: T, options: SchemaOptions = {}): TEnum { // prettier-ignore const values = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] // prettier-ignore From 085714eb8c49ec59dcc49ede8c53f584763e0ada Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 26 Aug 2023 16:20:22 +0900 Subject: [PATCH 180/369] Revision 0.31.4 (#550) --- package-lock.json | 4 ++-- package.json | 2 +- src/system/index.ts | 1 + test/runtime/{schema => compiler-ajv}/any.ts | 0 test/runtime/{schema => compiler-ajv}/array.ts | 0 test/runtime/{schema => compiler-ajv}/boolean.ts | 0 test/runtime/{schema => compiler-ajv}/composite.ts | 0 test/runtime/{schema => compiler-ajv}/date.ts | 0 test/runtime/{schema => compiler-ajv}/enum.ts | 0 test/runtime/{schema => compiler-ajv}/index.ts | 0 test/runtime/{schema => compiler-ajv}/integer.ts | 0 test/runtime/{schema => compiler-ajv}/intersect.ts | 0 test/runtime/{schema => compiler-ajv}/keyof.ts | 0 test/runtime/{schema => compiler-ajv}/literal.ts | 0 test/runtime/{schema => compiler-ajv}/never.ts | 0 test/runtime/{schema => compiler-ajv}/not.ts | 0 test/runtime/{schema => compiler-ajv}/null.ts | 0 test/runtime/{schema => compiler-ajv}/number.ts | 0 test/runtime/{schema => compiler-ajv}/object.ts | 0 test/runtime/{schema => compiler-ajv}/omit.ts | 0 test/runtime/{schema => compiler-ajv}/optional.ts | 0 test/runtime/{schema => compiler-ajv}/partial.ts | 0 test/runtime/{schema => compiler-ajv}/pick.ts | 0 test/runtime/{schema => compiler-ajv}/readonly-optional.ts | 0 test/runtime/{schema => compiler-ajv}/readonly.ts | 0 test/runtime/{schema => compiler-ajv}/record.ts | 0 test/runtime/{schema => compiler-ajv}/recursive.ts | 0 test/runtime/{schema => compiler-ajv}/ref.ts | 0 test/runtime/{schema => compiler-ajv}/regexp.ts | 0 test/runtime/{schema => compiler-ajv}/required.ts | 0 test/runtime/{schema => compiler-ajv}/string.ts | 0 test/runtime/{schema => compiler-ajv}/template-literal.ts | 0 test/runtime/{schema => compiler-ajv}/tuple.ts | 0 test/runtime/{schema => compiler-ajv}/uint8array.ts | 0 test/runtime/{schema => compiler-ajv}/union.ts | 0 test/runtime/{schema => compiler-ajv}/unknown.ts | 0 test/runtime/{schema => compiler-ajv}/unsafe.ts | 0 test/runtime/{schema => compiler-ajv}/validate.ts | 0 test/runtime/{schema => compiler-ajv}/void.ts | 0 test/runtime/index.ts | 2 +- 40 files changed, 5 insertions(+), 4 deletions(-) rename test/runtime/{schema => compiler-ajv}/any.ts (100%) rename test/runtime/{schema => compiler-ajv}/array.ts (100%) rename test/runtime/{schema => compiler-ajv}/boolean.ts (100%) rename test/runtime/{schema => compiler-ajv}/composite.ts (100%) rename test/runtime/{schema => compiler-ajv}/date.ts (100%) rename test/runtime/{schema => compiler-ajv}/enum.ts (100%) rename test/runtime/{schema => compiler-ajv}/index.ts (100%) rename test/runtime/{schema => compiler-ajv}/integer.ts (100%) rename test/runtime/{schema => compiler-ajv}/intersect.ts (100%) rename test/runtime/{schema => compiler-ajv}/keyof.ts (100%) rename test/runtime/{schema => compiler-ajv}/literal.ts (100%) rename test/runtime/{schema => compiler-ajv}/never.ts (100%) rename test/runtime/{schema => compiler-ajv}/not.ts (100%) rename test/runtime/{schema => compiler-ajv}/null.ts (100%) rename test/runtime/{schema => compiler-ajv}/number.ts (100%) rename test/runtime/{schema => compiler-ajv}/object.ts (100%) rename test/runtime/{schema => compiler-ajv}/omit.ts (100%) rename test/runtime/{schema => compiler-ajv}/optional.ts (100%) rename test/runtime/{schema => compiler-ajv}/partial.ts (100%) rename test/runtime/{schema => compiler-ajv}/pick.ts (100%) rename test/runtime/{schema => compiler-ajv}/readonly-optional.ts (100%) rename test/runtime/{schema => compiler-ajv}/readonly.ts (100%) rename test/runtime/{schema => compiler-ajv}/record.ts (100%) rename test/runtime/{schema => compiler-ajv}/recursive.ts (100%) rename test/runtime/{schema => compiler-ajv}/ref.ts (100%) rename test/runtime/{schema => compiler-ajv}/regexp.ts (100%) rename test/runtime/{schema => compiler-ajv}/required.ts (100%) rename test/runtime/{schema => compiler-ajv}/string.ts (100%) rename test/runtime/{schema => compiler-ajv}/template-literal.ts (100%) rename test/runtime/{schema => compiler-ajv}/tuple.ts (100%) rename test/runtime/{schema => compiler-ajv}/uint8array.ts (100%) rename test/runtime/{schema => compiler-ajv}/union.ts (100%) rename test/runtime/{schema => compiler-ajv}/unknown.ts (100%) rename test/runtime/{schema => compiler-ajv}/unsafe.ts (100%) rename test/runtime/{schema => compiler-ajv}/validate.ts (100%) rename test/runtime/{schema => compiler-ajv}/void.ts (100%) diff --git a/package-lock.json b/package-lock.json index 283e9a681..8cb18ccff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.3", + "version": "0.31.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.3", + "version": "0.31.4", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7ceb60964..475121baf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.3", + "version": "0.31.4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/system/index.ts b/src/system/index.ts index 457186e0b..21c978add 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -26,4 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +export { ValueErrorType } from '../errors/errors' export * from './system' diff --git a/test/runtime/schema/any.ts b/test/runtime/compiler-ajv/any.ts similarity index 100% rename from test/runtime/schema/any.ts rename to test/runtime/compiler-ajv/any.ts diff --git a/test/runtime/schema/array.ts b/test/runtime/compiler-ajv/array.ts similarity index 100% rename from test/runtime/schema/array.ts rename to test/runtime/compiler-ajv/array.ts diff --git a/test/runtime/schema/boolean.ts b/test/runtime/compiler-ajv/boolean.ts similarity index 100% rename from test/runtime/schema/boolean.ts rename to test/runtime/compiler-ajv/boolean.ts diff --git a/test/runtime/schema/composite.ts b/test/runtime/compiler-ajv/composite.ts similarity index 100% rename from test/runtime/schema/composite.ts rename to test/runtime/compiler-ajv/composite.ts diff --git a/test/runtime/schema/date.ts b/test/runtime/compiler-ajv/date.ts similarity index 100% rename from test/runtime/schema/date.ts rename to test/runtime/compiler-ajv/date.ts diff --git a/test/runtime/schema/enum.ts b/test/runtime/compiler-ajv/enum.ts similarity index 100% rename from test/runtime/schema/enum.ts rename to test/runtime/compiler-ajv/enum.ts diff --git a/test/runtime/schema/index.ts b/test/runtime/compiler-ajv/index.ts similarity index 100% rename from test/runtime/schema/index.ts rename to test/runtime/compiler-ajv/index.ts diff --git a/test/runtime/schema/integer.ts b/test/runtime/compiler-ajv/integer.ts similarity index 100% rename from test/runtime/schema/integer.ts rename to test/runtime/compiler-ajv/integer.ts diff --git a/test/runtime/schema/intersect.ts b/test/runtime/compiler-ajv/intersect.ts similarity index 100% rename from test/runtime/schema/intersect.ts rename to test/runtime/compiler-ajv/intersect.ts diff --git a/test/runtime/schema/keyof.ts b/test/runtime/compiler-ajv/keyof.ts similarity index 100% rename from test/runtime/schema/keyof.ts rename to test/runtime/compiler-ajv/keyof.ts diff --git a/test/runtime/schema/literal.ts b/test/runtime/compiler-ajv/literal.ts similarity index 100% rename from test/runtime/schema/literal.ts rename to test/runtime/compiler-ajv/literal.ts diff --git a/test/runtime/schema/never.ts b/test/runtime/compiler-ajv/never.ts similarity index 100% rename from test/runtime/schema/never.ts rename to test/runtime/compiler-ajv/never.ts diff --git a/test/runtime/schema/not.ts b/test/runtime/compiler-ajv/not.ts similarity index 100% rename from test/runtime/schema/not.ts rename to test/runtime/compiler-ajv/not.ts diff --git a/test/runtime/schema/null.ts b/test/runtime/compiler-ajv/null.ts similarity index 100% rename from test/runtime/schema/null.ts rename to test/runtime/compiler-ajv/null.ts diff --git a/test/runtime/schema/number.ts b/test/runtime/compiler-ajv/number.ts similarity index 100% rename from test/runtime/schema/number.ts rename to test/runtime/compiler-ajv/number.ts diff --git a/test/runtime/schema/object.ts b/test/runtime/compiler-ajv/object.ts similarity index 100% rename from test/runtime/schema/object.ts rename to test/runtime/compiler-ajv/object.ts diff --git a/test/runtime/schema/omit.ts b/test/runtime/compiler-ajv/omit.ts similarity index 100% rename from test/runtime/schema/omit.ts rename to test/runtime/compiler-ajv/omit.ts diff --git a/test/runtime/schema/optional.ts b/test/runtime/compiler-ajv/optional.ts similarity index 100% rename from test/runtime/schema/optional.ts rename to test/runtime/compiler-ajv/optional.ts diff --git a/test/runtime/schema/partial.ts b/test/runtime/compiler-ajv/partial.ts similarity index 100% rename from test/runtime/schema/partial.ts rename to test/runtime/compiler-ajv/partial.ts diff --git a/test/runtime/schema/pick.ts b/test/runtime/compiler-ajv/pick.ts similarity index 100% rename from test/runtime/schema/pick.ts rename to test/runtime/compiler-ajv/pick.ts diff --git a/test/runtime/schema/readonly-optional.ts b/test/runtime/compiler-ajv/readonly-optional.ts similarity index 100% rename from test/runtime/schema/readonly-optional.ts rename to test/runtime/compiler-ajv/readonly-optional.ts diff --git a/test/runtime/schema/readonly.ts b/test/runtime/compiler-ajv/readonly.ts similarity index 100% rename from test/runtime/schema/readonly.ts rename to test/runtime/compiler-ajv/readonly.ts diff --git a/test/runtime/schema/record.ts b/test/runtime/compiler-ajv/record.ts similarity index 100% rename from test/runtime/schema/record.ts rename to test/runtime/compiler-ajv/record.ts diff --git a/test/runtime/schema/recursive.ts b/test/runtime/compiler-ajv/recursive.ts similarity index 100% rename from test/runtime/schema/recursive.ts rename to test/runtime/compiler-ajv/recursive.ts diff --git a/test/runtime/schema/ref.ts b/test/runtime/compiler-ajv/ref.ts similarity index 100% rename from test/runtime/schema/ref.ts rename to test/runtime/compiler-ajv/ref.ts diff --git a/test/runtime/schema/regexp.ts b/test/runtime/compiler-ajv/regexp.ts similarity index 100% rename from test/runtime/schema/regexp.ts rename to test/runtime/compiler-ajv/regexp.ts diff --git a/test/runtime/schema/required.ts b/test/runtime/compiler-ajv/required.ts similarity index 100% rename from test/runtime/schema/required.ts rename to test/runtime/compiler-ajv/required.ts diff --git a/test/runtime/schema/string.ts b/test/runtime/compiler-ajv/string.ts similarity index 100% rename from test/runtime/schema/string.ts rename to test/runtime/compiler-ajv/string.ts diff --git a/test/runtime/schema/template-literal.ts b/test/runtime/compiler-ajv/template-literal.ts similarity index 100% rename from test/runtime/schema/template-literal.ts rename to test/runtime/compiler-ajv/template-literal.ts diff --git a/test/runtime/schema/tuple.ts b/test/runtime/compiler-ajv/tuple.ts similarity index 100% rename from test/runtime/schema/tuple.ts rename to test/runtime/compiler-ajv/tuple.ts diff --git a/test/runtime/schema/uint8array.ts b/test/runtime/compiler-ajv/uint8array.ts similarity index 100% rename from test/runtime/schema/uint8array.ts rename to test/runtime/compiler-ajv/uint8array.ts diff --git a/test/runtime/schema/union.ts b/test/runtime/compiler-ajv/union.ts similarity index 100% rename from test/runtime/schema/union.ts rename to test/runtime/compiler-ajv/union.ts diff --git a/test/runtime/schema/unknown.ts b/test/runtime/compiler-ajv/unknown.ts similarity index 100% rename from test/runtime/schema/unknown.ts rename to test/runtime/compiler-ajv/unknown.ts diff --git a/test/runtime/schema/unsafe.ts b/test/runtime/compiler-ajv/unsafe.ts similarity index 100% rename from test/runtime/schema/unsafe.ts rename to test/runtime/compiler-ajv/unsafe.ts diff --git a/test/runtime/schema/validate.ts b/test/runtime/compiler-ajv/validate.ts similarity index 100% rename from test/runtime/schema/validate.ts rename to test/runtime/compiler-ajv/validate.ts diff --git a/test/runtime/schema/void.ts b/test/runtime/compiler-ajv/void.ts similarity index 100% rename from test/runtime/schema/void.ts rename to test/runtime/compiler-ajv/void.ts diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 8f53de837..fb4f897fd 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -1,5 +1,5 @@ import './compiler/index' -import './schema/index' +import './compiler-ajv/index' import './errors/index' import './system/index' import './type/index' From dd71b52db61a43216ef6864b0caff9ead5d17c9c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 27 Aug 2023 15:00:39 +0900 Subject: [PATCH 181/369] Revision 0.31.5 (#552) --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 38 +++++++++++++++++++------------------- src/typebox.ts | 14 +++++++------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cb18ccff..3d79d5069 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.4", + "version": "0.31.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.4", + "version": "0.31.5", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 475121baf..7d45a33f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.4", + "version": "0.31.5", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 05345fec0..4a28c9058 100644 --- a/readme.md +++ b/readme.md @@ -277,7 +277,7 @@ The following table lists the supported Json types. These types are fully compat │ const T = Type.Tuple([ │ type T = [number, number] │ const T = { │ │ Type.Number(), │ │ type: 'array', │ │ Type.Number() │ │ items: [{ │ -│ ]) │ │ type: 'number' │ +│ ]) │ │ type: 'number' │ │ │ │ }, { │ │ │ │ type: 'number' │ │ │ │ }], │ @@ -312,9 +312,9 @@ The following table lists the supported Json types. These types are fully compat ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Union([ │ type T = string | number │ const T = { │ │ Type.String(), │ │ anyOf: [{ │ -│ Type.Number() │ │ type: 'string' │ +│ Type.Number() │ │ type: 'string' │ │ ]) │ │ }, { │ -│ │ │ type: 'number' │ +│ │ │ type: 'number' │ │ │ │ }] │ │ │ │ } │ │ │ │ │ @@ -472,10 +472,10 @@ The following table lists the supported Json types. These types are fully compat ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const A = Type.Tuple([ │ type A = [0, 1] │ const T = { │ │ Type.Literal(0), │ type B = [2, 3] │ type: 'array', │ -│ Type.Literal(1) │ type T = [...A, ...B] │ items: [ │ -│ ]) │ │ { const: 0 }, │ -│ const B = Type.Tuple([ │ │ { const: 1 }, │ -| Type.Literal(2), │ │ { const: 2 }, │ +│ Type.Literal(1) │ type T = [ │ items: [ │ +│ ]) │ ...A, │ { const: 0 }, │ +│ const B = Type.Tuple([ │ ...B │ { const: 1 }, │ +| Type.Literal(2), │ ] │ { const: 2 }, │ | Type.Literal(3) │ │ { const: 3 } │ │ ]) │ │ ], │ │ const T = Type.Tuple([ │ │ additionalItems: false, │ @@ -485,26 +485,26 @@ The following table lists the supported Json types. These types are fully compat │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Uncapitalize( │ type T = Uncapitalize< │ const T = { │ -│ Type.Literal('Hello') │ 'Hello' │ type: 'string', │ -│ ) │ > │ const: 'hello' │ +│ Type.Literal('Hello') │ 'Hello' │ type: 'string', │ +│ ) │ > │ const: 'hello' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Capitalize( │ type T = Capitalize< │ const T = { │ -│ Type.Literal('hello') │ 'hello' │ type: 'string', │ -│ ) │ > │ const: 'Hello' │ +│ Type.Literal('hello') │ 'hello' │ type: 'string', │ +│ ) │ > │ const: 'Hello' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Uppercase( │ type T = Uppercase< │ const T = { │ -│ Type.Literal('hello') │ 'hello' │ type: 'string', │ -│ ) │ > │ const: 'HELLO' │ +│ Type.Literal('hello') │ 'hello' │ type: 'string', │ +│ ) │ > │ const: 'HELLO' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Lowercase( │ type T = Lowercase< │ const T = { │ -│ Type.Literal('HELLO') │ 'HELLO' │ type: 'string', │ -│ ) │ > │ const: 'hello' │ +│ Type.Literal('HELLO') │ 'HELLO' │ type: 'string', │ +│ ) │ > │ const: 'hello' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -533,26 +533,26 @@ TypeBox provides an extended type set that can be used to create schematics for │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Constructor([ │ type T = new ( │ const T = { │ -│ Type.String(), │ arg0: string, │ type: 'constructor', │ +│ Type.String(), │ arg0: string, │ type: 'Constructor', │ │ Type.Number() │ arg0: number │ parameters: [{ │ │ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ │ │ │ type: 'number' │ │ │ │ }], │ -│ │ │ return: { │ +│ │ │ returns: { │ │ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Function([ │ type T = ( │ const T = { │ -| Type.String(), │ arg0: string, │ type: 'function', │ +| Type.String(), │ arg0: string, │ type: 'Function', │ │ Type.Number() │ arg1: number │ parameters: [{ │ │ ], Type.Boolean()) │ ) => boolean │ type: 'string' │ │ │ │ }, { │ │ │ │ type: 'number' │ │ │ │ }], │ -│ │ │ return: { │ +│ │ │ returns: { │ │ │ │ type: 'boolean' │ │ │ │ } │ │ │ │ } │ diff --git a/src/typebox.ts b/src/typebox.ts index d33380c12..d4f7ca769 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -311,7 +311,7 @@ export type TConstructorParameterArray extends TSchema { [Kind]: 'Constructor' static: new (...param: TConstructorParameterArray) => Static - type: 'constructor' + type: 'Constructor' parameters: T returns: U } @@ -390,7 +390,7 @@ export type TFunctionParameters = [... export interface TFunction extends TSchema { [Kind]: 'Function' static: (...param: TFunctionParameters) => Static - type: 'function' + type: 'Function' parameters: T returns: U } @@ -641,7 +641,7 @@ export type TPick export interface TPromise extends TSchema { [Kind]: 'Promise' static: Promise> - type: 'promise' + type: 'Promise' item: TSchema } // -------------------------------------------------------------------------- @@ -1204,7 +1204,7 @@ export namespace TypeGuard { // prettier-ignore return ( TKindOf(schema, 'Constructor') && - schema.type === 'constructor' && + schema.type === 'Constructor' && IsOptionalString(schema.$id) && ValueGuard.IsArray(schema.parameters) && schema.parameters.every(schema => TSchema(schema)) && @@ -1229,7 +1229,7 @@ export namespace TypeGuard { // prettier-ignore return ( TKindOf(schema, 'Function') && - schema.type === 'function' && + schema.type === 'Function' && IsOptionalString(schema.$id) && ValueGuard.IsArray(schema.parameters) && schema.parameters.every(schema => TSchema(schema)) && @@ -3300,7 +3300,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { /** `[JavaScript]` Creates a Constructor type */ public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Constructor', type: 'constructor', parameters: clonedParameters, returns: clonedReturns }) + return this.Create({ ...options, [Kind]: 'Constructor', type: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) } /** `[JavaScript]` Creates a Date type */ public Date(options: DateOptions = {}): TDate { @@ -3309,7 +3309,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { /** `[JavaScript]` Creates a Function type */ public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Function', type: 'function', parameters: clonedParameters, returns: clonedReturns }) + return this.Create({ ...options, [Kind]: 'Function', type: 'Function', parameters: clonedParameters, returns: clonedReturns }) } /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { From 67925c10a36c786e538d502034c7af08397e52e9 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 29 Aug 2023 18:24:26 +0900 Subject: [PATCH 182/369] Revision 0.31.6 (#560) --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 47 +++++++++++++++----------------- src/value/value.ts | 8 +++--- test/static/readonly-optional.ts | 19 ++++++++++++- test/static/transform.ts | 33 +++++++++++++++++++++- 6 files changed, 79 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d79d5069..c9126a5d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 7d45a33f4..d4ca52eea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.5", + "version": "0.31.6", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index d4f7ca769..b89bab520 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -66,7 +66,7 @@ export type AssertType = T extends E ? T : TNeve // -------------------------------------------------------------------------- // Modifiers // -------------------------------------------------------------------------- -export type TModifier = TOptional | TReadonly +export type TReadonlyOptional = TOptional & TReadonly export type TReadonly = T & { [Readonly]: 'Readonly' } export type TOptional = T & { [Optional]: 'Optional' } // -------------------------------------------------------------------------- @@ -855,37 +855,34 @@ export interface TTemplateLiteral = - T extends TReadonly & TOptional ? TReadonly> : - T extends TReadonly ? TReadonly : - T extends TOptional ? TOptional : - D -// prettier-ignore export type DecodeProperties = { [K in keyof T]: DecodeType } // prettier-ignore -export type DecodeRest = T extends [infer L, ...infer R] - ? [DecodeType>, ...DecodeRest>] +export type DecodeRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [DecodeType, ...DecodeRest] : [] // prettier-ignore -export type DecodeType = - T extends TTransform ? DecodeModifier, T> : - T extends TArray ? DecodeModifier>, T> : - T extends TAsyncIterator ? DecodeModifier>, T> : - T extends TConstructor ? DecodeModifier>, DecodeType>, T> : - T extends TFunction ? DecodeModifier>, DecodeType>, T> : - T extends TIntersect ? DecodeModifier>>, T> : - T extends TIterator ? DecodeModifier>, T> : - T extends TNot ? DecodeModifier>, T> : - T extends TObject ? DecodeModifier>>, T> : - T extends TPromise ? DecodeModifier>, T> : - T extends TRecord ? DecodeModifier>, T> : - T extends TRecursive ? DecodeModifier>, T> : - T extends TRef ? DecodeModifier>, T> : - T extends TTuple ? DecodeModifier>>, T> : - T extends TUnion ? DecodeModifier>>, T> : +export type DecodeType = ( + T extends TOptional ? TOptional> : + T extends TReadonly ? TReadonly> : + T extends TTransform ? TUnsafe : + T extends TArray ? Array> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TConstructor ? TConstructor> : + T extends TFunction ? TFunction> : + T extends TIntersect ? TIntersect : + T extends TIterator ? TIterator> : + T extends TNot ? TNot> : + T extends TObject ? TObject>> : + T extends TPromise ? TPromise> : + T extends TRecord ? TRecord> : + T extends TRecursive ? TRecursive> : + T extends TRef ? TRef> : + T extends TTuple ? TTuple : + T extends TUnion ? TUnion : T +) export type TransformFunction = (value: T) => U export interface TransformOptions { Decode: TransformFunction, O> diff --git a/src/value/value.ts b/src/value/value.ts index 59b8dbe00..edeec7dae 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -78,9 +78,9 @@ export namespace Value { return ValueClone.Clone(value) } /** Decodes a value or throws if error */ - export function Decode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticDecode + export function Decode>(schema: T, references: Types.TSchema[], value: unknown): D /** Decodes a value or throws if error */ - export function Decode(schema: T, value: unknown): Types.StaticDecode + export function Decode>(schema: T, value: unknown): D /** Decodes a value or throws if error */ export function Decode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] @@ -88,9 +88,9 @@ export namespace Value { return ValueTransform.DecodeTransform.Decode(schema, references, value, ValueCheck.Check) } /** Encodes a value or throws if error */ - export function Encode(schema: T, references: Types.TSchema[], value: unknown): Types.StaticEncode + export function Encode>(schema: T, references: Types.TSchema[], value: unknown): E /** Encodes a value or throws if error */ - export function Encode(schema: T, value: unknown): Types.StaticEncode + export function Encode>(schema: T, value: unknown): E /** Encodes a value or throws if error */ export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts index 3191dcd75..aca58d0ba 100644 --- a/test/static/readonly-optional.ts +++ b/test/static/readonly-optional.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TSchema } from '@sinclair/typebox' +import { Type, TSchema, TReadonlyOptional } from '@sinclair/typebox' { const T = Type.Object({ @@ -9,3 +9,20 @@ import { Type, TSchema } from '@sinclair/typebox' readonly A?: string }>() } +{ + const T = Type.ReadonlyOptional(Type.String()) + function test(_: TReadonlyOptional) {} + test(T) +} +{ + const T = Type.Readonly(Type.String()) + function test(_: TReadonlyOptional) {} + // @ts-expect-error + test(T) +} +{ + const T = Type.Optional(Type.String()) + function test(_: TReadonlyOptional) {} + // @ts-expect-error + test(T) +} \ No newline at end of file diff --git a/test/static/transform.ts b/test/static/transform.ts index 3e02df125..e6770917e 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -1,4 +1,4 @@ -import { Type, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' +import { Type, TSchema, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' import { Expect } from './assert' { // string > number @@ -214,3 +214,34 @@ import { Expect } from './assert' Expect(T).ToStaticDecode<{ x: '1' }>() Expect(T).ToStaticDecode<{ x: undefined }>() } +{ // should decode within generic function context + // https://github.com/sinclairzx81/typebox/issues/554 + // prettier-ignore + const ArrayOrSingle = (schema: T) => + Type.Transform(Type.Union([schema, Type.Array(schema)])) + .Decode((value) => (Array.isArray(value) ? value : [value])) + .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); + const T = ArrayOrSingle(Type.String()) + Expect(T).ToStaticDecode() +} +{ + // should correctly decode record keys + // https://github.com/sinclairzx81/typebox/issues/555 + // prettier-ignore + const T = Type.Object({ + x: Type.Optional(Type.Record(Type.Number(), Type.String())) + }) + type A = StaticDecode + type Test = E extends A ? true : false + type E1 = Test + type E2 = Test + type E3 = Test + type E4 = Test + type E5 = Test + // assignment + const E1: E1 = true + const E2: E2 = true + const E3: E3 = true + const E4: E4 = false + const E5: E5 = true +} From c41d29ae3b480a61eff2228270de267afd0c53be Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 29 Aug 2023 18:25:24 +0900 Subject: [PATCH 183/369] Formatting --- test/static/readonly-optional.ts | 2 +- test/static/transform.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/static/readonly-optional.ts b/test/static/readonly-optional.ts index aca58d0ba..f51696bd7 100644 --- a/test/static/readonly-optional.ts +++ b/test/static/readonly-optional.ts @@ -25,4 +25,4 @@ import { Type, TSchema, TReadonlyOptional } from '@sinclair/typebox' function test(_: TReadonlyOptional) {} // @ts-expect-error test(T) -} \ No newline at end of file +} diff --git a/test/static/transform.ts b/test/static/transform.ts index e6770917e..a6a529e42 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -214,7 +214,8 @@ import { Expect } from './assert' Expect(T).ToStaticDecode<{ x: '1' }>() Expect(T).ToStaticDecode<{ x: undefined }>() } -{ // should decode within generic function context +{ + // should decode within generic function context // https://github.com/sinclairzx81/typebox/issues/554 // prettier-ignore const ArrayOrSingle = (schema: T) => From 233cc2215a4ee58b2fa2bd9c4bb59d03bfb37874 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 30 Aug 2023 03:44:17 +0900 Subject: [PATCH 184/369] Revision 0.31.7 (#562) --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 2 +- test/static/transform.ts | 8 ++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9126a5d3..3870c3066 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.6", + "version": "0.31.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.6", + "version": "0.31.7", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index d4ca52eea..8b547772a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.6", + "version": "0.31.7", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index b89bab520..7a3fa3e01 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -867,7 +867,7 @@ export type DecodeType = ( T extends TOptional ? TOptional> : T extends TReadonly ? TReadonly> : T extends TTransform ? TUnsafe : - T extends TArray ? Array> : + T extends TArray ? TArray> : T extends TAsyncIterator ? TAsyncIterator> : T extends TConstructor ? TConstructor> : T extends TFunction ? TFunction> : diff --git a/test/static/transform.ts b/test/static/transform.ts index a6a529e42..f1e4f6368 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -246,3 +246,11 @@ import { Expect } from './assert' const E4: E4 = false const E5: E5 = true } +{ + // should correctly decode array + // https://github.com/sinclairzx81/typebox/issues/561 + const T = Type.Object({ + x: Type.Array(Type.Object({ y: Type.String() })), + }) + Expect(T).ToStaticDecode<{ x: { y: string }[] }>() +} From 010ff1840c7ab43d3df6a52f922e365184635217 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 30 Aug 2023 04:59:00 +0900 Subject: [PATCH 185/369] Documentation --- readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 4a28c9058..cd4af3af3 100644 --- a/readme.md +++ b/readme.md @@ -58,11 +58,11 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime checked using standard Json Schema validation. +TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime asserted using standard Json Schema validation. -TypeBox types are designed to express industry standard schematics as TypeScript types. All types are runtime reflectable, serializable and publishable by default. It includes an extensible type system able to represent type safe schematics for multiple schema specifications. It also provides a high performance validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. +TypeBox uses industry standard schematics for runtime type representation; enabling types to be reflected, serialized and published directly. Its type system is fully extensible and able to support type representation for multiple schema specifications. It also provides a high performance validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. -TypeBox can be used as a simple tool to build up complex schemas or integrated into applications to enable high performance runtime validation for data received over the wire. +TypeBox can be used as a simple tool to build up complex schemas or integrated into applications and frameworks to enable high performance runtime type checking for data received over the wire. License MIT From 9c42cbc1e00caaed670d12de36e73021b2afc1a0 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 31 Aug 2023 05:35:49 +0900 Subject: [PATCH 186/369] Revision 0.31.8 (#566) * Discard on Mapped Object Types * Discard Identifier Tests --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 22 ++++++++++--------- test/runtime/type/guard/omit.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/partial.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/pick.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/required.ts | 34 +++++++++++++++++++---------- 7 files changed, 103 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3870c3066..8333c62ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 8b547772a..a50bc679e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 7a3fa3e01..bd4190a2e 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2842,10 +2842,12 @@ export class TypeBuilder { protected Throw(message: string): never { throw new TypeBuilderError(message) } - /** `[Internal]` Discards a property key from the given schema */ - protected Discard(schema: TSchema, key: PropertyKey): TSchema { - const { [key as any]: _, ...rest } = schema - return rest as TSchema + /** `[Internal]` Discards property keys from the given record type */ + protected Discard(record: Record, keys: PropertyKey[]) { + return keys.reduce((acc, key) => { + const { [key as any]: _, ...rest } = acc + return rest + }, record) as any } /** `[Json]` Omits compositing symbols from this schema */ public Strict(schema: T): T { @@ -3082,7 +3084,7 @@ export class JsonTypeBuilder extends TypeBuilder { public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: string) => !keys.includes(key as any)) if (object.required.length === 0) delete object.required @@ -3096,11 +3098,11 @@ export class JsonTypeBuilder extends TypeBuilder { /** `[Json]` Constructs a type where all properties are optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { return { ...acc, [key]: this.Optional(object.properties[key]) } }, {} as TProperties) - return this.Object(properties, this.Discard(object, 'required') /* object used as options to retain other constraints */) + return this.Object(properties, this.Discard(object, ['required']) /* object used as options to retain other constraints */) }, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ @@ -3117,7 +3119,7 @@ export class JsonTypeBuilder extends TypeBuilder { public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: any) => keys.includes(key)) if (object.required.length === 0) delete object.required @@ -3182,9 +3184,9 @@ export class JsonTypeBuilder extends TypeBuilder { /** `[Json]` Constructs a type where all properties are required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Discard(object.properties[key], Optional) as TSchema } + return { ...acc, [key]: this.Discard(object.properties[key], [Optional]) as TSchema } }, {} as TProperties) return this.Object(properties, object /* object used as options to retain other constraints */) }, options) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 1ceb07b1e..7b19d146c 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -102,18 +102,28 @@ describe('type/guard/TOmit', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ad)) Assert.IsEqual(T.required, ['ad']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x']) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Omit(S, ['x']) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Omit(S, ['x']) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index 8cb963bde..cd248bb31 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -40,18 +40,28 @@ describe('type/guard/TPartial', () => { Assert.IsEqual(T.anyOf[0].required, undefined) Assert.IsEqual(T.anyOf[1].required, undefined) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Partial(S) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Partial(S) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index b25094ad5..0fcd90573 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -104,18 +104,28 @@ describe('type/guard/TPick', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ac)) Assert.IsEqual(T.required, ['ab', 'ac']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x']) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Pick(S, ['x']) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Pick(S, ['x']) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/required.ts index 67c0b3858..5af8134d5 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/required.ts @@ -37,18 +37,28 @@ describe('type/guard/TRequired', () => { Assert.IsEqual(T.anyOf[0].required, ['x']) Assert.IsEqual(T.anyOf[1].required, ['y']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Required(S) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Required(S) + Assert.IsFalse(Transform in R) }) }) From 224a4bee5bbc57c57cd7ba3e2f2d5bdcd3ba1898 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 3 Sep 2023 23:04:51 +0900 Subject: [PATCH 187/369] Revision 0.31.9 (#567) * Experimental Enum * Reset Example * More strict type assertions (#569) * Add experimental type assertions and change tests accordingly * Improve assertion debugging showing the expected type in the error * Add complex constraint * Add reference to equality condition * Fix intersection assertion * Add ToStaticNever assertion and fix tests accordingly * Improve test for record with enum key * Formatting | Enum Union Test * version * Constrain to TEnumValue * Constrain TEnum Argument to TEnumValue * Ensure Distinct Values * Enum and Record Guard Tests * Minor Refactor on Static Assert --------- Co-authored-by: Angelo Di Pilla --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 23 ++++----- test/runtime/type/guard/enum.ts | 81 +++++++++++++++++++++++++++++++ test/runtime/type/guard/index.ts | 1 + test/runtime/type/guard/record.ts | 13 +++++ test/static/assert.ts | 64 ++++++++++++++++++++++-- test/static/awaited.ts | 4 +- test/static/composite.ts | 2 +- test/static/enum.ts | 4 +- test/static/exclude.ts | 8 +-- test/static/indexed.ts | 8 +-- test/static/intersect.ts | 5 +- test/static/never.ts | 2 +- test/static/record.ts | 14 ++++++ test/static/recursive.ts | 4 +- test/static/rest.ts | 4 +- test/static/transform.ts | 19 +++----- test/static/union.ts | 2 +- 19 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 test/runtime/type/guard/enum.ts diff --git a/package-lock.json b/package-lock.json index 8333c62ba..67acd05fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.8", + "version": "0.31.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.8", + "version": "0.31.9", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index a50bc679e..ce7831fe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.8", + "version": "0.31.9", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index bd4190a2e..edca58f8a 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -176,7 +176,6 @@ export type TAnySchema = | TBoolean | TConstructor | TDate - | TEnum | TFunction | TInteger | TIntersect @@ -338,15 +337,12 @@ export interface TDate extends TSchema, DateOptions { // -------------------------------------------------------------------------- // TEnum // -------------------------------------------------------------------------- -export interface TEnumOption { - type: 'number' | 'string' - const: T -} -export interface TEnum = Record> extends TSchema { - [Kind]: 'Union' - static: T[keyof T] - anyOf: TLiteral[] -} +export type TEnumRecord = Record +export type TEnumValue = string | number +export type TEnumKey = string +export type TEnumToLiteralUnion = T extends TEnumValue ? TLiteral : never +export type TEnumToLiteralTuple = UnionToTuple> +export type TEnum = Ensure>>> // -------------------------------------------------------------------------- // TExtends // -------------------------------------------------------------------------- @@ -2900,11 +2896,12 @@ export class JsonTypeBuilder extends TypeBuilder { return Type.Object(properties, options) as TComposite } /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { + public Enum>(item: T, options?: SchemaOptions): TEnum { // prettier-ignore - const values = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] + const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] + const values2 = [...new Set(values1)] // ensure distinct // prettier-ignore - const anyOf = values.map((value) => ValueGuard.IsString(value) + const anyOf = values2.map((value) => ValueGuard.IsString(value) ? { [Kind]: 'Literal', type: 'string' as const, const: value } : { [Kind]: 'Literal', type: 'number' as const, const: value } ) diff --git a/test/runtime/type/guard/enum.ts b/test/runtime/type/guard/enum.ts new file mode 100644 index 000000000..9294ddee9 --- /dev/null +++ b/test/runtime/type/guard/enum.ts @@ -0,0 +1,81 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TEnum', () => { + // ---------------------------------------------------------------- + // Enum + // ---------------------------------------------------------------- + it('Should guard for TEnum Enum 1', () => { + enum E { + A = 1, + B = 2, + C = 3, + } + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 1) + Assert.IsEqual(T.anyOf[1].const, 2) + Assert.IsEqual(T.anyOf[2].const, 3) + }) + it('Should guard for TEnum Enum 2', () => { + enum E { + A = 'X', + B = 'Y', + C = 'Z', + } + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf[2].const, 'Z') + }) + it('Should guard for TEnum Enum 3', () => { + enum E { + A = 'X', + B = 'Y', + C = 'X', + } + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf.length, 2) + }) + // ---------------------------------------------------------------- + // Object Literal + // ---------------------------------------------------------------- + it('Should guard for TEnum Object Literal 1', () => { + const T = Type.Enum({ + A: 1, + B: 2, + C: 3, + }) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 1) + Assert.IsEqual(T.anyOf[1].const, 2) + Assert.IsEqual(T.anyOf[2].const, 3) + }) + it('Should guard for TEnum Object Literal 2', () => { + const T = Type.Enum({ + A: 'X', + B: 'Y', + C: 'Z', + }) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf[2].const, 'Z') + }) + it('Should guard for TEnum Object Literal 3', () => { + const T = Type.Enum({ + A: 'X', + B: 'Y', + C: 'X', + }) + Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf.length, 2) + }) +}) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 534c10f17..3533d6d53 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -8,6 +8,7 @@ import './capitalize' import './composite' import './constructor' import './date' +import './enum' import './exclude' import './extract' import './function' diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 6855175fa..987d8bd84 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -76,6 +76,19 @@ describe('type/guard/TRecord', () => { Assert.IsTrue(TypeGuard.TString(T.properties.B)) Assert.IsTrue(TypeGuard.TString(T.properties.C)) }) + it('Should guard overload 12', () => { + enum E { + A = 'X', + B = 'Y', + C = 'Z', + } + const T = Type.Enum(E) + const R = Type.Record(T, Type.Null()) + Assert.IsTrue(TypeGuard.TObject(R)) + Assert.IsTrue(TypeGuard.TNull(R.properties.X)) + Assert.IsTrue(TypeGuard.TNull(R.properties.Y)) + Assert.IsTrue(TypeGuard.TNull(R.properties.Z)) + }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- diff --git a/test/static/assert.ts b/test/static/assert.ts index 0e764d992..9a10c8c07 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -1,9 +1,65 @@ import { Static, StaticDecode, StaticEncode, TSchema } from '@sinclair/typebox' +// ------------------------------------------------------------------ +// Symbols +// ------------------------------------------------------------------ +export declare const Unsatisfiable: unique symbol +// Warning: `never` and `any` satisfy the constraint `extends Expected<...>` +export type Expected<_> = { [Unsatisfiable]: never } +// ------------------------------------------------------------------ +// Gates +// ------------------------------------------------------------------ +export type If = T extends true ? Y : N +export type And = If +export type Or = If +export type Not = If +// ------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------ +export type Extends = [T] extends [U] ? true : false +export type IsAny = 0 extends 1 & T ? true : false +export type IsNever = Extends +// ------------------------------------------------------------------ +// Constraints +// ------------------------------------------------------------------ +// See https://github.com/microsoft/TypeScript/issues/51011 +export type CircularHelper = [T] extends U ? T : Expected +// See https://github.com/Microsoft/TypeScript/issues/27024 +export type ConstrainEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? T : Expected +export type ConstraintMutuallyExtend = CircularHelper +// If U is never, there's nothing we can do +export type ComplexConstraint = If< + // If U is any, we can't use Expect or it would satisfy the constraint + And>, IsAny>, + never, + If< + Or< + // If they are both any we are happy + And, IsAny>, + // If T extends U, but not because it's any, we are happy + And, Not>> + >, + T, + Expected + > +> +// ------------------------------------------------------------------ +// Expect +// ------------------------------------------------------------------ +export type ExpectResult = If< + IsNever>, + { ToStaticNever(): void }, + { + ToStatic, U>>(): void + ToStaticDecode, U>>(): void + ToStaticEncode, U>>(): void + } +> export function Expect(schema: T) { return { - ToStatic: >() => {}, - ToStaticDecode: >() => {}, - ToStaticEncode: >() => {}, - } + ToStatic() {}, + ToStaticNever() {}, + ToStaticDecode() {}, + ToStaticEncode() {}, + } as ExpectResult } diff --git a/test/static/awaited.ts b/test/static/awaited.ts index 303e30f89..e9c235c65 100644 --- a/test/static/awaited.ts +++ b/test/static/awaited.ts @@ -11,10 +11,10 @@ Expect(Type.Awaited(Type.Promise(Type.Promise(Type.String())))).ToStatic Expect(Type.Awaited(Type.Union([Type.Promise(Type.String()), Type.Number()]))).ToStatic() -Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.String()), Type.Number()]))).ToStatic() +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Object({ a: Type.String() })), Type.Object({ b: Type.Number() })]))).ToStatic<{ a: string } & { b: number }>() // Two Levels Expect(Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToStatic() -Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()]))).ToStatic() +Expect(Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.Object({ a: Type.String() }))), Type.Object({ b: Type.Number() })]))).ToStatic<{ a: string } & { b: number }>() diff --git a/test/static/composite.ts b/test/static/composite.ts index 62528245c..70751619e 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -63,7 +63,7 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' }) const T = Type.Composite([A, B]) Expect(T).ToStatic<{ - A: number | undefined + A?: number | undefined }>() } { diff --git a/test/static/enum.ts b/test/static/enum.ts index 2833716b7..6d754e2f4 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -7,8 +7,6 @@ import { Type, Static } from '@sinclair/typebox' B = 'hello', C = 42, } - const T = Type.Enum(E) - - Expect(T).ToStatic>() // ? + Expect(T).ToStatic() } diff --git a/test/static/exclude.ts b/test/static/exclude.ts index bca6d820a..14e1a5104 100644 --- a/test/static/exclude.ts +++ b/test/static/exclude.ts @@ -3,7 +3,7 @@ import { Expect } from './assert' { const T = Type.Exclude(Type.String(), Type.String()) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { const T = Type.Exclude(Type.String(), Type.Number()) @@ -21,7 +21,7 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) @@ -44,7 +44,7 @@ import { Expect } from './assert' const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) @@ -67,7 +67,7 @@ import { Expect } from './assert' const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) diff --git a/test/static/indexed.ts b/test/static/indexed.ts index cc0c9f24a..a33af5130 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -48,19 +48,19 @@ import { Type, Static } from '@sinclair/typebox' const A = Type.Tuple([]) const R = Type.Index(A, Type.Number()) type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } { const A = Type.Object({}) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStatic() } { const A = Type.Array(Type.Number()) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStatic() } // ------------------------------------------------------------------ // Intersections @@ -115,7 +115,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Intersect([A, B]) const R = Type.Index(C, ['x']) type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } { type A = { x: string; y: number } diff --git a/test/static/intersect.ts b/test/static/intersect.ts index 8fc36160e..f4fbffbf0 100644 --- a/test/static/intersect.ts +++ b/test/static/intersect.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TOptional, TString, Static } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' { const A = Type.Object({ @@ -32,8 +32,7 @@ import { Type, TOptional, TString, Static } from '@sinclair/typebox' }) const T = Type.Intersect([A, B]) - Expect(T.properties.A).ToStatic>() - Expect(T.properties.B).ToStatic() + Expect(T).ToStatic<{ A?: string | undefined } & { B: string }>() } // https://github.com/sinclairzx81/typebox/issues/113 diff --git a/test/static/never.ts b/test/static/never.ts index 8261621e6..18dc62564 100644 --- a/test/static/never.ts +++ b/test/static/never.ts @@ -3,5 +3,5 @@ import { Type } from '@sinclair/typebox' { const T = Type.Never() - Expect(T).ToStatic() + Expect(T).ToStaticNever() } diff --git a/test/static/record.ts b/test/static/record.ts index a314c46ff..35aa0bb13 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -70,3 +70,17 @@ import { Type, Static } from '@sinclair/typebox' Expect(T).ToStatic>() } + +{ + enum E { + A = 'X', + B = 'Y', + C = 'Z', + } + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{ + X: number + Y: number + Z: number + }>() +} diff --git a/test/static/recursive.ts b/test/static/recursive.ts index 7f79ab6f5..cfd5a76c5 100644 --- a/test/static/recursive.ts +++ b/test/static/recursive.ts @@ -33,8 +33,8 @@ import { Type, Static } from '@sinclair/typebox' ) const T = Type.Partial(R) Expect(T).ToStatic<{ - id: string | undefined - nodes: Static[] | undefined + id?: string | undefined + nodes?: Static[] | undefined }>() } { diff --git a/test/static/rest.ts b/test/static/rest.ts index 4f2bba7f0..0d364a406 100644 --- a/test/static/rest.ts +++ b/test/static/rest.ts @@ -4,13 +4,13 @@ import { Type, Static } from '@sinclair/typebox' // union never const A = Type.String() const B = Type.Union(Type.Rest(A)) - Expect(B).ToStatic() + Expect(B).ToStaticNever() } { // intersect never const A = Type.String() const B = Type.Intersect(Type.Rest(A)) - Expect(B).ToStatic() + Expect(B).ToStaticNever() } { // tuple diff --git a/test/static/transform.ts b/test/static/transform.ts index f1e4f6368..b633f9674 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -84,7 +84,7 @@ import { Expect } from './assert' .Encode((value) => ({ id: 'A', nodes: [ - { id: 'B', nodes: [] }, + { id: 'B', nodes: [] }, { id: 'C', nodes: [] } ] })) @@ -112,7 +112,7 @@ import { Expect } from './assert' .Encode((value) => ({ id: 'A', nodes: [ - { id: 'B', nodes: [] }, + { id: 'B', nodes: [] }, { id: 'C', nodes: [] } ] })) @@ -160,8 +160,8 @@ import { Expect } from './assert' // null to typebox type // prettier-ignore const T = Type.Transform(Type.Null()) - .Decode(value => Type.Object({ - x: Type.Number(), + .Decode(value => Type.Object({ + x: Type.Number(), y: Type.Number(), z: Type.Number() })) @@ -180,7 +180,7 @@ import { Expect } from './assert' // x: number; // y: number; // z: number; - // } // lol + // } } { // ensure decode as optional @@ -189,8 +189,7 @@ import { Expect } from './assert' x: Type.Optional(Type.Number()), y: Type.Optional(Type.Number()) }) - Expect(T).ToStaticDecode<{ x: undefined; y: undefined }>() - Expect(T).ToStaticDecode<{ x: 1; y: 1 }>() + Expect(T).ToStaticDecode<{ x?: number | undefined; y?: number | undefined }>() } { // ensure decode as readonly @@ -199,7 +198,7 @@ import { Expect } from './assert' x: Type.Readonly(Type.Number()), y: Type.Readonly(Type.Number()) }) - Expect(T).ToStaticDecode<{ readonly x: 1; readonly y: 1 }>() + Expect(T).ToStaticDecode<{ readonly x: number; readonly y: number }>() } { // ensure decode as optional union @@ -210,9 +209,7 @@ import { Expect } from './assert' Type.Number() ])) }) - Expect(T).ToStaticDecode<{ x: 1 }>() - Expect(T).ToStaticDecode<{ x: '1' }>() - Expect(T).ToStaticDecode<{ x: undefined }>() + Expect(T).ToStaticDecode<{ x?: string | number | undefined }>() } { // should decode within generic function context diff --git a/test/static/union.ts b/test/static/union.ts index 3529e5465..9c9895ee0 100644 --- a/test/static/union.ts +++ b/test/static/union.ts @@ -69,5 +69,5 @@ import { Type, Static } from '@sinclair/typebox' { const T = Type.Union([]) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } From 2b83dc6ad26bd20e3f0ff3991b8ac487bbfc61dc Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 3 Sep 2023 23:47:17 +0900 Subject: [PATCH 188/369] Revision 0.31.9 (#570) --- src/typebox.ts | 16 ++++++-------- test/runtime/type/guard/enum.ts | 39 +++++++++++++++++++++++++++++++++ test/static/enum.ts | 32 ++++++++++++++++++++++++++- test/static/index.ts | 4 ++-- 4 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/typebox.ts b/src/typebox.ts index edca58f8a..4934b2bf5 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -342,7 +342,8 @@ export type TEnumValue = string | number export type TEnumKey = string export type TEnumToLiteralUnion = T extends TEnumValue ? TLiteral : never export type TEnumToLiteralTuple = UnionToTuple> -export type TEnum = Ensure>>> +export type TEnumToUnion>>> = R extends TLiteralString ? TNever : R // Note: Empty enum evaluates as TLiteralString +export type TEnum = Ensure> // -------------------------------------------------------------------------- // TExtends // -------------------------------------------------------------------------- @@ -2896,16 +2897,13 @@ export class JsonTypeBuilder extends TypeBuilder { return Type.Object(properties, options) as TComposite } /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options?: SchemaOptions): TEnum { + public Enum>(item: T, options: SchemaOptions = {}): TEnum { + if (item === undefined) return this.Union([], options) as TEnum // prettier-ignore const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const values2 = [...new Set(values1)] // ensure distinct - // prettier-ignore - const anyOf = values2.map((value) => ValueGuard.IsString(value) - ? { [Kind]: 'Literal', type: 'string' as const, const: value } - : { [Kind]: 'Literal', type: 'number' as const, const: value } - ) - return this.Create({ ...options, [Kind]: 'Union', anyOf }) + const values2 = [...new Set(values1)] // distinct + const anyOf = values2.map((value) => Type.Literal(value)) + return this.Union(anyOf, options) as TEnum } /** `[Json]` Creates a Conditional type */ public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { diff --git a/test/runtime/type/guard/enum.ts b/test/runtime/type/guard/enum.ts index 9294ddee9..81a415a0c 100644 --- a/test/runtime/type/guard/enum.ts +++ b/test/runtime/type/guard/enum.ts @@ -3,6 +3,45 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TEnum', () => { + // ---------------------------------------------------------------- + // Options + // ---------------------------------------------------------------- + it('Should guard for Options 1', () => { + const T = Type.Enum({ x: 1 }, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 2', () => { + enum E { + x, + } + const T = Type.Enum(E, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 3', () => { + enum E {} + const T = Type.Enum(E, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 4', () => { + const T = Type.Enum({}, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + // ---------------------------------------------------------------- + // Empty + // ---------------------------------------------------------------- + it('Should guard for Empty 1', () => { + const T = Type.Enum({}) + Assert.IsTrue(TypeGuard.TNever(T)) + }) + it('Should guard for Empty 2', () => { + enum E {} + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TNever(T)) + }) // ---------------------------------------------------------------- // Enum // ---------------------------------------------------------------- diff --git a/test/static/enum.ts b/test/static/enum.ts index 6d754e2f4..456e60e2c 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -1,7 +1,8 @@ import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' { + // expect all variants enum E { A, B = 'hello', @@ -10,3 +11,32 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Enum(E) Expect(T).ToStatic() } +{ + // expect all variants + const T = Type.Enum({ + A: 1, + B: 2, + C: 3, + }) + Expect(T).ToStatic<1 | 2 | 3>() +} +{ + // expect variant overlap to reduce + const T = Type.Enum({ + A: 1, + B: 2, + C: 2, // overlap + }) + Expect(T).ToStatic<1 | 2>() +} +{ + // expect empty enum to be never + enum E {} + const T = Type.Enum(E) + Expect(T).ToStaticNever() +} +{ + // expect empty enum to be never + const T = Type.Enum({}) + Expect(T).ToStaticNever() +} diff --git a/test/static/index.ts b/test/static/index.ts index d1c757f10..911f5ec19 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -6,10 +6,10 @@ import './bigint' import './boolean' import './capitalize' import './composite' -import './date' import './constructor-parameters' import './constructor' -import './emum' +import './date' +import './enum' import './extract' import './exclude' import './function' From 0a52b29cb5dcd2d7aa26c101ac827dcd75f594bd Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 3 Sep 2023 23:53:14 +0900 Subject: [PATCH 189/369] Revision 0.31.9 (#571) --- src/typebox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typebox.ts b/src/typebox.ts index 4934b2bf5..509722211 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2898,7 +2898,7 @@ export class JsonTypeBuilder extends TypeBuilder { } /** `[Json]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { - if (item === undefined) return this.Union([], options) as TEnum + if (ValueGuard.IsUndefined(item)) return this.Union([], options) as TEnum // prettier-ignore const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] const values2 = [...new Set(values1)] // distinct From 276ddeb443522b312ed9a290404d99f028a7d743 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 5 Sep 2023 02:49:33 +0900 Subject: [PATCH 190/369] Revision 0.31.10 (#573) * Union, Intersect and Tuple Decode Rest * Version * Transform Tests --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 6 +++--- test/static/transform.ts | 27 +++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67acd05fd..12d0df7ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.9", + "version": "0.31.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.9", + "version": "0.31.10", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index ce7831fe5..3e0138cb3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.9", + "version": "0.31.10", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 509722211..f8f5b946f 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -868,7 +868,7 @@ export type DecodeType = ( T extends TAsyncIterator ? TAsyncIterator> : T extends TConstructor ? TConstructor> : T extends TFunction ? TFunction> : - T extends TIntersect ? TIntersect : + T extends TIntersect ? TIntersect> : T extends TIterator ? TIterator> : T extends TNot ? TNot> : T extends TObject ? TObject>> : @@ -876,8 +876,8 @@ export type DecodeType = ( T extends TRecord ? TRecord> : T extends TRecursive ? TRecursive> : T extends TRef ? TRef> : - T extends TTuple ? TTuple : - T extends TUnion ? TUnion : + T extends TTuple ? TTuple> : + T extends TUnion ? TUnion> : T ) export type TransformFunction = (value: T) => U diff --git a/test/static/transform.ts b/test/static/transform.ts index b633f9674..6395e19ff 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -251,3 +251,30 @@ import { Expect } from './assert' }) Expect(T).ToStaticDecode<{ x: { y: string }[] }>() } +{ + // should decode generic union + const GenericUnion = (t: T) => Type.Union([t, Type.Null()]) + const T = Type.Transform(Type.String()) + .Decode((value) => new Date(value)) + .Encode((value) => value.toISOString()) + Expect(T).ToStaticDecode() + Expect(GenericUnion(T)).ToStaticDecode() +} +{ + // should decode generic tuple + const GenericTuple = (t: T) => Type.Tuple([t, Type.Null()]) + const T = Type.Transform(Type.String()) + .Decode((value) => new Date(value)) + .Encode((value) => value.toISOString()) + Expect(T).ToStaticDecode() + Expect(GenericTuple(T)).ToStaticDecode<[Date, null]>() +} +{ + // should decode generic intersect + const GenericIntersect = (t: T) => Type.Intersect([t, Type.Literal(1)]) + const T = Type.Transform(Type.Number()) + .Decode((value) => value) + .Encode((value) => value) + Expect(T).ToStaticDecode() + Expect(GenericIntersect(T)).ToStaticDecode<1>() +} From fc0bb0e9f25d2e8cc40abf88944dd15299a65b39 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 6 Sep 2023 00:46:25 +0900 Subject: [PATCH 191/369] Revision 0.31.11 (#575) * Resolve for Single String Enum * Version * Update TEnum to accept TEnumRecord as Generic Argument * Use TEnum as Assert --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 14 +++++----- test/runtime/type/guard/enum.ts | 47 ++++++++++++++++++++++++--------- test/static/enum.ts | 4 +-- 5 files changed, 47 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12d0df7ec..db3a526b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.10", + "version": "0.31.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.10", + "version": "0.31.11", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 3e0138cb3..e3d9b895c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.10", + "version": "0.31.11", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index f8f5b946f..78539e42f 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -340,10 +340,10 @@ export interface TDate extends TSchema, DateOptions { export type TEnumRecord = Record export type TEnumValue = string | number export type TEnumKey = string -export type TEnumToLiteralUnion = T extends TEnumValue ? TLiteral : never +export type TEnumToLiteralUnion = T extends TEnumValue ? TLiteral : never // Note: Empty enums infer as TLiteral export type TEnumToLiteralTuple = UnionToTuple> -export type TEnumToUnion>>> = R extends TLiteralString ? TNever : R // Note: Empty enum evaluates as TLiteralString -export type TEnum = Ensure> +export type TEnumToUnion = UnionType>> +export type TEnum = TEnumToUnion // -------------------------------------------------------------------------- // TExtends // -------------------------------------------------------------------------- @@ -2897,13 +2897,13 @@ export class JsonTypeBuilder extends TypeBuilder { return Type.Object(properties, options) as TComposite } /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { - if (ValueGuard.IsUndefined(item)) return this.Union([], options) as TEnum + public Enum>(item: T, options: SchemaOptions = {}): TEnum { + if (ValueGuard.IsUndefined(item)) return this.Never(options) as TEnum // prettier-ignore const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const values2 = [...new Set(values1)] // distinct + const values2 = [...new Set(values1)] const anyOf = values2.map((value) => Type.Literal(value)) - return this.Union(anyOf, options) as TEnum + return this.Union(anyOf, options) as TEnum } /** `[Json]` Creates a Conditional type */ public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { diff --git a/test/runtime/type/guard/enum.ts b/test/runtime/type/guard/enum.ts index 81a415a0c..fd4ee99e1 100644 --- a/test/runtime/type/guard/enum.ts +++ b/test/runtime/type/guard/enum.ts @@ -42,10 +42,24 @@ describe('type/guard/TEnum', () => { const T = Type.Enum(E) Assert.IsTrue(TypeGuard.TNever(T)) }) + // ---------------------------------------------------------------- // Enum // ---------------------------------------------------------------- + it('Should guard for TEnum Enum 0', () => { + enum E {} + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TNever(T)) + }) it('Should guard for TEnum Enum 1', () => { + enum E { + A, + } + const T = Type.Enum(E) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, E.A) + }) + it('Should guard for TEnum Enum 2', () => { enum E { A = 1, B = 2, @@ -53,11 +67,11 @@ describe('type/guard/TEnum', () => { } const T = Type.Enum(E) Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsEqual(T.anyOf[0].const, 1) - Assert.IsEqual(T.anyOf[1].const, 2) - Assert.IsEqual(T.anyOf[2].const, 3) + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) + Assert.IsEqual(T.anyOf[2].const, E.C) }) - it('Should guard for TEnum Enum 2', () => { + it('Should guard for TEnum Enum 3', () => { enum E { A = 'X', B = 'Y', @@ -65,11 +79,11 @@ describe('type/guard/TEnum', () => { } const T = Type.Enum(E) Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsEqual(T.anyOf[0].const, 'X') - Assert.IsEqual(T.anyOf[1].const, 'Y') - Assert.IsEqual(T.anyOf[2].const, 'Z') + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) + Assert.IsEqual(T.anyOf[2].const, E.C) }) - it('Should guard for TEnum Enum 3', () => { + it('Should guard for TEnum Enum 4', () => { enum E { A = 'X', B = 'Y', @@ -77,14 +91,23 @@ describe('type/guard/TEnum', () => { } const T = Type.Enum(E) Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsEqual(T.anyOf[0].const, 'X') - Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) Assert.IsEqual(T.anyOf.length, 2) }) // ---------------------------------------------------------------- // Object Literal // ---------------------------------------------------------------- + it('Should guard for TEnum Object Literal 0', () => { + const T = Type.Enum({}) + Assert.IsTrue(TypeGuard.TNever(T)) + }) it('Should guard for TEnum Object Literal 1', () => { + const T = Type.Enum({ A: 1 }) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 1) + }) + it('Should guard for TEnum Object Literal 2', () => { const T = Type.Enum({ A: 1, B: 2, @@ -95,7 +118,7 @@ describe('type/guard/TEnum', () => { Assert.IsEqual(T.anyOf[1].const, 2) Assert.IsEqual(T.anyOf[2].const, 3) }) - it('Should guard for TEnum Object Literal 2', () => { + it('Should guard for TEnum Object Literal 3', () => { const T = Type.Enum({ A: 'X', B: 'Y', @@ -106,7 +129,7 @@ describe('type/guard/TEnum', () => { Assert.IsEqual(T.anyOf[1].const, 'Y') Assert.IsEqual(T.anyOf[2].const, 'Z') }) - it('Should guard for TEnum Object Literal 3', () => { + it('Should guard for TEnum Object Literal 4', () => { const T = Type.Enum({ A: 'X', B: 'Y', diff --git a/test/static/enum.ts b/test/static/enum.ts index 456e60e2c..9818acfe1 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -30,10 +30,10 @@ import { Type } from '@sinclair/typebox' Expect(T).ToStatic<1 | 2>() } { - // expect empty enum to be never + // expect empty enum to be string (as empty enums T[keyof T] evaluates as string) enum E {} const T = Type.Enum(E) - Expect(T).ToStaticNever() + Expect(T).ToStatic() } { // expect empty enum to be never From df991dee6b389f22e4224821a5212aa46786b286 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 6 Sep 2023 01:45:10 +0900 Subject: [PATCH 192/369] Revision 0.31.12 (#581) * Detected Empty Enum via Reverse Extends Check * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 3 ++- test/static/enum.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index db3a526b8..24a7963cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.11", + "version": "0.31.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.11", + "version": "0.31.12", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index e3d9b895c..db2656753 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.11", + "version": "0.31.12", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 78539e42f..08318db0a 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -340,7 +340,8 @@ export interface TDate extends TSchema, DateOptions { export type TEnumRecord = Record export type TEnumValue = string | number export type TEnumKey = string -export type TEnumToLiteralUnion = T extends TEnumValue ? TLiteral : never // Note: Empty enums infer as TLiteral +export type TEnumToLiteralUnion = T extends TEnumValue ? string extends T ? TNever : TLiteral : never +// ^ empty enums evaluate as string export type TEnumToLiteralTuple = UnionToTuple> export type TEnumToUnion = UnionType>> export type TEnum = TEnumToUnion diff --git a/test/static/enum.ts b/test/static/enum.ts index 9818acfe1..b49472864 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -33,7 +33,7 @@ import { Type } from '@sinclair/typebox' // expect empty enum to be string (as empty enums T[keyof T] evaluates as string) enum E {} const T = Type.Enum(E) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { // expect empty enum to be never From 4211f4a669261ee9cfafce53038bafc15b61eea4 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 6 Sep 2023 01:46:21 +0900 Subject: [PATCH 193/369] Revision 0.31.12 --- src/typebox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typebox.ts b/src/typebox.ts index 08318db0a..c4059ac31 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -340,7 +340,7 @@ export interface TDate extends TSchema, DateOptions { export type TEnumRecord = Record export type TEnumValue = string | number export type TEnumKey = string -export type TEnumToLiteralUnion = T extends TEnumValue ? string extends T ? TNever : TLiteral : never +export type TEnumToLiteralUnion = T extends TEnumValue ? (string extends T ? TNever : TLiteral) : never // ^ empty enums evaluate as string export type TEnumToLiteralTuple = UnionToTuple> export type TEnumToUnion = UnionType>> From 4edc46c2efed4b7ff2a0f1689802dcfdb02b50cc Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 6 Sep 2023 02:49:46 +0900 Subject: [PATCH 194/369] Revision 0.31.13 (#583) * Support Intersect Value Conversion * Update Test To Include Non Converted Property --- package-lock.json | 4 +-- package.json | 2 +- src/value/convert.ts | 9 ++++- test/runtime/value/convert/index.ts | 1 + test/runtime/value/convert/intersect.ts | 45 +++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 test/runtime/value/convert/intersect.ts diff --git a/package-lock.json b/package-lock.json index 24a7963cb..2a2ee6347 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.12", + "version": "0.31.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.12", + "version": "0.31.13", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index db2656753..fbb0e3392 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.12", + "version": "0.31.13", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert.ts b/src/value/convert.ts index fd64cfacf..60ba2e1f3 100644 --- a/src/value/convert.ts +++ b/src/value/convert.ts @@ -173,6 +173,12 @@ function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): un function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { return TryConvertInteger(value) } +function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { + // prettier-ignore + return (schema.allOf.every(schema => Types.TypeGuard.TObject(schema))) + ? Visit(Types.Type.Composite(schema.allOf as Types.TObject[]), references, value) + : Visit(schema.allOf[0], references, value) +} function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { return TryConvertLiteral(schema, value) } @@ -247,6 +253,8 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): return TDate(schema_, references_, value) case 'Integer': return TInteger(schema_, references_, value) + case 'Intersect': + return TIntersect(schema_, references_, value) case 'Literal': return TLiteral(schema_, references_, value) case 'Null': @@ -278,7 +286,6 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): case 'AsyncIterator': case 'Constructor': case 'Function': - case 'Intersect': case 'Iterator': case 'Never': case 'Promise': diff --git a/test/runtime/value/convert/index.ts b/test/runtime/value/convert/index.ts index 09f006f09..ec3e1b6e8 100644 --- a/test/runtime/value/convert/index.ts +++ b/test/runtime/value/convert/index.ts @@ -10,6 +10,7 @@ import './date' import './enum' import './function' import './integer' +import './intersect' import './iterator' import './keyof' import './literal' diff --git a/test/runtime/value/convert/intersect.ts b/test/runtime/value/convert/intersect.ts new file mode 100644 index 000000000..5c2e2bb37 --- /dev/null +++ b/test/runtime/value/convert/intersect.ts @@ -0,0 +1,45 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/convert/Intersect', () => { + it('Should convert intersected objects', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Convert(T, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should not convert for non object exclusive intersect', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Convert(T, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: '1', y: '2' }) + }) + it('Should convert first type for object exclusive intersect 1', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Convert(T, '123') + Assert.IsEqual(R, 123) + }) + it('Should convert first type for object exclusive intersect 2', () => { + // prettier-ignore + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + Type.Number(), + ]) + const R = Value.Convert(T, { x: '3', y: '4' }) + Assert.IsEqual(R, { x: 3, y: '4' }) + }) +}) From 6b27faba280aefed60fb598e1b05896e603b221d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 6 Sep 2023 05:12:54 +0900 Subject: [PATCH 195/369] Revision 0.31.14 (#584) * Revert 0.31.8 Enum * Intercept for Enum on StaticDecode --- package-lock.json | 4 +-- package.json | 2 +- src/typebox.ts | 16 +++++----- test/runtime/value/transform/enum.ts | 45 +++++++++++++++++++++++++++ test/runtime/value/transform/index.ts | 1 + test/static/enum.ts | 2 +- test/static/record.ts | 6 +--- test/static/transform.ts | 13 ++++++++ 8 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 test/runtime/value/transform/enum.ts diff --git a/package-lock.json b/package-lock.json index 2a2ee6347..51286a331 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.13", + "version": "0.31.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.13", + "version": "0.31.14", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index fbb0e3392..13a376c7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.13", + "version": "0.31.14", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index c4059ac31..3e4f0c420 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -176,6 +176,7 @@ export type TAnySchema = | TBoolean | TConstructor | TDate + | TEnum | TFunction | TInteger | TIntersect @@ -340,11 +341,12 @@ export interface TDate extends TSchema, DateOptions { export type TEnumRecord = Record export type TEnumValue = string | number export type TEnumKey = string -export type TEnumToLiteralUnion = T extends TEnumValue ? (string extends T ? TNever : TLiteral) : never -// ^ empty enums evaluate as string -export type TEnumToLiteralTuple = UnionToTuple> -export type TEnumToUnion = UnionType>> -export type TEnum = TEnumToUnion +export interface TEnum = Record> extends TSchema { + [Kind]: 'Union' + [Hint]: 'Enum' + static: T[keyof T] + anyOf: TLiteral[] +} // -------------------------------------------------------------------------- // TExtends // -------------------------------------------------------------------------- @@ -868,6 +870,7 @@ export type DecodeType = ( T extends TArray ? TArray> : T extends TAsyncIterator ? TAsyncIterator> : T extends TConstructor ? TConstructor> : + T extends TEnum ? TEnum : // intercept for union. interior non decodable T extends TFunction ? TFunction> : T extends TIntersect ? TIntersect> : T extends TIterator ? TIterator> : @@ -2899,12 +2902,11 @@ export class JsonTypeBuilder extends TypeBuilder { } /** `[Json]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { - if (ValueGuard.IsUndefined(item)) return this.Never(options) as TEnum // prettier-ignore const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] const values2 = [...new Set(values1)] const anyOf = values2.map((value) => Type.Literal(value)) - return this.Union(anyOf, options) as TEnum + return this.Union(anyOf, { ...options, [Hint]: 'Enum' }) as TEnum } /** `[Json]` Creates a Conditional type */ public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { diff --git a/test/runtime/value/transform/enum.ts b/test/runtime/value/transform/enum.ts new file mode 100644 index 000000000..1df8f7793 --- /dev/null +++ b/test/runtime/value/transform/enum.ts @@ -0,0 +1,45 @@ +import { Assert } from '../../assert' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +describe('value/transform/Enum', () => { + enum E { + A, + B, + C, + } + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform(Type.Enum(E)) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Value.Decode(T0, E.A) + Assert.IsEqual(R, E.A) + }) + it('Should encode identity', () => { + const R = Value.Encode(T0, E.A) + Assert.IsEqual(R, E.A) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Value.Decode(T0, null)) + }) + // -------------------------------------------------------- + // Mapped + // -------------------------------------------------------- + const T1 = Type.Transform(Type.Enum(E)) + .Decode((value) => 1) + .Encode((value) => E.A) + it('Should decode mapped', () => { + const R = Value.Decode(T1, E.A) + Assert.IsEqual(R, 1) + }) + it('Should encode mapped', () => { + const R = Value.Encode(T1, null) + Assert.IsEqual(R, E.A) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Value.Decode(T1, null)) + }) +}) diff --git a/test/runtime/value/transform/index.ts b/test/runtime/value/transform/index.ts index 2dc90ff81..bc4fb931a 100644 --- a/test/runtime/value/transform/index.ts +++ b/test/runtime/value/transform/index.ts @@ -6,6 +6,7 @@ import './bigint' import './boolean' import './constructor' import './date' +import './enum' import './function' import './integer' import './intersect' diff --git a/test/static/enum.ts b/test/static/enum.ts index b49472864..9818acfe1 100644 --- a/test/static/enum.ts +++ b/test/static/enum.ts @@ -33,7 +33,7 @@ import { Type } from '@sinclair/typebox' // expect empty enum to be string (as empty enums T[keyof T] evaluates as string) enum E {} const T = Type.Enum(E) - Expect(T).ToStaticNever() + Expect(T).ToStatic() } { // expect empty enum to be never diff --git a/test/static/record.ts b/test/static/record.ts index 35aa0bb13..9a2ccb639 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -78,9 +78,5 @@ import { Type, Static } from '@sinclair/typebox' C = 'Z', } const T = Type.Record(Type.Enum(E), Type.Number()) - Expect(T).ToStatic<{ - X: number - Y: number - Z: number - }>() + Expect(T).ToStatic<{}>() } diff --git a/test/static/transform.ts b/test/static/transform.ts index 6395e19ff..468787ef2 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -278,3 +278,16 @@ import { Expect } from './assert' Expect(T).ToStaticDecode() Expect(GenericIntersect(T)).ToStaticDecode<1>() } +{ + // should decode enum + enum E { + A, + B, + C, + } + const T = Type.Transform(Type.Enum(E)) + .Decode((value) => 1 as const) + .Encode((value) => E.A) + Expect(T).ToStaticDecode<1>() + Expect(T).ToStaticEncode() +} From f2ccea962345dcee8f8880d315d7f4d7a1bde0ee Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 14 Sep 2023 16:15:24 +0900 Subject: [PATCH 196/369] Revision 0.31.15 (#594) * Support Unicode Hash * Version --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 6 ++---- src/value/hash.ts | 13 ++++++++++++- test/runtime/value/hash/hash.ts | 11 +++++++++++ 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 51286a331..fb6e03401 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.14", + "version": "0.31.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.14", + "version": "0.31.15", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 13a376c7c..4ecd1237b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.14", + "version": "0.31.15", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index cd4af3af3..3133e0c8a 100644 --- a/readme.md +++ b/readme.md @@ -58,11 +58,9 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder that creates Json Schema objects that infer as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript or runtime asserted using standard Json Schema validation. +TypeBox is a runtime type builder 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 assertion rules of the TypeScript compiler. TypeBox enables one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. -TypeBox uses industry standard schematics for runtime type representation; enabling types to be reflected, serialized and published directly. Its type system is fully extensible and able to support type representation for multiple schema specifications. It also provides a high performance validation compiler, various tools for working with dynamic data and offers detailed structured error reporting. - -TypeBox can be used as a simple tool to build up complex schemas or integrated into applications and frameworks to enable high performance runtime type checking for data received over the wire. +This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. License MIT diff --git a/src/value/hash.ts b/src/value/hash.ts index fdeec07a5..cd8935ead 100644 --- a/src/value/hash.ts +++ b/src/value/hash.ts @@ -62,6 +62,15 @@ const F64 = new Float64Array(1) const F64In = new DataView(F64.buffer) const F64Out = new Uint8Array(F64.buffer) // -------------------------------------------------------------------------- +// NumberToBytes +// -------------------------------------------------------------------------- +function* NumberToBytes(value: number): IterableIterator { + const byteCount = value === 0 ? 1 : Math.ceil(Math.floor(Math.log2(value) + 1) / 8) + for (let i = 0; i < byteCount; i++) { + yield (value >> (8 * (byteCount - 1 - i))) & 0xff + } +} +// -------------------------------------------------------------------------- // Hashing Functions // -------------------------------------------------------------------------- function ArrayType(value: Array) { @@ -105,7 +114,9 @@ function ObjectType(value: Record) { function StringType(value: string) { FNV1A64(ByteMarker.String) for (let i = 0; i < value.length; i++) { - FNV1A64(value.charCodeAt(i)) + for (const byte of NumberToBytes(value.charCodeAt(i))) { + FNV1A64(byte) + } } } function SymbolType(value: symbol) { diff --git a/test/runtime/value/hash/hash.ts b/test/runtime/value/hash/hash.ts index b93b676f1..8d06d509f 100644 --- a/test/runtime/value/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -108,4 +108,15 @@ describe('value/hash/Hash', () => { const B = ValueHash.Hash(BigInt(1)) Assert.IsEqual(A, B) }) + // ---------------------------------------------------------------- + // Unicode + // ---------------------------------------------------------------- + it('Should hash unicode 1 (retain single byte hash)', () => { + const hash = ValueHash.Hash('a') + Assert.IsEqual(hash, 586962220959696054n) + }) + it('Should hash unicode 2', () => { + const hash = ValueHash.Hash('안녕 세계') + Assert.IsEqual(hash, 11219208047802711777n) + }) }) From ce94ca097f4f581a7006545e7e3b7b0055dd51c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Acosta?= Date: Mon, 18 Sep 2023 21:51:27 -0300 Subject: [PATCH 197/369] Ecosystem (#599) --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 3133e0c8a..d93cf5fb1 100644 --- a/readme.md +++ b/readme.md @@ -1608,6 +1608,7 @@ The following is a list of community packages that offer general tooling, extend | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | +| [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox schemas from your openapi spec and use it as an http client library. | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | From edd757833430d056ef776212729a45305471e717 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 19 Sep 2023 09:58:37 +0900 Subject: [PATCH 198/369] Ecosystem --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index d93cf5fb1..16449a07b 100644 --- a/readme.md +++ b/readme.md @@ -1608,7 +1608,7 @@ The following is a list of community packages that offer general tooling, extend | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | -| [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox schemas from your openapi spec and use it as an http client library. | +| [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | From dc4e70de42ab96b8d6b1d215fc3cee0ca2e057a6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 25 Sep 2023 15:55:50 +0900 Subject: [PATCH 199/369] Revision 0.31.16 (#605) * Support Infinite Template Literal Record Key * Tests * Version --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 4 +- test/runtime/compiler-ajv/record.ts | 63 +++++++++++++++++++++++++++++ test/runtime/compiler/record.ts | 63 +++++++++++++++++++++++++++++ test/static/record.ts | 17 +++++++- 6 files changed, 147 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb6e03401..88a7f79c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.15", + "version": "0.31.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.15", + "version": "0.31.16", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 4ecd1237b..fe958029c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.15", + "version": "0.31.16", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 3e4f0c420..4514b9c80 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -656,11 +656,11 @@ export type TRecordFromUnionRest = K ext L extends TLiteralNumber ? TRecordFromUnionLiteralNumber & TRecordFromUnionRest, T> : {}) : {} export type TRecordFromUnion = Ensure>>>> -export type TRecordFromTemplateLiteralKeyInfinite = Ensure> +export type TRecordFromTemplateLiteralKeyInfinite = Ensure> export type TRecordFromTemplateLiteralKeyFinite> = Ensure]: T }>>> // prettier-ignore export type TRecordFromTemplateLiteralKey = IsTemplateLiteralFinite extends false - ? TRecordFromTemplateLiteralKeyInfinite + ? TRecordFromTemplateLiteralKeyInfinite : TRecordFromTemplateLiteralKeyFinite export type TRecordFromLiteralStringKey = Ensure> export type TRecordFromLiteralNumberKey = Ensure> diff --git a/test/runtime/compiler-ajv/record.ts b/test/runtime/compiler-ajv/record.ts index 082a58fb2..75b739b9c 100644 --- a/test/runtime/compiler-ajv/record.ts +++ b/test/runtime/compiler-ajv/record.ts @@ -175,4 +175,67 @@ describe('compiler-ajv/Record', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) Ok(T, { 1: '', 2: '', x: true }) }) + // ---------------------------------------------------------------- + // TemplateLiteral + // ---------------------------------------------------------------- + it('TemplateLiteral 1', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number(), { additionalProperties: false }) + Ok(R, { + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 2', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + Ok(R, { keyA: 0 }) + }) + it('TemplateLiteral 3', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number(), { additionalProperties: false }) + Fail(R, { keyA: 0 }) + }) + it('TemplateLiteral 4', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T], { unevaluatedProperties: false }) + Ok(I, { + x: 1, + y: 2, + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 5', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T]) + Ok(I, { + x: 1, + y: 2, + z: 3, + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 6', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T], { unevaluatedProperties: false }) + Fail(I, { + x: 1, + y: 2, + z: 3, + key0: 1, + key1: 1, + key2: 1, + }) + }) }) diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 89368d724..8b7cfbb65 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -206,4 +206,67 @@ describe('compiler/Record', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean() }) Ok(T, { 1: '', 2: '', x: true }) }) + // ---------------------------------------------------------------- + // TemplateLiteral + // ---------------------------------------------------------------- + it('TemplateLiteral 1', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number(), { additionalProperties: false }) + Ok(R, { + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 2', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + Ok(R, { keyA: 0 }) + }) + it('TemplateLiteral 3', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number(), { additionalProperties: false }) + Fail(R, { keyA: 0 }) + }) + it('TemplateLiteral 4', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T], { unevaluatedProperties: false }) + Ok(I, { + x: 1, + y: 2, + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 5', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T]) + Ok(I, { + x: 1, + y: 2, + z: 3, + key0: 1, + key1: 1, + key2: 1, + }) + }) + it('TemplateLiteral 6', () => { + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T], { unevaluatedProperties: false }) + Fail(I, { + x: 1, + y: 2, + z: 3, + key0: 1, + key1: 1, + key2: 1, + }) + }) }) diff --git a/test/static/record.ts b/test/static/record.ts index 9a2ccb639..819c0ad27 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -70,7 +70,6 @@ import { Type, Static } from '@sinclair/typebox' Expect(T).ToStatic>() } - { enum E { A = 'X', @@ -80,3 +79,19 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Record(Type.Enum(E), Type.Number()) Expect(T).ToStatic<{}>() } +{ + // should support infinite record keys + // https://github.com/sinclairzx81/typebox/issues/604 + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + Expect(R).ToStatic>() +} +{ + // should support infinite record keys with intersect + // https://github.com/sinclairzx81/typebox/issues/604 + const K = Type.TemplateLiteral('key${number}') + const R = Type.Record(K, Type.Number()) + const T = Type.Object({ x: Type.Number(), y: Type.Number() }) + const I = Type.Intersect([R, T]) + Expect(I).ToStatic & { x: number; y: number }>() +} From 04f4da2fd670c8e6e341a02a007f8fe47265a099 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 26 Sep 2023 16:20:50 +0900 Subject: [PATCH 200/369] Revision 0.31.17 (#608) * Fix Compiler Contains Check * Updates * Version --- package-lock.json | 4 +-- package.json | 2 +- src/compiler/compiler.ts | 2 +- test/runtime/compiler-ajv/array.ts | 38 +++++++++++++++++++++++++++ test/runtime/compiler/array.ts | 38 +++++++++++++++++++++++++++ test/runtime/value/check/array.ts | 42 ++++++++++++++++++++++++++++++ 6 files changed, 122 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 88a7f79c7..849e6bbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index fe958029c..198997ca2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.16", + "version": "0.31.17", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 0c9d2b958..9e840ca92 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -204,7 +204,7 @@ export namespace TypeCompiler { const checkExpression = CreateExpression(containsSchema, references, 'value') const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] - const checkCount = `const count = ${value}.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` + const checkCount = `const count = value.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)` const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ') yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})` } diff --git a/test/runtime/compiler-ajv/array.ts b/test/runtime/compiler-ajv/array.ts index dd9feaea4..589bc03ac 100644 --- a/test/runtime/compiler-ajv/array.ts +++ b/test/runtime/compiler-ajv/array.ts @@ -145,4 +145,42 @@ describe('compiler-ajv/Array', () => { Fail(T, [1, 1, 1, 1, 1]) Fail(T, [1, 1, 1, 1, 1, 1]) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Fail(Question, { text: 'A' }) + Fail(Question, { text: 'A', options: [] }) + Ok(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] }) + Ok(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }) + Fail(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] }) + Fail(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }) + }) }) diff --git a/test/runtime/compiler/array.ts b/test/runtime/compiler/array.ts index ae8fa4a6a..70af4c500 100644 --- a/test/runtime/compiler/array.ts +++ b/test/runtime/compiler/array.ts @@ -145,4 +145,42 @@ describe('compiler/Array', () => { Fail(T, [1, 1, 1, 1, 1]) Fail(T, [1, 1, 1, 1, 1, 1]) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Fail(Question, { text: 'A' }) + Fail(Question, { text: 'A', options: [] }) + Ok(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] }) + Ok(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }) + Fail(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] }) + Fail(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }) + }) }) diff --git a/test/runtime/value/check/array.ts b/test/runtime/value/check/array.ts index 421a3e19a..9ad1be58c 100644 --- a/test/runtime/value/check/array.ts +++ b/test/runtime/value/check/array.ts @@ -110,4 +110,46 @@ describe('value/check/Array', () => { Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1])) Assert.IsFalse(Value.Check(T, [1, 1, 1, 1, 1, 1])) }) + // ---------------------------------------------------------------- + // Issue: https://github.com/sinclairzx81/typebox/discussions/607 + // ---------------------------------------------------------------- + it('Should correctly handle undefined array properties', () => { + const Answer = Type.Object({ + text: Type.String(), + isCorrect: Type.Boolean(), + }) + const Question = Type.Object({ + text: Type.String(), + options: Type.Array(Answer, { + minContains: 1, + maxContains: 1, + contains: Type.Object({ + text: Type.String(), + isCorrect: Type.Literal(true), + }), + }), + }) + Assert.IsFalse(Value.Check(Question, { text: 'A' })) + Assert.IsFalse(Value.Check(Question, { text: 'A', options: [] })) + Assert.IsTrue(Value.Check(Question, { text: 'A', options: [{ text: 'A', isCorrect: true }] })) + Assert.IsTrue( + Value.Check(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: false }, + ], + }), + ) + Assert.IsFalse(Value.Check(Question, { text: 'A', options: [{ text: 'A', isCorrect: false }] })) + Assert.IsFalse( + Value.Check(Question, { + text: 'A', + options: [ + { text: 'A', isCorrect: true }, + { text: 'B', isCorrect: true }, + ], + }), + ) + }) }) From 8f723677ad27917945ac27db52203a5e727bd814 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 5 Oct 2023 16:12:18 +0900 Subject: [PATCH 201/369] Hammer 0.18.0 --- package-lock.json | 391 ++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 184 insertions(+), 209 deletions(-) diff --git a/package-lock.json b/package-lock.json index 849e6bbd8..9869d6ff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.31.17", "license": "MIT", "devDependencies": { - "@sinclair/hammer": "^0.17.1", + "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", "ajv": "^8.12.0", @@ -19,26 +19,10 @@ "typescript": "^5.2.2" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", + "integrity": "sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==", "cpu": [ "loong64" ], @@ -52,12 +36,12 @@ } }, "node_modules/@sinclair/hammer": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.2.tgz", - "integrity": "sha512-Nnzq4JuC3VB0JNEfxU4U9W65yCe4+/ft0+FGf6/HBn7BoxW3aigusstFdrzVdhjJ6NVABCwUBMgtYbD9X7Z94g==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.18.0.tgz", + "integrity": "sha512-nIrsOWvvCV/SjTSU4qcOsY3mEO8ErN2WBWzbn6mVpqDajy36lG9WbDEfR6Agm3LbN2pdPl1HGjKuiHpbpOTZ2A==", "dev": true, "dependencies": { - "esbuild": "^0.15.7" + "esbuild": "0.15.7" }, "bin": { "hammer": "hammer" @@ -362,9 +346,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz", + "integrity": "sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==", "dev": true, "hasInstallScript": true, "bin": { @@ -374,34 +358,33 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "@esbuild/linux-loong64": "0.15.7", + "esbuild-android-64": "0.15.7", + "esbuild-android-arm64": "0.15.7", + "esbuild-darwin-64": "0.15.7", + "esbuild-darwin-arm64": "0.15.7", + "esbuild-freebsd-64": "0.15.7", + "esbuild-freebsd-arm64": "0.15.7", + "esbuild-linux-32": "0.15.7", + "esbuild-linux-64": "0.15.7", + "esbuild-linux-arm": "0.15.7", + "esbuild-linux-arm64": "0.15.7", + "esbuild-linux-mips64le": "0.15.7", + "esbuild-linux-ppc64le": "0.15.7", + "esbuild-linux-riscv64": "0.15.7", + "esbuild-linux-s390x": "0.15.7", + "esbuild-netbsd-64": "0.15.7", + "esbuild-openbsd-64": "0.15.7", + "esbuild-sunos-64": "0.15.7", + "esbuild-windows-32": "0.15.7", + "esbuild-windows-64": "0.15.7", + "esbuild-windows-arm64": "0.15.7" } }, "node_modules/esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz", + "integrity": "sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==", "cpu": [ "x64" ], @@ -415,9 +398,9 @@ } }, "node_modules/esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz", + "integrity": "sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==", "cpu": [ "arm64" ], @@ -431,9 +414,9 @@ } }, "node_modules/esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz", + "integrity": "sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==", "cpu": [ "x64" ], @@ -447,9 +430,9 @@ } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz", + "integrity": "sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==", "cpu": [ "arm64" ], @@ -463,9 +446,9 @@ } }, "node_modules/esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz", + "integrity": "sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==", "cpu": [ "x64" ], @@ -479,9 +462,9 @@ } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz", + "integrity": "sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==", "cpu": [ "arm64" ], @@ -495,9 +478,9 @@ } }, "node_modules/esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz", + "integrity": "sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==", "cpu": [ "ia32" ], @@ -511,9 +494,9 @@ } }, "node_modules/esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz", + "integrity": "sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==", "cpu": [ "x64" ], @@ -527,9 +510,9 @@ } }, "node_modules/esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz", + "integrity": "sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==", "cpu": [ "arm" ], @@ -543,9 +526,9 @@ } }, "node_modules/esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz", + "integrity": "sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==", "cpu": [ "arm64" ], @@ -559,9 +542,9 @@ } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz", + "integrity": "sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==", "cpu": [ "mips64el" ], @@ -575,9 +558,9 @@ } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz", + "integrity": "sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==", "cpu": [ "ppc64" ], @@ -591,9 +574,9 @@ } }, "node_modules/esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz", + "integrity": "sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==", "cpu": [ "riscv64" ], @@ -607,9 +590,9 @@ } }, "node_modules/esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz", + "integrity": "sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==", "cpu": [ "s390x" ], @@ -623,9 +606,9 @@ } }, "node_modules/esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz", + "integrity": "sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==", "cpu": [ "x64" ], @@ -639,9 +622,9 @@ } }, "node_modules/esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz", + "integrity": "sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==", "cpu": [ "x64" ], @@ -655,9 +638,9 @@ } }, "node_modules/esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz", + "integrity": "sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==", "cpu": [ "x64" ], @@ -671,9 +654,9 @@ } }, "node_modules/esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz", + "integrity": "sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==", "cpu": [ "ia32" ], @@ -687,9 +670,9 @@ } }, "node_modules/esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz", + "integrity": "sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==", "cpu": [ "x64" ], @@ -703,9 +686,9 @@ } }, "node_modules/esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz", + "integrity": "sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==", "cpu": [ "arm64" ], @@ -1464,27 +1447,20 @@ } }, "dependencies": { - "@esbuild/android-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", - "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", - "dev": true, - "optional": true - }, "@esbuild/linux-loong64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", - "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", + "integrity": "sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==", "dev": true, "optional": true }, "@sinclair/hammer": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.17.2.tgz", - "integrity": "sha512-Nnzq4JuC3VB0JNEfxU4U9W65yCe4+/ft0+FGf6/HBn7BoxW3aigusstFdrzVdhjJ6NVABCwUBMgtYbD9X7Z94g==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.18.0.tgz", + "integrity": "sha512-nIrsOWvvCV/SjTSU4qcOsY3mEO8ErN2WBWzbn6mVpqDajy36lG9WbDEfR6Agm3LbN2pdPl1HGjKuiHpbpOTZ2A==", "dev": true, "requires": { - "esbuild": "^0.15.7" + "esbuild": "0.15.7" } }, "@types/mocha": { @@ -1711,172 +1687,171 @@ "dev": true }, "esbuild": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", - "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz", + "integrity": "sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==", "dev": true, "requires": { - "@esbuild/android-arm": "0.15.18", - "@esbuild/linux-loong64": "0.15.18", - "esbuild-android-64": "0.15.18", - "esbuild-android-arm64": "0.15.18", - "esbuild-darwin-64": "0.15.18", - "esbuild-darwin-arm64": "0.15.18", - "esbuild-freebsd-64": "0.15.18", - "esbuild-freebsd-arm64": "0.15.18", - "esbuild-linux-32": "0.15.18", - "esbuild-linux-64": "0.15.18", - "esbuild-linux-arm": "0.15.18", - "esbuild-linux-arm64": "0.15.18", - "esbuild-linux-mips64le": "0.15.18", - "esbuild-linux-ppc64le": "0.15.18", - "esbuild-linux-riscv64": "0.15.18", - "esbuild-linux-s390x": "0.15.18", - "esbuild-netbsd-64": "0.15.18", - "esbuild-openbsd-64": "0.15.18", - "esbuild-sunos-64": "0.15.18", - "esbuild-windows-32": "0.15.18", - "esbuild-windows-64": "0.15.18", - "esbuild-windows-arm64": "0.15.18" + "@esbuild/linux-loong64": "0.15.7", + "esbuild-android-64": "0.15.7", + "esbuild-android-arm64": "0.15.7", + "esbuild-darwin-64": "0.15.7", + "esbuild-darwin-arm64": "0.15.7", + "esbuild-freebsd-64": "0.15.7", + "esbuild-freebsd-arm64": "0.15.7", + "esbuild-linux-32": "0.15.7", + "esbuild-linux-64": "0.15.7", + "esbuild-linux-arm": "0.15.7", + "esbuild-linux-arm64": "0.15.7", + "esbuild-linux-mips64le": "0.15.7", + "esbuild-linux-ppc64le": "0.15.7", + "esbuild-linux-riscv64": "0.15.7", + "esbuild-linux-s390x": "0.15.7", + "esbuild-netbsd-64": "0.15.7", + "esbuild-openbsd-64": "0.15.7", + "esbuild-sunos-64": "0.15.7", + "esbuild-windows-32": "0.15.7", + "esbuild-windows-64": "0.15.7", + "esbuild-windows-arm64": "0.15.7" } }, "esbuild-android-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", - "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz", + "integrity": "sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==", "dev": true, "optional": true }, "esbuild-android-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", - "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz", + "integrity": "sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", - "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz", + "integrity": "sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", - "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz", + "integrity": "sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", - "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz", + "integrity": "sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", - "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz", + "integrity": "sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", - "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz", + "integrity": "sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", - "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz", + "integrity": "sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", - "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz", + "integrity": "sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", - "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz", + "integrity": "sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", - "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz", + "integrity": "sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", - "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz", + "integrity": "sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==", "dev": true, "optional": true }, "esbuild-linux-riscv64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", - "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz", + "integrity": "sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==", "dev": true, "optional": true }, "esbuild-linux-s390x": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", - "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz", + "integrity": "sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", - "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz", + "integrity": "sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", - "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz", + "integrity": "sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", - "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz", + "integrity": "sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", - "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz", + "integrity": "sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", - "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz", + "integrity": "sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.15.18", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", - "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz", + "integrity": "sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==", "dev": true, "optional": true }, diff --git a/package.json b/package.json index 198997ca2..d3e7e5d90 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "publish:dev": "hammer task publish_dev" }, "devDependencies": { - "@sinclair/hammer": "^0.17.1", + "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", "@types/node": "^18.11.9", "ajv": "^8.12.0", From 0252b4a696aebf22c912bf70d897cc7898329540 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 17 Oct 2023 22:54:22 +0900 Subject: [PATCH 202/369] Revision 0.31.18 (#635) * Support Generic Union Interior Transform * Update Test * Optimize Union Encode Check --- package-lock.json | 4 +-- package.json | 2 +- src/value/transform.ts | 7 +++++ test/runtime/value/transform/union.ts | 43 +++++++++++++++++++++++---- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9869d6ff3..12347b661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index d3e7e5d90..b3a03d866 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.17", + "version": "0.31.18", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/transform.ts b/src/value/transform.ts index 12faf5834..281917f18 100644 --- a/src/value/transform.ts +++ b/src/value/transform.ts @@ -403,11 +403,18 @@ export namespace EncodeTransform { return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] } function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { + // test value against union variants for (const subschema of schema.anyOf) { if (!checkFunction(subschema, references, value)) continue const value1 = Visit(subschema, references, value) return Default(schema, value1) } + // test transformed value against union variants + for (const subschema of schema.anyOf) { + const value1 = Visit(subschema, references, value) + if (!checkFunction(schema, references, value1)) continue + return Default(schema, value1) + } return Default(schema, value) } function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts index 993c87002..d1a9cebc5 100644 --- a/test/runtime/value/transform/union.ts +++ b/test/runtime/value/transform/union.ts @@ -1,6 +1,6 @@ import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { Type, TSchema } from '@sinclair/typebox' describe('value/transform/Union', () => { // -------------------------------------------------------- @@ -126,11 +126,10 @@ describe('value/transform/Union', () => { const N41 = Type.Transform(Type.Number()) .Decode((value) => value + 1) .Encode((value) => value - 1) - const N42 = Type.Transform( - Type.Object({ - x: Type.Number(), - }), - ) + // prettier-ignore + const N42 = Type.Transform(Type.Object({ + x: Type.Number() + })) .Decode((value) => ({ x: value.x + 1 })) .Encode((value) => ({ x: value.x - 1 })) const N43 = Type.Transform(Type.Tuple([Type.Number()])) @@ -167,4 +166,36 @@ describe('value/transform/Union', () => { it('Should throw on mixed types decode', () => { Assert.Throws(() => Value.Decode(T4, null)) }) + // -------------------------------------------------------- + // Interior Union Transform + // + // https://github.com/sinclairzx81/typebox/issues/631 + // -------------------------------------------------------- + const T51 = Type.Transform(Type.String()) + .Decode((value) => new Date(value)) + .Encode((value) => value.toISOString()) + const T52 = Type.Union([Type.Null(), T51]) + it('Should decode interior union 1', () => { + const R = Value.Decode(T52, null) + Assert.IsEqual(R, null) + }) + it('Should decode interior union 2', () => { + const R = Value.Decode(T52, new Date().toISOString()) + Assert.IsInstanceOf(R, Date) + }) + it('Should encode interior union 1', () => { + const R = Value.Encode(T52, null) + Assert.IsEqual(R, null) + }) + it('Should encode interior union 2', () => { + const D = new Date() + const R = Value.Encode(T52, D) + Assert.IsEqual(R, D.toISOString()) + }) + it('Should throw on interior union decode', () => { + Assert.Throws(() => Value.Decode(T52, {})) + }) + it('Should throw on interior union encode', () => { + Assert.Throws(() => Value.Encode(T52, 1)) + }) }) From b0b66890008bab3b8fcaf94498f753bb72eaf8d5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 26 Oct 2023 03:26:27 +0900 Subject: [PATCH 203/369] Revision 0.31.19 (#644) * Encode Error Path for RFC9601 Escape Sequences * Record Tests + Version --- package-lock.json | 4 +- package.json | 2 +- src/errors/errors.ts | 24 +-- test/runtime/errors/types/index.ts | 2 + .../errors/types/object-pointer-property.ts | 139 ++++++++++++++++++ .../errors/types/record-pointer-property.ts | 70 +++++++++ 6 files changed, 229 insertions(+), 12 deletions(-) create mode 100644 test/runtime/errors/types/object-pointer-property.ts create mode 100644 test/runtime/errors/types/record-pointer-property.ts diff --git a/package-lock.json b/package-lock.json index 12347b661..c831cfa01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.18", + "version": "0.31.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.18", + "version": "0.31.19", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index b3a03d866..85d102ed9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.18", + "version": "0.31.19", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index f9b4b4edc..9f09e1f55 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -119,6 +119,12 @@ export class ValueErrorsUnknownTypeError extends Types.TypeBoxError { } } // -------------------------------------------------------------------------- +// EscapeKey +// -------------------------------------------------------------------------- +export function EscapeKey(key: string): string { + return key.replace(/~/g, '~0').replace(/\//g, '~1') // RFC6901 Path +} +// -------------------------------------------------------------------------- // Guards // -------------------------------------------------------------------------- function IsDefined(value: unknown): value is T { @@ -319,31 +325,31 @@ function* TObject(schema: Types.TObject, references: Types.TSchema[], path: stri const unknownKeys = Object.getOwnPropertyNames(value) for (const requiredKey of requiredKeys) { if (unknownKeys.includes(requiredKey)) continue - yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${requiredKey}`, undefined) + yield Create(ValueErrorType.ObjectRequiredProperty, schema.properties[requiredKey], `${path}/${EscapeKey(requiredKey)}`, undefined) } if (schema.additionalProperties === false) { for (const valueKey of unknownKeys) { if (!knownKeys.includes(valueKey)) { - yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${valueKey}`, value[valueKey]) + yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(valueKey)}`, value[valueKey]) } } } if (typeof schema.additionalProperties === 'object') { for (const valueKey of unknownKeys) { if (knownKeys.includes(valueKey)) continue - yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${valueKey}`, value[valueKey]) + yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(valueKey)}`, value[valueKey]) } } for (const knownKey of knownKeys) { const property = schema.properties[knownKey] if (schema.required && schema.required.includes(knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey]) if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { - yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${knownKey}`, undefined) + yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${EscapeKey(knownKey)}`, undefined) } } else { if (TypeSystemPolicy.IsExactOptionalProperty(value, knownKey)) { - yield* Visit(property, references, `${path}/${knownKey}`, value[knownKey]) + yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey]) } } } @@ -362,17 +368,17 @@ function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: stri const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] const regex = new RegExp(patternKey) for (const [propertyKey, propertyValue] of Object.entries(value)) { - if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${propertyKey}`, propertyValue) + if (regex.test(propertyKey)) yield* Visit(patternSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue) } if (typeof schema.additionalProperties === 'object') { for (const [propertyKey, propertyValue] of Object.entries(value)) { - if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${propertyKey}`, propertyValue) + if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue) } } if (schema.additionalProperties === false) { for (const [propertyKey, propertyValue] of Object.entries(value)) { if (regex.test(propertyKey)) continue - return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${propertyKey}`, propertyValue) + return yield Create(ValueErrorType.ObjectAdditionalProperties, schema, `${path}/${EscapeKey(propertyKey)}`, propertyValue) } } } diff --git a/test/runtime/errors/types/index.ts b/test/runtime/errors/types/index.ts index 077ccbe9e..44730ba30 100644 --- a/test/runtime/errors/types/index.ts +++ b/test/runtime/errors/types/index.ts @@ -43,9 +43,11 @@ import './number-multiple-of' import './object-additional-properties' import './object-max-properties' import './object-min-properties' +import './object-pointer-property' import './object-required-property' import './object' import './promise' +import './record-pointer-property' import './string-format-unknown' import './string-format' import './string-max-length' diff --git a/test/runtime/errors/types/object-pointer-property.ts b/test/runtime/errors/types/object-pointer-property.ts new file mode 100644 index 000000000..ccf045896 --- /dev/null +++ b/test/runtime/errors/types/object-pointer-property.ts @@ -0,0 +1,139 @@ +import { Type } from '@sinclair/typebox' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/ObjectPointerProperty', () => { + // ---------------------------------------------------------------- + // Known + // ---------------------------------------------------------------- + it('Should produce known pointer property path 1', () => { + const T = Type.Object({ 'a/b': Type.String() }) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce known pointer property path 2', () => { + const T = Type.Object({ 'a~b': Type.String() }) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + it('Should produce known pointer property path 3', () => { + const T = Type.Object({ 'a/b~c': Type.String() }) + const R = Resolve(T, { 'a/b~c': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b~0c') + }) + it('Should produce known pointer property path 4', () => { + const T = Type.Object({ 'a~b/c': Type.String() }) + const R = Resolve(T, { 'a~b/c': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b~1c') + }) + it('Should produce known pointer property path 5', () => { + const T = Type.Object({ 'a~b/c/d': Type.String() }) + const R = Resolve(T, { 'a~b/c/d': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b~1c~1d') + }) + it('Should produce known pointer property path 6', () => { + const T = Type.Object({ 'a~b/c/d~e': Type.String() }) + const R = Resolve(T, { 'a~b/c/d~e': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b~1c~1d~0e') + }) + // ---------------------------------------------------------------- + // Unknown Additional + // ---------------------------------------------------------------- + it('Should produce unknown pointer property path 1', () => { + const T = Type.Object({}, { additionalProperties: false }) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce unknown pointer property path 2', () => { + const T = Type.Object({}, { additionalProperties: false }) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + // ---------------------------------------------------------------- + // Unknown Constrained + // ---------------------------------------------------------------- + it('Should produce unknown constrained pointer property path 1', () => { + const T = Type.Object({}, { additionalProperties: Type.String() }) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce unknown constrained pointer property path 2', () => { + const T = Type.Object({}, { additionalProperties: Type.String() }) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + // ---------------------------------------------------------------- + // Nested + // ---------------------------------------------------------------- + it('Should produce nested pointer 1', () => { + const T = Type.Object({ + 'x/y': Type.Object({ + z: Type.Object({ + w: Type.String(), + }), + }), + }) + const R = Resolve(T, { 'x/y': { z: { w: 1 } } }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x~1y/z/w') + }) + it('Should produce nested pointer 2', () => { + const T = Type.Object({ + x: Type.Object({ + 'y/z': Type.Object({ + w: Type.String(), + }), + }), + }) + const R = Resolve(T, { x: { 'y/z': { w: 1 } } }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x/y~1z/w') + }) + it('Should produce nested pointer 3', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Object({ + 'z/w': Type.String(), + }), + }), + }) + const R = Resolve(T, { x: { y: { 'z/w': 1 } } }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x/y/z~1w') + }) + // ---------------------------------------------------------------- + // Nested Array + // ---------------------------------------------------------------- + it('Should produce nested array pointer property path 1', () => { + const T = Type.Object({ + 'x/y': Type.Object({ + z: Type.Array(Type.String()), + }), + }) + const R = Resolve(T, { 'x/y': { z: ['a', 'b', 1] } }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x~1y/z/2') + }) + it('Should produce nested array pointer property path 2', () => { + const T = Type.Object({ + x: Type.Array( + Type.Object({ + 'y/z': Type.String(), + }), + ), + }) + const R = Resolve(T, { x: [{ 'y/z': 'a' }, { 'y/z': 'b' }, { 'y/z': 1 }] }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x/2/y~1z') + }) +}) diff --git a/test/runtime/errors/types/record-pointer-property.ts b/test/runtime/errors/types/record-pointer-property.ts new file mode 100644 index 000000000..a2dbd3436 --- /dev/null +++ b/test/runtime/errors/types/record-pointer-property.ts @@ -0,0 +1,70 @@ +import { Type } from '@sinclair/typebox' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/RecordPointerProperty', () => { + // ---------------------------------------------------------------- + // Known + // ---------------------------------------------------------------- + it('Should produce known pointer property path 1', () => { + const T = Type.Record(Type.String(), Type.String()) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce known pointer property path 2', () => { + const T = Type.Record(Type.String(), Type.String()) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + // ---------------------------------------------------------------- + // Unknown + // ---------------------------------------------------------------- + it('Should produce unknown pointer property path 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: false, + }) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce unknown pointer property path 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: false, + }) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + // ---------------------------------------------------------------- + // Unknown Constrained + // ---------------------------------------------------------------- + it('Should produce unknown constrained pointer property path 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.String(), + }) + const R = Resolve(T, { 'a/b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~1b') + }) + it('Should produce unknown constrained pointer property path 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.String(), + }) + const R = Resolve(T, { 'a~b': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/a~0b') + }) + // ---------------------------------------------------------------- + // PatternProperties + // ---------------------------------------------------------------- + it('Should produce pattern pointer property path 1', () => { + const T = Type.Record(Type.TemplateLiteral('${string}/${string}/c'), Type.String(), { + additionalProperties: false, + }) + const R = Resolve(T, { 'x/y/z': 1 }) + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].path, '/x~1y~1z') + }) +}) From a9cfd7e9c34a211a242d4baa0d1b5655563f237c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 26 Oct 2023 18:21:38 +0900 Subject: [PATCH 204/369] Revision 0.31.20 (#647) * Support Enum Key Inference on Record Types * Version * Use Constrained Key Type of TEnum --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 3 +++ test/static/record.ts | 35 ++++++++++++++++++++++++++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c831cfa01..49319b749 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.19", + "version": "0.31.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.19", + "version": "0.31.20", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 85d102ed9..42fe99b9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.19", + "version": "0.31.20", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 4514b9c80..700e1c3ad 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -650,6 +650,8 @@ export interface TPromise extends TSchema { export type TRecordFromUnionLiteralString = { [_ in K['const']]: T } export type TRecordFromUnionLiteralNumber = { [_ in K['const']]: T } // prettier-ignore +export type TRecordFromEnumKey = Ensure> +// prettier-ignore export type TRecordFromUnionRest = K extends [infer L, ...infer R] ? ( L extends TUnion ? TRecordFromUnionRest & TRecordFromUnionRest, T> : L extends TLiteralString ? TRecordFromUnionLiteralString & TRecordFromUnionRest, T> : @@ -669,6 +671,7 @@ export type TRecordFromNumberKey = Ensure< export type TRecordFromIntegerKey = Ensure> // prettier-ignore export type TRecordResolve = + K extends TEnum ? TRecordFromEnumKey : // Enum before Union (intercept Hint) K extends TUnion ? TRecordFromUnion : K extends TTemplateLiteral ? TRecordFromTemplateLiteralKey : K extends TLiteralString ? TRecordFromLiteralStringKey : diff --git a/test/static/record.ts b/test/static/record.ts index 819c0ad27..1951bd4cd 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -71,13 +71,46 @@ import { Type, Static } from '@sinclair/typebox' Expect(T).ToStatic>() } { + // Should support enum keys 1 enum E { A = 'X', B = 'Y', C = 'Z', } const T = Type.Record(Type.Enum(E), Type.Number()) - Expect(T).ToStatic<{}>() + Expect(T).ToStatic<{ + X: number + Y: number + Z: number + }>() +} +{ + // Should support enum keys 2 + enum E { + A, + B, + C, + } + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{ + 0: number + 1: number + 2: number + }>() +} +{ + // Should support enum keys 3 + enum E { + A = 1, + B = '2', + C = 'Z', + } + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{ + 1: number + 2: number + Z: number + }>() } { // should support infinite record keys From ce1ace5ffb8fe6625a793e01089f274a82c82ab8 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 29 Oct 2023 20:46:06 +0900 Subject: [PATCH 205/369] Revision 0.31.21 (#650) * Extends Optional Property Check * Additional Tests and Version --- package-lock.json | 4 +- package.json | 2 +- src/typebox.ts | 78 ++++++++++++++++------------- test/runtime/type/extends/object.ts | 51 +++++++++++++++++++ 4 files changed, 98 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49319b749..613c25ef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.20", + "version": "0.31.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.20", + "version": "0.31.21", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 42fe99b9f..0b8deb405 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.20", + "version": "0.31.21", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 700e1c3ad..0f9d2e03c 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1144,6 +1144,9 @@ export namespace TypeGuard { function IsOptionalSchema(value: unknown): value is boolean | undefined { return ValueGuard.IsUndefined(value) || TSchema(value) } + // ---------------------------------------------------------------- + // Types + // ---------------------------------------------------------------- /** Returns true if the given value is TAny */ export function TAny(schema: unknown): schema is TAny { // prettier-ignore @@ -1533,40 +1536,42 @@ export namespace TypeGuard { } /** Returns true if the given value is TSchema */ export function TSchema(schema: unknown): schema is TSchema { + // prettier-ignore return ( - ValueGuard.IsObject(schema) && - (TAny(schema) || - TArray(schema) || - TBoolean(schema) || - TBigInt(schema) || - TAsyncIterator(schema) || - TConstructor(schema) || - TDate(schema) || - TFunction(schema) || - TInteger(schema) || - TIntersect(schema) || - TIterator(schema) || - TLiteral(schema) || - TNever(schema) || - TNot(schema) || - TNull(schema) || - TNumber(schema) || - TObject(schema) || - TPromise(schema) || - TRecord(schema) || - TRef(schema) || - TString(schema) || - TSymbol(schema) || - TTemplateLiteral(schema) || - TThis(schema) || - TTuple(schema) || - TUndefined(schema) || - TUnion(schema) || - TUint8Array(schema) || - TUnknown(schema) || - TUnsafe(schema) || - TVoid(schema) || - (TKind(schema) && TypeRegistry.Has(schema[Kind] as any))) + ValueGuard.IsObject(schema) + ) && ( + TAny(schema) || + TArray(schema) || + TBoolean(schema) || + TBigInt(schema) || + TAsyncIterator(schema) || + TConstructor(schema) || + TDate(schema) || + TFunction(schema) || + TInteger(schema) || + TIntersect(schema) || + TIterator(schema) || + TLiteral(schema) || + TNever(schema) || + TNot(schema) || + TNull(schema) || + TNumber(schema) || + TObject(schema) || + TPromise(schema) || + TRecord(schema) || + TRef(schema) || + TString(schema) || + TSymbol(schema) || + TTemplateLiteral(schema) || + TThis(schema) || + TTuple(schema) || + TUndefined(schema) || + TUnion(schema) || + TUint8Array(schema) || + TUnknown(schema) || + TUnsafe(schema) || + TVoid(schema) || + (TKind(schema) && TypeRegistry.Has(schema[Kind] as any)) ) } } @@ -1986,7 +1991,12 @@ export namespace TypeExtends { !TypeGuard.TObject(right) ? TypeExtendsResult.False : (() => { for (const key of Object.getOwnPropertyNames(right.properties)) { - if (!(key in left.properties)) return TypeExtendsResult.False + if (!(key in left.properties) && !TypeGuard.TOptional(right.properties[key])) { + return TypeExtendsResult.False + } + if(TypeGuard.TOptional(right.properties[key])) { + return TypeExtendsResult.True + } if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { return TypeExtendsResult.False } diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts index 172eb46bf..8bc3a6e63 100644 --- a/test/runtime/type/extends/object.ts +++ b/test/runtime/type/extends/object.ts @@ -174,4 +174,55 @@ describe('type/extends/Object', () => { const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) Assert.IsEqual(R, TypeExtendsResult.False) }) + // ---------------------------------------------------------------- + // Optional + // ---------------------------------------------------------------- + it('Should extend optional 1', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Optional(Type.Number()) }) + const C = TypeExtends.Extends(A, B) + Assert.IsEqual(C, TypeExtendsResult.True) + }) + it('Should extend optional 2', () => { + const A = Type.Object({ a: Type.Number() }) + const B = Type.Object({ a: Type.Optional(Type.Number()) }) + const C = TypeExtends.Extends(B, A) + Assert.IsEqual(C, TypeExtendsResult.False) + }) + it('Should extend optional 3', () => { + const A = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number(), + }) + const B = Type.Object({ + y: Type.Number(), + z: Type.Number(), + }) + const R = TypeExtends.Extends(A, B) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend optional 4', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const B = Type.Object({ + y: Type.Number(), + z: Type.Number(), + }) + const R = TypeExtends.Extends(A, B) + Assert.IsEqual(R, TypeExtendsResult.False) + }) + it('Should extend optional 5', () => { + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const B = Type.Object({ + y: Type.Number(), + z: Type.Optional(Type.Number()), + }) + const R = TypeExtends.Extends(A, B) + Assert.IsEqual(R, TypeExtendsResult.True) + }) }) From f049ce389055bc4f170c8e707680499165f20e07 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 3 Nov 2023 00:44:16 +0900 Subject: [PATCH 206/369] Revision 0.31.22 (#652) * Ensure Record into Object for known Enum keys * Version and Static Tests --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 5 +++-- test/static/record.ts | 48 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 613c25ef0..a8b8a5930 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.21", + "version": "0.31.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.21", + "version": "0.31.22", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 0b8deb405..2cb992096 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.21", + "version": "0.31.22", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 0f9d2e03c..e105237f6 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -650,7 +650,7 @@ export interface TPromise extends TSchema { export type TRecordFromUnionLiteralString = { [_ in K['const']]: T } export type TRecordFromUnionLiteralNumber = { [_ in K['const']]: T } // prettier-ignore -export type TRecordFromEnumKey = Ensure> +export type TRecordFromEnumKey, T extends TSchema> = Ensure> // prettier-ignore export type TRecordFromUnionRest = K extends [infer L, ...infer R] ? ( L extends TUnion ? TRecordFromUnionRest & TRecordFromUnionRest, T> : @@ -671,7 +671,7 @@ export type TRecordFromNumberKey = Ensure< export type TRecordFromIntegerKey = Ensure> // prettier-ignore export type TRecordResolve = - K extends TEnum ? TRecordFromEnumKey : // Enum before Union (intercept Hint) + K extends TEnum ? TRecordFromEnumKey : // Enum before Union (intercept Hint) K extends TUnion ? TRecordFromUnion : K extends TTemplateLiteral ? TRecordFromTemplateLiteralKey : K extends TLiteralString ? TRecordFromLiteralStringKey : @@ -2915,6 +2915,7 @@ export class JsonTypeBuilder extends TypeBuilder { } /** `[Json]` Creates a Enum type */ public Enum>(item: T, options: SchemaOptions = {}): TEnum { + if (ValueGuard.IsUndefined(item)) return this.Throw('Enum undefined or empty') // prettier-ignore const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] const values2 = [...new Set(values1)] diff --git a/test/static/record.ts b/test/static/record.ts index 1951bd4cd..e21356595 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -128,3 +128,51 @@ import { Type, Static } from '@sinclair/typebox' const I = Type.Intersect([R, T]) Expect(I).ToStatic & { x: number; y: number }>() } +{ + // expect T as Object + enum E { + A, + B, + C, + } + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{ + 0: number + 1: number + 2: number + }> +} +{ + // expect T as Partial Object + enum E { + A, + B, + C, + } + const T = Type.Partial(Type.Record(Type.Enum(E), Type.Number())) + Expect(T).ToStatic<{ + 0?: number + 1?: number + 2?: number + }> +} +{ + // expect T to support named properties + enum E { + A = 'A', + B = 'B', + C = 'C', + } + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{ + A: number + B: number + C: number + }> +} +{ + // expect T to support named properties + enum E {} + const T = Type.Record(Type.Enum(E), Type.Number()) + Expect(T).ToStatic<{}> +} From f89bc81fac78f823718a9d50cece67bd3db48c2a Mon Sep 17 00:00:00 2001 From: Florian De la comble Date: Tue, 7 Nov 2023 15:48:21 +0100 Subject: [PATCH 207/369] Ecosystem (#656) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 16449a07b..48c5a97f0 100644 --- a/readme.md +++ b/readme.md @@ -1611,7 +1611,7 @@ The following is a list of community packages that offer general tooling, extend | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | -| [typebox-client](https://github.com/flodlc/typebox-client) | Type safe http client library for Fastify | +| [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | From a770b94c737d7771ca249fb645aad8e915032c60 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 8 Nov 2023 00:29:44 +0900 Subject: [PATCH 208/369] Revision 0.31.23 (#658) * Modifier Inference Path on Required and Partial * Formatting * Sort Ecosystem --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 2 +- src/typebox.ts | 12 ++++++++++-- test/static/partial.ts | 17 ++++++++++++++++- test/static/required.ts | 16 ++++++++++++++++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index a8b8a5930..92ca683d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.22", + "version": "0.31.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.22", + "version": "0.31.23", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 2cb992096..cc5ea9f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.22", + "version": "0.31.23", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 48c5a97f0..d4868e0d7 100644 --- a/readme.md +++ b/readme.md @@ -1608,10 +1608,10 @@ The following is a list of community packages that offer general tooling, extend | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | +| [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | -| [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | diff --git a/src/typebox.ts b/src/typebox.ts index e105237f6..64bb30df1 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -608,7 +608,11 @@ export type TPartialObjectArray = AssertRest<{ [K in keyof export type TPartialRest = AssertRest<{ [K in keyof T]: TPartial> }> // prettier-ignore export type TPartialProperties = Evaluate + [K in keyof T]: + T[K] extends (TReadonlyOptional) ? TReadonlyOptional : + T[K] extends (TReadonly) ? TReadonlyOptional : + T[K] extends (TOptional) ? TOptional : + TOptional }>> // prettier-ignore export type TPartial = @@ -722,7 +726,11 @@ export type TReturnType = T['returns'] export type TRequiredRest = AssertRest<{ [K in keyof T]: TRequired> }> // prettier-ignore export type TRequiredProperties = Evaluate ? S : T[K] + [K in keyof T]: + T[K] extends (TReadonlyOptional) ? TReadonly : + T[K] extends (TReadonly) ? TReadonly : + T[K] extends (TOptional) ? S : + T[K] }>> // prettier-ignore export type TRequired = diff --git a/test/static/partial.ts b/test/static/partial.ts index d0d075a84..f4f81b023 100644 --- a/test/static/partial.ts +++ b/test/static/partial.ts @@ -1,6 +1,6 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' - +import * as Types from '@sinclair/typebox' { const T = Type.Partial( Type.Object({ @@ -70,3 +70,18 @@ import { Type, Static } from '@sinclair/typebox' >() } } +{ + // https://github.com/sinclairzx81/typebox/issues/655 + const T = Type.Object({ + a: Type.ReadonlyOptional(Type.Number()), + b: Type.Readonly(Type.Number()), + c: Type.Optional(Type.Number()), + d: Type.Number(), + }) + const R: Types.TObject<{ + a: Types.TReadonlyOptional + b: Types.TReadonlyOptional + c: Types.TOptional + d: Types.TOptional + }> = Type.Partial(T) +} diff --git a/test/static/required.ts b/test/static/required.ts index cbf9fd4e4..a35c61f22 100644 --- a/test/static/required.ts +++ b/test/static/required.ts @@ -1,5 +1,6 @@ import { Expect } from './assert' import { Type, Static } from '@sinclair/typebox' +import * as Types from '@sinclair/typebox' { const T = Type.Required( Type.Object({ @@ -71,3 +72,18 @@ import { Type, Static } from '@sinclair/typebox' >() } } +{ + // https://github.com/sinclairzx81/typebox/issues/655 + const T = Type.Object({ + a: Type.ReadonlyOptional(Type.Number()), + b: Type.Readonly(Type.Number()), + c: Type.Optional(Type.Number()), + d: Type.Number(), + }) + const R: Types.TObject<{ + a: Types.TReadonly + b: Types.TReadonly + c: Types.TNumber + d: Types.TNumber + }> = Type.Required(T) +} From 9cb7b028a9267bb0ee96eda23760dca7937892af Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 15 Nov 2023 15:26:34 +0900 Subject: [PATCH 209/369] Revision 0.31.24 (#664) * Improve Function and Constructor Inference * Comment updates on Static types --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 34 ++++++++++++++++++++-------------- test/static/constructor.ts | 38 +++++++++++++++++++++++++++++--------- test/static/function.ts | 35 ++++++++++++++++++++++++++++------- test/static/transform.ts | 18 ++++++++++++++++++ 6 files changed, 98 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92ca683d4..184112058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index cc5ea9f99..471eb0a1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.23", + "version": "0.31.24", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 64bb30df1..9afde820d 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -242,9 +242,10 @@ export interface TArray extends TSchema, ArrayOptio // -------------------------------------------------------------------------- // TAsyncIterator // -------------------------------------------------------------------------- +export type TAsyncIteratorResolve = Ensure>> export interface TAsyncIterator extends TSchema { [Kind]: 'AsyncIterator' - static: AsyncIterableIterator> + static: TAsyncIteratorResolve type: 'AsyncIterator' items: T } @@ -280,7 +281,7 @@ export interface TBoolean extends TSchema { // -------------------------------------------------------------------------- // TConstructorParameters // -------------------------------------------------------------------------- -export type TConstructorParameters> = TTuple +export type TConstructorParameters> = Ensure> // -------------------------------------------------------------------------- // TInstanceType // -------------------------------------------------------------------------- @@ -307,10 +308,12 @@ export type TComposite = TIntersect extends TIntersect // -------------------------------------------------------------------------- // TConstructor // -------------------------------------------------------------------------- -export type TConstructorParameterArray = [...{ [K in keyof T]: Static, P> }] +export type TConstructorReturnTypeResolve = Static +export type TConstructorParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] +export type TConstructorResolve = Ensure) => TConstructorReturnTypeResolve> export interface TConstructor extends TSchema { [Kind]: 'Constructor' - static: new (...param: TConstructorParameterArray) => Static + static: TConstructorResolve type: 'Constructor' parameters: T returns: U @@ -386,10 +389,12 @@ export type TExtract = // -------------------------------------------------------------------------- // TFunction // -------------------------------------------------------------------------- -export type TFunctionParameters = [...{ [K in keyof T]: Static, P> }] +export type TFunctionReturnTypeResolve = Static +export type TFunctionParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] +export type TFunctionResolve = Ensure<(...param: TFunctionParametersResolve) => TFunctionReturnTypeResolve> export interface TFunction extends TSchema { [Kind]: 'Function' - static: (...param: TFunctionParameters) => Static + static: TFunctionResolve type: 'Function' parameters: T returns: U @@ -472,9 +477,10 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // TIterator // -------------------------------------------------------------------------- +export type TIteratorResolve = Ensure>> export interface TIterator extends TSchema { [Kind]: 'Iterator' - static: IterableIterator> + static: TIteratorResolve type: 'Iterator' items: T } @@ -880,9 +886,9 @@ export type DecodeType = ( T extends TTransform ? TUnsafe : T extends TArray ? TArray> : T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor> : + T extends TConstructor ? TConstructor, DecodeType> : T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction> : + T extends TFunction ? TFunction, DecodeType> : T extends TIntersect ? TIntersect> : T extends TIterator ? TIterator> : T extends TNot ? TNot> : @@ -988,11 +994,11 @@ export interface TVoid extends TSchema { // -------------------------------------------------------------------------- // Static // -------------------------------------------------------------------------- -/** Creates the decoded static form for a TypeBox type */ +/** Creates an decoded static type from a TypeBox type */ export type StaticDecode = Static, P> -/** Creates the encoded static form for a TypeBox type */ +/** Creates an encoded static type from a TypeBox type */ export type StaticEncode = Static -/** Creates the static type for a TypeBox type */ +/** Creates a static type from a TypeBox type */ export type Static = (T & { params: P })['static'] // -------------------------------------------------------------------------- // TypeRegistry @@ -3313,7 +3319,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) } /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { return this.Tuple([...schema.parameters], { ...options }) } /** `[JavaScript]` Creates a Constructor type */ @@ -3339,7 +3345,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Type(items) }) } /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { return this.Tuple(schema.parameters, { ...options }) } /** `[JavaScript]` Creates a Promise type */ diff --git a/test/static/constructor.ts b/test/static/constructor.ts index a05363991..c91d398e7 100644 --- a/test/static/constructor.ts +++ b/test/static/constructor.ts @@ -1,11 +1,31 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' - -const C = Type.Constructor( - [Type.Number(), Type.String()], - Type.Object({ - method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), - }), -) - -Expect(C).ToStatic { method: (param_0: number, param_1: string) => boolean }>() +{ + // simple + const T = Type.Constructor([Type.Number(), Type.Boolean()], Type.String()) + Expect(T).ToStatic string>() +} +{ + // nested + // prettier-ignore + const T = Type.Constructor([Type.Number(), Type.String()], Type.Object({ + method: Type.Constructor([Type.Number(), Type.String()], Type.Boolean()), + })) + Expect(T).ToStatic { method: new (param_0: number, param_1: string) => boolean }>() +} +{ + // decode 2 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([S], Type.String()) + Expect(T).ToStaticDecode string>() +} +{ + // decode 1 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([Type.Number()], S) + Expect(T).ToStaticDecode Date>() +} diff --git a/test/static/function.ts b/test/static/function.ts index 623aa911e..a204187a6 100644 --- a/test/static/function.ts +++ b/test/static/function.ts @@ -1,11 +1,32 @@ import { Expect } from './assert' import { Type } from '@sinclair/typebox' -const C = Type.Function( - [Type.Number(), Type.String()], - Type.Object({ +{ + // simple + const T = Type.Function([Type.Number(), Type.Boolean()], Type.String()) + Expect(T).ToStatic<(param_0: number, param_1: boolean) => string>() +} +{ + // nested + // prettier-ignore + const T = Type.Function([Type.Number(), Type.String()], Type.Object({ method: Type.Function([Type.Number(), Type.String()], Type.Boolean()), - }), -) - -Expect(C).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() + })) + Expect(T).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() +} +{ + // decode 2 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([S], Type.String()) + Expect(T).ToStaticDecode<(param_0: Date) => string>() +} +{ + // decode 1 + const S = Type.Transform(Type.Integer()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([Type.Number()], S) + Expect(T).ToStaticDecode<(param_0: number) => Date>() +} diff --git a/test/static/transform.ts b/test/static/transform.ts index 468787ef2..917628a67 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -291,3 +291,21 @@ import { Expect } from './assert' Expect(T).ToStaticDecode<1>() Expect(T).ToStaticEncode() } +{ + // should transform functions + const S = Type.Transform(Type.Number()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Function([S], S) + Expect(T).ToStaticDecode<(x: Date) => Date>() + Expect(T).ToStaticEncode<(x: number) => number>() +} +{ + // should transform constructors + const S = Type.Transform(Type.Number()) + .Decode((value) => new Date(value)) + .Encode((value) => value.getTime()) + const T = Type.Constructor([S], S) + Expect(T).ToStaticDecode Date>() + Expect(T).ToStaticEncode number>() +} From 5bd9f12eb16cbede35a3ec7f9d4d325e4aee04a1 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 15 Nov 2023 22:43:37 +0900 Subject: [PATCH 210/369] Revision 0.31.25 (#666) * Github Rendering Fix * Resolve Iterators * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 184112058..79a891d45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.24", + "version": "0.31.25", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.24", + "version": "0.31.25", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 471eb0a1a..62752db56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.24", + "version": "0.31.25", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 9afde820d..aef60231f 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -242,7 +242,7 @@ export interface TArray extends TSchema, ArrayOptio // -------------------------------------------------------------------------- // TAsyncIterator // -------------------------------------------------------------------------- -export type TAsyncIteratorResolve = Ensure>> +export type TAsyncIteratorResolve = AsyncIterableIterator>> export interface TAsyncIterator extends TSchema { [Kind]: 'AsyncIterator' static: TAsyncIteratorResolve @@ -477,7 +477,7 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // TIterator // -------------------------------------------------------------------------- -export type TIteratorResolve = Ensure>> +export type TIteratorResolve = IterableIterator>> export interface TIterator extends TSchema { [Kind]: 'Iterator' static: TIteratorResolve @@ -858,13 +858,13 @@ export type TTemplateLiteralConst = T extends TBoolean ? `${boolean}` : never // prettier-ignore -export type TTemplateLiteralUnion = +export type TTemplateLiteralUnion = T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : Acc export type TTemplateLiteralKeyRest = Assert>, TPropertyKey[]> export interface TTemplateLiteral extends TSchema { [Kind]: 'TemplateLiteral' - static: TTemplateLiteralUnion + static: TTemplateLiteralUnion type: 'string' pattern: string // todo: it may be possible to infer this pattern } From 262de46e6c406ce101807885c4e76e2df535a8b8 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 18 Nov 2023 18:09:15 +0900 Subject: [PATCH 211/369] Revision 0.31.26 (#669) * Tuple Transform Check | Check Both Value and Compiler Encoding in Tests * Iterator Update | Version * Revert Iterator Inference --- package-lock.json | 4 +- package.json | 2 +- src/compiler/compiler.ts | 4 +- src/typebox.ts | 6 +- src/value/transform.ts | 19 +++--- src/value/value.ts | 12 ++-- test/runtime/value/transform/_encoder.ts | 33 +++++++++++ test/runtime/value/transform/any.ts | 9 +-- test/runtime/value/transform/array.ts | 31 +++++----- .../runtime/value/transform/async-iterator.ts | 13 ++-- test/runtime/value/transform/bigint.ts | 13 ++-- test/runtime/value/transform/boolean.ts | 13 ++-- test/runtime/value/transform/constructor.ts | 13 ++-- test/runtime/value/transform/date.ts | 13 ++-- test/runtime/value/transform/enum.ts | 13 ++-- test/runtime/value/transform/function.ts | 13 ++-- test/runtime/value/transform/integer.ts | 13 ++-- test/runtime/value/transform/intersect.ts | 31 +++++----- test/runtime/value/transform/iterator.ts | 13 ++-- test/runtime/value/transform/literal.ts | 23 ++++---- test/runtime/value/transform/never.ts | 9 +-- test/runtime/value/transform/not.ts | 13 ++-- test/runtime/value/transform/null.ts | 13 ++-- test/runtime/value/transform/number.ts | 13 ++-- test/runtime/value/transform/object.ts | 39 ++++++------ test/runtime/value/transform/promise.ts | 13 ++-- test/runtime/value/transform/record.ts | 25 ++++---- test/runtime/value/transform/recursive.ts | 19 +++--- test/runtime/value/transform/ref.ts | 19 +++--- test/runtime/value/transform/string.ts | 35 +++++------ test/runtime/value/transform/symbol.ts | 13 ++-- .../value/transform/template-literal.ts | 13 ++-- test/runtime/value/transform/tuple.ts | 31 +++++----- test/runtime/value/transform/undefined.ts | 13 ++-- test/runtime/value/transform/union.ts | 59 ++++++++++--------- test/runtime/value/transform/unknown.ts | 9 +-- test/runtime/value/transform/unsafe.ts | 13 ++-- 37 files changed, 349 insertions(+), 291 deletions(-) create mode 100644 test/runtime/value/transform/_encoder.ts diff --git a/package-lock.json b/package-lock.json index 79a891d45..48e7f7e19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.25", + "version": "0.31.26", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.25", + "version": "0.31.26", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index 62752db56..f7aa93d5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.25", + "version": "0.31.26", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9e840ca92..fe03f6d2e 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -61,11 +61,11 @@ export class TypeCheck { /** Decodes a value or throws if error */ public Decode(value: unknown): Types.StaticDecode { if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) - return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value + return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value) : value } /** Encodes a value or throws if error */ public Encode(value: unknown): Types.StaticEncode { - const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value, (_, __, value) => this.Check(value)) : value + const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) return encoded } diff --git a/src/typebox.ts b/src/typebox.ts index aef60231f..bb6c50bf4 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -242,10 +242,9 @@ export interface TArray extends TSchema, ArrayOptio // -------------------------------------------------------------------------- // TAsyncIterator // -------------------------------------------------------------------------- -export type TAsyncIteratorResolve = AsyncIterableIterator>> export interface TAsyncIterator extends TSchema { [Kind]: 'AsyncIterator' - static: TAsyncIteratorResolve + static: AsyncIterableIterator> type: 'AsyncIterator' items: T } @@ -477,10 +476,9 @@ export interface TIntersect extends TSchema, In // -------------------------------------------------------------------------- // TIterator // -------------------------------------------------------------------------- -export type TIteratorResolve = IterableIterator>> export interface TIterator extends TSchema { [Kind]: 'Iterator' - static: TIteratorResolve + static: IterableIterator> type: 'Iterator' items: T } diff --git a/src/value/transform.ts b/src/value/transform.ts index 281917f18..d0c7daba5 100644 --- a/src/value/transform.ts +++ b/src/value/transform.ts @@ -26,9 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsString, IsPlainObject, IsArray, IsValueType } from './guard' +import { IsString, IsPlainObject, IsArray, IsValueType, IsUndefined } from './guard' import { ValueError } from '../errors/errors' import { Deref } from './deref' +import { Check } from './check' import * as Types from '../typebox' // ------------------------------------------------------------------------- @@ -112,7 +113,7 @@ export namespace HasTransform { return Visit(Deref(schema, references), references) } function TTuple(schema: Types.TTuple, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || (Types.TypeGuard.TSchema(schema.items) && schema.items.some((schema) => Visit(schema, references))) + return Types.TypeGuard.TTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references))) } function TUnion(schema: Types.TUnion, references: Types.TSchema[]) { return Types.TypeGuard.TTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) @@ -262,7 +263,7 @@ export namespace DecodeTransform { function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { const value1 = Default(schema, value) for (const subschema of schema.anyOf) { - if (!checkFunction(subschema, references, value1)) continue + if (!Check(subschema, references, value1)) continue return Visit(subschema, references, value1) } return value1 @@ -323,9 +324,7 @@ export namespace DecodeTransform { return Default(schema_, value) } } - let checkFunction: CheckFunction = () => false - export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown { - checkFunction = check + export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { return Visit(schema, references, value) } } @@ -405,14 +404,14 @@ export namespace EncodeTransform { function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { // test value against union variants for (const subschema of schema.anyOf) { - if (!checkFunction(subschema, references, value)) continue + if (!Check(subschema, references, value)) continue const value1 = Visit(subschema, references, value) return Default(schema, value1) } // test transformed value against union variants for (const subschema of schema.anyOf) { const value1 = Visit(subschema, references, value) - if (!checkFunction(schema, references, value1)) continue + if (!Check(schema, references, value1)) continue return Default(schema, value1) } return Default(schema, value) @@ -472,9 +471,7 @@ export namespace EncodeTransform { return Default(schema_, value) } } - let checkFunction: CheckFunction = () => false - export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown, check: CheckFunction): unknown { - checkFunction = check + export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { return Visit(schema, references, value) } } diff --git a/src/value/value.ts b/src/value/value.ts index edeec7dae..9cf27168b 100644 --- a/src/value/value.ts +++ b/src/value/value.ts @@ -78,23 +78,23 @@ export namespace Value { return ValueClone.Clone(value) } /** Decodes a value or throws if error */ - export function Decode>(schema: T, references: Types.TSchema[], value: unknown): D + export function Decode>(schema: T, references: Types.TSchema[], value: unknown): R /** Decodes a value or throws if error */ - export function Decode>(schema: T, value: unknown): D + export function Decode>(schema: T, value: unknown): R /** Decodes a value or throws if error */ export function Decode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] if (!Check(schema, references, value)) throw new ValueTransform.TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return ValueTransform.DecodeTransform.Decode(schema, references, value, ValueCheck.Check) + return ValueTransform.DecodeTransform.Decode(schema, references, value) } /** Encodes a value or throws if error */ - export function Encode>(schema: T, references: Types.TSchema[], value: unknown): E + export function Encode>(schema: T, references: Types.TSchema[], value: unknown): R /** Encodes a value or throws if error */ - export function Encode>(schema: T, value: unknown): E + export function Encode>(schema: T, value: unknown): R /** Encodes a value or throws if error */ export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value, ValueCheck.Check) + const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value) if (!Check(schema, references, encoded)) throw new ValueTransform.TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) return encoded } diff --git a/test/runtime/value/transform/_encoder.ts b/test/runtime/value/transform/_encoder.ts new file mode 100644 index 000000000..8449787e6 --- /dev/null +++ b/test/runtime/value/transform/_encoder.ts @@ -0,0 +1,33 @@ +import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol } from '@sinclair/typebox/value/guard' +import { TSchema, StaticDecode, StaticEncode } from '@sinclair/typebox' +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Value } from '@sinclair/typebox/value' +import { Assert } from '../../assert/index' + +function AssertSame(actual: unknown, expect: unknown) { + if (IsAsyncIterator(actual) && IsAsyncIterator(expect)) return + if (IsIterator(actual) && IsIterator(expect)) return + if (IsSymbol(actual) && IsSymbol(expect)) return + if (IsFunction(actual) && IsFunction(expect)) return + Assert.IsEqual(actual, expect) +} + +export function Decode>(schema: T, references: TSchema[], value: unknown): R +export function Decode>(schema: T, value: unknown): R +export function Decode(...args: any[]) { + const [schema, references, value] = args.length === 2 ? [args[0], [], args[1]] : [args[0], args[1], args[2]] + const value1 = TypeCompiler.Compile(schema as TSchema, references).Decode(value) + const value2 = Value.Decode(schema as TSchema, references, value) + AssertSame(value1, value2) + return value2 +} + +export function Encode>(schema: T, references: TSchema[], value: unknown): R +export function Encode>(schema: T, value: unknown): R +export function Encode(...args: any[]) { + const [schema, references, value] = args.length === 2 ? [args[0], [], args[1]] : [args[0], args[1], args[2]] + const value1 = TypeCompiler.Compile(schema as TSchema, references).Encode(value) + const value2 = Value.Encode(schema as TSchema, references, value) + AssertSame(value1, value2) + return value2 +} diff --git a/test/runtime/value/transform/any.ts b/test/runtime/value/transform/any.ts index 91281c315..a1b27e28c 100644 --- a/test/runtime/value/transform/any.ts +++ b/test/runtime/value/transform/any.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,11 +11,11 @@ describe('value/transform/Any', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { - const R = Value.Decode(T0, 123) + const R = Encoder.Decode(T0, 123) Assert.IsEqual(R, 123) }) it('Should encode mapped', () => { - const R = Value.Encode(T0, 123) + const R = Encoder.Encode(T0, 123) Assert.IsEqual(R, 123) }) // -------------------------------------------------------- @@ -24,11 +25,11 @@ describe('value/transform/Any', () => { .Decode((value) => 1) .Encode((value) => 2) it('Should decode mapped', () => { - const R = Value.Decode(T1, null) + const R = Encoder.Decode(T1, null) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 2) }) }) diff --git a/test/runtime/value/transform/array.ts b/test/runtime/value/transform/array.ts index 96afb073e..8f688b2e8 100644 --- a/test/runtime/value/transform/array.ts +++ b/test/runtime/value/transform/array.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Array', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { - const R = Value.Decode(T0, [0, 1, 2]) + const R = Encoder.Decode(T0, [0, 1, 2]) Assert.IsEqual(R, [0, 1, 2]) }) it('Should encode mapped', () => { - const R = Value.Encode(T0, [0, 1, 2]) + const R = Encoder.Encode(T0, [0, 1, 2]) Assert.IsEqual(R, [0, 1, 2]) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,15 +28,15 @@ describe('value/transform/Array', () => { .Decode((value) => 1) .Encode((value) => [0, 1, 2]) it('Should decode mapped', () => { - const R = Value.Decode(T1, []) + const R = Encoder.Decode(T1, []) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, [0, 1, 2]) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // -------------------------------------------------------- // Elements @@ -45,15 +46,15 @@ describe('value/transform/Array', () => { .Encode((value) => (value === 'TRUE' ? true : false)) const T2 = Type.Array(B2) it('Should decode elements', () => { - const R = Value.Decode(T2, [true, false]) + const R = Encoder.Decode(T2, [true, false]) Assert.IsEqual(R, ['TRUE', 'FALSE']) }) it('Should encode elements', () => { - const R = Value.Encode(T2, ['TRUE', 'FALSE']) + const R = Encoder.Encode(T2, ['TRUE', 'FALSE']) Assert.IsEqual(R, [true, false]) }) it('Should throw on elements decode', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) // -------------------------------------------------------- // Elements Contains (Not Supported) @@ -65,14 +66,14 @@ describe('value/transform/Array', () => { contains: N3, }) it('Should decode contains', () => { - const R = Value.Decode(T3, [1, 2, 3]) + const R = Encoder.Decode(T3, [1, 2, 3]) Assert.IsEqual(R, [1, 2, 3]) }) it('Should throw on contains encode', () => { - Assert.Throws(() => Value.Encode(T3, ['hello', 2, 3])) + Assert.Throws(() => Encoder.Encode(T3, ['hello', 2, 3])) }) it('Should throw on contains decode', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) // ------------------------------------------------------------ // Set @@ -81,17 +82,17 @@ describe('value/transform/Array', () => { .Decode((value) => new Set(value)) .Encode((value) => [...value]) it('should decode set', () => { - const R = Value.Decode(T4, [1, 1, 2, 3]) + const R = Encoder.Decode(T4, [1, 1, 2, 3]) Assert.IsInstanceOf(R, Set) Assert.IsTrue(R.has(1)) Assert.IsTrue(R.has(2)) Assert.IsTrue(R.has(3)) }) it('should encode set', () => { - const R = Value.Encode(T4, new Set([1, 2, 3])) + const R = Encoder.Encode(T4, new Set([1, 2, 3])) Assert.IsEqual(R, [1, 2, 3]) }) it('Should throw on set decode', () => { - Assert.Throws(() => Value.Decode(T4, {})) + Assert.Throws(() => Encoder.Decode(T4, {})) }) }) diff --git a/test/runtime/value/transform/async-iterator.ts b/test/runtime/value/transform/async-iterator.ts index c418d723f..aed1f3eb3 100644 --- a/test/runtime/value/transform/async-iterator.ts +++ b/test/runtime/value/transform/async-iterator.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/AsyncIterator', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, (async function* (): any {})()) + const R = Encoder.Decode(T0, (async function* (): any {})()) Assert.IsTrue(Symbol.asyncIterator in R) }) it('Should encode identity', () => { - const R = Value.Encode(T0, (async function* (): any {})()) + const R = Encoder.Encode(T0, (async function* (): any {})()) Assert.IsTrue(Symbol.asyncIterator in R) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/AsyncIterator', () => { .Decode((value) => 1) .Encode((value) => (async function* (): any {})()) it('Should decode', () => { - const R = Value.Decode(T1, (async function* (): any {})()) + const R = Encoder.Decode(T1, (async function* (): any {})()) Assert.IsEqual(R, 1) }) it('Should encode', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsTrue(Symbol.asyncIterator in R) }) it('Should throw on decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/bigint.ts b/test/runtime/value/transform/bigint.ts index ffc66b058..280af3114 100644 --- a/test/runtime/value/transform/bigint.ts +++ b/test/runtime/value/transform/bigint.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/BigInt', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 5n) + const R = Encoder.Decode(T0, 5n) Assert.IsEqual(R, 5n) }) it('Should encode identity', () => { - const R = Value.Encode(T0, 5n) + const R = Encoder.Encode(T0, 5n) Assert.IsEqual(R, 5n) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/BigInt', () => { .Decode((value) => 1) .Encode((value) => 2n) it('Should decode mapped', () => { - const R = Value.Decode(T1, 1n) + const R = Encoder.Decode(T1, 1n) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 2n) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/boolean.ts b/test/runtime/value/transform/boolean.ts index 1421a1e8a..ed9c47cee 100644 --- a/test/runtime/value/transform/boolean.ts +++ b/test/runtime/value/transform/boolean.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Boolean', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, true) + const R = Encoder.Decode(T0, true) Assert.IsEqual(R, true) }) it('Should encode identity', () => { - const R = Value.Encode(T0, false) + const R = Encoder.Encode(T0, false) Assert.IsEqual(R, false) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Boolean', () => { .Decode((value) => 1) .Encode((value) => true) it('Should decode mapped', () => { - const R = Value.Decode(T1, true) + const R = Encoder.Decode(T1, true) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, true) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/constructor.ts b/test/runtime/value/transform/constructor.ts index 0cfe28d43..67ae9d19a 100644 --- a/test/runtime/value/transform/constructor.ts +++ b/test/runtime/value/transform/constructor.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Constructor', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, class {}) + const R = Encoder.Decode(T0, class {}) Assert.IsTypeOf(R, 'function') }) it('Should encode identity', () => { - const R = Value.Encode(T0, class {}) + const R = Encoder.Encode(T0, class {}) Assert.IsTypeOf(R, 'function') }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Constructor', () => { .Decode((value) => 1) .Encode((value) => class {}) it('Should decode mapped', () => { - const R = Value.Decode(T1, class {}) + const R = Encoder.Decode(T1, class {}) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsTypeOf(R, 'function') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/date.ts b/test/runtime/value/transform/date.ts index 1d83713e0..bc7fec5f5 100644 --- a/test/runtime/value/transform/date.ts +++ b/test/runtime/value/transform/date.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Date', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, new Date(123)) + const R = Encoder.Decode(T0, new Date(123)) Assert.IsEqual(R, new Date(123)) }) it('Should encode identity', () => { - const R = Value.Encode(T0, new Date(123)) + const R = Encoder.Encode(T0, new Date(123)) Assert.IsEqual(R, new Date(123)) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Date', () => { .Decode((value) => 1) .Encode((value) => new Date()) it('Should decode mapped', () => { - const R = Value.Decode(T1, new Date()) + const R = Encoder.Decode(T1, new Date()) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsInstanceOf(R, Date) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/enum.ts b/test/runtime/value/transform/enum.ts index 1df8f7793..b681ae33b 100644 --- a/test/runtime/value/transform/enum.ts +++ b/test/runtime/value/transform/enum.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -15,15 +16,15 @@ describe('value/transform/Enum', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, E.A) + const R = Encoder.Decode(T0, E.A) Assert.IsEqual(R, E.A) }) it('Should encode identity', () => { - const R = Value.Encode(T0, E.A) + const R = Encoder.Encode(T0, E.A) Assert.IsEqual(R, E.A) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -32,14 +33,14 @@ describe('value/transform/Enum', () => { .Decode((value) => 1) .Encode((value) => E.A) it('Should decode mapped', () => { - const R = Value.Decode(T1, E.A) + const R = Encoder.Decode(T1, E.A) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, E.A) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/function.ts b/test/runtime/value/transform/function.ts index 5d94b8d72..4bd89abc6 100644 --- a/test/runtime/value/transform/function.ts +++ b/test/runtime/value/transform/function.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Function', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, class {}) + const R = Encoder.Decode(T0, class {}) Assert.IsTypeOf(R, 'function') }) it('Should encode identity', () => { - const R = Value.Encode(T0, class {}) + const R = Encoder.Encode(T0, class {}) Assert.IsTypeOf(R, 'function') }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Function', () => { .Decode((value) => 1) .Encode((value) => function () {}) it('Should decode mapped', () => { - const R = Value.Decode(T1, function () {}) + const R = Encoder.Decode(T1, function () {}) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsTypeOf(R, 'function') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/integer.ts b/test/runtime/value/transform/integer.ts index 2f91f3f35..099439aff 100644 --- a/test/runtime/value/transform/integer.ts +++ b/test/runtime/value/transform/integer.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Integer', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 42) + const R = Encoder.Decode(T0, 42) Assert.IsEqual(R, 42) }) it('Should encode identity', () => { - const R = Value.Encode(T0, 42) + const R = Encoder.Encode(T0, 42) Assert.IsEqual(R, 42) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Integer', () => { .Decode((value) => 1) .Encode((value) => 2) it('Should decode mapped', () => { - const R = Value.Decode(T1, 1) + const R = Encoder.Decode(T1, 1) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 2) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/intersect.ts b/test/runtime/value/transform/intersect.ts index 342f93abe..0d7f39043 100644 --- a/test/runtime/value/transform/intersect.ts +++ b/test/runtime/value/transform/intersect.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -14,15 +15,15 @@ describe('value/transform/Intersect', () => { .Decode(value => value) .Encode(value => value) it('Should decode identity', () => { - const R = Value.Decode(T0, { x: 1, y: 2 }) + const R = Encoder.Decode(T0, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should encode identity', () => { - const R = Value.Encode(T0, { x: 1, y: 2 }) + const R = Encoder.Encode(T0, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -35,15 +36,15 @@ describe('value/transform/Intersect', () => { .Decode((value) => 1) .Encode((value) => ({ x: 1, y: 2 })) it('Should decode mapped', () => { - const R = Value.Decode(T1, { x: 1, y: 2 }) + const R = Encoder.Decode(T1, { x: 1, y: 2 }) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // -------------------------------------------------------- // Mapped Property @@ -59,15 +60,15 @@ describe('value/transform/Intersect', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode property', () => { - const R = Value.Decode(T2, { x: 1, y: 2 }) + const R = Encoder.Decode(T2, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: '2' }) }) it('Should encode property', () => { - const R = Value.Encode(T2, { x: 1, y: '2' }) + const R = Encoder.Encode(T2, { x: 1, y: '2' }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on property decode', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) // -------------------------------------------------------- // Unevaluated Property @@ -83,15 +84,15 @@ describe('value/transform/Intersect', () => { unevaluatedProperties: N3 }) it('Should decode unevaluated property', () => { - const R = Value.Decode(T3, { x: 1, y: 2, z: 3 }) + const R = Encoder.Decode(T3, { x: 1, y: 2, z: 3 }) Assert.IsEqual(R, { x: 1, y: 2, z: '3' }) }) it('Should encode unevaluated property', () => { - const R = Value.Encode(T3, { x: 1, y: 2, z: '3' }) + const R = Encoder.Encode(T3, { x: 1, y: 2, z: '3' }) Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) }) it('Should throw on unevaluated property decode', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) // -------------------------------------------------------- // Transform Intersection Interior (Not Supported) @@ -109,14 +110,14 @@ describe('value/transform/Intersect', () => { .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode exterior value type', () => { - const R = Value.Decode(T4, 1) + const R = Encoder.Decode(T4, 1) Assert.IsEqual(R, 2) }) it('Should encode exterior value type', () => { - const R = Value.Encode(T4, 2) + const R = Encoder.Encode(T4, 2) Assert.IsEqual(R, 1) }) it('Should throw on exterior value type decode', () => { - Assert.Throws(() => Value.Decode(T4, null)) + Assert.Throws(() => Encoder.Decode(T4, null)) }) }) diff --git a/test/runtime/value/transform/iterator.ts b/test/runtime/value/transform/iterator.ts index 90bc03773..66188acf1 100644 --- a/test/runtime/value/transform/iterator.ts +++ b/test/runtime/value/transform/iterator.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Iterator', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, (function* (): any {})()) + const R = Encoder.Decode(T0, (function* (): any {})()) Assert.IsTrue(Symbol.iterator in R) }) it('Should encode identity', () => { - const R = Value.Encode(T0, (function* (): any {})()) + const R = Encoder.Encode(T0, (function* (): any {})()) Assert.IsTrue(Symbol.iterator in R) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Iterator', () => { .Decode((value) => 1) .Encode((value) => (function* (): any {})()) it('Should decode mapped', () => { - const R = Value.Decode(T1, (function* (): any {})()) + const R = Encoder.Decode(T1, (function* (): any {})()) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsTrue(Symbol.iterator in R) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/literal.ts b/test/runtime/value/transform/literal.ts index 9e0f9e41c..ef88502bb 100644 --- a/test/runtime/value/transform/literal.ts +++ b/test/runtime/value/transform/literal.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,11 +11,11 @@ describe('value/transform/Literal', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { - const R = Value.Decode(T0, 123) + const R = Encoder.Decode(T0, 123) Assert.IsEqual(R, 123) }) it('Should encode mapped', () => { - const R = Value.Encode(T0, 123) + const R = Encoder.Encode(T0, 123) Assert.IsEqual(R, 123) }) // ----------------------------------------------- @@ -24,15 +25,15 @@ describe('value/transform/Literal', () => { .Decode((value) => 1) .Encode((value) => 'hello' as const) it('Should decode literal string', () => { - const R = Value.Decode(T1, 'hello') + const R = Encoder.Decode(T1, 'hello') Assert.IsEqual(R, 1) }) it('Should encode literal string', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 'hello') }) it('Should throw on decode literal string', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // ----------------------------------------------- // LiteralNumber @@ -41,15 +42,15 @@ describe('value/transform/Literal', () => { .Decode((value) => 1) .Encode((value) => 2 as const) it('Should decode literal number', () => { - const R = Value.Decode(T2, 2) + const R = Encoder.Decode(T2, 2) Assert.IsEqual(R, 1) }) it('Should encode literal number', () => { - const R = Value.Encode(T2, null) + const R = Encoder.Encode(T2, null) Assert.IsEqual(R, 2) }) it('Should throw on decode literal number', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) // ----------------------------------------------- // LiteralBoolean @@ -58,14 +59,14 @@ describe('value/transform/Literal', () => { .Decode((value) => 1) .Encode((value) => true as const) it('Should decode literal boolean', () => { - const R = Value.Decode(T3, true) + const R = Encoder.Decode(T3, true) Assert.IsEqual(R, 1) }) it('Should encode literal boolean', () => { - const R = Value.Encode(T3, null) + const R = Encoder.Encode(T3, null) Assert.IsEqual(R, true) }) it('Should throw on decode literal boolean', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) }) diff --git a/test/runtime/value/transform/never.ts b/test/runtime/value/transform/never.ts index 3a5414821..97e03478a 100644 --- a/test/runtime/value/transform/never.ts +++ b/test/runtime/value/transform/never.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -11,10 +12,10 @@ describe('value/transform/Never', () => { // @ts-ignore .Encode((value) => value) it('Should throw on identity encode', () => { - Assert.Throws(() => Value.Encode(T0, undefined)) + Assert.Throws(() => Encoder.Encode(T0, undefined)) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, undefined)) + Assert.Throws(() => Encoder.Decode(T0, undefined)) }) // -------------------------------------------------------- // Mapped @@ -25,9 +26,9 @@ describe('value/transform/Never', () => { // @ts-ignore .Encode((value) => 1) it('Should throw on mapped encode', () => { - Assert.Throws(() => Value.Encode(T1, undefined)) + Assert.Throws(() => Encoder.Encode(T1, undefined)) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, undefined)) + Assert.Throws(() => Encoder.Decode(T1, undefined)) }) }) diff --git a/test/runtime/value/transform/not.ts b/test/runtime/value/transform/not.ts index 97277223e..68387ee58 100644 --- a/test/runtime/value/transform/not.ts +++ b/test/runtime/value/transform/not.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Not', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 1) + const R = Encoder.Decode(T0, 1) Assert.IsEqual(R, 1) }) it('Should encode identity', () => { - const R = Value.Encode(T0, 1) + const R = Encoder.Encode(T0, 1) Assert.IsEqual(R, 1) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, '')) + Assert.Throws(() => Encoder.Decode(T0, '')) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Not', () => { .Decode((value) => 1) .Encode((value) => 2) it('Should decode mapped', () => { - const R = Value.Decode(T1, null) + const R = Encoder.Decode(T1, null) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 2) }) it('Should throw on decode not', () => { - Assert.Throws(() => Value.Decode(T1, 'hello')) + Assert.Throws(() => Encoder.Decode(T1, 'hello')) }) }) diff --git a/test/runtime/value/transform/null.ts b/test/runtime/value/transform/null.ts index a113d1566..0cf90dc05 100644 --- a/test/runtime/value/transform/null.ts +++ b/test/runtime/value/transform/null.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Null', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, null) + const R = Encoder.Decode(T0, null) Assert.IsEqual(R, null) }) it('Should encode identity', () => { - const R = Value.Encode(T0, null) + const R = Encoder.Encode(T0, null) Assert.IsEqual(R, null) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, undefined)) + Assert.Throws(() => Encoder.Decode(T0, undefined)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Null', () => { .Decode((value) => 1) .Encode((value) => null) it('Should decode mapped', () => { - const R = Value.Decode(T1, null) + const R = Encoder.Decode(T1, null) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 123) + const R = Encoder.Encode(T1, 123) Assert.IsEqual(R, null) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, undefined)) + Assert.Throws(() => Encoder.Decode(T1, undefined)) }) }) diff --git a/test/runtime/value/transform/number.ts b/test/runtime/value/transform/number.ts index dcd9328c7..2f06b50ff 100644 --- a/test/runtime/value/transform/number.ts +++ b/test/runtime/value/transform/number.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Number', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 42) + const R = Encoder.Decode(T0, 42) Assert.IsEqual(R, 42) }) it('Should encode identity', () => { - const R = Value.Encode(T0, 42) + const R = Encoder.Encode(T0, 42) Assert.IsEqual(R, 42) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Number', () => { .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode mapped', () => { - const R = Value.Decode(T1, 1) + const R = Encoder.Decode(T1, 1) Assert.IsEqual(R, 2) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 2) + const R = Encoder.Encode(T1, 2) Assert.IsEqual(R, 1) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, undefined)) + Assert.Throws(() => Encoder.Decode(T1, undefined)) }) }) diff --git a/test/runtime/value/transform/object.ts b/test/runtime/value/transform/object.ts index 2c3c0b423..0a73a998e 100644 --- a/test/runtime/value/transform/object.ts +++ b/test/runtime/value/transform/object.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -15,15 +16,15 @@ describe('value/transform/Object', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, { x: 1, y: 2 }) + const R = Encoder.Decode(T0, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should encode identity', () => { - const R = Value.Encode(T0, { x: 1, y: 2 }) + const R = Encoder.Encode(T0, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, undefined)) + Assert.Throws(() => Encoder.Decode(T0, undefined)) }) // ---------------------------------------------------------- // Object @@ -37,15 +38,15 @@ describe('value/transform/Object', () => { .Decode((value) => 42) .Encode((value) => ({ x: 1, y: 2 })) it('Should decode mapped', () => { - const R = Value.Decode(T1, { x: 1, y: 2 }) + const R = Encoder.Decode(T1, { x: 1, y: 2 }) Assert.IsEqual(R, 42) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, undefined)) + Assert.Throws(() => Encoder.Decode(T1, undefined)) }) // ---------------------------------------------------------- // Object: Transform Property @@ -58,15 +59,15 @@ describe('value/transform/Object', () => { y: N2, }) it('Should decode transform property', () => { - const R = Value.Decode(T2, { x: 1, y: 2 }) + const R = Encoder.Decode(T2, { x: 1, y: 2 }) Assert.IsEqual(R, { x: '1', y: '2' }) }) it('Should encode transform property', () => { - const R = Value.Encode(T2, { x: '1', y: '2' }) + const R = Encoder.Encode(T2, { x: '1', y: '2' }) Assert.IsEqual(R, { x: 1, y: 2 }) }) it('Should throw on decode transform property', () => { - Assert.Throws(() => Value.Decode(T2, undefined)) + Assert.Throws(() => Encoder.Decode(T2, undefined)) }) // ---------------------------------------------------------- // Object: Transform Property Nested (Twizzle) @@ -83,15 +84,15 @@ describe('value/transform/Object', () => { .Decode((value) => ({ x: value.y, y: value.x })) .Encode((value) => ({ x: value.y, y: value.x })) it('Should decode transform property nested', () => { - const R = Value.Decode(T3, { x: 1, y: 2 }) + const R = Encoder.Decode(T3, { x: 1, y: 2 }) Assert.IsEqual(R, { x: '2', y: '1' }) }) it('Should encode transform property nested', () => { - const R = Value.Encode(T3, { x: '1', y: '2' }) + const R = Encoder.Encode(T3, { x: '1', y: '2' }) Assert.IsEqual(R, { x: 2, y: 1 }) }) it('Should throw on decode transform property nested', () => { - Assert.Throws(() => Value.Decode(T3, undefined)) + Assert.Throws(() => Encoder.Decode(T3, undefined)) }) // ---------------------------------------------------------- // Object Additional Properties @@ -112,18 +113,18 @@ describe('value/transform/Object', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode additional property', () => { - const R = Value.Decode(T4, { x: 1, y: 2 }) + const R = Encoder.Decode(T4, { x: 1, y: 2 }) Assert.IsEqual(R, { x: 1, y: '2' }) }) it('Should encode additional property', () => { - const R = Value.Encode(T4, { x: 1, y: '5' }) + const R = Encoder.Encode(T4, { x: 1, y: '5' }) Assert.IsEqual(R, { x: 1, y: 5 }) }) it('Should throw on additional property 1', () => { - Assert.Throws(() => Value.Decode(T4, undefined)) + Assert.Throws(() => Encoder.Decode(T4, undefined)) }) it('Should throw on additional property 2', () => { - Assert.Throws(() => Value.Decode(T4, { x: 1, y: true })) + Assert.Throws(() => Encoder.Decode(T4, { x: 1, y: true })) }) // ------------------------------------------------------------ // Map @@ -132,13 +133,13 @@ describe('value/transform/Object', () => { .Decode((value) => new Map(Object.entries(value))) .Encode((value) => Object.fromEntries(value.entries()) as any) it('should decode map', () => { - const R = Value.Decode(T5, { x: 'hello', y: 'world' }) + const R = Encoder.Decode(T5, { x: 'hello', y: 'world' }) Assert.IsInstanceOf(R, Map) Assert.IsEqual(R.get('x'), 'hello') Assert.IsEqual(R.get('y'), 'world') }) it('should encode map', () => { - const R = Value.Encode( + const R = Encoder.Encode( T5, new Map([ ['x', 'hello'], @@ -148,6 +149,6 @@ describe('value/transform/Object', () => { Assert.IsEqual(R, { x: 'hello', y: 'world' }) }) it('Should throw on map decode', () => { - Assert.Throws(() => Value.Decode(T5, {})) + Assert.Throws(() => Encoder.Decode(T5, {})) }) }) diff --git a/test/runtime/value/transform/promise.ts b/test/runtime/value/transform/promise.ts index 9a4f33c45..991bf5822 100644 --- a/test/runtime/value/transform/promise.ts +++ b/test/runtime/value/transform/promise.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Promise', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, Promise.resolve(1)) + const R = Encoder.Decode(T0, Promise.resolve(1)) Assert.IsTrue(R instanceof Promise) }) it('Should encode identity', () => { - const R = Value.Encode(T0, Promise.resolve(1)) + const R = Encoder.Encode(T0, Promise.resolve(1)) Assert.IsTrue(R instanceof Promise) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, undefined)) + Assert.Throws(() => Encoder.Decode(T0, undefined)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Promise', () => { .Decode((value) => 1) .Encode((value) => Promise.resolve(1)) it('Should decode mapped', () => { - const R = Value.Decode(T, Promise.resolve(1)) + const R = Encoder.Decode(T, Promise.resolve(1)) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T, null) + const R = Encoder.Encode(T, null) Assert.IsTrue(R instanceof Promise) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T, null)) + Assert.Throws(() => Encoder.Decode(T, null)) }) }) diff --git a/test/runtime/value/transform/record.ts b/test/runtime/value/transform/record.ts index 2b2bc3d82..a91172dbf 100644 --- a/test/runtime/value/transform/record.ts +++ b/test/runtime/value/transform/record.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,7 +11,7 @@ describe('value/transform/Record', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, { + const R = Encoder.Decode(T0, { a: true, b: false, }) @@ -20,7 +21,7 @@ describe('value/transform/Record', () => { }) }) it('Should encode identity', () => { - const R = Value.Encode(T0, { + const R = Encoder.Encode(T0, { a: true, b: false, }) @@ -30,7 +31,7 @@ describe('value/transform/Record', () => { }) }) it('Should throw on identity', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // ------------------------------------------------------------ // Additional Properties True @@ -40,7 +41,7 @@ describe('value/transform/Record', () => { .Encode((value) => parseFloat(value.replace(/number-/g, ''))) const T1 = Type.Record(Type.Number(), N1) it('Should decode additional properties allowed', () => { - const R = Value.Decode(T1, { + const R = Encoder.Decode(T1, { 0: 1, a: true, }) @@ -50,7 +51,7 @@ describe('value/transform/Record', () => { }) }) it('Should encode additional properties allowed', () => { - const R = Value.Encode(T1, { + const R = Encoder.Encode(T1, { 0: 'number-1', a: true, }) @@ -60,7 +61,7 @@ describe('value/transform/Record', () => { }) }) it('Should throw on additional properties allowed', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // ------------------------------------------------------------ // Complex Transform @@ -73,7 +74,7 @@ describe('value/transform/Record', () => { .Encode((value) => (value === 'TRUE' ? true : false)) const T3 = Type.Record(Type.Number(), N2, { additionalProperties: B2 }) it('Should decode complex', () => { - const R = Value.Decode(T3, { + const R = Encoder.Decode(T3, { 0: 1, a: true, }) @@ -83,7 +84,7 @@ describe('value/transform/Record', () => { }) }) it('Should encode complex', () => { - const R = Value.Encode(T3, { + const R = Encoder.Encode(T3, { 0: 'number-1', a: 'TRUE', }) @@ -93,7 +94,7 @@ describe('value/transform/Record', () => { }) }) it('Should throw on complex decode', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) // ------------------------------------------------------------ // Map @@ -102,13 +103,13 @@ describe('value/transform/Record', () => { .Decode((value) => new Map(Object.entries(value))) .Encode((value) => Object.fromEntries(value.entries())) it('should decode map', () => { - const R = Value.Decode(T4, { x: 'hello', y: 'world' }) + const R = Encoder.Decode(T4, { x: 'hello', y: 'world' }) Assert.IsInstanceOf(R, Map) Assert.IsEqual(R.get('x'), 'hello') Assert.IsEqual(R.get('y'), 'world') }) it('should encode map', () => { - const R = Value.Encode( + const R = Encoder.Encode( T4, new Map([ ['x', 'hello'], @@ -118,6 +119,6 @@ describe('value/transform/Record', () => { Assert.IsEqual(R, { x: 'hello', y: 'world' }) }) it('Should throw on map decode', () => { - Assert.Throws(() => Value.Decode(T4, null)) + Assert.Throws(() => Encoder.Decode(T4, null)) }) }) diff --git a/test/runtime/value/transform/recursive.ts b/test/runtime/value/transform/recursive.ts index 2b22d42e1..abea62630 100644 --- a/test/runtime/value/transform/recursive.ts +++ b/test/runtime/value/transform/recursive.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -17,7 +18,7 @@ describe('value/transform/Recursive', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, { + const R = Encoder.Decode(T0, { value: 1, nodes: [ { value: 2, nodes: [] }, @@ -33,7 +34,7 @@ describe('value/transform/Recursive', () => { }) }) it('Should encode identity', () => { - const R = Value.Encode(T0, { + const R = Encoder.Encode(T0, { value: 1, nodes: [ { value: 2, nodes: [] }, @@ -49,7 +50,7 @@ describe('value/transform/Recursive', () => { }) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, undefined)) + Assert.Throws(() => Encoder.Decode(T0, undefined)) }) // ----------------------------------------------- // Mapped @@ -65,7 +66,7 @@ describe('value/transform/Recursive', () => { .Decode((value) => 1) .Encode((value) => ({ value: 1, nodes: [] })) it('Should decode mapped', () => { - const R = Value.Decode(T1, { + const R = Encoder.Decode(T1, { value: 1, nodes: [ { value: 2, nodes: [] }, @@ -75,11 +76,11 @@ describe('value/transform/Recursive', () => { Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, { value: 1, nodes: [] }) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // ----------------------------------------------- // Recursive Property Remap @@ -94,7 +95,7 @@ describe('value/transform/Recursive', () => { }), ) it('Should decode property', () => { - const R = Value.Decode(T2, { + const R = Encoder.Decode(T2, { value: 1, nodes: [ { value: 2, nodes: [] }, @@ -110,7 +111,7 @@ describe('value/transform/Recursive', () => { }) }) it('Should encode property', () => { - const R = Value.Encode(T2, { + const R = Encoder.Encode(T2, { value: new Date(1), nodes: [ { value: new Date(2), nodes: [] }, @@ -126,6 +127,6 @@ describe('value/transform/Recursive', () => { }) }) it('Should throw on decode property', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) }) diff --git a/test/runtime/value/transform/ref.ts b/test/runtime/value/transform/ref.ts index 20385f9bb..92d252c29 100644 --- a/test/runtime/value/transform/ref.ts +++ b/test/runtime/value/transform/ref.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -11,15 +12,15 @@ describe('value/transform/Ref', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { - const R = Value.Decode(T0, [N0], 0) + const R = Encoder.Decode(T0, [N0], 0) Assert.IsEqual(R, 0) }) it('Should encode mapped', () => { - const R = Value.Encode(T0, [N0], 0) + const R = Encoder.Encode(T0, [N0], 0) Assert.IsEqual(R, 0) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -29,15 +30,15 @@ describe('value/transform/Ref', () => { .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode mapped', () => { - const R = Value.Decode(T1, [N1], 0) + const R = Encoder.Decode(T1, [N1], 0) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, [N1], 1) + const R = Encoder.Encode(T1, [N1], 1) Assert.IsEqual(R, 0) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // -------------------------------------------------------- // Mapped Remote @@ -49,14 +50,14 @@ describe('value/transform/Ref', () => { .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode mapped remote', () => { - const R = Value.Decode(T2, [N2], 0) + const R = Encoder.Decode(T2, [N2], 0) Assert.IsEqual(R, 2) }) it('Should encode mapped remote', () => { - const R = Value.Encode(T2, [N2], 2) + const R = Encoder.Encode(T2, [N2], 2) Assert.IsEqual(R, 0) }) it('Should throw on mapped remote decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/string.ts b/test/runtime/value/transform/string.ts index 6c15c074f..3419c8691 100644 --- a/test/runtime/value/transform/string.ts +++ b/test/runtime/value/transform/string.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -6,20 +7,20 @@ describe('value/transform/String', () => { // -------------------------------------------------------- // Identity // -------------------------------------------------------- - const T0 = Type.Transform(Type.String()) - .Decode((value) => value) - .Encode((value) => value) - it('Should decode identity', () => { - const R = Value.Decode(T0, 'hello') - Assert.IsEqual(R, 'hello') - }) - it('Should encode identity', () => { - const R = Value.Encode(T0, 'hello') - Assert.IsEqual(R, 'hello') - }) - it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) - }) + // const T0 = Type.Transform(Type.String()) + // .Decode((value) => value) + // .Encode((value) => value) + // it('Should decode identity', () => { + // const R = Encoder.Decode(T0, 'hello') + // Assert.IsEqual(R, 'hello') + // }) + // it('Should encode identity', () => { + // const R = Encoder.Encode(T0, 'hello') + // Assert.IsEqual(R, 'hello') + // }) + // it('Should throw on identity decode', () => { + // Assert.Throws(() => Encoder.Decode(T0, null)) + // }) // -------------------------------------------------------- // Mapped // -------------------------------------------------------- @@ -27,14 +28,14 @@ describe('value/transform/String', () => { .Decode((value) => value.split('').reverse().join('')) .Encode((value) => value.split('').reverse().join('')) it('Should decode mapped', () => { - const R = Value.Decode(T1, 'ABC') + const R = Encoder.Decode(T1, 'ABC') Assert.IsEqual(R, 'CBA') }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 'CBA') + const R = Encoder.Encode(T1, 'CBA') Assert.IsEqual(R, 'ABC') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/symbol.ts b/test/runtime/value/transform/symbol.ts index 5dfca318d..a04a9d661 100644 --- a/test/runtime/value/transform/symbol.ts +++ b/test/runtime/value/transform/symbol.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/String', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, Symbol('hello')) + const R = Encoder.Decode(T0, Symbol('hello')) Assert.IsEqual(R.description, 'hello') }) it('Should encode identity', () => { - const R = Value.Encode(T0, Symbol('hello')) + const R = Encoder.Encode(T0, Symbol('hello')) Assert.IsEqual(R.description, 'hello') }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/String', () => { .Decode((value) => Symbol(value.description?.split('').reverse().join(''))) .Encode((value) => Symbol(value.description?.split('').reverse().join(''))) it('Should decode mapped', () => { - const R = Value.Decode(T1, Symbol('ABC')) + const R = Encoder.Decode(T1, Symbol('ABC')) Assert.IsEqual(R.description, 'CBA') }) it('Should encode mapped', () => { - const R = Value.Encode(T1, Symbol('CBA')) + const R = Encoder.Encode(T1, Symbol('CBA')) Assert.IsEqual(R.description, 'ABC') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/template-literal.ts b/test/runtime/value/transform/template-literal.ts index 174abaa2b..3c8130a04 100644 --- a/test/runtime/value/transform/template-literal.ts +++ b/test/runtime/value/transform/template-literal.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/TemplateLiteral', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 'hello') + const R = Encoder.Decode(T0, 'hello') Assert.IsEqual(R, 'hello') }) it('Should encode identity', () => { - const R = Value.Encode(T0, 'hello') + const R = Encoder.Encode(T0, 'hello') Assert.IsEqual(R, 'hello') }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/TemplateLiteral', () => { .Decode((value) => value.split('').reverse().join('') as 'CBA') .Encode((value) => value.split('').reverse().join('') as 'ABC') it('Should decode mapped', () => { - const R = Value.Decode(T1, 'ABC') + const R = Encoder.Decode(T1, 'ABC') Assert.IsEqual(R, 'CBA') }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 'CBA') + const R = Encoder.Encode(T1, 'CBA') Assert.IsEqual(R, 'ABC') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/tuple.ts b/test/runtime/value/transform/tuple.ts index cf6b9d51f..4b48377bc 100644 --- a/test/runtime/value/transform/tuple.ts +++ b/test/runtime/value/transform/tuple.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Tuple', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, [1, 2]) + const R = Encoder.Decode(T0, [1, 2]) Assert.IsEqual(R, [1, 2]) }) it('Should encode identity', () => { - const R = Value.Encode(T0, [1, 2]) + const R = Encoder.Encode(T0, [1, 2]) Assert.IsEqual(R, [1, 2]) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,15 +28,15 @@ describe('value/transform/Tuple', () => { .Decode((value) => [value[0] + 1] as [number]) .Encode((value) => [value[0] - 1] as [number]) it('Should decode mapped', () => { - const R = Value.Decode(T1, [0]) + const R = Encoder.Decode(T1, [0]) Assert.IsEqual(R, [1]) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, [1]) + const R = Encoder.Encode(T1, [1]) Assert.IsEqual(R, [0]) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // -------------------------------------------------------- // Mapped Element @@ -47,15 +48,15 @@ describe('value/transform/Tuple', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped element', () => { - const R = Value.Decode(T2, [0]) + const R = Encoder.Decode(T2, [0]) Assert.IsEqual(R, [1]) }) it('Should encode mapped element', () => { - const R = Value.Encode(T2, [1]) + const R = Encoder.Encode(T2, [1]) Assert.IsEqual(R, [0]) }) it('Should throw on mapped element decode', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) // -------------------------------------------------------- // Mapped Element @@ -67,15 +68,15 @@ describe('value/transform/Tuple', () => { .Decode((value) => [value[0].toString()]) .Encode((value) => [parseFloat(value[0])] as [number]) it('Should decode mapped element', () => { - const R = Value.Decode(T3, [0]) + const R = Encoder.Decode(T3, [0]) Assert.IsEqual(R, ['1']) }) it('Should encode mapped element', () => { - const R = Value.Encode(T3, ['1']) + const R = Encoder.Encode(T3, ['1']) Assert.IsEqual(R, [0]) }) it('Should throw on mapped element decode', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) // ------------------------------------------------------------ // Set @@ -84,15 +85,15 @@ describe('value/transform/Tuple', () => { .Decode((value) => new Set(value)) .Encode((value) => [...value] as any) it('should decode set', () => { - const R = Value.Decode(T4, [1]) + const R = Encoder.Decode(T4, [1]) Assert.IsInstanceOf(R, Set) Assert.IsTrue(R.has(1)) }) it('should encode set', () => { - const R = Value.Encode(T4, new Set([1])) + const R = Encoder.Encode(T4, new Set([1])) Assert.IsEqual(R, [1]) }) it('Should throw on set decode', () => { - Assert.Throws(() => Value.Decode(T4, {})) + Assert.Throws(() => Encoder.Decode(T4, {})) }) }) diff --git a/test/runtime/value/transform/undefined.ts b/test/runtime/value/transform/undefined.ts index 7f8582a9e..60c5a4c01 100644 --- a/test/runtime/value/transform/undefined.ts +++ b/test/runtime/value/transform/undefined.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,15 +11,15 @@ describe('value/transform/Undefined', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, undefined) + const R = Encoder.Decode(T0, undefined) Assert.IsEqual(R, undefined) }) it('Should encode identity', () => { - const R = Value.Encode(T0, undefined) + const R = Encoder.Encode(T0, undefined) Assert.IsEqual(R, undefined) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -27,14 +28,14 @@ describe('value/transform/Undefined', () => { .Decode((value) => null) .Encode((value) => undefined) it('Should decode mapped', () => { - const R = Value.Decode(T1, undefined) + const R = Encoder.Decode(T1, undefined) Assert.IsEqual(R, null) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, undefined) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts index d1a9cebc5..93eeff81c 100644 --- a/test/runtime/value/transform/union.ts +++ b/test/runtime/value/transform/union.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type, TSchema } from '@sinclair/typebox' @@ -14,15 +15,15 @@ describe('value/transform/Union', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, { x: 1 }) + const R = Encoder.Decode(T0, { x: 1 }) Assert.IsEqual(R, { x: 1 }) }) it('Should encode identity', () => { - const R = Value.Encode(T0, { y: 2 }) + const R = Encoder.Encode(T0, { y: 2 }) Assert.IsEqual(R, { y: 2 }) }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -35,15 +36,15 @@ describe('value/transform/Union', () => { .Decode((value) => 'test' as const) .Encode((value) => ({ type: 'hello' as const })) it('Should decode mapped', () => { - const R = Value.Decode(T1, { type: 'hello' }) + const R = Encoder.Decode(T1, { type: 'hello' }) Assert.IsEqual(R, 'test') }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 'test') + const R = Encoder.Encode(T1, 'test') Assert.IsEqual(R, { type: 'hello' }) }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) // -------------------------------------------------------- // Mapped ValueType @@ -62,23 +63,23 @@ describe('value/transform/Union', () => { return value }) it('Should decode value type 1', () => { - const R = Value.Decode(T2, 0) + const R = Encoder.Decode(T2, 0) Assert.IsEqual(R, 2) }) it('Should decode value type 2', () => { - const R = Value.Decode(T2, 'hello') + const R = Encoder.Decode(T2, 'hello') Assert.IsEqual(R, 'hello') }) it('Should encode value type 1', () => { - const R = Value.Encode(T2, 'hello') + const R = Encoder.Encode(T2, 'hello') Assert.IsEqual(R, 'world') }) it('Should encode value type 2', () => { - const R = Value.Encode(T2, 2) + const R = Encoder.Encode(T2, 2) Assert.IsEqual(R, 0) }) it('Should throw on value type decode', () => { - Assert.Throws(() => Value.Decode(T2, null)) + Assert.Throws(() => Encoder.Decode(T2, null)) }) // -------------------------------------------------------- // Mapped ObjectType @@ -102,23 +103,23 @@ describe('value/transform/Union', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode object types 1', () => { - const R = Value.Decode(T3, { x: 0 }) + const R = Encoder.Decode(T3, { x: 0 }) Assert.IsEqual(R, { x: 1 }) }) it('Should decode object types 2', () => { - const R = Value.Decode(T3, { x: 'abc' }) + const R = Encoder.Decode(T3, { x: 'abc' }) Assert.IsEqual(R, { x: 'cba' }) }) it('Should encode object types 1', () => { - const R = Value.Encode(T3, { x: 1 }) + const R = Encoder.Encode(T3, { x: 1 }) Assert.IsEqual(R, { x: 0 }) }) it('Should encode object types 2', () => { - const R = Value.Encode(T3, { x: 'cba' }) + const R = Encoder.Encode(T3, { x: 'cba' }) Assert.IsEqual(R, { x: 'abc' }) }) it('Should throw on object types decode', () => { - Assert.Throws(() => Value.Decode(T3, null)) + Assert.Throws(() => Encoder.Decode(T3, null)) }) // -------------------------------------------------------- // Mapped Mixed Types @@ -140,31 +141,31 @@ describe('value/transform/Union', () => { .Decode((value) => typeof value === 'number' ? value + 1 : value) .Encode((value) => typeof value === 'number' ? value - 1 : value) it('Should decode mixed types 1', () => { - const R = Value.Decode(T4, { x: 0 }) + const R = Encoder.Decode(T4, { x: 0 }) Assert.IsEqual(R, { x: 1 }) }) it('Should decode mixed types 2', () => { - const R = Value.Decode(T4, 0) + const R = Encoder.Decode(T4, 0) Assert.IsEqual(R, 2) }) it('Should decode mixed types 3', () => { - const R = Value.Decode(T4, [0]) + const R = Encoder.Decode(T4, [0]) Assert.IsEqual(R, [1]) }) it('Should encode mixed types 1', () => { - const R = Value.Encode(T4, { x: 1 }) + const R = Encoder.Encode(T4, { x: 1 }) Assert.IsEqual(R, { x: 0 }) }) it('Should encode mixed types 2', () => { - const R = Value.Encode(T4, 2) + const R = Encoder.Encode(T4, 2) Assert.IsEqual(R, 0) }) it('Should encode mixed types 3', () => { - const R = Value.Encode(T4, [1]) + const R = Encoder.Encode(T4, [1]) Assert.IsEqual(R, [0]) }) it('Should throw on mixed types decode', () => { - Assert.Throws(() => Value.Decode(T4, null)) + Assert.Throws(() => Encoder.Decode(T4, null)) }) // -------------------------------------------------------- // Interior Union Transform @@ -176,26 +177,26 @@ describe('value/transform/Union', () => { .Encode((value) => value.toISOString()) const T52 = Type.Union([Type.Null(), T51]) it('Should decode interior union 1', () => { - const R = Value.Decode(T52, null) + const R = Encoder.Decode(T52, null) Assert.IsEqual(R, null) }) it('Should decode interior union 2', () => { - const R = Value.Decode(T52, new Date().toISOString()) + const R = Encoder.Decode(T52, new Date().toISOString()) Assert.IsInstanceOf(R, Date) }) it('Should encode interior union 1', () => { - const R = Value.Encode(T52, null) + const R = Encoder.Encode(T52, null) Assert.IsEqual(R, null) }) it('Should encode interior union 2', () => { const D = new Date() - const R = Value.Encode(T52, D) + const R = Encoder.Encode(T52, D) Assert.IsEqual(R, D.toISOString()) }) it('Should throw on interior union decode', () => { - Assert.Throws(() => Value.Decode(T52, {})) + Assert.Throws(() => Encoder.Decode(T52, {})) }) it('Should throw on interior union encode', () => { - Assert.Throws(() => Value.Encode(T52, 1)) + Assert.Throws(() => Encoder.Encode(T52, 1)) }) }) diff --git a/test/runtime/value/transform/unknown.ts b/test/runtime/value/transform/unknown.ts index 2fe673a17..463980d57 100644 --- a/test/runtime/value/transform/unknown.ts +++ b/test/runtime/value/transform/unknown.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -10,11 +11,11 @@ describe('value/transform/Unknown', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { - const R = Value.Decode(T0, 123) + const R = Encoder.Decode(T0, 123) Assert.IsEqual(R, 123) }) it('Should encode mapped', () => { - const R = Value.Encode(T0, 123) + const R = Encoder.Encode(T0, 123) Assert.IsEqual(R, 123) }) // -------------------------------------------------------- @@ -24,11 +25,11 @@ describe('value/transform/Unknown', () => { .Decode((value) => 1) .Encode((value) => 2) it('Should decode mapped', () => { - const R = Value.Decode(T1, null) + const R = Encoder.Decode(T1, null) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { - const R = Value.Encode(T1, null) + const R = Encoder.Encode(T1, null) Assert.IsEqual(R, 2) }) }) diff --git a/test/runtime/value/transform/unsafe.ts b/test/runtime/value/transform/unsafe.ts index 76d31cb4f..f679e01c9 100644 --- a/test/runtime/value/transform/unsafe.ts +++ b/test/runtime/value/transform/unsafe.ts @@ -1,3 +1,4 @@ +import * as Encoder from './_encoder' import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type, Kind, TypeRegistry } from '@sinclair/typebox' @@ -16,15 +17,15 @@ describe('value/transform/Unsafe', () => { .Decode((value) => value) .Encode((value) => value) it('Should decode identity', () => { - const R = Value.Decode(T0, 'hello') + const R = Encoder.Decode(T0, 'hello') Assert.IsEqual(R, 'hello') }) it('Should encode identity', () => { - const R = Value.Encode(T0, 'hello') + const R = Encoder.Encode(T0, 'hello') Assert.IsEqual(R, 'hello') }) it('Should throw on identity decode', () => { - Assert.Throws(() => Value.Decode(T0, null)) + Assert.Throws(() => Encoder.Decode(T0, null)) }) // -------------------------------------------------------- // Mapped @@ -33,14 +34,14 @@ describe('value/transform/Unsafe', () => { .Decode((value) => value.split('').reverse().join('')) .Encode((value) => value.split('').reverse().join('')) it('Should decode mapped', () => { - const R = Value.Decode(T1, 'ABC') + const R = Encoder.Decode(T1, 'ABC') Assert.IsEqual(R, 'CBA') }) it('Should encode mapped', () => { - const R = Value.Encode(T1, 'CBA') + const R = Encoder.Encode(T1, 'CBA') Assert.IsEqual(R, 'ABC') }) it('Should throw on mapped decode', () => { - Assert.Throws(() => Value.Decode(T1, null)) + Assert.Throws(() => Encoder.Decode(T1, null)) }) }) From 06bd0b8ded36feaa57403396c9131b875b62b191 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 18 Nov 2023 18:21:09 +0900 Subject: [PATCH 212/369] Formatting --- test/runtime/value/transform/_encoder.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/runtime/value/transform/_encoder.ts b/test/runtime/value/transform/_encoder.ts index 8449787e6..06d82410b 100644 --- a/test/runtime/value/transform/_encoder.ts +++ b/test/runtime/value/transform/_encoder.ts @@ -1,4 +1,4 @@ -import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol } from '@sinclair/typebox/value/guard' +import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol, IsDate } from '@sinclair/typebox/value/guard' import { TSchema, StaticDecode, StaticEncode } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value } from '@sinclair/typebox/value' @@ -9,6 +9,9 @@ function AssertSame(actual: unknown, expect: unknown) { if (IsIterator(actual) && IsIterator(expect)) return if (IsSymbol(actual) && IsSymbol(expect)) return if (IsFunction(actual) && IsFunction(expect)) return + if (IsDate(actual) && IsDate(expect)) { + return Assert.IsEqual(actual.getTime(), expect.getTime()) + } Assert.IsEqual(actual, expect) } From ea217ccc06f4349c6830d55c698231275e72004a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 20 Nov 2023 04:42:13 +0900 Subject: [PATCH 213/369] Revision 0.31.27 (#671) * Clone Date and Uint8Array types * Date Default is Current Time * Tests --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 26 +++++++++++++++++---- src/value/create.ts | 2 +- test/runtime/assert/assert.ts | 5 +++++ test/runtime/value/cast/date.ts | 10 ++++----- test/runtime/value/create/date.ts | 30 +++++++++++++++++++++++++ test/runtime/value/create/index.ts | 1 + test/runtime/value/create/uint8array.ts | 8 +++++++ test/runtime/value/transform/date.ts | 4 ++-- 10 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 test/runtime/value/create/date.ts diff --git a/package-lock.json b/package-lock.json index 48e7f7e19..b7f256f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.26", + "version": "0.31.27", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.26", + "version": "0.31.27", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index f7aa93d5b..e5fc6ea76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.26", + "version": "0.31.27", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index bb6c50bf4..57e8cb27b 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -1087,6 +1087,10 @@ export namespace ValueGuard { export function IsBoolean(value: unknown): value is boolean { return typeof value === 'boolean' } + /** Returns true if this value is a Date object */ + export function IsDate(value: unknown): value is Date { + return value instanceof globalThis.Date + } /** Returns true if this value is null */ export function IsNull(value: unknown): value is null { return value === null @@ -1103,6 +1107,10 @@ export namespace ValueGuard { export function IsString(value: unknown): value is string { return typeof value === 'string' } + /** Returns true if this value is a Uint8Array */ + export function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof globalThis.Uint8Array + } /** Returns true if this value is undefined */ export function IsUndefined(value: unknown): value is undefined { return value === undefined @@ -2287,19 +2295,29 @@ export namespace TypeExtends { // -------------------------------------------------------------------------- /** Specialized Clone for Types */ export namespace TypeClone { + function ArrayType(value: unknown[]) { + return (value as any).map((value: unknown) => Visit(value as any)) + } + function DateType(value: Date) { + return new Date(value.getTime()) + } + function Uint8ArrayType(value: Uint8Array) { + return new Uint8Array(value) + } function ObjectType(value: Record) { const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) return { ...clonedProperties, ...clonedSymbols } } - function ArrayType(value: unknown[]) { - return (value as any).map((value: unknown) => Visit(value as any)) - } function Visit(value: unknown): any { // prettier-ignore - return ValueGuard.IsArray(value) ? ArrayType(value) : + return ( + ValueGuard.IsArray(value) ? ArrayType(value) : + ValueGuard.IsDate(value) ? DateType(value) : + ValueGuard.IsUint8Array(value) ? Uint8ArrayType(value) : ValueGuard.IsObject(value) ? ObjectType(value) : value + ) } /** Clones a Rest */ export function Rest(schemas: [...T]): T { diff --git a/src/value/create.ts b/src/value/create.ts index 1013bbbdb..b36b42f41 100644 --- a/src/value/create.ts +++ b/src/value/create.ts @@ -135,7 +135,7 @@ function TDate(schema: Types.TDate, references: Types.TSchema[]): any { } else if (schema.minimumTimestamp !== undefined) { return new Date(schema.minimumTimestamp) } else { - return new Date(0) + return new Date() } } function TFunction(schema: Types.TFunction, references: Types.TSchema[]): any { diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index 65ca4aa10..2e9a92944 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -17,6 +17,11 @@ export namespace Assert { export function NotEqual(actual: unknown, expect: unknown) { return assert.notEqual(actual, expect) } + /** Asserts a numeric value is within range of the expected */ + export function InRange(value: number, expect: number, range: number) { + if (Math.abs(value - expect) <= range) return + throw Error('Expected value to be in range') + } let nextIdOrdinal = 0 export function NextId() { return `$id-${nextIdOrdinal++}` diff --git a/test/runtime/value/cast/date.ts b/test/runtime/value/cast/date.ts index 4e6fc6016..e204acf36 100644 --- a/test/runtime/value/cast/date.ts +++ b/test/runtime/value/cast/date.ts @@ -8,27 +8,27 @@ describe('value/cast/Date', () => { it('Should upcast from string', () => { const value = 'world' const result = Value.Cast(T, value) - Assert.IsEqual(result, E) + Assert.InRange(result.getTime(), new Date().getTime(), 1000) }) it('Should upcast from object', () => { const value = {} const result = Value.Cast(T, value) - Assert.IsEqual(result, E) + Assert.InRange(result.getTime(), new Date().getTime(), 1000) }) it('Should upcast from array', () => { const value = [1] const result = Value.Cast(T, value) - Assert.IsEqual(result, E) + Assert.InRange(result.getTime(), new Date().getTime(), 1000) }) it('Should upcast from undefined', () => { const value = undefined const result = Value.Cast(T, value) - Assert.IsEqual(result, E) + Assert.InRange(result.getTime(), new Date().getTime(), 1000) }) it('Should upcast from null', () => { const value = null const result = Value.Cast(T, value) - Assert.IsEqual(result, E) + Assert.InRange(result.getTime(), new Date().getTime(), 1000) }) it('Should preseve', () => { const value = new Date(100) diff --git a/test/runtime/value/create/date.ts b/test/runtime/value/create/date.ts new file mode 100644 index 000000000..37c12b7ee --- /dev/null +++ b/test/runtime/value/create/date.ts @@ -0,0 +1,30 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Date', () => { + it('Should create value', () => { + const T = Type.Date() + const A = Value.Create(T) + const B = new Date() + Assert.InRange(A.getTime(), B.getTime(), 1000) + }) + it('Should create default', () => { + const T = Type.Date({ default: new Date(1001) }) + const A = Value.Create(T) + const B = new Date(1001) + Assert.IsEqual(A, B) + }) + it('Should create value nested', () => { + const T = Type.Object({ value: Type.Date() }) + const A = Value.Create(T) + const B = { value: new Date() } + Assert.InRange(A.value.getTime(), B.value.getTime(), 1000) + }) + it('Should create default nested', () => { + const T = Type.Object({ value: Type.Date({ default: new Date(1001) }) }) + const A = Value.Create(T) + const B = { value: new Date(1001) } + Assert.IsEqual(A, B) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 9bb482979..7b51cf3bf 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -5,6 +5,7 @@ import './bigint' import './boolean' import './composite' import './constructor' +import './date' import './enum' import './function' import './integer' diff --git a/test/runtime/value/create/uint8array.ts b/test/runtime/value/create/uint8array.ts index 0dac37786..faf044f43 100644 --- a/test/runtime/value/create/uint8array.ts +++ b/test/runtime/value/create/uint8array.ts @@ -23,4 +23,12 @@ describe('value/create/Uint8Array', () => { Assert.IsEqual(value.length, 4) Assert.IsEqual([value[0], value[1], value[2], value[3]], [0, 0, 0, 0]) }) + it('Should create value nested', () => { + const T = Type.Object({ value: Type.Uint8Array() }) + Assert.IsEqual(Value.Create(T), { value: new Uint8Array() }) + }) + it('Should create default nested', () => { + const T = Type.Object({ value: Type.Date({ default: new Uint8Array([1, 2, 3, 4]) }) }) + Assert.IsEqual(Value.Create(T), { value: new Uint8Array([1, 2, 3, 4]) }) + }) }) diff --git a/test/runtime/value/transform/date.ts b/test/runtime/value/transform/date.ts index bc7fec5f5..e81ff7a89 100644 --- a/test/runtime/value/transform/date.ts +++ b/test/runtime/value/transform/date.ts @@ -26,9 +26,9 @@ describe('value/transform/Date', () => { // -------------------------------------------------------- const T1 = Type.Transform(Type.Date()) .Decode((value) => 1) - .Encode((value) => new Date()) + .Encode((value) => new Date(0)) it('Should decode mapped', () => { - const R = Encoder.Decode(T1, new Date()) + const R = Encoder.Decode(T1, new Date(0)) Assert.IsEqual(R, 1) }) it('Should encode mapped', () => { From 6fd7dac6a4e0aa9229c37d76ec2c4238c968ce8b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 20 Nov 2023 21:53:12 +0900 Subject: [PATCH 214/369] Revision 0.31.28 (#674) * Update Intersect Transform Logic * Tests * Version --- package-lock.json | 4 +- package.json | 2 +- readme.md | 10 +- src/value/transform.ts | 308 ++++++++++------------ test/runtime/value/transform/intersect.ts | 57 ++++ 5 files changed, 200 insertions(+), 181 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7f256f22..a9ffcd256 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.27", + "version": "0.31.28", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.27", + "version": "0.31.28", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.18.0", diff --git a/package.json b/package.json index e5fc6ea76..04d98e936 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.27", + "version": "0.31.28", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index d4868e0d7..8ba350169 100644 --- a/readme.md +++ b/readme.md @@ -1719,11 +1719,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '148.9 kb' │ ' 65.8 kb' │ '2.26 x' │ -│ typebox/errors │ '111.5 kb' │ ' 49.1 kb' │ '2.27 x' │ -│ typebox/system │ ' 82.6 kb' │ ' 36.8 kb' │ '2.24 x' │ -│ typebox/value │ '190.5 kb' │ ' 82.4 kb' │ '2.31 x' │ -│ typebox │ ' 72.4 kb' │ ' 31.6 kb' │ '2.29 x' │ +│ typebox/compiler │ '163.6 kb' │ ' 71.6 kb' │ '2.28 x' │ +│ typebox/errors │ '113.3 kb' │ ' 50.1 kb' │ '2.26 x' │ +│ typebox/system │ ' 83.9 kb' │ ' 37.5 kb' │ '2.24 x' │ +│ typebox/value │ '191.1 kb' │ ' 82.3 kb' │ '2.32 x' │ +│ typebox │ ' 73.8 kb' │ ' 32.3 kb' │ '2.29 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/value/transform.ts b/src/value/transform.ts index d0c7daba5..bb377f9a1 100644 --- a/src/value/transform.ts +++ b/src/value/transform.ts @@ -32,19 +32,9 @@ import { Deref } from './deref' import { Check } from './check' import * as Types from '../typebox' -// ------------------------------------------------------------------------- -// CheckFunction -// ------------------------------------------------------------------------- -export type CheckFunction = (schema: Types.TSchema, references: Types.TSchema[], value: unknown) => boolean - // ------------------------------------------------------------------------- // Errors // ------------------------------------------------------------------------- -export class TransformUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TRef | Types.TThis) { - super(`Unknown type`) - } -} export class TransformDecodeCheckError extends Types.TypeBoxError { constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { super(`Unable to decode due to invalid value`) @@ -65,9 +55,9 @@ export class TransformEncodeError extends Types.TypeBoxError { super(`${error instanceof Error ? error.message : 'Unknown error'}`) } } -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ // HasTransform -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Recursively checks a schema for transform codecs */ export namespace HasTransform { function TArray(schema: Types.TArray, references: Types.TSchema[]): boolean { @@ -124,9 +114,6 @@ export namespace HasTransform { if (schema.$id && visited.has(schema.$id)) return false if (schema.$id) visited.add(schema.$id) switch (schema[Types.Kind]) { - // ------------------------------------------------------ - // Structural - // ------------------------------------------------------ case 'Array': return TArray(schema_, references_) case 'AsyncIterator': @@ -155,28 +142,7 @@ export namespace HasTransform { return TTuple(schema_, references_) case 'Union': return TUnion(schema_, references_) - // ------------------------------------------------------ - // Default - // ------------------------------------------------------ - case 'Any': - case 'BigInt': - case 'Boolean': - case 'Date': - case 'Integer': - case 'Literal': - case 'Never': - case 'Null': - case 'Number': - case 'String': - case 'Symbol': - case 'TemplateLiteral': - case 'Undefined': - case 'Uint8Array': - case 'Unknown': - case 'Void': - return Types.TypeGuard.TTransform(schema) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) return Types.TypeGuard.TTransform(schema) } } @@ -187,9 +153,9 @@ export namespace HasTransform { return Visit(schema, references) } } -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ // DecodeTransform -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Decodes a value using transform decoders if available. Does not ensure correct results. */ export namespace DecodeTransform { function Default(schema: Types.TSchema, value: any) { @@ -199,82 +165,105 @@ export namespace DecodeTransform { throw new TransformDecodeError(schema, value, error) } } + // prettier-ignore function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - const elements1 = value.map((value: any) => Visit(schema.items, references, value)) as unknown[] - return Default(schema, elements1) + return (IsArray(value)) + ? Default(schema, value.map((value: any) => Visit(schema.items, references, value))) + : Default(schema, value) } + // prettier-ignore function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) - const keys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const properties1 = Object.entries(value).reduce((acc, [key, value]) => { - return !keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(Types.IndexedAccessor.Resolve(schema, [key]), value) } - }, {} as Record) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) return Default(schema, properties1) - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(schema.unevaluatedProperties as Types.TSchema, value) } - }, {} as Record) - return Default(schema, properties2) + const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } + : value + }, value) + if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) } function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - const value1 = Visit(schema.not, references, value) - return Default(schema, value1) + return Default(schema, Visit(schema.not, references, value)) } + // prettier-ignore function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { if (!IsPlainObject(value)) return Default(schema, value) - const properties1 = Object.entries(value).reduce((acc, [key, value]) => { - return !(key in schema.properties) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(schema.properties[key], references, value) } - }, {} as Record) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties1) + const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + : value + }, value) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as Types.TSchema - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return key in schema.properties ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } - }, {} as Record) - return Default(schema, properties2) - } + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) + } + // prettier-ignore function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { if (!IsPlainObject(value)) return Default(schema, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[pattern] - const regex = new RegExp(pattern) - const properties1 = Object.entries(value).reduce((acc, [key, value]) => { - return !regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(property, references, value) } - }, {} as Record) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties1) + const knownKeys = new RegExp(pattern) + const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { + return knownKeys.test(key) + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + : value + }, value) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as Types.TSchema - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } - }, {} as Record) - return Default(schema, properties2) + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.test(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) } function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) + return Default(schema, Visit(target, references, value)) } function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) + return Default(schema, Visit(target, references, value)) } + // prettier-ignore function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { - const value1 = IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value[index])) : [] - return Default(schema, value1) + return (IsArray(value) && IsArray(schema.items)) + ? Default(schema, schema.items.map((schema, index) => Visit(schema, references, value[index]))) + : Default(schema, value) } function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { - const value1 = Default(schema, value) + const defaulted = Default(schema, value) for (const subschema of schema.anyOf) { - if (!Check(subschema, references, value1)) continue - return Visit(subschema, references, value1) + if (!Check(subschema, references, defaulted)) continue + return Visit(subschema, references, defaulted) } - return value1 + return defaulted } function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { const references_ = typeof schema.$id === 'string' ? [...references, schema] : references const schema_ = schema as any switch (schema[Types.Kind]) { - // ------------------------------------------------------ - // Structural - // ------------------------------------------------------ case 'Array': return TArray(schema_, references_, value) case 'Intersect': @@ -295,32 +284,7 @@ export namespace DecodeTransform { return TTuple(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) - // ------------------------------------------------------ - // Default - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'BigInt': - case 'Boolean': - case 'Constructor': - case 'Date': - case 'Function': - case 'Integer': - case 'Iterator': - case 'Literal': - case 'Never': - case 'Null': - case 'Number': - case 'Promise': - case 'String': - case 'TemplateLiteral': - case 'Undefined': - case 'Uint8Array': - case 'Unknown': - case 'Void': - return Default(schema_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) return Default(schema_, value) } } @@ -328,9 +292,9 @@ export namespace DecodeTransform { return Visit(schema, references, value) } } -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ // DecodeTransform -// ------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Encodes a value using transform encoders if available. Does not ensure correct results. */ export namespace EncodeTransform { function Default(schema: Types.TSchema, value: any) { @@ -340,52 +304,79 @@ export namespace EncodeTransform { throw new TransformEncodeError(schema, value, error) } } + // prettier-ignore function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - const elements1 = Default(schema, value) - return elements1.map((value: any) => Visit(schema.items, references, value)) as unknown[] + const defaulted = Default(schema, value) + return IsArray(defaulted) + ? defaulted.map((value: any) => Visit(schema.items, references, value)) + : defaulted } + // prettier-ignore function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { - const properties1 = Default(schema, value) - if (!IsPlainObject(value) || IsValueType(value)) return properties1 - const keys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return !keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(Types.IndexedAccessor.Resolve(schema, [key]), value) } - }, {} as Record) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) return Default(schema, properties2) - return Object.entries(properties2).reduce((acc, [key, value]) => { - return keys.includes(key) ? { ...acc, [key]: value } : { ...acc, [key]: Default(schema.unevaluatedProperties as Types.TSchema, value) } - }, {} as Record) + const defaulted = Default(schema, value) + if (!IsPlainObject(value) || IsValueType(value)) return defaulted + const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const knownProperties = knownKeys.reduce((value, key) => { + return key in defaulted + ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } + : value + }, defaulted) + if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema + return unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + : value + }, knownProperties) } function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - const value1 = Default(schema, value) - return Default(schema.not, value1) + return Default(schema.not, Default(schema, value)) } + // prettier-ignore function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { - const properties1 = Default(schema, value) as Record - if (!IsPlainObject(value)) return properties1 - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return !(key in schema.properties) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(schema.properties[key], references, value) } - }, {} as Record) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return properties2 + const defaulted = Default(schema, value) + if (!IsPlainObject(value)) return defaulted + const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + const knownProperties = knownKeys.reduce((value, key) => { + return key in value + ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + : value + }, defaulted) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { + return knownProperties + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as Types.TSchema - return Object.entries(properties2).reduce((acc, [key, value]) => { - return key in schema.properties ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } - }, {} as Record) + return unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) } + // prettier-ignore function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { - const properties1 = Default(schema, value) as Record - if (!IsPlainObject(value)) return properties1 + const defaulted = Default(schema, value) as Record + if (!IsPlainObject(value)) return defaulted const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[pattern] - const regex = new RegExp(pattern) - const properties2 = Object.entries(properties1).reduce((acc, [key, value]) => { - return !regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(property, references, value) } - }, {} as Record) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) return Default(schema, properties2) + const knownKeys = new RegExp(pattern) + const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { + return knownKeys.test(key) + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + : value + }, defaulted) + if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as Types.TSchema - return Object.entries(properties2).reduce((acc, [key, value]) => { - return regex.test(key) ? { ...acc, [key]: value } : { ...acc, [key]: Visit(additionalProperties, references, value) } - }, {} as Record) + return unknownKeys.reduce((value, key) => { + return !knownKeys.test(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) } function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { const target = Deref(schema, references) @@ -420,9 +411,6 @@ export namespace EncodeTransform { const references_ = typeof schema.$id === 'string' ? [...references, schema] : references const schema_ = schema as any switch (schema[Types.Kind]) { - // ------------------------------------------------------ - // Structural - // ------------------------------------------------------ case 'Array': return TArray(schema_, references_, value) case 'Intersect': @@ -441,33 +429,7 @@ export namespace EncodeTransform { return TTuple(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) - // ------------------------------------------------------ - // Apply - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'BigInt': - case 'Boolean': - case 'Constructor': - case 'Date': - case 'Function': - case 'Integer': - case 'Iterator': - case 'Literal': - case 'Never': - case 'Null': - case 'Number': - case 'Promise': - case 'String': - case 'Symbol': - case 'TemplateLiteral': - case 'Undefined': - case 'Uint8Array': - case 'Unknown': - case 'Void': - return Default(schema_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TransformUnknownTypeError(schema_) return Default(schema_, value) } } diff --git a/test/runtime/value/transform/intersect.ts b/test/runtime/value/transform/intersect.ts index 0d7f39043..7e0bcc825 100644 --- a/test/runtime/value/transform/intersect.ts +++ b/test/runtime/value/transform/intersect.ts @@ -120,4 +120,61 @@ describe('value/transform/Intersect', () => { it('Should throw on exterior value type decode', () => { Assert.Throws(() => Encoder.Decode(T4, null)) }) + // -------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/discussions/672 + // -------------------------------------------------------- + // prettier-ignore + { + const A = Type.Object({ isHybrid: Type.Boolean() }) + const T = Type.Transform(A) + .Decode((value) => ({ isHybrid: value.isHybrid ? 1 : 0 })) + .Encode((value) => ({ isHybrid: value.isHybrid === 1 ? true : false })) + const I = Type.Intersect([ + Type.Object({ model: Type.String() }), + Type.Object({ features: Type.Array(T) }), + ]) + it('Should decode nested 1', () => { + const value = Value.Decode(T, { isHybrid: true }) + Assert.IsEqual(value, { isHybrid: 1 }) + }) + // prettier-ignore + it('Should decode nested 2', () => { + const value = Value.Decode(I, { + model: 'Prius', + features: [ + { isHybrid: true }, + { isHybrid: false } + ], + }) + Assert.IsEqual(value, { + model: 'Prius', + features: [ + { isHybrid: 1 }, + { isHybrid: 0 } + ], + }) + }) + it('should encode nested 1', () => { + let value = Value.Encode(T, { isHybrid: 1 }) + Assert.IsEqual(value, { isHybrid: true }) + }) + // prettier-ignore + it('Should encode nested 2', () => { + const value = Value.Encode(I, { + model: 'Prius', + features: [ + { isHybrid: 1 }, + { isHybrid: 0 } + ], + }) + Assert.IsEqual(value, { + model: 'Prius', + features: [ + { isHybrid: true }, + { isHybrid: false } + + ], + }) + }) + } }) From 573cdb169456b6d0ede981559e2184c6caf601da Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 21 Nov 2023 12:22:00 +0900 Subject: [PATCH 215/369] TypeScript 5.3.2 --- hammer.mjs | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 76e8ea38e..2e02a034e 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -36,7 +36,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index a9ffcd256..88740ec7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.2.2" + "typescript": "^5.3.2" } }, "node_modules/@esbuild/linux-loong64": { @@ -1317,9 +1317,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2279,9 +2279,9 @@ } }, "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 04d98e936..064dc20a4 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.2.2" + "typescript": "^5.3.2" } } From ca4000cc9a4e0343f764ef65113ba62928f5cf0d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 22 Dec 2023 14:00:46 +0900 Subject: [PATCH 216/369] Revision 0.32.0 (#689) --- .github/workflows/build.yml | 2 +- .github/workflows/nightly.yml | 2 +- benchmark/measurement/index.ts | 5 - changelog/0.24.6.md | 2 +- changelog/0.32.0.md | 473 +++ example/annotation/annotation.ts | 148 + example/annotation/index.ts | 1 + {examples => example}/collections/array.ts | 0 {examples => example}/collections/index.ts | 0 {examples => example}/collections/map.ts | 0 {examples => example}/collections/readme.md | 0 {examples => example}/collections/set.ts | 0 {examples => example}/formats/date-time.ts | 0 {examples => example}/formats/date.ts | 0 {examples => example}/formats/email.ts | 0 {examples => example}/formats/index.ts | 0 {examples => example}/formats/ipv4.ts | 0 {examples => example}/formats/ipv6.ts | 0 {examples => example}/formats/time.ts | 0 {examples => example}/formats/url.ts | 0 {examples => example}/formats/uuid.ts | 0 {examples => example}/index.ts | 0 {examples => example}/prototypes/evaluate.ts | 23 +- {examples => example}/prototypes/index.ts | 1 - .../prototypes/partial-deep.ts | 15 +- {examples => example}/prototypes/readme.md | 0 .../prototypes/union-enum.ts | 0 .../prototypes/union-oneof.ts | 0 {examples => example}/typedef/index.ts | 0 {examples => example}/typedef/readme.md | 0 {examples => example}/typedef/typedef.ts | 53 +- examples/trpc/readme.md | 46 - hammer.mjs | 46 +- license | 4 +- package-lock.json | 623 ++- package.json | 36 +- readme.md | 803 ++-- src/compiler/compiler.ts | 334 +- src/compiler/index.ts | 2 +- src/errors/errors.ts | 264 +- src/errors/function.ts | 193 + src/errors/index.ts | 3 +- src/index.ts | 117 + src/system/index.ts | 4 +- src/system/policy.ts | 67 + src/system/system.ts | 225 +- src/tsconfig.json | 2 +- src/type/any/any.ts | 40 + src/type/any/index.ts | 29 + src/type/array/array.ts | 62 + src/type/array/index.ts | 29 + src/type/async-iterator/async-iterator.ts | 48 + src/type/async-iterator/index.ts | 29 + src/type/awaited/awaited.ts | 105 + src/type/awaited/index.ts | 29 + src/type/bigint/bigint.ts | 51 + src/type/bigint/index.ts | 29 + src/type/boolean/boolean.ts | 44 + src/type/boolean/index.ts | 29 + src/type/clone/index.ts | 30 + src/type/clone/type.ts | 39 + src/type/clone/value.ts | 62 + src/type/composite/composite.ts | 75 + src/type/composite/index.ts | 29 + src/type/const/const.ts | 135 + src/type/const/index.ts | 29 + .../constructor-parameters.ts | 46 + src/type/constructor-parameters/index.ts | 29 + src/type/constructor/constructor.ts | 67 + src/type/constructor/index.ts | 29 + src/type/date/date.ts | 56 + src/type/date/index.ts | 29 + src/type/deref/deref.ts | 174 + src/type/deref/index.ts | 29 + src/type/discard/discard.ts | 35 + src/type/discard/index.ts | 29 + src/type/enum/enum.ts | 58 + src/type/enum/index.ts | 29 + src/type/error/error.ts | 34 + src/type/error/index.ts | 29 + .../exclude/exclude-from-mapped-result.ts | 89 + src/type/exclude/exclude.ts | 96 + src/type/exclude/index.ts | 30 + src/type/extends/extends-check.ts | 776 ++++ src/type/extends/extends-from-mapped-key.ts | 129 + .../extends/extends-from-mapped-result.ts | 101 + src/type/extends/extends-undefined.ts | 55 + src/type/extends/extends.ts | 80 + src/type/extends/index.ts | 33 + .../extract/extract-from-mapped-result.ts | 89 + src/type/extract/extract.ts | 94 + src/type/extract/index.ts | 30 + src/type/function/function.ts | 67 + src/type/function/index.ts | 29 + src/type/guard/index.ts | 30 + src/type/guard/type.ts | 618 +++ src/type/guard/value.ts | 88 + src/type/helpers/helpers.ts | 69 + src/type/helpers/index.ts | 29 + src/type/index.ts | 98 + src/type/indexed/index.ts | 32 + src/type/indexed/indexed-from-mapped-key.ts | 103 + .../indexed/indexed-from-mapped-result.ts | 90 + src/type/indexed/indexed-property-keys.ts | 103 + src/type/indexed/indexed.ts | 241 ++ src/type/instance-type/index.ts | 29 + src/type/instance-type/instance-type.ts | 38 + src/type/integer/index.ts | 29 + src/type/integer/integer.ts | 51 + src/type/intersect/index.ts | 31 + src/type/intersect/intersect-create.ts | 52 + src/type/intersect/intersect-evaluated.ts | 127 + .../type/intersect/intersect-type.ts | 55 +- src/type/intersect/intersect.ts | 55 + src/type/intrinsic/capitalize.ts | 37 + src/type/intrinsic/index.ts | 34 + .../intrinsic/intrinsic-from-mapped-key.ts | 113 + src/type/intrinsic/intrinsic.ts | 148 + src/type/intrinsic/lowercase.ts | 37 + src/type/intrinsic/uncapitalize.ts | 37 + src/type/intrinsic/uppercase.ts | 37 + src/type/iterator/index.ts | 29 + src/type/iterator/iterator.ts | 48 + src/type/keyof/index.ts | 31 + src/type/keyof/keyof-from-mapped-result.ts | 83 + src/type/keyof/keyof-property-keys.ts | 176 + src/type/keyof/keyof.ts | 85 + src/type/literal/index.ts | 29 + src/type/literal/literal.ts | 53 + src/type/mapped/index.ts | 31 + src/type/mapped/mapped-key.ts | 43 + src/type/mapped/mapped-result.ts | 44 + src/type/mapped/mapped.ts | 283 ++ src/type/never/index.ts | 28 + src/type/never/never.ts | 44 + src/type/not/index.ts | 29 + src/type/not/not.ts | 46 + src/type/null/index.ts | 29 + src/type/null/null.ts | 44 + src/type/number/index.ts | 29 + src/type/number/number.ts | 51 + src/type/object/index.ts | 29 + src/type/object/object.ts | 99 + src/type/omit/index.ts | 31 + src/type/omit/omit-from-mapped-key.ts | 111 + src/type/omit/omit-from-mapped-result.ts | 89 + src/type/omit/omit.ts | 132 + src/type/optional/index.ts | 30 + .../optional/optional-from-mapped-result.ts | 88 + src/type/optional/optional.ts | 82 + src/type/parameters/index.ts | 29 + src/type/parameters/parameters.ts | 43 + src/type/partial/index.ts | 30 + .../partial/partial-from-mapped-result.ts | 83 + src/type/partial/partial.ts | 114 + src/type/patterns/index.ts | 29 + src/type/patterns/patterns.ts | 34 + src/type/pick/index.ts | 31 + src/type/pick/pick-from-mapped-key.ts | 111 + src/type/pick/pick-from-mapped-result.ts | 89 + src/type/pick/pick.ts | 122 + src/type/promise/index.ts | 29 + src/type/promise/promise.ts | 48 + src/type/readonly-optional/index.ts | 29 + .../readonly-optional/readonly-optional.ts | 38 + src/type/readonly/index.ts | 30 + .../readonly/readonly-from-mapped-result.ts | 88 + src/type/readonly/readonly.ts | 82 + src/type/record/index.ts | 29 + src/type/record/record.ts | 224 ++ src/type/recursive/index.ts | 29 + src/type/recursive/recursive.ts | 63 + src/type/ref/index.ts | 29 + src/type/ref/ref.ts | 57 + src/type/regexp/index.ts | 29 + src/type/regexp/regexp.ts | 50 + src/type/registry/format.ts | 55 + src/type/registry/index.ts | 30 + src/type/registry/type.ts | 56 + src/type/required/index.ts | 30 + .../required/required-from-mapped-result.ts | 83 + src/type/required/required.ts | 118 + src/type/rest/index.ts | 29 + src/type/rest/rest.ts | 65 + src/type/return-type/index.ts | 29 + src/type/return-type/return-type.ts | 38 + src/type/schema/anyschema.ts | 97 + src/type/schema/index.ts | 30 + src/type/schema/schema.ts | 58 + src/type/sets/index.ts | 29 + src/type/sets/set.ts | 147 + src/type/static/index.ts | 29 + src/type/static/static.ts | 93 + src/type/strict/index.ts | 29 + src/type/strict/strict.ts | 34 + src/type/string/index.ts | 29 + src/type/string/string.ts | 86 + src/type/symbol/index.ts | 29 + src/type/symbol/symbol.ts | 41 + src/type/symbols/index.ts | 29 + src/type/symbols/symbols.ts | 38 + src/type/template-literal/finite.ts | 119 + src/type/template-literal/generate.ts | 148 + src/type/template-literal/index.ts | 35 + src/type/template-literal/parse.ts | 167 + src/type/template-literal/pattern.ts | 77 + src/type/template-literal/syntax.ts | 117 + src/type/template-literal/template-literal.ts | 102 + src/type/template-literal/union.ts | 44 + src/type/transform/index.ts | 29 + src/type/transform/transform.ts | 88 + src/type/tuple/index.ts | 29 + src/type/tuple/tuple.ts | 64 + src/type/type/index.ts | 43 + src/type/type/javascript.ts | 123 + src/type/type/json.ts | 338 ++ src/type/type/type.ts | 89 + src/type/uint8array/index.ts | 29 + src/type/uint8array/uint8array.ts | 44 + src/type/undefined/index.ts | 29 + src/type/undefined/undefined.ts | 40 + src/type/union/index.ts | 31 + src/type/union/union-create.ts | 36 + src/type/union/union-evaluated.ts | 122 + src/type/union/union-type.ts | 48 + src/type/union/union.ts | 49 + src/type/unknown/index.ts | 29 + src/type/unknown/unknown.ts | 42 + src/type/unsafe/index.ts | 29 + src/type/unsafe/unsafe.ts | 45 + src/type/void/index.ts | 29 + src/type/void/void.ts | 44 + src/typebox.ts | 3410 ----------------- src/value/{ => cast}/cast.ts | 230 +- src/value/cast/index.ts | 29 + src/value/{ => check}/check.ts | 243 +- src/value/check/index.ts | 29 + src/value/clean/clean.ts | 184 + src/value/clean/index.ts | 29 + src/value/{ => clone}/clone.ts | 15 +- src/value/clone/index.ts | 29 + src/value/{ => convert}/convert.ts | 277 +- src/value/convert/index.ts | 29 + src/value/{ => create}/create.ts | 280 +- src/value/create/index.ts | 29 + src/value/default/default.ts | 185 + src/value/default/index.ts | 29 + src/value/{ => delta}/delta.ts | 99 +- src/value/delta/index.ts | 29 + src/value/{ => deref}/deref.ts | 5 +- src/value/deref/index.ts | 29 + src/value/{ => equal}/equal.ts | 12 +- src/value/equal/index.ts | 29 + src/value/{ => guard}/guard.ts | 4 +- src/value/guard/index.ts | 29 + src/value/{ => hash}/hash.ts | 31 +- src/value/hash/index.ts | 29 + src/value/index.ts | 56 +- src/value/mutate/index.ts | 29 + src/value/{ => mutate}/mutate.ts | 42 +- src/value/pointer.ts | 121 - src/value/pointer/index.ts | 29 + src/value/pointer/pointer.ts | 127 + src/value/transform.ts | 439 --- src/value/transform/decode.ts | 216 ++ src/value/transform/encode.ts | 223 ++ src/value/transform/has.ts | 167 + src/value/transform/index.ts | 31 + src/value/value.ts | 129 - src/value/value/index.ts | 29 + src/value/value/value.ts | 146 + .../benchmark}/compression/index.ts | 6 +- .../compression/module/typebox-compiler.ts | 0 .../compression/module/typebox-errors.ts | 0 .../compression/module/typebox-system.ts | 0 .../compression/module/typebox-value.ts | 0 .../benchmark}/compression/module/typebox.ts | 0 {benchmark => task/benchmark}/index.ts | 0 task/benchmark/measurement/index.ts | 5 + .../measurement/module/benchmark.ts | 0 .../benchmark}/measurement/module/cases.ts | 2 +- .../benchmark}/measurement/module/check.ts | 2 +- .../benchmark}/measurement/module/compile.ts | 2 +- .../benchmark}/measurement/module/index.ts | 0 .../benchmark}/measurement/module/result.ts | 0 task/build/common/remove-notices.ts | 82 + task/build/import/build.ts | 40 + task/build/import/compile.ts | 40 + task/build/import/convert-to-esm.ts | 110 + task/build/index.ts | 31 + task/build/redirect/build.ts | 38 + task/build/redirect/create-package-json.ts | 96 + task/build/redirect/create-redirect-paths.ts | 51 + task/build/require/build.ts | 38 + task/build/require/compile.ts | 40 + test/runtime/assert/assert.ts | 6 +- test/runtime/compiler-ajv/const.ts | 39 + test/runtime/compiler-ajv/index.ts | 3 +- test/runtime/compiler-ajv/partial.ts | 14 +- test/runtime/compiler-ajv/required.ts | 10 +- test/runtime/compiler-ajv/string-pattern.ts | 65 + test/runtime/compiler-ajv/validate.ts | 14 +- test/runtime/compiler/const.ts | 39 + test/runtime/compiler/index.ts | 2 + test/runtime/compiler/partial.ts | 14 +- test/runtime/compiler/regexp.ts | 63 +- test/runtime/compiler/required.ts | 10 +- .../regexp.ts => compiler/string-pattern.ts} | 18 +- test/runtime/errors/types/index.ts | 1 + test/runtime/errors/types/regexp.ts | 17 + test/runtime/type/extends/any.ts | 75 +- test/runtime/type/extends/array.ts | 203 +- test/runtime/type/extends/async-iterator.ts | 51 +- test/runtime/type/extends/bigint.ts | 79 +- test/runtime/type/extends/boolean.ts | 71 +- test/runtime/type/extends/constructor.ts | 167 +- test/runtime/type/extends/date.ts | 75 +- test/runtime/type/extends/function.ts | 167 +- test/runtime/type/extends/index.ts | 1 + test/runtime/type/extends/integer.ts | 75 +- test/runtime/type/extends/iterator.ts | 51 +- test/runtime/type/extends/literal.ts | 203 +- test/runtime/type/extends/not.ts | 103 +- test/runtime/type/extends/null.ts | 79 +- test/runtime/type/extends/number.ts | 75 +- test/runtime/type/extends/object.ts | 135 +- test/runtime/type/extends/promise.ts | 159 +- test/runtime/type/extends/record.ts | 131 +- test/runtime/type/extends/regexp.ts | 113 + test/runtime/type/extends/string.ts | 87 +- test/runtime/type/extends/symbol.ts | 83 +- test/runtime/type/extends/template-literal.ts | 123 +- test/runtime/type/extends/tuple.ts | 127 +- test/runtime/type/extends/uint8array.ts | 75 +- test/runtime/type/extends/undefined.ts | 71 +- test/runtime/type/extends/union.ts | 107 +- test/runtime/type/extends/unknown.ts | 79 +- test/runtime/type/extends/void.ts | 83 +- test/runtime/type/guard/any.ts | 6 +- test/runtime/type/guard/array.ts | 16 +- test/runtime/type/guard/async-iterator.ts | 6 +- test/runtime/type/guard/awaited.ts | 22 +- test/runtime/type/guard/bigint.ts | 6 +- test/runtime/type/guard/boolean.ts | 6 +- test/runtime/type/guard/capitalize.ts | 10 +- test/runtime/type/guard/composite.ts | 18 +- test/runtime/type/guard/const.ts | 124 + test/runtime/type/guard/constructor.ts | 14 +- test/runtime/type/guard/date.ts | 16 +- test/runtime/type/guard/deref.ts | 110 + test/runtime/type/guard/enum.ts | 24 +- test/runtime/type/guard/exclude.ts | 18 +- test/runtime/type/guard/extract.ts | 12 +- test/runtime/type/guard/function.ts | 14 +- test/runtime/type/guard/index.ts | 4 + test/runtime/type/guard/indexed.ts | 169 +- test/runtime/type/guard/integer.ts | 16 +- test/runtime/type/guard/intersect.ts | 4 +- test/runtime/type/guard/iterator.ts | 6 +- test/runtime/type/guard/keyof.ts | 28 +- test/runtime/type/guard/kind.ts | 4 +- test/runtime/type/guard/literal.ts | 12 +- test/runtime/type/guard/lowercase.ts | 10 +- test/runtime/type/guard/mapped.ts | 609 +++ test/runtime/type/guard/not.ts | 6 +- test/runtime/type/guard/null.ts | 6 +- test/runtime/type/guard/number.ts | 16 +- test/runtime/type/guard/object.ts | 20 +- test/runtime/type/guard/omit.ts | 14 +- test/runtime/type/guard/partial.ts | 6 +- test/runtime/type/guard/pick.ts | 24 +- test/runtime/type/guard/promise.ts | 10 +- test/runtime/type/guard/record.ts | 70 +- test/runtime/type/guard/recursive.ts | 8 +- test/runtime/type/guard/ref.ts | 6 +- test/runtime/type/guard/regexp.ts | 23 + test/runtime/type/guard/required.ts | 6 +- test/runtime/type/guard/rest.ts | 24 +- test/runtime/type/guard/string.ts | 12 +- test/runtime/type/guard/symbol.ts | 6 +- test/runtime/type/guard/template-literal.ts | 18 +- test/runtime/type/guard/this.ts | 4 +- test/runtime/type/guard/tuple.ts | 8 +- test/runtime/type/guard/uint8array.ts | 10 +- test/runtime/type/guard/uncapitalize.ts | 10 +- test/runtime/type/guard/undefined.ts | 6 +- test/runtime/type/guard/union.ts | 24 +- test/runtime/type/guard/unknown.ts | 6 +- test/runtime/type/guard/unsafe.ts | 10 +- test/runtime/type/guard/uppercase.ts | 10 +- test/runtime/type/guard/void.ts | 6 +- test/runtime/type/index.ts | 2 +- test/runtime/type/intrinsic/intrinsic.ts | 109 +- test/runtime/type/normalize/exclude.ts | 6 +- test/runtime/type/normalize/extract.ts | 10 +- test/runtime/type/normalize/indexed.ts | 96 +- test/runtime/type/normalize/intersect.ts | 6 +- test/runtime/type/normalize/record.ts | 2 +- test/runtime/type/normalize/union.ts | 6 +- test/runtime/type/template-literal/finite.ts | 71 + .../runtime/type/template-literal/generate.ts | 199 + .../{template => template-literal}/index.ts | 2 +- .../parser.ts => template-literal/parse.ts} | 82 +- .../{template => template-literal}/pattern.ts | 2 +- test/runtime/type/template/finite.ts | 71 - test/runtime/type/template/generate.ts | 199 - test/runtime/type/value/guard.ts | 78 + test/runtime/value/cast/regexp.ts | 7 + test/runtime/value/check/const.ts | 42 + test/runtime/value/check/index.ts | 1 + test/runtime/value/clean/any.ts | 11 + test/runtime/value/clean/array.ts | 21 + test/runtime/value/clean/async-iterator.ts | 11 + test/runtime/value/clean/bigint.ts | 11 + test/runtime/value/clean/boolean.ts | 11 + test/runtime/value/clean/composite.ts | 11 + test/runtime/value/clean/constructor.ts | 11 + test/runtime/value/clean/date.ts | 11 + test/runtime/value/clean/enum.ts | 11 + test/runtime/value/clean/function.ts | 11 + test/runtime/value/clean/index.ts | 34 + test/runtime/value/clean/integer.ts | 11 + test/runtime/value/clean/intersect.ts | 346 ++ test/runtime/value/clean/iterator.ts | 11 + test/runtime/value/clean/keyof.ts | 11 + test/runtime/value/clean/kind.ts | 11 + test/runtime/value/clean/literal.ts | 11 + test/runtime/value/clean/never.ts | 11 + test/runtime/value/clean/not.ts | 11 + test/runtime/value/clean/null.ts | 11 + test/runtime/value/clean/number.ts | 11 + test/runtime/value/clean/object.ts | 178 + test/runtime/value/clean/promise.ts | 11 + test/runtime/value/clean/record.ts | 114 + test/runtime/value/clean/recursive.ts | 112 + test/runtime/value/clean/ref.ts | 78 + test/runtime/value/clean/regexp.ts | 11 + test/runtime/value/clean/string.ts | 11 + test/runtime/value/clean/symbol.ts | 11 + test/runtime/value/clean/template-literal.ts | 11 + test/runtime/value/clean/tuple.ts | 110 + test/runtime/value/clean/uint8array.ts | 11 + test/runtime/value/clean/undefined.ts | 11 + test/runtime/value/clean/union.ts | 134 + test/runtime/value/clean/unknown.ts | 11 + test/runtime/value/clean/void.ts | 11 + test/runtime/value/create/regexp.ts | 7 + test/runtime/value/default/any.ts | 16 + test/runtime/value/default/array.ts | 24 + test/runtime/value/default/async-iterator.ts | 16 + test/runtime/value/default/bigint.ts | 16 + test/runtime/value/default/boolean.ts | 16 + test/runtime/value/default/composite.ts | 16 + test/runtime/value/default/constructor.ts | 16 + test/runtime/value/default/date.ts | 16 + test/runtime/value/default/enum.ts | 16 + test/runtime/value/default/function.ts | 16 + test/runtime/value/default/index.ts | 35 + test/runtime/value/default/integer.ts | 16 + test/runtime/value/default/intersect.ts | 106 + test/runtime/value/default/iterator.ts | 16 + test/runtime/value/default/keyof.ts | 16 + test/runtime/value/default/kind.ts | 16 + test/runtime/value/default/literal.ts | 16 + test/runtime/value/default/never.ts | 16 + test/runtime/value/default/not.ts | 16 + test/runtime/value/default/null.ts | 16 + test/runtime/value/default/number.ts | 16 + test/runtime/value/default/object.ts | 180 + test/runtime/value/default/promise.ts | 16 + test/runtime/value/default/record.ts | 66 + test/runtime/value/default/recursive.ts | 58 + test/runtime/value/default/ref.ts | 33 + test/runtime/value/default/regexp.ts | 16 + test/runtime/value/default/string.ts | 16 + test/runtime/value/default/symbol.ts | 16 + .../runtime/value/default/template-literal.ts | 16 + test/runtime/value/default/tuple.ts | 44 + test/runtime/value/default/uint8array.ts | 16 + test/runtime/value/default/undefined.ts | 16 + test/runtime/value/default/union.ts | 110 + test/runtime/value/default/unknown.ts | 16 + test/runtime/value/default/void.ts | 16 + test/runtime/value/guard/guard.ts | 2 +- test/runtime/value/hash/hash.ts | 108 +- test/runtime/value/index.ts | 2 + test/runtime/value/transform/_encoder.ts | 2 +- test/runtime/value/transform/union.ts | 43 +- test/static/assert.ts | 41 +- test/static/const.ts | 41 + test/static/deref.ts | 56 + test/static/index.ts | 3 + test/static/indexed.ts | 25 +- test/static/mapped.ts | 404 ++ test/static/optional.ts | 35 + test/static/readonly.ts | 36 + test/static/record.ts | 3 +- test/static/template-literal.ts | 7 + test/static/transform.ts | 12 +- test/static/union.ts | 33 + tsconfig.json | 64 +- 501 files changed, 25083 insertions(+), 8133 deletions(-) delete mode 100644 benchmark/measurement/index.ts create mode 100644 changelog/0.32.0.md create mode 100644 example/annotation/annotation.ts create mode 100644 example/annotation/index.ts rename {examples => example}/collections/array.ts (100%) rename {examples => example}/collections/index.ts (100%) rename {examples => example}/collections/map.ts (100%) rename {examples => example}/collections/readme.md (100%) rename {examples => example}/collections/set.ts (100%) rename {examples => example}/formats/date-time.ts (100%) rename {examples => example}/formats/date.ts (100%) rename {examples => example}/formats/email.ts (100%) rename {examples => example}/formats/index.ts (100%) rename {examples => example}/formats/ipv4.ts (100%) rename {examples => example}/formats/ipv6.ts (100%) rename {examples => example}/formats/time.ts (100%) rename {examples => example}/formats/url.ts (100%) rename {examples => example}/formats/uuid.ts (100%) rename {examples => example}/index.ts (100%) rename {examples => example}/prototypes/evaluate.ts (82%) rename {examples => example}/prototypes/index.ts (98%) rename {examples => example}/prototypes/partial-deep.ts (84%) rename {examples => example}/prototypes/readme.md (100%) rename {examples => example}/prototypes/union-enum.ts (100%) rename {examples => example}/prototypes/union-oneof.ts (100%) rename {examples => example}/typedef/index.ts (100%) rename {examples => example}/typedef/readme.md (100%) rename {examples => example}/typedef/typedef.ts (93%) delete mode 100644 examples/trpc/readme.md create mode 100644 src/errors/function.ts create mode 100644 src/index.ts create mode 100644 src/system/policy.ts create mode 100644 src/type/any/any.ts create mode 100644 src/type/any/index.ts create mode 100644 src/type/array/array.ts create mode 100644 src/type/array/index.ts create mode 100644 src/type/async-iterator/async-iterator.ts create mode 100644 src/type/async-iterator/index.ts create mode 100644 src/type/awaited/awaited.ts create mode 100644 src/type/awaited/index.ts create mode 100644 src/type/bigint/bigint.ts create mode 100644 src/type/bigint/index.ts create mode 100644 src/type/boolean/boolean.ts create mode 100644 src/type/boolean/index.ts create mode 100644 src/type/clone/index.ts create mode 100644 src/type/clone/type.ts create mode 100644 src/type/clone/value.ts create mode 100644 src/type/composite/composite.ts create mode 100644 src/type/composite/index.ts create mode 100644 src/type/const/const.ts create mode 100644 src/type/const/index.ts create mode 100644 src/type/constructor-parameters/constructor-parameters.ts create mode 100644 src/type/constructor-parameters/index.ts create mode 100644 src/type/constructor/constructor.ts create mode 100644 src/type/constructor/index.ts create mode 100644 src/type/date/date.ts create mode 100644 src/type/date/index.ts create mode 100644 src/type/deref/deref.ts create mode 100644 src/type/deref/index.ts create mode 100644 src/type/discard/discard.ts create mode 100644 src/type/discard/index.ts create mode 100644 src/type/enum/enum.ts create mode 100644 src/type/enum/index.ts create mode 100644 src/type/error/error.ts create mode 100644 src/type/error/index.ts create mode 100644 src/type/exclude/exclude-from-mapped-result.ts create mode 100644 src/type/exclude/exclude.ts create mode 100644 src/type/exclude/index.ts create mode 100644 src/type/extends/extends-check.ts create mode 100644 src/type/extends/extends-from-mapped-key.ts create mode 100644 src/type/extends/extends-from-mapped-result.ts create mode 100644 src/type/extends/extends-undefined.ts create mode 100644 src/type/extends/extends.ts create mode 100644 src/type/extends/index.ts create mode 100644 src/type/extract/extract-from-mapped-result.ts create mode 100644 src/type/extract/extract.ts create mode 100644 src/type/extract/index.ts create mode 100644 src/type/function/function.ts create mode 100644 src/type/function/index.ts create mode 100644 src/type/guard/index.ts create mode 100644 src/type/guard/type.ts create mode 100644 src/type/guard/value.ts create mode 100644 src/type/helpers/helpers.ts create mode 100644 src/type/helpers/index.ts create mode 100644 src/type/index.ts create mode 100644 src/type/indexed/index.ts create mode 100644 src/type/indexed/indexed-from-mapped-key.ts create mode 100644 src/type/indexed/indexed-from-mapped-result.ts create mode 100644 src/type/indexed/indexed-property-keys.ts create mode 100644 src/type/indexed/indexed.ts create mode 100644 src/type/instance-type/index.ts create mode 100644 src/type/instance-type/instance-type.ts create mode 100644 src/type/integer/index.ts create mode 100644 src/type/integer/integer.ts create mode 100644 src/type/intersect/index.ts create mode 100644 src/type/intersect/intersect-create.ts create mode 100644 src/type/intersect/intersect-evaluated.ts rename examples/prototypes/const.ts => src/type/intersect/intersect-type.ts (50%) create mode 100644 src/type/intersect/intersect.ts create mode 100644 src/type/intrinsic/capitalize.ts create mode 100644 src/type/intrinsic/index.ts create mode 100644 src/type/intrinsic/intrinsic-from-mapped-key.ts create mode 100644 src/type/intrinsic/intrinsic.ts create mode 100644 src/type/intrinsic/lowercase.ts create mode 100644 src/type/intrinsic/uncapitalize.ts create mode 100644 src/type/intrinsic/uppercase.ts create mode 100644 src/type/iterator/index.ts create mode 100644 src/type/iterator/iterator.ts create mode 100644 src/type/keyof/index.ts create mode 100644 src/type/keyof/keyof-from-mapped-result.ts create mode 100644 src/type/keyof/keyof-property-keys.ts create mode 100644 src/type/keyof/keyof.ts create mode 100644 src/type/literal/index.ts create mode 100644 src/type/literal/literal.ts create mode 100644 src/type/mapped/index.ts create mode 100644 src/type/mapped/mapped-key.ts create mode 100644 src/type/mapped/mapped-result.ts create mode 100644 src/type/mapped/mapped.ts create mode 100644 src/type/never/index.ts create mode 100644 src/type/never/never.ts create mode 100644 src/type/not/index.ts create mode 100644 src/type/not/not.ts create mode 100644 src/type/null/index.ts create mode 100644 src/type/null/null.ts create mode 100644 src/type/number/index.ts create mode 100644 src/type/number/number.ts create mode 100644 src/type/object/index.ts create mode 100644 src/type/object/object.ts create mode 100644 src/type/omit/index.ts create mode 100644 src/type/omit/omit-from-mapped-key.ts create mode 100644 src/type/omit/omit-from-mapped-result.ts create mode 100644 src/type/omit/omit.ts create mode 100644 src/type/optional/index.ts create mode 100644 src/type/optional/optional-from-mapped-result.ts create mode 100644 src/type/optional/optional.ts create mode 100644 src/type/parameters/index.ts create mode 100644 src/type/parameters/parameters.ts create mode 100644 src/type/partial/index.ts create mode 100644 src/type/partial/partial-from-mapped-result.ts create mode 100644 src/type/partial/partial.ts create mode 100644 src/type/patterns/index.ts create mode 100644 src/type/patterns/patterns.ts create mode 100644 src/type/pick/index.ts create mode 100644 src/type/pick/pick-from-mapped-key.ts create mode 100644 src/type/pick/pick-from-mapped-result.ts create mode 100644 src/type/pick/pick.ts create mode 100644 src/type/promise/index.ts create mode 100644 src/type/promise/promise.ts create mode 100644 src/type/readonly-optional/index.ts create mode 100644 src/type/readonly-optional/readonly-optional.ts create mode 100644 src/type/readonly/index.ts create mode 100644 src/type/readonly/readonly-from-mapped-result.ts create mode 100644 src/type/readonly/readonly.ts create mode 100644 src/type/record/index.ts create mode 100644 src/type/record/record.ts create mode 100644 src/type/recursive/index.ts create mode 100644 src/type/recursive/recursive.ts create mode 100644 src/type/ref/index.ts create mode 100644 src/type/ref/ref.ts create mode 100644 src/type/regexp/index.ts create mode 100644 src/type/regexp/regexp.ts create mode 100644 src/type/registry/format.ts create mode 100644 src/type/registry/index.ts create mode 100644 src/type/registry/type.ts create mode 100644 src/type/required/index.ts create mode 100644 src/type/required/required-from-mapped-result.ts create mode 100644 src/type/required/required.ts create mode 100644 src/type/rest/index.ts create mode 100644 src/type/rest/rest.ts create mode 100644 src/type/return-type/index.ts create mode 100644 src/type/return-type/return-type.ts create mode 100644 src/type/schema/anyschema.ts create mode 100644 src/type/schema/index.ts create mode 100644 src/type/schema/schema.ts create mode 100644 src/type/sets/index.ts create mode 100644 src/type/sets/set.ts create mode 100644 src/type/static/index.ts create mode 100644 src/type/static/static.ts create mode 100644 src/type/strict/index.ts create mode 100644 src/type/strict/strict.ts create mode 100644 src/type/string/index.ts create mode 100644 src/type/string/string.ts create mode 100644 src/type/symbol/index.ts create mode 100644 src/type/symbol/symbol.ts create mode 100644 src/type/symbols/index.ts create mode 100644 src/type/symbols/symbols.ts create mode 100644 src/type/template-literal/finite.ts create mode 100644 src/type/template-literal/generate.ts create mode 100644 src/type/template-literal/index.ts create mode 100644 src/type/template-literal/parse.ts create mode 100644 src/type/template-literal/pattern.ts create mode 100644 src/type/template-literal/syntax.ts create mode 100644 src/type/template-literal/template-literal.ts create mode 100644 src/type/template-literal/union.ts create mode 100644 src/type/transform/index.ts create mode 100644 src/type/transform/transform.ts create mode 100644 src/type/tuple/index.ts create mode 100644 src/type/tuple/tuple.ts create mode 100644 src/type/type/index.ts create mode 100644 src/type/type/javascript.ts create mode 100644 src/type/type/json.ts create mode 100644 src/type/type/type.ts create mode 100644 src/type/uint8array/index.ts create mode 100644 src/type/uint8array/uint8array.ts create mode 100644 src/type/undefined/index.ts create mode 100644 src/type/undefined/undefined.ts create mode 100644 src/type/union/index.ts create mode 100644 src/type/union/union-create.ts create mode 100644 src/type/union/union-evaluated.ts create mode 100644 src/type/union/union-type.ts create mode 100644 src/type/union/union.ts create mode 100644 src/type/unknown/index.ts create mode 100644 src/type/unknown/unknown.ts create mode 100644 src/type/unsafe/index.ts create mode 100644 src/type/unsafe/unsafe.ts create mode 100644 src/type/void/index.ts create mode 100644 src/type/void/void.ts delete mode 100644 src/typebox.ts rename src/value/{ => cast}/cast.ts (51%) create mode 100644 src/value/cast/index.ts rename src/value/{ => check}/check.ts (58%) create mode 100644 src/value/check/index.ts create mode 100644 src/value/clean/clean.ts create mode 100644 src/value/clean/index.ts rename src/value/{ => clone}/clone.ts (92%) create mode 100644 src/value/clone/index.ts rename src/value/{ => convert}/convert.ts (53%) create mode 100644 src/value/convert/index.ts rename src/value/{ => create}/create.ts (52%) create mode 100644 src/value/create/index.ts create mode 100644 src/value/default/default.ts create mode 100644 src/value/default/index.ts rename src/value/{ => delta}/delta.ts (71%) create mode 100644 src/value/delta/index.ts rename src/value/{ => deref}/deref.ts (89%) create mode 100644 src/value/deref/index.ts rename src/value/{ => equal}/equal.ts (97%) create mode 100644 src/value/equal/index.ts rename src/value/{ => guard}/guard.ts (99%) create mode 100644 src/value/guard/index.ts rename src/value/{ => hash}/hash.ts (95%) create mode 100644 src/value/hash/index.ts create mode 100644 src/value/mutate/index.ts rename src/value/{ => mutate}/mutate.ts (85%) delete mode 100644 src/value/pointer.ts create mode 100644 src/value/pointer/index.ts create mode 100644 src/value/pointer/pointer.ts delete mode 100644 src/value/transform.ts create mode 100644 src/value/transform/decode.ts create mode 100644 src/value/transform/encode.ts create mode 100644 src/value/transform/has.ts create mode 100644 src/value/transform/index.ts delete mode 100644 src/value/value.ts create mode 100644 src/value/value/index.ts create mode 100644 src/value/value/value.ts rename {benchmark => task/benchmark}/compression/index.ts (71%) rename {benchmark => task/benchmark}/compression/module/typebox-compiler.ts (100%) rename {benchmark => task/benchmark}/compression/module/typebox-errors.ts (100%) rename {benchmark => task/benchmark}/compression/module/typebox-system.ts (100%) rename {benchmark => task/benchmark}/compression/module/typebox-value.ts (100%) rename {benchmark => task/benchmark}/compression/module/typebox.ts (100%) rename {benchmark => task/benchmark}/index.ts (100%) create mode 100644 task/benchmark/measurement/index.ts rename {benchmark => task/benchmark}/measurement/module/benchmark.ts (100%) rename {benchmark => task/benchmark}/measurement/module/cases.ts (97%) rename {benchmark => task/benchmark}/measurement/module/check.ts (93%) rename {benchmark => task/benchmark}/measurement/module/compile.ts (95%) rename {benchmark => task/benchmark}/measurement/module/index.ts (100%) rename {benchmark => task/benchmark}/measurement/module/result.ts (100%) create mode 100644 task/build/common/remove-notices.ts create mode 100644 task/build/import/build.ts create mode 100644 task/build/import/compile.ts create mode 100644 task/build/import/convert-to-esm.ts create mode 100644 task/build/index.ts create mode 100644 task/build/redirect/build.ts create mode 100644 task/build/redirect/create-package-json.ts create mode 100644 task/build/redirect/create-redirect-paths.ts create mode 100644 task/build/require/build.ts create mode 100644 task/build/require/compile.ts create mode 100644 test/runtime/compiler-ajv/const.ts create mode 100644 test/runtime/compiler-ajv/string-pattern.ts create mode 100644 test/runtime/compiler/const.ts rename test/runtime/{compiler-ajv/regexp.ts => compiler/string-pattern.ts} (72%) create mode 100644 test/runtime/errors/types/regexp.ts create mode 100644 test/runtime/type/extends/regexp.ts create mode 100644 test/runtime/type/guard/const.ts create mode 100644 test/runtime/type/guard/deref.ts create mode 100644 test/runtime/type/guard/mapped.ts create mode 100644 test/runtime/type/guard/regexp.ts create mode 100644 test/runtime/type/template-literal/finite.ts create mode 100644 test/runtime/type/template-literal/generate.ts rename test/runtime/type/{template => template-literal}/index.ts (76%) rename test/runtime/type/{template/parser.ts => template-literal/parse.ts} (82%) rename test/runtime/type/{template => template-literal}/pattern.ts (98%) delete mode 100644 test/runtime/type/template/finite.ts delete mode 100644 test/runtime/type/template/generate.ts create mode 100644 test/runtime/value/check/const.ts create mode 100644 test/runtime/value/clean/any.ts create mode 100644 test/runtime/value/clean/array.ts create mode 100644 test/runtime/value/clean/async-iterator.ts create mode 100644 test/runtime/value/clean/bigint.ts create mode 100644 test/runtime/value/clean/boolean.ts create mode 100644 test/runtime/value/clean/composite.ts create mode 100644 test/runtime/value/clean/constructor.ts create mode 100644 test/runtime/value/clean/date.ts create mode 100644 test/runtime/value/clean/enum.ts create mode 100644 test/runtime/value/clean/function.ts create mode 100644 test/runtime/value/clean/index.ts create mode 100644 test/runtime/value/clean/integer.ts create mode 100644 test/runtime/value/clean/intersect.ts create mode 100644 test/runtime/value/clean/iterator.ts create mode 100644 test/runtime/value/clean/keyof.ts create mode 100644 test/runtime/value/clean/kind.ts create mode 100644 test/runtime/value/clean/literal.ts create mode 100644 test/runtime/value/clean/never.ts create mode 100644 test/runtime/value/clean/not.ts create mode 100644 test/runtime/value/clean/null.ts create mode 100644 test/runtime/value/clean/number.ts create mode 100644 test/runtime/value/clean/object.ts create mode 100644 test/runtime/value/clean/promise.ts create mode 100644 test/runtime/value/clean/record.ts create mode 100644 test/runtime/value/clean/recursive.ts create mode 100644 test/runtime/value/clean/ref.ts create mode 100644 test/runtime/value/clean/regexp.ts create mode 100644 test/runtime/value/clean/string.ts create mode 100644 test/runtime/value/clean/symbol.ts create mode 100644 test/runtime/value/clean/template-literal.ts create mode 100644 test/runtime/value/clean/tuple.ts create mode 100644 test/runtime/value/clean/uint8array.ts create mode 100644 test/runtime/value/clean/undefined.ts create mode 100644 test/runtime/value/clean/union.ts create mode 100644 test/runtime/value/clean/unknown.ts create mode 100644 test/runtime/value/clean/void.ts create mode 100644 test/runtime/value/default/any.ts create mode 100644 test/runtime/value/default/array.ts create mode 100644 test/runtime/value/default/async-iterator.ts create mode 100644 test/runtime/value/default/bigint.ts create mode 100644 test/runtime/value/default/boolean.ts create mode 100644 test/runtime/value/default/composite.ts create mode 100644 test/runtime/value/default/constructor.ts create mode 100644 test/runtime/value/default/date.ts create mode 100644 test/runtime/value/default/enum.ts create mode 100644 test/runtime/value/default/function.ts create mode 100644 test/runtime/value/default/index.ts create mode 100644 test/runtime/value/default/integer.ts create mode 100644 test/runtime/value/default/intersect.ts create mode 100644 test/runtime/value/default/iterator.ts create mode 100644 test/runtime/value/default/keyof.ts create mode 100644 test/runtime/value/default/kind.ts create mode 100644 test/runtime/value/default/literal.ts create mode 100644 test/runtime/value/default/never.ts create mode 100644 test/runtime/value/default/not.ts create mode 100644 test/runtime/value/default/null.ts create mode 100644 test/runtime/value/default/number.ts create mode 100644 test/runtime/value/default/object.ts create mode 100644 test/runtime/value/default/promise.ts create mode 100644 test/runtime/value/default/record.ts create mode 100644 test/runtime/value/default/recursive.ts create mode 100644 test/runtime/value/default/ref.ts create mode 100644 test/runtime/value/default/regexp.ts create mode 100644 test/runtime/value/default/string.ts create mode 100644 test/runtime/value/default/symbol.ts create mode 100644 test/runtime/value/default/template-literal.ts create mode 100644 test/runtime/value/default/tuple.ts create mode 100644 test/runtime/value/default/uint8array.ts create mode 100644 test/runtime/value/default/undefined.ts create mode 100644 test/runtime/value/default/union.ts create mode 100644 test/runtime/value/default/unknown.ts create mode 100644 test/runtime/value/default/void.ts create mode 100644 test/static/const.ts create mode 100644 test/static/deref.ts create mode 100644 test/static/mapped.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e482eb7e1..7d9b21121 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: node: [16.x, 18.x, 20.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7ad6b9d88..d43b3bc2c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -10,7 +10,7 @@ jobs: node: [20.x] os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Node uses: actions/setup-node@v3 with: diff --git a/benchmark/measurement/index.ts b/benchmark/measurement/index.ts deleted file mode 100644 index 6175663af..000000000 --- a/benchmark/measurement/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { shell } from '@sinclair/hammer' - -export async function measurement() { - await shell(`hammer run benchmark/measurement/module/index.ts --dist target/benchmark/measurement`) -} diff --git a/changelog/0.24.6.md b/changelog/0.24.6.md index 68477e071..b329dec8f 100644 --- a/changelog/0.24.6.md +++ b/changelog/0.24.6.md @@ -13,7 +13,7 @@ const T: any = {} // T is any const { type } = T // unsafe: type is any -if(TypeGuard.TString(T)) { +if(TypeGuard.IsString(T)) { const { type } = T // safe: type is 'string' } diff --git a/changelog/0.32.0.md b/changelog/0.32.0.md new file mode 100644 index 000000000..83a1bddfc --- /dev/null +++ b/changelog/0.32.0.md @@ -0,0 +1,473 @@ +## [0.32.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.32.0) + +## Overview + +Revision 0.32.0 adds support for ESM and carries out the work necessary to fully modularize the TypeBox type system to enable selective type imports. This revision also adds three new types (Mapped, Const, and Deref), along with two new Value functions (Clean and Default) as well as many enhancements to existing types (Index, KeyOf, RegExp, Optional and Readonly). This revision also carries out many internal optimizations to enhance type inference across all types. + +This revision is a milestone revision for the TypeBox project. It has several breaking changes and requires a minor revision. + +## Contents + +- [Type Imports](#Type-Imports) +- [Value Function Import](#Value-Function-Imports) +- [CommonJS and ESM](#CommonJS-and-ESM) +- [Types](#Types) + - [Mapped Type](#Types-Mapped-Type) + - [Const Type](#Types-Const-Type) + - [Deref Type](#Types-Deref-Type) + - [RegExp Type](#Types-RegExp-Type) + - [Subtract Modifiers](#Types-Subtract-Modifiers) +- [Values](#Values) + - [Clean Function](#Values-Clean-Function) + - [Default Function](#Values-Default-Function) +- [Errors](#Errors) + - [Error Parameter](#Errors-Error-Parameter) +- [Optimizations](#Optimizations) + - [Bundle Size](#Optimizations-Bundle-Size) +- [Breaking](#Breaking) + - [Renamed Symbols](#Breaking-Renamed-Symbols) + - [TypeGuard Interface Change](#Breaking-TypeGuard-Interface-Change) + - [Value Submodule Imports](#Breaking-Value-Submodule-Imports) + - [Error Function](#Breaking-Error-Function) + - [RegEx](#Breaking-RegEx) + + + +### Type Imports + +Revision 0.32.0 adds the ability to import types individually. + +```typescript +import { Type, type Static } from '@sinclair/typebox' // classic - 37.0 kb minified + +import { Object, String, Number, type Static } from '@sinclair/typebox' // selective - 6.5 kb minified +``` + + + +### Value Function Imports + +Revision 0.32.0 adds the ability to import value functions from the `/value` module path. + +```typescript +import { Value } from '@sinclair/typebox/value' // classic - 61.5 kb minified + +import { Check } from '@sinclair/typebox/value' // selective - 18.2 kb minified +``` + +### CommonJS and ESM + + + +Revision 0.32.0 now publishes both CommonJS and ESM builds of TypeBox. Existing CommonJS users should not be impacted by the addition of ESM. For ESM users however, particularily those using bundlers, it's now possible to benefit from deep tree shake optimizations provided by modern bundler tooling. + + + +## Types + +Revision 0.32.0 adds three new types to the type system and makes enhancements to Readonly and Optional modifiers. + + + +### Mapped Type + +Revision 0.32.0 adds the Mapped type which replicates TS [Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html) at runtime. The following shows the syntax comparison between TypeScript and TypeBox. + +#### TypeScript + +```typescript +type T = { + x: number, + y: number, + z: number +} + +type M = { [K in keyof T]: T[K] } // a mapped type +``` +#### TypeBox +```typescript +const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() +}) + +const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) // a mapped type +``` +Mapped types use a functional design to replicate the TypeScript feature. For users interested in this type, it may be helpful to use the [TypeBox Workbench](https://sinclairzx81.github.io/typebox-workbench/) which can generate runtime Mapped types from TypeScript syntax. + + + +### Const Type + +Revision 0.32.0 adds a new Const type that creates `readonly` types from object, array and primitive literal values. This type analogs the TypeScript `as const` syntax. The following shows general usage. + +```typescript +const A = Type.Const(1 as const) // const A: TLiteral<1> + +const B = Type.Const([1, 2, 3] as const) // const B: TReadonly, + // TLiteral<2>, + // TLiteral<3> + // ]>> + +const C = Type.Const({ // const C: TObject<{ + x: 1, // x: TReadonly>, + y: 2, // y: TReadonly>, + z: 3 // z: TReadonly>, +} as const) // }> +``` +Revision 0.32.0 continues support for TypeScript 4.0, and because of this, the `as const` syntax must be appended to each literal value passed to the Const type. When TypeBox ends support for 4.0, updates will be made to this type to make use of [Const Type Parameters](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#const-type-parameters). This update will enable TypeBox to correctly infer the readonly literal type without the need for `as const`. + + + + +### Deref Type + +Revision 0.32.0 adds a new Type.Deref type which can be used to dereference type schematics. + +```typescript +const Vector = Type.Object({ // const Vector = { + x: Type.Number(), // type: 'object', + y: Type.Number(), // required: ['x', 'y', 'z'], +}, { $id: 'Vector' }) // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // }, + // $id: 'Vector' + // } + +const VectorRef = Type.Ref(Vector) // const VectorRef = { + // $ref: 'Vector' + // } +// ... Embedded Reference Type + +const Vertex = Type.Object({ // const Vertex = { + position: VectorRef, // type: 'object', + texcoord: VectorRef, // required: ['position', 'texcoord'], +}) // properties: { + // position: { $ref: 'Vector' }, + // texcoord: { $ref: 'Vector' } + // } + // } + +// ... Dereferenced Embedded Reference Type + +const VertexDeref = Type.Deref(Vertex, [Vector]) // const VertexDeref = { + // type: 'object', + // required: ['position', 'texcoord'], + // properties: { + // position: { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // } + // }, + // texcoord: { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // } + // } + // } + // } +``` +The addition of Deref was prompted by issues composing reference types with mapping types (such as Partial, Required, Pick and Omit) which is generally not supported. Prior to Revision 0.32.0, there was some expectation for users to maintain and dereference types manually. In 0.32.0, users will still need to maintain references, but Deref will offer a more convenient mechanism to normalize reference types prior to composition. + + + + +### RegExp Type + +Revision 0.32.0 updates RegExp to support the full ECMA 262 regular expression syntax. In previous revisions, this type had been expressed as an alias for `TString` with a `pattern` to try ensure compliance with the [regular expression](https://json-schema.org/understanding-json-schema/reference/regular_expressions) subset supported by Json Schema. In Revision 0.32.0, RegExp is given a new type representation unto itself (named `TRegExp`) which houses both `source` and `flags` properties used to reconstruct a JavaScript regular expression object, making it properly distinct from `TString` and fully supportive of UTF-16. + +```typescript +// Case Insensitive + +const T = Type.RegExp(/abc/i) // const T = { + // type: 'RegExp', + // source: 'abc', + // flags: 'i' + // } + +type T = Static // type T = string + +Value.Check(T, 'abc') // ok +Value.Check(T, 'ABC') // ok + +// Extended Syntax + +const E = Type.RegExp(/|\p{Extended_Pictographic}/gu) + +Value.Check(E, '♥️♦️♠️♣️') // ok - emoji supported +``` + +The RegExp type can be thought of as a more capable TemplateLiteral that can only reasonably infer as `string`. Additionally, the RegExp inference type of `string` is unique to the other `[JavaScript]` types in that it does not infer as it's named type. The updates to RegExp were prompted by the limitations with Json Schema expressions, and to provide better options for users requiring general Unicode validation support. For Json Schema compliance, the recommendation moving forward will be to use either String with pattern or TemplateLiteral. + +```typescript +const T = Type.String({ pattern: '^(a|b|c)$' }) // Json Schema compliant + +const T = Type.TemplateLiteral('${a|b|c}') // Json Schema compliant + +const T = Type.RegExp(/$(a|b|c)$/) // Non Json Schema compliant +``` + + + +### Subtract Modifier + +Revision 0.32.0 adds new overloads for Readonly and Optional modifiers that enable them to subtract (or remove) that modifier from a type. Both Readonly and Optional now accept an optional secondary boolean argument that if `false`, will remove the modifier. + +#### TypeScript +```typescript +type T = { + x?: number, + y?: number +} +type M = { [K in keyof T]-?: T[K] } // -? - subtract optional modifier +``` +#### TypeBox +```typescript +const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()) +}) +const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Optional(Type.Index(T, K), false) // false - subtract optional modifier +}) +``` +Subtractive modifiers are provided in support of the new Mapped type feature. + + + +## Values + +Revision 0.32.0 adds two new functions to the Value module. + + + +### Clean Function + +Revision 0.32.0 adds a new Clean function that can be used to omit any values unknown to the type. This function will work irrespective of if `additionalProperties` is specified on the type. The Clean function is intended to replicate the functionality of Ajv's `removeAdditional` configuration. + +```typescript +const T = Type.Object({ + x: Type.Number(), + y: Type.Number() +}) + +const X = Value.Clean(T, null) // const 'X = null + +const Y = Value.Clean(T, { x: 1 }) // const 'Y = { x: 1 } + +const Z = Value.Clean(T, { x: 1, y: 2, z: 3 }) // const 'Z = { x: 1, y: 2 } +``` + +Note: the Clean function does not check the validity of the value being cleaned, and does not provide assurances that the result will be valid. Its return value is `unknown` and should be checked before use. + + + +### Default Function + +Revision 0.32.0 adds a new Default function that can be used to add missing values if the type specifies a `default` annotation. This function is intended to replicate Ajv's `useDefaults` functionality. + +```typescript +const T = Type.Object({ + x: Type.Number({ default: 0 }), + y: Type.Number({ default: 0 }) +}) + +const X = Value.Default(T, null) // const 'X = null - non-enumerable + +const Y = Value.Default(T, { }) // const 'Y = { x: 0, y: 0 } + +const Z = Value.Default(T, { x: 1 }) // const 'Z = { x: 1, y: 0 } +``` + +The Default function does not check the validity of the value being defaulted, and does not provide assurances that the result will be valid. Its return value is `unknown` and should be checked before use. + + + +## Optimizations + +Following the work to modularize TypeBox's type system, additional optimizations were carried out across each submodule to only import dependent type infrastructure. This has led to some fairly significant reductions in output sizes across each submodule. The main TypeBox import has increased in size due in part to the new Mapped types feature and other associative types, however selective imports supported on this revision should offer options for users concerned about output bundle size. There will be contined work to optimize the new type system throughout 0.32.0 and subsequent revisions. + +The following shows the comparisons between 0.31.0 and 0.32.0. + + + +```typescript +// Revision 0.31.0 + +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '163.6 kb' │ ' 71.6 kb' │ '2.28 x' │ +│ typebox/errors │ '113.3 kb' │ ' 50.1 kb' │ '2.26 x' │ +│ typebox/system │ ' 83.9 kb' │ ' 37.5 kb' │ '2.24 x' │ +│ typebox/value │ '191.1 kb' │ ' 82.3 kb' │ '2.32 x' │ +│ typebox │ ' 73.8 kb' │ ' 32.3 kb' │ '2.29 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ + +// Revision 0.32.0 + +┌──────────────────────┬────────────┬────────────┬─────────────┐ +│ (index) │ Compiled │ Minified │ Compression │ +├──────────────────────┼────────────┼────────────┼─────────────┤ +│ typebox/compiler │ '120.6 kb' │ ' 52.9 kb' │ '2.28 x' │ +│ typebox/errors │ ' 55.7 kb' │ ' 25.5 kb' │ '2.19 x' │ +│ typebox/system │ ' 4.7 kb' │ ' 2.0 kb' │ '2.33 x' │ +│ typebox/value │ '146.2 kb' │ ' 62.0 kb' │ '2.36 x' │ +│ typebox │ ' 91.4 kb' │ ' 37.8 kb' │ '2.42 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ +``` + + +## Errors + +Revision 0.32.0 makes some enhancements to errors. + + + +### Error Parameter + +Revision 0.32.0 updates TypeBox's ErrorFunction to accept an ErrorParameter that contains additional information regarding the cause of a validation error. In Revision 0.31.0, only `errorType` and `schema` were passed through to this function. In 0.32.0 the additional properties `value` and `path` are also passed through. This update was prompted by some users needing to be able to generate specific error messages derived from specific values or other associated information. + +The following shows the changes from 0.31.0 to 0.32.0. + +```typescript +// Revision 0.31.0 + +import { TypeSystemErrorFunction } from '@sinclair/typebox/system' + +TypeSystemErrorFunction.Set((schema, errorType) => { + + return 'oh no, an error!' +}) + +// Revision 0.32.0 + +import { SetErrorFunction } from '@sinclair/typebox/errors' + +SetErrorFunction(({ schema, errorType, path, value }) => { // as destructured object + + return 'oh no, an error!' +}) +``` +Note that Revision 0.32.0 does make a breaking interface change by moving the ErrorFunction from `/system` to `/errors`. See breaking changes for more information. + + + +## Breaking + +The following list the breaking changes in Revision 0.32.0. + + + + +### Renamed Symbols + +Revision 0.32.0 renames the `Optional`, `Required` and `Transform` symbols to `OptionalKind`, `RequiredKind` and `TransformKind`. This change was necessary to avoid conflicts with exported type functions. + +```typescript +// Revision 0.31.0 +import { Kind, Hint, Optional, Required, Transform } from '@sinclair/typebox' // these are symbols + +// Revision 0.32.0 +import { + Kind, Hint, OptionalKind, RequiredKind, TransformKind, // these are symbols + Optional, Required, Transform // these are type imports +} from '@sinclair/typebox' +``` + + + +### TypeGuard Interface Change + +Revision 0.32.0 has a breaking interface change on the TypeGuard utility where the `T` prefixed guard functions have been updated to use the `Is` prefix. This naming change is perhaps somewhat more sensible than the previous naming, however the update was largely prompted by TypeScript compiler issues where interface types (i.e. `TString`) where conflicting with the `TString` functions leading to breakage in CommonJS. + +```typescript +// Revision 0.31.0 + +import { TypeGuard, Kind } from '@sinclair/typebox' + +const R = TypeGuard.TString({ ... }) + +// Revision 0.32.0 + +import { TypeGuard } from '@sinclair/typebox' + +const R = TypeGuard.IsString({ ... }) +``` + + + +### Value Submodule Imports + +The value submodule function import paths are unfortunately no longer supported. Instead, these can be imported directly on the `/value` path. The need to break the submodule paths was mostly due to complexities configuring dual ESM and CommonJS publishing for the package, as well as retaining support for pre and post node16 module resolution (of which many complexities reside, both for Node as well as for TypeScript type module resolution) + +```typescript +// Revision 0.31.0 + +import { Check } from '@sinclair/typebox/value/check' + +// Revision 0.32.0 + +import { Check } from '@sinclair/typebox/value' +``` + + + +### Error Function + +The TypeSystemErrorFunction has been replaced with SetErrorFunction which can be imported on the `/errors` submodule. This change is generally a tidy up, and to reserve the `/system` submodule for type system policy configuration, as well as future Json Schema generation options (draft 2020-12) + +```typescript +// Revision 0.31.0 + +import { TypeSystemErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/system' + +TypeSystemErrorFunction.Set((schema, errorType) => { // i18n override + switch(errorType) { + /* en-US */ case ValueErrorType.String: return 'Expected string' + /* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu' + /* ko-KR */ case ValueErrorType.Boolean: return '예상 부울' + /* en-US */ default: return DefaultErrorFunction(schema, errorType) + } +}) + +// Revision 0.32.0 + +import { SetErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/errors' + +SetErrorFunction((error) => { // i18n override + switch(error.errorType) { + /* en-US */ case ValueErrorType.String: return 'Expected string' + /* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu' + /* ko-KR */ case ValueErrorType.Boolean: return '예상 부울' + /* en-US */ default: return DefaultErrorFunction(error) + } +}) +``` + + + +### RegEx + +This RegEx function was flagged for deprecation on 0.30.0. It has been removed on Revision 0.32.0. Use the Type.RegExp type, or Type.String with a pattern to remain compatible with the Json Schema specification. + +```typescript +// Revision 0.31.0 + +const T = Type.RegEx(/abc/) // deprecation warning + +// Revision 0.32.0 + +const A = Type.RegExp(/abc/) // JavaScript Type + +const B = Type.String({ pattern: /abc/.source }) // Json Type +``` \ No newline at end of file diff --git a/example/annotation/annotation.ts b/example/annotation/annotation.ts new file mode 100644 index 000000000..26bebaf0d --- /dev/null +++ b/example/annotation/annotation.ts @@ -0,0 +1,148 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' + +// ------------------------------------------------------------------- +// Annotation +// +// Generates TypeScript Type Annotations from TypeBox types +// ------------------------------------------------------------------- +/** Generates TypeScript Type Annotations from TypeBox types */ +export namespace Annotation { + // ----------------------------------------------------------------- + // Escape + // ----------------------------------------------------------------- + function Escape(content: string) { + return content.replace(/'/g, "\\'") + } + // ----------------------------------------------------------------- + // Types + // ----------------------------------------------------------------- + function Intersect(schema: Types.TSchema[], references: Types.TSchema[]): string { + const [L, ...R] = schema + // prettier-ignore + return R.length === 0 + ? `${Visit(L, references)}` + : `${Visit(L, references)} & ${Intersect(R, references)}` + } + function Union(schema: Types.TSchema[], references: Types.TSchema[]): string { + const [L, ...R] = schema + // prettier-ignore + return R.length === 0 + ? `${Visit(L, references)}` + : `${Visit(L, references)} | ${Union(R, references)}` + } + function Tuple(schema: Types.TSchema[], references: Types.TSchema[]): string { + const [L, ...R] = schema + // prettier-ignore + return R.length > 0 + ? `${Visit(L, references)}, ${Tuple(R, references)}` + : `` + } + function Property(schema: Types.TProperties, K: string, references: Types.TSchema[]): string { + const TK = schema[K] + // prettier-ignore + return ( + Types.TypeGuard.IsOptional(TK) && Types.TypeGuard.IsReadonly(TK) ? `readonly ${K}?: ${Visit(TK, references)}` : + Types.TypeGuard.IsReadonly(TK) ? `readonly ${K}: ${Visit(TK, references)}` : + Types.TypeGuard.IsOptional(TK) ? `${K}?: ${Visit(TK, references)}` : + `${K}: ${Visit(TK, references)}` + ) + } + function Properties(schema: Types.TProperties, K: string[], references: Types.TSchema[]): string { + const [L, ...R] = K + // prettier-ignore + return R.length === 0 + ? `${Property(schema, L, references)}` + : `${Property(schema, L, references)}; ${Properties(schema, R, references)}` + } + function Parameters(schema: Types.TSchema[], I: number, references: Types.TSchema[]): string { + const [L, ...R] = schema + // prettier-ignore + return R.length === 0 + ? `param_${I}: ${Visit(L, references)}` + : `param_${I}: ${Visit(L, references)}, ${Parameters(R, I + 1, references)}` + } + function Literal(schema: Types.TLiteral, references: Types.TSchema[]): string { + return typeof schema.const === 'string' ? `'${Escape(schema.const)}'` : schema.const.toString() + } + function Record(schema: Types.TRecord, references: Types.TSchema[]): string { + // prettier-ignore + return ( + Types.PatternBooleanExact in schema.patternProperties ? `Record` : + Types.PatternNumberExact in schema.patternProperties ? `Record` : + Types.PatternStringExact in schema.patternProperties ? `Record` : + `{}` + ) + } + function TemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { + const E = Types.TemplateLiteralParseExact(schema.pattern) + if (!Types.IsTemplateLiteralExpressionFinite(E)) return 'string' + return [...Types.TemplateLiteralExpressionGenerate(E)].map((literal) => `'${Escape(literal)}'`).join(' | ') + } + function Visit(schema: Types.TSchema, references: Types.TSchema[]): string { + // prettier-ignore + return ( + Types.TypeGuard.IsAny(schema) ? 'any' : + Types.TypeGuard.IsArray(schema) ? `${Visit(schema.items, references)}[]` : + Types.TypeGuard.IsAsyncIterator(schema) ? `AsyncIterableIterator<${Visit(schema.items, references)}>` : + Types.TypeGuard.IsBigInt(schema) ? `bigint` : + Types.TypeGuard.IsBoolean(schema) ? `boolean` : + Types.TypeGuard.IsConstructor(schema) ? `new (${Parameters(schema.parameter, 0, references)}) => ${Visit(schema.returns, references)}` : + Types.TypeGuard.IsDate(schema) ? 'Date' : + Types.TypeGuard.IsFunction(schema) ? `(${Parameters(schema.parameters, 0, references)}) => ${Visit(schema.returns, references)}` : + Types.TypeGuard.IsInteger(schema) ? 'number' : + Types.TypeGuard.IsIntersect(schema) ? `(${Intersect(schema.allOf, references)})` : + Types.TypeGuard.IsIterator(schema) ? `IterableIterator<${Visit(schema.items, references)}>` : + Types.TypeGuard.IsLiteral(schema) ? `${Literal(schema, references)}` : + Types.TypeGuard.IsNever(schema) ? `never` : + Types.TypeGuard.IsNull(schema) ? `null` : + Types.TypeGuard.IsNot(schema) ? 'unknown' : + Types.TypeGuard.IsNumber(schema) ? 'number' : + Types.TypeGuard.IsObject(schema) ? `{ ${Properties(schema.properties, Object.getOwnPropertyNames(schema.properties), references)} }` : + Types.TypeGuard.IsPromise(schema) ? `Promise<${Visit(schema.item, references)}>` : + Types.TypeGuard.IsRecord(schema) ? `${Record(schema, references)}` : + Types.TypeGuard.IsRef(schema) ? `${Visit(Types.Type.Deref(schema, references), references)}` : + Types.TypeGuard.IsString(schema) ? 'string' : + Types.TypeGuard.IsSymbol(schema) ? 'symbol' : + Types.TypeGuard.IsTemplateLiteral(schema) ? `${TemplateLiteral(schema, references)}` : + Types.TypeGuard.IsThis(schema) ? 'unknown' : // requires named interface + Types.TypeGuard.IsTuple(schema) ? `[${Tuple(schema.items || [], references)}]` : + Types.TypeGuard.IsUint8Array(schema) ? `Uint8Array` : + Types.TypeGuard.IsUndefined(schema) ? 'undefined' : + Types.TypeGuard.IsUnion(schema) ? `${Union(schema.anyOf, references)}` : + Types.TypeGuard.IsVoid(schema) ? `void` : + 'unknown' + ) + } + /** Generates a TypeScript annotation for the given schema */ + export function Code(schema: Types.TSchema, references: Types.TSchema[] = []): string { + return Visit(schema, references) + } +} \ No newline at end of file diff --git a/example/annotation/index.ts b/example/annotation/index.ts new file mode 100644 index 000000000..34ccf2617 --- /dev/null +++ b/example/annotation/index.ts @@ -0,0 +1 @@ +export * from './annotation' \ No newline at end of file diff --git a/examples/collections/array.ts b/example/collections/array.ts similarity index 100% rename from examples/collections/array.ts rename to example/collections/array.ts diff --git a/examples/collections/index.ts b/example/collections/index.ts similarity index 100% rename from examples/collections/index.ts rename to example/collections/index.ts diff --git a/examples/collections/map.ts b/example/collections/map.ts similarity index 100% rename from examples/collections/map.ts rename to example/collections/map.ts diff --git a/examples/collections/readme.md b/example/collections/readme.md similarity index 100% rename from examples/collections/readme.md rename to example/collections/readme.md diff --git a/examples/collections/set.ts b/example/collections/set.ts similarity index 100% rename from examples/collections/set.ts rename to example/collections/set.ts diff --git a/examples/formats/date-time.ts b/example/formats/date-time.ts similarity index 100% rename from examples/formats/date-time.ts rename to example/formats/date-time.ts diff --git a/examples/formats/date.ts b/example/formats/date.ts similarity index 100% rename from examples/formats/date.ts rename to example/formats/date.ts diff --git a/examples/formats/email.ts b/example/formats/email.ts similarity index 100% rename from examples/formats/email.ts rename to example/formats/email.ts diff --git a/examples/formats/index.ts b/example/formats/index.ts similarity index 100% rename from examples/formats/index.ts rename to example/formats/index.ts diff --git a/examples/formats/ipv4.ts b/example/formats/ipv4.ts similarity index 100% rename from examples/formats/ipv4.ts rename to example/formats/ipv4.ts diff --git a/examples/formats/ipv6.ts b/example/formats/ipv6.ts similarity index 100% rename from examples/formats/ipv6.ts rename to example/formats/ipv6.ts diff --git a/examples/formats/time.ts b/example/formats/time.ts similarity index 100% rename from examples/formats/time.ts rename to example/formats/time.ts diff --git a/examples/formats/url.ts b/example/formats/url.ts similarity index 100% rename from examples/formats/url.ts rename to example/formats/url.ts diff --git a/examples/formats/uuid.ts b/example/formats/uuid.ts similarity index 100% rename from examples/formats/uuid.ts rename to example/formats/uuid.ts diff --git a/examples/index.ts b/example/index.ts similarity index 100% rename from examples/index.ts rename to example/index.ts diff --git a/examples/prototypes/evaluate.ts b/example/prototypes/evaluate.ts similarity index 82% rename from examples/prototypes/evaluate.ts rename to example/prototypes/evaluate.ts index 5fa7b2128..530972fa7 100644 --- a/examples/prototypes/evaluate.ts +++ b/example/prototypes/evaluate.ts @@ -44,7 +44,6 @@ import { TTuple, TProperties, TIntersect, - IntersectType, TUnion, TNever } from '@sinclair/typebox' @@ -75,7 +74,7 @@ export type TEvaluateArray = T extends [infer L, ...infer [] // prettier-ignore export type TEvaluate = - T extends TIntersect ? IntersectType> : + T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TConstructor ? TConstructor, TEvaluate> : T extends TFunction ? TFunction, TEvaluate> : @@ -119,16 +118,16 @@ export function EvaluateArray(rest: T) { // prettier-ignore export function Evaluate(schema: T): TEvaluate { return ( - TypeGuard.TIntersect(schema) ? Type.Intersect(EvaluateIntersectRest(schema.allOf)) : - TypeGuard.TUnion(schema) ? Type.Union(EvaluateArray(schema.anyOf)) : - TypeGuard.TAsyncIterator(schema) ? Type.AsyncIterator(Evaluate(schema.items)) : - TypeGuard.TIterator(schema) ? Type.Iterator(Evaluate(schema.items)) : - TypeGuard.TObject(schema) ? Type.Object(EvaluateProperties(schema.properties)) : - TypeGuard.TConstructor(schema) ? Type.Constructor(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : - TypeGuard.TFunction(schema) ? Type.Function(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : - TypeGuard.TTuple(schema) ? Type.Tuple(EvaluateArray(schema.items)) : - TypeGuard.TArray(schema) ? Type.Promise(Evaluate(schema.items)) : - TypeGuard.TPromise(schema) ? Type.Promise(Evaluate(schema.item)) : + TypeGuard.IsIntersect(schema) ? Type.Intersect(EvaluateIntersectRest(schema.allOf)) : + TypeGuard.IsUnion(schema) ? Type.Union(EvaluateArray(schema.anyOf)) : + TypeGuard.IsAsyncIterator(schema) ? Type.AsyncIterator(Evaluate(schema.items)) : + TypeGuard.IsIterator(schema) ? Type.Iterator(Evaluate(schema.items)) : + TypeGuard.IsObject(schema) ? Type.Object(EvaluateProperties(schema.properties)) : + TypeGuard.IsConstructor(schema) ? Type.Constructor(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : + TypeGuard.IsFunction(schema) ? Type.Function(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : + TypeGuard.IsTuple(schema) ? Type.Tuple(EvaluateArray(schema.items)) : + TypeGuard.IsArray(schema) ? Type.Promise(Evaluate(schema.items)) : + TypeGuard.IsPromise(schema) ? Type.Promise(Evaluate(schema.item)) : schema ) as TEvaluate } diff --git a/examples/prototypes/index.ts b/example/prototypes/index.ts similarity index 98% rename from examples/prototypes/index.ts rename to example/prototypes/index.ts index ef44e7d95..4a70931c8 100644 --- a/examples/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -26,7 +26,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './const' export * from './evaluate' export * from './partial-deep' export * from './union-enum' diff --git a/examples/prototypes/partial-deep.ts b/example/prototypes/partial-deep.ts similarity index 84% rename from examples/prototypes/partial-deep.ts rename to example/prototypes/partial-deep.ts index 1315b65b0..c0896186c 100644 --- a/examples/prototypes/partial-deep.ts +++ b/example/prototypes/partial-deep.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TProperties, AssertRest, AssertType, Evaluate } from '@sinclair/typebox' +import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TProperties, Evaluate } from '@sinclair/typebox' // ------------------------------------------------------------------------------------- // TDeepPartial @@ -34,9 +34,10 @@ import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TPrope export type TPartialDeepProperties = { [K in keyof T]: TPartial } -export type TPartialDeepRest = T extends [infer L, ...infer R] - ? [TPartial>, ...TPartialDeepRest>] - : [] +export type TPartialDeepRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [TPartial, ...TPartialDeepRest] + : [] export type TPartialDeep = T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : @@ -57,9 +58,9 @@ function PartialDeepRest(rest: [...T]): TPartialDeepRest /** Maps the given schema as deep partial, making all properties and sub properties optional */ export function PartialDeep(type: T): TPartialDeep { return ( - TypeGuard.TIntersect(type) ? Type.Intersect(PartialDeepRest(type.allOf)) : - TypeGuard.TUnion(type) ? Type.Union(PartialDeepRest(type.anyOf)) : - TypeGuard.TObject(type) ? Type.Partial(Type.Object(PartialDeepProperties(type.properties))) : + TypeGuard.IsIntersect(type) ? Type.Intersect(PartialDeepRest(type.allOf)) : + TypeGuard.IsUnion(type) ? Type.Union(PartialDeepRest(type.anyOf)) : + TypeGuard.IsObject(type) ? Type.Partial(Type.Object(PartialDeepProperties(type.properties))) : type ) as any } \ No newline at end of file diff --git a/examples/prototypes/readme.md b/example/prototypes/readme.md similarity index 100% rename from examples/prototypes/readme.md rename to example/prototypes/readme.md diff --git a/examples/prototypes/union-enum.ts b/example/prototypes/union-enum.ts similarity index 100% rename from examples/prototypes/union-enum.ts rename to example/prototypes/union-enum.ts diff --git a/examples/prototypes/union-oneof.ts b/example/prototypes/union-oneof.ts similarity index 100% rename from examples/prototypes/union-oneof.ts rename to example/prototypes/union-oneof.ts diff --git a/examples/typedef/index.ts b/example/typedef/index.ts similarity index 100% rename from examples/typedef/index.ts rename to example/typedef/index.ts diff --git a/examples/typedef/readme.md b/example/typedef/readme.md similarity index 100% rename from examples/typedef/readme.md rename to example/typedef/readme.md diff --git a/examples/typedef/typedef.ts b/example/typedef/typedef.ts similarity index 93% rename from examples/typedef/typedef.ts rename to example/typedef/typedef.ts index 5eb12a372..233ac5c7c 100644 --- a/examples/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -26,22 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeSystemErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/system' -import * as Types from '@sinclair/typebox' +import { SetErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/errors' +import * as Types from '@sinclair/typebox/type' // -------------------------------------------------------------------------- -// Utility Types -// -------------------------------------------------------------------------- -export type Assert = T extends U ? T : never -export type Base = { m: string, t: string } -export type Base16 = { m: 'F', t: '01', '0': '1', '1': '2', '2': '3', '3': '4', '4': '5', '5': '6', '6': '7', '7': '8', '8': '9', '9': 'A', 'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': '0' } -export type Base10 = { m: '9', t: '01', '0': '1', '1': '2', '2': '3', '3': '4', '4': '5', '5': '6', '6': '7', '7': '8', '8': '9', '9': '0' } -export type Reverse = T extends `${infer L}${infer R}` ? `${Reverse}${L}` : T -export type Tick = T extends keyof B ? B[T] : never -export type Next = T extends Assert['m'] ? Assert['t'] : T extends `${infer L}${infer R}` ? L extends Assert['m'] ? `${Assert, string>}${Next}` : `${Assert, string>}${R}` : never -export type Increment = Reverse, B>> -// -------------------------------------------------------------------------- -// SchemaOptions +// Metadata // -------------------------------------------------------------------------- export interface Metadata { [name: string]: any @@ -65,9 +54,10 @@ export interface TBoolean extends Types.TSchema { // -------------------------------------------------------------------------- // TUnion // -------------------------------------------------------------------------- -type InferUnion = T extends [infer L, ...infer R] - ? Types.Evaluate<{ [_ in D]: Index } & Types.Static>> | InferUnion, D, Increment>> - : never +export type InferUnion = + T extends [infer L extends TStruct, ...infer R extends TStruct[]] + ? Types.Evaluate<{ [_ in D]: Index } & Types.Static> | InferUnion>> + : never export interface TUnion extends Types.TSchema { [Types.Kind]: 'TypeDef:Union' @@ -170,14 +160,31 @@ export interface TString extends Types.TSchema { // -------------------------------------------------------------------------- // TStruct // -------------------------------------------------------------------------- +// used for structural type inference type OptionalKeys = { [K in keyof T]: T[K] extends (Types.TOptional) ? T[K] : never } type RequiredKeys = { [K in keyof T]: T[K] extends (Types.TOptional) ? never : T[K] } +// static inference +type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends Types.TReadonly ? (T[K] extends Types.TOptional ? K : never) : never }[keyof T] +type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends Types.TReadonly ? (T[K] extends Types.TOptional ? never : K) : never }[keyof T] +type OptionalPropertyKeys = { [K in keyof T]: T[K] extends Types.TOptional ? (T[K] extends Types.TReadonly ? never : K) : never }[keyof T] +type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> +// prettier-ignore +type StructStaticProperties> = Types.Evaluate<( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +)> +// prettier-ignore +export type StructStatic = StructStaticProperties +}> export interface StructMetadata extends Metadata { additionalProperties?: boolean } export interface TStruct extends Types.TSchema, StructMetadata { [Types.Kind]: 'TypeDef:Struct' - static: Types.PropertiesReduce + static: StructStatic optionalProperties: { [K in Types.Assert, keyof T>]: T[K] } properties: { [K in Types.Assert, keyof T>]: T[K] } } @@ -487,8 +494,8 @@ Types.TypeRegistry.Set('TypeDef:Timestamp', (schema, value) => Value // -------------------------------------------------------------------------- // TypeSystemErrorFunction // -------------------------------------------------------------------------- -TypeSystemErrorFunction.Set((schema, type) => { - switch(schema[Types.Kind]) { +SetErrorFunction((error) => { + switch(error.schema[Types.Kind]) { case 'TypeDef:Array': return 'Expected Array' case 'TypeDef:Boolean': return 'Expected Boolean' case 'TypeDef:Union': return 'Expected Union' @@ -503,7 +510,7 @@ TypeSystemErrorFunction.Set((schema, type) => { case 'TypeDef:Struct': return 'Expected Struct' case 'TypeDef:Timestamp': return 'Expected Timestamp' } - return DefaultErrorFunction(schema, type) + return DefaultErrorFunction(error) }) // -------------------------------------------------------------------------- // TypeDefBuilder @@ -588,8 +595,8 @@ export class TypeDefBuilder { } /** [Standard] Creates a Struct type */ public Struct(fields: T, metadata: StructMetadata = {}): TStruct { - const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) - const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.TOptional(fields[key]) ? { ...acc } : { ...acc, [key]: fields[key] }), {} as TFields) + const optionalProperties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.IsOptional(fields[key]) ? { ...acc, [key]: fields[key] } : { ...acc }), {} as TFields) + const properties = globalThis.Object.getOwnPropertyNames(fields).reduce((acc, key) => (Types.TypeGuard.IsOptional(fields[key]) ? { ...acc } : { ...acc, [key]: fields[key] }), {} as TFields) const optionalObject = globalThis.Object.getOwnPropertyNames(optionalProperties).length > 0 ? { optionalProperties: optionalProperties } : {} const requiredObject = globalThis.Object.getOwnPropertyNames(properties).length === 0 ? {} : { properties: properties } return this.Create({ [Types.Kind]: 'TypeDef:Struct', ...requiredObject, ...optionalObject }, metadata) diff --git a/examples/trpc/readme.md b/examples/trpc/readme.md deleted file mode 100644 index c5c7051e8..000000000 --- a/examples/trpc/readme.md +++ /dev/null @@ -1,46 +0,0 @@ -# Using TypeBox with TRPC - -To use TypeBox with TRPC, you will need to wrap types in a TRPC compatible validation function. The following shows wrapping a type using the TypeCompiler. - - - -## Example - -[TypeScript Link Here](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzsAdsGAVASgBQMIA0c2+AolFNHAL5wBmlIcA5AAIxRgDGA9AM4BTKADchzALAAoUJFiJiATzAC8EcMAA2QmvUYtWfVFw0BDYFB4wlAgEYQAHjy5qwmsVJnR4SDNaIYAZS4ACwEQEx0GNX1DFGMzCytlO3sJSSkeHjgAWly8-ILCouKS0rLyvIys1XUtPjgI32UGlAATOCgBGABXKBR6iOMIPl6BeioSPCqcitm5+YWCqQF7WXg6briYYAgUOCxuJoEAHgw4FZgBNvrAkLCTAD4ACj478IAuYiJOuiErrgEfE+t1C4QA2gBdOAAXjgkIAlIgpHA4M5+vA7lwANYwxTKGquLRQAB0BLcLzeJm+Al+nTigPhyI6XV6eyewhMGm6Ak+myxKAgAHcUIjoQ8kZIUSjgHQ4E9MVjSaFsezOdz4YjOj0+nAOVyBEyUWi+N44GATDBgkQQIC+CYAOZjWiwhXE8iUKB8VX6+HEgBi5hNT3hAEJDXBLZRBXAUAJo5N3dAnkgbXw7Y7PgADAAkCFT6YEtDoVFz5st1EzRGcrR5LAAQgBBAAiAH0sKQAIoAVVIAQwzBojMlNCk1Gmiwnk6nlUkmTgXYL4+ny5XZSkxvg8FhqHQk2JXE6FoEwfXuxNDVa7VhMGJYEoANaoyZxNQYG6MCeBy4Rye4aOxIAeRsAArAQuA-BBwxRExgWsYkADluhAGwhGDAgoLgGxYOUBCkJQqA0PDaghxRDVnwgd83w-L8fz-ODEOQ1CSNI5jiQAR25KAFCeZNkBQKjBxhcVX3fYkIgAaj4qjiRsRE5ySARsjtX4pGWVYvFRM94BMMAwCwCjLigXEb0od9UKQJkTEvOBR3hIA) - -```typescript -import { initTRPC, TRPCError } from '@trpc/server' -import { TypeCompiler } from '@sinclair/typebox/compiler' -import { Type, TSchema } from '@sinclair/typebox' - -// --------------------------------------------------------------------------------- -// Compiles a Type and returns a closure for TRPC -// --------------------------------------------------------------------------------- -export function RpcType(schema: T, references: TSchema[] = []) { - const check = TypeCompiler.Compile(schema, references) - return (value: unknown) => { - if (check.Check(value)) return value - const { path, message } = check.Errors(value).First()! - throw new TRPCError({ message: `${message} for ${path}`, code: 'BAD_REQUEST' }) - } -} -// --------------------------------------------------------------------------------- -// Usage -// --------------------------------------------------------------------------------- -const t = initTRPC.create() -const add = t.procedure - .input(RpcType( - Type.Object({ - a: Type.Number(), - b: Type.Number(), - }) - )) - .output(RpcType( - Type.Number() - )) - .query(({ input }) => input.a + input.b) // type-safe - -export const appRouter = t.router({ - add -}) -``` \ No newline at end of file diff --git a/hammer.mjs b/hammer.mjs index 2e02a034e..661d90a28 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -1,36 +1,32 @@ -import { compression, measurement } from './benchmark' -import { readFileSync } from 'fs' +import * as Benchmark from './task/benchmark' +import * as Build from './task/build' +import * as Fs from 'fs' // ------------------------------------------------------------------------------- // Clean // ------------------------------------------------------------------------------- export async function clean() { + await folder('node_modules/typebox').delete() await folder('target').delete() } // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { - await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test examples/index.ts benchmark') + await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test task example/index.ts') } // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- export async function start() { - await shell(`hammer run examples/index.ts --dist target/examples`) + await shell(`hammer run example/index.ts --dist target/example`) } // ------------------------------------------------------------------------------- // Benchmark // ------------------------------------------------------------------------------- -export async function benchmark_compression() { - await compression() -} -export async function benchmark_measurement() { - await measurement() -} export async function benchmark() { - await benchmark_compression() - await benchmark_measurement() + await Benchmark.compression() + await Benchmark.measurement() } // ------------------------------------------------------------------------------- // Test @@ -56,20 +52,36 @@ export async function test(filter = '') { // ------------------------------------------------------------------------------- // Build // ------------------------------------------------------------------------------- +export async function build_check(target = 'target/build') { + const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) + await shell(`cd ${target} && attw sinclair-typebox-${version}.tgz`) +} export async function build(target = 'target/build') { await test() - await folder(target).delete() - await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) - await folder(target).add('package.json') + await clean() + await Promise.all([ + Build.Import.build(target), + Build.Require.build(target), + Build.Redirect.build(target) + ]) await folder(target).add('readme.md') await folder(target).add('license') await shell(`cd ${target} && npm pack`) + await build_check(target) +} +// ------------------------------------------------------------------------------- +// Install +// ------------------------------------------------------------------------------- +export async function install_local() { + await clean() + await build('target/typebox') + await folder('node_modules').add('target/typebox') } // ------------------------------------------------------------- // Publish // ------------------------------------------------------------- export async function publish(otp, target = 'target/build') { - const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) if(version.includes('-dev')) throw Error(`package version should not include -dev specifier`) await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp}`) await shell(`git tag ${version}`) @@ -79,7 +91,7 @@ export async function publish(otp, target = 'target/build') { // Publish-Dev // ------------------------------------------------------------- export async function publish_dev(otp, target = 'target/build') { - const { version } = JSON.parse(readFileSync(`${target}/package.json`, 'utf8')) + const { version } = JSON.parse(Fs.readFileSync(`${target}/package.json`, 'utf8')) if(!version.includes('-dev')) throw Error(`development package version should include -dev specifier`) await shell(`cd ${target} && npm publish sinclair-typebox-${version}.tgz --access=public --otp ${otp} --tag dev`) } \ No newline at end of file diff --git a/license b/license index 08641fd64..971ec5da8 100644 --- a/license +++ b/license @@ -1,4 +1,6 @@ -TypeBox: JSON Schema Type Builder with Static Type Resolution for TypeScript +TypeBox + +Json Schema Type Builder with Static Type Resolution for TypeScript The MIT License (MIT) diff --git a/package-lock.json b/package-lock.json index 88740ec7e..6ce4d3fae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "@sinclair/typebox", - "version": "0.31.28", + "version": "0.32.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.28", + "version": "0.32.0", "license": "MIT", "devDependencies": { + "@arethetypeswrong/cli": "^0.13.2", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", - "@types/node": "^18.11.9", + "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "mocha": "^9.2.2", @@ -19,6 +20,59 @@ "typescript": "^5.3.2" } }, + "node_modules/@andrewbranch/untar.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", + "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==", + "dev": true + }, + "node_modules/@arethetypeswrong/cli": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.2.tgz", + "integrity": "sha512-eqRWeFFiI58xwsiUfZSdZsmNCaqqtxmSPP9554ajiCDrB/aNzq5VktVK7dNiT9PamunNeoej4KbDBnkNwVacvg==", + "dev": true, + "dependencies": { + "@arethetypeswrong/core": "0.13.2", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^6.0.0", + "semver": "^7.5.4" + }, + "bin": { + "attw": "dist/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@arethetypeswrong/core": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.2.tgz", + "integrity": "sha512-1l6ygar+6TH4o1JipWWGCEZlOhAwEShm1yKx+CgIByNjCzufbu6k9DNbDmBjdouusNRhBIOYQe1UHnJig+GtAw==", + "dev": true, + "dependencies": { + "@andrewbranch/untar.js": "^1.0.3", + "fflate": "^0.7.4", + "semver": "^7.5.4", + "typescript": "5.3.2", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/@esbuild/linux-loong64": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", @@ -47,6 +101,18 @@ "hammer": "hammer" } }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -54,10 +120,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.17.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.2.tgz", - "integrity": "sha512-wBo3KqP/PBqje5TI9UTiuL3yWfP6sdPtjtygSOqcYZWT232dfDeDOnkDps5wqZBP9NgGgYrNejinl0faAuE+HQ==", - "dev": true + "version": "20.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", + "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", @@ -107,6 +176,21 @@ "node": ">=6" } }, + "node_modules/ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, + "dependencies": { + "type-fest": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -131,6 +215,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -193,6 +283,15 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -205,6 +304,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -233,6 +345,15 @@ "node": ">=8" } }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -260,6 +381,21 @@ "fsevents": "~2.3.2" } }, + "node_modules/cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -289,6 +425,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -345,6 +490,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, "node_modules/esbuild": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz", @@ -722,12 +873,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1008,6 +1178,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/marked-terminal": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", + "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cardinal": "^2.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "node-emoji": "^2.1.0", + "supports-hyperlinks": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <11" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", @@ -1081,6 +1307,21 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1204,6 +1445,15 @@ "node": ">=8.10.0" } }, + "node_modules/redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "dependencies": { + "esprima": "~4.0.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -1242,6 +1492,21 @@ } ] }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -1251,6 +1516,18 @@ "randombytes": "^2.1.0" } }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1304,6 +1581,31 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1316,6 +1618,18 @@ "node": ">=8.0" } }, + "node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", @@ -1329,6 +1643,21 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1338,6 +1667,18 @@ "punycode": "^2.1.0" } }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -1391,6 +1732,12 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -1447,6 +1794,47 @@ } }, "dependencies": { + "@andrewbranch/untar.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", + "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==", + "dev": true + }, + "@arethetypeswrong/cli": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/cli/-/cli-0.13.2.tgz", + "integrity": "sha512-eqRWeFFiI58xwsiUfZSdZsmNCaqqtxmSPP9554ajiCDrB/aNzq5VktVK7dNiT9PamunNeoej4KbDBnkNwVacvg==", + "dev": true, + "requires": { + "@arethetypeswrong/core": "0.13.2", + "chalk": "^4.1.2", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "marked": "^9.1.2", + "marked-terminal": "^6.0.0", + "semver": "^7.5.4" + } + }, + "@arethetypeswrong/core": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@arethetypeswrong/core/-/core-0.13.2.tgz", + "integrity": "sha512-1l6ygar+6TH4o1JipWWGCEZlOhAwEShm1yKx+CgIByNjCzufbu6k9DNbDmBjdouusNRhBIOYQe1UHnJig+GtAw==", + "dev": true, + "requires": { + "@andrewbranch/untar.js": "^1.0.3", + "fflate": "^0.7.4", + "semver": "^7.5.4", + "typescript": "5.3.2", + "validate-npm-package-name": "^5.0.0" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true + }, "@esbuild/linux-loong64": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz", @@ -1463,6 +1851,12 @@ "esbuild": "0.15.7" } }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, "@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -1470,10 +1864,13 @@ "dev": true }, "@types/node": { - "version": "18.17.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.2.tgz", - "integrity": "sha512-wBo3KqP/PBqje5TI9UTiuL3yWfP6sdPtjtygSOqcYZWT232dfDeDOnkDps5wqZBP9NgGgYrNejinl0faAuE+HQ==", - "dev": true + "version": "20.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", + "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@ungap/promise-all-settled": { "version": "1.1.2", @@ -1508,6 +1905,15 @@ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, + "ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, + "requires": { + "type-fest": "^3.0.0" + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1523,6 +1929,12 @@ "color-convert": "^2.0.1" } }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", + "dev": true + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1576,12 +1988,31 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, + "cardinal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", + "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1603,6 +2034,12 @@ } } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1619,6 +2056,16 @@ "readdirp": "~3.6.0" } }, + "cli-table3": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", + "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -1645,6 +2092,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1686,6 +2139,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, "esbuild": { "version": "0.15.7", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz", @@ -1867,12 +2326,24 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fflate": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz", + "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==", + "dev": true + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2073,6 +2544,43 @@ "is-unicode-supported": "^0.1.0" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "marked": { + "version": "9.1.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", + "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", + "dev": true + }, + "marked-terminal": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.1.0.tgz", + "integrity": "sha512-QaCSF6NV82oo6K0szEnmc65ooDeW0T/Adcyf0fcW+Hto2GT1VADFg8dn1zaeHqzj65fqDH1hMNChGNRaC/lbkA==", + "dev": true, + "requires": { + "ansi-escapes": "^6.2.0", + "cardinal": "^2.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "node-emoji": "^2.1.0", + "supports-hyperlinks": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true + } + } + }, "minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", @@ -2126,6 +2634,18 @@ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, + "node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2207,6 +2727,15 @@ "picomatch": "^2.2.1" } }, + "redeyed": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", + "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2225,6 +2754,15 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -2234,6 +2772,15 @@ "randombytes": "^2.1.0" } }, + "skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "requires": { + "unicode-emoji-modifier-base": "^1.0.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2269,6 +2816,27 @@ "has-flag": "^4.0.0" } }, + "supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2278,12 +2846,30 @@ "is-number": "^7.0.0" } }, + "type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true + }, "typescript": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2293,6 +2879,15 @@ "punycode": "^2.1.0" } }, + "validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2331,6 +2926,12 @@ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 064dc20a4..342d3bc79 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@sinclair/typebox", - "version": "0.31.28", - "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", + "version": "0.32.0", + "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", "json-schema", @@ -10,28 +10,6 @@ ], "author": "sinclairzx81", "license": "MIT", - "main": "./typebox.js", - "types": "./typebox.d.ts", - "exports": { - "./compiler": "./compiler/index.js", - "./errors": "./errors/index.js", - "./system": "./system/index.js", - "./value/cast": "./value/cast.js", - "./value/check": "./value/check.js", - "./value/clone": "./value/clone.js", - "./value/convert": "./value/convert.js", - "./value/create": "./value/create.js", - "./value/delta": "./value/delta.js", - "./value/deref": "./value/deref.js", - "./value/equal": "./value/equal.js", - "./value/guard": "./value/guard.js", - "./value/hash": "./value/hash.js", - "./value/mutate": "./value/mutate.js", - "./value/pointer": "./value/pointer.js", - "./value/transform": "./value/transform.js", - "./value": "./value/index.js", - ".": "./typebox.js" - }, "repository": { "type": "git", "url": "https://github.com/sinclairzx81/typebox" @@ -40,21 +18,21 @@ "test:typescript": "hammer task test_typescript", "test:static": "hammer task test_static", "test:runtime": "hammer task test_runtime", + "install:local": "hammer task install_local", + "benchmark": "hammer task benchmark", + "build": "hammer task build", "test": "hammer task test", "clean": "hammer task clean", "format": "hammer task format", "start": "hammer task start", - "benchmark:compression": "hammer task benchmark_compression", - "benchmark:measurement": "hammer task benchmark_measurement", - "benchmark": "hammer task benchmark", - "build": "hammer task build", "publish": "hammer task publish", "publish:dev": "hammer task publish_dev" }, "devDependencies": { + "@arethetypeswrong/cli": "^0.13.2", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", - "@types/node": "^18.11.9", + "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "mocha": "^9.2.2", diff --git a/readme.md b/readme.md index 8ba350169..d6671201b 100644 --- a/readme.md +++ b/readme.md @@ -20,21 +20,14 @@ ## Install -#### Npm ```bash $ npm install @sinclair/typebox --save ``` -#### Esm + Deno - -```typescript -import { Type, Static } from 'https://esm.sh/@sinclair/typebox' -``` - ## Example ```typescript -import { Type, Static } from '@sinclair/typebox' +import { Type, type Static } from '@sinclair/typebox' const T = Type.Object({ // const T = { x: Type.Number(), // type: 'object', @@ -58,9 +51,9 @@ type T = Static // type T = { ## Overview -TypeBox is a runtime type builder 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 assertion rules of the TypeScript compiler. TypeBox enables one to create a unified type that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation. +TypeBox is a runtime type builder that creates in-memory Json Schema objects that infer as TypeScript types. The schematics produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime asserted using standard Json Schema validation. -This library is designed to enable JSON schema to compose with the same flexibility as TypeScript's type system. It can be used as a simple tool to build up complex schemas or integrated into REST or RPC services to help validate data received over the wire. +This library is built to be a runtime type system offering similar capabilities to TypeScript's static type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. License MIT @@ -71,17 +64,19 @@ License MIT - [Types](#types) - [Json](#types-json) - [JavaScript](#types-javascript) + - [Import](#types-import) - [Options](#types-options) - [Properties](#types-properties) - [Generics](#types-generics) - [References](#types-references) - [Recursive](#types-recursive) - - [Conditional](#types-conditional) - - [Template Literal](#types-templateliteral) + - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) - - [Rest](#types-rest) - - [Transform](#types-transform) + - [Mapped](#types-mapped) + - [Conditional](#types-conditional) - [Intrinsic](#types-intrinsic) + - [Transform](#types-transform) + - [Rest](#types-rest) - [Guard](#types-guard) - [Unsafe](#types-unsafe) - [Strict](#types-strict) @@ -90,6 +85,8 @@ License MIT - [Clone](#values-clone) - [Check](#values-check) - [Convert](#values-convert) + - [Default](#values-default) + - [Clean](#values-clean) - [Cast](#values-cast) - [Decode](#values-decode) - [Encode](#values-decode) @@ -107,10 +104,8 @@ License MIT - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) - [TypeSystem](#typesystem) - - [Types](#typesystem-types) - - [Formats](#typesystem-formats) - - [Errors](#typesystem-errors) - [Policies](#typesystem-policies) +- [Error Function](#error-function) - [Workbench](#workbench) - [Codegen](#codegen) - [Ecosystem](#ecosystem) @@ -127,7 +122,7 @@ License MIT The following shows general usage. ```typescript -import { Type, Static } from '@sinclair/typebox' +import { Type, type Static } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- // @@ -297,6 +292,22 @@ The following table lists the supported Json types. These types are fully compat │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Const({ │ type T = { │ const T = { │ +│ x: 1, │ readonly x: 1, │ type: 'object', │ +│ y: 2, │ readonly y: 2 │ required: ['x', 'y'], │ +│ } as const) │ } │ properties: { │ +│ │ │ x: { │ +│ │ │ type: 'number', │ +│ │ │ const: 1 │ +│ │ │ }, │ +│ │ │ y: { │ +│ │ │ type: 'number', │ +│ │ │ const: 2 │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.KeyOf( │ type T = keyof { │ const T = { │ │ Type.Object({ │ x: number, │ anyOf: [{ │ │ x: Type.Number(), │ y: number │ type: 'string', │ @@ -365,8 +376,8 @@ The following table lists the supported Json types. These types are fully compat ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const T = Type.Extends( │ type T = │ const T = { │ │ Type.String(), │ string extends number │ const: false, │ -│ Type.Number(), │ true : false │ type: 'boolean' │ -│ Type.Literal(true), │ │ } │ +│ Type.Number(), │ ? true │ type: 'boolean' │ +│ Type.Literal(true), │ : false │ } │ │ Type.Literal(false) │ │ │ │ ) │ │ │ │ │ │ │ @@ -389,6 +400,20 @@ The following table lists the supported Json types. These types are fully compat │ ) │ │ │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ +│ const T = Type.Mapped( │ type T = { │ const T = { │ +│ Type.Union([ │ [_ in 'x' | 'y'] : number │ type: 'object', │ +│ Type.Literal('x'), │ } │ required: ['x', 'y'], │ +│ Type.Literal('y') │ │ properties: { │ +│ ]), │ │ x: { │ +│ () => Type.Number() │ │ type: 'number' │ +│ ) │ │ }, │ +│ │ │ y: { │ +│ │ │ type: 'number' │ +│ │ │ } │ +│ │ │ } │ +│ │ │ } │ +│ │ │ │ +├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ │ const U = Type.Union([ │ type U = 'open' | 'close' │ const T = { │ │ Type.Literal('open'), │ │ type: 'string', │ │ Type.Literal('close') │ type T = `on${U}` │ pattern: '^on(open|close)$' │ @@ -580,9 +605,10 @@ TypeBox provides an extended type set that can be used to create schematics for │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.RegExp(/abc/) │ type T = string │ const T = { │ -│ │ │ type: 'string' │ -│ │ │ pattern: 'abc' │ +│ const T = Type.RegExp(/abc/i) │ type T = string │ const T = { │ +│ │ │ type: 'RegExp' │ +│ │ │ source: 'abc' │ +│ │ │ flags: 'i' │ │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ @@ -618,11 +644,27 @@ TypeBox provides an extended type set that can be used to create schematics for └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` + + +### Import + +Import the Type namespace to bring in TypeBox's full type system. This is recommended for most users. + +```typescript +import { Type, type Static } from '@sinclair/typebox' +``` + +You can also selectively import types. This enables modern bundlers to tree shake for unused types. + +```typescript +import { Object, Number, String, Boolean, type Static } from '@sinclair/typebox' +``` + ### Options -You can pass Json Schema options on the last argument of any type. Option hints specific to each type are provided for convenience. +You can pass Json Schema options on the last argument of any given type. Option hints specific to each type are provided for convenience. ```typescript // String must be an email @@ -690,35 +732,27 @@ Object properties can be modified with Readonly and Optional. The following tabl │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` + ### Generic Types -Generic types can be created with generic functions. All types extend the base type TSchema. It is common to constrain generic function arguments to this type. The following creates a generic Vector type. +Generic types can be created with functions. TypeBox types extend the TSchema interface so you should constrain parameters to this type. The following creates a generic Vector type. ```typescript -import { Type, Static, TSchema } from '@sinclair/typebox' +import { Type, type Static, type TSchema } from '@sinclair/typebox' -const Vector = (t: T) => Type.Object({ x: t, y: t, z: t }) +const Vector = (T: T) => + Type.Object({ // type Vector = { + x: T, // x: T, + y: T, // y: T, + z: T // z: T + }) // } -const NumberVector = Vector(Type.Number()) // const NumberVector = { - // type: 'object', - // required: ['x', 'y', 'z'], - // properties: { - // x: { type: 'number' }, - // y: { type: 'number' }, - // z: { type: 'number' } - // } - // } - -type NumberVector = Static // type NumberVector = { - // x: number, - // y: number, - // z: number - // } +const NumberVector = Vector(Type.Number()) // type NumberVector = Vector ``` -Generic types are often used to create aliases for more complex types. The following creates a Nullable generic type. +Generic types are often used to create aliases for complex types. The following creates a Nullable generic type. ```typescript const Nullable = (schema: T) => Type.Union([schema, Type.Null()]) @@ -737,26 +771,69 @@ type T = Static // type T = string | null ### Reference Types -Reference types are supported with Ref. +Reference types can be created with Ref. These types infer the same as the target type but only store a named `$ref` to the target type. ```typescript -const T = Type.String({ $id: 'T' }) // const T = { - // $id: 'T', - // type: 'string' - // } +const Vector = Type.Object({ // const Vector = { + x: Type.Number(), // type: 'object', + y: Type.Number(), // required: ['x', 'y', 'z'], +}, { $id: 'Vector' }) // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // }, + // $id: 'Vector' + // } -const R = Type.Ref('T') // const R = { - // $ref: 'T' +const VectorRef = Type.Ref(Vector) // const VectorRef = { + // $ref: 'Vector' // } -type R = Static // type R = string +type VectorRef = Static // type VectorRef = { + // x: number, + // y: number + // } +``` +Use Deref to dereference a type. This function will replace any interior reference with the target type. +```typescript +const Vertex = Type.Object({ // const Vertex = { + position: VectorRef, // type: 'object', + texcoord: VectorRef, // required: ['position', 'texcoord'], +}) // properties: { + // position: { $ref: 'Vector' }, + // texcoord: { $ref: 'Vector' } + // } + // } + +const VertexDeref = Type.Deref(Vertex, [Vector]) // const VertexDeref = { + // type: 'object', + // required: ['position', 'texcoord'], + // properties: { + // position: { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // } + // }, + // texcoord: { + // type: 'object', + // required: ['x', 'y', 'z'], + // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // } + // } + // } + // } ``` +Note that Ref types do not store structural information about the type they're referencing. Because of this, these types cannot be used with some mapping types (such as Partial or Pick). For applications that require mapping on Ref, use Deref to normalize the type first. ### Recursive Types -TypeBox supports singular recursive data structures. Recursive type inference is also supported. The following creates a recursive Node data structure. +TypeBox supports recursive data structures with Recursive. This type wraps an interior type and provides it a `this` context that allows the type to reference itself. The following creates a recursive type. Singular recursive inference is also supported. ```typescript const Node = Type.Recursive(This => Type.Object({ // const Node = { @@ -789,175 +866,187 @@ function test(node: Node) { } ``` - + -### Conditional Types +### Template Literal Types -TypeBox supports runtime conditional types with Extends. This type will perform a structural assignability check for the first two arguments and returns one of the second two arguments based on the result. The Extends type is modelled after TypeScript conditional types. The Exclude and Extract conditional types are also supported. +TypeBox supports template literal types with TemplateLiteral. This type can be created using a syntax similar to the TypeScript template literal syntax or composed from exterior types. TypeBox encodes template literals as regular expressions which enables the template to be checked by Json Schema validators. This type also supports regular expression parsing that enables template patterns to be used for generative types. The following shows both TypeScript and TypeBox usage. ```typescript // TypeScript -type T0 = string extends number ? true : false // type T0 = false - -type T1 = Extract<(1 | 2 | 3), 1> // type T1 = 1 +type K = `prop${'A'|'B'|'C'}` // type T = 'propA' | 'propB' | 'propC' -type T2 = Exclude<(1 | 2 | 3), 1> // type T2 = 2 | 3 +type R = Record // type R = { + // propA: string + // propB: string + // propC: string + // } // TypeBox -const T0 = Type.Extends( // const T0: TLiteral = { - Type.String(), // type: 'boolean', - Type.Number(), // const: false - Type.Literal(true), // } - Type.Literal(false) -) - -const T1 = Type.Extract( // const T1: TLiteral<1> = { - Type.Union([ // type: 'number', - Type.Literal(1), // const: 1 - Type.Literal(2), // } - Type.Literal(3) - ]), - Type.Literal(1) -) +const K = Type.TemplateLiteral('prop${A|B|C}') // const K: TTemplateLiteral<[ + // TLiteral<'prop'>, + // TUnion<[ + // TLiteral<'A'>, + // TLiteral<'B'>, + // TLiteral<'C'>, + // ]> + // ]> -const T2 = Type.Exclude( // const T2: TUnion<[ - Type.Union([ // TLiteral<2>, - Type.Literal(1), // TLiteral<3> - Type.Literal(2), // ]> = { - Type.Literal(3) // anyOf: [{ - ]), // type: 'number', - Type.Literal(1) // const: 2 -) // }, { - // type: 'number', - // const: 3 - // }] - // } +const R = Type.Record(K, Type.String()) // const R: TObject<{ + // hello1: TString, + // hello2: TString, + // hello3: TString, + // }> ``` - + -### Template Literal Types +### Indexed Access Types -TypeBox supports template literal types with TemplateLiteral. This type can be created using a string syntax that is similar to the TypeScript template literal syntax. This type can also be constructed by passing an array of Union and Literal types in sequence. The following example shows the string syntax. +TypeBox supports indexed access types with Index. This type enables uniform access to interior property and element types without having to extract them from the underlying schema representation. This type is supported for Object, Array, Tuple, Union and Intersect types. ```typescript -// TypeScript +const T = Type.Object({ // type T = { + x: Type.Number(), // x: number, + y: Type.String(), // y: string, + z: Type.Boolean() // z: boolean +}) // } + +const A = Type.Index(T, ['x']) // type A = T['x'] + // + // ... evaluated as + // + // const A: TNumber + +const B = Type.Index(T, ['x', 'y']) // type B = T['x' | 'y'] + // + // ... evaluated as + // + // const B: TUnion<[ + // TNumber, + // TString, + // ]> -type T = `option${'A'|'B'|'C'}` // type T = 'optionA' | 'optionB' | 'optionC' +const C = Type.Index(T, Type.KeyOf(T)) // type C = T[keyof T] + // + // ... evaluated as + // + // const C: TUnion<[ + // TNumber, + // TString, + // TBoolean + // ]> +``` -type R = Record // type R = { - // optionA: string - // optionB: string - // optionC: string - // } + -// TypeBox +### Mapped Types -const T = Type.TemplateLiteral('option${A|B|C}') // const T = { - // pattern: '^option(A|B|C)$', - // type: 'string' - // } +TypeBox supports mapped object types with Mapped. This type accepts two arguments, the first is a union type typically derived from KeyOf, the second is a mapping function that receives a mapping key `K` that can be used to index properties of a type. The following implements Partial using mapped types. -const R = Type.Record(T, Type.String()) // const R = { - // type: 'object', - // required: ['optionA', 'optionB'], - // properties: { - // optionA: { - // type: 'string' - // }, - // optionB: { - // type: 'string' - // } - // optionC: { - // type: 'string' - // } - // } - // } +```typescript +const T = Type.Object({ // type T = { + x: Type.Number(), // x: number, + y: Type.String(), // y: string, + z: Type.Boolean() // z: boolean +}) // } + +const M = Type.Mapped(Type.KeyOf(T), K => { // type M = { [K in keyof T]?: T[K] } + return Type.Optional(Type.Index(T, K)) // +}) // ... evaluated as + // + // const M: TObject<{ + // x: TOptional, + // y: TOptional, + // z: TOptional + // }> ``` - + -### Indexed Access Types +### Conditional Types -TypeBox supports Indexed Access Types with Index. This type enables uniform access to interior property and array element types without having to extract them from the underlying schema representation. This type is supported for Object, Array, Tuple, Union and Intersect types. +TypeBox supports runtime conditional types with Extends. This type performs a structural assignability check against the first (`left`) and second (`right`) arguments and will return either the third (`true`) or fourth (`false`) argument based on the result. The conditional types Exclude and Extract are also supported. The following shows both TypeScript and TypeBox examples of conditional types. ```typescript -const T = Type.Object({ // const T = { - x: Type.Number(), // type: 'object', - y: Type.String(), // required: ['x', 'y', 'z'], - z: Type.Boolean() // properties: { -}) // x: { type: 'number' }, - // y: { type: 'string' }, - // z: { type: 'string' } - // } - // } - -const A = Type.Index(T, ['x']) // const A = { type: 'number' } +// Extends +const A = Type.Extends( // type A = string extends number ? 1 : 2 + Type.String(), // + Type.Number(), // ... evaluated as + Type.Literal(1), // + Type.Literal(2) // const A: TLiteral<2> +) -const B = Type.Index(T, ['x', 'y']) // const B = { - // anyOf: [ - // { type: 'number' }, - // { type: 'string' } - // ] - // } +// Extract +const B = Type.Extract( // type B = Extract<1 | 2 | 3, 1> + Type.Union([ // + Type.Literal(1), // ... evaluated as + Type.Literal(2), // + Type.Literal(3) // const B: TLiteral<1> + ]), + Type.Literal(1) +) -const C = Type.Index(T, Type.KeyOf(T)) // const C = { - // anyOf: [ - // { type: 'number' }, - // { type: 'string' }, - // { type: 'boolean' } - // ] - // } +// Exclude +const C = Type.Exclude( // type C = Exclude<1 | 2 | 3, 1> + Type.Union([ // + Type.Literal(1), // ... evaluated as + Type.Literal(2), // + Type.Literal(3) // const C: TUnion<[ + ]), // TLiteral<2>, + Type.Literal(1) // TLiteral<3>, +) // ]> ``` - + -### Rest Types +### Intrinsic Types -TypeBox provides the Rest type to uniformly extract variadic tuples from Intersect, Union and Tuple types. This type can be useful to remap variadic types into different forms. The following uses Rest to remap a Tuple into a Union. +TypeBox supports the TypeScript intrinsic string manipulation types Uppercase, Lowercase, Capitalize and Uncapitalize. These types can be used to remap Literal, Template Literal and Union of Literal types. ```typescript -const T = Type.Tuple([ // const T = { - Type.String(), // type: 'array', - Type.Number() // items: [ -]) // { type: 'string' }, - // { type: 'number' } - // ], - // additionalItems: false, - // minItems: 2, - // maxItems: 2, - // } +// TypeScript +type A = Capitalize<'hello'> // type A = 'Hello' -const R = Type.Rest(T) // const R = [ - // { type: 'string' }, - // { type: 'number' } - // ] +type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' -const U = Type.Union(R) // const U = { - // anyOf: [ - // { type: 'string' }, - // { type: 'number' } - // ] - // } +type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' + +// TypeBox +const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> + +const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ + Type.Literal('hello'), // TLiteral<'Hello'>, + Type.Literal('world') // TLiteral<'World'> +])) // ]> + +const C = Type.Capitalize( // const C: TTemplateLiteral<[ + Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, +) // TUnion<[ + // TLiteral<'1'>, + // TLiteral<'2'>, + // TLiteral<'3'> + // ]> + // ]> ``` ### Transform Types -TypeBox supports value decoding and encoding with Transform types. These types work in tandem with the Encode and Decode functions available on the Value and TypeCompiler modules. Transform types can be used to convert Json encoded values into constructs more natural to JavaScript. The following creates a Transform type to decode numbers into Dates using the Value module. +TypeBox supports value decoding and encoding with Transform types. These types work in tandem with the Encode and Decode functions available on the Value and TypeCompiler submodules. Transform types can be used to convert Json encoded values into constructs more natural to JavaScript. The following creates a Transform type to decode numbers into Dates using the Value submodule. ```typescript import { Value } from '@sinclair/typebox/value' const T = Type.Transform(Type.Number()) - .Decode(value => new Date(value)) // required: number to Date - .Encode(value => value.getTime()) // required: Date to number + .Decode(value => new Date(value)) // decode: number to Date + .Encode(value => value.getTime()) // encode: Date to number -const decoded = Value.Decode(T, 0) // const decoded = Date(1970-01-01T00:00:00.000Z) -const encoded = Value.Encode(T, decoded) // const encoded = 0 +const D = Value.Decode(T, 0) // const D = Date(1970-01-01T00:00:00.000Z) +const E = Value.Encode(T, D) // const E = 0 ``` Use the StaticEncode or StaticDecode types to infer a Transform type. ```typescript @@ -972,35 +1061,23 @@ type E = StaticEncode // type E = Array type T = Static // type T = Array ``` - + -### Intrinsic Types +### Rest Types -TypeBox supports the TypeScript Intrinsic String Manipulation types Uppercase, Lowercase, Capitalize and Uncapitalize. These types can be used to remap String Literal, TemplateLiteral and Union types. +TypeBox provides the Rest type to uniformly extract variadic tuples from Intersect, Union and Tuple types. This type can be useful to remap variadic types into different forms. The following uses Rest to remap a Tuple into a Union. ```typescript -// TypeScript - -type A = Capitalize<'hello'> // type A = 'Hello' -type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' -type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' - -// TypeBox - -const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> +const T = Type.Tuple([ // const T: TTuple<[ + Type.String(), // TString, + Type.Number() // TNumber +]) // ]> -const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ - Type.Literal('hello'), // TLiteral<'Hello'>, - Type.Literal('world') // TLiteral<'World'> -])) // ]> +const R = Type.Rest(T) // const R: [TString, TNumber] -const C = Type.Capitalize( // const C: TTemplateLiteral<[ - Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, -) // TUnion<[ - // TLiteral<'1'>, - // TLiteral<'2'>, - // TLiteral<'3'> - // ]> +const U = Type.Union(R) // const T: TUnion<[ + // TString, + // TNumber // ]> ``` @@ -1011,13 +1088,11 @@ const C = Type.Capitalize( // const C: TTemplateLitera TypeBox supports user defined types with Unsafe. This type allows you to specify both schema representation and inference type. The following creates an Unsafe type with a number schema that infers as string. ```typescript -const T = Type.Unsafe({ type: 'number' }) // const T = { - // type: 'number' - // } +const T = Type.Unsafe({ type: 'number' }) // const T = { type: 'number' } type T = Static // type T = string - ? ``` -The Unsafe type is often used to create schematics for extended specifications like OpenAPI +The Unsafe type is often used to create schematics for extended specifications like OpenAPI. ```typescript const Nullable = (schema: T) => Type.Unsafe | null>({ @@ -1042,18 +1117,18 @@ type S = Static // type S = 'A' | 'B' | 'C' ``` -### Type Guard +### TypeGuard -TypeBox can type check its own types with the TypeGuard module. This module is written for reflection and provides structural tests for every TypeBox type. Functions of this module return `is` guards which can be used with TypeScript control flow assertions to obtain schema inference. The following guards that the value A is TString. +TypeBox can check its own types with the TypeGuard module. This module is written for type introspection and provides structural tests for every built-in TypeBox type. Functions of this module return `is` guards which can be used with control flow assertions to obtain schema inference for unknown values. The following guards that the value `T` is TString. ```typescript -import { Type, Kind, TypeGuard } from '@sinclair/typebox' +import { TypeGuard, Kind } from '@sinclair/typebox' -const A: unknown = { ... } +const T = { [Kind]: 'String', type: 'string' } -if(TypeGuard.TString(A)) { +if(TypeGuard.IsString(T)) { - A.type // A.type = 'string' + // T is TString } ``` @@ -1065,13 +1140,13 @@ TypeBox types contain various symbol properties that are used for reflection, co ```typescript const T = Type.Object({ // const T = { - name: Type.Optional(Type.String()) // [Kind]: 'Object', + name: Type.Optional(Type.String()) // [Symbol(TypeBox.Kind)]: 'Object', }) // type: 'object', // properties: { // name: { // type: 'string', - // [Kind]: 'String', - // [Optional]: 'Optional' + // [Symbol(TypeBox.Kind)]: 'String', + // [Symbol(TypeBox.Optional)]: 'Optional' // } // } // } @@ -1090,7 +1165,7 @@ const U = Type.Strict(T) // const U = { ## Values -TypeBox provides an optional utility module that can be used to perform structural operations on JavaScript values. This module includes functionality to create, check and cast values from types as well as check equality, clone, diff and patch JavaScript values. This module is provided via optional import. +TypeBox provides an optional Value submodule that can be used to perform structural operations on JavaScript values. This submodule includes functionality to create, check and cast values from types as well as check equality, clone, diff and patch JavaScript values. This submodule is provided via optional import. ```typescript import { Value } from '@sinclair/typebox/value' @@ -1144,11 +1219,49 @@ const R1 = Value.Convert(T, { x: '3.14' }) // const R1 = { x: 3.14 } const R2 = Value.Convert(T, { x: 'not a number' }) // const R2 = { x: 'not a number' } ``` + + +### Clean + +Use Clean to remove excess properties from a value. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. + +```typescript +const T = Type.Object({ + x: Type.Number(), + y: Type.Number() +}) + +const X = Value.Clean(T, null) // const 'X = null + +const Y = Value.Clean(T, { x: 1 }) // const 'Y = { x: 1 } + +const Z = Value.Clean(T, { x: 1, y: 2, z: 3 }) // const 'Z = { x: 1, y: 2 } +``` + + + +### Default + +Use Default to generate missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. + +```typescript +const T = Type.Object({ + x: Type.Number({ default: 0 }), + y: Type.Number({ default: 0 }) +}) + +const X = Value.Default(T, null) // const 'X = null - non-enumerable + +const Y = Value.Default(T, { }) // const 'Y = { x: 0, y: 0 } + +const Z = Value.Default(T, { x: 1 }) // const 'Z = { x: 1, y: 0 } +``` + ### Cast -Use the Cast function to cast a value with a type. The cast function will retain as much information as possible from the original value. +Use the Cast function to upcast a value into a target type. This function will retain as much infomation as possible from the original value. The Cast function is intended to be used in data migration scenarios where existing values need to be upgraded to match a modified type. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) @@ -1164,7 +1277,7 @@ const Z = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const Z = { x: 1, y: 2 } ### Decode -Use the Decode function to decode a value from a type, or throw if the value is invalid. The return value will infer as the decoded type. This function will run Transform codecs if available. +Use the Decode function to decode a value from a type or throw if the value is invalid. The return value will infer as the decoded type. This function will run Transform codecs if available. ```typescript const A = Value.Decode(Type.String(), 'hello') // const A = 'hello' @@ -1175,7 +1288,7 @@ const B = Value.Decode(Type.String(), 42) // throw ### Encode -Use the Encode function to encode a value to a type, or throw if the value is invalid. The return value will infer as the encoded type. This function will run Transform codecs if available. +Use the Encode function to encode a value to a type or throw if the value is invalid. The return value will infer as the encoded type. This function will run Transform codecs if available. ```typescript const A = Value.Encode(Type.String(), 'hello') // const A = 'hello' @@ -1275,7 +1388,7 @@ const Y = { z: 1 } // const Y = { z: 1 } const X = { y: Y } // const X = { y: { z: 1 } } const A = { x: X } // const A = { x: { y: { z: 1 } } } -Value.Mutate(A, { x: { y: { z: 2 } } }) // const A' = { x: { y: { z: 2 } } } +Value.Mutate(A, { x: { y: { z: 2 } } }) // A' = { x: { y: { z: 2 } } } const R0 = A.x.y.z === 2 // const R0 = true const R1 = A.x.y === Y // const R1 = true @@ -1293,9 +1406,9 @@ import { ValuePointer } from '@sinclair/typebox/value' const A = { x: 0, y: 0, z: 0 } -ValuePointer.Set(A, '/x', 1) // const A' = { x: 1, y: 0, z: 0 } -ValuePointer.Set(A, '/y', 1) // const A' = { x: 1, y: 1, z: 0 } -ValuePointer.Set(A, '/z', 1) // const A' = { x: 1, y: 1, z: 1 } +ValuePointer.Set(A, '/x', 1) // A' = { x: 1, y: 0, z: 0 } +ValuePointer.Set(A, '/y', 1) // A' = { x: 1, y: 1, z: 0 } +ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1 } ``` @@ -1308,15 +1421,18 @@ The TypeBox type system can be extended with additional types and formats using ### TypeRegistry -Use the TypeRegistry to register a new type. The Kind must match the registered type name. +Use the TypeRegistry to register a type. The Kind must match the registered type name. ```typescript -import { TypeRegistry, Kind } from '@sinclair/typebox' +import { TSchema, Kind, TypeRegistry } from '@sinclair/typebox' TypeRegistry.Set('Foo', (schema, value) => value === 'foo') -const A = Value.Check({ [Kind]: 'Foo' }, 'foo') // const A = true -const B = Value.Check({ [Kind]: 'Foo' }, 'bar') // const B = false +const Foo = { [Kind]: 'Foo' } as TSchema + +const A = Value.Check(Foo, 'foo') // const A = true + +const B = Value.Check(Foo, 'bar') // const B = false ``` @@ -1333,6 +1449,7 @@ FormatRegistry.Set('foo', (value) => value === 'foo') const T = Type.String({ format: 'foo' }) const A = Value.Check(T, 'foo') // const A = true + const B = Value.Check(T, 'bar') // const B = false ``` @@ -1445,7 +1562,7 @@ const all = [...C.Errors(value)] // const all = [{ // }] ``` -Use the Code function to generate assertion functions as strings. This function can be used to create high performance assertions that can be written to disk as importable modules. The following generates code to check a string. +Use the Code function to generate assertion functions as strings. This function can be used to generate code that can be written to disk as importable modules. This technique is sometimes referred to as Ahead of Time (AOT) compilation. The following generates code to check a string. ```typescript const C = TypeCompiler.Code(Type.String()) // const C = `return function check(value) { @@ -1459,68 +1576,70 @@ const C = TypeCompiler.Code(Type.String()) // const C = `return functi ## TypeSystem -The TypeBox TypeSystem module provides functionality to define types above and beyond the built-in Json and JavaScript type sets. They also manage TypeBox's localization options (i18n) for error message generation and can control various assertion policies used when type checking. Configurations made to the TypeSystem module are observed by the TypeCompiler, Value and Error modules. +The TypeBox TypeSystem module provides configurations to use either Json Schema or TypeScript type checking semantics. Configurations made to the TypeSystem module are observed by the TypeCompiler, Value and Error modules. - + -### Types +### Policies -Use the TypeSystem Type function to register a user defined type. +TypeBox validates using standard Json Schema assertion policies by default. The TypeSystemPolicy module can override some of these to have TypeBox assert values inline with TypeScript static checks. It also provides overrides for certain checking rules related to non-serializable values (such as void) which can be helpful in Json based protocols such as Json Rpc 2.0. -```typescript -import { TypeSystem } from '@sinclair/typebox/system' +The following overrides are available. -const StringSet = TypeSystem.Type>('StringSet', (options, value) => { - return value instanceof Set && [...value].every(value => typeof value === 'string') -}) +```typescript +import { TypeSystemPolicy } from '@sinclair/typebox/system' -const T = StringSet({}) // Pass options if any +// Disallow undefined values for optional properties (default is false) +// +// const A: { x?: number } = { x: undefined } - disallowed when enabled -const A = Value.Check(T, new Set()) // const A = true -const B = Value.Check(T, new Set(['hello'])) // const B = true -const C = Value.Check(T, new Set([1])) // const C = false +TypeSystemPolicy.ExactOptionalPropertyTypes = true -``` +// Allow arrays to validate as object types (default is false) +// +// const A: {} = [] - allowed in TS - +TypeSystemPolicy.AllowArrayObject = true -### Formats +// Allow numeric values to be NaN or + or - Infinity (default is false) +// +// const A: number = NaN - allowed in TS -Use the TypeSystem Format function to register a string format. +TypeSystemPolicy.AllowNaN = true -```typescript -import { TypeSystem } from '@sinclair/typebox/system' +// Allow void types to check with undefined and null (default is false) +// +// Used to signal void return on Json-Rpc 2.0 protocol -const F = TypeSystem.Format('foo', value => value === 'Foo') +TypeSystemPolicy.AllowNullVoid = true +``` -const T = Type.String({ format: F }) + -const A = Value.Check(T, 'foo') // const A = true -const B = Value.Check(T, 'bar') // const B = false -``` +## Error Function - +Error messages in TypeBox can be customized by defining an ErrorFunction. This function allows for the localization of error messages as well as enabling custom error messages for custom types. By default, TypeBox will generate messages using the `en-US` locale. To support additional locales, you can replicate the function found in `src/errors/function.ts` and create a locale specific translation. The function can then be set via SetErrorFunction. -### Errors +The following example shows an inline error function that intercepts errors for String, Number and Boolean only. The DefaultErrorFunction is used to return a default error message. -Use the TypeSystemErrorFunction to override validation error messages. This can be used to localize errors or create error messages for user defined types. ```typescript -import { TypeSystemErrorFunction, ValueErrorType, DefaultErrorFunction } from '@sinclair/typebox/system' +import { SetErrorFunction, DefaultErrorFunction, ValueErrorType } from '@sinclair/typebox/errors' -TypeSystemErrorFunction.Set((schema, errorType) => { // i18n override - switch(errorType) { +SetErrorFunction((error) => { // i18n override + switch(error.errorType) { /* en-US */ case ValueErrorType.String: return 'Expected string' /* fr-FR */ case ValueErrorType.Number: return 'Nombre attendu' /* ko-KR */ case ValueErrorType.Boolean: return '예상 부울' - /* en-US */ default: return DefaultErrorFunction(schema, errorType) + /* en-US */ default: return DefaultErrorFunction(error) } }) -const T = Type.Object({ // const T = { ... } - x: Type.String(), - y: Type.Number(), - z: Type.Boolean() -}) +const T = Type.Object({ // const T: TObject<{ + x: Type.String(), // TString, + y: Type.Number(), // TNumber, + z: Type.Boolean() // TBoolean +}) // }> + const E = [...Value.Errors(T, { // const E = [{ x: null, // type: 48, y: null, // schema: { ... }, @@ -1542,42 +1661,6 @@ const E = [...Value.Errors(T, { // const E = [{ // }] ``` - - -### Policies - -TypeBox validates using standard Json Schema assertion policies by default. The TypeSystemPolicy module can override some of these to have TypeBox check values inline with TypeScript static assertions. It also provides overrides for certain checking rules related to non-serializable values (such as void) which can be useful in Json based protocols such as JsonRpc-2. - -The following overrides are available. - -```typescript -import { TypeSystemPolicy } from '@sinclair/typebox/system' - -// Disallow undefined values for optional properties (default is false) -// -// const A: { x?: number } = { x: undefined } - disallowed when enabled - -TypeSystemPolicy.ExactOptionalPropertyTypes = true - -// Allow arrays to validate as object types (default is false) -// -// const A: {} = [] - allowed in TS - -TypeSystemPolicy.AllowArrayObject = true - -// Allow numeric values to be NaN or + or - Infinity (default is false) -// -// const A: number = NaN - allowed in TS - -TypeSystemPolicy.AllowNaN = true - -// Allow void types to check with undefined and null (default is false) -// -// Used to signal void return on Json-RPC 2.0 protocol - -TypeSystemPolicy.AllowNullVoid = true -``` - ## TypeBox Workbench @@ -1590,7 +1673,7 @@ TypeBox offers a web based code generation tool that can convert TypeScript type ## TypeBox Codegen -TypeBox provides a code generation library that can be used to automate type translation between TypeScript and TypeBox. This library also includes functionality to transform TypeScript types to other ecosystem libraries. +TypeBox provides a code generation library that can be integrated into toolchains to automate type translation between TypeScript and TypeBox. This library also includes functionality to transform TypeScript types to other ecosystem libraries. [TypeBox Codegen Link Here](https://github.com/sinclairzx81/typebox-codegen) @@ -1619,7 +1702,7 @@ The following is a list of community packages that offer general tooling, extend ## Benchmark -This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.12.0 running on Node 20.0.0. +This project maintains a set of benchmarks that measure Ajv, Value and TypeCompiler compilation and validation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. The results below show for Ajv version 8.12.0 running on Node 20.10.0. For additional comparative benchmarks, please refer to [typescript-runtime-type-benchmarks](https://moltar.github.io/typescript-runtime-type-benchmarks/). @@ -1627,41 +1710,41 @@ For additional comparative benchmarks, please refer to [typescript-runtime-type- ### Compile -This benchmark measures compilation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/compile.ts). +This benchmark measures compilation performance for varying types. ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 216 ms' │ ' 9 ms' │ ' 24.00 x' │ -│ Literal_Number │ 1000 │ ' 169 ms' │ ' 7 ms' │ ' 24.14 x' │ -│ Literal_Boolean │ 1000 │ ' 150 ms' │ ' 5 ms' │ ' 30.00 x' │ -│ Primitive_Number │ 1000 │ ' 161 ms' │ ' 7 ms' │ ' 23.00 x' │ -│ Primitive_String │ 1000 │ ' 148 ms' │ ' 6 ms' │ ' 24.67 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 185 ms' │ ' 9 ms' │ ' 20.56 x' │ -│ Primitive_Boolean │ 1000 │ ' 132 ms' │ ' 4 ms' │ ' 33.00 x' │ -│ Primitive_Null │ 1000 │ ' 141 ms' │ ' 3 ms' │ ' 47.00 x' │ -│ Object_Unconstrained │ 1000 │ ' 1109 ms' │ ' 30 ms' │ ' 36.97 x' │ -│ Object_Constrained │ 1000 │ ' 1200 ms' │ ' 24 ms' │ ' 50.00 x' │ -│ Object_Vector3 │ 1000 │ ' 379 ms' │ ' 9 ms' │ ' 42.11 x' │ -│ Object_Box3D │ 1000 │ ' 1709 ms' │ ' 30 ms' │ ' 56.97 x' │ -│ Tuple_Primitive │ 1000 │ ' 456 ms' │ ' 14 ms' │ ' 32.57 x' │ -│ Tuple_Object │ 1000 │ ' 1229 ms' │ ' 17 ms' │ ' 72.29 x' │ -│ Composite_Intersect │ 1000 │ ' 570 ms' │ ' 17 ms' │ ' 33.53 x' │ -│ Composite_Union │ 1000 │ ' 513 ms' │ ' 19 ms' │ ' 27.00 x' │ -│ Math_Vector4 │ 1000 │ ' 782 ms' │ ' 13 ms' │ ' 60.15 x' │ -│ Math_Matrix4 │ 1000 │ ' 393 ms' │ ' 12 ms' │ ' 32.75 x' │ -│ Array_Primitive_Number │ 1000 │ ' 361 ms' │ ' 12 ms' │ ' 30.08 x' │ -│ Array_Primitive_String │ 1000 │ ' 296 ms' │ ' 5 ms' │ ' 59.20 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 315 ms' │ ' 4 ms' │ ' 78.75 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1721 ms' │ ' 22 ms' │ ' 78.23 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1450 ms' │ ' 21 ms' │ ' 69.05 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 813 ms' │ ' 13 ms' │ ' 62.54 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1537 ms' │ ' 17 ms' │ ' 90.41 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 753 ms' │ ' 17 ms' │ ' 44.29 x' │ -│ Array_Composite_Union │ 1000 │ ' 808 ms' │ ' 16 ms' │ ' 50.50 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1118 ms' │ ' 16 ms' │ ' 69.88 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 690 ms' │ ' 9 ms' │ ' 76.67 x' │ +│ Literal_String │ 1000 │ ' 242 ms' │ ' 10 ms' │ ' 24.20 x' │ +│ Literal_Number │ 1000 │ ' 200 ms' │ ' 8 ms' │ ' 25.00 x' │ +│ Literal_Boolean │ 1000 │ ' 168 ms' │ ' 6 ms' │ ' 28.00 x' │ +│ Primitive_Number │ 1000 │ ' 165 ms' │ ' 8 ms' │ ' 20.63 x' │ +│ Primitive_String │ 1000 │ ' 154 ms' │ ' 6 ms' │ ' 25.67 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 208 ms' │ ' 14 ms' │ ' 14.86 x' │ +│ Primitive_Boolean │ 1000 │ ' 142 ms' │ ' 6 ms' │ ' 23.67 x' │ +│ Primitive_Null │ 1000 │ ' 143 ms' │ ' 6 ms' │ ' 23.83 x' │ +│ Object_Unconstrained │ 1000 │ ' 1217 ms' │ ' 31 ms' │ ' 39.26 x' │ +│ Object_Constrained │ 1000 │ ' 1275 ms' │ ' 26 ms' │ ' 49.04 x' │ +│ Object_Vector3 │ 1000 │ ' 405 ms' │ ' 12 ms' │ ' 33.75 x' │ +│ Object_Box3D │ 1000 │ ' 1833 ms' │ ' 27 ms' │ ' 67.89 x' │ +│ Tuple_Primitive │ 1000 │ ' 475 ms' │ ' 13 ms' │ ' 36.54 x' │ +│ Tuple_Object │ 1000 │ ' 1267 ms' │ ' 30 ms' │ ' 42.23 x' │ +│ Composite_Intersect │ 1000 │ ' 604 ms' │ ' 18 ms' │ ' 33.56 x' │ +│ Composite_Union │ 1000 │ ' 545 ms' │ ' 20 ms' │ ' 27.25 x' │ +│ Math_Vector4 │ 1000 │ ' 829 ms' │ ' 12 ms' │ ' 69.08 x' │ +│ Math_Matrix4 │ 1000 │ ' 405 ms' │ ' 10 ms' │ ' 40.50 x' │ +│ Array_Primitive_Number │ 1000 │ ' 372 ms' │ ' 12 ms' │ ' 31.00 x' │ +│ Array_Primitive_String │ 1000 │ ' 327 ms' │ ' 5 ms' │ ' 65.40 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 300 ms' │ ' 4 ms' │ ' 75.00 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1755 ms' │ ' 21 ms' │ ' 83.57 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1516 ms' │ ' 20 ms' │ ' 75.80 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 825 ms' │ ' 14 ms' │ ' 58.93 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1616 ms' │ ' 16 ms' │ ' 101.00 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 776 ms' │ ' 16 ms' │ ' 48.50 x' │ +│ Array_Composite_Union │ 1000 │ ' 820 ms' │ ' 14 ms' │ ' 58.57 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1166 ms' │ ' 15 ms' │ ' 77.73 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 695 ms' │ ' 8 ms' │ ' 86.88 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1669,43 +1752,43 @@ This benchmark measures compilation performance for varying types. You can revie ### Validate -This benchmark measures validation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/measurement/module/check.ts). +This benchmark measures validation performance for varying types. ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ │ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 24 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 15 ms' │ ' 20 ms' │ ' 10 ms' │ ' 2.00 x' │ -│ Literal_Boolean │ 1000000 │ ' 14 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ -│ Primitive_Number │ 1000000 │ ' 25 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ -│ Primitive_String │ 1000000 │ ' 21 ms' │ ' 24 ms' │ ' 9 ms' │ ' 2.67 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 156 ms' │ ' 43 ms' │ ' 38 ms' │ ' 1.13 x' │ +│ Literal_String │ 1000000 │ ' 18 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ +│ Literal_Number │ 1000000 │ ' 16 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Literal_Boolean │ 1000000 │ ' 15 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_Number │ 1000000 │ ' 21 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_String │ 1000000 │ ' 22 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 155 ms' │ ' 41 ms' │ ' 34 ms' │ ' 1.21 x' │ │ Primitive_Boolean │ 1000000 │ ' 18 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Null │ 1000000 │ ' 20 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1055 ms' │ ' 32 ms' │ ' 24 ms' │ ' 1.33 x' │ -│ Object_Constrained │ 1000000 │ ' 1232 ms' │ ' 49 ms' │ ' 43 ms' │ ' 1.14 x' │ -│ Object_Vector3 │ 1000000 │ ' 432 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Object_Box3D │ 1000000 │ ' 1993 ms' │ ' 54 ms' │ ' 46 ms' │ ' 1.17 x' │ -│ Object_Recursive │ 1000000 │ ' 5115 ms' │ ' 342 ms' │ ' 159 ms' │ ' 2.15 x' │ -│ Tuple_Primitive │ 1000000 │ ' 156 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Tuple_Object │ 1000000 │ ' 740 ms' │ ' 29 ms' │ ' 18 ms' │ ' 1.61 x' │ -│ Composite_Intersect │ 1000000 │ ' 797 ms' │ ' 26 ms' │ ' 14 ms' │ ' 1.86 x' │ -│ Composite_Union │ 1000000 │ ' 530 ms' │ ' 23 ms' │ ' 13 ms' │ ' 1.77 x' │ -│ Math_Vector4 │ 1000000 │ ' 240 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1036 ms' │ ' 39 ms' │ ' 27 ms' │ ' 1.44 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 248 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Array_Primitive_String │ 1000000 │ ' 227 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 138 ms' │ ' 21 ms' │ ' 13 ms' │ ' 1.62 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 5540 ms' │ ' 66 ms' │ ' 59 ms' │ ' 1.12 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 5750 ms' │ ' 123 ms' │ ' 108 ms' │ ' 1.14 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 21842 ms' │ ' 1771 ms' │ ' 599 ms' │ ' 2.96 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 715 ms' │ ' 36 ms' │ ' 29 ms' │ ' 1.24 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3131 ms' │ ' 63 ms' │ ' 50 ms' │ ' 1.26 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3064 ms' │ ' 44 ms' │ ' 35 ms' │ ' 1.26 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2172 ms' │ ' 65 ms' │ ' 31 ms' │ ' 2.10 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1032 ms' │ ' 37 ms' │ ' 24 ms' │ ' 1.54 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 4859 ms' │ ' 114 ms' │ ' 86 ms' │ ' 1.33 x' │ +│ Primitive_Null │ 1000000 │ ' 19 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ +│ Object_Unconstrained │ 1000000 │ ' 1003 ms' │ ' 32 ms' │ ' 24 ms' │ ' 1.33 x' │ +│ Object_Constrained │ 1000000 │ ' 1265 ms' │ ' 49 ms' │ ' 38 ms' │ ' 1.29 x' │ +│ Object_Vector3 │ 1000000 │ ' 418 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Object_Box3D │ 1000000 │ ' 2035 ms' │ ' 56 ms' │ ' 49 ms' │ ' 1.14 x' │ +│ Object_Recursive │ 1000000 │ ' 5243 ms' │ ' 326 ms' │ ' 157 ms' │ ' 2.08 x' │ +│ Tuple_Primitive │ 1000000 │ ' 153 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ +│ Tuple_Object │ 1000000 │ ' 781 ms' │ ' 28 ms' │ ' 18 ms' │ ' 1.56 x' │ +│ Composite_Intersect │ 1000000 │ ' 742 ms' │ ' 25 ms' │ ' 14 ms' │ ' 1.79 x' │ +│ Composite_Union │ 1000000 │ ' 558 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ +│ Math_Vector4 │ 1000000 │ ' 246 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ +│ Math_Matrix4 │ 1000000 │ ' 1052 ms' │ ' 43 ms' │ ' 28 ms' │ ' 1.54 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 272 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ +│ Array_Primitive_String │ 1000000 │ ' 235 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 134 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 6280 ms' │ ' 65 ms' │ ' 59 ms' │ ' 1.10 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 6076 ms' │ ' 130 ms' │ ' 119 ms' │ ' 1.09 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 22738 ms' │ ' 1730 ms' │ ' 635 ms' │ ' 2.72 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 689 ms' │ ' 35 ms' │ ' 30 ms' │ ' 1.17 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 3266 ms' │ ' 63 ms' │ ' 52 ms' │ ' 1.21 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 3310 ms' │ ' 44 ms' │ ' 36 ms' │ ' 1.22 x' │ +│ Array_Composite_Union │ 1000000 │ ' 2432 ms' │ ' 69 ms' │ ' 33 ms' │ ' 2.09 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 1158 ms' │ ' 37 ms' │ ' 24 ms' │ ' 1.54 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 5435 ms' │ ' 132 ms' │ ' 92 ms' │ ' 1.43 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1719,11 +1802,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '163.6 kb' │ ' 71.6 kb' │ '2.28 x' │ -│ typebox/errors │ '113.3 kb' │ ' 50.1 kb' │ '2.26 x' │ -│ typebox/system │ ' 83.9 kb' │ ' 37.5 kb' │ '2.24 x' │ -│ typebox/value │ '191.1 kb' │ ' 82.3 kb' │ '2.32 x' │ -│ typebox │ ' 73.8 kb' │ ' 32.3 kb' │ '2.29 x' │ +│ typebox/compiler │ '120.6 kb' │ ' 52.9 kb' │ '2.28 x' │ +│ typebox/errors │ ' 55.7 kb' │ ' 25.5 kb' │ '2.19 x' │ +│ typebox/system │ ' 4.7 kb' │ ' 2.0 kb' │ '2.33 x' │ +│ typebox/value │ '146.2 kb' │ ' 62.0 kb' │ '2.36 x' │ +│ typebox │ ' 91.4 kb' │ ' 37.8 kb' │ '2.42 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index fe03f6d2e..7f7368c19 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -26,25 +26,70 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { EncodeTransform, DecodeTransform, HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform' -import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard' -import { Errors, ValueErrorIterator } from '../errors/errors' +import { TransformEncode, TransformDecode, HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform/index' +import { Errors, ValueErrorIterator } from '../errors/index' import { TypeSystemPolicy } from '../system/index' -import { Deref } from '../value/deref' -import { Hash } from '../value/hash' -import * as Types from '../typebox' +import { TypeBoxError } from '../type/error/index' +import { Deref } from '../value/deref/index' +import { Hash } from '../value/hash/index' +import { Kind } from '../type/symbols/index' -// ------------------------------------------------------------------- +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { KeyOfPattern } from '../type/keyof/index' +import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined' + +import type { TSchema } from '../type/schema/index' +import type { TAsyncIterator } from '../type/async-iterator/index' +import type { TAny } from '../type/any/index' +import type { TArray } from '../type/array/index' +import type { TBigInt } from '../type/bigint/index' +import type { TBoolean } from '../type/boolean/index' +import type { TDate } from '../type/date/index' +import type { TConstructor } from '../type/constructor/index' +import type { TFunction } from '../type/function/index' +import type { TInteger } from '../type/integer/index' +import type { TIntersect } from '../type/intersect/index' +import type { TIterator } from '../type/iterator/index' +import type { TLiteral } from '../type/literal/index' +import { Never, type TNever } from '../type/never/index' +import type { TNot } from '../type/not/index' +import type { TNull } from '../type/null/index' +import type { TNumber } from '../type/number/index' +import type { TObject } from '../type/object/index' +import type { TPromise } from '../type/promise/index' +import type { TRecord } from '../type/record/index' +import type { TRef } from '../type/ref/index' +import type { TRegExp } from '../type/regexp/index' +import type { TTemplateLiteral } from '../type/template-literal/index' +import type { TThis } from '../type/recursive/index' +import type { TTuple } from '../type/tuple/index' +import type { TUnion } from '../type/union/index' +import type { TUnknown } from '../type/unknown/index' +import type { Static, StaticDecode, StaticEncode } from '../type/static/index' +import type { TString } from '../type/string/index' +import type { TSymbol } from '../type/symbol/index' +import type { TUndefined } from '../type/undefined/index' +import type { TUint8Array } from '../type/uint8array/index' +import type { TVoid } from '../type/void/index' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsSchema } from '../type/guard/type' +// ------------------------------------------------------------------ // CheckFunction -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export type CheckFunction = (value: unknown) => boolean -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeCheck -// ------------------------------------------------------------------- -export class TypeCheck { +// ------------------------------------------------------------------ +export class TypeCheck { private readonly hasTransform: boolean - constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) { - this.hasTransform = HasTransform.Has(schema, references) + constructor(private readonly schema: T, private readonly references: TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) { + this.hasTransform = HasTransform(schema, references) } /** Returns the generated assertion code used to validate this type. */ public Code(): string { @@ -55,24 +100,24 @@ export class TypeCheck { return Errors(this.schema, this.references, value) } /** Returns true if the value matches the compiled type. */ - public Check(value: unknown): value is Types.Static { + public Check(value: unknown): value is Static { return this.checkFunc(value) } /** Decodes a value or throws if error */ - public Decode(value: unknown): Types.StaticDecode { + public Decode(value: unknown): StaticDecode { if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) - return this.hasTransform ? DecodeTransform.Decode(this.schema, this.references, value) : value + return this.hasTransform ? TransformDecode(this.schema, this.references, value) : value } /** Encodes a value or throws if error */ - public Encode(value: unknown): Types.StaticEncode { - const encoded = this.hasTransform ? EncodeTransform.Encode(this.schema, this.references, value) : value + public Encode(value: unknown): StaticEncode { + const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) return encoded } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Character -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace Character { export function DollarSign(code: number) { return code === 36 @@ -87,9 +132,9 @@ namespace Character { return code >= 48 && code <= 57 } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // MemberExpression -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace MemberExpression { function IsFirstCharacterNumeric(value: string) { if (value.length === 0) return false @@ -111,9 +156,9 @@ namespace MemberExpression { return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']` } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Identifier -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace Identifier { export function Encode($id: string) { const buffer: string[] = [] @@ -128,30 +173,30 @@ namespace Identifier { return buffer.join('').replace(/__/g, '_') } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // LiteralString -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace LiteralString { export function Escape(content: string) { return content.replace(/'/g, "\\'") } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// ------------------------------------------------------------------- -export class TypeCompilerUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +// ------------------------------------------------------------------ +export class TypeCompilerUnknownTypeError extends TypeBoxError { + constructor(public readonly schema: TSchema) { super('Unknown type') } } -export class TypeCompilerTypeGuardError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class TypeCompilerTypeGuardError extends TypeBoxError { + constructor(public readonly schema: TSchema) { super('Preflight validation check failed to guard for the given schema') } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Policy -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export namespace Policy { export function IsExactOptionalProperty(value: string, key: string, expression: string) { return TypeSystemPolicy.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)` @@ -171,36 +216,36 @@ export namespace Policy { return TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeCompiler -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export type TypeCompilerLanguageOption = 'typescript' | 'javascript' export interface TypeCompilerCodegenOptions { language?: TypeCompilerLanguageOption } /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------- // Guards - // ---------------------------------------------------------------------- - function IsAnyOrUnknown(schema: Types.TSchema) { - return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' + // ---------------------------------------------------------------- + function IsAnyOrUnknown(schema: TSchema) { + return schema[Kind] === 'Any' || schema[Kind] === 'Unknown' } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Types - // ------------------------------------------------------------------- - function* TAny(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { + // ---------------------------------------------------------------- + function* FromAny(schema: TAny, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TArray(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { + function* FromArray(schema: TArray, references: TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')] if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` const elementExpression = CreateExpression(schema.items, references, 'value') yield `${value}.every((${parameter}) => ${elementExpression})` - if (Types.TypeGuard.TSchema(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { - const containsSchema = Types.TypeGuard.TSchema(schema.contains) ? schema.contains : Types.Type.Never() + if (IsSchema(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { + const containsSchema = IsSchema(schema.contains) ? schema.contains : Never() const checkExpression = CreateExpression(containsSchema, references, 'value') const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] @@ -214,10 +259,10 @@ export namespace TypeCompiler { yield `((${parameter}) => { ${block} )(${value})` } } - function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: string): IterableIterator { + function* FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})` } - function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { + function* FromBigInt(schema: TBigInt, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` @@ -225,13 +270,13 @@ export namespace TypeCompiler { if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` } - function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { + function* FromBoolean(schema: TBoolean, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'boolean')` } - function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { + function* FromConstructor(schema: TConstructor, references: TSchema[], value: string): IterableIterator { yield* Visit(schema.returns, references, `${value}.prototype`) } - function* TDate(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { + function* FromDate(schema: TDate, references: TSchema[], value: string): IterableIterator { yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())` if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` @@ -239,10 +284,10 @@ export namespace TypeCompiler { if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` if (IsNumber(schema.multipleOfTimestamp)) yield `(${value}.getTime() % ${schema.multipleOfTimestamp}) === 0` } - function* TFunction(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { + function* FromFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } - function* TInteger(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { + function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` @@ -250,41 +295,41 @@ export namespace TypeCompiler { if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } - function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { - const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ') + function* FromIntersect(schema: TIntersect, references: TSchema[], value: string): IterableIterator { + const check1 = schema.allOf.map((schema: TSchema) => CreateExpression(schema, references, value)).join(' && ') if (schema.unevaluatedProperties === false) { - const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + const keyCheck = CreateVariable(`${new RegExp(KeyOfPattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))` yield `(${check1} && ${check2})` - } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const keyCheck = CreateVariable(`${new RegExp(Types.KeyResolver.ResolvePattern(schema))};`) + } else if (IsSchema(schema.unevaluatedProperties)) { + const keyCheck = CreateVariable(`${new RegExp(KeyOfPattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})` yield `(${check1} && ${check2})` } else { yield `(${check1})` } } - function* TIterator(schema: Types.TIterator, references: Types.TSchema[], value: string): IterableIterator { + function* FromIterator(schema: TIterator, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && Symbol.iterator in ${value})` } - function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* FromLiteral(schema: TLiteral, references: TSchema[], value: string): IterableIterator { if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { yield `(${value} === ${schema.const})` } else { yield `(${value} === '${LiteralString.Escape(schema.const)}')` } } - function* TNever(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { + function* FromNever(schema: TNever, references: TSchema[], value: string): IterableIterator { yield `false` } - function* TNot(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { + function* FromNot(schema: TNot, references: TSchema[], value: string): IterableIterator { const expression = CreateExpression(schema.not, references, value) yield `(!${expression})` } - function* TNull(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { + function* FromNull(schema: TNull, references: TSchema[], value: string): IterableIterator { yield `(${value} === null)` } - function* TNumber(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { + function* FromNumber(schema: TNumber, references: TSchema[], value: string): IterableIterator { yield Policy.IsNumberLike(value) if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` @@ -292,7 +337,7 @@ export namespace TypeCompiler { if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } - function* TObject(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { + function* FromObject(schema: TObject, references: TSchema[], value: string): IterableIterator { yield Policy.IsObjectLike(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` @@ -302,7 +347,7 @@ export namespace TypeCompiler { const property = schema.properties[knownKey] if (schema.required && schema.required.includes(knownKey)) { yield* Visit(property, references, memberExpression) - if (Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` + if (ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` } else { const expression = CreateExpression(property, references, memberExpression) yield Policy.IsExactOptionalProperty(value, knownKey, expression) @@ -322,28 +367,33 @@ export namespace TypeCompiler { yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } } - function* TPromise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { + function* FromPromise(schema: TPromise, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } - function* TRecord(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { + function* FromRecord(schema: TRecord, references: TSchema[], value: string): IterableIterator { yield Policy.IsRecordLike(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0] const variable = CreateVariable(`${new RegExp(patternKey)}`) const check1 = CreateExpression(patternSchema, references, 'value') - const check2 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' + const check2 = IsSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' const expression = `(${variable}.test(key) ? ${check1} : ${check2})` yield `(Object.entries(${value}).every(([key, value]) => ${expression}))` } - function* TRef(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { + function* FromRef(schema: TRef, references: TSchema[], value: string): IterableIterator { const target = Deref(schema, references) // Reference: If we have seen this reference before we can just yield and return the function call. // If this isn't the case we defer to visit to generate and set the function for subsequent passes. if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } - function* TString(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { + function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator { + const variable = CreateVariable(`${new RegExp(schema.source, schema.flags)};`) + yield `(typeof ${value} === 'string')` + yield `${variable}.test(${value})` + } + function* FromString(schema: TString, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` @@ -355,19 +405,19 @@ export namespace TypeCompiler { yield `format('${schema.format}', ${value})` } } - function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { + function* FromSymbol(schema: TSymbol, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'symbol')` } - function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` const variable = CreateVariable(`${new RegExp(schema.pattern)};`) yield `${variable}.test(${value})` } - function* TThis(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { + function* FromThis(schema: TThis, references: TSchema[], value: string): IterableIterator { // Note: This types are assured to be hoisted prior to this call. Just yield the function. yield `${CreateFunctionName(schema.$ref)}(${value})` } - function* TTuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { + function* FromTuple(schema: TTuple, references: TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` if (schema.items === undefined) return yield `${value}.length === 0` yield `(${value}.length === ${schema.maxItems})` @@ -376,35 +426,35 @@ export namespace TypeCompiler { yield `${expression}` } } - function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { + function* FromUndefined(schema: TUndefined, references: TSchema[], value: string): IterableIterator { yield `${value} === undefined` } - function* TUnion(schema: Types.TUnion, references: Types.TSchema[], value: string): IterableIterator { - const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) + function* FromUnion(schema: TUnion, references: TSchema[], value: string): IterableIterator { + const expressions = schema.anyOf.map((schema: TSchema) => CreateExpression(schema, references, value)) yield `(${expressions.join(' || ')})` } - function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { + function* FromUint8Array(schema: TUint8Array, references: TSchema[], value: string): IterableIterator { yield `${value} instanceof Uint8Array` if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } - function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { + function* FromUnknown(schema: TUnknown, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TVoid(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { + function* FromVoid(schema: TVoid, references: TSchema[], value: string): IterableIterator { yield Policy.IsVoidLike(value) } - function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { + function* FromKind(schema: TSchema, references: TSchema[], value: string): IterableIterator { const instance = state.instances.size state.instances.set(instance, schema) - yield `kind('${schema[Types.Kind]}', ${instance}, ${value})` + yield `kind('${schema[Kind]}', ${instance}, ${value})` } - function* Visit(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator { + function* Visit(schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): IterableIterator { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - // ---------------------------------------------------------------------------------- + // -------------------------------------------------------------- // Hoisting - // ---------------------------------------------------------------------------------- + // -------------------------------------------------------------- if (useHoisting && IsString(schema.$id)) { const functionName = CreateFunctionName(schema.$id) if (state.functions.has(functionName)) { @@ -415,86 +465,88 @@ export namespace TypeCompiler { return yield `${functionName}(${value})` } } - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': - return yield* TAny(schema_, references_, value) + return yield* FromAny(schema_, references_, value) case 'Array': - return yield* TArray(schema_, references_, value) + return yield* FromArray(schema_, references_, value) case 'AsyncIterator': - return yield* TAsyncIterator(schema_, references_, value) + return yield* FromAsyncIterator(schema_, references_, value) case 'BigInt': - return yield* TBigInt(schema_, references_, value) + return yield* FromBigInt(schema_, references_, value) case 'Boolean': - return yield* TBoolean(schema_, references_, value) + return yield* FromBoolean(schema_, references_, value) case 'Constructor': - return yield* TConstructor(schema_, references_, value) + return yield* FromConstructor(schema_, references_, value) case 'Date': - return yield* TDate(schema_, references_, value) + return yield* FromDate(schema_, references_, value) case 'Function': - return yield* TFunction(schema_, references_, value) + return yield* FromFunction(schema_, references_, value) case 'Integer': - return yield* TInteger(schema_, references_, value) + return yield* FromInteger(schema_, references_, value) case 'Intersect': - return yield* TIntersect(schema_, references_, value) + return yield* FromIntersect(schema_, references_, value) case 'Iterator': - return yield* TIterator(schema_, references_, value) + return yield* FromIterator(schema_, references_, value) case 'Literal': - return yield* TLiteral(schema_, references_, value) + return yield* FromLiteral(schema_, references_, value) case 'Never': - return yield* TNever(schema_, references_, value) + return yield* FromNever(schema_, references_, value) case 'Not': - return yield* TNot(schema_, references_, value) + return yield* FromNot(schema_, references_, value) case 'Null': - return yield* TNull(schema_, references_, value) + return yield* FromNull(schema_, references_, value) case 'Number': - return yield* TNumber(schema_, references_, value) + return yield* FromNumber(schema_, references_, value) case 'Object': - return yield* TObject(schema_, references_, value) + return yield* FromObject(schema_, references_, value) case 'Promise': - return yield* TPromise(schema_, references_, value) + return yield* FromPromise(schema_, references_, value) case 'Record': - return yield* TRecord(schema_, references_, value) + return yield* FromRecord(schema_, references_, value) case 'Ref': - return yield* TRef(schema_, references_, value) + return yield* FromRef(schema_, references_, value) + case 'RegExp': + return yield* FromRegExp(schema_, references_, value) case 'String': - return yield* TString(schema_, references_, value) + return yield* FromString(schema_, references_, value) case 'Symbol': - return yield* TSymbol(schema_, references_, value) + return yield* FromSymbol(schema_, references_, value) case 'TemplateLiteral': - return yield* TTemplateLiteral(schema_, references_, value) + return yield* FromTemplateLiteral(schema_, references_, value) case 'This': - return yield* TThis(schema_, references_, value) + return yield* FromThis(schema_, references_, value) case 'Tuple': - return yield* TTuple(schema_, references_, value) + return yield* FromTuple(schema_, references_, value) case 'Undefined': - return yield* TUndefined(schema_, references_, value) + return yield* FromUndefined(schema_, references_, value) case 'Union': - return yield* TUnion(schema_, references_, value) + return yield* FromUnion(schema_, references_, value) case 'Uint8Array': - return yield* TUint8Array(schema_, references_, value) + return yield* FromUint8Array(schema_, references_, value) case 'Unknown': - return yield* TUnknown(schema_, references_, value) + return yield* FromUnknown(schema_, references_, value) case 'Void': - return yield* TVoid(schema_, references_, value) + return yield* FromVoid(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) - return yield* TKind(schema_, references_, value) + if (!TypeRegistry.Has(schema_[Kind])) throw new TypeCompilerUnknownTypeError(schema) + return yield* FromKind(schema_, references_, value) } } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Compiler State - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // prettier-ignore const state = { - language: 'javascript', // target language - functions: new Map(), // local functions - variables: new Map(), // local variables - instances: new Map() // exterior kind instances + language: 'javascript', // target language + functions: new Map(), // local functions + variables: new Map(), // local variables + instances: new Map() // exterior kind instances } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Compiler Factory - // ------------------------------------------------------------------- - function CreateExpression(schema: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + // ---------------------------------------------------------------- + function CreateExpression(schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string { return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})` } function CreateFunctionName($id: string) { @@ -505,7 +557,7 @@ export namespace TypeCompiler { state.variables.set(variableName, `const ${variableName} = ${expression}`) return variableName } - function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + function CreateFunction(name: string, schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string { const [newline, pad] = ['\n', (length: number) => ''.padStart(length, ' ')] const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') @@ -519,10 +571,10 @@ export namespace TypeCompiler { function CreateReturns(type: string) { return state.language === 'typescript' ? `: ${type}` : '' } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Compile - // ------------------------------------------------------------------- - function Build(schema: T, references: Types.TSchema[], options: TypeCompilerCodegenOptions): string { + // ---------------------------------------------------------------- + function Build(schema: T, references: TSchema[], options: TypeCompilerCodegenOptions): string { const functionCode = CreateFunction('check', schema, references, 'value') // will populate functions and variables const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') @@ -535,9 +587,9 @@ export namespace TypeCompiler { return [...variables, ...functions, checkFunction].join('\n') } /** Generates the code used to assert this type and returns it as a string */ - export function Code(schema: T, references: Types.TSchema[], options?: TypeCompilerCodegenOptions): string + export function Code(schema: T, references: TSchema[], options?: TypeCompilerCodegenOptions): string /** Generates the code used to assert this type and returns it as a string */ - export function Code(schema: T, options?: TypeCompilerCodegenOptions): string + export function Code(schema: T, options?: TypeCompilerCodegenOptions): string /** Generates the code used to assert this type and returns it as a string */ export function Code(...args: any[]) { const defaults = { language: 'javascript' } @@ -554,24 +606,24 @@ export namespace TypeCompiler { state.variables.clear() state.functions.clear() state.instances.clear() - if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) - for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) + if (!IsSchema(schema)) throw new TypeCompilerTypeGuardError(schema) + for (const schema of references) if (!IsSchema(schema)) throw new TypeCompilerTypeGuardError(schema) return Build(schema, references, options) } /** Compiles a TypeBox type for optimal runtime type checking. Types must be valid TypeBox types of TSchema */ - export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { + export function Compile(schema: T, references: TSchema[] = []): TypeCheck { const generatedCode = Code(schema, references, { language: 'javascript' }) const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) const instances = new Map(state.instances) function typeRegistryFunction(kind: string, instance: number, value: unknown) { - if (!Types.TypeRegistry.Has(kind) || !instances.has(instance)) return false - const checkFunc = Types.TypeRegistry.Get(kind)! + if (!TypeRegistry.Has(kind) || !instances.has(instance)) return false + const checkFunc = TypeRegistry.Get(kind)! const schema = instances.get(instance)! return checkFunc(schema, value) } function formatRegistryFunction(format: string, value: string) { - if (!Types.FormatRegistry.Has(format)) return false - const checkFunc = Types.FormatRegistry.Get(format)! + if (!FormatRegistry.Has(format)) return false + const checkFunc = FormatRegistry.Get(format)! return checkFunc(value) } function hashFunction(value: unknown) { diff --git a/src/compiler/index.ts b/src/compiler/index.ts index cc91cb40b..48b4c10e8 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -27,4 +27,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' -export * from './compiler' +export { TypeCompiler, TypeCheck, type TypeCompilerCodegenOptions, type TypeCompilerLanguageOption, TypeCompilerTypeGuardError, TypeCompilerUnknownTypeError } from './compiler' diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 9f09e1f55..c84ebab9e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -26,15 +26,72 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../value/guard' -import { TypeSystemPolicy, TypeSystemErrorFunction } from '../system/system' -import { Deref } from '../value/deref' -import { Hash } from '../value/hash' -import * as Types from '../typebox' +import { TypeSystemPolicy } from '../system/index' +import { KeyOfPattern } from '../type/keyof/index' +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined' +import { GetErrorFunction } from './function' +import { TypeBoxError } from '../type/error/index' +import { Deref } from '../value/deref/index' +import { Hash } from '../value/hash/index' +import { Kind } from '../type/symbols/index' -// -------------------------------------------------------------------------- +import type { TSchema } from '../type/schema/index' +import type { TAsyncIterator } from '../type/async-iterator/index' +import type { TAny } from '../type/any/index' +import type { TArray } from '../type/array/index' +import type { TBigInt } from '../type/bigint/index' +import type { TBoolean } from '../type/boolean/index' +import type { TDate } from '../type/date/index' +import type { TConstructor } from '../type/constructor/index' +import type { TFunction } from '../type/function/index' +import type { TInteger } from '../type/integer/index' +import type { TIntersect } from '../type/intersect/index' +import type { TIterator } from '../type/iterator/index' +import type { TLiteral } from '../type/literal/index' +import { Never, type TNever } from '../type/never/index' +import type { TNot } from '../type/not/index' +import type { TNull } from '../type/null/index' +import type { TNumber } from '../type/number/index' +import type { TObject } from '../type/object/index' +import type { TPromise } from '../type/promise/index' +import type { TRecord } from '../type/record/index' +import type { TRef } from '../type/ref/index' +import type { TRegExp } from '../type/regexp/index' +import type { TTemplateLiteral } from '../type/template-literal/index' +import type { TThis } from '../type/recursive/index' +import type { TTuple } from '../type/tuple/index' +import type { TUnion } from '../type/union/index' +import type { TUnknown } from '../type/unknown/index' +import type { TString } from '../type/string/index' +import type { TSymbol } from '../type/symbol/index' +import type { TUndefined } from '../type/undefined/index' +import type { TUint8Array } from '../type/uint8array/index' +import type { TVoid } from '../type/void/index' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsArray, + IsUint8Array, + IsDate, + IsPromise, + IsFunction, + IsAsyncIterator, + IsIterator, + IsBoolean, + IsNumber, + IsBigInt, + IsString, + IsSymbol, + IsInteger, + IsNull, + IsUndefined +} from '../value/guard/index' +// ------------------------------------------------------------------ // ValueErrorType -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export enum ValueErrorType { ArrayContains, ArrayMaxContains, @@ -84,6 +141,7 @@ export enum ValueErrorType { ObjectRequiredProperty, Object, Promise, + RegExp, StringFormatUnknown, StringFormat, StringMaxLength, @@ -100,39 +158,39 @@ export enum ValueErrorType { Union, Void, } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // ValueError -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export interface ValueError { type: ValueErrorType - schema: Types.TSchema + schema: TSchema path: string value: unknown message: string } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // ValueErrors -// -------------------------------------------------------------------------- -export class ValueErrorsUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +// ------------------------------------------------------------------ +export class ValueErrorsUnknownTypeError extends TypeBoxError { + constructor(public readonly schema: TSchema) { super('Unknown type') } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // EscapeKey -// -------------------------------------------------------------------------- -export function EscapeKey(key: string): string { +// ------------------------------------------------------------------ +function EscapeKey(key: string): string { return key.replace(/~/g, '~0').replace(/\//g, '~1') // RFC6901 Path } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Guards -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsDefined(value: unknown): value is T { return value !== undefined } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // ValueErrorIterator -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export class ValueErrorIterator { constructor(private readonly iterator: IterableIterator) {} public [Symbol.iterator]() { @@ -147,14 +205,14 @@ export class ValueErrorIterator { // -------------------------------------------------------------------------- // Create // -------------------------------------------------------------------------- -function Create(type: ValueErrorType, schema: Types.TSchema, path: string, value: unknown): ValueError { - return { type, schema, path, value, message: TypeSystemErrorFunction.Get()(schema, type) } +function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown): ValueError { + return { type: errorType, schema, path, value, message: GetErrorFunction()({ errorType, path, schema, value }) } } // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- -function* TAny(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} -function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromAny(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator {} +function* FromArray(schema: TArray, references: TSchema[], path: string, value: any): IterableIterator { if (!IsArray(value)) { return yield Create(ValueErrorType.Array, schema, path, value) } @@ -175,7 +233,7 @@ function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string if (!(IsDefined(schema.contains) || IsDefined(schema.minContains) || IsDefined(schema.maxContains))) { return } - const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() + const containsSchema = IsDefined(schema.contains) ? schema.contains : Never() const containsCount = value.reduce((acc: number, value, index) => (Visit(containsSchema, references, `${path}${index}`, value).next().done === true ? acc + 1 : acc), 0) if (containsCount === 0) { yield Create(ValueErrorType.ArrayContains, schema, path, value) @@ -187,10 +245,10 @@ function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string yield Create(ValueErrorType.ArrayMaxContains, schema, path, value) } } -function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], path: string, value: any): IterableIterator { if (!IsAsyncIterator(value)) yield Create(ValueErrorType.AsyncIterator, schema, path, value) } -function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromBigInt(schema: TBigInt, references: TSchema[], path: string, value: any): IterableIterator { if (!IsBigInt(value)) return yield Create(ValueErrorType.BigInt, schema, path, value) if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield Create(ValueErrorType.BigIntExclusiveMaximum, schema, path, value) @@ -208,13 +266,13 @@ function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], path: stri yield Create(ValueErrorType.BigIntMultipleOf, schema, path, value) } } -function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromBoolean(schema: TBoolean, references: TSchema[], path: string, value: any): IterableIterator { if (!IsBoolean(value)) yield Create(ValueErrorType.Boolean, schema, path, value) } -function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromConstructor(schema: TConstructor, references: TSchema[], path: string, value: any): IterableIterator { yield* Visit(schema.returns, references, path, value.prototype) } -function* TDate(schema: Types.TDate, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromDate(schema: TDate, references: TSchema[], path: string, value: any): IterableIterator { if (!IsDate(value)) return yield Create(ValueErrorType.Date, schema, path, value) if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { yield Create(ValueErrorType.DateExclusiveMaximumTimestamp, schema, path, value) @@ -232,10 +290,10 @@ function* TDate(schema: Types.TDate, references: Types.TSchema[], path: string, yield Create(ValueErrorType.DateMultipleOfTimestamp, schema, path, value) } } -function* TFunction(schema: Types.TFunction, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromFunction(schema: TFunction, references: TSchema[], path: string, value: any): IterableIterator { if (!IsFunction(value)) yield Create(ValueErrorType.Function, schema, path, value) } -function* TInteger(schema: Types.TInteger, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromInteger(schema: TInteger, references: TSchema[], path: string, value: any): IterableIterator { if (!IsInteger(value)) return yield Create(ValueErrorType.Integer, schema, path, value) if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield Create(ValueErrorType.IntegerExclusiveMaximum, schema, path, value) @@ -253,7 +311,7 @@ function* TInteger(schema: Types.TInteger, references: Types.TSchema[], path: st yield Create(ValueErrorType.IntegerMultipleOf, schema, path, value) } } -function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): IterableIterator { for (const inner of schema.allOf) { const next = Visit(inner, references, path, value).next() if (!next.done) { @@ -262,7 +320,7 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path } } if (schema.unevaluatedProperties === false) { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const keyCheck = new RegExp(KeyOfPattern(schema)) for (const valueKey of Object.getOwnPropertyNames(value)) { if (!keyCheck.test(valueKey)) { yield Create(ValueErrorType.IntersectUnevaluatedProperties, schema, `${path}/${valueKey}`, value) @@ -270,7 +328,7 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path } } if (typeof schema.unevaluatedProperties === 'object') { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const keyCheck = new RegExp(KeyOfPattern(schema)) for (const valueKey of Object.getOwnPropertyNames(value)) { if (!keyCheck.test(valueKey)) { const next = Visit(schema.unevaluatedProperties, references, `${path}/${valueKey}`, value[valueKey]).next() @@ -279,22 +337,22 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path } } } -function* TIterator(schema: Types.TIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromIterator(schema: TIterator, references: TSchema[], path: string, value: any): IterableIterator { if (!IsIterator(value)) yield Create(ValueErrorType.Iterator, schema, path, value) } -function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromLiteral(schema: TLiteral, references: TSchema[], path: string, value: any): IterableIterator { if (!(value === schema.const)) yield Create(ValueErrorType.Literal, schema, path, value) } -function* TNever(schema: Types.TNever, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromNever(schema: TNever, references: TSchema[], path: string, value: any): IterableIterator { yield Create(ValueErrorType.Never, schema, path, value) } -function* TNot(schema: Types.TNot, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromNot(schema: TNot, references: TSchema[], path: string, value: any): IterableIterator { if (Visit(schema.not, references, path, value).next().done === true) yield Create(ValueErrorType.Not, schema, path, value) } -function* TNull(schema: Types.TNull, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromNull(schema: TNull, references: TSchema[], path: string, value: any): IterableIterator { if (!IsNull(value)) yield Create(ValueErrorType.Null, schema, path, value) } -function* TNumber(schema: Types.TNumber, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromNumber(schema: TNumber, references: TSchema[], path: string, value: any): IterableIterator { if (!TypeSystemPolicy.IsNumberLike(value)) return yield Create(ValueErrorType.Number, schema, path, value) if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { yield Create(ValueErrorType.NumberExclusiveMaximum, schema, path, value) @@ -312,7 +370,7 @@ function* TNumber(schema: Types.TNumber, references: Types.TSchema[], path: stri yield Create(ValueErrorType.NumberMultipleOf, schema, path, value) } } -function* TObject(schema: Types.TObject, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromObject(schema: TObject, references: TSchema[], path: string, value: any): IterableIterator { if (!TypeSystemPolicy.IsObjectLike(value)) return yield Create(ValueErrorType.Object, schema, path, value) if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { yield Create(ValueErrorType.ObjectMinProperties, schema, path, value) @@ -337,14 +395,14 @@ function* TObject(schema: Types.TObject, references: Types.TSchema[], path: stri if (typeof schema.additionalProperties === 'object') { for (const valueKey of unknownKeys) { if (knownKeys.includes(valueKey)) continue - yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(valueKey)}`, value[valueKey]) + yield* Visit(schema.additionalProperties as TSchema, references, `${path}/${EscapeKey(valueKey)}`, value[valueKey]) } } for (const knownKey of knownKeys) { const property = schema.properties[knownKey] if (schema.required && schema.required.includes(knownKey)) { yield* Visit(property, references, `${path}/${EscapeKey(knownKey)}`, value[knownKey]) - if (Types.ExtendsUndefined.Check(schema) && !(knownKey in value)) { + if (ExtendsUndefinedCheck(schema) && !(knownKey in value)) { yield Create(ValueErrorType.ObjectRequiredProperty, property, `${path}/${EscapeKey(knownKey)}`, undefined) } } else { @@ -354,10 +412,10 @@ function* TObject(schema: Types.TObject, references: Types.TSchema[], path: stri } } } -function* TPromise(schema: Types.TPromise, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromPromise(schema: TPromise, references: TSchema[], path: string, value: any): IterableIterator { if (!IsPromise(value)) yield Create(ValueErrorType.Promise, schema, path, value) } -function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromRecord(schema: TRecord, references: TSchema[], path: string, value: any): IterableIterator { if (!TypeSystemPolicy.IsRecordLike(value)) return yield Create(ValueErrorType.Object, schema, path, value) if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { yield Create(ValueErrorType.ObjectMinProperties, schema, path, value) @@ -372,7 +430,7 @@ function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: stri } if (typeof schema.additionalProperties === 'object') { for (const [propertyKey, propertyValue] of Object.entries(value)) { - if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as Types.TSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue) + if (!regex.test(propertyKey)) yield* Visit(schema.additionalProperties as TSchema, references, `${path}/${EscapeKey(propertyKey)}`, propertyValue) } } if (schema.additionalProperties === false) { @@ -382,10 +440,16 @@ function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: stri } } } -function* TRef(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromRef(schema: TRef, references: TSchema[], path: string, value: any): IterableIterator { yield* Visit(Deref(schema, references), references, path, value) } -function* TString(schema: Types.TString, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromRegExp(schema: TRegExp, references: TSchema[], path: string, value: any): IterableIterator { + const regex = new RegExp(schema.source, schema.flags) + if (!regex.test(value)) { + return yield Create(ValueErrorType.RegExp, schema, path, value) + } +} +function* FromString(schema: TString, references: TSchema[], path: string, value: any): IterableIterator { if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value) if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { yield Create(ValueErrorType.StringMinLength, schema, path, value) @@ -400,30 +464,30 @@ function* TString(schema: Types.TString, references: Types.TSchema[], path: stri } } if (IsString(schema.format)) { - if (!Types.FormatRegistry.Has(schema.format)) { + if (!FormatRegistry.Has(schema.format)) { yield Create(ValueErrorType.StringFormatUnknown, schema, path, value) } else { - const format = Types.FormatRegistry.Get(schema.format)! + const format = FormatRegistry.Get(schema.format)! if (!format(value)) { yield Create(ValueErrorType.StringFormat, schema, path, value) } } } } -function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromSymbol(schema: TSymbol, references: TSchema[], path: string, value: any): IterableIterator { if (!IsSymbol(value)) yield Create(ValueErrorType.Symbol, schema, path, value) } -function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], path: string, value: any): IterableIterator { if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value) const regex = new RegExp(schema.pattern) if (!regex.test(value)) { yield Create(ValueErrorType.StringPattern, schema, path, value) } } -function* TThis(schema: Types.TThis, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromThis(schema: TThis, references: TSchema[], path: string, value: any): IterableIterator { yield* Visit(Deref(schema, references), references, path, value) } -function* TTuple(schema: Types.TTuple, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromTuple(schema: TTuple, references: TSchema[], path: string, value: any): IterableIterator { if (!IsArray(value)) return yield Create(ValueErrorType.Tuple, schema, path, value) if (schema.items === undefined && !(value.length === 0)) { return yield Create(ValueErrorType.TupleLength, schema, path, value) @@ -438,10 +502,10 @@ function* TTuple(schema: Types.TTuple, references: Types.TSchema[], path: yield* Visit(schema.items[i], references, `${path}/${i}`, value[i]) } } -function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromUndefined(schema: TUndefined, references: TSchema[], path: string, value: any): IterableIterator { if (!IsUndefined(value)) yield Create(ValueErrorType.Undefined, schema, path, value) } -function* TUnion(schema: Types.TUnion, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): IterableIterator { let count = 0 for (const subschema of schema.anyOf) { const errors = [...Visit(subschema, references, path, value)] @@ -452,7 +516,7 @@ function* TUnion(schema: Types.TUnion, references: Types.TSchema[], path: string yield Create(ValueErrorType.Union, schema, path, value) } } -function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromUint8Array(schema: TUint8Array, references: TSchema[], path: string, value: any): IterableIterator { if (!IsUint8Array(value)) return yield Create(ValueErrorType.Uint8Array, schema, path, value) if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) { yield Create(ValueErrorType.Uint8ArrayMaxByteLength, schema, path, value) @@ -461,87 +525,89 @@ function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], pa yield Create(ValueErrorType.Uint8ArrayMinByteLength, schema, path, value) } } -function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], path: string, value: any): IterableIterator {} -function* TVoid(schema: Types.TVoid, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* FromUnknown(schema: TUnknown, references: TSchema[], path: string, value: any): IterableIterator {} +function* FromVoid(schema: TVoid, references: TSchema[], path: string, value: any): IterableIterator { if (!TypeSystemPolicy.IsVoidLike(value)) yield Create(ValueErrorType.Void, schema, path, value) } -function* TKind(schema: Types.TSchema, references: Types.TSchema[], path: string, value: any): IterableIterator { - const check = Types.TypeRegistry.Get(schema[Types.Kind])! +function* FromKind(schema: TSchema, references: TSchema[], path: string, value: any): IterableIterator { + const check = TypeRegistry.Get(schema[Kind])! if (!check(schema, value)) yield Create(ValueErrorType.Kind, schema, path, value) } -function* Visit(schema: T, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* Visit(schema: T, references: TSchema[], path: string, value: any): IterableIterator { const references_ = IsDefined(schema.$id) ? [...references, schema] : references const schema_ = schema as any - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': - return yield* TAny(schema_, references_, path, value) + return yield* FromAny(schema_, references_, path, value) case 'Array': - return yield* TArray(schema_, references_, path, value) + return yield* FromArray(schema_, references_, path, value) case 'AsyncIterator': - return yield* TAsyncIterator(schema_, references_, path, value) + return yield* FromAsyncIterator(schema_, references_, path, value) case 'BigInt': - return yield* TBigInt(schema_, references_, path, value) + return yield* FromBigInt(schema_, references_, path, value) case 'Boolean': - return yield* TBoolean(schema_, references_, path, value) + return yield* FromBoolean(schema_, references_, path, value) case 'Constructor': - return yield* TConstructor(schema_, references_, path, value) + return yield* FromConstructor(schema_, references_, path, value) case 'Date': - return yield* TDate(schema_, references_, path, value) + return yield* FromDate(schema_, references_, path, value) case 'Function': - return yield* TFunction(schema_, references_, path, value) + return yield* FromFunction(schema_, references_, path, value) case 'Integer': - return yield* TInteger(schema_, references_, path, value) + return yield* FromInteger(schema_, references_, path, value) case 'Intersect': - return yield* TIntersect(schema_, references_, path, value) + return yield* FromIntersect(schema_, references_, path, value) case 'Iterator': - return yield* TIterator(schema_, references_, path, value) + return yield* FromIterator(schema_, references_, path, value) case 'Literal': - return yield* TLiteral(schema_, references_, path, value) + return yield* FromLiteral(schema_, references_, path, value) case 'Never': - return yield* TNever(schema_, references_, path, value) + return yield* FromNever(schema_, references_, path, value) case 'Not': - return yield* TNot(schema_, references_, path, value) + return yield* FromNot(schema_, references_, path, value) case 'Null': - return yield* TNull(schema_, references_, path, value) + return yield* FromNull(schema_, references_, path, value) case 'Number': - return yield* TNumber(schema_, references_, path, value) + return yield* FromNumber(schema_, references_, path, value) case 'Object': - return yield* TObject(schema_, references_, path, value) + return yield* FromObject(schema_, references_, path, value) case 'Promise': - return yield* TPromise(schema_, references_, path, value) + return yield* FromPromise(schema_, references_, path, value) case 'Record': - return yield* TRecord(schema_, references_, path, value) + return yield* FromRecord(schema_, references_, path, value) case 'Ref': - return yield* TRef(schema_, references_, path, value) + return yield* FromRef(schema_, references_, path, value) + case 'RegExp': + return yield* FromRegExp(schema_, references_, path, value) case 'String': - return yield* TString(schema_, references_, path, value) + return yield* FromString(schema_, references_, path, value) case 'Symbol': - return yield* TSymbol(schema_, references_, path, value) + return yield* FromSymbol(schema_, references_, path, value) case 'TemplateLiteral': - return yield* TTemplateLiteral(schema_, references_, path, value) + return yield* FromTemplateLiteral(schema_, references_, path, value) case 'This': - return yield* TThis(schema_, references_, path, value) + return yield* FromThis(schema_, references_, path, value) case 'Tuple': - return yield* TTuple(schema_, references_, path, value) + return yield* FromTuple(schema_, references_, path, value) case 'Undefined': - return yield* TUndefined(schema_, references_, path, value) + return yield* FromUndefined(schema_, references_, path, value) case 'Union': - return yield* TUnion(schema_, references_, path, value) + return yield* FromUnion(schema_, references_, path, value) case 'Uint8Array': - return yield* TUint8Array(schema_, references_, path, value) + return yield* FromUint8Array(schema_, references_, path, value) case 'Unknown': - return yield* TUnknown(schema_, references_, path, value) + return yield* FromUnknown(schema_, references_, path, value) case 'Void': - return yield* TVoid(schema_, references_, path, value) + return yield* FromVoid(schema_, references_, path, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) - return yield* TKind(schema_, references_, path, value) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueErrorsUnknownTypeError(schema) + return yield* FromKind(schema_, references_, path, value) } } /** Returns an iterator for each error in this value. */ -export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrorIterator +export function Errors(schema: T, references: TSchema[], value: unknown): ValueErrorIterator /** Returns an iterator for each error in this value. */ -export function Errors(schema: T, value: unknown): ValueErrorIterator +export function Errors(schema: T, value: unknown): ValueErrorIterator /** Returns an iterator for each error in this value. */ export function Errors(...args: any[]) { const iterator = args.length === 3 ? Visit(args[0], args[1], '', args[2]) : Visit(args[0], [], '', args[1]) diff --git a/src/errors/function.ts b/src/errors/function.ts new file mode 100644 index 000000000..bba4ff53b --- /dev/null +++ b/src/errors/function.ts @@ -0,0 +1,193 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/system + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema } from '../type/schema/index' +import { Kind } from '../type/symbols/index' +import { ValueErrorType } from './errors' + +/** Creates an error message using en-US as the default locale */ +export function DefaultErrorFunction(error: ErrorFunctionParameter) { + switch (error.errorType) { + case ValueErrorType.ArrayContains: + return 'Expected array to contain at least one matching value' + case ValueErrorType.ArrayMaxContains: + return `Expected array to contain no more than ${error.schema.maxContains} matching values` + case ValueErrorType.ArrayMinContains: + return `Expected array to contain at least ${error.schema.minContains} matching values` + case ValueErrorType.ArrayMaxItems: + return `Expected array length to be less or equal to ${error.schema.maxItems}` + case ValueErrorType.ArrayMinItems: + return `Expected array length to be greater or equal to ${error.schema.minItems}` + case ValueErrorType.ArrayUniqueItems: + return 'Expected array elements to be unique' + case ValueErrorType.Array: + return 'Expected array' + case ValueErrorType.AsyncIterator: + return 'Expected AsyncIterator' + case ValueErrorType.BigIntExclusiveMaximum: + return `Expected bigint to be less than ${error.schema.exclusiveMaximum}` + case ValueErrorType.BigIntExclusiveMinimum: + return `Expected bigint to be greater than ${error.schema.exclusiveMinimum}` + case ValueErrorType.BigIntMaximum: + return `Expected bigint to be less or equal to ${error.schema.maximum}` + case ValueErrorType.BigIntMinimum: + return `Expected bigint to be greater or equal to ${error.schema.minimum}` + case ValueErrorType.BigIntMultipleOf: + return `Expected bigint to be a multiple of ${error.schema.multipleOf}` + case ValueErrorType.BigInt: + return 'Expected bigint' + case ValueErrorType.Boolean: + return 'Expected boolean' + case ValueErrorType.DateExclusiveMinimumTimestamp: + return `Expected Date timestamp to be greater than ${error.schema.exclusiveMinimumTimestamp}` + case ValueErrorType.DateExclusiveMaximumTimestamp: + return `Expected Date timestamp to be less than ${error.schema.exclusiveMaximumTimestamp}` + case ValueErrorType.DateMinimumTimestamp: + return `Expected Date timestamp to be greater or equal to ${error.schema.minimumTimestamp}` + case ValueErrorType.DateMaximumTimestamp: + return `Expected Date timestamp to be less or equal to ${error.schema.maximumTimestamp}` + case ValueErrorType.DateMultipleOfTimestamp: + return `Expected Date timestamp to be a multiple of ${error.schema.multipleOfTimestamp}` + case ValueErrorType.Date: + return 'Expected Date' + case ValueErrorType.Function: + return 'Expected function' + case ValueErrorType.IntegerExclusiveMaximum: + return `Expected integer to be less than ${error.schema.exclusiveMaximum}` + case ValueErrorType.IntegerExclusiveMinimum: + return `Expected integer to be greater than ${error.schema.exclusiveMinimum}` + case ValueErrorType.IntegerMaximum: + return `Expected integer to be less or equal to ${error.schema.maximum}` + case ValueErrorType.IntegerMinimum: + return `Expected integer to be greater or equal to ${error.schema.minimum}` + case ValueErrorType.IntegerMultipleOf: + return `Expected integer to be a multiple of ${error.schema.multipleOf}` + case ValueErrorType.Integer: + return 'Expected integer' + case ValueErrorType.IntersectUnevaluatedProperties: + return 'Unexpected property' + case ValueErrorType.Intersect: + return 'Expected all values to match' + case ValueErrorType.Iterator: + return 'Expected Iterator' + case ValueErrorType.Literal: + return `Expected ${typeof error.schema.const === 'string' ? `'${error.schema.const}'` : error.schema.const}` + case ValueErrorType.Never: + return 'Never' + case ValueErrorType.Not: + return 'Value should not match' + case ValueErrorType.Null: + return 'Expected null' + case ValueErrorType.NumberExclusiveMaximum: + return `Expected number to be less than ${error.schema.exclusiveMaximum}` + case ValueErrorType.NumberExclusiveMinimum: + return `Expected number to be greater than ${error.schema.exclusiveMinimum}` + case ValueErrorType.NumberMaximum: + return `Expected number to be less or equal to ${error.schema.maximum}` + case ValueErrorType.NumberMinimum: + return `Expected number to be greater or equal to ${error.schema.minimum}` + case ValueErrorType.NumberMultipleOf: + return `Expected number to be a multiple of ${error.schema.multipleOf}` + case ValueErrorType.Number: + return 'Expected number' + case ValueErrorType.Object: + return 'Expected object' + case ValueErrorType.ObjectAdditionalProperties: + return 'Unexpected property' + case ValueErrorType.ObjectMaxProperties: + return `Expected object to have no more than ${error.schema.maxProperties} properties` + case ValueErrorType.ObjectMinProperties: + return `Expected object to have at least ${error.schema.minProperties} properties` + case ValueErrorType.ObjectRequiredProperty: + return 'Required property' + case ValueErrorType.Promise: + return 'Expected Promise' + case ValueErrorType.RegExp: + return 'Expected string to match regular expression' + case ValueErrorType.StringFormatUnknown: + return `Unknown format '${error.schema.format}'` + case ValueErrorType.StringFormat: + return `Expected string to match '${error.schema.format}' format` + case ValueErrorType.StringMaxLength: + return `Expected string length less or equal to ${error.schema.maxLength}` + case ValueErrorType.StringMinLength: + return `Expected string length greater or equal to ${error.schema.minLength}` + case ValueErrorType.StringPattern: + return `Expected string to match '${error.schema.pattern}'` + case ValueErrorType.String: + return 'Expected string' + case ValueErrorType.Symbol: + return 'Expected symbol' + case ValueErrorType.TupleLength: + return `Expected tuple to have ${error.schema.maxItems || 0} elements` + case ValueErrorType.Tuple: + return 'Expected tuple' + case ValueErrorType.Uint8ArrayMaxByteLength: + return `Expected byte length less or equal to ${error.schema.maxByteLength}` + case ValueErrorType.Uint8ArrayMinByteLength: + return `Expected byte length greater or equal to ${error.schema.minByteLength}` + case ValueErrorType.Uint8Array: + return 'Expected Uint8Array' + case ValueErrorType.Undefined: + return 'Expected undefined' + case ValueErrorType.Union: + return 'Expected union value' + case ValueErrorType.Void: + return 'Expected void' + case ValueErrorType.Kind: + return `Expected kind '${error.schema[Kind]}'` + default: + return 'Unknown error type' + } +} + +// ------------------------------------------------------------------ +// ErrorFunction +// ------------------------------------------------------------------ +export type ErrorFunctionParameter = { + /** The type of validation error */ + errorType: ValueErrorType + /** The path of the error */ + path: string + /** The schema associated with the error */ + schema: TSchema + /** The value associated with the error */ + value: unknown +} +export type ErrorFunction = (parameter: ErrorFunctionParameter) => string +/** Manages error message providers */ +let errorFunction: ErrorFunction = DefaultErrorFunction + +/** Sets the error function used to generate error messages. */ +export function SetErrorFunction(callback: ErrorFunction) { + errorFunction = callback +} +/** Gets the error function used to generate error messages */ +export function GetErrorFunction(): ErrorFunction { + return errorFunction +} diff --git a/src/errors/index.ts b/src/errors/index.ts index 71cdce9af..b5a10ef10 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -26,4 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './errors' +export { Errors, ValueError, ValueErrorIterator, ValueErrorType, ValueErrorsUnknownTypeError } from './errors' +export { DefaultErrorFunction, GetErrorFunction, SetErrorFunction, type ErrorFunction } from './function' diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..6c243906b --- /dev/null +++ b/src/index.ts @@ -0,0 +1,117 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Infrastructure +// ------------------------------------------------------------------ +export { Kind, Hint, ReadonlyKind, OptionalKind, TransformKind } from './type/symbols/index' +export { PatternBoolean, PatternBooleanExact, PatternNumber, PatternNumberExact, PatternString, PatternStringExact } from './type/patterns/index' +export { TypeRegistry, FormatRegistry } from './type/registry/index' +export { TypeGuard, ValueGuard } from './type/guard/index' +export { CloneType, CloneRest } from './type/clone/type' +export { TypeBoxError } from './type/error/index' +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +export { Any, type TAny } from './type/any/index' +export { Array, type TArray, type ArrayOptions } from './type/array/index' +export { AsyncIterator, type TAsyncIterator } from './type/async-iterator/index' +export { Awaited, type TAwaited } from './type/awaited/index' +export { BigInt, type TBigInt, type BigIntOptions } from './type/bigint/index' +export { Boolean, type TBoolean } from './type/boolean/index' +export { Composite, type TComposite } from './type/composite/index' +export { Const, type TConst } from './type/const/index' +export { Constructor, type TConstructor } from './type/constructor/index' +export { ConstructorParameters, type TConstructorParameters } from './type/constructor-parameters/index' +export { Date, type TDate, type DateOptions } from './type/date/index' +export { Deref, type TDeref } from './type/deref/index' +export { Enum, type TEnum } from './type/enum/index' +export { Exclude, type TExclude, type TExcludeFromMappedResult } from './type/exclude/index' +export { Extends, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck } from './type/extends/index' +export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index' +export { Function, type TFunction } from './type/function/index' +export { Increment, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' +export { Index, IndexPropertyKeys, IndexFromMappedKey, IndexFromMappedResult, type TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult } from './type/indexed/index' +export { InstanceType, type TInstanceType } from './type/instance-type/index' +export { Integer, type TInteger, type IntegerOptions } from './type/integer/index' +export { Intersect, IntersectEvaluated, type TIntersect, type TIntersectEvaluated, type IntersectOptions } from './type/intersect/index' +export { Iterator, type TIterator } from './type/iterator/index' +export { Intrinsic, IntrinsicFromMappedKey, type TIntrinsic, Capitalize, type TCapitalize, Lowercase, type TLowercase, Uncapitalize, type TUncapitalize, Uppercase, type TUppercase } from './type/intrinsic/index' +export { KeyOf, type TKeyOf, type KeyOfFromMappedResult, KeyOfPropertyKeys, KeyOfPattern } from './type/keyof/index' +export { Literal, type TLiteral } from './type/literal/index' +export { Mapped, MappedKey, MappedResult, type TMapped, type TMappedKey, type TMappedResult, type TMappedFunction } from './type/mapped/index' +export { Never, type TNever } from './type/never/index' +export { Not, type TNot } from './type/not/index' +export { Null, type TNull } from './type/null/index' +export { Number, type TNumber, type NumberOptions } from './type/number/index' +export { Object, type TObject, type TProperties, type ObjectOptions } from './type/object/index' +export { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' +export { Optional, OptionalFromMappedResult, type TOptional, type TOptionalWithFlag, type TOptionalFromMappedResult } from './type/optional/index' +export { Parameters, type TParameters } from './type/parameters/index' +export { Partial, PartialFromMappedResult, type TPartial, type TPartialFromMappedResult } from './type/partial/index' +export { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' +export { Promise, type TPromise } from './type/promise/index' +export { Readonly, ReadonlyFromMappedResult, type TReadonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from './type/readonly/index' +export { ReadonlyOptional, type TReadonlyOptional } from './type/readonly-optional/index' +export { Record, type TRecord } from './type/record/index' +export { Recursive, type TRecursive, type TThis } from './type/recursive/index' +export { Ref, type TRef } from './type/ref/index' +export { RegExp, type TRegExp } from './type/regexp/index' +export { Required, type TRequired, type TRequiredFromMappedResult } from './type/required/index' +export { Rest, type TRest } from './type/rest/index' +export { ReturnType, type TReturnType } from './type/return-type/index' +export { type TSchema, type TKind, type SchemaOptions, type TAnySchema } from './type/schema/index' +export { type Static, type StaticDecode, type StaticEncode } from './type/static/index' +export { Strict } from './type/strict/index' +export { String, type TString, type StringOptions, type StringFormatOption, type StringContentEncodingOption } from './type/string/index' +export { Symbol, type TSymbol, type TSymbolValue as SymbolValue } from './type/symbol/index' +export { + TemplateLiteral, + IsTemplateLiteralFinite, + IsTemplateLiteralExpressionFinite, + TemplateLiteralParse, + TemplateLiteralParseExact, + TemplateLiteralGenerate, + TemplateLiteralExpressionGenerate, + type TTemplateLiteral, + type TIsTemplateLiteralFinite, + type TTemplateLiteralGenerate, + type TTemplateLiteralKind, +} from './type/template-literal/index' +export { Transform, TransformDecodeBuilder, TransformEncodeBuilder, type TTransform, type TransformOptions, type TransformFunction } from './type/transform/index' +export { Tuple, type TTuple } from './type/tuple/index' +export { Uint8Array, type TUint8Array, type Uint8ArrayOptions } from './type/uint8array/index' +export { Undefined, type TUndefined } from './type/undefined/index' +export { Union, UnionEvaluated, type TUnion, type TUnionEvaluated } from './type/union/index' +export { Unknown, type TUnknown } from './type/unknown/index' +export { Unsafe, type TUnsafe } from './type/unsafe/index' +export { Void, type TVoid } from './type/void/index' +// ------------------------------------------------------------------ +// Namespace +// ------------------------------------------------------------------ +export { Type, JsonTypeBuilder, JavaScriptTypeBuilder } from './type/type/index' diff --git a/src/system/index.ts b/src/system/index.ts index 21c978add..fe79f6d08 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -26,5 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { ValueErrorType } from '../errors/errors' -export * from './system' +export { TypeSystemPolicy } from './policy' +export { TypeSystem, TypeSystemDuplicateFormat, TypeSystemDuplicateTypeKind } from './system' diff --git a/src/system/policy.ts b/src/system/policy.ts new file mode 100644 index 000000000..04c4a4ec2 --- /dev/null +++ b/src/system/policy.ts @@ -0,0 +1,67 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/system + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard/index' + +export namespace TypeSystemPolicy { + // ------------------------------------------------------------------ + // TypeSystemPolicy + // ------------------------------------------------------------------ + /** Shared assertion routines used by the value and errors modules */ + /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ + export let ExactOptionalPropertyTypes: boolean = false + /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ + export let AllowArrayObject: boolean = false + /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ + export let AllowNaN: boolean = false + /** Sets whether `null` should validate for void types. The default is `false` */ + export let AllowNullVoid: boolean = false + /** Asserts this value using the ExactOptionalPropertyTypes policy */ + export function IsExactOptionalProperty(value: Record, key: string) { + return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined + } + /** Asserts this value using the AllowArrayObjects policy */ + export function IsObjectLike(value: unknown): value is Record { + const isObject = IsObject(value) + return AllowArrayObject ? isObject : isObject && !IsArray(value) + } + /** Asserts this value as a record using the AllowArrayObjects policy */ + export function IsRecordLike(value: unknown): value is Record { + return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array) + } + /** Asserts this value using the AllowNaN policy */ + export function IsNumberLike(value: unknown): value is number { + const isNumber = IsNumber(value) + return AllowNaN ? isNumber : isNumber && Number.isFinite(value) + } + /** Asserts this value using the AllowVoidNull policy */ + export function IsVoidLike(value: unknown): value is void { + const isUndefined = IsUndefined(value) + return AllowNullVoid ? isUndefined || value === null : isUndefined + } +} diff --git a/src/system/system.ts b/src/system/system.ts index 528286259..0c1535b05 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,234 +26,39 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard' -import { ValueErrorType } from '../errors/errors' -import * as Types from '../typebox' +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { Unsafe } from '../type/unsafe/index' +import { Kind } from '../type/symbols/index' +import { TypeBoxError } from '../type/error/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class TypeSystemDuplicateTypeKind extends Types.TypeBoxError { +// ------------------------------------------------------------------ +export class TypeSystemDuplicateTypeKind extends TypeBoxError { constructor(kind: string) { super(`Duplicate type kind '${kind}' detected`) } } -export class TypeSystemDuplicateFormat extends Types.TypeBoxError { +export class TypeSystemDuplicateFormat extends TypeBoxError { constructor(kind: string) { super(`Duplicate string format '${kind}' detected`) } } -// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeSystem -// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { /** Creates a new type */ export function Type>(kind: string, check: (options: Options, value: unknown) => boolean) { - if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) - Types.TypeRegistry.Set(kind, check) - return (options: Partial = {}) => Types.Type.Unsafe({ ...options, [Types.Kind]: kind }) + if (TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) + TypeRegistry.Set(kind, check) + return (options: Partial = {}) => Unsafe({ ...options, [Kind]: kind }) } /** Creates a new string format */ export function Format(format: F, check: (value: string) => boolean): F { - if (Types.FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format) - Types.FormatRegistry.Set(format, check) + if (FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format) + FormatRegistry.Set(format, check) return format } } -// -------------------------------------------------------------------------- -// TypeSystemErrorFunction -// -------------------------------------------------------------------------- -/** Manages error message providers */ -export namespace TypeSystemErrorFunction { - let errorMessageFunction: ErrorFunction = DefaultErrorFunction - /** Resets the error message function to en-us */ - export function Reset() { - errorMessageFunction = DefaultErrorFunction - } - /** Sets the error message function used to generate error messages */ - export function Set(callback: ErrorFunction) { - errorMessageFunction = callback - } - /** Gets the error message function */ - export function Get(): ErrorFunction { - return errorMessageFunction - } -} -// -------------------------------------------------------------------------- -// TypeSystemPolicy -// -------------------------------------------------------------------------- -/** Shared assertion routines used by the value and errors modules */ -export namespace TypeSystemPolicy { - /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ - export let ExactOptionalPropertyTypes: boolean = false - /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ - export let AllowArrayObject: boolean = false - /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ - export let AllowNaN: boolean = false - /** Sets whether `null` should validate for void types. The default is `false` */ - export let AllowNullVoid: boolean = false - /** Asserts this value using the ExactOptionalPropertyTypes policy */ - export function IsExactOptionalProperty(value: Record, key: string) { - return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined - } - /** Asserts this value using the AllowArrayObjects policy */ - export function IsObjectLike(value: unknown): value is Record { - const isObject = IsObject(value) - return AllowArrayObject ? isObject : isObject && !IsArray(value) - } - /** Asserts this value as a record using the AllowArrayObjects policy */ - export function IsRecordLike(value: unknown): value is Record { - return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array) - } - /** Asserts this value using the AllowNaN policy */ - export function IsNumberLike(value: unknown): value is number { - const isNumber = IsNumber(value) - return AllowNaN ? isNumber : isNumber && Number.isFinite(value) - } - /** Asserts this value using the AllowVoidNull policy */ - export function IsVoidLike(value: unknown): value is void { - const isUndefined = IsUndefined(value) - return AllowNullVoid ? isUndefined || value === null : isUndefined - } -} -// -------------------------------------------------------------------------- -// ErrorFunction -// -------------------------------------------------------------------------- -export type ErrorFunction = (schema: Types.TSchema, type: ValueErrorType) => string -// -------------------------------------------------------------------------- -// DefaultErrorFunction -// -------------------------------------------------------------------------- -/** Creates an error message using en-US as the default locale */ -export function DefaultErrorFunction(schema: Types.TSchema, errorType: ValueErrorType) { - switch (errorType) { - case ValueErrorType.ArrayContains: - return 'Expected array to contain at least one matching value' - case ValueErrorType.ArrayMaxContains: - return `Expected array to contain no more than ${schema.maxContains} matching values` - case ValueErrorType.ArrayMinContains: - return `Expected array to contain at least ${schema.minContains} matching values` - case ValueErrorType.ArrayMaxItems: - return `Expected array length to be less or equal to ${schema.maxItems}` - case ValueErrorType.ArrayMinItems: - return `Expected array length to be greater or equal to ${schema.minItems}` - case ValueErrorType.ArrayUniqueItems: - return 'Expected array elements to be unique' - case ValueErrorType.Array: - return 'Expected array' - case ValueErrorType.AsyncIterator: - return 'Expected AsyncIterator' - case ValueErrorType.BigIntExclusiveMaximum: - return `Expected bigint to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.BigIntExclusiveMinimum: - return `Expected bigint to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.BigIntMaximum: - return `Expected bigint to be less or equal to ${schema.maximum}` - case ValueErrorType.BigIntMinimum: - return `Expected bigint to be greater or equal to ${schema.minimum}` - case ValueErrorType.BigIntMultipleOf: - return `Expected bigint to be a multiple of ${schema.multipleOf}` - case ValueErrorType.BigInt: - return 'Expected bigint' - case ValueErrorType.Boolean: - return 'Expected boolean' - case ValueErrorType.DateExclusiveMinimumTimestamp: - return `Expected Date timestamp to be greater than ${schema.exclusiveMinimumTimestamp}` - case ValueErrorType.DateExclusiveMaximumTimestamp: - return `Expected Date timestamp to be less than ${schema.exclusiveMaximumTimestamp}` - case ValueErrorType.DateMinimumTimestamp: - return `Expected Date timestamp to be greater or equal to ${schema.minimumTimestamp}` - case ValueErrorType.DateMaximumTimestamp: - return `Expected Date timestamp to be less or equal to ${schema.maximumTimestamp}` - case ValueErrorType.DateMultipleOfTimestamp: - return `Expected Date timestamp to be a multiple of ${schema.multipleOfTimestamp}` - case ValueErrorType.Date: - return 'Expected Date' - case ValueErrorType.Function: - return 'Expected function' - case ValueErrorType.IntegerExclusiveMaximum: - return `Expected integer to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.IntegerExclusiveMinimum: - return `Expected integer to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.IntegerMaximum: - return `Expected integer to be less or equal to ${schema.maximum}` - case ValueErrorType.IntegerMinimum: - return `Expected integer to be greater or equal to ${schema.minimum}` - case ValueErrorType.IntegerMultipleOf: - return `Expected integer to be a multiple of ${schema.multipleOf}` - case ValueErrorType.Integer: - return 'Expected integer' - case ValueErrorType.IntersectUnevaluatedProperties: - return 'Unexpected property' - case ValueErrorType.Intersect: - return 'Expected all values to match' - case ValueErrorType.Iterator: - return 'Expected Iterator' - case ValueErrorType.Literal: - return `Expected ${typeof schema.const === 'string' ? `'${schema.const}'` : schema.const}` - case ValueErrorType.Never: - return 'Never' - case ValueErrorType.Not: - return 'Value should not match' - case ValueErrorType.Null: - return 'Expected null' - case ValueErrorType.NumberExclusiveMaximum: - return `Expected number to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.NumberExclusiveMinimum: - return `Expected number to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.NumberMaximum: - return `Expected number to be less or equal to ${schema.maximum}` - case ValueErrorType.NumberMinimum: - return `Expected number to be greater or equal to ${schema.minimum}` - case ValueErrorType.NumberMultipleOf: - return `Expected number to be a multiple of ${schema.multipleOf}` - case ValueErrorType.Number: - return 'Expected number' - case ValueErrorType.Object: - return 'Expected object' - case ValueErrorType.ObjectAdditionalProperties: - return 'Unexpected property' - case ValueErrorType.ObjectMaxProperties: - return `Expected object to have no more than ${schema.maxProperties} properties` - case ValueErrorType.ObjectMinProperties: - return `Expected object to have at least ${schema.minProperties} properties` - case ValueErrorType.ObjectRequiredProperty: - return 'Required property' - case ValueErrorType.Promise: - return 'Expected Promise' - case ValueErrorType.StringFormatUnknown: - return `Unknown format '${schema.format}'` - case ValueErrorType.StringFormat: - return `Expected string to match '${schema.format}' format` - case ValueErrorType.StringMaxLength: - return `Expected string length less or equal to ${schema.maxLength}` - case ValueErrorType.StringMinLength: - return `Expected string length greater or equal to ${schema.minLength}` - case ValueErrorType.StringPattern: - return `Expected string to match '${schema.pattern}'` - case ValueErrorType.String: - return 'Expected string' - case ValueErrorType.Symbol: - return 'Expected symbol' - case ValueErrorType.TupleLength: - return `Expected tuple to have ${schema.maxItems || 0} elements` - case ValueErrorType.Tuple: - return 'Expected tuple' - case ValueErrorType.Uint8ArrayMaxByteLength: - return `Expected byte length less or equal to ${schema.maxByteLength}` - case ValueErrorType.Uint8ArrayMinByteLength: - return `Expected byte length greater or equal to ${schema.minByteLength}` - case ValueErrorType.Uint8Array: - return 'Expected Uint8Array' - case ValueErrorType.Undefined: - return 'Expected undefined' - case ValueErrorType.Union: - return 'Expected union value' - case ValueErrorType.Void: - return 'Expected void' - case ValueErrorType.Kind: - return `Expected kind '${schema[Types.Kind]}'` - default: - return 'Unknown error type' - } -} diff --git a/src/tsconfig.json b/src/tsconfig.json index c78307cfa..e53e3ebf1 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "value/index.ts", "typebox.ts"] + "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] } diff --git a/src/type/any/any.ts b/src/type/any/any.ts new file mode 100644 index 000000000..e462db9f5 --- /dev/null +++ b/src/type/any/any.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TAny extends TSchema { + [Kind]: 'Any' + static: any +} + +/** `[Json]` Creates an Any type */ +export function Any(options: SchemaOptions = {}): TAny { + return { ...options, [Kind]: 'Any' } as unknown as TAny +} diff --git a/src/type/any/index.ts b/src/type/any/index.ts new file mode 100644 index 000000000..29688e122 --- /dev/null +++ b/src/type/any/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './any' diff --git a/src/type/array/array.ts b/src/type/array/array.ts new file mode 100644 index 000000000..1165e6268 --- /dev/null +++ b/src/type/array/array.ts @@ -0,0 +1,62 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +export interface ArrayOptions extends SchemaOptions { + /** The minimum number of items in this array */ + minItems?: number + /** The maximum number of items in this array */ + maxItems?: number + /** Should this schema contain unique items */ + uniqueItems?: boolean + /** A schema for which some elements should match */ + contains?: TSchema + /** A minimum number of contains schema matches */ + minContains?: number + /** A maximum number of contains schema matches */ + maxContains?: number +} +export interface TArray extends TSchema, ArrayOptions { + [Kind]: 'Array' + static: Array> + type: 'array' + items: T +} +/** `[Json]` Creates an Array type */ +export function Array(schema: T, options: ArrayOptions = {}): TArray { + return { + ...options, + [Kind]: 'Array', + type: 'array', + items: CloneType(schema), + } as unknown as TArray +} diff --git a/src/type/array/index.ts b/src/type/array/index.ts new file mode 100644 index 000000000..ae02b63c0 --- /dev/null +++ b/src/type/array/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './array' diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts new file mode 100644 index 000000000..9ee5f8755 --- /dev/null +++ b/src/type/async-iterator/async-iterator.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +export interface TAsyncIterator extends TSchema { + [Kind]: 'AsyncIterator' + static: AsyncIterableIterator> + type: 'AsyncIterator' + items: T +} +/** `[JavaScript]` Creates a AsyncIterator type */ +export function AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { + return { + ...options, + [Kind]: 'AsyncIterator', + type: 'AsyncIterator', + items: CloneType(items), + } as unknown as TAsyncIterator +} diff --git a/src/type/async-iterator/index.ts b/src/type/async-iterator/index.ts new file mode 100644 index 000000000..d6bc7cb22 --- /dev/null +++ b/src/type/async-iterator/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './async-iterator' diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts new file mode 100644 index 000000000..8eec616c4 --- /dev/null +++ b/src/type/awaited/awaited.ts @@ -0,0 +1,105 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Union, type TUnion } from '../union/index' +import { type TPromise } from '../promise/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsIntersect, IsUnion, IsPromise } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +// prettier-ignore +function FromRest(T: [...T]) : TFromRest { + return T.map(L => FromSchema(L)) as TFromRest +} +// ---------------------------------------------------------------- +// FromIntersect +// ---------------------------------------------------------------- +// prettier-ignore +type TFromIntersect = TIntersect> +// prettier-ignore +function FromIntersect(T: [...T]): TFromIntersect { + return Intersect(FromRest(T) as TSchema[]) as unknown as TFromIntersect +} +// ---------------------------------------------------------------- +// FromUnion +// ---------------------------------------------------------------- +// prettier-ignore +type TFromUnion = TUnion> +// prettier-ignore +function FromUnion(T: [...T]): TFromUnion { + return Union(FromRest(T) as TSchema[]) as unknown as TFromUnion +} +// ---------------------------------------------------------------- +// Promise +// ---------------------------------------------------------------- +type TFromPromise = TFromSchema +// prettier-ignore +function FromPromise(T: T): TFromPromise { + return FromSchema(T) as TFromPromise +} +// ---------------------------------------------------------------- +// FromSchema +// ---------------------------------------------------------------- +// prettier-ignore +type TFromSchema = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TPromise ? TFromSchema : + T +// prettier-ignore +function FromSchema(T: T): TFromSchema { + return ( + IsIntersect(T) ? FromIntersect(T.allOf) : + IsUnion(T) ? FromUnion(T.anyOf) : + IsPromise(T) ? FromPromise(T.item) : + T + ) as TFromSchema +} +// ------------------------------------------------------------------ +// TAwaited +// ------------------------------------------------------------------ +// prettier-ignore +export type TAwaited = ( + TFromSchema +) +/** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ +export function Awaited(T: T, options: SchemaOptions = {}): TFromSchema { + return CloneType(FromSchema(T), options) +} diff --git a/src/type/awaited/index.ts b/src/type/awaited/index.ts new file mode 100644 index 000000000..26a23a571 --- /dev/null +++ b/src/type/awaited/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './awaited' diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts new file mode 100644 index 000000000..53b0fb28d --- /dev/null +++ b/src/type/bigint/bigint.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface BigIntOptions extends SchemaOptions { + exclusiveMaximum?: bigint + exclusiveMinimum?: bigint + maximum?: bigint + minimum?: bigint + multipleOf?: bigint +} +export interface TBigInt extends TSchema, BigIntOptions { + [Kind]: 'BigInt' + static: bigint + type: 'bigint' +} +/** `[JavaScript]` Creates a BigInt type */ +export function BigInt(options: BigIntOptions = {}): TBigInt { + return { + ...options, + [Kind]: 'BigInt', + type: 'bigint', + } as TBigInt +} diff --git a/src/type/bigint/index.ts b/src/type/bigint/index.ts new file mode 100644 index 000000000..ce8f4fbf9 --- /dev/null +++ b/src/type/bigint/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './bigint' diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts new file mode 100644 index 000000000..7d3fe1aed --- /dev/null +++ b/src/type/boolean/boolean.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TBoolean extends TSchema { + [Kind]: 'Boolean' + static: boolean + type: 'boolean' +} +/** `[Json]` Creates a Boolean type */ +export function Boolean(options: SchemaOptions = {}): TBoolean { + return { + ...options, + [Kind]: 'Boolean', + type: 'boolean', + } as unknown as TBoolean +} diff --git a/src/type/boolean/index.ts b/src/type/boolean/index.ts new file mode 100644 index 000000000..aad153cb3 --- /dev/null +++ b/src/type/boolean/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './boolean' diff --git a/src/type/clone/index.ts b/src/type/clone/index.ts new file mode 100644 index 000000000..44bd8cb28 --- /dev/null +++ b/src/type/clone/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as TypeClone from './type' +export * as ValueClone from './value' diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts new file mode 100644 index 000000000..08d1c3f43 --- /dev/null +++ b/src/type/clone/type.ts @@ -0,0 +1,39 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Clone } from './value' + +/** Clones a Rest */ +export function CloneRest(schemas: T): T { + return schemas.map((schema) => CloneType(schema)) as T +} +/** Clones a Type */ +export function CloneType(schema: T, options: SchemaOptions = {}): T { + return { ...Clone(schema), ...options } +} diff --git a/src/type/clone/value.ts b/src/type/clone/value.ts new file mode 100644 index 000000000..03e880482 --- /dev/null +++ b/src/type/clone/value.ts @@ -0,0 +1,62 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ValueGuard from '../guard/value' + +function ArrayType(value: unknown[]) { + return (value as any).map((value: unknown) => Visit(value as any)) +} +function DateType(value: Date) { + return new Date(value.getTime()) +} +function Uint8ArrayType(value: Uint8Array) { + return new Uint8Array(value) +} +function RegExpType(value: RegExp) { + return new RegExp(value.source, value.flags) +} +function ObjectType(value: Record) { + const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) + const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) + return { ...clonedProperties, ...clonedSymbols } +} +// prettier-ignore +function Visit(value: unknown): any { + return ( + ValueGuard.IsArray(value) ? ArrayType(value) : + ValueGuard.IsDate(value) ? DateType(value) : + ValueGuard.IsUint8Array(value) ? Uint8ArrayType(value) : + ValueGuard.IsRegExp(value) ? RegExpType(value) : + ValueGuard.IsObject(value) ? ObjectType(value) : + value + ) +} +/** Clones a value */ +export function Clone(value: T): T { + return Visit(value) +} diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts new file mode 100644 index 000000000..0bfe80434 --- /dev/null +++ b/src/type/composite/composite.ts @@ -0,0 +1,75 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { UnionToTuple, Assert, Evaluate } from '../helpers/index' +import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Index, type TIndex } from '../indexed/index' +import { KeyOfPropertyKeys } from '../keyof/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TCompositeKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TCompositeKeys = + T extends [infer L extends TObject, ...infer R extends TObject[]] + ? TCompositeKeys + : Acc +// ------------------------------------------------------------------ +// TCompositeIndex +// ------------------------------------------------------------------ +// prettier-ignore +type TCompositeIndex, K extends string[], Acc extends TProperties = {}> = + K extends [infer L extends string, ...infer R extends string[]] + ? TCompositeIndex }> + : Acc +// prettier-ignore +type TCompositeReduce = UnionToTuple> extends infer K + ? Evaluate, Assert>> + : {} // ^ indexed via intersection of T +// prettier-ignore +type TCompositeResolve = TIntersect extends TIntersect + ? TObject> + : TObject<{}> +function CompositeResolve(T: [...T]): TCompositeResolve { + const intersect: TSchema = Intersect(T, {}) + const keys = KeyOfPropertyKeys(intersect) as string[] + const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties) + return Object(properties) as TCompositeResolve +} +// ------------------------------------------------------------------ +// TComposite +// ------------------------------------------------------------------ +export type TComposite = TCompositeResolve + +/** `[Json]` Creates a Composite object type */ +export function Composite(T: [...T], options?: ObjectOptions): TComposite { + return CloneType(CompositeResolve(T) as TObject, options) as TComposite +} diff --git a/src/type/composite/index.ts b/src/type/composite/index.ts new file mode 100644 index 000000000..2fe294b71 --- /dev/null +++ b/src/type/composite/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './composite' diff --git a/src/type/const/const.ts b/src/type/const/const.ts new file mode 100644 index 000000000..b3ce2ac58 --- /dev/null +++ b/src/type/const/const.ts @@ -0,0 +1,135 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { AssertRest, Evaluate } from '../helpers/index' +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' + +import { Any, type TAny } from '../any/index' +import { BigInt, type TBigInt } from '../bigint/index' +import { Date, type TDate } from '../date/index' +import { Function as FunctionType, type TFunction } from '../function/index' +import { Literal, type TLiteral } from '../literal/index' +import { type TNever } from '../never/index' +import { Null, type TNull } from '../null/index' +import { Object, type TObject } from '../object/index' +import { Symbol, type TSymbol } from '../symbol/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Readonly, type TReadonly } from '../readonly/index' +import { Undefined, type TUndefined } from '../undefined/index' +import { Uint8Array, type TUint8Array } from '../uint8array/index' +import { Unknown, type TUnknown } from '../unknown/index' +import { TypeClone } from '../clone/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsArray, IsNumber, IsBigInt, IsUint8Array, IsDate, IsIterator, IsObject, IsAsyncIterator, IsFunction, IsUndefined, IsNull, IsSymbol, IsBoolean, IsString } from '../guard/value' +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = + T extends readonly [infer L extends unknown, ...infer R extends unknown[]] + ? [FromValue, ...TFromArray] + : T +// prettier-ignore +function FromArray(T: [...T]): TFromArray { + return T.map(L => FromValue(L, false)) as TFromArray +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties> = { + -readonly [K in keyof T]: FromValue extends infer R extends TSchema + ? TReadonly + : TReadonly +} +// prettier-ignore +function FromProperties>(value: T): TFromProperties { + return globalThis.Object.getOwnPropertyNames(value).reduce((acc, key) => { + return { ...acc, [key]: Readonly(FromValue(value[key], false)) } + }, {} as TProperties) as unknown as TFromProperties +} +// ------------------------------------------------------------------ +// ConditionalReadonly - Only applied if not root +// ------------------------------------------------------------------ +type TConditionalReadonly = Root extends true ? T : TReadonly +function ConditionalReadonly(T: T, root: Root): TConditionalReadonly { + return (root === true ? T : Readonly(T)) as unknown as TConditionalReadonly +} +// ------------------------------------------------------------------ +// FromValue +// ------------------------------------------------------------------ +// prettier-ignore +type FromValue = + T extends AsyncIterableIterator ? TConditionalReadonly : + T extends IterableIterator ? TConditionalReadonly : + T extends readonly unknown[] ? TReadonly>>> : + T extends Uint8Array ? TUint8Array : + T extends Date ? TDate : + T extends Record ? TConditionalReadonly>>, Root> : + T extends Function ? TConditionalReadonly, Root> : + T extends undefined ? TUndefined : + T extends null ? TNull : + T extends symbol ? TSymbol : + T extends number ? TLiteral : + T extends boolean ? TLiteral : + T extends string ? TLiteral : + T extends bigint ? TBigInt : + TObject<{}> +// prettier-ignore +function FromValue(value: T, root: Root): FromValue { + return ( + IsAsyncIterator(value) ? ConditionalReadonly(Any(), root) : + IsIterator(value) ? ConditionalReadonly(Any(), root) : + IsArray(value) ? Readonly(Tuple(FromArray(value) as TSchema[])) : + IsUint8Array(value) ? Uint8Array() : + IsDate(value) ? Date() : + IsObject(value) ? ConditionalReadonly(Object(FromProperties(value as Record) as TProperties), root) : + IsFunction(value) ? ConditionalReadonly(FunctionType([], Unknown()), root) : + IsUndefined(value) ? Undefined() : + IsNull(value) ? Null() : + IsSymbol(value) ? Symbol() : + IsBigInt(value) ? BigInt() : + IsNumber(value) ? Literal(value) : + IsBoolean(value) ? Literal(value) : + IsString(value) ? Literal(value) : + Object({}) + ) as FromValue +} +// ------------------------------------------------------------------ +// TConst +// ------------------------------------------------------------------ +export type TConst = FromValue + +/** `[JavaScript]` Creates a readonly const type from the given value. */ +export function Const(T: T, options: SchemaOptions = {}): TConst { + return TypeClone.CloneType(FromValue(T, true), options) as TConst +} diff --git a/src/type/const/index.ts b/src/type/const/index.ts new file mode 100644 index 000000000..f13d0f549 --- /dev/null +++ b/src/type/const/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './const' diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts new file mode 100644 index 000000000..e2cb70e19 --- /dev/null +++ b/src/type/constructor-parameters/constructor-parameters.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Ensure } from '../helpers/index' +import type { TConstructor } from '../constructor/index' +import { Tuple, type TTuple } from '../tuple/index' +import { CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +export type TConstructorParameters> = ( + Ensure> +) + +/** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ +export function ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + return Tuple(CloneRest(schema.parameters), { ...options }) +} diff --git a/src/type/constructor-parameters/index.ts b/src/type/constructor-parameters/index.ts new file mode 100644 index 000000000..23f9c265b --- /dev/null +++ b/src/type/constructor-parameters/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './constructor-parameters' diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts new file mode 100644 index 000000000..c4e37e8fc --- /dev/null +++ b/src/type/constructor/constructor.ts @@ -0,0 +1,67 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import type { Ensure } from '../helpers/index' +import { CloneType, CloneRest } from '../clone/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TConstructorStatic +// ------------------------------------------------------------------ +type ConstructorStaticReturnType = Static +// prettier-ignore +type ConstructorStaticParameters = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? ConstructorStaticParameters]> + : Acc +// prettier-ignore +type ConstructorStatic = ( + Ensure) => ConstructorStaticReturnType> +) +// ------------------------------------------------------------------ +// TConstructor +// ------------------------------------------------------------------ +export interface TConstructor extends TSchema { + [Kind]: 'Constructor' + static: ConstructorStatic + type: 'Constructor' + parameters: T + returns: U +} +/** `[JavaScript]` Creates a Constructor type */ +export function Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { + return { + ...options, + [Kind]: 'Constructor', + type: 'Constructor', + parameters: CloneRest(parameters), + returns: CloneType(returns), + } as unknown as TConstructor +} diff --git a/src/type/constructor/index.ts b/src/type/constructor/index.ts new file mode 100644 index 000000000..8e75965f7 --- /dev/null +++ b/src/type/constructor/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './constructor' diff --git a/src/type/date/date.ts b/src/type/date/date.ts new file mode 100644 index 000000000..9e45ec214 --- /dev/null +++ b/src/type/date/date.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface DateOptions extends SchemaOptions { + /** The exclusive maximum timestamp value */ + exclusiveMaximumTimestamp?: number + /** The exclusive minimum timestamp value */ + exclusiveMinimumTimestamp?: number + /** The maximum timestamp value */ + maximumTimestamp?: number + /** The minimum timestamp value */ + minimumTimestamp?: number + /** The multiple of timestamp value */ + multipleOfTimestamp?: number +} +export interface TDate extends TSchema, DateOptions { + [Kind]: 'Date' + static: Date + type: 'date' +} +/** `[JavaScript]` Creates a Date type */ +export function Date(options: DateOptions = {}): TDate { + return { + ...options, + [Kind]: 'Date', + type: 'Date', + } as unknown as TDate +} diff --git a/src/type/date/index.ts b/src/type/date/index.ts new file mode 100644 index 000000000..932ee5def --- /dev/null +++ b/src/type/date/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './date' diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts new file mode 100644 index 000000000..84fa78421 --- /dev/null +++ b/src/type/deref/deref.ts @@ -0,0 +1,174 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Evaluate } from '../helpers/index' +import type { TTuple } from '../tuple/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TPromise } from '../promise/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TIterator } from '../iterator/index' +import type { TArray } from '../array/index' +import type { TConstructor } from '../constructor/index' +import type { TFunction } from '../function/index' +import type { TRef } from '../ref/index' +import type { TObject, TProperties } from '../object/index' +import { CloneType, CloneRest } from '../clone/type' +import { Discard } from '../discard/index' +import { IsUndefined } from '../guard/value' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsPromise, IsAsyncIterator, IsIterator, IsRef } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +) +function FromRest(schema: TSchema[], references: TSchema[]) { + return schema.map((schema) => Deref(schema, references)) +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties = Evaluate<{ + [K in keyof T]: DerefResolve +}> +// prettier-ignore +function FromProperties(properties: TProperties, references: TSchema[]) { + return globalThis.Object.getOwnPropertyNames(properties).reduce((acc, key) => { + return {...acc, [key]: Deref(properties[key], references) } + }, {} as TProperties) +} +// prettier-ignore +function FromConstructor(schema: TConstructor, references: TSchema[]) { + schema.parameters = FromRest(schema.parameters, references) + schema.returns = Deref(schema.returns, references) + return schema +} +// prettier-ignore +function FromFunction(schema: TFunction, references: TSchema[]) { + schema.parameters = FromRest(schema.parameters, references) + schema.returns = Deref(schema.returns, references) + return schema +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[]) { + schema.allOf = FromRest(schema.allOf, references) + return schema +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[]) { + schema.anyOf = FromRest(schema.anyOf, references) + return schema +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[]) { + if(IsUndefined(schema.items)) return schema + schema.items = FromRest(schema.items, references) + return schema +} +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[]) { + schema.items = Deref(schema.items, references) + return schema +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[]) { + schema.properties = FromProperties(schema.properties, references) + return schema +} +// prettier-ignore +function FromPromise(schema: TPromise, references: TSchema[]) { + schema.item = Deref(schema.item, references) + return schema +} +// prettier-ignore +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { + schema.items = Deref(schema.items, references) + return schema +} +// prettier-ignore +function FromIterator(schema: TIterator, references: TSchema[]) { + schema.items = Deref(schema.items, references) + return schema +} +// prettier-ignore +function FromRef(schema: TRef, references: TSchema[]) { + const target = references.find(remote => remote.$id === schema.$ref) + if(target === undefined) throw Error(`Unable to dereference schema with $id ${schema.$ref}`) + const discard = Discard(target, ['$id']) as TSchema + return Deref(discard, references) +} +// prettier-ignore +export type DerefResolve = + T extends TConstructor ? TConstructor, DerefResolve> : + T extends TFunction ? TFunction, DerefResolve> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TTuple ? TTuple> : + T extends TObject ? TObject> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TIterator ? TIterator> : + T extends TRef ? DerefResolve : + T +// prettier-ignore +export function DerefResolve(schema: T, references: TSchema[]): TDeref { + return ( + IsConstructor(schema) ? FromConstructor(schema, references) : + IsFunction(schema) ? FromFunction(schema, references) : + IsIntersect(schema) ? FromIntersect(schema, references) : + IsUnion(schema) ? FromUnion(schema, references) : + IsTuple(schema) ? FromTuple(schema, references) : + IsArray(schema) ? FromArray(schema, references) : + IsObject(schema) ? FromObject(schema, references) : + IsPromise(schema) ? FromPromise(schema, references) : + IsAsyncIterator(schema) ? FromAsyncIterator(schema, references) : + IsIterator(schema) ? FromIterator(schema, references) : + IsRef(schema) ? FromRef(schema, references) : + schema + ) as TDeref +} +// ------------------------------------------------------------------ +// TDeref +// ------------------------------------------------------------------ +export type TDeref = DerefResolve + +/** `[Json]` Creates a dereferenced type */ +export function Deref(schema: T, references: TSchema[]): TDeref { + return DerefResolve(CloneType(schema), CloneRest(references)) +} diff --git a/src/type/deref/index.ts b/src/type/deref/index.ts new file mode 100644 index 000000000..496b4d6f0 --- /dev/null +++ b/src/type/deref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './deref' diff --git a/src/type/discard/discard.ts b/src/type/discard/discard.ts new file mode 100644 index 000000000..f531f4d49 --- /dev/null +++ b/src/type/discard/discard.ts @@ -0,0 +1,35 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +function DiscardKey(value: Record, key: PropertyKey) { + const { [key]: _, ...rest } = value + return rest +} +export function Discard(value: Record, keys: PropertyKey[]) { + return keys.reduce((acc, key) => DiscardKey(acc, key), value) +} diff --git a/src/type/discard/index.ts b/src/type/discard/index.ts new file mode 100644 index 000000000..43fb9af56 --- /dev/null +++ b/src/type/discard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './discard' diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts new file mode 100644 index 000000000..4a92aeed2 --- /dev/null +++ b/src/type/enum/enum.ts @@ -0,0 +1,58 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Literal, type TLiteral } from '../literal/index' +import { Kind, Hint } from '../symbols/index' +import { Union } from '../union/index' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsUndefined } from '../guard/value' +// ------------------------------------------------------------------ +// TEnum +// ------------------------------------------------------------------ +export type TEnumRecord = Record +export type TEnumValue = string | number +export type TEnumKey = string +export interface TEnum = Record> extends TSchema { + [Kind]: 'Union' + [Hint]: 'Enum' + static: T[keyof T] + anyOf: TLiteral[] +} +/** `[Json]` Creates a Enum type */ +export function Enum>(item: T, options: SchemaOptions = {}): TEnum { + if (IsUndefined(item)) throw new Error('Enum undefined or empty') + const values1 = globalThis.Object.getOwnPropertyNames(item) + .filter((key) => isNaN(key as any)) + .map((key) => item[key]) as T[keyof T][] + const values2 = [...new Set(values1)] + const anyOf = values2.map((value) => Literal(value)) + return Union(anyOf, { ...options, [Hint]: 'Enum' }) as unknown as TEnum +} diff --git a/src/type/enum/index.ts b/src/type/enum/index.ts new file mode 100644 index 000000000..d5dfd4a6f --- /dev/null +++ b/src/type/enum/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './enum' diff --git a/src/type/error/error.ts b/src/type/error/error.ts new file mode 100644 index 000000000..652352632 --- /dev/null +++ b/src/type/error/error.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +/** The base Error type thrown for all TypeBox exceptions */ +export class TypeBoxError extends Error { + constructor(message: string) { + super(message) + } +} diff --git a/src/type/error/index.ts b/src/type/error/index.ts new file mode 100644 index 000000000..277d5e011 --- /dev/null +++ b/src/type/error/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './error' diff --git a/src/type/exclude/exclude-from-mapped-result.ts b/src/type/exclude/exclude-from-mapped-result.ts new file mode 100644 index 000000000..858bbfd9b --- /dev/null +++ b/src/type/exclude/exclude-from-mapped-result.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Exclude, type TExclude } from './exclude' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + K extends TProperties, + T extends TSchema +> = ( + { [K2 in keyof K]: TExclude } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + T extends TSchema +>(P: P, U: T, options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Exclude(P[K2], U, options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + T extends TSchema +> = ( + TFromProperties + ) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + T extends TSchema +>(R: R, T: T, options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, T, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// ExcludeFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TExcludeFromMappedResult< + R extends TMappedResult, + T extends TSchema, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function ExcludeFromMappedResult< + R extends TMappedResult, + T extends TSchema, + P extends TProperties = TFromMappedResult +>(R: R, T: T, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, T, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts new file mode 100644 index 000000000..751e9e154 --- /dev/null +++ b/src/type/exclude/exclude.ts @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { UnionToTuple, AssertRest, AssertType, Assert } from '../helpers/index' +import type { TMappedResult } from '../mapped/index' +import { TemplateLiteralToUnion, type TTemplateLiteral } from '../template-literal/index' +import { Union, type TUnion } from '../union/index' +import { Never, type TNever } from '../never/index' +import { type TLiteral } from '../literal/index' +import { type Static } from '../static/index' +import { type TUnionEvaluated } from '../union/index' +import { ExtendsCheck, ExtendsResult } from '../extends/index' +import { CloneType } from '../clone/type' +import { ExcludeFromMappedResult, type TExcludeFromMappedResult } from './exclude-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' +// ------------------------------------------------------------------ +// ExcludeResolve +// ------------------------------------------------------------------ +// prettier-ignore +type TExcludeTemplateLiteralResult = TUnionEvaluated }[T]>>> +// prettier-ignore +type TExcludeTemplateLiteral = ( + Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never +) +// prettier-ignore +type TExcludeArray = AssertRest> extends Static ? never : T[K] +}[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never +// prettier-ignore +type TExcludeResolve = + T extends TTemplateLiteral ? TExcludeTemplateLiteral : + T extends TUnion ? TExcludeArray : + T extends U + ? TNever + : T +// prettier-ignore +function ExcludeResolve(L: L, R: R): TExcludeResolve { + return ( + IsTemplateLiteral(L) ? ExcludeResolve(TemplateLiteralToUnion(L), R) : + IsTemplateLiteral(R) ? ExcludeResolve(L, TemplateLiteralToUnion(R)) : + IsUnion(L) ? (() => { + const narrowed = L.anyOf.filter((inner) => ExtendsCheck(inner, R) === ExtendsResult.False) + return (narrowed.length === 1 ? narrowed[0] : Union(narrowed)) + })() : + ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : + L + ) as TExcludeResolve +} +// ------------------------------------------------------------------ +// TExclude +// ------------------------------------------------------------------ +export type TExclude = TExcludeResolve + +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(unionType: TSchema, excludedMembers: TSchema, options: SchemaOptions = {}) { + if (IsMappedResult(unionType)) { + return ExcludeFromMappedResult(unionType, excludedMembers, options) + } else { + const E = ExcludeResolve(unionType, excludedMembers) as any + return CloneType(E, options) + } +} diff --git a/src/type/exclude/index.ts b/src/type/exclude/index.ts new file mode 100644 index 000000000..b83e080b5 --- /dev/null +++ b/src/type/exclude/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './exclude-from-mapped-result' +export * from './exclude' diff --git a/src/type/extends/extends-check.ts b/src/type/extends/extends-check.ts new file mode 100644 index 000000000..d726d91ea --- /dev/null +++ b/src/type/extends/extends-check.ts @@ -0,0 +1,776 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { type TAny, Any } from '../any/index' +import { type TArray } from '../array/index' +import { type TAsyncIterator } from '../async-iterator/index' +import { type TBigInt } from '../bigint/index' +import { type TBoolean } from '../boolean/index' +import { type TConstructor } from '../constructor/index' +import { type TDate } from '../date/index' +import { type TFunction, Function as FunctionType } from '../function/index' +import { type TInteger } from '../integer/index' +import { type TIntersect } from '../intersect/index' +import { type TIterator } from '../iterator/index' +import { type TLiteral } from '../literal/index' +import { type TNever } from '../never/index' +import { type TNot } from '../not/index' +import { type TNull } from '../null/index' +import { type TNumber, Number } from '../number/index' +import { type TObject } from '../object/index' +import { type TPromise } from '../promise/index' +import { type TRecord } from '../record/index' +import { type TSchema } from '../schema/index' +import { type TString, String } from '../string/index' +import { type TSymbol } from '../symbol/index' +import { type TTuple } from '../tuple/index' +import { type TUint8Array } from '../uint8array/index' +import { type TUndefined } from '../undefined/index' +import { type TUnion } from '../union/index' +import { type TUnknown, Unknown } from '../unknown/index' +import { type TVoid } from '../void/index' + +import { TemplateLiteralToUnion } from '../template-literal/index' +import { PatternNumberExact, PatternStringExact } from '../patterns/index' +import { Kind, Hint } from '../symbols/index' +import { TypeBoxError } from '../error/index' +import { TypeGuard, ValueGuard } from '../guard/index' + +export class ExtendsResolverError extends TypeBoxError {} + +export enum ExtendsResult { + Union, + True, + False, +} +// ------------------------------------------------------------------ +// IntoBooleanResult +// ------------------------------------------------------------------ +// prettier-ignore +function IntoBooleanResult(result: ExtendsResult) { + return result === ExtendsResult.False ? result : ExtendsResult.True +} +// ------------------------------------------------------------------ +// Throw +// ------------------------------------------------------------------ +// prettier-ignore +function Throw(message: string): never { + throw new ExtendsResolverError(message) +} +// ------------------------------------------------------------------ +// StructuralRight +// ------------------------------------------------------------------ +// prettier-ignore +function IsStructuralRight(right: TSchema): boolean { + return ( + TypeGuard.IsNever(right) || + TypeGuard.IsIntersect(right) || + TypeGuard.IsUnion(right) || + TypeGuard.IsUnknown(right) || + TypeGuard.IsAny(right) + ) +} +// prettier-ignore +function StructuralRight(left: TSchema, right: TSchema) { + return ( + TypeGuard.IsNever(right) ? FromNeverRight(left, right) : + TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) : + TypeGuard.IsUnion(right) ? FromUnionRight(left, right) : + TypeGuard.IsUnknown(right) ? FromUnknownRight(left, right) : + TypeGuard.IsAny(right) ? FromAnyRight(left, right) : + Throw('StructuralRight') + ) +} +// ------------------------------------------------------------------ +// Any +// ------------------------------------------------------------------ +// prettier-ignore +function FromAnyRight(left: TSchema, right: TAny) { + return ExtendsResult.True +} +// prettier-ignore +function FromAny(left: TAny, right: TSchema) { + return ( + TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) : + (TypeGuard.IsUnion(right) && right.anyOf.some((schema) => TypeGuard.IsAny(schema) || TypeGuard.IsUnknown(schema))) ? ExtendsResult.True : + TypeGuard.IsUnion(right) ? ExtendsResult.Union : + TypeGuard.IsUnknown(right) ? ExtendsResult.True : + TypeGuard.IsAny(right) ? ExtendsResult.True : + ExtendsResult.Union + ) +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +function FromArrayRight(left: TSchema, right: TArray) { + return ( + TypeGuard.IsUnknown(left) ? ExtendsResult.False : + TypeGuard.IsAny(left) ? ExtendsResult.Union : + TypeGuard.IsNever(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromArray(left: TArray, right: TSchema) { + return ( + TypeGuard.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.IsArray(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +function FromAsyncIterator(left: TAsyncIterator, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.IsAsyncIterator(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// BigInt +// ------------------------------------------------------------------ +// prettier-ignore +function FromBigInt(left: TBigInt, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsBigInt(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Boolean +// ------------------------------------------------------------------ +// prettier-ignore +function FromBooleanRight(left: TSchema, right: TBoolean) { + return ( + TypeGuard.IsLiteralBoolean(left) ? ExtendsResult.True : + TypeGuard.IsBoolean(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromBoolean(left: TBoolean, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsBoolean(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +function FromConstructor(left: TConstructor, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + !TypeGuard.IsConstructor(right) ? ExtendsResult.False : + left.parameters.length > right.parameters.length ? ExtendsResult.False : + (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === ExtendsResult.True)) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.returns, right.returns)) + ) +} +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +// prettier-ignore +function FromDate(left: TDate, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsDate(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +function FromFunction(left: TFunction, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + !TypeGuard.IsFunction(right) ? ExtendsResult.False : + left.parameters.length > right.parameters.length ? ExtendsResult.False : + (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === ExtendsResult.True)) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.returns, right.returns)) + ) +} +// ------------------------------------------------------------------ +// Integer +// ------------------------------------------------------------------ +// prettier-ignore +function FromIntegerRight(left: TSchema, right: TInteger) { + return ( + TypeGuard.IsLiteral(left) && ValueGuard.IsNumber(left.const) ? ExtendsResult.True : + TypeGuard.IsNumber(left) || TypeGuard.IsInteger(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromInteger(left: TInteger, right: TSchema): ExtendsResult { + return ( + TypeGuard.IsInteger(right) || TypeGuard.IsNumber(right) ? ExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +function FromIntersectRight(left: TSchema, right: TIntersect): ExtendsResult { + return right.allOf.every((schema) => Visit(left, schema) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// prettier-ignore +function FromIntersect(left: TIntersect, right: TSchema) { + return left.allOf.some((schema) => Visit(schema, right) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +function FromIterator(left: TIterator, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.IsIterator(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +function FromLiteral(left: TLiteral, right: TSchema): ExtendsResult { + return ( + TypeGuard.IsLiteral(right) && right.const === left.const ? ExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsString(right) ? FromStringRight(left, right) : + TypeGuard.IsNumber(right) ? FromNumberRight(left, right) : + TypeGuard.IsInteger(right) ? FromIntegerRight(left, right) : + TypeGuard.IsBoolean(right) ? FromBooleanRight(left, right) : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Never +// ------------------------------------------------------------------ +// prettier-ignore +function FromNeverRight(left: TSchema, right: TNever) { + return ExtendsResult.False +} +// prettier-ignore +function FromNever(left: TNever, right: TSchema) { + return ExtendsResult.True +} +// ------------------------------------------------------------------ +// Not +// ------------------------------------------------------------------ +// prettier-ignore +function UnwrapTNot(schema: T): TUnknown | TNot['not'] { + let [current, depth]: [TSchema, number] = [schema, 0] + while (true) { + if (!TypeGuard.IsNot(current)) break + current = current.not + depth += 1 + } + return depth % 2 === 0 ? current : Unknown() +} +// prettier-ignore +function FromNot(left: TSchema, right: TSchema) { + // TypeScript has no concept of negated types, and attempts to correctly check the negated + // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer + // the type. Instead we unwrap to either unknown or T and continue evaluating. + // prettier-ignore + return ( + TypeGuard.IsNot(left) ? Visit(UnwrapTNot(left), right) : + TypeGuard.IsNot(right) ? Visit(left, UnwrapTNot(right)) : + Throw('Invalid fallthrough for Not') + ) +} +// ------------------------------------------------------------------ +// Null +// ------------------------------------------------------------------ +// prettier-ignore +function FromNull(left: TNull, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsNull(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +function FromNumberRight(left: TSchema, right: TNumber) { + return ( + TypeGuard.IsLiteralNumber(left) ? ExtendsResult.True : + TypeGuard.IsNumber(left) || TypeGuard.IsInteger(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromNumber(left: TNumber, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsInteger(right) || TypeGuard.IsNumber(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +function IsObjectPropertyCount(schema: TObject, count: number) { + return Object.getOwnPropertyNames(schema.properties).length === count +} +// prettier-ignore +function IsObjectStringLike(schema: TObject) { + return IsObjectArrayLike(schema) +} +// prettier-ignore +function IsObjectSymbolLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) || ( + IsObjectPropertyCount(schema, 1) && 'description' in schema.properties && TypeGuard.IsUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && (( + TypeGuard.IsString(schema.properties.description.anyOf[0]) && + TypeGuard.IsUndefined(schema.properties.description.anyOf[1]) + ) || ( + TypeGuard.IsString(schema.properties.description.anyOf[1]) && + TypeGuard.IsUndefined(schema.properties.description.anyOf[0]) + )) + ) +} +// prettier-ignore +function IsObjectNumberLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) +} +// prettier-ignore +function IsObjectBooleanLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) +} +// prettier-ignore +function IsObjectBigIntLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) +} +// prettier-ignore +function IsObjectDateLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) +} +// prettier-ignore +function IsObjectUint8ArrayLike(schema: TObject) { + return IsObjectArrayLike(schema) +} +// prettier-ignore +function IsObjectFunctionLike(schema: TObject) { + const length = Number() + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === ExtendsResult.True) +} +// prettier-ignore +function IsObjectConstructorLike(schema: TObject) { + return IsObjectPropertyCount(schema, 0) +} +// prettier-ignore +function IsObjectArrayLike(schema: TObject) { + const length = Number() + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === ExtendsResult.True) +} +// prettier-ignore +function IsObjectPromiseLike(schema: TObject) { + const then = FunctionType([Any()], Any()) + return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'then' in schema.properties && IntoBooleanResult(Visit(schema.properties['then'], then)) === ExtendsResult.True) +} +// ------------------------------------------------------------------ +// Property +// ------------------------------------------------------------------ +// prettier-ignore +function Property(left: TSchema, right: TSchema) { + return ( + Visit(left, right) === ExtendsResult.False ? ExtendsResult.False : + TypeGuard.IsOptional(left) && !TypeGuard.IsOptional(right) ? ExtendsResult.False : + ExtendsResult.True + ) +} +// prettier-ignore +function FromObjectRight(left: TSchema, right: TObject) { + return ( + TypeGuard.IsUnknown(left) ? ExtendsResult.False : + TypeGuard.IsAny(left) ? ExtendsResult.Union : ( + TypeGuard.IsNever(left) || + (TypeGuard.IsLiteralString(left) && IsObjectStringLike(right)) || + (TypeGuard.IsLiteralNumber(left) && IsObjectNumberLike(right)) || + (TypeGuard.IsLiteralBoolean(left) && IsObjectBooleanLike(right)) || + (TypeGuard.IsSymbol(left) && IsObjectSymbolLike(right)) || + (TypeGuard.IsBigInt(left) && IsObjectBigIntLike(right)) || + (TypeGuard.IsString(left) && IsObjectStringLike(right)) || + (TypeGuard.IsSymbol(left) && IsObjectSymbolLike(right)) || + (TypeGuard.IsNumber(left) && IsObjectNumberLike(right)) || + (TypeGuard.IsInteger(left) && IsObjectNumberLike(right)) || + (TypeGuard.IsBoolean(left) && IsObjectBooleanLike(right)) || + (TypeGuard.IsUint8Array(left) && IsObjectUint8ArrayLike(right)) || + (TypeGuard.IsDate(left) && IsObjectDateLike(right)) || + (TypeGuard.IsConstructor(left) && IsObjectConstructorLike(right)) || + (TypeGuard.IsFunction(left) && IsObjectFunctionLike(right)) + ) ? ExtendsResult.True : + (TypeGuard.IsRecord(left) && TypeGuard.IsString(RecordKey(left))) ? (() => { + // When expressing a Record with literal key values, the Record is converted into a Object with + // the Hint assigned as `Record`. This is used to invert the extends logic. + return right[Hint] === 'Record' ? ExtendsResult.True : ExtendsResult.False + })() : + (TypeGuard.IsRecord(left) && TypeGuard.IsNumber(RecordKey(left))) ? (() => { + return IsObjectPropertyCount(right, 0) ? ExtendsResult.True : ExtendsResult.False + })() : + ExtendsResult.False + ) +} +// prettier-ignore +function FromObject(left: TObject, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + !TypeGuard.IsObject(right) ? ExtendsResult.False : + (() => { + for (const key of Object.getOwnPropertyNames(right.properties)) { + if (!(key in left.properties) && !TypeGuard.IsOptional(right.properties[key])) { + return ExtendsResult.False + } + if (TypeGuard.IsOptional(right.properties[key])) { + return ExtendsResult.True + } + if (Property(left.properties[key], right.properties[key]) === ExtendsResult.False) { + return ExtendsResult.False + } + } + return ExtendsResult.True + })() + ) +} +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +function FromPromise(left: TPromise, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) && IsObjectPromiseLike(right) ? ExtendsResult.True : + !TypeGuard.IsPromise(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.item, right.item)) + ) +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +function RecordKey(schema: TRecord) { + return ( + PatternNumberExact in schema.patternProperties ? Number() : + PatternStringExact in schema.patternProperties ? String() : + Throw('Unknown record key pattern') + ) +} +// prettier-ignore +function RecordValue(schema: TRecord) { + return ( + PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : + PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : + Throw('Unable to get record value schema') + ) +} +// prettier-ignore +function FromRecordRight(left: TSchema, right: TRecord) { + const [Key, Value] = [RecordKey(right), RecordValue(right)] + return ( + ( + TypeGuard.IsLiteralString(left) && TypeGuard.IsNumber(Key) && IntoBooleanResult(Visit(left, Value)) === ExtendsResult.True) ? ExtendsResult.True : + TypeGuard.IsUint8Array(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) : + TypeGuard.IsString(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) : + TypeGuard.IsArray(left) && TypeGuard.IsNumber(Key) ? Visit(left, Value) : + TypeGuard.IsObject(left) ? (() => { + for (const key of Object.getOwnPropertyNames(left.properties)) { + if (Property(Value, left.properties[key]) === ExtendsResult.False) { + return ExtendsResult.False + } + } + return ExtendsResult.True + })() : + ExtendsResult.False + ) +} +// prettier-ignore +function FromRecord(left: TRecord, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + !TypeGuard.IsRecord(right) ? ExtendsResult.False : + Visit(RecordValue(left), RecordValue(right)) + ) +} +// ------------------------------------------------------------------ +// RegExp +// ------------------------------------------------------------------ +// prettier-ignore +function FromRegExp(left: TSchema, right: TSchema) { + // Note: RegExp types evaluate as strings, not RegExp objects. + // Here we remap either into string and continue evaluating. + const L = TypeGuard.IsRegExp(left) ? String() : left + const R = TypeGuard.IsRegExp(right) ? String() : right + return Visit(L, R) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function FromStringRight(left: TSchema, right: TString) { + return ( + TypeGuard.IsLiteral(left) && ValueGuard.IsString(left.const) ? ExtendsResult.True : + TypeGuard.IsString(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromString(left: TString, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsString(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Symbol +// ------------------------------------------------------------------ +// prettier-ignore +function FromSymbol(left: TSymbol, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsSymbol(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// TemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +function FromTemplateLiteral(left: TSchema, right: TSchema) { + // TemplateLiteral types are resolved to either unions for finite expressions or string + // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for + // either type and continue evaluating. + return ( + TypeGuard.IsTemplateLiteral(left) ? Visit(TemplateLiteralToUnion(left), right) : + TypeGuard.IsTemplateLiteral(right) ? Visit(left, TemplateLiteralToUnion(right)) : + Throw('Invalid fallthrough for TemplateLiteral') + ) +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +function IsArrayOfTuple(left: TTuple, right: TSchema) { + return ( + TypeGuard.IsArray(right) && + left.items !== undefined && + left.items.every((schema) => Visit(schema, right.items) === ExtendsResult.True) + ) +} +// prettier-ignore +function FromTupleRight(left: TSchema, right: TTuple) { + return ( + TypeGuard.IsNever(left) ? ExtendsResult.True : + TypeGuard.IsUnknown(left) ? ExtendsResult.False : + TypeGuard.IsAny(left) ? ExtendsResult.Union : + ExtendsResult.False + ) +} +// prettier-ignore +function FromTuple(left: TTuple, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : + TypeGuard.IsArray(right) && IsArrayOfTuple(left, right) ? ExtendsResult.True : + !TypeGuard.IsTuple(right) ? ExtendsResult.False : + (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items)) ? ExtendsResult.False : + (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) ? ExtendsResult.True : + left.items!.every((schema, index) => Visit(schema, right.items![index]) === ExtendsResult.True) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Uint8Array +// ------------------------------------------------------------------ +// prettier-ignore +function FromUint8Array(left: TUint8Array, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsUint8Array(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Undefined +// ------------------------------------------------------------------ +// prettier-ignore +function FromUndefined(left: TUndefined, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsRecord(right) ? FromRecordRight(left, right) : + TypeGuard.IsVoid(right) ? FromVoidRight(left, right) : + TypeGuard.IsUndefined(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +function FromUnionRight(left: TSchema, right: TUnion): ExtendsResult { + return right.anyOf.some((schema) => Visit(left, schema) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// prettier-ignore +function FromUnion(left: TUnion, right: TSchema): ExtendsResult { + return left.anyOf.every((schema) => Visit(schema, right) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// ------------------------------------------------------------------ +// Unknown +// ------------------------------------------------------------------ +// prettier-ignore +function FromUnknownRight(left: TSchema, right: TUnknown) { + return ExtendsResult.True +} +// prettier-ignore +function FromUnknown(left: TUnknown, right: TSchema) { + return ( + TypeGuard.IsNever(right) ? FromNeverRight(left, right) : + TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) : + TypeGuard.IsUnion(right) ? FromUnionRight(left, right) : + TypeGuard.IsAny(right) ? FromAnyRight(left, right) : + TypeGuard.IsString(right) ? FromStringRight(left, right) : + TypeGuard.IsNumber(right) ? FromNumberRight(left, right) : + TypeGuard.IsInteger(right) ? FromIntegerRight(left, right) : + TypeGuard.IsBoolean(right) ? FromBooleanRight(left, right) : + TypeGuard.IsArray(right) ? FromArrayRight(left, right) : + TypeGuard.IsTuple(right) ? FromTupleRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsUnknown(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Void +// ------------------------------------------------------------------ +// prettier-ignore +function FromVoidRight(left: TSchema, right: TVoid) { + return ( + TypeGuard.IsUndefined(left) ? ExtendsResult.True : + TypeGuard.IsUndefined(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function FromVoid(left: TVoid, right: TSchema) { + return ( + TypeGuard.IsIntersect(right) ? FromIntersectRight(left, right) : + TypeGuard.IsUnion(right) ? FromUnionRight(left, right) : + TypeGuard.IsUnknown(right) ? FromUnknownRight(left, right) : + TypeGuard.IsAny(right) ? FromAnyRight(left, right) : + TypeGuard.IsObject(right) ? FromObjectRight(left, right) : + TypeGuard.IsVoid(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function Visit(left: TSchema, right: TSchema): ExtendsResult { + return ( + // resolvable + (TypeGuard.IsTemplateLiteral(left) || TypeGuard.IsTemplateLiteral(right)) ? FromTemplateLiteral(left, right) : + (TypeGuard.IsRegExp(left) || TypeGuard.IsRegExp(right)) ? FromRegExp(left, right) : + (TypeGuard.IsNot(left) || TypeGuard.IsNot(right)) ? FromNot(left, right) : + // standard + TypeGuard.IsAny(left) ? FromAny(left, right) : + TypeGuard.IsArray(left) ? FromArray(left, right) : + TypeGuard.IsBigInt(left) ? FromBigInt(left, right) : + TypeGuard.IsBoolean(left) ? FromBoolean(left, right) : + TypeGuard.IsAsyncIterator(left) ? FromAsyncIterator(left, right) : + TypeGuard.IsConstructor(left) ? FromConstructor(left, right) : + TypeGuard.IsDate(left) ? FromDate(left, right) : + TypeGuard.IsFunction(left) ? FromFunction(left, right) : + TypeGuard.IsInteger(left) ? FromInteger(left, right) : + TypeGuard.IsIntersect(left) ? FromIntersect(left, right) : + TypeGuard.IsIterator(left) ? FromIterator(left, right) : + TypeGuard.IsLiteral(left) ? FromLiteral(left, right) : + TypeGuard.IsNever(left) ? FromNever(left, right) : + TypeGuard.IsNull(left) ? FromNull(left, right) : + TypeGuard.IsNumber(left) ? FromNumber(left, right) : + TypeGuard.IsObject(left) ? FromObject(left, right) : + TypeGuard.IsRecord(left) ? FromRecord(left, right) : + TypeGuard.IsString(left) ? FromString(left, right) : + TypeGuard.IsSymbol(left) ? FromSymbol(left, right) : + TypeGuard.IsTuple(left) ? FromTuple(left, right) : + TypeGuard.IsPromise(left) ? FromPromise(left, right) : + TypeGuard.IsUint8Array(left) ? FromUint8Array(left, right) : + TypeGuard.IsUndefined(left) ? FromUndefined(left, right) : + TypeGuard.IsUnion(left) ? FromUnion(left, right) : + TypeGuard.IsUnknown(left) ? FromUnknown(left, right) : + TypeGuard.IsVoid(left) ? FromVoid(left, right) : + Throw(`Unknown left type operand '${left[Kind]}'`) + ) +} +export function ExtendsCheck(left: TSchema, right: TSchema): ExtendsResult { + return Visit(left, right) +} diff --git a/src/type/extends/extends-from-mapped-key.ts b/src/type/extends/extends-from-mapped-key.ts new file mode 100644 index 000000000..acc955ee5 --- /dev/null +++ b/src/type/extends/extends-from-mapped-key.ts @@ -0,0 +1,129 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import type { Assert } from '../helpers/index' +import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Extends, type TExtends } from './extends' + +// ------------------------------------------------------------------ +// FromPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKey< + K extends PropertyKey, + U extends TSchema, + L extends TSchema, + R extends TSchema +> = { + [_ in K]: TExtends>, U, L, R> + } +// prettier-ignore +function FromPropertyKey< + K extends PropertyKey, + U extends TSchema, + L extends TSchema, + R extends TSchema +>(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKey { + return { + [K]: Extends(Literal(K as TLiteralValue), U, L, R, options) as any + } as TFromPropertyKey +} +// ------------------------------------------------------------------ +// FromPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKeys< + K extends PropertyKey[], + U extends TSchema, + L extends TSchema, + R extends TSchema, + Acc extends TProperties = {} +> = ( + K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] + ? TFromPropertyKeys> + : Acc +) +// prettier-ignore +function FromPropertyKeys< + K extends PropertyKey[], + U extends TSchema, + L extends TSchema, + R extends TSchema +>(K: [...K], U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKeys { + return K.reduce((Acc, LK) => { + return { ...Acc, ...FromPropertyKey(LK, U, L, R, options) } + }, {} as TProperties) as TFromPropertyKeys +} +// ------------------------------------------------------------------ +// FromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedKey< + K extends TMappedKey, + U extends TSchema, + L extends TSchema, + R extends TSchema +> = ( + TFromPropertyKeys + ) +// prettier-ignore +function FromMappedKey< + K extends TMappedKey, + U extends TSchema, + L extends TSchema, + R extends TSchema +>(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(K.keys, U, L, R, options) as TFromMappedKey +} +// ------------------------------------------------------------------ +// ExtendsFromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export type TExtendsFromMappedKey< + T extends TMappedKey, + U extends TSchema, + L extends TSchema, + R extends TSchema, + P extends TProperties = TFromMappedKey +> = ( + TMappedResult

      +) +// prettier-ignore +export function ExtendsFromMappedKey< + T extends TMappedKey, + U extends TSchema, + L extends TSchema, + R extends TSchema, + P extends TProperties = TFromMappedKey +>(T: T, U: U, L: L, R: R, options: SchemaOptions): TMappedResult

      { + const P = FromMappedKey(T, U, L, R, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/extends/extends-from-mapped-result.ts b/src/type/extends/extends-from-mapped-result.ts new file mode 100644 index 000000000..d34c5dc80 --- /dev/null +++ b/src/type/extends/extends-from-mapped-result.ts @@ -0,0 +1,101 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Extends, type TExtends } from './extends' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + Right extends TSchema, + False extends TSchema, + True extends TSchema +> = ( + { [K2 in keyof P]: TExtends } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + Right extends TSchema, + True extends TSchema, + False extends TSchema +>(P: P, Right: Right, True: True, False: False, options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Extends(P[K2], Right, True, False, options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + Left extends TMappedResult, + Right extends TSchema, + True extends TSchema, + False extends TSchema +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + Left extends TMappedResult, + Right extends TSchema, + True extends TSchema, + False extends TSchema +>(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TFromMappedResult { + return FromProperties(Left.properties, Right, True, False, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// ExtendsFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TExtendsFromMappedResult< + Left extends TMappedResult, + Right extends TSchema, + True extends TSchema, + False extends TSchema, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function ExtendsFromMappedResult< + Left extends TMappedResult, + Right extends TSchema, + True extends TSchema, + False extends TSchema, + P extends TProperties = TFromMappedResult +>(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(Left, Right, True, False, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/extends/extends-undefined.ts b/src/type/extends/extends-undefined.ts new file mode 100644 index 000000000..f6c275c6a --- /dev/null +++ b/src/type/extends/extends-undefined.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TNot } from '../not/index' +import { Kind } from '../symbols/index' + +/** Fast undefined check used for properties of type undefined */ +function Intersect(schema: TIntersect) { + return schema.allOf.every((schema) => ExtendsUndefinedCheck(schema)) +} +function Union(schema: TUnion) { + return schema.anyOf.some((schema) => ExtendsUndefinedCheck(schema)) +} +function Not(schema: TNot) { + return !ExtendsUndefinedCheck(schema.not) +} +/** Fast undefined check used for properties of type undefined */ +// prettier-ignore +export function ExtendsUndefinedCheck(schema: TSchema): boolean { + return ( + schema[Kind] === 'Intersect' ? Intersect(schema as TIntersect) : + schema[Kind] === 'Union' ? Union(schema as TUnion) : + schema[Kind] === 'Not' ? Not(schema as TNot) : + schema[Kind] === 'Undefined' ? true : + false + ) +} diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts new file mode 100644 index 000000000..e91a9d629 --- /dev/null +++ b/src/type/extends/extends.ts @@ -0,0 +1,80 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { type TUnion, Union } from '../union/index' +import { TMappedKey, TMappedResult } from '../mapped/index' +import { ExtendsCheck, ExtendsResult } from './extends-check' +import { UnionToTuple } from '../helpers/index' +import { CloneType } from '../clone/type' +import { ExtendsFromMappedKey, type TExtendsFromMappedKey } from './extends-from-mapped-key' +import { ExtendsFromMappedResult, type TExtendsFromMappedResult } from './extends-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedKey, IsMappedResult } from '../guard/type' + +// prettier-ignore +type TExtendsResolve = ( + (Static extends Static ? T : U) extends infer O extends TSchema ? + UnionToTuple extends [infer X extends TSchema, infer Y extends TSchema] + ? TUnion<[X, Y]> + : O + : never +) +// prettier-ignore +function ExtendsResolve(left: L, right: R, trueType: T, falseType: U): TExtendsResolve { + const R = ExtendsCheck(left, right) + return ( + R === ExtendsResult.Union ? Union([trueType, falseType]) : + R === ExtendsResult.True ? trueType : + falseType + ) as unknown as TExtendsResolve +} +// ------------------------------------------------------------------ +// TExtends +// ------------------------------------------------------------------ +export type TExtends = TExtendsResolve + +/** `[Json]` Creates a Conditional type */ +export function Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedResult +/** `[Json]` Creates a Conditional type */ +export function Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedKey +/** `[Json]` Creates a Conditional type */ +export function Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtends +/** `[Json]` Creates a Conditional type */ +export function Extends(L: L, R: R, T: T, F: F, options: SchemaOptions = {}) { + // prettier-ignore + return ( + IsMappedResult(L) ? ExtendsFromMappedResult(L, R, T, F, options) : + IsMappedKey(L) ? CloneType(ExtendsFromMappedKey(L, R, T, F, options)) : + CloneType(ExtendsResolve(L, R, T, F), options) + ) as TExtends +} diff --git a/src/type/extends/index.ts b/src/type/extends/index.ts new file mode 100644 index 000000000..330381d55 --- /dev/null +++ b/src/type/extends/index.ts @@ -0,0 +1,33 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './extends-check' +export * from './extends-from-mapped-key' +export * from './extends-from-mapped-result' +export * from './extends-undefined' +export * from './extends' diff --git a/src/type/extract/extract-from-mapped-result.ts b/src/type/extract/extract-from-mapped-result.ts new file mode 100644 index 000000000..96191929c --- /dev/null +++ b/src/type/extract/extract-from-mapped-result.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Extract, type TExtract } from './extract' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + T extends TSchema +> = ( + { [K2 in keyof P]: TExtract } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + T extends TSchema +>(P: P, T: T, options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Extract(P[K2], T, options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + T extends TSchema +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + T extends TSchema +>(R: R, T: T, options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, T, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// ExtractFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TExtractFromMappedResult< + R extends TMappedResult, + T extends TSchema, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function ExtractFromMappedResult< + R extends TMappedResult, + T extends TSchema, + P extends TProperties = TFromMappedResult +>(R: R, T: T, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, T, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts new file mode 100644 index 000000000..2ef499831 --- /dev/null +++ b/src/type/extract/extract.ts @@ -0,0 +1,94 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Assert, AssertRest, AssertType, UnionToTuple } from '../helpers/index' +import type { TMappedResult } from '../mapped/index' +import { TemplateLiteralToUnion, type TTemplateLiteral } from '../template-literal/index' +import { type TLiteral } from '../literal/index' +import { Union, type TUnion } from '../union/index' +import { type Static } from '../static/index' +import { Never } from '../never/index' +import { type TUnionEvaluated } from '../union/index' +import { ExtendsCheck, ExtendsResult } from '../extends/index' +import { CloneType } from '../clone/type' +import { ExtractFromMappedResult, type TExtractFromMappedResult } from './extract-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' +// ------------------------------------------------------------------ +// ExtractResolve +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteralResult = TUnionEvaluated }[T]>>> +// prettier-ignore +type TFromTemplateLiteral = Extract, Static> extends infer S ? TFromTemplateLiteralResult> : never +// prettier-ignore +type TFromArray = AssertRest> extends Static ? T[K] : never +}[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never +// prettier-ignore +type TExtractResolve = ( + T extends TTemplateLiteral ? TFromTemplateLiteral : + T extends TUnion ? TFromArray : + T +) +// prettier-ignore +function ExtractResolve(L: L, R: R): TExtractResolve { + return ( + IsTemplateLiteral(L) ? ExtractResolve(TemplateLiteralToUnion(L), R) : + IsTemplateLiteral(R) ? ExtractResolve(L, TemplateLiteralToUnion(R) as any) : + IsUnion(L) ? (() => { + const narrowed = L.anyOf.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False) + return (narrowed.length === 1 ? narrowed[0] : Union(narrowed)) + })() : + ExtendsCheck(L, R) !== ExtendsResult.False ? L : + Never() + ) as TExtractResolve +} +// ------------------------------------------------------------------ +// TExtract +// ------------------------------------------------------------------ +// prettier-ignore +export type TExtract = TExtractResolve + +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(type: L, union: R, options?: SchemaOptions): TExtract +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(type: TSchema, union: TSchema, options: SchemaOptions = {}) { + if (IsMappedResult(type)) { + return ExtractFromMappedResult(type, union, options) + } else { + const E = ExtractResolve(type, union) + return CloneType(E, options) + } +} diff --git a/src/type/extract/index.ts b/src/type/extract/index.ts new file mode 100644 index 000000000..3fd310772 --- /dev/null +++ b/src/type/extract/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './extract-from-mapped-result' +export * from './extract' diff --git a/src/type/function/function.ts b/src/type/function/function.ts new file mode 100644 index 000000000..7c97429a4 --- /dev/null +++ b/src/type/function/function.ts @@ -0,0 +1,67 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import type { Ensure } from '../helpers/index' +import { CloneType, CloneRest } from '../clone/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// FunctionStatic +// ------------------------------------------------------------------ +type FunctionStaticReturnType = Static +// prettier-ignore +type FunctionStaticParameters = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? FunctionStaticParameters]> + : Acc +// prettier-ignore +type FunctionStatic = ( + Ensure<(...param: FunctionStaticParameters) => FunctionStaticReturnType> +) +// ------------------------------------------------------------------ +// TFunction +// ------------------------------------------------------------------ +export interface TFunction extends TSchema { + [Kind]: 'Function' + static: FunctionStatic + type: 'Function' + parameters: T + returns: U +} +/** `[JavaScript]` Creates a Function type */ +export function Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { + return { + ...options, + [Kind]: 'Function', + type: 'Function', + parameters: CloneRest(parameters), + returns: CloneType(returns), + } as unknown as TFunction +} diff --git a/src/type/function/index.ts b/src/type/function/index.ts new file mode 100644 index 000000000..3399c7270 --- /dev/null +++ b/src/type/function/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './function' diff --git a/src/type/guard/index.ts b/src/type/guard/index.ts new file mode 100644 index 000000000..2e7cc05ad --- /dev/null +++ b/src/type/guard/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as TypeGuard from './type' +export * as ValueGuard from './value' diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts new file mode 100644 index 000000000..9794a4fcb --- /dev/null +++ b/src/type/guard/type.ts @@ -0,0 +1,618 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ValueGuard from './value' +import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' +import { TypeBoxError } from '../error/index' +import { TransformOptions } from '../transform/index' +import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' +import { TArray } from '../array/index' +import { TBoolean } from '../boolean/index' +import type { TRecord } from '../record/index' +import type { TString } from '../string/index' +import type { TUnion } from '../union/index' +import type { TAny } from '../any/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TBigInt } from '../bigint/index' +import type { TConstructor } from '../constructor/index' +import type { TFunction } from '../function/index' +import type { TInteger } from '../integer/index' +import type { TIntersect } from '../intersect/index' +import type { TIterator } from '../iterator/index' +import type { TLiteral, TLiteralValue } from '../literal/index' +import type { TMappedKey, TMappedResult } from '../mapped/index' +import type { TNever } from '../never/index' +import type { TNot } from '../not/index' +import type { TNull } from '../null/index' +import type { TNumber } from '../number/index' +import type { TObject, TAdditionalProperties, TProperties } from '../object/index' +import type { TOptional } from '../optional/index' +import type { TPromise } from '../promise/index' +import type { TReadonly } from '../readonly/index' +import type { TRef } from '../ref/index' +import type { TRegExp } from '../regexp/index' +import type { TSchema } from '../schema/index' +import type { TSymbol } from '../symbol/index' +import type { TTuple } from '../tuple/index' +import type { TUint8Array } from '../uint8array/index' +import type { TUndefined } from '../undefined/index' +import type { TUnknown } from '../unknown/index' +import type { TUnsafe } from '../unsafe/index' +import type { TVoid } from '../void/index' +import type { TDate } from '../date/index' +import type { TThis } from '../recursive/index' + +export class TypeGuardUnknownTypeError extends TypeBoxError {} + +const KnownTypes = [ + 'Any', + 'Array', + 'AsyncIterator', + 'BigInt', + 'Boolean', + 'Constructor', + 'Date', + 'Enum', + 'Function', + 'Integer', + 'Intersect', + 'Iterator', + 'Literal', + 'MappedKey', + 'MappedResult', + 'Not', + 'Null', + 'Number', + 'Object', + 'Promise', + 'Record', + 'Ref', + 'RegExp', + 'String', + 'Symbol', + 'TemplateLiteral', + 'This', + 'Tuple', + 'Undefined', + 'Union', + 'Uint8Array', + 'Unknown', + 'Void', +] +function IsPattern(value: unknown): value is string { + try { + new RegExp(value as string) + return true + } catch { + return false + } +} +function IsControlCharacterFree(value: unknown): value is string { + if (!ValueGuard.IsString(value)) return false + for (let i = 0; i < value.length; i++) { + const code = value.charCodeAt(i) + if ((code >= 7 && code <= 13) || code === 27 || code === 127) { + return false + } + } + return true +} +function IsAdditionalProperties(value: unknown): value is TAdditionalProperties { + return IsOptionalBoolean(value) || IsSchema(value) +} +function IsOptionalBigInt(value: unknown): value is bigint | undefined { + return ValueGuard.IsUndefined(value) || ValueGuard.IsBigInt(value) +} +function IsOptionalNumber(value: unknown): value is number | undefined { + return ValueGuard.IsUndefined(value) || ValueGuard.IsNumber(value) +} +function IsOptionalBoolean(value: unknown): value is boolean | undefined { + return ValueGuard.IsUndefined(value) || ValueGuard.IsBoolean(value) +} +function IsOptionalString(value: unknown): value is string | undefined { + return ValueGuard.IsUndefined(value) || ValueGuard.IsString(value) +} +function IsOptionalPattern(value: unknown): value is string | undefined { + return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) +} +function IsOptionalFormat(value: unknown): value is string | undefined { + return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value)) +} +function IsOptionalSchema(value: unknown): value is boolean | undefined { + return ValueGuard.IsUndefined(value) || IsSchema(value) +} +// ------------------------------------------------------------------ +// Modifiers +// ------------------------------------------------------------------ +/** Returns true if this value has a Readonly symbol */ +export function IsReadonly(value: T): value is TReadonly { + return ValueGuard.IsObject(value) && value[ReadonlyKind] === 'Readonly' +} +/** Returns true if this value has a Optional symbol */ +export function IsOptional(value: T): value is TOptional { + return ValueGuard.IsObject(value) && value[OptionalKind] === 'Optional' +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +/** Returns true if the given value is TAny */ +export function IsAny(value: unknown): value is TAny { + // prettier-ignore + return ( + IsKindOf(value, 'Any') && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TArray */ +export function IsArray(value: unknown): value is TArray { + return ( + IsKindOf(value, 'Array') && + value.type === 'array' && + IsOptionalString(value.$id) && + IsSchema(value.items) && + IsOptionalNumber(value.minItems) && + IsOptionalNumber(value.maxItems) && + IsOptionalBoolean(value.uniqueItems) && + IsOptionalSchema(value.contains) && + IsOptionalNumber(value.minContains) && + IsOptionalNumber(value.maxContains) + ) +} +/** Returns true if the given value is TAsyncIterator */ +export function IsAsyncIterator(value: unknown): value is TAsyncIterator { + // prettier-ignore + return ( + IsKindOf(value, 'AsyncIterator') && + value.type === 'AsyncIterator' && + IsOptionalString(value.$id) && + IsSchema(value.items) + ) +} +/** Returns true if the given value is TBigInt */ +export function IsBigInt(value: unknown): value is TBigInt { + // prettier-ignore + return ( + IsKindOf(value, 'BigInt') && + value.type === 'bigint' && + IsOptionalString(value.$id) && + IsOptionalBigInt(value.exclusiveMaximum) && + IsOptionalBigInt(value.exclusiveMinimum) && + IsOptionalBigInt(value.maximum) && + IsOptionalBigInt(value.minimum) && + IsOptionalBigInt(value.multipleOf) + ) +} +/** Returns true if the given value is TBoolean */ +export function IsBoolean(value: unknown): value is TBoolean { + // prettier-ignore + return ( + IsKindOf(value, 'Boolean') && + value.type === 'boolean' && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TConstructor */ +export function IsConstructor(value: unknown): value is TConstructor { + // prettier-ignore + return ( + IsKindOf(value, 'Constructor') && + value.type === 'Constructor' && + IsOptionalString(value.$id) && + ValueGuard.IsArray(value.parameters) && + value.parameters.every(schema => IsSchema(schema)) && + IsSchema(value.returns) + ) +} +/** Returns true if the given value is TDate */ +export function IsDate(value: unknown): value is TDate { + return ( + IsKindOf(value, 'Date') && + value.type === 'Date' && + IsOptionalString(value.$id) && + IsOptionalNumber(value.exclusiveMaximumTimestamp) && + IsOptionalNumber(value.exclusiveMinimumTimestamp) && + IsOptionalNumber(value.maximumTimestamp) && + IsOptionalNumber(value.minimumTimestamp) && + IsOptionalNumber(value.multipleOfTimestamp) + ) +} +/** Returns true if the given value is TFunction */ +export function IsFunction(value: unknown): value is TFunction { + // prettier-ignore + return ( + IsKindOf(value, 'Function') && + value.type === 'Function' && + IsOptionalString(value.$id) && + ValueGuard.IsArray(value.parameters) && + value.parameters.every(schema => IsSchema(schema)) && + IsSchema(value.returns) + ) +} +/** Returns true if the given value is TInteger */ +export function IsInteger(value: unknown): value is TInteger { + return ( + IsKindOf(value, 'Integer') && + value.type === 'integer' && + IsOptionalString(value.$id) && + IsOptionalNumber(value.exclusiveMaximum) && + IsOptionalNumber(value.exclusiveMinimum) && + IsOptionalNumber(value.maximum) && + IsOptionalNumber(value.minimum) && + IsOptionalNumber(value.multipleOf) + ) +} +/** Returns true if the given schema is TProperties */ +export function IsProperties(value: unknown): value is TProperties { + // prettier-ignore + return ( + ValueGuard.IsObject(value) && + Object.entries(value).every(([key, schema]) => IsControlCharacterFree(key) && IsSchema(schema)) + ) +} +/** Returns true if the given value is TIntersect */ +export function IsIntersect(value: unknown): value is TIntersect { + // prettier-ignore + return ( + IsKindOf(value, 'Intersect') && + (ValueGuard.IsString(value.type) && value.type !== 'object' ? false : true) && + ValueGuard.IsArray(value.allOf) && + value.allOf.every(schema => IsSchema(schema) && !IsTransform(schema)) && + IsOptionalString(value.type) && + (IsOptionalBoolean(value.unevaluatedProperties) || IsOptionalSchema(value.unevaluatedProperties)) && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TIterator */ +export function IsIterator(value: unknown): value is TIterator { + // prettier-ignore + return ( + IsKindOf(value, 'Iterator') && + value.type === 'Iterator' && + IsOptionalString(value.$id) && + IsSchema(value.items) + ) +} +/** Returns true if the given value is a TKind with the given name. */ +export function IsKindOf(value: unknown, kind: T): value is Record & { [Kind]: T } { + return ValueGuard.IsObject(value) && Kind in value && value[Kind] === kind +} +/** Returns true if the given value is TLiteral */ +export function IsLiteralString(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsString(value.const) +} +/** Returns true if the given value is TLiteral */ +export function IsLiteralNumber(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsNumber(value.const) +} +/** Returns true if the given value is TLiteral */ +export function IsLiteralBoolean(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsBoolean(value.const) +} +/** Returns true if the given value is TLiteral */ +export function IsLiteral(value: unknown): value is TLiteral { + // prettier-ignore + return ( + IsKindOf(value, 'Literal') && + IsOptionalString(value.$id) && IsLiteralValue(value.const) + ) +} +/** Returns true if the given value is a TLiteralValue */ +export function IsLiteralValue(value: unknown): value is TLiteralValue { + return ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) || ValueGuard.IsString(value) +} +/** Returns true if the given value is a TMappedKey */ +export function IsMappedKey(value: unknown): value is TMappedKey { + // prettier-ignore + return ( + IsKindOf(value, 'MappedKey') && + ValueGuard.IsArray(value.keys) && + value.keys.every(key => ValueGuard.IsNumber(key) || ValueGuard.IsString(key)) + ) +} +/** Returns true if the given value is TMappedResult */ +export function IsMappedResult(value: unknown): value is TMappedResult { + // prettier-ignore + return ( + IsKindOf(value, 'MappedResult') && + IsProperties(value.properties) + ) +} +/** Returns true if the given value is TNever */ +export function IsNever(value: unknown): value is TNever { + // prettier-ignore + return ( + IsKindOf(value, 'Never') && + ValueGuard.IsObject(value.not) && + Object.getOwnPropertyNames(value.not).length === 0 + ) +} +/** Returns true if the given value is TNot */ +export function IsNot(value: unknown): value is TNot { + // prettier-ignore + return ( + IsKindOf(value, 'Not') && + IsSchema(value.not) + ) +} +/** Returns true if the given value is TNull */ +export function IsNull(value: unknown): value is TNull { + // prettier-ignore + return ( + IsKindOf(value, 'Null') && + value.type === 'null' && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TNumber */ +export function IsNumber(value: unknown): value is TNumber { + return ( + IsKindOf(value, 'Number') && + value.type === 'number' && + IsOptionalString(value.$id) && + IsOptionalNumber(value.exclusiveMaximum) && + IsOptionalNumber(value.exclusiveMinimum) && + IsOptionalNumber(value.maximum) && + IsOptionalNumber(value.minimum) && + IsOptionalNumber(value.multipleOf) + ) +} +/** Returns true if the given value is TObject */ +export function IsObject(value: unknown): value is TObject { + // prettier-ignore + return ( + IsKindOf(value, 'Object') && + value.type === 'object' && + IsOptionalString(value.$id) && + IsProperties(value.properties) && + IsAdditionalProperties(value.additionalProperties) && + IsOptionalNumber(value.minProperties) && + IsOptionalNumber(value.maxProperties) + ) +} +/** Returns true if the given value is TPromise */ +export function IsPromise(value: unknown): value is TPromise { + // prettier-ignore + return ( + IsKindOf(value, 'Promise') && + value.type === 'Promise' && + IsOptionalString(value.$id) && + IsSchema(value.item) + ) +} +/** Returns true if the given value is TRecord */ +export function IsRecord(value: unknown): value is TRecord { + // prettier-ignore + return ( + IsKindOf(value, 'Record') && + value.type === 'object' && + IsOptionalString(value.$id) && + IsAdditionalProperties(value.additionalProperties) && + ValueGuard.IsObject(value.patternProperties) && + ((schema: Record) => { + const keys = Object.getOwnPropertyNames(schema.patternProperties) + return ( + keys.length === 1 && + IsPattern(keys[0]) && + ValueGuard.IsObject(schema.patternProperties) && + IsSchema(schema.patternProperties[keys[0]]) + ) + })(value) + ) +} +/** Returns true if this value is TRecursive */ +export function IsRecursive(value: unknown): value is { [Hint]: 'Recursive' } { + return ValueGuard.IsObject(value) && Hint in value && value[Hint] === 'Recursive' +} +/** Returns true if the given value is TRef */ +export function IsRef(value: unknown): value is TRef { + // prettier-ignore + return ( + IsKindOf(value, 'Ref') && + IsOptionalString(value.$id) && + ValueGuard.IsString(value.$ref) + ) +} +/** Returns true if the given value is TRegExp */ +export function IsRegExp(value: unknown): value is TRegExp { + // prettier-ignore + return ( + IsKindOf(value, 'RegExp') && + IsOptionalString(value.$id) && + ValueGuard.IsString(value.source) && + ValueGuard.IsString(value.flags) + ) +} +/** Returns true if the given value is TString */ +export function IsString(value: unknown): value is TString { + // prettier-ignore + return ( + IsKindOf(value, 'String') && + value.type === 'string' && + IsOptionalString(value.$id) && + IsOptionalNumber(value.minLength) && + IsOptionalNumber(value.maxLength) && + IsOptionalPattern(value.pattern) && + IsOptionalFormat(value.format) + ) +} +/** Returns true if the given value is TSymbol */ +export function IsSymbol(value: unknown): value is TSymbol { + // prettier-ignore + return ( + IsKindOf(value, 'Symbol') && + value.type === 'symbol' && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TTemplateLiteral */ +export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral { + // prettier-ignore + return ( + IsKindOf(value, 'TemplateLiteral') && + value.type === 'string' && + ValueGuard.IsString(value.pattern) && + value.pattern[0] === '^' && + value.pattern[value.pattern.length - 1] === '$' + ) +} +/** Returns true if the given value is TThis */ +export function IsThis(value: unknown): value is TThis { + // prettier-ignore + return ( + IsKindOf(value, 'This') && + IsOptionalString(value.$id) && + ValueGuard.IsString(value.$ref) + ) +} +/** Returns true of this value is TTransform */ +export function IsTransform(value: unknown): value is { [TransformKind]: TransformOptions } { + return ValueGuard.IsObject(value) && TransformKind in value +} +/** Returns true if the given value is TTuple */ +export function IsTuple(value: unknown): value is TTuple { + // prettier-ignore + return ( + IsKindOf(value, 'Tuple') && + value.type === 'array' && + IsOptionalString(value.$id) && + ValueGuard.IsNumber(value.minItems) && + ValueGuard.IsNumber(value.maxItems) && + value.minItems === value.maxItems && + (( // empty + ValueGuard.IsUndefined(value.items) && + ValueGuard.IsUndefined(value.additionalItems) && + value.minItems === 0 + ) || ( + ValueGuard.IsArray(value.items) && + value.items.every(schema => IsSchema(schema)) + )) + ) +} +/** Returns true if the given value is TUndefined */ +export function IsUndefined(value: unknown): value is TUndefined { + // prettier-ignore + return ( + IsKindOf(value, 'Undefined') && + value.type === 'undefined' && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TUnion[]> */ +export function IsUnionLiteral(value: unknown): value is TUnion { + return IsUnion(value) && value.anyOf.every((schema) => IsLiteralString(schema) || IsLiteralNumber(schema)) +} +/** Returns true if the given value is TUnion */ +export function IsUnion(value: unknown): value is TUnion { + // prettier-ignore + return ( + IsKindOf(value, 'Union') && + IsOptionalString(value.$id) && + ValueGuard.IsObject(value) && + ValueGuard.IsArray(value.anyOf) && + value.anyOf.every(schema => IsSchema(schema)) + ) +} +/** Returns true if the given value is TUint8Array */ +export function IsUint8Array(value: unknown): value is TUint8Array { + // prettier-ignore + return ( + IsKindOf(value, 'Uint8Array') && + value.type === 'Uint8Array' && + IsOptionalString(value.$id) && + IsOptionalNumber(value.minByteLength) && + IsOptionalNumber(value.maxByteLength) + ) +} +/** Returns true if the given value is TUnknown */ +export function IsUnknown(value: unknown): value is TUnknown { + // prettier-ignore + return ( + IsKindOf(value, 'Unknown') && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is a raw TUnsafe */ +export function IsUnsafe(value: unknown): value is TUnsafe { + return IsKindOf(value, 'Unsafe') +} +/** Returns true if the given value is TVoid */ +export function IsVoid(value: unknown): value is TVoid { + // prettier-ignore + return ( + IsKindOf(value, 'Void') && + value.type === 'void' && + IsOptionalString(value.$id) + ) +} +/** Returns true if the given value is TKind */ +export function IsKind(value: unknown): value is Record & { [Kind]: string } { + return ValueGuard.IsObject(value) && Kind in value && ValueGuard.IsString(value[Kind]) && !KnownTypes.includes(value[Kind] as string) +} +/** Returns true if the given value is TSchema */ +export function IsSchema(value: unknown): value is TSchema { + // prettier-ignore + return ( + ValueGuard.IsObject(value) + ) && ( + IsAny(value) || + IsArray(value) || + IsBoolean(value) || + IsBigInt(value) || + IsAsyncIterator(value) || + IsConstructor(value) || + IsDate(value) || + IsFunction(value) || + IsInteger(value) || + IsIntersect(value) || + IsIterator(value) || + IsLiteral(value) || + IsMappedKey(value) || + IsMappedResult(value) || + IsNever(value) || + IsNot(value) || + IsNull(value) || + IsNumber(value) || + IsObject(value) || + IsPromise(value) || + IsRecord(value) || + IsRef(value) || + IsRegExp(value) || + IsString(value) || + IsSymbol(value) || + IsTemplateLiteral(value) || + IsThis(value) || + IsTuple(value) || + IsUndefined(value) || + IsUnion(value) || + IsUint8Array(value) || + IsUnknown(value) || + IsUnsafe(value) || + IsVoid(value) || + IsKind(value) + ) +} diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts new file mode 100644 index 000000000..e09fb9bbc --- /dev/null +++ b/src/type/guard/value.ts @@ -0,0 +1,88 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +/** Returns true if this value is an async iterator */ +export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator { + return IsObject(value) && !IsArray(value) && !IsUint8Array(value) && Symbol.asyncIterator in value +} +/** Returns true if this value is an array */ +export function IsArray(value: unknown): value is unknown[] { + return Array.isArray(value) +} +/** Returns true if this value is bigint */ +export function IsBigInt(value: unknown): value is bigint { + return typeof value === 'bigint' +} +/** Returns true if this value is a boolean */ +export function IsBoolean(value: unknown): value is boolean { + return typeof value === 'boolean' +} +/** Returns true if this value is a Date object */ +export function IsDate(value: unknown): value is Date { + return value instanceof globalThis.Date +} +/** Returns true if this value is a function */ +export function IsFunction(value: unknown): value is Function { + return typeof value === 'function' +} +/** Returns true if this value is an iterator */ +export function IsIterator(value: unknown): value is IterableIterator { + return IsObject(value) && !IsArray(value) && !IsUint8Array(value) && Symbol.iterator in value +} +/** Returns true if this value is null */ +export function IsNull(value: unknown): value is null { + return value === null +} +/** Returns true if this value is number */ +export function IsNumber(value: unknown): value is number { + return typeof value === 'number' +} +/** Returns true if this value is an object */ +export function IsObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null +} +/** Returns true if this value is RegExp */ +export function IsRegExp(value: unknown): value is RegExp { + return value instanceof globalThis.RegExp +} +/** Returns true if this value is string */ +export function IsString(value: unknown): value is string { + return typeof value === 'string' +} +/** Returns true if this value is symbol */ +export function IsSymbol(value: unknown): value is symbol { + return typeof value === 'symbol' +} +/** Returns true if this value is a Uint8Array */ +export function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof globalThis.Uint8Array +} +/** Returns true if this value is undefined */ +export function IsUndefined(value: unknown): value is undefined { + return value === undefined +} diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts new file mode 100644 index 000000000..84263c6d5 --- /dev/null +++ b/src/type/helpers/helpers.ts @@ -0,0 +1,69 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TProperties } from '../object/index' +import type { TNever } from '../never/index' +// ------------------------------------------------------------------ +// Helper: Common +// ------------------------------------------------------------------ +export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never +export type TupleToUnion = { [K in keyof T]: T[K] }[number] +export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never +export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never +export type UnionToTuple> = [U] extends [never] ? Acc : UnionToTuple, [Extract, ...Acc]> +export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T +export type Assert = T extends E ? T : never +export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never +export type Ensure = T extends infer U ? U : never +export type EmptyString = '' +export type ZeroString = '0' +// ------------------------------------------------------------------ +// Helper: Increment +// ------------------------------------------------------------------ +type IncrementBase = { m: '9'; t: '01'; '0': '1'; '1': '2'; '2': '3'; '3': '4'; '4': '5'; '5': '6'; '6': '7'; '7': '8'; '8': '9'; '9': '0' } +type IncrementTake = IncrementBase[T] +type IncrementStep = T extends IncrementBase['m'] + ? IncrementBase['t'] + : T extends `${infer L extends keyof IncrementBase}${infer R}` + ? L extends IncrementBase['m'] + ? `${IncrementTake}${IncrementStep}` + : `${IncrementTake}${R}` + : never +type IncrementReverse = T extends `${infer L}${infer R}` ? `${IncrementReverse}${L}` : T +export type Increment = IncrementReverse>> +/** Increments the given string value + 1 */ +export function Increment(T: T): Increment { + return (parseInt(T) + 1).toString() as Increment +} +// ------------------------------------------------------------------ +// Helper: Type Asserts +// ------------------------------------------------------------------ +export type AssertProperties = T extends TProperties ? T : TProperties +export type AssertRest = T extends E ? T : [] +export type AssertType = T extends E ? T : TNever diff --git a/src/type/helpers/index.ts b/src/type/helpers/index.ts new file mode 100644 index 000000000..4d6559c8a --- /dev/null +++ b/src/type/helpers/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './helpers' diff --git a/src/type/index.ts b/src/type/index.ts new file mode 100644 index 000000000..d47dbf308 --- /dev/null +++ b/src/type/index.ts @@ -0,0 +1,98 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './any/index' +export * from './array/index' +export * from './async-iterator/index' +export * from './awaited/index' +export * from './bigint/index' +export * from './boolean/index' +export * from './clone/index' +export * from './composite/index' +export * from './const/index' +export * from './constructor/index' +export * from './constructor-parameters/index' +export * from './date/index' +export * from './deref/index' +export * from './discard/index' +export * from './enum/index' +export * from './error/index' +export * from './exclude/index' +export * from './extends/index' +export * from './extract/index' +export * from './function/index' +export * from './guard/index' +export * from './helpers/index' +export * from './indexed/index' +export * from './instance-type/index' +export * from './integer/index' +export * from './intersect/index' +export * from './intrinsic/index' +export * from './iterator/index' +export * from './keyof/index' +export * from './literal/index' +export * from './mapped/index' +export * from './never/index' +export * from './not/index' +export * from './null/index' +export * from './number/index' +export * from './object/index' +export * from './omit/index' +export * from './optional/index' +export * from './parameters/index' +export * from './partial/index' +export * from './patterns/index' +export * from './pick/index' +export * from './promise/index' +export * from './readonly/index' +export * from './readonly-optional/index' +export * from './record/index' +export * from './recursive/index' +export * from './ref/index' +export * from './regexp/index' +export * from './registry/index' +export * from './required/index' +export * from './rest/index' +export * from './return-type/index' +export * from './schema/index' +export * from './sets/index' +export * from './static/index' +export * from './strict/index' +export * from './string/index' +export * from './symbol/index' +export * from './symbols/index' +export * from './template-literal/index' +export * from './transform/index' +export * from './tuple/index' +export * from './type/index' +export * from './uint8array/index' +export * from './undefined/index' +export * from './union/index' +export * from './unknown/index' +export * from './unsafe/index' +export * from './void/index' diff --git a/src/type/indexed/index.ts b/src/type/indexed/index.ts new file mode 100644 index 000000000..bdd973af4 --- /dev/null +++ b/src/type/indexed/index.ts @@ -0,0 +1,32 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './indexed-from-mapped-key' +export * from './indexed-from-mapped-result' +export * from './indexed-property-keys' +export * from './indexed' diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts new file mode 100644 index 000000000..5b1567c49 --- /dev/null +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -0,0 +1,103 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Evaluate } from '../helpers/index' +import type { TProperties } from '../object/index' +import { Index, type TIndex } from './indexed' +import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' + +// ------------------------------------------------------------------ +// MappedIndexPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIndexPropertyKey< + T extends TSchema, + K extends PropertyKey +> = { + [_ in K]: TIndex +} +// prettier-ignore +function MappedIndexPropertyKey< + T extends TSchema, + K extends PropertyKey +>(T: T, K: K, options: SchemaOptions): TMappedIndexPropertyKey { + return { [K]: Index(T, [K], options) } as TMappedIndexPropertyKey +} +// ------------------------------------------------------------------ +// MappedIndexPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIndexPropertyKeys = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TMappedIndexPropertyKeys> + : Acc +) +// prettier-ignore +function MappedIndexPropertyKeys< + T extends TSchema, + K extends PropertyKey[] +>(T: T, K: [...K], options: SchemaOptions): TMappedIndexPropertyKeys { + return K.reduce((Acc, L) => { + return { ...Acc, ...MappedIndexPropertyKey(T, L, options) } + }, {} as TProperties) as TMappedIndexPropertyKeys +} +// ------------------------------------------------------------------ +// MappedIndexProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIndexProperties = Evaluate< + TMappedIndexPropertyKeys +> +// prettier-ignore +function MappedIndexProperties< + T extends TSchema, + K extends TMappedKey +>(T: T, K: K, options: SchemaOptions): TMappedIndexProperties { + return MappedIndexPropertyKeys(T, K.keys, options) as TMappedIndexProperties +} +// ------------------------------------------------------------------ +// TIndexFromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export type TIndexFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TMappedIndexProperties +> = ( + TMappedResult

      +) +// prettier-ignore +export function IndexFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TMappedIndexProperties +>(T: T, K: K, options: SchemaOptions): TMappedResult

      { + const P = MappedIndexProperties(T, K, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts new file mode 100644 index 000000000..f685717ca --- /dev/null +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -0,0 +1,90 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys' +import { Index, type TIndex } from './index' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + T extends TSchema, + P extends TProperties +> = ( + { [K2 in keyof P]: TIndex> } +) +// prettier-ignore +function FromProperties< + T extends TSchema, + P extends TProperties +>(T: T, P: P, options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Index(T, IndexPropertyKeys(P[K2]), options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + T extends TSchema, + R extends TMappedResult +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + T extends TSchema, + R extends TMappedResult +>(T: T, R: R, options: SchemaOptions): TFromMappedResult { + return FromProperties(T, R.properties, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// TIndexFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TIndexFromMappedResult< + T extends TSchema, + R extends TMappedResult, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function IndexFromMappedResult< + T extends TSchema, + R extends TMappedResult, + P extends TProperties = TFromMappedResult +>(T: T, R: R, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(T, R, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts new file mode 100644 index 000000000..58f689811 --- /dev/null +++ b/src/type/indexed/indexed-property-keys.ts @@ -0,0 +1,103 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TemplateLiteralGenerate, type TTemplateLiteralGenerate, type TTemplateLiteral } from '../template-literal/index' +import type { TLiteral, TLiteralValue } from '../literal/index' +import type { TInteger } from '../integer/index' +import type { TNumber } from '../number/index' +import type { TSchema } from '../schema/index' +import type { TUnion } from '../union/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../guard/type' +// ------------------------------------------------------------------ +// FromTemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteral> = (R) +// prettier-ignore +function FromTemplateLiteral(T: T): TFromTemplateLiteral { + const R = TemplateLiteralGenerate(T) as string[] + return R.map(S => S.toString()) as TFromTemplateLiteral +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromUnion]> + : Acc +) +// prettier-ignore +function FromUnion(T: T): TFromUnion { + return T.reduce((Acc, L) => { + return [...Acc, ...IndexPropertyKeys(L)] + }, [] as string[]) as TFromUnion +} +// ------------------------------------------------------------------ +// FromLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type TFromLiteral = ( + T extends PropertyKey + ? [`${T}`] + : [] +) +// prettier-ignore +function FromLiteral(T: T): TFromLiteral { + return ( + [(T as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString() + ) as TFromLiteral +} +// ------------------------------------------------------------------ +// IndexedKeyResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TIndexPropertyKeys = ( + T extends TTemplateLiteral ? TFromTemplateLiteral : + T extends TUnion ? TFromUnion : + T extends TLiteral ? TFromLiteral : + T extends TNumber ? ['[number]'] : + T extends TInteger ? ['[number]'] : + [] +) +/** Returns a tuple of PropertyKeys derived from the given TSchema */ +// prettier-ignore +export function IndexPropertyKeys(T: T): TIndexPropertyKeys { + return [...new Set(( + IsTemplateLiteral(T) ? FromTemplateLiteral(T) : + IsUnion(T) ? FromUnion(T.anyOf) : + IsLiteral(T) ? FromLiteral(T.const) : + IsNumber(T) ? ['[number]'] : + IsInteger(T) ? ['[number]'] : + [] + ))] as TIndexPropertyKeys +} diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts new file mode 100644 index 000000000..bea542a69 --- /dev/null +++ b/src/type/indexed/indexed.ts @@ -0,0 +1,241 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { type TSchema, SchemaOptions } from '../schema/index' +import { type TObject, type TProperties } from '../object/index' +import { type Assert } from '../helpers/index' +import { Never, type TNever } from '../never/index' +import { type TRecursive } from '../recursive/index' +import { type TIntersect } from '../intersect/index' +import { TMappedResult, type TMappedKey } from '../mapped/index' +import { type TUnion } from '../union/index' +import { type TTuple } from '../tuple/index' +import { type TArray } from '../array/index' +import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index' +import { UnionEvaluated, type TUnionEvaluated } from '../union/index' +import { CloneType } from '../clone/type' + +import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys' +import { IndexFromMappedKey, type TIndexFromMappedKey } from './indexed-from-mapped-key' +import { IndexFromMappedResult, type TIndexFromMappedResult } from './indexed-from-mapped-result' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion } from '../guard/type' + +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest, TSchema>]> + : Acc +) +// prettier-ignore +function FromRest(T: [...T], K: K): TFromRest { + return T.map(L => FromKey(L, K)) as TFromRest +} +// ------------------------------------------------------------------ +// FromIntersectRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntersectRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TNever + ? TFromIntersectRest + : TFromIntersectRest + : Acc +) +// prettier-ignore +function FromIntersectRest(T: [...T]): TFromIntersectRest { + return T.filter(L => !IsNever(L)) as never +} +// prettier-ignore +type TFromIntersect = ( + TIntersectEvaluated>> +) +// prettier-ignore +function FromIntersect(T: [...T], K: K): TFromIntersect { + return ( + IntersectEvaluated(FromIntersectRest(FromRest(T as TSchema[], K))) + ) as TFromIntersect +} +// ------------------------------------------------------------------ +// FromUnionRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnionRest = T +// prettier-ignore +function FromUnionRest(T: [...T]): TFromUnionRest { + return T as never // review this +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion = ( + TUnionEvaluated>> +) +// prettier-ignore +function FromUnion(T: [...T], K: K): TFromUnion { + return ( + UnionEvaluated(FromUnionRest(FromRest(T as TSchema[], K))) + ) as TFromUnion +} +// ------------------------------------------------------------------ +// FromTuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + K extends keyof T ? T[K] : + K extends '[number]' ? TUnionEvaluated : + TNever +) +// prettier-ignore +function FromTuple(T: [...T], K: K): TFromTuple { + return ( + K in T ? T[K as number] : + K === '[number]' ? UnionEvaluated(T) : + Never() + ) as TFromTuple +} +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = ( + K extends '[number]' + ? T + : TNever +) +// prettier-ignore +function FromArray(T: T, K: K): TFromArray { + return ( + K === '[number]' + ? T + : Never() + ) as TFromArray +} +// ------------------------------------------------------------------ +// FromProperty +// ------------------------------------------------------------------ +type AssertPropertyKey = Assert + +// prettier-ignore +type TFromProperty< + T extends TProperties, + K extends PropertyKey, +> = ( + // evaluate for string keys + K extends keyof T + ? T[K] + // evaluate for numeric keys + : `${AssertPropertyKey}` extends `${AssertPropertyKey}` + ? T[AssertPropertyKey] + : TNever +) +// prettier-ignore +function FromProperty(T: T, K: K): TFromProperty { + return (K in T ? T[K as string] : Never()) as TFromProperty +} +// ------------------------------------------------------------------ +// FromKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromKey = ( + T extends TRecursive ? TFromKey : + T extends TIntersect ? TFromIntersect : + T extends TUnion ? TFromUnion : + T extends TTuple ? TFromTuple : + T extends TArray ? TFromArray : + T extends TObject ? TFromProperty : + TNever +) +// prettier-ignore +function FromKey(T: T, K: K): TFromKey { + return ( + IsIntersect(T) ? FromIntersect(T.allOf, K) : + IsUnion(T) ? FromUnion(T.anyOf, K) : + IsTuple(T) ? FromTuple(T.items ?? [], K) : + IsArray(T) ? FromArray(T.items, K) : + IsObject(T) ? FromProperty(T.properties, K) : + Never() + ) as TFromKey +} +// ------------------------------------------------------------------ +// FromKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TFromKeys = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TFromKeys, TSchema>]> + : Acc +) +// prettier-ignore +function FromKeys(T: T, K: [...K]): TFromKeys { + return K.map(L => FromKey(T, L)) as TFromKeys +} +// ------------------------------------------------------------------ +// FromSchema +// ------------------------------------------------------------------ +// prettier-ignore +type FromSchema = ( + TUnionEvaluated> +) +// prettier-ignore +function FromSchema(T: T, K: [...K]): FromSchema { + return ( + UnionEvaluated(FromKeys(T, K as PropertyKey[])) + ) as FromSchema +} +// ------------------------------------------------------------------ +// TIndex +// ------------------------------------------------------------------ +// prettier-ignore +export type TIndex = ( + FromSchema +) +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedResult +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedKey +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index>(T: T, K: K, options?: SchemaOptions): TIndex +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(T: TSchema, K: any, options: SchemaOptions = {}): any { + // prettier-ignore + return ( + IsMappedResult(K) ? CloneType(IndexFromMappedResult(T, K, options)) : + IsMappedKey(K) ? CloneType(IndexFromMappedKey(T, K, options)) : + IsSchema(K) ? CloneType(FromSchema(T, IndexPropertyKeys(K)), options) : + CloneType(FromSchema(T, K as string[]), options) + ) +} diff --git a/src/type/instance-type/index.ts b/src/type/instance-type/index.ts new file mode 100644 index 000000000..aa85b717a --- /dev/null +++ b/src/type/instance-type/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './instance-type' diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts new file mode 100644 index 000000000..f26503419 --- /dev/null +++ b/src/type/instance-type/instance-type.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TConstructor } from '../constructor/index' +import { CloneType } from '../clone/type' + +export type TInstanceType> = T['returns'] + +/** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ +export function InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + return CloneType(schema.returns, options) +} diff --git a/src/type/integer/index.ts b/src/type/integer/index.ts new file mode 100644 index 000000000..73513ebc8 --- /dev/null +++ b/src/type/integer/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './integer' diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts new file mode 100644 index 000000000..23ed802af --- /dev/null +++ b/src/type/integer/integer.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface IntegerOptions extends SchemaOptions { + exclusiveMaximum?: number + exclusiveMinimum?: number + maximum?: number + minimum?: number + multipleOf?: number +} +export interface TInteger extends TSchema, IntegerOptions { + [Kind]: 'Integer' + static: number + type: 'integer' +} +/** `[Json]` Creates an Integer type */ +export function Integer(options: IntegerOptions = {}): TInteger { + return { + ...options, + [Kind]: 'Integer', + type: 'integer', + } as unknown as TInteger +} diff --git a/src/type/intersect/index.ts b/src/type/intersect/index.ts new file mode 100644 index 000000000..c2f3d3ba4 --- /dev/null +++ b/src/type/intersect/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './intersect-evaluated' +export * from './intersect-type' +export * from './intersect' diff --git a/src/type/intersect/intersect-create.ts b/src/type/intersect/intersect-create.ts new file mode 100644 index 000000000..815c27e9a --- /dev/null +++ b/src/type/intersect/intersect-create.ts @@ -0,0 +1,52 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { Kind } from '../symbols/index' +import { CloneType, CloneRest } from '../clone/type' +import type { TIntersect, IntersectOptions } from './intersect-type' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsObject, IsSchema } from '../guard/type' +// ------------------------------------------------------------------ +// IntersectCreate +// ------------------------------------------------------------------ +// prettier-ignore +export function IntersectCreate(T: [...T], options: IntersectOptions): TIntersect { + const allObjects = T.every((schema) => IsObject(schema)) + const clonedUnevaluatedProperties = IsSchema(options.unevaluatedProperties) + ? { unevaluatedProperties: CloneType(options.unevaluatedProperties) } + : {} + return ( + (options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects + ? { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: CloneRest(T) } + : { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: CloneRest(T) }) + ) as TIntersect +} diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts new file mode 100644 index 000000000..5f6f25ba4 --- /dev/null +++ b/src/type/intersect/intersect-evaluated.ts @@ -0,0 +1,127 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions, TSchema } from '../schema/index' +import { OptionalKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' + +import { Never, type TNever } from '../never/index' +import { Optional, type TOptional } from '../optional/index' +import type { TReadonly } from '../readonly/index' + +import { TIntersect, IntersectOptions } from './intersect-type' +import { IntersectCreate } from './intersect-create' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsOptional, + IsTransform, +} from '../guard/type' + +// ------------------------------------------------------------------ +// IsIntersectOptional +// ------------------------------------------------------------------ +// prettier-ignore +type TIsIntersectOptional = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TOptional + ? TIsIntersectOptional + : false + : true +) +// prettier-ignore +function IsIntersectOptional(T: T): TIsIntersectOptional { + return T.every(L => IsOptional(L)) as TIsIntersectOptional +} +// ------------------------------------------------------------------ +// RemoveOptionalFromType +// ------------------------------------------------------------------ +// prettier-ignore +type TRemoveOptionalFromType = ( + T extends TReadonly ? TReadonly> : + T extends TOptional ? TRemoveOptionalFromType : + T +) +// prettier-ignore +function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { + return ( + Discard(T, [OptionalKind]) + ) as TRemoveOptionalFromType +} +// ------------------------------------------------------------------ +// RemoveOptionalFromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TRemoveOptionalFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TOptional + ? TRemoveOptionalFromRest]> + : TRemoveOptionalFromRest + : Acc +) +// prettier-ignore +function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { + return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as TRemoveOptionalFromRest +} +// ------------------------------------------------------------------ +// ResolveIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type TResolveIntersect = ( + TIsIntersectOptional extends true + ? TOptional>> + : TIntersect> +) +// prettier-ignore +function ResolveIntersect(T: [...T], options: SchemaOptions): TResolveIntersect { + return ( + IsIntersectOptional(T) + ? Optional(IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options)) + : IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options) + ) as unknown as TResolveIntersect +} +// ------------------------------------------------------------------ +// IntersectEvaluated +// ------------------------------------------------------------------ +// prettier-ignore +export type TIntersectEvaluated = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + TResolveIntersect +) +/** `[Json]` Creates an evaluated Intersect type */ +export function IntersectEvaluated>(T: [...T], options: IntersectOptions = {}): R { + if (T.length === 0) return Never(options) as R + if (T.length === 1) return CloneType(T[0], options) as R + if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') + return ResolveIntersect(T, options) as R +} diff --git a/examples/prototypes/const.ts b/src/type/intersect/intersect-type.ts similarity index 50% rename from examples/prototypes/const.ts rename to src/type/intersect/intersect-type.ts index 75399a0aa..991c3674e 100644 --- a/examples/prototypes/const.ts +++ b/src/type/intersect/intersect-type.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/prototypes +@sinclair/typebox/type The MIT License (MIT) @@ -26,36 +26,31 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Type, ObjectMap, TypeGuard, TSchema, TIntersect, TUnion, TObject, TProperties, TReadonly, AssertProperties, AssertType, AssertRest, Evaluate, ReadonlyUnwrapType } from '@sinclair/typebox' +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' -// ------------------------------------------------------------------------------------- -// TConst -// ------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ +// IntersectStatic +// ------------------------------------------------------------------ // prettier-ignore -export type TConstArray = T extends [infer L, ...infer R] - ? [TConst, E>, ...TConstArray, E>] - : [] +type TIntersectStatic = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TIntersectStatic> + : Acc +// ------------------------------------------------------------------ +// TIntersect +// ------------------------------------------------------------------ // prettier-ignore -export type TConstProperties = Evaluate, E> -}>> +export type TUnevaluatedProperties = undefined | TSchema | boolean // prettier-ignore -export type TConst = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - E extends true ? T : TReadonly -// ------------------------------------------------------------------------------------- -// Const -// ------------------------------------------------------------------------------------- -/** `[Experimental]` Assigns readonly to all interior properties */ -export function Const(schema: T): TConst { - const mappable = (TypeGuard.TIntersect(schema) || TypeGuard.TUnion(schema) || TypeGuard.TObject(schema)) - // prettier-ignore - return mappable ? ObjectMap.Map(schema, (object) => { - const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: Type.Readonly(object.properties[key] )} - }, {} as TProperties) - return Type.Object(properties, {...object}) - }, {}) : schema as any -} \ No newline at end of file +export interface IntersectOptions extends SchemaOptions { + unevaluatedProperties?: TUnevaluatedProperties +} +// prettier-ignore +export interface TIntersect extends TSchema, IntersectOptions { + [Kind]: 'Intersect' + static: TIntersectStatic + type?: 'object' + allOf: [...T] +} diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts new file mode 100644 index 000000000..b06c1dd80 --- /dev/null +++ b/src/type/intersect/intersect.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { CloneType } from '../clone/type' +import { Never, type TNever } from '../never/index' + +import { TIntersect, IntersectOptions } from './intersect-type' +import { IntersectCreate } from './intersect-create' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTransform } from '../guard/type' +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +export type Intersect = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + TIntersect +) +/** `[Json]` Creates an evaluated Intersect type */ +export function Intersect(T: [...T], options: IntersectOptions = {}): Intersect { + if (T.length === 0) return Never(options) as Intersect + if (T.length === 1) return CloneType(T[0], options) as Intersect + if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') + return IntersectCreate(T, options) as Intersect +} diff --git a/src/type/intrinsic/capitalize.ts b/src/type/intrinsic/capitalize.ts new file mode 100644 index 000000000..0bb053da8 --- /dev/null +++ b/src/type/intrinsic/capitalize.ts @@ -0,0 +1,37 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Intrinsic, type TIntrinsic } from './intrinsic' + +// prettier-ignore +export type TCapitalize = TIntrinsic +/** `[Json]` Intrinsic function to Capitalize LiteralString types */ +export function Capitalize(T: T, options: SchemaOptions = {}): TCapitalize { + return Intrinsic(T, 'Capitalize', options) +} diff --git a/src/type/intrinsic/index.ts b/src/type/intrinsic/index.ts new file mode 100644 index 000000000..d5c9aedc3 --- /dev/null +++ b/src/type/intrinsic/index.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './capitalize' +export * from './intrinsic-from-mapped-key' +export * from './intrinsic' +export * from './lowercase' +export * from './uncapitalize' +export * from './uppercase' diff --git a/src/type/intrinsic/intrinsic-from-mapped-key.ts b/src/type/intrinsic/intrinsic-from-mapped-key.ts new file mode 100644 index 000000000..b1c1b5f3d --- /dev/null +++ b/src/type/intrinsic/intrinsic-from-mapped-key.ts @@ -0,0 +1,113 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { Assert } from '../helpers/index' +import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' +import { Intrinsic, type TIntrinsic, type IntrinsicMode } from './intrinsic' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' + +// ------------------------------------------------------------------ +// MappedIntrinsicPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIntrinsicPropertyKey< + K extends PropertyKey, + M extends IntrinsicMode, +> = { + [_ in K]: TIntrinsic>, M> + } +// prettier-ignore +function MappedIntrinsicPropertyKey< + K extends PropertyKey, + M extends IntrinsicMode, +>(K: K, M: M, options: SchemaOptions): TMappedIntrinsicPropertyKey { + return { + [K]: Intrinsic(Literal(K as TLiteralValue), M, options) + } as TMappedIntrinsicPropertyKey +} +// ------------------------------------------------------------------ +// MappedIntrinsicPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIntrinsicPropertyKeys< + K extends PropertyKey[], + M extends IntrinsicMode, + Acc extends TProperties = {} +> = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TMappedIntrinsicPropertyKeys> + : Acc +) +// prettier-ignore +function MappedIntrinsicPropertyKeys< + K extends PropertyKey[], + M extends IntrinsicMode +>(K: [...K], M: M, options: SchemaOptions): TMappedIntrinsicPropertyKeys { + return K.reduce((Acc, L) => { + return { ...Acc, ...MappedIntrinsicPropertyKey(L, M, options) } + }, {} as TProperties) as TMappedIntrinsicPropertyKeys +} +// ------------------------------------------------------------------ +// MappedIntrinsicProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedIntrinsicProperties< + K extends TMappedKey, + M extends IntrinsicMode, +> = ( + TMappedIntrinsicPropertyKeys + ) +// prettier-ignore +function MappedIntrinsicProperties< + K extends TMappedKey, + M extends IntrinsicMode, +>(T: K, M: M, options: SchemaOptions): TMappedIntrinsicProperties { + return MappedIntrinsicPropertyKeys(T['keys'], M, options) as TMappedIntrinsicProperties +} +// ------------------------------------------------------------------ +// IntrinsicFromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export type TIntrinsicFromMappedKey< + K extends TMappedKey, + M extends IntrinsicMode, + P extends TProperties = TMappedIntrinsicProperties +> = ( + TMappedResult

      +) +// prettier-ignore +export function IntrinsicFromMappedKey< + K extends TMappedKey, + M extends IntrinsicMode, + P extends TProperties = TMappedIntrinsicProperties +>(T: K, M: M, options: SchemaOptions): TMappedResult

      { + const P = MappedIntrinsicProperties(T, M, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts new file mode 100644 index 000000000..5080aa60f --- /dev/null +++ b/src/type/intrinsic/intrinsic.ts @@ -0,0 +1,148 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { TemplateLiteral, TemplateLiteralParseExact, IsTemplateLiteralExpressionFinite, TemplateLiteralExpressionGenerate, type TTemplateLiteral, type TTemplateLiteralKind } from '../template-literal/index' +import { IntrinsicFromMappedKey, type TIntrinsicFromMappedKey } from './intrinsic-from-mapped-key' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Union, type TUnion } from '../union/index' +import { type TMappedKey } from '../mapped/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedKey, IsTemplateLiteral, IsUnion, IsLiteral } from '../guard/type' +// ------------------------------------------------------------------ +// Apply +// ------------------------------------------------------------------ +function ApplyUncapitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return [first.toLowerCase(), rest].join('') +} +function ApplyCapitalize(value: string): string { + const [first, rest] = [value.slice(0, 1), value.slice(1)] + return [first.toUpperCase(), rest].join('') +} +function ApplyUppercase(value: string): string { + return value.toUpperCase() +} +function ApplyLowercase(value: string): string { + return value.toLowerCase() +} +// ------------------------------------------------------------------ +// IntrinsicMode +// ------------------------------------------------------------------ +export type IntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' +// ------------------------------------------------------------------ +// FromTemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteral = + M extends IntrinsicMode ? + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? [TIntrinsic, ...TFromTemplateLiteral] + : T + : T +function FromTemplateLiteral(schema: TTemplateLiteral, mode: IntrinsicMode, options: SchemaOptions): TFromTemplateLiteral { + // note: template literals require special runtime handling as they are encoded in string patterns. + // This diverges from the mapped type which would otherwise map on the template literal kind. + const expression = TemplateLiteralParseExact(schema.pattern) + const finite = IsTemplateLiteralExpressionFinite(expression) + if (!finite) return { ...schema, pattern: FromLiteralValue(schema.pattern, mode) } as any + const strings = [...TemplateLiteralExpressionGenerate(expression)] + const literals = strings.map((value) => Literal(value)) + const mapped = FromRest(literals as any, mode) + const union = Union(mapped) + return TemplateLiteral([union], options) as unknown as TFromTemplateLiteral +} +// ------------------------------------------------------------------ +// FromLiteralValue +// ------------------------------------------------------------------ +// prettier-ignore +type TFromLiteralValue = ( + T extends string ? + M extends 'Uncapitalize' ? Uncapitalize : + M extends 'Capitalize' ? Capitalize : + M extends 'Uppercase' ? Uppercase : + M extends 'Lowercase' ? Lowercase : + string + : T +) +// prettier-ignore +function FromLiteralValue(value: TLiteralValue, mode: IntrinsicMode) { + return ( + typeof value === 'string' ? ( + mode === 'Uncapitalize' ? ApplyUncapitalize(value) : + mode === 'Capitalize' ? ApplyCapitalize(value) : + mode === 'Uppercase' ? ApplyUppercase(value) : + mode === 'Lowercase' ? ApplyLowercase(value) : + value + ) : value.toString() + ) +} +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +// prettier-ignore +function FromRest(T: [...T], M: M): TFromRest { + return T.map(L => Intrinsic(L, M)) as TFromRest +} +// ------------------------------------------------------------------ +// TIntrinsic +// ------------------------------------------------------------------ +// prettier-ignore +export type TIntrinsic = + // Intrinsic-Mapped-Inference + T extends TMappedKey ? TIntrinsicFromMappedKey : + // Standard-Inference + T extends TTemplateLiteral ? TTemplateLiteral> : + T extends TUnion ? TUnion> : + T extends TLiteral ? TLiteral> : + T +/** Applies an intrinsic string manipulation to the given type. */ +export function Intrinsic(schema: T, mode: M, options?: SchemaOptions): TIntrinsicFromMappedKey +/** Applies an intrinsic string manipulation to the given type. */ +export function Intrinsic(schema: T, mode: M, options?: SchemaOptions): TIntrinsic +/** Applies an intrinsic string manipulation to the given type. */ +export function Intrinsic(schema: TSchema, mode: IntrinsicMode, options: SchemaOptions = {}): any { + // prettier-ignore + return ( + // Intrinsic-Mapped-Inference + IsMappedKey(schema) ? IntrinsicFromMappedKey(schema, mode, options) : + // Standard-Inference + IsTemplateLiteral(schema) ? FromTemplateLiteral(schema, mode, schema) : + IsUnion(schema) ? Union(FromRest(schema.anyOf, mode), options) : + IsLiteral(schema) ? Literal(FromLiteralValue(schema.const, mode), options) : + schema + ) +} diff --git a/src/type/intrinsic/lowercase.ts b/src/type/intrinsic/lowercase.ts new file mode 100644 index 000000000..835d39b4f --- /dev/null +++ b/src/type/intrinsic/lowercase.ts @@ -0,0 +1,37 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Intrinsic, type TIntrinsic } from './intrinsic' + +// prettier-ignore +export type TLowercase = TIntrinsic +/** `[Json]` Intrinsic function to Lowercase LiteralString types */ +export function Lowercase(T: T, options: SchemaOptions = {}): TLowercase { + return Intrinsic(T, 'Lowercase', options) +} diff --git a/src/type/intrinsic/uncapitalize.ts b/src/type/intrinsic/uncapitalize.ts new file mode 100644 index 000000000..d69502253 --- /dev/null +++ b/src/type/intrinsic/uncapitalize.ts @@ -0,0 +1,37 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Intrinsic, type TIntrinsic } from './intrinsic' + +// prettier-ignore +export type TUncapitalize = TIntrinsic +/** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ +export function Uncapitalize(T: T, options: SchemaOptions = {}): TUncapitalize { + return Intrinsic(T, 'Uncapitalize', options) +} diff --git a/src/type/intrinsic/uppercase.ts b/src/type/intrinsic/uppercase.ts new file mode 100644 index 000000000..52ee4fa67 --- /dev/null +++ b/src/type/intrinsic/uppercase.ts @@ -0,0 +1,37 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Intrinsic, type TIntrinsic } from './intrinsic' + +// prettier-ignore +export type TUppercase = TIntrinsic +/** `[Json]` Intrinsic function to Uppercase LiteralString types */ +export function Uppercase(T: T, options: SchemaOptions = {}): TUppercase { + return Intrinsic(T, 'Uppercase', options) +} diff --git a/src/type/iterator/index.ts b/src/type/iterator/index.ts new file mode 100644 index 000000000..8c41f54e3 --- /dev/null +++ b/src/type/iterator/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './iterator' diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts new file mode 100644 index 000000000..74a37a236 --- /dev/null +++ b/src/type/iterator/iterator.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { CloneType } from '../clone/type' +import { Kind } from '../symbols/index' + +export interface TIterator extends TSchema { + [Kind]: 'Iterator' + static: IterableIterator> + type: 'Iterator' + items: T +} +/** `[JavaScript]` Creates an Iterator type */ +export function Iterator(items: T, options: SchemaOptions = {}): TIterator { + return { + ...options, + [Kind]: 'Iterator', + type: 'Iterator', + items: CloneType(items), + } as unknown as TIterator +} diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts new file mode 100644 index 000000000..03a32c098 --- /dev/null +++ b/src/type/keyof/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './keyof-from-mapped-result' +export * from './keyof-property-keys' +export * from './keyof' diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts new file mode 100644 index 000000000..97709295c --- /dev/null +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -0,0 +1,83 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { KeyOf, type TKeyOf } from './keyof' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + K extends TProperties +> = ( + { [K2 in keyof K]: TKeyOf } +) +// prettier-ignore +function FromProperties< + K extends TProperties +>(K: K, options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { + return {...Acc, [K2]: KeyOf(K[K2], options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult +>(R: R, options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// KeyOfFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TKeyOfFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function KeyOfFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +>(R: R, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts new file mode 100644 index 000000000..63cb1a49e --- /dev/null +++ b/src/type/keyof/keyof-property-keys.ts @@ -0,0 +1,176 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { type ZeroString, type UnionToTuple, Increment } from '../helpers/index' +import type { TRecursive } from '../recursive/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TTuple } from '../tuple/index' +import type { TArray } from '../array/index' +import type { TObject, TProperties } from '../object/index' +import { SetUnionMany, SetIntersectMany, type TSetUnionMany, type TSetIntersectMany } from '../sets/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsRecord } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +) +// prettier-ignore +function FromRest(T: [...T]): TFromRest { + return T.reduce((Acc, L) => { + return [...Acc, KeyOfPropertyKeys(L)] + }, [] as PropertyKey[][]) as TFromRest +} +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect< + T extends TSchema[], + C extends PropertyKey[][] = TFromRest, + R extends PropertyKey[] = TSetUnionMany +> = R +// prettier-ignore +function FromIntersect(T: [...T]): FromIntersect { + const C = FromRest(T) as PropertyKey[][] + const R = SetUnionMany(C) + return R as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion< + T extends TSchema[], + C extends PropertyKey[][] = TFromRest, + R extends PropertyKey[] = TSetIntersectMany +> = R +// prettier-ignore +function FromUnion(T: [...T]): FromUnion { + const C = FromRest(T) as PropertyKey[][] + const R = SetIntersectMany(C) + return R as FromUnion +} +// ------------------------------------------------------------------ +// FromTuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = + T extends [infer _ extends TSchema, ...infer R extends TSchema[]] + ? TFromTuple, [...Acc, I]> + : Acc +// prettier-ignore +function FromTuple(T: [...T]): TFromTuple { + return T.map((_, I) => I.toString()) as TFromTuple +} +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray<_ extends TSchema> = ( + ['[number]'] +) +// prettier-ignore +function FromArray<_ extends TSchema>(_: _): TFromArray<_> { + return ( + ['[number]'] + ) +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties = ( + UnionToTuple +) +// prettier-ignore +function FromProperties(T: T): TFromProperties { + return ( + globalThis.Object.getOwnPropertyNames(T) + ) as TFromProperties +} +// ------------------------------------------------------------------ +// FromPatternProperties +// ------------------------------------------------------------------ +// prettier-ignore +function FromPatternProperties(patternProperties: Record): string[] { + if(!includePatternProperties) return [] + const patternPropertyKeys = globalThis.Object.getOwnPropertyNames(patternProperties) + return patternPropertyKeys.map(key => { + return (key[0] === '^' && key[key.length - 1] === '$') + ? key.slice(1, key.length - 1) + : key + }) +} +// ------------------------------------------------------------------ +// KeyOfPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +export type TKeyOfPropertyKeys = ( + T extends TRecursive ? TKeyOfPropertyKeys : + T extends TIntersect ? FromIntersect : + T extends TUnion ? FromUnion : + T extends TTuple ? TFromTuple : + T extends TArray ? TFromArray : + T extends TObject ? TFromProperties : + [] +) +/** Returns a tuple of PropertyKeys derived from the given TSchema. */ +// prettier-ignore +export function KeyOfPropertyKeys(T: T): TKeyOfPropertyKeys { + return ( + IsIntersect(T) ? FromIntersect(T.allOf) : + IsUnion(T) ? FromUnion(T.anyOf) : + IsTuple(T) ? FromTuple(T.items ?? []) : + IsArray(T) ? FromArray(T.items) : + IsObject(T) ? FromProperties(T.properties) : + IsRecord(T) ? FromPatternProperties(T.patternProperties) : + [] + ) as TKeyOfPropertyKeys +} +// ---------------------------------------------------------------- +// KeyOfPattern +// ---------------------------------------------------------------- +let includePatternProperties = false +/** Returns a regular expression pattern derived from the given TSchema */ +export function KeyOfPattern(schema: TSchema): string { + includePatternProperties = true + const keys = KeyOfPropertyKeys(schema) + includePatternProperties = false + const pattern = keys.map((key) => `(${key})`) + return `^(${pattern.join('|')})$` +} diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts new file mode 100644 index 000000000..1c6192136 --- /dev/null +++ b/src/type/keyof/keyof.ts @@ -0,0 +1,85 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Assert, Ensure } from '../helpers/index' +import type { TMappedResult } from '../mapped/index' +import type { SchemaOptions } from '../schema/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Number, type TNumber } from '../number/index' +import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from './keyof-property-keys' +import { UnionEvaluated, type TUnionEvaluated } from '../union/index' +import { CloneType } from '../clone/type' +import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedResult } from '../guard/type' +// ------------------------------------------------------------------ +// FromPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKeys = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? L extends '[number]' + ? TFromPropertyKeys + : TFromPropertyKeys>]> + : Acc +) +// prettier-ignore +function FromPropertyKeys(T: [...T]): TFromPropertyKeys { + return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as TFromPropertyKeys +} +// ------------------------------------------------------------------ +// KeyOfTypeResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TKeyOf< + T extends TSchema, + K extends PropertyKey[] = TKeyOfPropertyKeys, + S extends TSchema[] = TFromPropertyKeys, + U = TUnionEvaluated +> = ( + Ensure +) +/** `[Json]` Creates a KeyOf type */ +export function KeyOf(T: T, options?: SchemaOptions): TKeyOfFromMappedResult +/** `[Json]` Creates a KeyOf type */ +export function KeyOf(T: T, options?: SchemaOptions): TKeyOf +/** `[Json]` Creates a KeyOf type */ +export function KeyOf(T: TSchema, options: SchemaOptions = {}): any { + if (IsMappedResult(T)) { + return KeyOfFromMappedResult(T, options) + } else { + const K = KeyOfPropertyKeys(T) + const S = FromPropertyKeys(K) + const U = UnionEvaluated(S) + return CloneType(U, options) + } +} diff --git a/src/type/literal/index.ts b/src/type/literal/index.ts new file mode 100644 index 000000000..d4cb53ef2 --- /dev/null +++ b/src/type/literal/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './literal' diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts new file mode 100644 index 000000000..8754ffd7c --- /dev/null +++ b/src/type/literal/literal.ts @@ -0,0 +1,53 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TLiteralValue +// ------------------------------------------------------------------ +export type TLiteralValue = boolean | number | string // | bigint - supported but variant disable due to potential numeric type conflicts + +// ------------------------------------------------------------------ +// TLiteral +// ------------------------------------------------------------------ +export interface TLiteral extends TSchema { + [Kind]: 'Literal' + static: T + const: T +} +/** `[Json]` Creates a Literal type */ +export function Literal(value: T, options: SchemaOptions = {}): TLiteral { + return { + ...options, + [Kind]: 'Literal', + const: value, + type: typeof value as 'string' | 'number' | 'boolean', + } as unknown as TLiteral +} diff --git a/src/type/mapped/index.ts b/src/type/mapped/index.ts new file mode 100644 index 000000000..5e1f19bb1 --- /dev/null +++ b/src/type/mapped/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './mapped-key' +export * from './mapped-result' +export * from './mapped' diff --git a/src/type/mapped/mapped-key.ts b/src/type/mapped/mapped-key.ts new file mode 100644 index 000000000..24ad79526 --- /dev/null +++ b/src/type/mapped/mapped-key.ts @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TMappedKey extends TSchema { + [Kind]: 'MappedKey' + static: T[number] + keys: T +} +// prettier-ignore +export function MappedKey(T: [...T]): TMappedKey { + return { + [Kind]: 'MappedKey', + keys: T + } as unknown as TMappedKey +} diff --git a/src/type/mapped/mapped-result.ts b/src/type/mapped/mapped-result.ts new file mode 100644 index 000000000..84908ea3b --- /dev/null +++ b/src/type/mapped/mapped-result.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TProperties } from '../object/index' +import { Kind } from '../symbols/index' + +export interface TMappedResult extends TSchema { + [Kind]: 'MappedResult' + properties: T + static: unknown +} +// prettier-ignore +export function MappedResult(properties: T): TMappedResult { + return { + [Kind]: 'MappedResult', + properties + } as never +} diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts new file mode 100644 index 000000000..49ab254e7 --- /dev/null +++ b/src/type/mapped/mapped.ts @@ -0,0 +1,283 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Ensure, Evaluate, Assert } from '../helpers/index' +import { Kind, OptionalKind, ReadonlyKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' +// evaluation types +import { Array, type TArray } from '../array/index' +import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' +import { Constructor, type TConstructor } from '../constructor/index' +import { Function as FunctionType, type TFunction } from '../function/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Iterator, type TIterator } from '../iterator/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' +import { Optional, type TOptional } from '../optional/index' +import { Promise, type TPromise } from '../promise/index' +import { Readonly, type TReadonly } from '../readonly/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Union, type TUnion } from '../union/index' +// operator +import { SetIncludes, type TSetIncludes } from '../sets/index' +// mapping types +import { MappedResult, type TMappedResult } from './mapped-result' +import type { TMappedKey } from './mapped-key' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsArray, + IsAsyncIterator, + IsConstructor, + IsFunction, + IsIntersect, + IsIterator, + IsReadonly, + IsMappedResult, + IsMappedKey, + IsObject, + IsOptional, + IsPromise, + IsSchema, + IsTuple, + IsUnion, +} from '../guard/type' +// ------------------------------------------------------------------ +// FromMappedResult +// +// We only evaluate the context (K) if it is a keyof P. Otherwise we +// remap the back to a MappedResult with the expectation it will be +// evaluated by an outer context. Note that overlapping keys in the +// outer context may result in incorrect evaluation of the outer +// context. Reproduction code below. +// +// type S = { +// [L in 'A' | 'B']: { +// [R in 'B' | 'C']: [L, R] // issue: overlapping 'B' +// } +// } +// +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult = ( + K extends keyof P + ? FromSchemaType + : TMappedResult

      +) +// prettier-ignore +function FromMappedResult(K: K, P: P): TFromMappedResult { + return ( + K in P + ? FromSchemaType(K, P[K as string]) + : MappedResult(P) + ) as TFromMappedResult +} +// ------------------------------------------------------------------ +// MappedKeyToKnownMappedResultProperties +// +// This path is used when K is in the PropertyKey set of P. This is +// the standard (fast) path when not nesting mapped types. +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedKeyToKnownMappedResultProperties = { + [_ in K]: TLiteral> +} +// prettier-ignore +function MappedKeyToKnownMappedResultProperties(K: K): TMappedKeyToKnownMappedResultProperties { + return { [K]: Literal(K as TLiteralValue) } as TMappedKeyToKnownMappedResultProperties +} +// ------------------------------------------------------------------ +// MappedKeyToUnknownMappedResultProperties +// +// This path is used when K is outside the set of P. This indicates +// that the K originates from outside the current mappped type. This +// path is very slow as we need to construct a full MappedResult +// to be evaluated by the exterior mapped type. +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedKeyToUnknownMappedResultProperties

      = ( + P extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TMappedKeyToUnknownMappedResultProperties> }> + : Acc +) +// prettier-ignore +function MappedKeyToUnknownMappedResultProperties

      (P: [...P]): TMappedKeyToUnknownMappedResultProperties

      { + return P.reduce((Acc, L) => { + return { ...Acc, [L]: Literal(L as TLiteralValue) } + }, {} as TProperties) as TMappedKeyToUnknownMappedResultProperties

      +} +// ------------------------------------------------------------------ +// MappedKeyToMappedResultProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TMappedKeyToMappedResultProperties = ( + TSetIncludes extends true + ? TMappedKeyToKnownMappedResultProperties + : TMappedKeyToUnknownMappedResultProperties

      +) +// prettier-ignore +function MappedKeyToMappedResultProperties(K: K, P: [...P]): TMappedKeyToMappedResultProperties { + return ( + SetIncludes(P, K) + ? MappedKeyToKnownMappedResultProperties(K) + : MappedKeyToUnknownMappedResultProperties(P) + ) as TMappedKeyToMappedResultProperties +} +// prettier-ignore +type TFromMappedKey< + K extends PropertyKey, + P extends PropertyKey[], + R extends TProperties = TMappedKeyToMappedResultProperties +> = ( + TFromMappedResult +) +// prettier-ignore +function FromMappedKey(K: K, P: [...P]) { + const R = MappedKeyToMappedResultProperties(K, P) + return FromMappedResult(K, R) +} +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +) +// prettier-ignore +function FromRest(K: K, T: [...T]): TFromRest { + return T.map(L => FromSchemaType(K, L)) as TFromRest +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties +}>> = R +// prettier-ignore +function FromProperties(K: K, T: T): FromProperties { + return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K2) => { + return { ...Acc, [K2]: FromSchemaType(K, T[K2]) } + }, {}) as FromProperties +} +// ------------------------------------------------------------------ +// FromMappedPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromSchemaType = ( + // unevaluated modifier types + T extends TReadonly ? TReadonly> : + T extends TOptional ? TOptional> : + // unevaluated mapped types + T extends TMappedResult ? TFromMappedResult : + T extends TMappedKey ? TFromMappedKey : + // unevaluated types + T extends TConstructor ? TConstructor, FromSchemaType> : + T extends TFunction ? TFunction, FromSchemaType> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TIterator ? TIterator> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TTuple ? TTuple> : + T extends TObject ? TObject> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : + T +) +// prettier-ignore +function FromSchemaType(K: K, T: T): FromSchemaType { + return ( + // unevaluated modifier types + IsOptional(T) ? Optional(FromSchemaType(K, Discard(T, [OptionalKind]) as TSchema)) : + IsReadonly(T) ? Readonly(FromSchemaType(K, Discard(T, [ReadonlyKind]) as TSchema)) : + // unevaluated mapped types + IsMappedResult(T) ? FromMappedResult(K, T.properties) : + IsMappedKey(T) ? FromMappedKey(K, T.keys) : + // unevaluated types + IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) : + IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) : + IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items)) : + IsIterator(T) ? Iterator(FromSchemaType(K, T.items)) : + IsIntersect(T) ? Intersect(FromRest(K, T.allOf)) : + IsUnion(T) ? Union(FromRest(K, T.anyOf)) : + IsTuple(T) ? Tuple(FromRest(K, T.items ?? [])) : + IsObject(T) ? Object(FromProperties(K, T.properties)) : + IsArray(T) ? Array(FromSchemaType(K, T.items)) : + IsPromise(T) ? Promise(FromSchemaType(K, T.item)) : + T + ) as FromSchemaType +} +// ------------------------------------------------------------------ +// FromMappedFunctionReturnType +// ------------------------------------------------------------------ +// prettier-ignore +type FromMappedFunctionReturnType = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? FromMappedFunctionReturnType }> + : Acc +) +// prettier-ignore +function FromMappedFunctionReturnType(K: [...K], T: T, Acc: TProperties = {}): FromMappedFunctionReturnType { + return K.reduce((Acc, L) => { + return { ...Acc, [L]: FromSchemaType(L, T) } + }, {} as TProperties) as FromMappedFunctionReturnType +} +// ------------------------------------------------------------------ +// TMappedFunction +// ------------------------------------------------------------------ +// prettier-ignore +export type TMappedFunction> = (T: I) => TSchema +// ------------------------------------------------------------------ +// TMapped +// ------------------------------------------------------------------ +// prettier-ignore +export type TMapped< + K extends PropertyKey[], + F extends TMappedFunction, + R extends TProperties = Evaluate>>, +> = Ensure> + +/** `[Json]` Creates a Mapped object type */ +export function Mapped, F extends TMappedFunction = TMappedFunction, R extends TMapped = TMapped>(key: K, map: F, options?: ObjectOptions): R +/** `[Json]` Creates a Mapped object type */ +export function Mapped = TMappedFunction, R extends TMapped = TMapped>(key: [...K], map: F, options?: ObjectOptions): R +/** `[Json]` Creates a Mapped object type */ +export function Mapped(key: any, map: Function, options: ObjectOptions = {}) { + const K = IsSchema(key) ? IndexPropertyKeys(key) : (key as PropertyKey[]) + const RT = map({ [Kind]: 'MappedKey', keys: K } as TMappedKey) + const R = FromMappedFunctionReturnType(K, RT) + return CloneType(Object(R), options) +} diff --git a/src/type/never/index.ts b/src/type/never/index.ts new file mode 100644 index 000000000..913504405 --- /dev/null +++ b/src/type/never/index.ts @@ -0,0 +1,28 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ +export * from './never' diff --git a/src/type/never/never.ts b/src/type/never/never.ts new file mode 100644 index 000000000..7fc22f7fe --- /dev/null +++ b/src/type/never/never.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TNever extends TSchema { + [Kind]: 'Never' + static: never + not: {} +} +/** `[Json]` Creates a Never type */ +export function Never(options: SchemaOptions = {}): TNever { + return { + ...options, + [Kind]: 'Never', + not: {}, + } as unknown as TNever +} diff --git a/src/type/not/index.ts b/src/type/not/index.ts new file mode 100644 index 000000000..49cf9dca8 --- /dev/null +++ b/src/type/not/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './not' diff --git a/src/type/not/not.ts b/src/type/not/not.ts new file mode 100644 index 000000000..df0a93a74 --- /dev/null +++ b/src/type/not/not.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { CloneType } from '../clone/type' +import { Kind } from '../symbols/index' + +export interface TNot extends TSchema { + [Kind]: 'Not' + static: T extends TNot ? Static : unknown + not: T +} +/** `[Json]` Creates a Not type */ +export function Not(schema: T, options?: SchemaOptions): TNot { + return { + ...options, + [Kind]: 'Not', + not: CloneType(schema), + } as unknown as TNot +} diff --git a/src/type/null/index.ts b/src/type/null/index.ts new file mode 100644 index 000000000..281bef4f4 --- /dev/null +++ b/src/type/null/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './null' diff --git a/src/type/null/null.ts b/src/type/null/null.ts new file mode 100644 index 000000000..6dad0c7d8 --- /dev/null +++ b/src/type/null/null.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TNull extends TSchema { + [Kind]: 'Null' + static: null + type: 'null' +} +/** `[Json]` Creates a Null type */ +export function Null(options: SchemaOptions = {}): TNull { + return { + ...options, + [Kind]: 'Null', + type: 'null', + } as unknown as TNull +} diff --git a/src/type/number/index.ts b/src/type/number/index.ts new file mode 100644 index 000000000..c43f623c7 --- /dev/null +++ b/src/type/number/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './number' diff --git a/src/type/number/number.ts b/src/type/number/number.ts new file mode 100644 index 000000000..1ae3ed8fd --- /dev/null +++ b/src/type/number/number.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface NumberOptions extends SchemaOptions { + exclusiveMaximum?: number + exclusiveMinimum?: number + maximum?: number + minimum?: number + multipleOf?: number +} +export interface TNumber extends TSchema, NumberOptions { + [Kind]: 'Number' + static: number + type: 'number' +} +/** `[Json]` Creates a Number type */ +export function Number(options: NumberOptions = {}): TNumber { + return { + ...options, + [Kind]: 'Number', + type: 'number', + } as unknown as TNumber +} diff --git a/src/type/object/index.ts b/src/type/object/index.ts new file mode 100644 index 000000000..5bebd5824 --- /dev/null +++ b/src/type/object/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './object' diff --git a/src/type/object/object.ts b/src/type/object/object.ts new file mode 100644 index 000000000..a68c53f9e --- /dev/null +++ b/src/type/object/object.ts @@ -0,0 +1,99 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import type { Evaluate } from '../helpers/index' +import type { TReadonly } from '../readonly/index' +import type { TOptional } from '../optional/index' +import { CloneType } from '../clone/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsOptional, IsSchema } from '../guard/type' + +// ------------------------------------------------------------------ +// ObjectStatic +// ------------------------------------------------------------------ +type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? K : never) : never }[keyof T] +type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? never : K) : never }[keyof T] +type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? (T[K] extends TReadonly ? never : K) : never }[keyof T] +type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> + +// prettier-ignore +type ObjectStaticProperties> = Evaluate<( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +)> +// prettier-ignore +type ObjectStatic = ObjectStaticProperties +}> +// ------------------------------------------------------------------ +// TProperties +// ------------------------------------------------------------------ +export type TPropertyKey = string | number // Consider making this PropertyKey +export type TProperties = Record +// ------------------------------------------------------------------ +// TObject +// ------------------------------------------------------------------ +export type TAdditionalProperties = undefined | TSchema | boolean +export interface ObjectOptions extends SchemaOptions { + /** Additional property constraints for this object */ + additionalProperties?: TAdditionalProperties + /** The minimum number of properties allowed on this object */ + minProperties?: number + /** The maximum number of properties allowed on this object */ + maxProperties?: number +} +export interface TObject extends TSchema, ObjectOptions { + [Kind]: 'Object' + static: ObjectStatic + additionalProperties?: TAdditionalProperties + type: 'object' + properties: T + required?: string[] +} +/** `[Json]` Creates an Object type */ +export function _Object(properties: T, options: ObjectOptions = {}): TObject { + const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) + const optionalKeys = propertyKeys.filter((key) => IsOptional(properties[key])) + const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) + const clonedAdditionalProperties = IsSchema(options.additionalProperties) ? { additionalProperties: CloneType(options.additionalProperties) } : {} + const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: CloneType(properties[key]) }), {} as TProperties) + return (requiredKeys.length > 0 + ? { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys } + : { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) as unknown as TObject +} + +/** `[Json]` Creates an Object type */ +export const Object = _Object diff --git a/src/type/omit/index.ts b/src/type/omit/index.ts new file mode 100644 index 000000000..c5e44415a --- /dev/null +++ b/src/type/omit/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './omit-from-mapped-key' +export * from './omit-from-mapped-result' +export * from './omit' diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts new file mode 100644 index 000000000..efac50d8f --- /dev/null +++ b/src/type/omit/omit-from-mapped-key.ts @@ -0,0 +1,111 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' +import { Omit, type TOmit } from './omit' + +// ------------------------------------------------------------------ +// FromPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKey< + T extends TSchema, + K extends PropertyKey, +> = { + [_ in K]: TOmit + } +// prettier-ignore +function FromPropertyKey< + T extends TSchema, + K extends PropertyKey, +>(T: T, K: K, options: SchemaOptions): TFromPropertyKey { + return { + [K]: Omit(T, [K], options) + } as TFromPropertyKey +} +// ------------------------------------------------------------------ +// FromPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKeys< + T extends TSchema, + K extends PropertyKey[], + Acc extends TProperties = {} +> = ( + K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] + ? TFromPropertyKeys> + : Acc +) +// prettier-ignore +function FromPropertyKeys< + T extends TSchema, + K extends PropertyKey[] +>(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { + return K.reduce((Acc, LK) => { + return { ...Acc, ...FromPropertyKey(T, LK, options) } + }, {} as TProperties) as TFromPropertyKeys +} +// ------------------------------------------------------------------ +// FromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedKey< + T extends TSchema, + K extends TMappedKey, +> = ( + TFromPropertyKeys +) +// prettier-ignore +function FromMappedKey< + T extends TSchema, + K extends TMappedKey, +>(T: T, K: K, options: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(T, K.keys, options) as TFromMappedKey +} +// ------------------------------------------------------------------ +// OmitFromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export type TOmitFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TFromMappedKey +> = ( + TMappedResult

      +) +// prettier-ignore +export function OmitFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TFromMappedKey +>(T: T, K: K, options: SchemaOptions): TMappedResult

      { + const P = FromMappedKey(T, K, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts new file mode 100644 index 000000000..c6dd8b9a7 --- /dev/null +++ b/src/type/omit/omit-from-mapped-result.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Omit, type TOmit } from './omit' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + K extends PropertyKey[], +> = ( + { [K2 in keyof P]: TOmit } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + K extends PropertyKey[], +>(P: P, K: [...K], options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Omit(P[K2], K, options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + K extends PropertyKey[], +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + K extends PropertyKey[] +>(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, K, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// TOmitFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TOmitFromMappedResult< + T extends TMappedResult, + K extends PropertyKey[], + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function OmitFromMappedResult< + R extends TMappedResult, + K extends PropertyKey[], + P extends TProperties = TFromMappedResult +>(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, K, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts new file mode 100644 index 000000000..e0afd1060 --- /dev/null +++ b/src/type/omit/omit.ts @@ -0,0 +1,132 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TupleToUnion, Evaluate } from '../helpers/index' +import { type TRecursive } from '../recursive/index' +import type { TMappedKey, TMappedResult } from '../mapped/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Union, type TUnion } from '../union/index' +import { Object, type TObject, type TProperties } from '../object/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' +import { Discard } from '../discard/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key' +import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } from '../guard/type' + +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntersect = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromIntersect]> + : Acc +) +// prettier-ignore +function FromIntersect(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as TFromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromUnion]> + : Acc +) +// prettier-ignore +function FromUnion(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as TFromUnion +} +// ------------------------------------------------------------------ +// FromProperty +// ------------------------------------------------------------------ +// prettier-ignore +function FromProperty, K extends PropertyKey>(T: T, K: K) { + const { [K]: _, ...R } = T + return R as TProperties +} +// prettier-ignore +type TFromProperties> = Evaluate> +// prettier-ignore +function FromProperties(T: T, K: K) { + return K.reduce((T, K2) => { + return FromProperty(T, K2) + }, T as TProperties) +} +// ------------------------------------------------------------------ +// OmitResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TOmitResolve = + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +// prettier-ignore +export function OmitResolve(T: T, K: [...K]): TOmitResolve { + return ( + IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : + IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : + IsObject(T) ? Object(FromProperties(T.properties, K)) : + Object({}) + ) as TOmitResolve +} +// ------------------------------------------------------------------ +// TOmit +// ------------------------------------------------------------------ +// prettier-ignore +export type TOmit = TOmitResolve + +/** `[Json]` Constructs a type whose keys are omitted from the given type */ +export function Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult +/** `[Json]` Constructs a type whose keys are omitted from the given type */ +export function Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey +/** `[Json]` Constructs a type whose keys are omitted from the given type */ +export function Omit>(T: T, K: K, options?: SchemaOptions): TOmit +/** `[Json]` Constructs a type whose keys are omitted from the given type */ +export function Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit +export function Omit(T: TSchema, K: any, options: SchemaOptions = {}): any { + // mapped + if (IsMappedKey(K)) return OmitFromMappedKey(T, K, options) + if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) + // non-mapped + const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(OmitResolve(T, I), options) + return { ...D, ...R } +} diff --git a/src/type/optional/index.ts b/src/type/optional/index.ts new file mode 100644 index 000000000..b861f8e73 --- /dev/null +++ b/src/type/optional/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './optional-from-mapped-result' +export * from './optional' diff --git a/src/type/optional/optional-from-mapped-result.ts b/src/type/optional/optional-from-mapped-result.ts new file mode 100644 index 000000000..28ad4eaf6 --- /dev/null +++ b/src/type/optional/optional-from-mapped-result.ts @@ -0,0 +1,88 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Optional, type TOptionalWithFlag } from './optional' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + F extends boolean, +> = ( + { [K2 in keyof P]: TOptionalWithFlag } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + F extends boolean, +>(P: P, F: F): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Optional(P[K2], F) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + F extends boolean, +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + F extends boolean, +>(R: R, F: F): TFromMappedResult { + return FromProperties(R.properties, F) as TFromMappedResult +} +// ------------------------------------------------------------------ +// OptionalFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TOptionalFromMappedResult< + R extends TMappedResult, + F extends boolean, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function OptionalFromMappedResult< + R extends TMappedResult, + F extends boolean, + P extends TProperties = TFromMappedResult +>(R: R, F: F): TMappedResult

      { + const P = FromMappedResult(R, F) as unknown as P + return MappedResult(P) +} diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts new file mode 100644 index 000000000..867117b4d --- /dev/null +++ b/src/type/optional/optional.ts @@ -0,0 +1,82 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Ensure } from '../helpers/index' +import { OptionalKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' +import type { TMappedResult } from '../mapped/index' + +import { OptionalFromMappedResult, type TOptionalFromMappedResult } from './optional-from-mapped-result' +import { IsMappedResult } from '../guard/type' +// ------------------------------------------------------------------ +// RemoveOptional +// ------------------------------------------------------------------ +type TRemoveOptional = T extends TOptional ? S : T +function RemoveOptional(schema: T) { + return Discard(CloneType(schema), [OptionalKind]) +} +// ------------------------------------------------------------------ +// AddOptional +// ------------------------------------------------------------------ +type TAddOptional = T extends TOptional ? TOptional : Ensure> +function AddOptional(schema: T) { + return { ...CloneType(schema), [OptionalKind]: 'Optional' } +} +// prettier-ignore +export type TOptionalWithFlag = + F extends false + ? TRemoveOptional + : TAddOptional +// prettier-ignore +function OptionalWithFlag(schema: T, F: F) { + return ( + F === false + ? RemoveOptional(schema) + : AddOptional(schema) + ) +} +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +export type TOptional = T & { [OptionalKind]: 'Optional' } + +/** `[Json]` Creates a Optional property */ +export function Optional(schema: T, enable: F): TOptionalFromMappedResult +/** `[Json]` Creates a Optional property */ +export function Optional(schema: T, enable: F): TOptionalWithFlag +/** `[Json]` Creates a Optional property */ +export function Optional(schema: T): TOptionalFromMappedResult +/** `[Json]` Creates a Optional property */ +export function Optional(schema: T): TOptionalWithFlag +/** `[Json]` Creates a Optional property */ +export function Optional(schema: TSchema, enable?: boolean): any { + const F = enable ?? true + return IsMappedResult(schema) ? OptionalFromMappedResult(schema, F) : OptionalWithFlag(schema, F) +} diff --git a/src/type/parameters/index.ts b/src/type/parameters/index.ts new file mode 100644 index 000000000..195fafbb0 --- /dev/null +++ b/src/type/parameters/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './parameters' diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts new file mode 100644 index 000000000..e2d4bb2f0 --- /dev/null +++ b/src/type/parameters/parameters.ts @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TFunction } from '../function/index' +import type { Ensure } from '../helpers/index' +import { Tuple, type TTuple } from '../tuple/index' +import { CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +export type TParameters = Ensure> + +/** `[JavaScript]` Extracts the Parameters from the given Function type */ +export function Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + return Tuple(CloneRest(schema.parameters), { ...options }) +} diff --git a/src/type/partial/index.ts b/src/type/partial/index.ts new file mode 100644 index 000000000..af7c8ba58 --- /dev/null +++ b/src/type/partial/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './partial-from-mapped-result' +export * from './partial' diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts new file mode 100644 index 000000000..23fb89d14 --- /dev/null +++ b/src/type/partial/partial-from-mapped-result.ts @@ -0,0 +1,83 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Partial, type TPartial } from './partial' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties +> = ( + { [K2 in keyof P]: TPartial } +) +// prettier-ignore +function FromProperties< + P extends TProperties +>(K: P, options: SchemaOptions): TFromProperties

      { + return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { + return {...Acc, [K2]: Partial(K[K2], options) } + }, {}) as TFromProperties

      +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult +>(R: R, options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// TPartialFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TPartialFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function PartialFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +>(R: R, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts new file mode 100644 index 000000000..d49dad8ce --- /dev/null +++ b/src/type/partial/partial.ts @@ -0,0 +1,114 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Evaluate } from '../helpers/index' +import type { TMappedResult } from '../mapped/index' +import { type TReadonlyOptional } from '../readonly-optional/index' +import { type TOptional, Optional } from '../optional/index' +import { type TReadonly } from '../readonly/index' +import { type TRecursive } from '../recursive/index' +import { type TObject, type TProperties, Object } from '../object/index' +import { type TIntersect, Intersect } from '../intersect/index' +import { type TUnion, Union } from '../union/index' +import { Discard } from '../discard/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' + +import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +) +// prettier-ignore +function FromRest(T: [...T]): TFromRest { + return T.map(L => PartialResolve(L)) as TFromRest +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties = Evaluate<{ + [K in keyof T]: + T[K] extends (TReadonlyOptional) ? TReadonlyOptional : + T[K] extends (TReadonly) ? TReadonlyOptional : + T[K] extends (TOptional) ? TOptional : + TOptional +}> +// prettier-ignore +function FromProperties(T: T) { + return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K) => { + return { ...Acc, [K]: Optional(T[K]) } + }, {} as TProperties) +} +// ------------------------------------------------------------------ +// PartialResolve +// ------------------------------------------------------------------ +// prettier-ignore +type PartialResolve = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) +// prettier-ignore +function PartialResolve(T: T): PartialResolve { + return ( + IsIntersect(T) ? Intersect(FromRest(T.allOf)) : + IsUnion(T) ? Union(FromRest(T.anyOf)) : + IsObject(T) ? Object(FromProperties(T.properties)) : + Object({}) + ) as PartialResolve +} +// ------------------------------------------------------------------ +// TPartial +// ------------------------------------------------------------------ +export type TPartial = PartialResolve + +/** `[Json]` Constructs a type where all properties are optional */ +export function Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult +/** `[Json]` Constructs a type where all properties are optional */ +export function Partial(T: T, options?: SchemaOptions): TPartial +/** `[Json]` Constructs a type where all properties are optional */ +export function Partial(T: TSchema, options: SchemaOptions = {}): any { + if (IsMappedResult(T)) return PartialFromMappedResult(T, options) + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(PartialResolve(T), options) + return { ...D, ...R } as any +} diff --git a/src/type/patterns/index.ts b/src/type/patterns/index.ts new file mode 100644 index 000000000..7dbf0a210 --- /dev/null +++ b/src/type/patterns/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './patterns' diff --git a/src/type/patterns/patterns.ts b/src/type/patterns/patterns.ts new file mode 100644 index 000000000..d9b9e8d79 --- /dev/null +++ b/src/type/patterns/patterns.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export const PatternBoolean = '(true|false)' +export const PatternNumber = '(0|[1-9][0-9]*)' +export const PatternString = '(.*)' +export const PatternBooleanExact = `^${PatternBoolean}$` +export const PatternNumberExact = `^${PatternNumber}$` +export const PatternStringExact = `^${PatternString}$` diff --git a/src/type/pick/index.ts b/src/type/pick/index.ts new file mode 100644 index 000000000..1fa9844ab --- /dev/null +++ b/src/type/pick/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './pick-from-mapped-key' +export * from './pick-from-mapped-result' +export * from './pick' diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts new file mode 100644 index 000000000..b5e30fa1e --- /dev/null +++ b/src/type/pick/pick-from-mapped-key.ts @@ -0,0 +1,111 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' +import { Pick, type TPick } from './pick' + +// ------------------------------------------------------------------ +// FromPropertyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKey< + T extends TSchema, + K extends PropertyKey, +> = { + [_ in K]: TPick + } +// prettier-ignore +function FromPropertyKey< + T extends TSchema, + K extends PropertyKey, +>(T: T, K: K, options: SchemaOptions): TFromPropertyKey { + return { + [K]: Pick(T, [K], options) + } as TFromPropertyKey +} +// ------------------------------------------------------------------ +// FromPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPropertyKeys< + T extends TSchema, + K extends PropertyKey[], + Acc extends TProperties = {} +> = ( + K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] + ? TFromPropertyKeys> + : Acc +) +// prettier-ignore +function FromPropertyKeys< + T extends TSchema, + K extends PropertyKey[] +>(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { + return K.reduce((Acc, LK) => { + return { ...Acc, ...FromPropertyKey(T, LK, options) } + }, {} as TProperties) as TFromPropertyKeys +} +// ------------------------------------------------------------------ +// FromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedKey< + T extends TSchema, + K extends TMappedKey, +> = ( + TFromPropertyKeys +) +// prettier-ignore +function FromMappedKey< + T extends TSchema, + K extends TMappedKey, +>(T: T, K: K, options: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(T, K.keys, options) as TFromMappedKey +} +// ------------------------------------------------------------------ +// PickFromMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export type TPickFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TFromMappedKey +> = ( + TMappedResult

      +) +// prettier-ignore +export function PickFromMappedKey< + T extends TSchema, + K extends TMappedKey, + P extends TProperties = TFromMappedKey +>(T: T, K: K, options: SchemaOptions): TMappedResult

      { + const P = FromMappedKey(T, K, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts new file mode 100644 index 000000000..4da4a6108 --- /dev/null +++ b/src/type/pick/pick-from-mapped-result.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Pick, type TPick } from './pick' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + K extends PropertyKey[], +> = ( + { [K2 in keyof P]: TPick } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + K extends PropertyKey[], +>(P: P, K: [...K], options: SchemaOptions): TFromProperties { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Pick(P[K2], K, options) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + K extends PropertyKey[], +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + K extends PropertyKey[] +>(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, K, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// PickFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TPickFromMappedResult< + T extends TMappedResult, + K extends PropertyKey[], + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function PickFromMappedResult< + R extends TMappedResult, + K extends PropertyKey[], + P extends TProperties = TFromMappedResult +>(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, K, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts new file mode 100644 index 000000000..336d47585 --- /dev/null +++ b/src/type/pick/pick.ts @@ -0,0 +1,122 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { TupleToUnion, Evaluate } from '../helpers/index' +import { type TRecursive } from '../recursive/index' +import { type TIntersect, Intersect } from '../intersect/index' +import { type TUnion, Union } from '../union/index' +import { type TObject, type TProperties, Object } from '../object/index' +import type { TMappedKey, TMappedResult } from '../mapped/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' +import { Discard } from '../discard/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' +import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } from '../guard/type' +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? FromIntersect]> + : Acc +function FromIntersect(T: T, K: K) { + return T.map((T) => PickResolve(T, K)) as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? FromUnion]> + : Acc +// prettier-ignore +function FromUnion(T: T, K: K) { + return T.map((T) => PickResolve(T, K)) as FromUnion +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties> = Evaluate> +// prettier-ignore +function FromProperties(T: T, K: K) { + return K.reduce((Acc, K) => { + return K in T ? { ...Acc, [K]: T[K as keyof T] } : Acc + }, {}) +} +// ------------------------------------------------------------------ +// PickResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type PickResolve = + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +// prettier-ignore +export function PickResolve(T: T, K: [...K]): PickResolve { + return ( + IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : + IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : + IsObject(T) ? Object(FromProperties(T.properties, K)) : + Object({}) + ) as PickResolve +} +// ------------------------------------------------------------------ +// TPick +// ------------------------------------------------------------------ +export type TPick = PickResolve + +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Pick>(T: T, K: K, options?: SchemaOptions): TPick +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick +export function Pick(T: TSchema, K: any, options: SchemaOptions = {}): any { + // mapped + if (IsMappedKey(K)) return PickFromMappedKey(T, K, options) + if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) + // non-mapped + const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(PickResolve(T, I), options) + return { ...D, ...R } +} diff --git a/src/type/promise/index.ts b/src/type/promise/index.ts new file mode 100644 index 000000000..55150952e --- /dev/null +++ b/src/type/promise/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './promise' diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts new file mode 100644 index 000000000..48b99d0ce --- /dev/null +++ b/src/type/promise/promise.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { CloneType } from '../clone/type' +import { Kind } from '../symbols/index' + +export interface TPromise extends TSchema { + [Kind]: 'Promise' + static: Promise> + type: 'Promise' + item: TSchema +} +/** `[JavaScript]` Creates a Promise type */ +export function Promise(item: T, options: SchemaOptions = {}): TPromise { + return { + ...options, + [Kind]: 'Promise', + type: 'Promise', + item: CloneType(item), + } as unknown as TPromise +} diff --git a/src/type/readonly-optional/index.ts b/src/type/readonly-optional/index.ts new file mode 100644 index 000000000..3e44a84c0 --- /dev/null +++ b/src/type/readonly-optional/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './readonly-optional' diff --git a/src/type/readonly-optional/readonly-optional.ts b/src/type/readonly-optional/readonly-optional.ts new file mode 100644 index 000000000..788ede112 --- /dev/null +++ b/src/type/readonly-optional/readonly-optional.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { type TReadonly, Readonly } from '../readonly/index' +import { type TOptional, Optional } from '../optional/index' + +export type TReadonlyOptional = TOptional & TReadonly + +/** `[Json]` Creates a Readonly and Optional property */ +export function ReadonlyOptional(schema: T): TReadonly> { + return Readonly(Optional(schema)) as TReadonly> +} diff --git a/src/type/readonly/index.ts b/src/type/readonly/index.ts new file mode 100644 index 000000000..f39c08474 --- /dev/null +++ b/src/type/readonly/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './readonly-from-mapped-result' +export * from './readonly' diff --git a/src/type/readonly/readonly-from-mapped-result.ts b/src/type/readonly/readonly-from-mapped-result.ts new file mode 100644 index 000000000..9f303bb40 --- /dev/null +++ b/src/type/readonly/readonly-from-mapped-result.ts @@ -0,0 +1,88 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Readonly, type TReadonlyWithFlag } from './readonly' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties, + F extends boolean, +> = ( + { [K2 in keyof P]: TReadonlyWithFlag } +) +// prettier-ignore +function FromProperties< + P extends TProperties, + F extends boolean, +>(K: P, F: F): TFromProperties { + return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { + return {...Acc, [K2]: Readonly(K[K2], F) } + }, {}) as TFromProperties +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult, + F extends boolean, +> = ( + TFromProperties + ) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult, + F extends boolean, +>(R: R, F: F): TFromMappedResult { + return FromProperties(R.properties, F) as TFromMappedResult +} +// ------------------------------------------------------------------ +// ReadonlyFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TReadonlyFromMappedResult< + R extends TMappedResult, + F extends boolean, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function ReadonlyFromMappedResult< + R extends TMappedResult, + F extends boolean, + P extends TProperties = TFromMappedResult +>(R: R, F: F): TMappedResult

      { + const P = FromMappedResult(R, F) as unknown as P + return MappedResult(P) +} diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts new file mode 100644 index 000000000..c5bc936c9 --- /dev/null +++ b/src/type/readonly/readonly.ts @@ -0,0 +1,82 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Ensure } from '../helpers/index' +import { ReadonlyKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' +import type { TMappedResult } from '../mapped/index' + +import { ReadonlyFromMappedResult, type TReadonlyFromMappedResult } from './readonly-from-mapped-result' +import { IsMappedResult } from '../guard/type' +// ------------------------------------------------------------------ +// RemoveReadonly +// ------------------------------------------------------------------ +type TRemoveReadonly = T extends TReadonly ? S : T +function RemoveReadonly(schema: T) { + return Discard(CloneType(schema), [ReadonlyKind]) +} +// ------------------------------------------------------------------ +// AddReadonly +// ------------------------------------------------------------------ +type TAddReadonly = T extends TReadonly ? TReadonly : Ensure> +function AddReadonly(schema: T) { + return { ...CloneType(schema), [ReadonlyKind]: 'Readonly' } +} +// prettier-ignore +export type TReadonlyWithFlag = + F extends false + ? TRemoveReadonly + : TAddReadonly +// prettier-ignore +function ReadonlyWithFlag(schema: T, F: F) { + return ( + F === false + ? RemoveReadonly(schema) + : AddReadonly(schema) + ) +} +// ------------------------------------------------------------------ +// TReadonly +// ------------------------------------------------------------------ +export type TReadonly = T & { [ReadonlyKind]: 'Readonly' } + +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: T, enable: F): TReadonlyFromMappedResult +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: T, enable: F): TReadonlyWithFlag +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: T): TReadonlyFromMappedResult +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: T): TReadonlyWithFlag +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: TSchema, enable?: boolean): any { + const F = enable ?? true + return IsMappedResult(schema) ? ReadonlyFromMappedResult(schema, F) : ReadonlyWithFlag(schema, F) +} diff --git a/src/type/record/index.ts b/src/type/record/index.ts new file mode 100644 index 000000000..10eb1ceb9 --- /dev/null +++ b/src/type/record/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './record' diff --git a/src/type/record/record.ts b/src/type/record/record.ts new file mode 100644 index 000000000..ec724bd30 --- /dev/null +++ b/src/type/record/record.ts @@ -0,0 +1,224 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Static } from '../static/index' +import type { Evaluate, Ensure, Assert } from '../helpers/index' +import { Object, type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions } from '../object/index' +import { type TLiteral, type TLiteralValue } from '../literal/index' +import { Never, type TNever } from '../never/index' +import { Union, type TUnion } from '../union/index' +import { type TRegExp } from '../regexp/index' +import { type TString } from '../string/index' +import { type TInteger } from '../integer/index' +import { type TNumber } from '../number/index' +import { type TEnum } from '../enum/index' + +import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLiteral } from '../template-literal/index' +import { PatternStringExact, PatternNumberExact } from '../patterns/index' +import { IndexPropertyKeys } from '../indexed/index' +import { Kind, Hint } from '../symbols/index' +import { CloneType } from '../clone/type' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsUndefined } from '../guard/value' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsInteger, IsLiteral, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/type' +// ------------------------------------------------------------------ +// RecordCreateFromPattern +// ------------------------------------------------------------------ +// prettier-ignore +function RecordCreateFromPattern(pattern: string, T: TSchema, options: ObjectOptions): TRecord { + return { + ...options, + [Kind]: 'Record', + type: 'object', + patternProperties: { [pattern]: CloneType(T) } + } as unknown as TRecord +} +// ------------------------------------------------------------------ +// RecordCreateFromKeys +// ------------------------------------------------------------------ +// prettier-ignore +function RecordCreateFromKeys(K: string[], T: TSchema, options: ObjectOptions): TObject { + const P = K.reduce((Acc, K) => ({ ...Acc, [K]: CloneType(T) }), {} as TProperties) + return Object(P, { ...options, [Hint]: 'Record' }) +} +// ------------------------------------------------------------------ +// FromTemplateLiteralKey (Fast Inference) +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteralKeyInfinite = Ensure> +// prettier-ignore +type TFromTemplateLiteralKeyFinite> = ( + Ensure>> +) +// prettier-ignore +type TFromTemplateLiteralKey = TIsTemplateLiteralFinite extends false + ? TFromTemplateLiteralKeyInfinite + : TFromTemplateLiteralKeyFinite +// prettier-ignore +function FromTemplateLiteralKey(K: K, T: T, options: ObjectOptions): TFromTemplateLiteralKey { + return ( + IsTemplateLiteralFinite(K) + ? RecordCreateFromKeys(IndexPropertyKeys(K), T, options) + : RecordCreateFromPattern(K.pattern, T, options) + ) as TFromTemplateLiteralKey +} +// ------------------------------------------------------------------ +// FromEnumKey (Special Case) +// ------------------------------------------------------------------ +// prettier-ignore +type TFromEnumKey, T extends TSchema> = Ensure> +// ------------------------------------------------------------------ +// FromUnionKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnionKeyLiteralString, T extends TSchema> = { [_ in K['const']]: T } +// prettier-ignore +type TFromUnionKeyLiteralNumber, T extends TSchema> = { [_ in K['const']]: T } +// prettier-ignore +type TFromUnionKeyRest = + K extends [infer L extends TSchema, ...infer R extends TSchema[]] ? ( + L extends TUnion ? TFromUnionKeyRest & TFromUnionKeyRest : + L extends TLiteral ? TFromUnionKeyLiteralString & TFromUnionKeyRest : + L extends TLiteral ? TFromUnionKeyLiteralNumber & TFromUnionKeyRest : + {}) : {} +// prettier-ignore +type TFromUnionKey> = ( + Ensure>> +) +// prettier-ignore +function FromUnionKey(K: K, T: T, options: ObjectOptions): TFromUnionKey { + return RecordCreateFromKeys(IndexPropertyKeys(Union(K)), T, options) as TFromUnionKey +} +// ------------------------------------------------------------------ +// FromLiteralKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromLiteralKey = ( + Ensure]: T }>> +) +// prettier-ignore +function FromLiteralKey(K: K, T: T, options: ObjectOptions): TFromLiteralKey { + return RecordCreateFromKeys([(K as string).toString()], T, options) as TFromLiteralKey +} +// ------------------------------------------------------------------ +// TFromRegExpKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRegExpKey<_ extends TRegExp, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromRegExpKey(K: K, T: T, options: ObjectOptions): TFromRegExpKey { + return RecordCreateFromPattern(K.source, T, options) as TFromRegExpKey +} +// ------------------------------------------------------------------ +// FromStringKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromStringKey<_ extends TString, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromStringKey(K: K, T: T, options: ObjectOptions): TFromStringKey { + const pattern = IsUndefined(K.pattern) ? PatternStringExact : K.pattern + return RecordCreateFromPattern(pattern, T, options) as TFromStringKey +} +// ------------------------------------------------------------------ +// FromIntegerKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntegerKey<_ extends TSchema, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromIntegerKey(_: K, T: T, options: ObjectOptions): TFromIntegerKey { + return RecordCreateFromPattern(PatternNumberExact, T, options) as TFromIntegerKey +} +// ------------------------------------------------------------------ +// FromNumberKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromNumberKey<_ extends TSchema, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromNumberKey(_: K, T: T, options: ObjectOptions): TFromNumberKey { + return RecordCreateFromPattern(PatternNumberExact, T, options) as TFromNumberKey +} +// ------------------------------------------------------------------ +// TRecord +// ------------------------------------------------------------------ +// prettier-ignore +type RecordStatic = ( + Record, PropertyKey>, Static> +) +// prettier-ignore +export interface TRecord extends TSchema { + [Kind]: 'Record' + static: RecordStatic + type: 'object' + patternProperties: { [pattern: string]: T } + additionalProperties: TAdditionalProperties +} +// ------------------------------------------------------------------ +// TRecordOrObject +// ------------------------------------------------------------------ +// prettier-ignore +export type TRecordOrObject = + K extends TTemplateLiteral ? TFromTemplateLiteralKey : + K extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) + K extends TUnion ? TFromUnionKey : + K extends TLiteral ? TFromLiteralKey : + K extends TInteger ? TFromIntegerKey : + K extends TNumber ? TFromNumberKey : + K extends TRegExp ? TFromRegExpKey : + K extends TString ? TFromStringKey : + TNever +// ------------------------------------------------------------------ +// TRecordOrObject +// ------------------------------------------------------------------ +/** `[Json]` Creates a Record type */ +export function Record(K: K, T: T, options: ObjectOptions = {}): TRecordOrObject { + // prettier-ignore + return ( + IsUnion(K) ? FromUnionKey(K.anyOf, T, options) : + IsTemplateLiteral(K) ? FromTemplateLiteralKey(K, T, options) : + IsLiteral(K) ? FromLiteralKey(K.const, T, options) : + IsInteger(K) ? FromIntegerKey(K, T, options) : + IsNumber(K) ? FromNumberKey(K, T, options) : + IsRegExp(K) ? FromRegExpKey(K, T, options) : + IsString(K) ? FromStringKey(K, T, options) : + Never(options) + ) as TRecordOrObject +} diff --git a/src/type/recursive/index.ts b/src/type/recursive/index.ts new file mode 100644 index 000000000..9bb19d9f4 --- /dev/null +++ b/src/type/recursive/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './recursive' diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts new file mode 100644 index 000000000..cff19af46 --- /dev/null +++ b/src/type/recursive/recursive.ts @@ -0,0 +1,63 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { CloneType } from '../clone/type' +import { IsUndefined } from '../guard/value' +import { Kind, Hint } from '../symbols/index' +import { Static } from '../static/index' + +// ------------------------------------------------------------------ +// TThis +// ------------------------------------------------------------------ +export interface TThis extends TSchema { + [Kind]: 'This' + static: this['params'][0] + $ref: string +} +// ------------------------------------------------------------------ +// RecursiveStatic +// ------------------------------------------------------------------ +type RecursiveStatic = Static]> +// ------------------------------------------------------------------ +// TRecursive +// ------------------------------------------------------------------ +export interface TRecursive extends TSchema { + [Hint]: 'Recursive' + static: RecursiveStatic +} +// Auto Tracked For Recursive Types without ID's +let Ordinal = 0 +/** `[Json]` Creates a Recursive type */ +export function Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { + if (IsUndefined(options.$id)) (options as any).$id = `T${Ordinal++}` + const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) + thisType.$id = options.$id + // prettier-ignore + return CloneType({ ...options, [Hint]: 'Recursive', ...thisType }) as unknown as TRecursive +} diff --git a/src/type/ref/index.ts b/src/type/ref/index.ts new file mode 100644 index 000000000..1db9fa4e0 --- /dev/null +++ b/src/type/ref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './ref' diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts new file mode 100644 index 000000000..1df55fe2d --- /dev/null +++ b/src/type/ref/ref.ts @@ -0,0 +1,57 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsString, IsUndefined } from '../guard/value' +// ------------------------------------------------------------------ +// TRef +// ------------------------------------------------------------------ +export interface TRef extends TSchema { + [Kind]: 'Ref' + static: Static + $ref: string +} +/** `[Json]` Creates a Ref type. The referenced type must contain a $id */ +export function Ref(schema: T, options?: SchemaOptions): TRef +/** `[Json]` Creates a Ref type. */ +export function Ref($ref: string, options?: SchemaOptions): TRef +/** `[Json]` Creates a Ref type. */ +export function Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { + if (IsString(unresolved)) return { ...options, [Kind]: 'Ref', $ref: unresolved } + if (IsUndefined(unresolved.$id)) throw new Error('Reference target type must specify an $id') + return { + ...options, + [Kind]: 'Ref', + $ref: unresolved.$id!, + } +} diff --git a/src/type/regexp/index.ts b/src/type/regexp/index.ts new file mode 100644 index 000000000..00fab30b4 --- /dev/null +++ b/src/type/regexp/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './regexp' diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts new file mode 100644 index 000000000..3029d4240 --- /dev/null +++ b/src/type/regexp/regexp.ts @@ -0,0 +1,50 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TSchema } from '../schema/index' +import { IsString } from '../guard/value' +import { Kind } from '../symbols/index' + +export interface TRegExp extends TSchema { + [Kind]: 'RegExp' + static: `${string}` + type: 'RegExp' + source: string + flags: string +} + +/** `[JavaScript]` Creates a RegExp type */ +export function RegExp(pattern: string, options?: SchemaOptions): TRegExp +/** `[JavaScript]` Creates a RegExp type */ +export function RegExp(regex: RegExp, options?: SchemaOptions): TRegExp +/** `[JavaScript]` Creates a RegExp type */ +export function RegExp(unresolved: RegExp | string, options: SchemaOptions = {}) { + const expr = IsString(unresolved) ? new globalThis.RegExp(unresolved) : unresolved + return { ...options, [Kind]: 'RegExp', type: 'RegExp', source: expr.source, flags: expr.flags } as never +} diff --git a/src/type/registry/format.ts b/src/type/registry/format.ts new file mode 100644 index 000000000..ebcf3d74a --- /dev/null +++ b/src/type/registry/format.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type FormatRegistryValidationFunction = (value: string) => boolean +/** A registry for user defined string formats */ +const map = new Map() +/** Returns the entries in this registry */ +export function Entries() { + return new Map(map) +} +/** Clears all user defined string formats */ +export function Clear() { + return map.clear() +} +/** Deletes a registered format */ +export function Delete(format: string) { + return map.delete(format) +} +/** Returns true if the user defined string format exists */ +export function Has(format: string) { + return map.has(format) +} +/** Sets a validation function for a user defined string format */ +export function Set(format: string, func: FormatRegistryValidationFunction) { + map.set(format, func) +} +/** Gets a validation function for a user defined string format */ +export function Get(format: string) { + return map.get(format) +} diff --git a/src/type/registry/index.ts b/src/type/registry/index.ts new file mode 100644 index 000000000..a0fb0e043 --- /dev/null +++ b/src/type/registry/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as FormatRegistry from './format' +export * as TypeRegistry from './type' diff --git a/src/type/registry/type.ts b/src/type/registry/type.ts new file mode 100644 index 000000000..78e313b4a --- /dev/null +++ b/src/type/registry/type.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type TypeRegistryValidationFunction = (schema: TSchema, value: unknown) => boolean +/** A registry for user defined types */ + +const map = new Map>() +/** Returns the entries in this registry */ +export function Entries() { + return new Map(map) +} +/** Clears all user defined types */ +export function Clear() { + return map.clear() +} +/** Deletes a registered type */ +export function Delete(kind: string) { + return map.delete(kind) +} +/** Returns true if this registry contains this kind */ +export function Has(kind: string) { + return map.has(kind) +} +/** Sets a validation function for a user defined type */ +export function Set(kind: string, func: TypeRegistryValidationFunction) { + map.set(kind, func) +} +/** Gets a custom validation function for a user defined type */ +export function Get(kind: string) { + return map.get(kind) +} diff --git a/src/type/required/index.ts b/src/type/required/index.ts new file mode 100644 index 000000000..428fd821c --- /dev/null +++ b/src/type/required/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './required-from-mapped-result' +export * from './required' diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts new file mode 100644 index 000000000..2f6da2807 --- /dev/null +++ b/src/type/required/required-from-mapped-result.ts @@ -0,0 +1,83 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TProperties } from '../object/index' +import { MappedResult, type TMappedResult } from '../mapped/index' +import { Required, type TRequired } from './required' + +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties< + P extends TProperties +> = ( + { [K2 in keyof P]: TRequired } +) +// prettier-ignore +function FromProperties< + P extends TProperties +>(P: P, options: SchemaOptions): TFromProperties

      { + return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { + return {...Acc, [K2]: Required(P[K2], options) } + }, {}) as TFromProperties

      +} +// ------------------------------------------------------------------ +// FromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +type TFromMappedResult< + R extends TMappedResult +> = ( + TFromProperties +) +// prettier-ignore +function FromMappedResult< + R extends TMappedResult +>(R: R, options: SchemaOptions): TFromMappedResult { + return FromProperties(R.properties, options) as TFromMappedResult +} +// ------------------------------------------------------------------ +// TRequiredFromMappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type TRequiredFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +> = ( + TMappedResult

      +) +// prettier-ignore +export function RequiredFromMappedResult< + R extends TMappedResult, + P extends TProperties = TFromMappedResult +>(R: R, options: SchemaOptions): TMappedResult

      { + const P = FromMappedResult(R, options) as unknown as P + return MappedResult(P) +} diff --git a/src/type/required/required.ts b/src/type/required/required.ts new file mode 100644 index 000000000..e26e8f857 --- /dev/null +++ b/src/type/required/required.ts @@ -0,0 +1,118 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Evaluate } from '../helpers/index' +import type { TMappedResult } from '../mapped/index' +import { type TReadonlyOptional } from '../readonly-optional/index' +import { type TOptional } from '../optional/index' +import { type TReadonly } from '../readonly/index' +import { type TRecursive } from '../recursive/index' +import { type TIntersect, Intersect } from '../intersect/index' +import { type TUnion, Union } from '../union/index' +import { type TObject, type TProperties, Object } from '../object/index' + +import { OptionalKind, TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' + +import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Acc +) +// prettier-ignore +function FromRest(T: [...T]) : TFromRest { + return T.map(L => RequiredResolve(L)) as TFromRest +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties = Evaluate<{ + [K in keyof T]: + T[K] extends (TReadonlyOptional) ? TReadonly : + T[K] extends (TReadonly) ? TReadonly : + T[K] extends (TOptional) ? S : + T[K] +}> +// prettier-ignore +function FromProperties(T: T) { + return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K) => { + return { ...Acc, [K]: Discard(T[K], [OptionalKind]) as TSchema } + }, {} as TProperties) +} +// ------------------------------------------------------------------ +// RequiredResolve +// ------------------------------------------------------------------ +// prettier-ignore +type TRequiredResolve = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) +// prettier-ignore +function RequiredResolve(T: T): TRequiredResolve { + return ( + IsIntersect(T) ? Intersect(FromRest(T.allOf)) : + IsUnion(T) ? Union(FromRest(T.anyOf)) : + IsObject(T) ? Object(FromProperties(T.properties)) : + Object({}) + ) as TRequiredResolve +} +// ------------------------------------------------------------------ +// TRequired +// ------------------------------------------------------------------ +export type TRequired = TRequiredResolve + +/** `[Json]` Constructs a type where all properties are required */ +export function Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult +/** `[Json]` Constructs a type where all properties are required */ +export function Required(T: T, options?: SchemaOptions): TRequired +/** `[Json]` Constructs a type where all properties are required */ +export function Required(T: T, options: SchemaOptions = {}) { + if (IsMappedResult(T)) { + return RequiredFromMappedResult(T, options) + } else { + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(RequiredResolve(T) as any, options) + return { ...D, ...R } + } +} diff --git a/src/type/rest/index.ts b/src/type/rest/index.ts new file mode 100644 index 000000000..b1442f480 --- /dev/null +++ b/src/type/rest/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './rest' diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts new file mode 100644 index 000000000..8ca450780 --- /dev/null +++ b/src/type/rest/rest.ts @@ -0,0 +1,65 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TTuple } from '../tuple/index' +import { CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsIntersect, IsUnion, IsTuple } from '../guard/type' +// ------------------------------------------------------------------ +// RestResolve +// ------------------------------------------------------------------ +// prettier-ignore +type TRestResolve = + T extends TIntersect ? [...S] : + T extends TUnion ? [...S] : + T extends TTuple ? [...S] : + [] +// prettier-ignore +function RestResolve(T: T) { + return ( + IsIntersect(T) ? [...T.allOf] : + IsUnion(T) ? [...T.anyOf] : + IsTuple(T) ? [...(T.items ?? [])] : + [] + ) as TRestResolve +} +// ------------------------------------------------------------------ +// TRest +// ------------------------------------------------------------------ +export type TRest = TRestResolve + +/** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ +export function Rest(T: T): TRest { + return CloneRest(RestResolve(T)) +} diff --git a/src/type/return-type/index.ts b/src/type/return-type/index.ts new file mode 100644 index 000000000..610d3adc0 --- /dev/null +++ b/src/type/return-type/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './return-type' diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts new file mode 100644 index 000000000..fd31f8d59 --- /dev/null +++ b/src/type/return-type/return-type.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions } from '../schema/index' +import type { TFunction } from '../function/index' +import { CloneType } from '../clone/type' + +export type TReturnType = T['returns'] + +/** `[JavaScript]` Extracts the ReturnType from the given Function type */ +export function ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return CloneType(schema.returns, options) +} diff --git a/src/type/schema/anyschema.ts b/src/type/schema/anyschema.ts new file mode 100644 index 000000000..44fa18e5f --- /dev/null +++ b/src/type/schema/anyschema.ts @@ -0,0 +1,97 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Type: Module +// ------------------------------------------------------------------ +import type { TAny } from '../any/index' +import type { TArray } from '../array/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TBigInt } from '../bigint/index' +import type { TBoolean } from '../boolean/index' +import type { TConstructor } from '../constructor/index' +import type { TDate } from '../date/index' +import type { TEnum } from '../enum/index' +import type { TFunction } from '../function/index' +import type { TInteger } from '../integer/index' +import type { TIntersect } from '../intersect/index' +import type { TIterator } from '../iterator/index' +import type { TLiteral } from '../literal/index' +import type { TNot } from '../not/index' +import type { TNull } from '../null/index' +import type { TNumber } from '../number/index' +import type { TObject } from '../object/index' +import type { TPromise } from '../promise/index' +import type { TRecord } from '../record/index' +import type { TThis } from '../recursive/index' +import type { TRef } from '../ref/index' +import type { TRegExp } from '../regexp/index' +import type { TString } from '../string/index' +import type { TSymbol } from '../symbol/index' +import type { TTemplateLiteral } from '../template-literal/index' +import type { TTuple } from '../tuple/index' +import type { TUint8Array } from '../uint8array/index' +import type { TUndefined } from '../undefined/index' +import type { TUnion } from '../union/index' +import type { TUnknown } from '../unknown/index' +import type { TVoid } from '../void/index' +import type { TSchema } from './schema' + +export type TAnySchema = + | TSchema + | TAny + | TArray + | TAsyncIterator + | TBigInt + | TBoolean + | TConstructor + | TDate + | TEnum + | TFunction + | TInteger + | TIntersect + | TIterator + | TLiteral + | TNot + | TNull + | TNumber + | TObject + | TPromise + | TRecord + | TRef + | TRegExp + | TString + | TSymbol + | TTemplateLiteral + | TThis + | TTuple + | TUndefined + | TUnion + | TUint8Array + | TUnknown + | TVoid diff --git a/src/type/schema/index.ts b/src/type/schema/index.ts new file mode 100644 index 000000000..bfd714bb5 --- /dev/null +++ b/src/type/schema/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './anyschema' +export * from './schema' diff --git a/src/type/schema/schema.ts b/src/type/schema/schema.ts new file mode 100644 index 000000000..287088f98 --- /dev/null +++ b/src/type/schema/schema.ts @@ -0,0 +1,58 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, Hint, ReadonlyKind, OptionalKind } from '../symbols/index' + +export interface SchemaOptions { + $schema?: string + /** Id for this schema */ + $id?: string + /** Title of this schema */ + title?: string + /** Description of this schema */ + description?: string + /** Default value for this schema */ + default?: any + /** Example values matching this schema */ + examples?: any + /** Optional annotation for readOnly */ + readOnly?: boolean + /** Optional annotation for writeOnly */ + writeOnly?: boolean + [prop: string]: any +} +export interface TKind { + [Kind]: string +} +export interface TSchema extends TKind, SchemaOptions { + [ReadonlyKind]?: string + [OptionalKind]?: string + [Hint]?: string + params: unknown[] + static: unknown +} diff --git a/src/type/sets/index.ts b/src/type/sets/index.ts new file mode 100644 index 000000000..e672ba02e --- /dev/null +++ b/src/type/sets/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './set' diff --git a/src/type/sets/set.ts b/src/type/sets/set.ts new file mode 100644 index 000000000..c478204ff --- /dev/null +++ b/src/type/sets/set.ts @@ -0,0 +1,147 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// SetIncludes +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetIncludes = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? S extends L + ? true + : TSetIncludes + : false +) +/** Returns true if element S is in the set of T */ +// prettier-ignore +export function SetIncludes(T: [...T], S: S): TSetIncludes { + return T.includes(S) as TSetIncludes +} +// ------------------------------------------------------------------ +// SetIsSubset +// ------------------------------------------------------------------ +// prettier-ignore +export type SetIsSubset = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TSetIncludes extends true + ? SetIsSubset + : false + : true +) +/** Returns true if T is a subset of S */ +export function SetIsSubset(T: [...T], S: [...S]): SetIsSubset { + return T.every((L) => SetIncludes(S, L)) as SetIsSubset +} +// ------------------------------------------------------------------ +// SetDistinct +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetDistinct = + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TSetIncludes extends false + ? TSetDistinct + : TSetDistinct + : Acc +/** Returns a distinct set of elements */ +export function SetDistinct(T: [...T]): TSetDistinct { + return [...new Set(T)] as TSetDistinct +} +// ------------------------------------------------------------------ +// SetIntersect +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetIntersect = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TSetIncludes extends true + ? TSetIntersect + : TSetIntersect + : Acc +) +/** Returns the Intersect of the given sets */ +export function SetIntersect(T: [...T], S: [...S]): TSetIntersect { + return T.filter((L) => S.includes(L)) as TSetIntersect +} +// ------------------------------------------------------------------ +// SetUnion +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetUnion = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TSetUnion + : Acc +) +/** Returns the Union of the given sets */ +export function SetUnion(T: [...T], S: [...S]): TSetUnion { + return [...T, ...S] as never +} +// ------------------------------------------------------------------ +// SetComplement +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetComplement = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TSetIncludes extends true + ? TSetComplement + : TSetComplement + : Acc +) +/** Returns the Complement by omitting elements in T that are in S */ +// prettier-ignore +export function SetComplement(T: [...T], S: [...S]): TSetComplement { + return T.filter(L => !S.includes(L)) as TSetComplement +} +// ------------------------------------------------------------------ +// SetIntersectMany +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetIntersectMany = ( + T extends [infer L extends PropertyKey[]] ? L : + T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? TSetIntersectMany> + : Acc +) +/** Returns the Intersect of multiple sets */ +// prettier-ignore +export function SetIntersectMany(T: [...T]): TSetIntersectMany { + return ( + T.length === 1 ? T[0] : T.reduce((Acc, L) => [...SetIntersect(Acc, L)], []) + ) as TSetIntersectMany +} +// ------------------------------------------------------------------ +// SetUnionMany +// ------------------------------------------------------------------ +// prettier-ignore +export type TSetUnionMany = ( + T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? TSetUnionMany> + : Acc +) +/** Returns the Union of multiple sets */ +export function SetUnionMany(T: [...T]): TSetUnionMany { + return T.reduce((Acc, L) => [...Acc, ...L], [] as PropertyKey[]) as TSetUnionMany +} diff --git a/src/type/static/index.ts b/src/type/static/index.ts new file mode 100644 index 000000000..8f4f0c9dc --- /dev/null +++ b/src/type/static/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './static' diff --git a/src/type/static/static.ts b/src/type/static/static.ts new file mode 100644 index 000000000..e38b3765f --- /dev/null +++ b/src/type/static/static.ts @@ -0,0 +1,93 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { Evaluate } from '../helpers/index' +import type { TOptional } from '../optional/index' +import type { TReadonly } from '../readonly/index' +import type { TArray } from '../array/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TConstructor } from '../constructor/index' +import type { TEnum } from '../enum/index' +import type { TFunction } from '../function/index' +import type { TIntersect } from '../intersect/index' +import type { TIterator } from '../iterator/index' +import type { TNot } from '../not/index' +import type { TObject, TProperties } from '../object/index' +import type { TPromise } from '../promise/index' +import type { TRecursive } from '../recursive/index' +import type { TRecord } from '../record/index' +import type { TRef } from '../ref/index' +import type { TTuple } from '../tuple/index' +import type { TUnion } from '../union/index' +import type { TUnsafe } from '../unsafe/index' +import type { TSchema } from '../schema/index' +import type { TTransform } from '../transform/index' + +// ------------------------------------------------------------------ +// DecodeType +// ------------------------------------------------------------------ +// prettier-ignore +export type DecodeProperties = { + [K in keyof T]: DecodeType +} +// prettier-ignore +export type DecodeRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? DecodeRest]> + : Acc +// prettier-ignore +export type DecodeType = ( + T extends TOptional ? TOptional> : + T extends TReadonly ? TReadonly> : + T extends TTransform ? TUnsafe : + T extends TArray ? TArray> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TConstructor ? TConstructor, DecodeType> : + T extends TEnum ? TEnum : // intercept for union. interior non decodable + T extends TFunction ? TFunction, DecodeType> : + T extends TIntersect ? TIntersect> : + T extends TIterator ? TIterator> : + T extends TNot ? TNot> : + T extends TObject ? TObject>> : + T extends TPromise ? TPromise> : + T extends TRecord ? TRecord> : + T extends TRecursive ? TRecursive> : + T extends TRef ? TRef> : + T extends TTuple ? TTuple> : + T extends TUnion ? TUnion> : + T +) +// ------------------------------------------------------------------ +// Static +// ------------------------------------------------------------------ +/** Creates an decoded static type from a TypeBox type */ +export type StaticDecode = Static, P> +/** Creates an encoded static type from a TypeBox type */ +export type StaticEncode = Static +/** Creates a static type from a TypeBox type */ +export type Static = (T & { params: P })['static'] diff --git a/src/type/strict/index.ts b/src/type/strict/index.ts new file mode 100644 index 000000000..caf80eab1 --- /dev/null +++ b/src/type/strict/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './strict' diff --git a/src/type/strict/strict.ts b/src/type/strict/strict.ts new file mode 100644 index 000000000..de47e425b --- /dev/null +++ b/src/type/strict/strict.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' + +/** `[Json]` Omits compositing symbols from this schema. */ +export function Strict(schema: T): T { + return JSON.parse(JSON.stringify(schema)) +} diff --git a/src/type/string/index.ts b/src/type/string/index.ts new file mode 100644 index 000000000..1c188b4fd --- /dev/null +++ b/src/type/string/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './string' diff --git a/src/type/string/string.ts b/src/type/string/string.ts new file mode 100644 index 000000000..5bcda7176 --- /dev/null +++ b/src/type/string/string.ts @@ -0,0 +1,86 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TString +// ------------------------------------------------------------------ +export type StringFormatOption = + | 'date-time' + | 'time' + | 'date' + | 'email' + | 'idn-email' + | 'hostname' + | 'idn-hostname' + | 'ipv4' + | 'ipv6' + | 'uri' + | 'uri-reference' + | 'iri' + | 'uuid' + | 'iri-reference' + | 'uri-template' + | 'json-pointer' + | 'relative-json-pointer' + | 'regex' + | ({} & string) +// prettier-ignore +export type StringContentEncodingOption = + | '7bit' + | '8bit' + | 'binary' + | 'quoted-printable' + | 'base64' + | ({} & string) +export interface StringOptions extends SchemaOptions { + /** The maximum string length */ + maxLength?: number + /** The minimum string length */ + minLength?: number + /** A regular expression pattern this string should match */ + pattern?: string + /** A format this string should match */ + format?: StringFormatOption + /** The content encoding for this string */ + contentEncoding?: StringContentEncodingOption + /** The content media type for this string */ + contentMediaType?: string +} +export interface TString extends TSchema, StringOptions { + [Kind]: 'String' + static: string + type: 'string' +} + +/** `[Json]` Creates a String type */ +export function String(options: StringOptions = {}): TString { + return { ...options, [Kind]: 'String', type: 'string' } as unknown as TString +} diff --git a/src/type/symbol/index.ts b/src/type/symbol/index.ts new file mode 100644 index 000000000..abd306265 --- /dev/null +++ b/src/type/symbol/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './symbol' diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts new file mode 100644 index 000000000..4c5adc9ab --- /dev/null +++ b/src/type/symbol/symbol.ts @@ -0,0 +1,41 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export type TSymbolValue = string | number | undefined +export interface TSymbol extends TSchema, SchemaOptions { + [Kind]: 'Symbol' + static: symbol + type: 'symbol' +} +/** `[JavaScript]` Creates a Symbol type */ +export function Symbol(options?: SchemaOptions): TSymbol { + return { ...options, [Kind]: 'Symbol', type: 'symbol' } as unknown as TSymbol +} diff --git a/src/type/symbols/index.ts b/src/type/symbols/index.ts new file mode 100644 index 000000000..bac16a322 --- /dev/null +++ b/src/type/symbols/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './symbols' diff --git a/src/type/symbols/symbols.ts b/src/type/symbols/symbols.ts new file mode 100644 index 000000000..dbb90a99a --- /dev/null +++ b/src/type/symbols/symbols.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +/** Symbol key applied to transform types */ +export const TransformKind = Symbol.for('TypeBox.Transform') +/** Symbol key applied to readonly types */ +export const ReadonlyKind = Symbol.for('TypeBox.Readonly') +/** Symbol key applied to optional types */ +export const OptionalKind = Symbol.for('TypeBox.Optional') +/** Symbol key applied to types */ +export const Hint = Symbol.for('TypeBox.Hint') +/** Symbol key applied to types */ +export const Kind = Symbol.for('TypeBox.Kind') diff --git a/src/type/template-literal/finite.ts b/src/type/template-literal/finite.ts new file mode 100644 index 000000000..43a03ac33 --- /dev/null +++ b/src/type/template-literal/finite.ts @@ -0,0 +1,119 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TemplateLiteralParseExact } from './parse' +import { TypeBoxError } from '../error/index' +import type { TTemplateLiteral, TTemplateLiteralKind } from './index' +import type { TUnion } from '../union/index' +import type { TString } from '../string/index' +import type { TBoolean } from '../boolean/index' +import type { TNumber } from '../number/index' +import type { TInteger } from '../integer/index' +import type { TBigInt } from '../bigint/index' +import type { TLiteral } from '../literal/index' +import type { Expression } from './parse' + +// ------------------------------------------------------------------ +// TemplateLiteralFiniteError +// ------------------------------------------------------------------ +export class TemplateLiteralFiniteError extends TypeBoxError {} + +// ------------------------------------------------------------------ +// IsTemplateLiteralFiniteCheck +// ------------------------------------------------------------------ +// prettier-ignore +function IsNumberExpression(expression: Expression): boolean { + return ( + expression.type === 'or' && + expression.expr.length === 2 && + expression.expr[0].type === 'const' && + expression.expr[0].const === '0' && + expression.expr[1].type === 'const' && + expression.expr[1].const === '[1-9][0-9]*' + ) +} +// prettier-ignore +function IsBooleanExpression(expression: Expression): boolean { + return ( + expression.type === 'or' && + expression.expr.length === 2 && + expression.expr[0].type === 'const' && + expression.expr[0].const === 'true' && + expression.expr[1].type === 'const' && + expression.expr[1].const === 'false' + ) +} +// prettier-ignore +function IsStringExpression(expression: Expression) { + return expression.type === 'const' && expression.const === '.*' +} +// prettier-ignore +type TFromTemplateLiteralKind = + T extends TTemplateLiteral ? TFromTemplateLiteralKinds : + T extends TUnion ? TFromTemplateLiteralKinds : + T extends TString ? false : + T extends TNumber ? false : + T extends TInteger ? false : + T extends TBigInt ? false : + T extends TBoolean ? true : + T extends TLiteral ? true : + false +// prettier-ignore +type TFromTemplateLiteralKinds = + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? TFromTemplateLiteralKind extends false + ? false + : TFromTemplateLiteralKinds : + true +// ------------------------------------------------------------------ +// IsTemplateLiteralExpressionFinite +// ------------------------------------------------------------------ +// prettier-ignore +export function IsTemplateLiteralExpressionFinite(expression: Expression): boolean { + return ( + IsNumberExpression(expression) || IsStringExpression(expression) ? false : + IsBooleanExpression(expression) ? true : + (expression.type === 'and') ? expression.expr.every((expr) => IsTemplateLiteralExpressionFinite(expr)) : + (expression.type === 'or') ? expression.expr.every((expr) => IsTemplateLiteralExpressionFinite(expr)) : + (expression.type === 'const') ? true : + (() => { throw new TemplateLiteralFiniteError(`Unknown expression type`) })() + ) +} +// ------------------------------------------------------------------ +// TIsTemplateLiteralFinite +// ------------------------------------------------------------------ +// prettier-ignore +export type TIsTemplateLiteralFinite = + T extends TTemplateLiteral + ? TFromTemplateLiteralKinds + : false +/** Returns true if this TemplateLiteral resolves to a finite set of values */ +export function IsTemplateLiteralFinite(schema: T) { + const expression = TemplateLiteralParseExact(schema.pattern) + return IsTemplateLiteralExpressionFinite(expression) +} diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts new file mode 100644 index 000000000..671dd40e2 --- /dev/null +++ b/src/type/template-literal/generate.ts @@ -0,0 +1,148 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IsTemplateLiteralExpressionFinite, TIsTemplateLiteralFinite } from './finite' +import { TemplateLiteralParseExact } from './parse' +import { TypeBoxError } from '../error/index' + +import type { Assert } from '../helpers/index' +import type { TBoolean } from '../boolean/index' +import type { TTemplateLiteral, TTemplateLiteralKind } from './index' +import type { TLiteral, TLiteralValue } from '../literal/index' +import type { Expression, ExpressionAnd, ExpressionOr, ExpressionConst } from './parse' +import type { TUnion } from '../union/index' + +// ------------------------------------------------------------------ +// TemplateLiteralGenerateError +// ------------------------------------------------------------------ +export class TemplateLiteralGenerateError extends TypeBoxError {} +// ------------------------------------------------------------------ +// StringReducers +// ------------------------------------------------------------------ +// StringReduceUnary<"A", ["B", "C"]> -> ["AB", "AC"] +// prettier-ignore +type TStringReduceUnary = + R extends [infer A extends string, ...infer B extends string[]] + ? TStringReduceUnary + : Acc +// StringReduceBinary<['A', 'B'], ['C', 'D']> -> ["AC", "AD", "BC", "BD"] +// prettier-ignore +type TStringReduceBinary = + L extends [infer A extends string, ...infer B extends string[]] + ? TStringReduceBinary]> + : Acc +// StringReduceMany<[['A', 'B'], ['C', 'D'], ['E']]> -> [["ACE", "ADE", "BCE", "BDE"]] +// prettier-ignore +type TStringReduceMany = // consider optimizing + T extends [infer L extends string[], infer R extends string[], ...infer Rest extends string[][]] + ? TStringReduceMany<[TStringReduceBinary, ...Rest]> + : T +// Reduce<[['A', 'B'], ['C', 'D'], ['E']]> -> ["ACE", "ADE", "BCE", "BDE"] +// prettier-ignore +type TStringReduce> = + 0 extends keyof O + ? Assert + : [] +// ------------------------------------------------------------------ +// FromTemplateLiteralUnionKinds +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteralUnionKinds = + T extends [infer L extends TLiteral, ...infer R extends TLiteral[]] + ? [`${L['const']}`, ...TFromTemplateLiteralUnionKinds] + : [] +// ------------------------------------------------------------------ +// FromTemplateLiteralKinds +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTemplateLiteralKinds = + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? ( + L extends TLiteral ? TFromTemplateLiteralKinds : + L extends TUnion ? TFromTemplateLiteralKinds]> : + L extends TBoolean ? TFromTemplateLiteralKinds : + Acc + ) : Acc +// ------------------------------------------------------------------ +// TemplateLiteralExpressionGenerate +// ------------------------------------------------------------------ +// prettier-ignore +function* GenerateReduce(buffer: string[][]): IterableIterator { + if (buffer.length === 1) return yield* buffer[0] + for (const left of buffer[0]) { + for (const right of GenerateReduce(buffer.slice(1))) { + yield `${left}${right}` + } + } +} +// prettier-ignore +function* GenerateAnd(expression: ExpressionAnd): IterableIterator { + return yield* GenerateReduce(expression.expr.map((expr) => [...TemplateLiteralExpressionGenerate(expr)])) +} +// prettier-ignore +function* GenerateOr(expression: ExpressionOr): IterableIterator { + for (const expr of expression.expr) yield* TemplateLiteralExpressionGenerate(expr) +} +// prettier-ignore +function* GenerateConst(expression: ExpressionConst): IterableIterator { + return yield expression.const +} +export function* TemplateLiteralExpressionGenerate(expression: Expression): IterableIterator { + return expression.type === 'and' + ? yield* GenerateAnd(expression) + : expression.type === 'or' + ? yield* GenerateOr(expression) + : expression.type === 'const' + ? yield* GenerateConst(expression) + : (() => { + throw new TemplateLiteralGenerateError('Unknown expression') + })() +} +// ------------------------------------------------------------------ +// TTemplateLiteralGenerate +// ------------------------------------------------------------------ +// prettier-ignore +export type TTemplateLiteralGenerate> = + F extends true + ? ( + T extends TTemplateLiteral + ? TFromTemplateLiteralKinds extends infer R extends string[][] + ? TStringReduce + : [] + : [] + ) : [] +/** Generates a tuple of strings from the given TemplateLiteral. Returns an empty tuple if infinite. */ +export function TemplateLiteralGenerate(schema: T): TTemplateLiteralGenerate { + const expression = TemplateLiteralParseExact(schema.pattern) + // prettier-ignore + return ( + IsTemplateLiteralExpressionFinite(expression) + ? [...TemplateLiteralExpressionGenerate(expression)] + : [] + ) as TTemplateLiteralGenerate +} diff --git a/src/type/template-literal/index.ts b/src/type/template-literal/index.ts new file mode 100644 index 000000000..17ae27505 --- /dev/null +++ b/src/type/template-literal/index.ts @@ -0,0 +1,35 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './finite' +export * from './generate' +export * from './syntax' +export * from './parse' +export * from './pattern' +export * from './union' +export * from './template-literal' diff --git a/src/type/template-literal/parse.ts b/src/type/template-literal/parse.ts new file mode 100644 index 000000000..f856a1eac --- /dev/null +++ b/src/type/template-literal/parse.ts @@ -0,0 +1,167 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeBoxError } from '../error/index' + +// ------------------------------------------------------------------ +// TemplateLiteralParserError +// ------------------------------------------------------------------ +export class TemplateLiteralParserError extends TypeBoxError {} +// ------------------------------------------------------------------ +// TemplateLiteralParse +// ------------------------------------------------------------------ +// prettier-ignore +export type Expression = ExpressionAnd | ExpressionOr | ExpressionConst +export type ExpressionConst = { type: 'const'; const: string } +export type ExpressionAnd = { type: 'and'; expr: Expression[] } +export type ExpressionOr = { type: 'or'; expr: Expression[] } +// prettier-ignore +function IsNonEscaped(pattern: string, index: number, char: string) { + return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 +} +// prettier-ignore +function IsOpenParen(pattern: string, index: number) { + return IsNonEscaped(pattern, index, '(') +} +// prettier-ignore +function IsCloseParen(pattern: string, index: number) { + return IsNonEscaped(pattern, index, ')') +} +// prettier-ignore +function IsSeparator(pattern: string, index: number) { + return IsNonEscaped(pattern, index, '|') +} +// prettier-ignore +function IsGroup(pattern: string) { + if (!(IsOpenParen(pattern, 0) && IsCloseParen(pattern, pattern.length - 1))) return false + let count = 0 + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (count === 0 && index !== pattern.length - 1) return false + } + return true +} +// prettier-ignore +function InGroup(pattern: string) { + return pattern.slice(1, pattern.length - 1) +} +// prettier-ignore +function IsPrecedenceOr(pattern: string) { + let count = 0 + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (IsSeparator(pattern, index) && count === 0) return true + } + return false +} +// prettier-ignore +function IsPrecedenceAnd(pattern: string) { + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) return true + } + return false +} +// prettier-ignore +function Or(pattern: string): Expression { + let [count, start] = [0, 0] + const expressions: Expression[] = [] + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) count += 1 + if (IsCloseParen(pattern, index)) count -= 1 + if (IsSeparator(pattern, index) && count === 0) { + const range = pattern.slice(start, index) + if (range.length > 0) expressions.push(TemplateLiteralParse(range)) + start = index + 1 + } + } + const range = pattern.slice(start) + if (range.length > 0) expressions.push(TemplateLiteralParse(range)) + if (expressions.length === 0) return { type: 'const', const: '' } + if (expressions.length === 1) return expressions[0] + return { type: 'or', expr: expressions } +} +// prettier-ignore +function And(pattern: string): Expression { + function Group(value: string, index: number): [number, number] { + if (!IsOpenParen(value, index)) throw new TemplateLiteralParserError(`TemplateLiteralParser: Index must point to open parens`) + let count = 0 + for (let scan = index; scan < value.length; scan++) { + if (IsOpenParen(value, scan)) count += 1 + if (IsCloseParen(value, scan)) count -= 1 + if (count === 0) return [index, scan] + } + throw new TemplateLiteralParserError(`TemplateLiteralParser: Unclosed group parens in expression`) + } + function Range(pattern: string, index: number): [number, number] { + for (let scan = index; scan < pattern.length; scan++) { + if (IsOpenParen(pattern, scan)) return [index, scan] + } + return [index, pattern.length] + } + const expressions: Expression[] = [] + for (let index = 0; index < pattern.length; index++) { + if (IsOpenParen(pattern, index)) { + const [start, end] = Group(pattern, index) + const range = pattern.slice(start, end + 1) + expressions.push(TemplateLiteralParse(range)) + index = end + } else { + const [start, end] = Range(pattern, index) + const range = pattern.slice(start, end) + if (range.length > 0) expressions.push(TemplateLiteralParse(range)) + index = end - 1 + } + } + return ( + (expressions.length === 0) ? { type: 'const', const: '' } : + (expressions.length === 1) ? expressions[0] : + { type: 'and', expr: expressions } + ) +} +// ------------------------------------------------------------------ +// TemplateLiteralParse +// ------------------------------------------------------------------ +/** Parses a pattern and returns an expression tree */ +export function TemplateLiteralParse(pattern: string): Expression { + // prettier-ignore + return ( + IsGroup(pattern) ? TemplateLiteralParse(InGroup(pattern)) : + IsPrecedenceOr(pattern) ? Or(pattern) : + IsPrecedenceAnd(pattern) ? And(pattern) : + { type: 'const', const: pattern } + ) +} +// ------------------------------------------------------------------ +// TemplateLiteralParseExact +// ------------------------------------------------------------------ +/** Parses a pattern and strips forward and trailing ^ and $ */ +export function TemplateLiteralParseExact(pattern: string): Expression { + return TemplateLiteralParse(pattern.slice(1, pattern.length - 1)) +} diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts new file mode 100644 index 000000000..6af7e3f9d --- /dev/null +++ b/src/type/template-literal/pattern.ts @@ -0,0 +1,77 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TTemplateLiteralKind } from './index' +import { PatternNumber, PatternString, PatternBoolean } from '../patterns/index' +import { Kind } from '../symbols/index' +import { TypeBoxError } from '../error/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsTemplateLiteral, + IsUnion, + IsNumber, + IsInteger, + IsBigInt, + IsString, + IsLiteral, + IsBoolean +} from '../guard/type' + +// ------------------------------------------------------------------ +// TemplateLiteralPatternError +// ------------------------------------------------------------------ +export class TemplateLiteralPatternError extends TypeBoxError {} + +// ------------------------------------------------------------------ +// TemplateLiteralPattern +// ------------------------------------------------------------------ +function Escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} +// prettier-ignore +function Visit(schema: TSchema, acc: string): string { + return ( + IsTemplateLiteral(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : + IsUnion(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : + IsNumber(schema) ? `${acc}${PatternNumber}` : + IsInteger(schema) ? `${acc}${PatternNumber}` : + IsBigInt(schema) ? `${acc}${PatternNumber}` : + IsString(schema) ? `${acc}${PatternString}` : + IsLiteral(schema) ? `${acc}${Escape(schema.const.toString())}` : + IsBoolean(schema) ? `${acc}${PatternBoolean}` : + (() => { throw new TemplateLiteralPatternError(`Unexpected Kind '${schema[Kind]}'`) })() + ) +} +export function TemplateLiteralPattern(kinds: TTemplateLiteralKind[]): string { + return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` +} diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts new file mode 100644 index 000000000..f70df69f8 --- /dev/null +++ b/src/type/template-literal/syntax.ts @@ -0,0 +1,117 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { Assert, Trim } from '../helpers/index' +import type { TTemplateLiteral, TTemplateLiteralKind } from './index' +import { Literal, type TLiteral } from '../literal/index' +import { Boolean, type TBoolean } from '../boolean/index' +import { BigInt, type TBigInt } from '../bigint/index' +import { Number, type TNumber } from '../number/index' +import { String, type TString } from '../string/index' +import { UnionEvaluated, type TUnionEvaluated } from '../union/index' +import { Never } from '../never/index' + +// ------------------------------------------------------------------ +// SyntaxParsers +// ------------------------------------------------------------------ +// prettier-ignore +function* FromUnion(syntax: string): IterableIterator { + const trim = syntax.trim().replace(/"|'/g, '') + return ( + trim === 'boolean' ? yield Boolean() : + trim === 'number' ? yield Number() : + trim === 'bigint' ? yield BigInt() : + trim === 'string' ? yield String() : + yield (() => { + const literals = trim.split('|').map((literal) => Literal(literal.trim())) + return ( + literals.length === 0 ? Never() : + literals.length === 1 ? literals[0] : + UnionEvaluated(literals) + ) + })() + ) +} +// prettier-ignore +function* FromTerminal(syntax: string): IterableIterator { + if (syntax[1] !== '{') { + const L = Literal('$') + const R = FromSyntax(syntax.slice(1)) + return yield* [L, ...R] + } + for (let i = 2; i < syntax.length; i++) { + if (syntax[i] === '}') { + const L = FromUnion(syntax.slice(2, i)) + const R = FromSyntax(syntax.slice(i + 1)) + return yield* [...L, ...R] + } + } + yield Literal(syntax) +} +// prettier-ignore +function* FromSyntax(syntax: string): IterableIterator { + for (let i = 0; i < syntax.length; i++) { + if (syntax[i] === '$') { + const L = Literal(syntax.slice(0, i)) + const R = FromTerminal(syntax.slice(i)) + return yield* [L, ...R] + } + } + yield Literal(syntax) +} +// prettier-ignore +type FromUnionLiteral = + T extends `${infer L}|${infer R}` ? [TLiteral>, ...FromUnionLiteral] : + T extends `${infer L}` ? [TLiteral>] : + [] +type FromUnion = TUnionEvaluated> +// prettier-ignore +type FromTerminal = + T extends 'boolean' ? TBoolean : + T extends 'bigint' ? TBigInt : + T extends 'number' ? TNumber : + T extends 'string' ? TString : + FromUnion +// prettier-ignore +type FromString = + T extends `{${infer L}}${infer R}` ? [FromTerminal, ...FromString] : + T extends `${infer L}$${infer R}` ? [TLiteral, ...FromString] : + T extends `${infer L}` ? [TLiteral] : + [] + +// ------------------------------------------------------------------ +// TTemplateLiteralSyntax +// ------------------------------------------------------------------ +// prettier-ignore +export type TTemplateLiteralSyntax = ( + TTemplateLiteral, TTemplateLiteralKind[]>> +) +/** Parses TemplateLiteralSyntax and returns a tuple of TemplateLiteralKinds */ +export function TemplateLiteralSyntax(syntax: string): TTemplateLiteralKind[] { + return [...FromSyntax(syntax)] +} diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts new file mode 100644 index 000000000..c3436b8f2 --- /dev/null +++ b/src/type/template-literal/template-literal.ts @@ -0,0 +1,102 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Assert } from '../helpers/index' +import type { TUnion } from '../union/index' +import type { TLiteral } from '../literal/index' +import type { TInteger } from '../integer/index' +import type { TNumber } from '../number/index' +import type { TBigInt } from '../bigint/index' +import type { TString } from '../string/index' +import type { TBoolean } from '../boolean/index' +import type { TNever } from '../never/index' +import type { Static } from '../static/index' + +import { TemplateLiteralSyntax, type TTemplateLiteralSyntax } from './syntax' +import { TemplateLiteralPattern } from './pattern' +import { EmptyString } from '../helpers/index' +import { IsString } from '../guard/value' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TemplateLiteralStaticKind +// ------------------------------------------------------------------ +// prettier-ignore +type TemplateLiteralStaticKind = + T extends TUnion ? { [K in keyof U]: TemplateLiteralStatic, Acc> }[number] : + T extends TTemplateLiteral ? `${Static}` : + T extends TLiteral ? `${U}` : + T extends TString ? `${string}` : + T extends TNumber ? `${number}` : + T extends TBigInt ? `${bigint}` : + T extends TBoolean ? `${boolean}` : + never +// ------------------------------------------------------------------ +// TemplateLiteralStatic +// ------------------------------------------------------------------ +// prettier-ignore +type TemplateLiteralStatic = + T extends [infer L, ...infer R] ? `${TemplateLiteralStaticKind}${TemplateLiteralStatic, Acc>}` : + Acc +// ------------------------------------------------------------------ +// TTemplateLiteralKind +// ------------------------------------------------------------------ +// prettier-ignore +export type TTemplateLiteralKind = + | TTemplateLiteral + | TUnion + | TLiteral + | TInteger + | TNumber + | TBigInt + | TString + | TBoolean + | TNever +// ------------------------------------------------------------------ +// TTemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +export interface TTemplateLiteral extends TSchema { + [Kind]: 'TemplateLiteral' + static: TemplateLiteralStatic + type: 'string' + pattern: string // todo: it may be possible to infer this pattern +} +/** `[Json]` Creates a TemplateLiteral type from template dsl string */ +export function TemplateLiteral(syntax: T, options?: SchemaOptions): TTemplateLiteralSyntax +/** `[Json]` Creates a TemplateLiteral type */ +export function TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral +/** `[Json]` Creates a TemplateLiteral type */ +// prettier-ignore +export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { + const pattern = IsString(unresolved) + ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) + : TemplateLiteralPattern(unresolved as TTemplateLiteralKind[]) + return { ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern } +} diff --git a/src/type/template-literal/union.ts b/src/type/template-literal/union.ts new file mode 100644 index 000000000..9de4dbc83 --- /dev/null +++ b/src/type/template-literal/union.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TTemplateLiteral } from './template-literal' +import { Union, type TUnion } from '../union/index' +import { Literal, type TLiteral } from '../literal/index' +import { type TString } from '../string/index' +import { type TNever } from '../never/index' +import { TemplateLiteralGenerate } from './generate' + +// ------------------------------------------------------------------ +// TemplateLiteralToUnion +// ------------------------------------------------------------------ +/** Returns a Union from the given TemplateLiteral */ +export function TemplateLiteralToUnion(schema: TTemplateLiteral): TNever | TString | TUnion { + const R = TemplateLiteralGenerate(schema) as string[] + const L = R.map((S) => Literal(S)) + return Union(L) +} diff --git a/src/type/transform/index.ts b/src/type/transform/index.ts new file mode 100644 index 000000000..4e6ab0218 --- /dev/null +++ b/src/type/transform/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './transform' diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts new file mode 100644 index 000000000..cb7099544 --- /dev/null +++ b/src/type/transform/transform.ts @@ -0,0 +1,88 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Static, StaticDecode } from '../static/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTransform } from '../guard/type' + +// ------------------------------------------------------------------ +// TransformBuilders +// ------------------------------------------------------------------ +export class TransformDecodeBuilder { + constructor(private readonly schema: T) {} + public Decode, U>>(decode: D): TransformEncodeBuilder { + return new TransformEncodeBuilder(this.schema, decode) + } +} +// prettier-ignore +export class TransformEncodeBuilder { + constructor(private readonly schema: T, private readonly decode: D) { } + private EncodeTransform, StaticDecode>>(encode: E, schema: TTransform) { + const Encode = (value: unknown) => schema[TransformKind as any].Encode(encode(value as any)) + const Decode = (value: unknown) => this.decode(schema[TransformKind as any].Decode(value)) + const Codec = { Encode: Encode, Decode: Decode } + return { ...schema, [TransformKind]: Codec } + } + private EncodeSchema, StaticDecode>>(encode: E, schema: TSchema) { + const Codec = { Decode: this.decode, Encode: encode } + return { ...schema as TSchema, [TransformKind]: Codec } + } + public Encode, StaticDecode>>(encode: E): TTransform> { + const schema = CloneType(this.schema) + return ( + IsTransform(schema) ? this.EncodeTransform(encode, schema): this.EncodeSchema(encode, schema) + ) as unknown as TTransform> + } +} +// ------------------------------------------------------------------ +// TransformStatic +// ------------------------------------------------------------------ +type TransformStatic = T extends TTransform ? S : Static +// ------------------------------------------------------------------ +// TTransform +// ------------------------------------------------------------------ +export type TransformFunction = (value: T) => U +export interface TransformOptions { + Decode: TransformFunction, O> + Encode: TransformFunction> +} +export interface TTransform extends TSchema { + static: TransformStatic + [TransformKind]: TransformOptions + [key: string]: any +} +/** `[Json]` Creates a Transform type */ +export function Transform(schema: I): TransformDecodeBuilder { + return new TransformDecodeBuilder(schema) +} diff --git a/src/type/tuple/index.ts b/src/type/tuple/index.ts new file mode 100644 index 000000000..c1eb788d2 --- /dev/null +++ b/src/type/tuple/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './tuple' diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts new file mode 100644 index 000000000..32119f78d --- /dev/null +++ b/src/type/tuple/tuple.ts @@ -0,0 +1,64 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { CloneRest } from '../clone/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TupleStatic +// ------------------------------------------------------------------ +// prettier-ignore +type TupleStatic = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TupleStatic]> + : Acc +// ------------------------------------------------------------------ +// TTuple +// ------------------------------------------------------------------ +export interface TTuple extends TSchema { + [Kind]: 'Tuple' + static: TupleStatic + type: 'array' + items?: T + additionalItems?: false + minItems: number + maxItems: number +} +/** `[Json]` Creates a Tuple type */ +export function Tuple(items: [...T], options: SchemaOptions = {}): TTuple { + // return TupleResolver.Resolve(T) + const [additionalItems, minItems, maxItems] = [false, items.length, items.length] + // prettier-ignore + return ( + items.length > 0 ? + { ...options, [Kind]: 'Tuple', type: 'array', items: CloneRest(items), additionalItems, minItems, maxItems } : + { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems } + ) as unknown as TTuple +} diff --git a/src/type/type/index.ts b/src/type/type/index.ts new file mode 100644 index 000000000..081b459d1 --- /dev/null +++ b/src/type/type/index.ts @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// JsonTypeBuilder +// ------------------------------------------------------------------ +export { JsonTypeBuilder } from './json' + +// ------------------------------------------------------------------ +// JavaScriptTypeBuilder +// ------------------------------------------------------------------ +import * as TypeBuilder from './type' +import { JavaScriptTypeBuilder } from './javascript' + +/** JavaScript Type Builder with Static Resolution for TypeScript */ +const Type = TypeBuilder as InstanceType +export { JavaScriptTypeBuilder } +export { Type } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts new file mode 100644 index 000000000..7975887fd --- /dev/null +++ b/src/type/type/javascript.ts @@ -0,0 +1,123 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { JsonTypeBuilder } from './json' +import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' +import { Awaited, type TAwaited } from '../awaited/index' +import { BigInt, type TBigInt, type BigIntOptions } from '../bigint/index' +import { Constructor, type TConstructor } from '../constructor/index' +import { ConstructorParameters, type TConstructorParameters } from '../constructor-parameters/index' +import { Date, type TDate, type DateOptions } from '../date/index' +import { Function as FunctionType, type TFunction } from '../function/index' +import { InstanceType, type TInstanceType } from '../instance-type/index' +import { Iterator, type TIterator } from '../iterator/index' +import { Parameters, type TParameters } from '../parameters/index' +import { Promise, type TPromise } from '../promise/index' +import { RegExp, type TRegExp } from '../regexp/index' +import { ReturnType, type TReturnType } from '../return-type/index' +import { type TSchema, type SchemaOptions } from '../schema/index' +import { Symbol, type TSymbol } from '../symbol/index' +import { Uint8Array, type TUint8Array, type Uint8ArrayOptions } from '../uint8array/index' +import { Undefined, type TUndefined } from '../undefined/index' +import { Void, type TVoid } from '../void/index' + +/** JavaScript Type Builder with Static Resolution for TypeScript */ +export class JavaScriptTypeBuilder extends JsonTypeBuilder { + /** `[JavaScript]` Creates a AsyncIterator type */ + public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { + return AsyncIterator(items, options) + } + /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ + public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { + return Awaited(schema, options) + } + /** `[JavaScript]` Creates a BigInt type */ + public BigInt(options: BigIntOptions = {}): TBigInt { + return BigInt(options) + } + /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ + public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + return ConstructorParameters(schema, options) + } + /** `[JavaScript]` Creates a Constructor type */ + public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { + return Constructor(parameters, returns, options) + } + /** `[JavaScript]` Creates a Date type */ + public Date(options: DateOptions = {}): TDate { + return Date(options) + } + /** `[JavaScript]` Creates a Function type */ + public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { + return FunctionType(parameters, returns, options) + } + /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ + public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + return InstanceType(schema, options) + } + /** `[JavaScript]` Creates an Iterator type */ + public Iterator(items: T, options: SchemaOptions = {}): TIterator { + return Iterator(items, options) + } + /** `[JavaScript]` Extracts the Parameters from the given Function type */ + public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + return Parameters(schema, options) + } + /** `[JavaScript]` Creates a Promise type */ + public Promise(item: T, options: SchemaOptions = {}): TPromise { + return Promise(item, options) + } + /** `[JavaScript]` Creates a RegExp type */ + public RegExp(pattern: string, options?: SchemaOptions): TRegExp + /** `[JavaScript]` Creates a RegExp type */ + public RegExp(regex: RegExp, options?: SchemaOptions): TRegExp + /** `[JavaScript]` Creates a RegExp type */ + public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { + return RegExp(unresolved as any, options) + } + /** `[JavaScript]` Extracts the ReturnType from the given Function type */ + public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return ReturnType(schema, options) + } + /** `[JavaScript]` Creates a Symbol type */ + public Symbol(options?: SchemaOptions): TSymbol { + return Symbol(options) + } + /** `[JavaScript]` Creates a Undefined type */ + public Undefined(options: SchemaOptions = {}): TUndefined { + return Undefined(options) + } + /** `[JavaScript]` Creates a Uint8Array type */ + public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return Uint8Array(options) + } + /** `[JavaScript]` Creates a Void type */ + public Void(options: SchemaOptions = {}): TVoid { + return Void(options) + } +} diff --git a/src/type/type/json.ts b/src/type/type/json.ts new file mode 100644 index 000000000..d4e6214f6 --- /dev/null +++ b/src/type/type/json.ts @@ -0,0 +1,338 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Any, type TAny } from '../any/index' +import { Array, type TArray, type ArrayOptions } from '../array/index' +import { Boolean, type TBoolean } from '../boolean/index' +import { Composite, type TComposite } from '../composite/index' +import { Const, type TConst } from '../const/index' +import { Deref, type TDeref } from '../deref/index' +import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index' +import { Exclude, type TExclude, type TExcludeFromMappedResult } from '../exclude/index' +import { Extends, type TExtends, type TExtendsFromMappedKey, type TExtendsFromMappedResult } from '../extends/index' +import { Extract, type TExtract, type TExtractFromMappedResult } from '../extract/index' +import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult } from '../indexed/index' +import { Integer, type IntegerOptions, type TInteger } from '../integer/index' +import { Intersect, type IntersectOptions } from '../intersect/index' +import { Capitalize, Uncapitalize, Lowercase, Uppercase, type TCapitalize, type TUncapitalize, type TLowercase, type TUppercase } from '../intrinsic/index' +import { KeyOf, type TKeyOf, type TKeyOfFromMappedResult } from '../keyof/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Mapped, type TMappedFunction, type TMapped, type TMappedResult } from '../mapped/index' +import { Never, type TNever } from '../never/index' +import { Not, type TNot } from '../not/index' +import { Null, type TNull } from '../null/index' +import { type TMappedKey } from '../mapped/index' +import { Number, type TNumber, type NumberOptions } from '../number/index' +import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' +import { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from '../omit/index' +import { Optional, type TOptionalWithFlag, type TOptionalFromMappedResult } from '../optional/index' +import { Partial, type TPartial, type TPartialFromMappedResult } from '../partial/index' +import { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from '../pick/index' +import { Readonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from '../readonly/index' +import { ReadonlyOptional, type TReadonlyOptional } from '../readonly-optional/index' +import { Record, type TRecordOrObject } from '../record/index' +import { Recursive, type TRecursive, type TThis } from '../recursive/index' +import { Ref, type TRef } from '../ref/index' +import { Required, type TRequired, type TRequiredFromMappedResult } from '../required/index' +import { Rest, type TRest } from '../rest/index' +import { type TSchema, type SchemaOptions } from '../schema/index' +import { Strict } from '../strict/index' +import { String, type TString, type StringOptions } from '../string/index' +import { TemplateLiteral, type TTemplateLiteral, type TTemplateLiteralKind, type TTemplateLiteralSyntax } from '../template-literal/index' +import { Transform, TransformDecodeBuilder } from '../transform/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Union } from '../union/index' +import { Unknown, type TUnknown } from '../unknown/index' +import { Unsafe, type TUnsafe, type UnsafeOptions } from '../unsafe/index' + +/** Json Type Builder with Static Resolution for TypeScript */ +export class JsonTypeBuilder { + // ------------------------------------------------------------------------ + // Strict + // ------------------------------------------------------------------------ + /** `[Json]` Omits compositing symbols from this schema */ + public Strict(schema: T): T { + return Strict(schema) + } + // ------------------------------------------------------------------------ + // Modifiers + // ------------------------------------------------------------------------ + /** `[Json]` Creates a Readonly and Optional property */ + public ReadonlyOptional(schema: T): TReadonlyOptional { + return ReadonlyOptional(schema) + } + /** `[Json]` Creates a Readonly property */ + public Readonly(schema: T, enable: F): TReadonlyFromMappedResult + /** `[Json]` Creates a Readonly property */ + public Readonly(schema: T, enable: F): TReadonlyWithFlag + /** `[Json]` Creates a Optional property */ + public Readonly(schema: T): TReadonlyFromMappedResult + /** `[Json]` Creates a Readonly property */ + public Readonly(schema: T): TReadonlyWithFlag + /** `[Json]` Creates a Readonly property */ + public Readonly(schema: TSchema, enable?: boolean): any { + return Readonly(schema, enable ?? true) + } + /** `[Json]` Creates a Optional property */ + public Optional(schema: T, enable: F): TOptionalFromMappedResult + /** `[Json]` Creates a Optional property */ + public Optional(schema: T, enable: F): TOptionalWithFlag + /** `[Json]` Creates a Optional property */ + public Optional(schema: T): TOptionalFromMappedResult + /** `[Json]` Creates a Optional property */ + public Optional(schema: T): TOptionalWithFlag + /** `[Json]` Creates a Optional property */ + public Optional(schema: TSchema, enable?: boolean): any { + return Optional(schema, enable ?? true) + } + // ------------------------------------------------------------------------ + // Types + // ------------------------------------------------------------------------ + /** `[Json]` Creates an Any type */ + public Any(options: SchemaOptions = {}): TAny { + return Any(options) + } + /** `[Json]` Creates an Array type */ + public Array(schema: T, options: ArrayOptions = {}): TArray { + return Array(schema, options) + } + /** `[Json]` Creates a Boolean type */ + public Boolean(options: SchemaOptions = {}): TBoolean { + return Boolean(options) + } + /** `[Json]` Intrinsic function to Capitalize LiteralString types */ + public Capitalize(schema: T, options: SchemaOptions = {}): TCapitalize { + return Capitalize(schema, options) + } + /** `[Json]` Creates a Composite object type */ + public Composite(objects: [...T], options?: ObjectOptions): TComposite { + return Composite(objects, options) as any // (error) TS 5.4.0-dev - review TComposite implementation + } + /** `[JavaScript]` Creates a readonly const type from the given value. */ + public Const(value: T, options: SchemaOptions = {}): TConst { + return Const(value, options) + } + /** `[Json]` Creates a dereferenced type */ + public Deref(schema: T, references: TSchema[]): TDeref { + return Deref(schema, references) + } + /** `[Json]` Creates a Enum type */ + public Enum>(item: T, options: SchemaOptions = {}): TEnum { + return Enum(item, options) + } + /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult + /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude + /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + public Exclude(unionType: TSchema, excludedMembers: TSchema, options: SchemaOptions = {}): any { + return Exclude(unionType, excludedMembers, options) + } + /** `[Json]` Creates a Conditional type */ + public Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedResult + /** `[Json]` Creates a Conditional type */ + public Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtendsFromMappedKey + /** `[Json]` Creates a Conditional type */ + public Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtends + /** `[Json]` Creates a Conditional type */ + public Extends(L: L, R: R, T: T, F: F, options: SchemaOptions = {}) { + return Extends(L, R, T, F, options) + } + /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ + public Extract(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult + /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ + public Extract(type: L, union: R, options?: SchemaOptions): TExtract + /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ + public Extract(type: TSchema, union: TSchema, options: SchemaOptions = {}): any { + return Extract(type, union, options) + } + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedResult + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedKey + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index>(T: T, K: K, options?: SchemaOptions): TIndex + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + return Index(schema, unresolved, options) + } + /** `[Json]` Creates an Integer type */ + public Integer(options: IntegerOptions = {}): TInteger { + return Integer(options) + } + /** `[Json]` Creates an Intersect type */ + public Intersect(T: [...T], options: IntersectOptions = {}): Intersect { + return Intersect(T, options) + } + /** `[Json]` Creates a KeyOf type */ + public KeyOf(schema: T, options?: SchemaOptions): TKeyOfFromMappedResult + /** `[Json]` Creates a KeyOf type */ + public KeyOf(schema: T, options?: SchemaOptions): TKeyOf + /** `[Json]` Creates a KeyOf type */ + public KeyOf(schema: TSchema, options: SchemaOptions = {}): any { + return KeyOf(schema, options) + } + /** `[Json]` Creates a Literal type */ + public Literal(value: T, options: SchemaOptions = {}): TLiteral { + return Literal(value, options) + } + /** `[Json]` Intrinsic function to Lowercase LiteralString types */ + public Lowercase(schema: T, options: SchemaOptions = {}): TLowercase { + return Lowercase(schema, options) + } + /** `[Json]` Creates a Mapped object type */ + public Mapped, F extends TMappedFunction = TMappedFunction, R extends TMapped = TMapped>(key: K, map: F, options?: ObjectOptions): R + /** `[Json]` Creates a Mapped object type */ + public Mapped = TMappedFunction, R extends TMapped = TMapped>(key: [...K], map: F, options?: ObjectOptions): R + /** `[Json]` Creates a Mapped object type */ + public Mapped(key: any, map: TMappedFunction, options: ObjectOptions = {}): any { + return Mapped(key, map, options) + } + /** `[Json]` Creates a Never type */ + public Never(options: SchemaOptions = {}): TNever { + return Never(options) + } + /** `[Json]` Creates a Not type */ + public Not(schema: T, options?: SchemaOptions): TNot { + return Not(schema, options) + } + /** `[Json]` Creates a Null type */ + public Null(options: SchemaOptions = {}): TNull { + return Null(options) + } + /** `[Json]` Creates a Number type */ + public Number(options: NumberOptions = {}): TNumber { + return Number(options) + } + /** `[Json]` Creates an Object type */ + public Object(properties: T, options: ObjectOptions = {}): TObject { + return Object(properties, options) + } + /** `[Json]` Constructs a type whose keys are omitted from the given type */ + public Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult + /** `[Json]` Constructs a type whose keys are omitted from the given type */ + public Omit(T: T, K: K): TOmitFromMappedKey + /** `[Json]` Constructs a type whose keys are omitted from the given type */ + public Omit>(T: T, K: K, options?: SchemaOptions): TOmit + /** `[Json]` Constructs a type whose keys are omitted from the given type */ + public Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit + /** `[Json]` Constructs a type whose keys are omitted from the given type */ + public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + return Omit(schema, unresolved, options) + } + /** `[Json]` Constructs a type where all properties are optional */ + public Partial(T: T, options?: ObjectOptions): TPartialFromMappedResult + /** `[Json]` Constructs a type where all properties are optional */ + public Partial(schema: T, options?: ObjectOptions): TPartial + /** `[Json]` Constructs a type where all properties are optional */ + public Partial(schema: TSchema, options: ObjectOptions = {}): any { + return Partial(schema, options) + } + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Pick(T: T, K: [...K]): TPickFromMappedResult + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Pick(T: T, K: K): TPickFromMappedKey + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Pick>(T: T, K: K, options?: SchemaOptions): TPick + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + return Pick(schema, unresolved, options) + } + /** `[Json]` Creates a Record type */ + public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordOrObject { + return Record(key, schema) + } + /** `[Json]` Creates a Recursive type */ + public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { + return Recursive(callback, options) + } + /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ + public Ref(schema: T, options?: SchemaOptions): TRef + /** `[Json]` Creates a Ref type. */ + public Ref($ref: string, options?: SchemaOptions): TRef + /** `[Json]` Creates a Ref type. */ + public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { + return Ref(unresolved as any, options) + } + /** `[Json]` Constructs a type where all properties are required */ + public Required(T: T, options?: ObjectOptions): TRequiredFromMappedResult + /** `[Json]` Constructs a type where all properties are required */ + public Required(schema: T, options?: ObjectOptions): TRequired + /** `[Json]` Constructs a type where all properties are required */ + public Required(schema: TSchema, options: ObjectOptions = {}): any { + return Required(schema, options) + } + /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ + public Rest(schema: T): TRest { + return Rest(schema) + } + /** `[Json]` Creates a String type */ + public String(options: StringOptions = {}): TString { + return String(options) + } + /** `[Json]` Creates a TemplateLiteral type from template dsl string */ + public TemplateLiteral(syntax: T, options?: SchemaOptions): TTemplateLiteralSyntax + /** `[Json]` Creates a TemplateLiteral type */ + public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral + /** `[Json]` Creates a TemplateLiteral type */ + public TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { + return TemplateLiteral(unresolved as any, options) + } + /** `[Json]` Creates a Transform type */ + public Transform(schema: I): TransformDecodeBuilder { + return Transform(schema) + } + /** `[Json]` Creates a Tuple type */ + public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { + return Tuple(items, options) + } + /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ + public Uncapitalize(schema: T, options: SchemaOptions = {}): TUncapitalize { + return Uncapitalize(schema, options) + } + /** `[Json]` Creates a Union type */ + public Union(schemas: [...T], options: SchemaOptions = {}): Union { + return Union(schemas, options) + } + /** `[Json]` Creates an Unknown type */ + public Unknown(options: SchemaOptions = {}): TUnknown { + return Unknown(options) + } + /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ + public Unsafe(options: UnsafeOptions = {}): TUnsafe { + return Unsafe(options) + } + /** `[Json]` Intrinsic function to Uppercase LiteralString types */ + public Uppercase(schema: T, options: SchemaOptions = {}): TUppercase { + return Uppercase(schema, options) + } +} diff --git a/src/type/type/type.ts b/src/type/type/type.ts new file mode 100644 index 000000000..53f275419 --- /dev/null +++ b/src/type/type/type.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Type: Module +// ------------------------------------------------------------------ +export { Any } from '../any/index' +export { Array } from '../array/index' +export { AsyncIterator } from '../async-iterator/index' +export { Awaited } from '../awaited/index' +export { BigInt } from '../bigint/index' +export { Boolean } from '../boolean/index' +export { Composite } from '../composite/index' +export { Const } from '../const/index' +export { Constructor } from '../constructor/index' +export { ConstructorParameters } from '../constructor-parameters/index' +export { Date } from '../date/index' +export { Deref } from '../deref/index' +export { Enum } from '../enum/index' +export { Exclude } from '../exclude/index' +export { Extends } from '../extends/index' +export { Extract } from '../extract/index' +export { Function } from '../function/index' +export { Index } from '../indexed/index' +export { InstanceType } from '../instance-type/index' +export { Integer } from '../integer/index' +export { Intersect } from '../intersect/index' +export { Capitalize, Uncapitalize, Lowercase, Uppercase } from '../intrinsic/index' +export { Iterator } from '../iterator/index' +export { KeyOf } from '../keyof/index' +export { Literal } from '../literal/index' +export { Mapped } from '../mapped/index' +export { Never } from '../never/index' +export { Not } from '../not/index' +export { Null } from '../null/index' +export { Number } from '../number/index' +export { Object } from '../object/index' +export { Omit } from '../omit/index' +export { Optional } from '../optional/index' +export { Parameters } from '../parameters/index' +export { Partial } from '../partial/index' +export { Pick } from '../pick/index' +export { Promise } from '../promise/index' +export { Readonly } from '../readonly/index' +export { ReadonlyOptional } from '../readonly-optional/index' +export { Record } from '../record/index' +export { Recursive } from '../recursive/index' +export { Ref } from '../ref/index' +export { RegExp } from '../regexp/index' +export { Required } from '../required/index' +export { Rest } from '../rest/index' +export { ReturnType } from '../return-type/index' +export { Strict } from '../strict/index' +export { String } from '../string/index' +export { Symbol } from '../symbol/index' +export { TemplateLiteral } from '../template-literal/index' +export { Transform } from '../transform/index' +export { Tuple } from '../tuple/index' +export { Uint8Array } from '../uint8array/index' +export { Undefined } from '../undefined/index' +export { Union } from '../union/index' +export { Unknown } from '../unknown/index' +export { Unsafe } from '../unsafe/index' +export { Void } from '../void/index' diff --git a/src/type/uint8array/index.ts b/src/type/uint8array/index.ts new file mode 100644 index 000000000..4c2296a42 --- /dev/null +++ b/src/type/uint8array/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './uint8array' diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts new file mode 100644 index 000000000..1c1732ced --- /dev/null +++ b/src/type/uint8array/uint8array.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface Uint8ArrayOptions extends SchemaOptions { + maxByteLength?: number + minByteLength?: number +} +export interface TUint8Array extends TSchema, Uint8ArrayOptions { + [Kind]: 'Uint8Array' + static: Uint8Array + type: 'uint8array' +} +/** `[JavaScript]` Creates a Uint8Array type */ +export function Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return { ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' } as unknown as TUint8Array +} diff --git a/src/type/undefined/index.ts b/src/type/undefined/index.ts new file mode 100644 index 000000000..cbc1c28e3 --- /dev/null +++ b/src/type/undefined/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './undefined' diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts new file mode 100644 index 000000000..a069fe06f --- /dev/null +++ b/src/type/undefined/undefined.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TUndefined extends TSchema { + [Kind]: 'Undefined' + static: undefined + type: 'undefined' +} +/** `[JavaScript]` Creates a Undefined type */ +export function Undefined(options: SchemaOptions = {}): TUndefined { + return { ...options, [Kind]: 'Undefined', type: 'undefined' } as unknown as TUndefined +} diff --git a/src/type/union/index.ts b/src/type/union/index.ts new file mode 100644 index 000000000..17dd10d72 --- /dev/null +++ b/src/type/union/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './union-evaluated' +export * from './union-type' +export * from './union' diff --git a/src/type/union/union-create.ts b/src/type/union/union-create.ts new file mode 100644 index 000000000..c2f261933 --- /dev/null +++ b/src/type/union/union-create.ts @@ -0,0 +1,36 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { CloneRest } from '../clone/type' +import { TUnion } from './union-type' +import { Kind } from '../symbols/index' + +export function UnionCreate(T: [...T], options: SchemaOptions): TUnion { + return { ...options, [Kind]: 'Union', anyOf: CloneRest(T) } as unknown as TUnion +} diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts new file mode 100644 index 000000000..248d4e600 --- /dev/null +++ b/src/type/union/union-evaluated.ts @@ -0,0 +1,122 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { SchemaOptions, TSchema } from '../schema/index' +import { OptionalKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { Discard } from '../discard/index' +import { Never, type TNever } from '../never/index' +import { Optional, type TOptional } from '../optional/index' +import type { TReadonly } from '../readonly/index' +import type { TUnion } from './union-type' +import { UnionCreate } from './union-create' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsOptional } from '../guard/type' +// ------------------------------------------------------------------ +// IsUnionOptional +// ------------------------------------------------------------------ +// prettier-ignore +type TIsUnionOptional = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? + L extends TOptional + ? true + : TIsUnionOptional + : false +) +// prettier-ignore +function IsUnionOptional(T: T): TIsUnionOptional { + return T.some(L => IsOptional(L)) as TIsUnionOptional +} +// ------------------------------------------------------------------ +// RemoveOptionalFromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TRemoveOptionalFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TOptional + ? TRemoveOptionalFromRest]> + : TRemoveOptionalFromRest + : Acc +) +// prettier-ignore +function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { + return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as TRemoveOptionalFromRest +} +// ------------------------------------------------------------------ +// RemoveOptionalFromType +// ------------------------------------------------------------------ +// prettier-ignore +type TRemoveOptionalFromType = ( + T extends TReadonly ? TReadonly> : + T extends TOptional ? TRemoveOptionalFromType : + T +) +// prettier-ignore +function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { + return ( + Discard(T, [OptionalKind]) + ) as TRemoveOptionalFromType +} +// ------------------------------------------------------------------ +// ResolveUnion +// ------------------------------------------------------------------ +// prettier-ignore +type TResolveUnion> = ( + TIsUnionOptional extends true + ? TOptional> + : TUnion +) +// prettier-ignore +function ResolveUnion(T: T, options: SchemaOptions): TResolveUnion { + return ( + IsUnionOptional(T) + ? Optional(UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options)) + : UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options) + ) as TResolveUnion +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +export type TUnionEvaluated = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + TResolveUnion +) +/** `[Json]` Creates an evaluated Union type */ +export function UnionEvaluated>(T: [...T], options: SchemaOptions = {}): R { + // prettier-ignore + return ( + T.length === 0 ? Never(options) : + T.length === 1 ? CloneType(T[0], options) : + ResolveUnion(T, options) + ) as R +} diff --git a/src/type/union/union-type.ts b/src/type/union/union-type.ts new file mode 100644 index 000000000..6eb5ba4d3 --- /dev/null +++ b/src/type/union/union-type.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// UnionStatic +// ------------------------------------------------------------------ +// prettier-ignore +type UnionStatic = { + [K in keyof T]: T[K] extends TSchema ? Static : never +}[number] + +// ------------------------------------------------------------------ +// TUnion +// ------------------------------------------------------------------ +export interface TUnion extends TSchema { + [Kind]: 'Union' + static: UnionStatic + anyOf: T +} diff --git a/src/type/union/union.ts b/src/type/union/union.ts new file mode 100644 index 000000000..5c5265e97 --- /dev/null +++ b/src/type/union/union.ts @@ -0,0 +1,49 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { type TNever, Never } from '../never/index' +import type { TUnion } from './union-type' +import { CloneType } from '../clone/type' +import { UnionCreate } from './union-create' + +// prettier-ignore +export type Union = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + TUnion +) +/** `[Json]` Creates a Union type */ +export function Union(T: [...T], options: SchemaOptions = {}): Union { + // prettier-ignore + return ( + T.length === 0 ? Never(options) : + T.length === 1 ? CloneType(T[0], options) : + UnionCreate(T, options) + ) as Union +} diff --git a/src/type/unknown/index.ts b/src/type/unknown/index.ts new file mode 100644 index 000000000..7d9c7e293 --- /dev/null +++ b/src/type/unknown/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './unknown' diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts new file mode 100644 index 000000000..f5a0d9a3d --- /dev/null +++ b/src/type/unknown/unknown.ts @@ -0,0 +1,42 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TUnknown extends TSchema { + [Kind]: 'Unknown' + static: unknown +} +/** `[Json]` Creates an Unknown type */ +export function Unknown(options: SchemaOptions = {}): TUnknown { + return { + ...options, + [Kind]: 'Unknown', + } as unknown as TUnknown +} diff --git a/src/type/unsafe/index.ts b/src/type/unsafe/index.ts new file mode 100644 index 000000000..78141b392 --- /dev/null +++ b/src/type/unsafe/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './unsafe' diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts new file mode 100644 index 000000000..26fb987af --- /dev/null +++ b/src/type/unsafe/unsafe.ts @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface UnsafeOptions extends SchemaOptions { + [Kind]?: string +} +export interface TUnsafe extends TSchema { + [Kind]: string + static: T +} +/** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ +export function Unsafe(options: UnsafeOptions = {}): TUnsafe { + return { + ...options, + [Kind]: options[Kind] ?? 'Unsafe', + } as unknown as TUnsafe +} diff --git a/src/type/void/index.ts b/src/type/void/index.ts new file mode 100644 index 000000000..3b9bf5199 --- /dev/null +++ b/src/type/void/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './void' diff --git a/src/type/void/void.ts b/src/type/void/void.ts new file mode 100644 index 000000000..867f33f47 --- /dev/null +++ b/src/type/void/void.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TVoid extends TSchema { + [Kind]: 'Void' + static: void + type: 'void' +} +/** `[JavaScript]` Creates a Void type */ +export function Void(options: SchemaOptions = {}): TVoid { + return { + ...options, + [Kind]: 'Void', + type: 'void', + } as unknown as TVoid +} diff --git a/src/typebox.ts b/src/typebox.ts deleted file mode 100644 index 57e8cb27b..000000000 --- a/src/typebox.ts +++ /dev/null @@ -1,3410 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -// -------------------------------------------------------------------------- -// Symbols -// -------------------------------------------------------------------------- -export const Transform = Symbol.for('TypeBox.Transform') -export const Readonly = Symbol.for('TypeBox.Readonly') -export const Optional = Symbol.for('TypeBox.Optional') -export const Hint = Symbol.for('TypeBox.Hint') -export const Kind = Symbol.for('TypeBox.Kind') -// -------------------------------------------------------------------------- -// Patterns -// -------------------------------------------------------------------------- -export const PatternBoolean = '(true|false)' -export const PatternNumber = '(0|[1-9][0-9]*)' -export const PatternString = '(.*)' -export const PatternBooleanExact = `^${PatternBoolean}$` -export const PatternNumberExact = `^${PatternNumber}$` -export const PatternStringExact = `^${PatternString}$` -// -------------------------------------------------------------------------- -// Helpers -// -------------------------------------------------------------------------- -export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never -export type TupleToUnion = { [K in keyof T]: T[K] }[number] -export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never -export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never -export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] -export type Discard = T extends [infer L, ...infer R] ? (L extends D ? Discard : [L, ...Discard]) : [] -export type Flat = T extends [] ? [] : T extends [infer L] ? [...Flat] : T extends [infer L, ...infer R] ? [...Flat, ...Flat] : [T] -export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T -export type Assert = T extends E ? T : never -export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never -export type Ensure = T extends infer U ? U : never -// -------------------------------------------------------------------------- -// Type Assertions -// -------------------------------------------------------------------------- -export type AssertProperties = T extends TProperties ? T : TProperties -export type AssertRest = T extends E ? T : [] -export type AssertType = T extends E ? T : TNever -// -------------------------------------------------------------------------- -// Modifiers -// -------------------------------------------------------------------------- -export type TReadonlyOptional = TOptional & TReadonly -export type TReadonly = T & { [Readonly]: 'Readonly' } -export type TOptional = T & { [Optional]: 'Optional' } -// -------------------------------------------------------------------------- -// Readonly Unwrap -// -------------------------------------------------------------------------- -// prettier-ignore -export type ReadonlyUnwrapType = - T extends TReadonly ? ReadonlyUnwrapType : - T extends TOptional ? TOptional> : - T -// prettier-ignore -export type ReadonlyUnwrapRest = T extends [infer L, ...infer R] - ? L extends TReadonly - ? [ReadonlyUnwrapType>, ...ReadonlyUnwrapRest>] - : [L, ...ReadonlyUnwrapRest>] - : [] -// -------------------------------------------------------------------------- -// Optional Unwrap -// -------------------------------------------------------------------------- -// prettier-ignore -export type OptionalUnwrapType = - T extends TReadonly ? TReadonly> : - T extends TOptional ? OptionalUnwrapType : - T -// prettier-ignore -export type OptionalUnwrapRest = T extends [infer L, ...infer R] - ? L extends TOptional - ? [OptionalUnwrapType>, ...OptionalUnwrapRest>] - : [L, ...OptionalUnwrapRest>] - : [] -// -------------------------------------------------------------------------- -// IntersectType -// -------------------------------------------------------------------------- -// prettier-ignore -export type IntersectOptional = T extends [infer L, ...infer R] - ? L extends TOptional> - ? IntersectOptional> - : false - : true -// prettier-ignore -export type IntersectResolve>> = IntersectOptional> extends true - ? TOptional>> - : TIntersect> -// prettier-ignore -export type IntersectType = - T extends [] ? TNever : - T extends [TSchema] ? AssertType : - IntersectResolve -// -------------------------------------------------------------------------- -// UnionType -// -------------------------------------------------------------------------- -// prettier-ignore -export type UnionOptional = T extends [infer L, ...infer R] - ? L extends (TOptional>) - ? true - : UnionOptional> - : false -// prettier-ignore -export type UnionResolve>> = UnionOptional> extends true - ? TOptional>> - : TUnion> -// prettier-ignore -export type UnionType = - T extends [] ? TNever : - T extends [TSchema] ? AssertType : - UnionResolve -// -------------------------------------------------------------------------- -// TSchema -// -------------------------------------------------------------------------- -export interface SchemaOptions { - $schema?: string - /** Id for this schema */ - $id?: string - /** Title of this schema */ - title?: string - /** Description of this schema */ - description?: string - /** Default value for this schema */ - default?: any - /** Example values matching this schema */ - examples?: any - /** Optional annotation for readOnly */ - readOnly?: boolean - /** Optional annotation for writeOnly */ - writeOnly?: boolean - [prop: string]: any -} -export interface TKind { - [Kind]: string -} -export interface TSchema extends SchemaOptions, TKind { - [Readonly]?: string - [Optional]?: string - [Hint]?: string - params: unknown[] - static: unknown -} -// -------------------------------------------------------------------------- -// TAnySchema -// -------------------------------------------------------------------------- -export type TAnySchema = - | TSchema - | TAny - | TArray - | TAsyncIterator - | TBigInt - | TBoolean - | TConstructor - | TDate - | TEnum - | TFunction - | TInteger - | TIntersect - | TIterator - | TLiteral - | TNot - | TNull - | TNumber - | TObject - | TPromise - | TRecord - | TRef - | TString - | TSymbol - | TTemplateLiteral - | TThis - | TTuple - | TUndefined - | TUnion - | TUint8Array - | TUnknown - | TVoid -// -------------------------------------------------------------------------- -// TNumeric -// -------------------------------------------------------------------------- -export interface NumericOptions extends SchemaOptions { - exclusiveMaximum?: N - exclusiveMinimum?: N - maximum?: N - minimum?: N - multipleOf?: N -} -// -------------------------------------------------------------------------- -// TAny -// -------------------------------------------------------------------------- -export interface TAny extends TSchema { - [Kind]: 'Any' - static: any -} -// -------------------------------------------------------------------------- -// TArray -// -------------------------------------------------------------------------- -export interface ArrayOptions extends SchemaOptions { - /** The minimum number of items in this array */ - minItems?: number - /** The maximum number of items in this array */ - maxItems?: number - /** Should this schema contain unique items */ - uniqueItems?: boolean - /** A schema for which some elements should match */ - contains?: TSchema - /** A minimum number of contains schema matches */ - minContains?: number - /** A maximum number of contains schema matches */ - maxContains?: number -} -export interface TArray extends TSchema, ArrayOptions { - [Kind]: 'Array' - static: Static[] - type: 'array' - items: T -} -// -------------------------------------------------------------------------- -// TAsyncIterator -// -------------------------------------------------------------------------- -export interface TAsyncIterator extends TSchema { - [Kind]: 'AsyncIterator' - static: AsyncIterableIterator> - type: 'AsyncIterator' - items: T -} -// ------------------------------------------------------------------------------- -// TAwaited -// ------------------------------------------------------------------------------- -// prettier-ignore -export type TAwaitedRest = T extends [infer L, ...infer R] - ? [TAwaited>, ...TAwaitedRest>] - : [] -// prettier-ignore -export type TAwaited = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TPromise ? TAwaited : - T -// -------------------------------------------------------------------------- -// TBigInt -// -------------------------------------------------------------------------- -export interface TBigInt extends TSchema, NumericOptions { - [Kind]: 'BigInt' - static: bigint - type: 'bigint' -} -// -------------------------------------------------------------------------- -// TBoolean -// -------------------------------------------------------------------------- -export interface TBoolean extends TSchema { - [Kind]: 'Boolean' - static: boolean - type: 'boolean' -} -// -------------------------------------------------------------------------- -// TConstructorParameters -// -------------------------------------------------------------------------- -export type TConstructorParameters> = Ensure> -// -------------------------------------------------------------------------- -// TInstanceType -// -------------------------------------------------------------------------- -export type TInstanceType> = T['returns'] -// -------------------------------------------------------------------------- -// TComposite -// -------------------------------------------------------------------------- -// prettier-ignore -export type TCompositeKeys = T extends [infer L, ...infer R] - ? keyof Assert['properties'] | TCompositeKeys> - : never -// prettier-ignore -export type TCompositeIndex, K extends string[]> = K extends [infer L, ...infer R] - ? { [_ in Assert]: TIndexType> } & TCompositeIndex> - : {} -// prettier-ignore -export type TCompositeReduce = UnionToTuple> extends infer K - ? Evaluate, Assert>> - : {} // ^ indexed via intersection of T -// prettier-ignore -export type TComposite = TIntersect extends TIntersect - ? TObject> - : TObject<{}> -// -------------------------------------------------------------------------- -// TConstructor -// -------------------------------------------------------------------------- -export type TConstructorReturnTypeResolve = Static -export type TConstructorParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] -export type TConstructorResolve = Ensure) => TConstructorReturnTypeResolve> -export interface TConstructor extends TSchema { - [Kind]: 'Constructor' - static: TConstructorResolve - type: 'Constructor' - parameters: T - returns: U -} -// -------------------------------------------------------------------------- -// TDate -// -------------------------------------------------------------------------- -export interface DateOptions extends SchemaOptions { - /** The exclusive maximum timestamp value */ - exclusiveMaximumTimestamp?: number - /** The exclusive minimum timestamp value */ - exclusiveMinimumTimestamp?: number - /** The maximum timestamp value */ - maximumTimestamp?: number - /** The minimum timestamp value */ - minimumTimestamp?: number - /** The multiple of timestamp value */ - multipleOfTimestamp?: number -} -export interface TDate extends TSchema, DateOptions { - [Kind]: 'Date' - static: Date - type: 'date' -} -// -------------------------------------------------------------------------- -// TEnum -// -------------------------------------------------------------------------- -export type TEnumRecord = Record -export type TEnumValue = string | number -export type TEnumKey = string -export interface TEnum = Record> extends TSchema { - [Kind]: 'Union' - [Hint]: 'Enum' - static: T[keyof T] - anyOf: TLiteral[] -} -// -------------------------------------------------------------------------- -// TExtends -// -------------------------------------------------------------------------- -// prettier-ignore -export type TExtends = - (Static extends Static ? T : U) extends infer O ? - UnionToTuple extends [infer X, infer Y] ? TUnion<[AssertType, AssertType]> : AssertType - : never -// -------------------------------------------------------------------------- -// TExclude -// -------------------------------------------------------------------------- -export type TExcludeTemplateLiteralResult = UnionType }[T]>>> -export type TExcludeTemplateLiteral = Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never -// prettier-ignore -export type TExcludeArray = AssertRest> extends Static ? never : T[K] -}[number]>> extends infer R ? UnionType> : never -// prettier-ignore -export type TExclude = - T extends TTemplateLiteral ? TExcludeTemplateLiteral : - T extends TUnion ? TExcludeArray : - T extends U ? TNever : T -// -------------------------------------------------------------------------- -// TExtract -// -------------------------------------------------------------------------- -export type TExtractTemplateLiteralResult = UnionType }[T]>>> -export type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never -// prettier-ignore -export type TExtractArray = AssertRest> extends Static ? T[K] : never -}[number]>> extends infer R ? UnionType> : never -// prettier-ignore -export type TExtract = - T extends TTemplateLiteral ? TExtractTemplateLiteral : - T extends TUnion ? TExtractArray : - T extends U ? T : T -// -------------------------------------------------------------------------- -// TFunction -// -------------------------------------------------------------------------- -export type TFunctionReturnTypeResolve = Static -export type TFunctionParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] -export type TFunctionResolve = Ensure<(...param: TFunctionParametersResolve) => TFunctionReturnTypeResolve> -export interface TFunction extends TSchema { - [Kind]: 'Function' - static: TFunctionResolve - type: 'Function' - parameters: T - returns: U -} -// -------------------------------------------------------------------------- -// TIndex -// -------------------------------------------------------------------------- -export type TIndexRest = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] -export type TIndexProperty = K extends keyof T ? [T[K]] : [] -export type TIndexTuple = K extends keyof T ? [T[K]] : [] -// prettier-ignore -export type TIndexType = - T extends TRecursive ? TIndexType : - T extends TIntersect ? IntersectType>, TNever>>> : - T extends TUnion ? UnionType>>> : - T extends TObject ? UnionType>>> : - T extends TTuple ? UnionType>>> : - [] -// prettier-ignore -export type TIndexRestMany = - K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : - [] -// prettier-ignore -export type TIndex = - T extends TRecursive ? TIndex : - T extends TIntersect ? UnionType>> : - T extends TUnion ? UnionType>> : - T extends TObject ? UnionType>> : - T extends TTuple ? UnionType>> : - TNever -// -------------------------------------------------------------------------- -// TIntrinsic -// -------------------------------------------------------------------------- -export type TIntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' -// prettier-ignore -export type TIntrinsicTemplateLiteral = - M extends ('Lowercase' | 'Uppercase') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...TIntrinsicTemplateLiteral, M>] : T : - M extends ('Capitalize' | 'Uncapitalize') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...R] : T : - T -// prettier-ignore -export type TIntrinsicLiteral = - T extends string ? - M extends 'Uncapitalize' ? Uncapitalize : - M extends 'Capitalize' ? Capitalize : - M extends 'Uppercase' ? Uppercase : - M extends 'Lowercase' ? Lowercase : - string - : T -// prettier-ignore -export type TIntrinsicRest = T extends [infer L, ...infer R] - ? [TIntrinsic, M>, ...TIntrinsicRest, M>] - : [] -// prettier-ignore -export type TIntrinsic = - T extends TTemplateLiteral ? TTemplateLiteral> : - T extends TUnion ? TUnion> : - T extends TLiteral ? TLiteral> : - T -// -------------------------------------------------------------------------- -// TInteger -// -------------------------------------------------------------------------- -export interface TInteger extends TSchema, NumericOptions { - [Kind]: 'Integer' - static: number - type: 'integer' -} -// -------------------------------------------------------------------------- -// TIntersect -// -------------------------------------------------------------------------- -export type TUnevaluatedProperties = undefined | TSchema | boolean -export interface IntersectOptions extends SchemaOptions { - unevaluatedProperties?: TUnevaluatedProperties -} -export interface TIntersect extends TSchema, IntersectOptions { - [Kind]: 'Intersect' - static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> - type?: 'object' - allOf: [...T] -} -// -------------------------------------------------------------------------- -// TIterator -// -------------------------------------------------------------------------- -export interface TIterator extends TSchema { - [Kind]: 'Iterator' - static: IterableIterator> - type: 'Iterator' - items: T -} -// -------------------------------------------------------------------------- -// TKeyOf -// -------------------------------------------------------------------------- -// prettier-ignore -export type TKeyOfProperties = Discard extends infer S - ? UnionToTuple<{[K in keyof S]: TLiteral>}[keyof S]> - : [], undefined> // note: optional properties produce undefined types in tuple result. discard. -// prettier-ignore -export type TKeyOfIndicesArray = UnionToTuple -// prettier-ignore -export type TKeyOfIndices = AssertRest extends infer R ? { - [K in keyof R] : TLiteral> -}: []> -// prettier-ignore -export type TKeyOf = ( - T extends TRecursive ? TKeyOfProperties : - T extends TIntersect ? TKeyOfProperties : - T extends TUnion ? TKeyOfProperties : - T extends TObject ? TKeyOfProperties : - T extends TTuple ? TKeyOfIndices : - T extends TArray ? [TNumber] : - T extends TRecord ? [K] : - [] -) extends infer R ? UnionType> : never -// -------------------------------------------------------------------------- -// TLiteral -// -------------------------------------------------------------------------- -export type TLiteralValue = boolean | number | string // | bigint - supported but variant disable due to potential numeric type conflicts -export type TLiteralBoolean = TLiteral -export type TLiteralNumber = TLiteral -export type TLiteralString = TLiteral -export interface TLiteral extends TSchema { - [Kind]: 'Literal' - static: T - const: T -} -// -------------------------------------------------------------------------- -// TNever -// -------------------------------------------------------------------------- -export interface TNever extends TSchema { - [Kind]: 'Never' - static: never - not: {} -} -// -------------------------------------------------------------------------- -// TNot -// -------------------------------------------------------------------------- -export interface TNot extends TSchema { - [Kind]: 'Not' - static: T extends TNot ? Static : unknown - not: T -} -// -------------------------------------------------------------------------- -// TNull -// -------------------------------------------------------------------------- -export interface TNull extends TSchema { - [Kind]: 'Null' - static: null - type: 'null' -} -// -------------------------------------------------------------------------- -// TNumber -// -------------------------------------------------------------------------- -export interface TNumber extends TSchema, NumericOptions { - [Kind]: 'Number' - static: number - type: 'number' -} -// -------------------------------------------------------------------------- -// TObject -// -------------------------------------------------------------------------- -export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? K : never) : never }[keyof T] -export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? never : K) : never }[keyof T] -export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? (T[K] extends TReadonly ? never : K) : never }[keyof T] -export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> -// prettier-ignore -export type PropertiesReducer> = Evaluate<( - Readonly>>> & - Readonly>> & - Partial>> & - Required>> -)> -// prettier-ignore -export type PropertiesReduce = PropertiesReducer -}> -export type TPropertyKey = string | number -export type TProperties = Record -export type ObjectProperties = T extends TObject ? U : never -export type ObjectPropertyKeys = T extends TObject ? keyof U : never -export type TAdditionalProperties = undefined | TSchema | boolean -export interface ObjectOptions extends SchemaOptions { - /** Additional property constraints for this object */ - additionalProperties?: TAdditionalProperties - /** The minimum number of properties allowed on this object */ - minProperties?: number - /** The maximum number of properties allowed on this object */ - maxProperties?: number -} -export interface TObject extends TSchema, ObjectOptions { - [Kind]: 'Object' - static: PropertiesReduce - additionalProperties?: TAdditionalProperties - type: 'object' - properties: T - required?: string[] -} -// -------------------------------------------------------------------------- -// TOmit -// -------------------------------------------------------------------------- -export type TOmitProperties = Evaluate>> -export type TOmitRest = AssertRest<{ [K2 in keyof T]: TOmit, K> }> -// prettier-ignore -export type TOmit = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TParameters -// -------------------------------------------------------------------------- -export type TParameters = Ensure> -// -------------------------------------------------------------------------- -// TPartial -// -------------------------------------------------------------------------- -export type TPartialObjectArray = AssertRest<{ [K in keyof T]: TPartial> }, TObject[]> -export type TPartialRest = AssertRest<{ [K in keyof T]: TPartial> }> -// prettier-ignore -export type TPartialProperties = Evaluate) ? TReadonlyOptional : - T[K] extends (TReadonly) ? TReadonlyOptional : - T[K] extends (TOptional) ? TOptional : - TOptional -}>> -// prettier-ignore -export type TPartial = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TPick -// -------------------------------------------------------------------------- -// Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, -// we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. -// prettier-ignore -export type TPickProperties = - Pick, keyof T>> extends infer R ? ({ - [K in keyof R]: AssertType extends TSchema ? R[K] : never - }): never -export type TPickRest = { [K2 in keyof T]: TPick, K> } -// prettier-ignore -export type TPick = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TPromise -// -------------------------------------------------------------------------- -export interface TPromise extends TSchema { - [Kind]: 'Promise' - static: Promise> - type: 'Promise' - item: TSchema -} -// -------------------------------------------------------------------------- -// TRecord -// -------------------------------------------------------------------------- -export type TRecordFromUnionLiteralString = { [_ in K['const']]: T } -export type TRecordFromUnionLiteralNumber = { [_ in K['const']]: T } -// prettier-ignore -export type TRecordFromEnumKey, T extends TSchema> = Ensure> -// prettier-ignore -export type TRecordFromUnionRest = K extends [infer L, ...infer R] ? ( - L extends TUnion ? TRecordFromUnionRest & TRecordFromUnionRest, T> : - L extends TLiteralString ? TRecordFromUnionLiteralString & TRecordFromUnionRest, T> : - L extends TLiteralNumber ? TRecordFromUnionLiteralNumber & TRecordFromUnionRest, T> : -{}) : {} -export type TRecordFromUnion = Ensure>>>> -export type TRecordFromTemplateLiteralKeyInfinite = Ensure> -export type TRecordFromTemplateLiteralKeyFinite> = Ensure]: T }>>> -// prettier-ignore -export type TRecordFromTemplateLiteralKey = IsTemplateLiteralFinite extends false - ? TRecordFromTemplateLiteralKeyInfinite - : TRecordFromTemplateLiteralKeyFinite -export type TRecordFromLiteralStringKey = Ensure> -export type TRecordFromLiteralNumberKey = Ensure> -export type TRecordFromStringKey = Ensure> -export type TRecordFromNumberKey = Ensure> -export type TRecordFromIntegerKey = Ensure> -// prettier-ignore -export type TRecordResolve = - K extends TEnum ? TRecordFromEnumKey : // Enum before Union (intercept Hint) - K extends TUnion ? TRecordFromUnion : - K extends TTemplateLiteral ? TRecordFromTemplateLiteralKey : - K extends TLiteralString ? TRecordFromLiteralStringKey : - K extends TLiteralNumber ? TRecordFromLiteralNumberKey : - K extends TString ? TRecordFromStringKey : - K extends TNumber ? TRecordFromNumberKey : - K extends TInteger ? TRecordFromIntegerKey : - TNever -export interface TRecord extends TSchema { - [Kind]: 'Record' - static: Record, string | number>, Static> - type: 'object' - patternProperties: { [pattern: string]: T } - additionalProperties: TAdditionalProperties -} -// -------------------------------------------------------------------------- -// TRecursive -// -------------------------------------------------------------------------- -export interface TThis extends TSchema { - [Kind]: 'This' - static: this['params'][0] - $ref: string -} -export type TRecursiveReduce = Static]> -export interface TRecursive extends TSchema { - [Hint]: 'Recursive' - static: TRecursiveReduce -} -// -------------------------------------------------------------------------- -// TRef -// -------------------------------------------------------------------------- -export interface TRef extends TSchema { - [Kind]: 'Ref' - static: Static - $ref: string -} -// -------------------------------------------------------------------------- -// TRest -// -------------------------------------------------------------------------- -export type TRest = T extends TIntersect ? R : T extends TUnion ? R : T extends TTuple ? R : [] -// -------------------------------------------------------------------------- -// TReturnType -// -------------------------------------------------------------------------- -export type TReturnType = T['returns'] -// -------------------------------------------------------------------------- -// TRequired -// -------------------------------------------------------------------------- -export type TRequiredRest = AssertRest<{ [K in keyof T]: TRequired> }> -// prettier-ignore -export type TRequiredProperties = Evaluate) ? TReadonly : - T[K] extends (TReadonly) ? TReadonly : - T[K] extends (TOptional) ? S : - T[K] -}>> -// prettier-ignore -export type TRequired = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TString -// -------------------------------------------------------------------------- -export type StringFormatOption = - | 'date-time' - | 'time' - | 'date' - | 'email' - | 'idn-email' - | 'hostname' - | 'idn-hostname' - | 'ipv4' - | 'ipv6' - | 'uri' - | 'uri-reference' - | 'iri' - | 'uuid' - | 'iri-reference' - | 'uri-template' - | 'json-pointer' - | 'relative-json-pointer' - | 'regex' - | ({} & string) -// prettier-ignore -export type StringContentEncodingOption = - | '7bit' - | '8bit' - | 'binary' - | 'quoted-printable' - | 'base64' - | ({} & string) -export interface StringOptions extends SchemaOptions { - /** The maximum string length */ - maxLength?: number - /** The minimum string length */ - minLength?: number - /** A regular expression pattern this string should match */ - pattern?: string - /** A format this string should match */ - format?: StringFormatOption - /** The content encoding for this string */ - contentEncoding?: StringContentEncodingOption - /** The content media type for this string */ - contentMediaType?: string -} -export interface TString extends TSchema, StringOptions { - [Kind]: 'String' - static: string - type: 'string' -} -// -------------------------------------------------------------------------- -// TSymbol -// -------------------------------------------------------------------------- -export type SymbolValue = string | number | undefined -export interface TSymbol extends TSchema, SchemaOptions { - [Kind]: 'Symbol' - static: symbol - type: 'symbol' -} -// ------------------------------------------------------------------------- -// TTemplateLiteralParserDsl -// ------------------------------------------------------------------------- -// prettier-ignore -export type TTemplateLiteralDslParserUnionLiteral = - T extends `${infer L}|${infer R}` ? [TLiteral>, ...TTemplateLiteralDslParserUnionLiteral] : - T extends `${infer L}` ? [TLiteral>] : - [] -export type TTemplateLiteralDslParserUnion = UnionType> -// prettier-ignore -export type TTemplateLiteralDslParserTerminal = - T extends 'boolean' ? TBoolean : - T extends 'bigint' ? TBigInt : - T extends 'number' ? TNumber : - T extends 'string' ? TString : - TTemplateLiteralDslParserUnion -// prettier-ignore -export type TTemplateLiteralDslParserTemplate = - T extends `{${infer L}}${infer R}` ? [TTemplateLiteralDslParserTerminal, ...TTemplateLiteralDslParserTemplate] : - T extends `${infer L}$${infer R}` ? [TLiteral, ...TTemplateLiteralDslParserTemplate] : - T extends `${infer L}` ? [TLiteral] : - [] -export type TTemplateLiteralDslParser = Ensure, TTemplateLiteralKind[]>>> -// -------------------------------------------------------------------------- -// TTemplateLiteral -// -------------------------------------------------------------------------- -// prettier-ignore -export type IsTemplateLiteralFiniteCheck = - T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray> : - T extends TUnion ? IsTemplateLiteralFiniteArray> : - T extends TString ? false : - T extends TBoolean ? false : - T extends TNumber ? false : - T extends TInteger ? false : - T extends TBigInt ? false : - T extends TLiteral ? true : - false -// prettier-ignore -export type IsTemplateLiteralFiniteArray = - T extends [infer L, ...infer R] ? IsTemplateLiteralFiniteCheck extends false ? false : IsTemplateLiteralFiniteArray> : - true -export type IsTemplateLiteralFinite = T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray : false -export type TTemplateLiteralKind = TUnion | TLiteral | TInteger | TTemplateLiteral | TNumber | TBigInt | TString | TBoolean | TNever -// prettier-ignore -export type TTemplateLiteralConst = - T extends TUnion ? { [K in keyof U]: TTemplateLiteralUnion, Acc> }[number] : - T extends TTemplateLiteral ? `${Static}` : - T extends TLiteral ? `${U}` : - T extends TString ? `${string}` : - T extends TNumber ? `${number}` : - T extends TBigInt ? `${bigint}` : - T extends TBoolean ? `${boolean}` : - never -// prettier-ignore -export type TTemplateLiteralUnion = - T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : - Acc -export type TTemplateLiteralKeyRest = Assert>, TPropertyKey[]> -export interface TTemplateLiteral extends TSchema { - [Kind]: 'TemplateLiteral' - static: TTemplateLiteralUnion - type: 'string' - pattern: string // todo: it may be possible to infer this pattern -} -// -------------------------------------------------------------------------- -// TTransform -// -------------------------------------------------------------------------- -// prettier-ignore -export type DecodeProperties = { - [K in keyof T]: DecodeType -} -// prettier-ignore -export type DecodeRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [DecodeType, ...DecodeRest] - : [] -// prettier-ignore -export type DecodeType = ( - T extends TOptional ? TOptional> : - T extends TReadonly ? TReadonly> : - T extends TTransform ? TUnsafe : - T extends TArray ? TArray> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor, DecodeType> : - T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction, DecodeType> : - T extends TIntersect ? TIntersect> : - T extends TIterator ? TIterator> : - T extends TNot ? TNot> : - T extends TObject ? TObject>> : - T extends TPromise ? TPromise> : - T extends TRecord ? TRecord> : - T extends TRecursive ? TRecursive> : - T extends TRef ? TRef> : - T extends TTuple ? TTuple> : - T extends TUnion ? TUnion> : - T -) -export type TransformFunction = (value: T) => U -export interface TransformOptions { - Decode: TransformFunction, O> - Encode: TransformFunction> -} -export type TTransformResolve = T extends TTransform ? S : Static -export interface TTransform extends TSchema { - static: TTransformResolve - [Transform]: TransformOptions - [key: string]: any -} -// -------------------------------------------------------------------------- -// TTuple -// -------------------------------------------------------------------------- -export type TTupleRest = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleRest, P>] : [] -export interface TTuple extends TSchema { - [Kind]: 'Tuple' - static: TTupleRest - type: 'array' - items?: T - additionalItems?: false - minItems: number - maxItems: number -} -// -------------------------------------------------------------------------- -// TUndefined -// -------------------------------------------------------------------------- -export interface TUndefined extends TSchema { - [Kind]: 'Undefined' - static: undefined - type: 'undefined' -} -// -------------------------------------------------------------------------- -// TUnionLiteral -// -------------------------------------------------------------------------- -// prettier-ignore -export type TLiteralUnionReduce[]> = - T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : - [] -// prettier-ignore -export type TUnionLiteralKeyRest[]>> = - T extends TUnion ? TLiteralUnionReduce[]>> : - [] -// -------------------------------------------------------------------------- -// TUnion -// -------------------------------------------------------------------------- -// prettier-ignore -export type TUnionTemplateLiteral> = Ensure}[S]>,TLiteral[]>>> -export interface TUnion extends TSchema { - [Kind]: 'Union' - static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] - anyOf: T -} -// -------------------------------------------------------------------------- -// TUint8Array -// -------------------------------------------------------------------------- -export interface Uint8ArrayOptions extends SchemaOptions { - maxByteLength?: number - minByteLength?: number -} -export interface TUint8Array extends TSchema, Uint8ArrayOptions { - [Kind]: 'Uint8Array' - static: Uint8Array - type: 'uint8array' -} -// -------------------------------------------------------------------------- -// TUnknown -// -------------------------------------------------------------------------- -export interface TUnknown extends TSchema { - [Kind]: 'Unknown' - static: unknown -} -// -------------------------------------------------------------------------- -// TUnsafe -// -------------------------------------------------------------------------- -export interface UnsafeOptions extends SchemaOptions { - [Kind]?: string -} -export interface TUnsafe extends TSchema { - [Kind]: string - static: T -} -// -------------------------------------------------------------------------- -// TVoid -// -------------------------------------------------------------------------- -export interface TVoid extends TSchema { - [Kind]: 'Void' - static: void - type: 'void' -} -// -------------------------------------------------------------------------- -// Static -// -------------------------------------------------------------------------- -/** Creates an decoded static type from a TypeBox type */ -export type StaticDecode = Static, P> -/** Creates an encoded static type from a TypeBox type */ -export type StaticEncode = Static -/** Creates a static type from a TypeBox type */ -export type Static = (T & { params: P })['static'] -// -------------------------------------------------------------------------- -// TypeRegistry -// -------------------------------------------------------------------------- -export type TypeRegistryValidationFunction = (schema: TSchema, value: unknown) => boolean -/** A registry for user defined types */ -export namespace TypeRegistry { - const map = new Map>() - /** Returns the entries in this registry */ - export function Entries() { - return new Map(map) - } - /** Clears all user defined types */ - export function Clear() { - return map.clear() - } - /** Deletes a registered type */ - export function Delete(kind: string) { - return map.delete(kind) - } - /** Returns true if this registry contains this kind */ - export function Has(kind: string) { - return map.has(kind) - } - /** Sets a validation function for a user defined type */ - export function Set(kind: string, func: TypeRegistryValidationFunction) { - map.set(kind, func) - } - /** Gets a custom validation function for a user defined type */ - export function Get(kind: string) { - return map.get(kind) - } -} -// -------------------------------------------------------------------------- -// TypeBoxError -// -------------------------------------------------------------------------- -export class TypeBoxError extends Error { - constructor(message: string) { - super(message) - } -} -// -------------------------------------------------------------------------- -// TypeRegistry -// -------------------------------------------------------------------------- -export type FormatRegistryValidationFunction = (value: string) => boolean -/** A registry for user defined string formats */ -export namespace FormatRegistry { - const map = new Map() - /** Returns the entries in this registry */ - export function Entries() { - return new Map(map) - } - /** Clears all user defined string formats */ - export function Clear() { - return map.clear() - } - /** Deletes a registered format */ - export function Delete(format: string) { - return map.delete(format) - } - /** Returns true if the user defined string format exists */ - export function Has(format: string) { - return map.has(format) - } - /** Sets a validation function for a user defined string format */ - export function Set(format: string, func: FormatRegistryValidationFunction) { - map.set(format, func) - } - /** Gets a validation function for a user defined string format */ - export function Get(format: string) { - return map.get(format) - } -} -// -------------------------------------------------------------------------- -// ValueGuard -// -------------------------------------------------------------------------- -/** Provides functions to type guard raw JavaScript values */ -export namespace ValueGuard { - /** Returns true if this value is an array */ - export function IsArray(value: unknown): value is unknown[] { - return Array.isArray(value) - } - /** Returns true if this value is bigint */ - export function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - /** Returns true if this value is a boolean */ - export function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - /** Returns true if this value is a Date object */ - export function IsDate(value: unknown): value is Date { - return value instanceof globalThis.Date - } - /** Returns true if this value is null */ - export function IsNull(value: unknown): value is null { - return value === null - } - /** Returns true if this value is number */ - export function IsNumber(value: unknown): value is number { - return typeof value === 'number' - } - /** Returns true if this value is an object */ - export function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null - } - /** Returns true if this value is string */ - export function IsString(value: unknown): value is string { - return typeof value === 'string' - } - /** Returns true if this value is a Uint8Array */ - export function IsUint8Array(value: unknown): value is Uint8Array { - return value instanceof globalThis.Uint8Array - } - /** Returns true if this value is undefined */ - export function IsUndefined(value: unknown): value is undefined { - return value === undefined - } -} -// -------------------------------------------------------------------------- -// TypeGuard -// -------------------------------------------------------------------------- -export class TypeGuardUnknownTypeError extends TypeBoxError {} -/** Provides functions to test if JavaScript values are TypeBox types */ -export namespace TypeGuard { - function IsPattern(value: unknown): value is string { - try { - new RegExp(value as string) - return true - } catch { - return false - } - } - function IsControlCharacterFree(value: unknown): value is string { - if (!ValueGuard.IsString(value)) return false - for (let i = 0; i < value.length; i++) { - const code = value.charCodeAt(i) - if ((code >= 7 && code <= 13) || code === 27 || code === 127) { - return false - } - } - return true - } - function IsAdditionalProperties(value: unknown): value is TAdditionalProperties { - return IsOptionalBoolean(value) || TSchema(value) - } - function IsOptionalBigInt(value: unknown): value is bigint | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsBigInt(value) - } - function IsOptionalNumber(value: unknown): value is number | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsNumber(value) - } - function IsOptionalBoolean(value: unknown): value is boolean | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsBoolean(value) - } - function IsOptionalString(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsString(value) - } - function IsOptionalPattern(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) - } - function IsOptionalFormat(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value)) - } - function IsOptionalSchema(value: unknown): value is boolean | undefined { - return ValueGuard.IsUndefined(value) || TSchema(value) - } - // ---------------------------------------------------------------- - // Types - // ---------------------------------------------------------------- - /** Returns true if the given value is TAny */ - export function TAny(schema: unknown): schema is TAny { - // prettier-ignore - return ( - TKindOf(schema, 'Any') && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TArray */ - export function TArray(schema: unknown): schema is TArray { - return ( - TKindOf(schema, 'Array') && - schema.type === 'array' && - IsOptionalString(schema.$id) && - TSchema(schema.items) && - IsOptionalNumber(schema.minItems) && - IsOptionalNumber(schema.maxItems) && - IsOptionalBoolean(schema.uniqueItems) && - IsOptionalSchema(schema.contains) && - IsOptionalNumber(schema.minContains) && - IsOptionalNumber(schema.maxContains) - ) - } - /** Returns true if the given value is TAsyncIterator */ - export function TAsyncIterator(schema: unknown): schema is TAsyncIterator { - // prettier-ignore - return ( - TKindOf(schema, 'AsyncIterator') && - schema.type === 'AsyncIterator' && - IsOptionalString(schema.$id) && - TSchema(schema.items) - ) - } - /** Returns true if the given value is TBigInt */ - export function TBigInt(schema: unknown): schema is TBigInt { - // prettier-ignore - return ( - TKindOf(schema, 'BigInt') && - schema.type === 'bigint' && - IsOptionalString(schema.$id) && - IsOptionalBigInt(schema.exclusiveMaximum) && - IsOptionalBigInt(schema.exclusiveMinimum) && - IsOptionalBigInt(schema.maximum) && - IsOptionalBigInt(schema.minimum) && - IsOptionalBigInt(schema.multipleOf) - ) - } - /** Returns true if the given value is TBoolean */ - export function TBoolean(schema: unknown): schema is TBoolean { - // prettier-ignore - return ( - TKindOf(schema, 'Boolean') && - schema.type === 'boolean' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TConstructor */ - export function TConstructor(schema: unknown): schema is TConstructor { - // prettier-ignore - return ( - TKindOf(schema, 'Constructor') && - schema.type === 'Constructor' && - IsOptionalString(schema.$id) && - ValueGuard.IsArray(schema.parameters) && - schema.parameters.every(schema => TSchema(schema)) && - TSchema(schema.returns) - ) - } - /** Returns true if the given value is TDate */ - export function TDate(schema: unknown): schema is TDate { - return ( - TKindOf(schema, 'Date') && - schema.type === 'Date' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximumTimestamp) && - IsOptionalNumber(schema.exclusiveMinimumTimestamp) && - IsOptionalNumber(schema.maximumTimestamp) && - IsOptionalNumber(schema.minimumTimestamp) && - IsOptionalNumber(schema.multipleOfTimestamp) - ) - } - /** Returns true if the given value is TFunction */ - export function TFunction(schema: unknown): schema is TFunction { - // prettier-ignore - return ( - TKindOf(schema, 'Function') && - schema.type === 'Function' && - IsOptionalString(schema.$id) && - ValueGuard.IsArray(schema.parameters) && - schema.parameters.every(schema => TSchema(schema)) && - TSchema(schema.returns) - ) - } - /** Returns true if the given value is TInteger */ - export function TInteger(schema: unknown): schema is TInteger { - return ( - TKindOf(schema, 'Integer') && - schema.type === 'integer' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.multipleOf) - ) - } - /** Returns true if the given value is TIntersect */ - export function TIntersect(schema: unknown): schema is TIntersect { - // prettier-ignore - return ( - TKindOf(schema, 'Intersect') && - (ValueGuard.IsString(schema.type) && schema.type !== 'object' ? false : true) && - ValueGuard.IsArray(schema.allOf) && - schema.allOf.every(schema => TSchema(schema) && !TTransform(schema)) && - IsOptionalString(schema.type) && - (IsOptionalBoolean(schema.unevaluatedProperties) || IsOptionalSchema(schema.unevaluatedProperties)) && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TIterator */ - export function TIterator(schema: unknown): schema is TIterator { - // prettier-ignore - return ( - TKindOf(schema, 'Iterator') && - schema.type === 'Iterator' && - IsOptionalString(schema.$id) && - TSchema(schema.items) - ) - } - /** Returns true if the given value is a TKind with the given name. */ - export function TKindOf(schema: unknown, kind: T): schema is Record & { [Kind]: T } { - return TKind(schema) && schema[Kind] === kind - } - /** Returns true if the given value is TKind */ - export function TKind(schema: unknown): schema is Record & { [Kind]: string } { - return ValueGuard.IsObject(schema) && Kind in schema && ValueGuard.IsString(schema[Kind]) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralString(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsString(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralNumber(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsNumber(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralBoolean(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsBoolean(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteral(schema: unknown): schema is TLiteral { - // prettier-ignore - return ( - TKindOf(schema, 'Literal') && - IsOptionalString(schema.$id) && ( - ValueGuard.IsBoolean(schema.const) || - ValueGuard.IsNumber(schema.const) || - ValueGuard.IsString(schema.const) - ) - ) - } - /** Returns true if the given value is TNever */ - export function TNever(schema: unknown): schema is TNever { - // prettier-ignore - return ( - TKindOf(schema, 'Never') && - ValueGuard.IsObject(schema.not) && - Object.getOwnPropertyNames(schema.not).length === 0 - ) - } - /** Returns true if the given value is TNot */ - export function TNot(schema: unknown): schema is TNot { - // prettier-ignore - return ( - TKindOf(schema, 'Not') && - TSchema(schema.not) - ) - } - /** Returns true if the given value is TNull */ - export function TNull(schema: unknown): schema is TNull { - // prettier-ignore - return ( - TKindOf(schema, 'Null') && - schema.type === 'null' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TNumber */ - export function TNumber(schema: unknown): schema is TNumber { - return ( - TKindOf(schema, 'Number') && - schema.type === 'number' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.multipleOf) - ) - } - /** Returns true if the given value is TObject */ - export function TObject(schema: unknown): schema is TObject { - // prettier-ignore - return ( - TKindOf(schema, 'Object') && - schema.type === 'object' && - IsOptionalString(schema.$id) && - ValueGuard.IsObject(schema.properties) && - IsAdditionalProperties(schema.additionalProperties) && - IsOptionalNumber(schema.minProperties) && - IsOptionalNumber(schema.maxProperties) && - Object.entries(schema.properties).every(([key, schema]) => IsControlCharacterFree(key) && TSchema(schema)) - ) - } - /** Returns true if the given value is TPromise */ - export function TPromise(schema: unknown): schema is TPromise { - // prettier-ignore - return ( - TKindOf(schema, 'Promise') && - schema.type === 'Promise' && - IsOptionalString(schema.$id) && - TSchema(schema.item) - ) - } - /** Returns true if the given value is TRecord */ - export function TRecord(schema: unknown): schema is TRecord { - // prettier-ignore - return ( - TKindOf(schema, 'Record') && - schema.type === 'object' && - IsOptionalString(schema.$id) && - IsAdditionalProperties(schema.additionalProperties) && - ValueGuard.IsObject(schema.patternProperties) && - ((schema: Record) => { - const keys = Object.getOwnPropertyNames(schema.patternProperties) - return ( - keys.length === 1 && - IsPattern(keys[0]) && - ValueGuard.IsObject(schema.patternProperties) && - TSchema(schema.patternProperties[keys[0]]) - ) - })(schema) - ) - } - /** Returns true if this value is TRecursive */ - export function TRecursive(schema: unknown): schema is { [Hint]: 'Recursive' } { - return ValueGuard.IsObject(schema) && Hint in schema && schema[Hint] === 'Recursive' - } - /** Returns true if the given value is TRef */ - export function TRef(schema: unknown): schema is TRef { - // prettier-ignore - return ( - TKindOf(schema, 'Ref') && - IsOptionalString(schema.$id) && - ValueGuard.IsString(schema.$ref) - ) - } - /** Returns true if the given value is TString */ - export function TString(schema: unknown): schema is TString { - // prettier-ignore - return ( - TKindOf(schema, 'String') && - schema.type === 'string' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minLength) && - IsOptionalNumber(schema.maxLength) && - IsOptionalPattern(schema.pattern) && - IsOptionalFormat(schema.format) - ) - } - /** Returns true if the given value is TSymbol */ - export function TSymbol(schema: unknown): schema is TSymbol { - // prettier-ignore - return ( - TKindOf(schema, 'Symbol') && - schema.type === 'symbol' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TTemplateLiteral */ - export function TTemplateLiteral(schema: unknown): schema is TTemplateLiteral { - // prettier-ignore - return ( - TKindOf(schema, 'TemplateLiteral') && - schema.type === 'string' && - ValueGuard.IsString(schema.pattern) && - schema.pattern[0] === '^' && - schema.pattern[schema.pattern.length - 1] === '$' - ) - } - /** Returns true if the given value is TThis */ - export function TThis(schema: unknown): schema is TThis { - // prettier-ignore - return ( - TKindOf(schema, 'This') && - IsOptionalString(schema.$id) && - ValueGuard.IsString(schema.$ref) - ) - } - /** Returns true of this value is TTransform */ - export function TTransform(schema: unknown): schema is { [Transform]: TransformOptions } { - return ValueGuard.IsObject(schema) && Transform in schema - } - /** Returns true if the given value is TTuple */ - export function TTuple(schema: unknown): schema is TTuple { - // prettier-ignore - return ( - TKindOf(schema, 'Tuple') && - schema.type === 'array' && - IsOptionalString(schema.$id) && - ValueGuard.IsNumber(schema.minItems) && - ValueGuard.IsNumber(schema.maxItems) && - schema.minItems === schema.maxItems && - (( // empty - ValueGuard.IsUndefined(schema.items) && - ValueGuard.IsUndefined(schema.additionalItems) && - schema.minItems === 0 - ) || ( - ValueGuard.IsArray(schema.items) && - schema.items.every(schema => TSchema(schema)) - )) - ) - } - /** Returns true if the given value is TUndefined */ - export function TUndefined(schema: unknown): schema is TUndefined { - // prettier-ignore - return ( - TKindOf(schema, 'Undefined') && - schema.type === 'undefined' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TUnion[]> */ - export function TUnionLiteral(schema: unknown): schema is TUnion { - return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) - } - /** Returns true if the given value is TUnion */ - export function TUnion(schema: unknown): schema is TUnion { - // prettier-ignore - return ( - TKindOf(schema, 'Union') && - IsOptionalString(schema.$id) && - ValueGuard.IsObject(schema) && - ValueGuard.IsArray(schema.anyOf) && - schema.anyOf.every(schema => TSchema(schema)) - ) - } - /** Returns true if the given value is TUint8Array */ - export function TUint8Array(schema: unknown): schema is TUint8Array { - // prettier-ignore - return ( - TKindOf(schema, 'Uint8Array') && - schema.type === 'Uint8Array' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minByteLength) && - IsOptionalNumber(schema.maxByteLength) - ) - } - /** Returns true if the given value is TUnknown */ - export function TUnknown(schema: unknown): schema is TUnknown { - // prettier-ignore - return ( - TKindOf(schema, 'Unknown') && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is a raw TUnsafe */ - export function TUnsafe(schema: unknown): schema is TUnsafe { - return TKindOf(schema, 'Unsafe') - } - /** Returns true if the given value is TVoid */ - export function TVoid(schema: unknown): schema is TVoid { - // prettier-ignore - return ( - TKindOf(schema, 'Void') && - schema.type === 'void' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if this value has a Readonly symbol */ - export function TReadonly(schema: T): schema is TReadonly { - return ValueGuard.IsObject(schema) && schema[Readonly] === 'Readonly' - } - /** Returns true if this value has a Optional symbol */ - export function TOptional(schema: T): schema is TOptional { - return ValueGuard.IsObject(schema) && schema[Optional] === 'Optional' - } - /** Returns true if the given value is TSchema */ - export function TSchema(schema: unknown): schema is TSchema { - // prettier-ignore - return ( - ValueGuard.IsObject(schema) - ) && ( - TAny(schema) || - TArray(schema) || - TBoolean(schema) || - TBigInt(schema) || - TAsyncIterator(schema) || - TConstructor(schema) || - TDate(schema) || - TFunction(schema) || - TInteger(schema) || - TIntersect(schema) || - TIterator(schema) || - TLiteral(schema) || - TNever(schema) || - TNot(schema) || - TNull(schema) || - TNumber(schema) || - TObject(schema) || - TPromise(schema) || - TRecord(schema) || - TRef(schema) || - TString(schema) || - TSymbol(schema) || - TTemplateLiteral(schema) || - TThis(schema) || - TTuple(schema) || - TUndefined(schema) || - TUnion(schema) || - TUint8Array(schema) || - TUnknown(schema) || - TUnsafe(schema) || - TVoid(schema) || - (TKind(schema) && TypeRegistry.Has(schema[Kind] as any)) - ) - } -} -// -------------------------------------------------------------------------- -// ExtendsUndefined -// -------------------------------------------------------------------------- -/** Fast undefined check used for properties of type undefined */ -export namespace ExtendsUndefined { - export function Check(schema: TSchema): boolean { - return schema[Kind] === 'Intersect' - ? (schema as TIntersect).allOf.every((schema) => Check(schema)) - : schema[Kind] === 'Union' - ? (schema as TUnion).anyOf.some((schema) => Check(schema)) - : schema[Kind] === 'Undefined' - ? true - : schema[Kind] === 'Not' - ? !Check(schema.not) - : false - } -} -// -------------------------------------------------------------------------- -// TypeExtends -// -------------------------------------------------------------------------- -export class TypeExtendsError extends TypeBoxError {} -export enum TypeExtendsResult { - Union, - True, - False, -} -export namespace TypeExtends { - // -------------------------------------------------------------------------- - // IntoBooleanResult - // -------------------------------------------------------------------------- - function IntoBooleanResult(result: TypeExtendsResult) { - return result === TypeExtendsResult.False ? result : TypeExtendsResult.True - } - // -------------------------------------------------------------------------- - // Throw - // -------------------------------------------------------------------------- - function Throw(message: string): never { - throw new TypeExtendsError(message) - } - // -------------------------------------------------------------------------- - // StructuralRight - // -------------------------------------------------------------------------- - function IsStructuralRight(right: TSchema): boolean { - // prettier-ignore - return ( - TypeGuard.TNever(right) || - TypeGuard.TIntersect(right) || - TypeGuard.TUnion(right) || - TypeGuard.TUnknown(right) || - TypeGuard.TAny(right) - ) - } - function StructuralRight(left: TSchema, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TNever(right) ? TNeverRight(left, right) : - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - Throw('StructuralRight') - ) - } - // -------------------------------------------------------------------------- - // Any - // -------------------------------------------------------------------------- - function TAnyRight(left: TSchema, right: TAny) { - return TypeExtendsResult.True - } - function TAny(left: TAny, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) ? TypeExtendsResult.True : - TypeGuard.TUnion(right) ? TypeExtendsResult.Union : - TypeGuard.TUnknown(right) ? TypeExtendsResult.True : - TypeGuard.TAny(right) ? TypeExtendsResult.True : - TypeExtendsResult.Union - ) - } - // -------------------------------------------------------------------------- - // Array - // -------------------------------------------------------------------------- - function TArrayRight(left: TSchema, right: TArray) { - // prettier-ignore - return ( - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ?TypeExtendsResult.Union : - TypeGuard.TNever(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TArray(left: TArray, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TArray(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // AsyncIterator - // -------------------------------------------------------------------------- - function TAsyncIterator(left: TAsyncIterator, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TAsyncIterator(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // BigInt - // -------------------------------------------------------------------------- - function TBigInt(left: TBigInt, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TBigInt(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Boolean - // -------------------------------------------------------------------------- - function TBooleanRight(left: TSchema, right: TBoolean) { - return TypeGuard.TLiteral(left) && ValueGuard.IsBoolean(left.const) ? TypeExtendsResult.True : TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False - } - function TBoolean(left: TBoolean, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TBoolean(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Constructor - // -------------------------------------------------------------------------- - function TConstructor(left: TConstructor, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TConstructor(right) ? TypeExtendsResult.False : - left.parameters.length > right.parameters.length ? TypeExtendsResult.False : - (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.returns, right.returns)) - ) - } - // -------------------------------------------------------------------------- - // Date - // -------------------------------------------------------------------------- - function TDate(left: TDate, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TDate(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Function - // -------------------------------------------------------------------------- - function TFunction(left: TFunction, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TFunction(right) ? TypeExtendsResult.False : - left.parameters.length > right.parameters.length ? TypeExtendsResult.False : - (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.returns, right.returns)) - ) - } - // -------------------------------------------------------------------------- - // Integer - // -------------------------------------------------------------------------- - function TIntegerRight(left: TSchema, right: TInteger) { - // prettier-ignore - return ( - TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const) ? TypeExtendsResult.True : - TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TInteger(left: TInteger, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Intersect - // -------------------------------------------------------------------------- - function TIntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { - // prettier-ignore - return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - function TIntersect(left: TIntersect, right: TSchema) { - // prettier-ignore - return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - // -------------------------------------------------------------------------- - // Iterator - // -------------------------------------------------------------------------- - function TIterator(left: TIterator, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TIterator(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // Literal - // -------------------------------------------------------------------------- - function TLiteral(left: TLiteral, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TString(right) ? TStringRight(left, right) : - TypeGuard.TNumber(right) ? TNumberRight(left, right) : - TypeGuard.TInteger(right) ? TIntegerRight(left, right) : - TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Never - // -------------------------------------------------------------------------- - function TNeverRight(left: TSchema, right: TNever) { - return TypeExtendsResult.False - } - function TNever(left: TNever, right: TSchema) { - return TypeExtendsResult.True - } - // -------------------------------------------------------------------------- - // Not - // -------------------------------------------------------------------------- - function UnwrapTNot(schema: T): TUnknown | TNot['not'] { - let [current, depth]: [TSchema, number] = [schema, 0] - while (true) { - if (!TypeGuard.TNot(current)) break - current = current.not - depth += 1 - } - return depth % 2 === 0 ? current : Type.Unknown() - } - function TNot(left: TSchema, right: TSchema) { - // TypeScript has no concept of negated types, and attempts to correctly check the negated - // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer - // the type. Instead we unwrap to either unknown or T and continue evaluating. - // prettier-ignore - return ( - TypeGuard.TNot(left) ? Visit(UnwrapTNot(left), right) : - TypeGuard.TNot(right) ? Visit(left, UnwrapTNot(right)) : - Throw('Invalid fallthrough for Not') - ) - } - // -------------------------------------------------------------------------- - // Null - // -------------------------------------------------------------------------- - function TNull(left: TNull, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TNull(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Number - // -------------------------------------------------------------------------- - function TNumberRight(left: TSchema, right: TNumber) { - // prettier-ignore - return ( - TypeGuard.TLiteralNumber(left) ? TypeExtendsResult.True : - TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TNumber(left: TNumber, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Object - // -------------------------------------------------------------------------- - function IsObjectPropertyCount(schema: TObject, count: number) { - return Object.getOwnPropertyNames(schema.properties).length === count - } - function IsObjectStringLike(schema: TObject) { - return IsObjectArrayLike(schema) - } - function IsObjectSymbolLike(schema: TObject) { - // prettier-ignore - return IsObjectPropertyCount(schema, 0) || ( - IsObjectPropertyCount(schema, 1) && 'description' in schema.properties && TypeGuard.TUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && (( - TypeGuard.TString(schema.properties.description.anyOf[0]) && - TypeGuard.TUndefined(schema.properties.description.anyOf[1]) - ) || ( - TypeGuard.TString(schema.properties.description.anyOf[1]) && - TypeGuard.TUndefined(schema.properties.description.anyOf[0]) - )) - ) - } - function IsObjectNumberLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectBooleanLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectBigIntLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectDateLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectUint8ArrayLike(schema: TObject) { - return IsObjectArrayLike(schema) - } - function IsObjectFunctionLike(schema: TObject) { - const length = Type.Number() - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) - } - function IsObjectConstructorLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectArrayLike(schema: TObject) { - const length = Type.Number() - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) - } - function IsObjectPromiseLike(schema: TObject) { - const then = Type.Function([Type.Any()], Type.Any()) - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'then' in schema.properties && IntoBooleanResult(Visit(schema.properties['then'], then)) === TypeExtendsResult.True) - } - // -------------------------------------------------------------------------- - // Property - // -------------------------------------------------------------------------- - function Property(left: TSchema, right: TSchema) { - // prettier-ignore - return ( - Visit(left, right) === TypeExtendsResult.False ? TypeExtendsResult.False : - TypeGuard.TOptional(left) && !TypeGuard.TOptional(right) ? TypeExtendsResult.False : - TypeExtendsResult.True - ) - } - function TObjectRight(left: TSchema, right: TObject) { - // prettier-ignore - return ( - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ? TypeExtendsResult.Union : ( - TypeGuard.TNever(left) || - (TypeGuard.TLiteralString(left) && IsObjectStringLike(right)) || - (TypeGuard.TLiteralNumber(left) && IsObjectNumberLike(right)) || - (TypeGuard.TLiteralBoolean(left) && IsObjectBooleanLike(right)) || - (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || - (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) || - (TypeGuard.TString(left) && IsObjectStringLike(right)) || - (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || - (TypeGuard.TNumber(left) && IsObjectNumberLike(right)) || - (TypeGuard.TInteger(left) && IsObjectNumberLike(right)) || - (TypeGuard.TBoolean(left) && IsObjectBooleanLike(right)) || - (TypeGuard.TUint8Array(left) && IsObjectUint8ArrayLike(right)) || - (TypeGuard.TDate(left) && IsObjectDateLike(right)) || - (TypeGuard.TConstructor(left) && IsObjectConstructorLike(right)) || - (TypeGuard.TFunction(left) && IsObjectFunctionLike(right)) - ) ? TypeExtendsResult.True : - (TypeGuard.TRecord(left) && TypeGuard.TString(RecordKey(left))) ? (() => { - // When expressing a Record with literal key values, the Record is converted into a Object with - // the Hint assigned as `Record`. This is used to invert the extends logic. - return right[Hint] === 'Record' ? TypeExtendsResult.True : TypeExtendsResult.False - })() : - (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) ? (() => { - return IsObjectPropertyCount(right, 0) - ? TypeExtendsResult.True - : TypeExtendsResult.False - })() : - TypeExtendsResult.False - ) - } - function TObject(left: TObject, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - !TypeGuard.TObject(right) ? TypeExtendsResult.False : - (() => { - for (const key of Object.getOwnPropertyNames(right.properties)) { - if (!(key in left.properties) && !TypeGuard.TOptional(right.properties[key])) { - return TypeExtendsResult.False - } - if(TypeGuard.TOptional(right.properties[key])) { - return TypeExtendsResult.True - } - if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False - } - } - return TypeExtendsResult.True - })() - ) - } - // -------------------------------------------------------------------------- - // Promise - // -------------------------------------------------------------------------- - function TPromise(left: TPromise, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) && IsObjectPromiseLike(right) ? TypeExtendsResult.True : - !TypeGuard.TPromise(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.item, right.item)) - ) - } - // -------------------------------------------------------------------------- - // Record - // -------------------------------------------------------------------------- - function RecordKey(schema: TRecord) { - // prettier-ignore - return ( - PatternNumberExact in schema.patternProperties ? Type.Number() : - PatternStringExact in schema.patternProperties ? Type.String() : - Throw('Unknown record key pattern') - ) - } - function RecordValue(schema: TRecord) { - // prettier-ignore - return ( - PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : - PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : - Throw('Unable to get record value schema') - ) - } - function TRecordRight(left: TSchema, right: TRecord) { - const [Key, Value] = [RecordKey(right), RecordValue(right)] - // prettier-ignore - return ( - (TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) ? TypeExtendsResult.True : - TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TString(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TArray(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TObject(left) ? (() => { - for (const key of Object.getOwnPropertyNames(left.properties)) { - if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False - } - } - return TypeExtendsResult.True - })() : - TypeExtendsResult.False - ) - } - function TRecord(left: TRecord, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TRecord(right) ? TypeExtendsResult.False : - Visit(RecordValue(left), RecordValue(right)) - ) - } - // -------------------------------------------------------------------------- - // String - // -------------------------------------------------------------------------- - function TStringRight(left: TSchema, right: TString) { - // prettier-ignore - return ( - TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const) ? TypeExtendsResult.True : - TypeGuard.TString(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TString(left: TString, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TString(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Symbol - // -------------------------------------------------------------------------- - function TSymbol(left: TSymbol, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TSymbol(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // TemplateLiteral - // -------------------------------------------------------------------------- - function TTemplateLiteral(left: TSchema, right: TSchema) { - // TemplateLiteral types are resolved to either unions for finite expressions or string - // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for - // either type and continue evaluating. - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(left) ? Visit(TemplateLiteralResolver.Resolve(left), right) : - TypeGuard.TTemplateLiteral(right) ? Visit(left, TemplateLiteralResolver.Resolve(right)) : - Throw('Invalid fallthrough for TemplateLiteral') - ) - } - // -------------------------------------------------------------------------- - // Tuple - // -------------------------------------------------------------------------- - function IsArrayOfTuple(left: TTuple, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TArray(right) && - left.items !== undefined && - left.items.every((schema) => Visit(schema, right.items) === TypeExtendsResult.True) - ) - } - function TTupleRight(left: TSchema, right: TTuple) { - // prettier-ignore - return ( - TypeGuard.TNever(left) ? TypeExtendsResult.True : - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ? TypeExtendsResult.Union : - TypeExtendsResult.False - ) - } - function TTuple(left: TTuple, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : - TypeGuard.TArray(right) && IsArrayOfTuple(left, right) ? TypeExtendsResult.True : - !TypeGuard.TTuple(right) ? TypeExtendsResult.False : - (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.False : - (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.True : - left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Uint8Array - // -------------------------------------------------------------------------- - function TUint8Array(left: TUint8Array, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Undefined - // -------------------------------------------------------------------------- - function TUndefined(left: TUndefined, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TVoid(right) ? VoidRight(left, right) : - TypeGuard.TUndefined(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Union - // -------------------------------------------------------------------------- - function TUnionRight(left: TSchema, right: TUnion): TypeExtendsResult { - // prettier-ignore - return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - function TUnion(left: TUnion, right: TSchema): TypeExtendsResult { - // prettier-ignore - return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - // -------------------------------------------------------------------------- - // Unknown - // -------------------------------------------------------------------------- - function TUnknownRight(left: TSchema, right: TUnknown) { - return TypeExtendsResult.True - } - function TUnknown(left: TUnknown, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TNever(right) ? TNeverRight(left, right) : - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - TypeGuard.TString(right) ? TStringRight(left, right) : - TypeGuard.TNumber(right) ? TNumberRight(left, right) : - TypeGuard.TInteger(right) ? TIntegerRight(left, right) : - TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : - TypeGuard.TArray(right) ? TArrayRight(left, right) : - TypeGuard.TTuple(right) ? TTupleRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TUnknown(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Void - // -------------------------------------------------------------------------- - function VoidRight(left: TSchema, right: TVoid) { - // prettier-ignore - return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : - TypeGuard.TUndefined(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - } - function TVoid(left: TVoid, right: TSchema) { - // prettier-ignore - return TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TVoid(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - } - function Visit(left: TSchema, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - // resolvable - (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) ? TTemplateLiteral(left, right) : - (TypeGuard.TNot(left) || TypeGuard.TNot(right)) ? TNot(left, right) : - // standard - TypeGuard.TAny(left) ? TAny(left, right) : - TypeGuard.TArray(left) ? TArray(left, right) : - TypeGuard.TBigInt(left) ? TBigInt(left, right) : - TypeGuard.TBoolean(left) ? TBoolean(left, right) : - TypeGuard.TAsyncIterator(left) ? TAsyncIterator(left, right) : - TypeGuard.TConstructor(left) ? TConstructor(left, right) : - TypeGuard.TDate(left) ? TDate(left, right) : - TypeGuard.TFunction(left) ? TFunction(left, right) : - TypeGuard.TInteger(left) ? TInteger(left, right) : - TypeGuard.TIntersect(left) ? TIntersect(left, right) : - TypeGuard.TIterator(left) ? TIterator(left, right) : - TypeGuard.TLiteral(left) ? TLiteral(left, right) : - TypeGuard.TNever(left) ? TNever(left, right) : - TypeGuard.TNull(left) ? TNull(left, right) : - TypeGuard.TNumber(left) ? TNumber(left, right) : - TypeGuard.TObject(left) ? TObject(left, right) : - TypeGuard.TRecord(left) ? TRecord(left, right) : - TypeGuard.TString(left) ? TString(left, right) : - TypeGuard.TSymbol(left) ? TSymbol(left, right) : - TypeGuard.TTuple(left) ? TTuple(left, right) : - TypeGuard.TPromise(left) ? TPromise(left, right) : - TypeGuard.TUint8Array(left) ? TUint8Array(left, right) : - TypeGuard.TUndefined(left) ? TUndefined(left, right) : - TypeGuard.TUnion(left) ? TUnion(left, right) : - TypeGuard.TUnknown(left) ? TUnknown(left, right) : - TypeGuard.TVoid(left) ? TVoid(left, right) : - Throw(`Unknown left type operand '${left[Kind]}'`) - ) - } - export function Extends(left: TSchema, right: TSchema): TypeExtendsResult { - return Visit(left, right) - } -} -// -------------------------------------------------------------------------- -// TypeClone -// -------------------------------------------------------------------------- -/** Specialized Clone for Types */ -export namespace TypeClone { - function ArrayType(value: unknown[]) { - return (value as any).map((value: unknown) => Visit(value as any)) - } - function DateType(value: Date) { - return new Date(value.getTime()) - } - function Uint8ArrayType(value: Uint8Array) { - return new Uint8Array(value) - } - function ObjectType(value: Record) { - const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) - const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) - return { ...clonedProperties, ...clonedSymbols } - } - function Visit(value: unknown): any { - // prettier-ignore - return ( - ValueGuard.IsArray(value) ? ArrayType(value) : - ValueGuard.IsDate(value) ? DateType(value) : - ValueGuard.IsUint8Array(value) ? Uint8ArrayType(value) : - ValueGuard.IsObject(value) ? ObjectType(value) : - value - ) - } - /** Clones a Rest */ - export function Rest(schemas: [...T]): T { - return schemas.map((schema) => Type(schema)) as T - } - /** Clones a Type */ - export function Type(schema: T, options: SchemaOptions = {}): T { - return { ...Visit(schema), ...options } - } -} -// -------------------------------------------------------------------------- -// IndexedAccessor -// -------------------------------------------------------------------------- -export namespace IndexedAccessor { - function OptionalUnwrap(schema: TSchema[]): TSchema[] { - return schema.map((schema) => { - const { [Optional]: _, ...clone } = TypeClone.Type(schema) - return clone - }) - } - function IsIntersectOptional(schema: TSchema[]): boolean { - return schema.every((schema) => TypeGuard.TOptional(schema)) - } - function IsUnionOptional(schema: TSchema[]): boolean { - return schema.some((schema) => TypeGuard.TOptional(schema)) - } - function ResolveIntersect(schema: TIntersect): TSchema { - return IsIntersectOptional(schema.allOf) ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema - } - function ResolveUnion(schema: TUnion): TSchema { - return IsUnionOptional(schema.anyOf) ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema - } - function ResolveOptional(schema: TSchema) { - // prettier-ignore - return schema[Kind] === 'Intersect' ? ResolveIntersect(schema as TIntersect) : - schema[Kind] === 'Union' ? ResolveUnion(schema as TUnion) : - schema - } - function TIntersect(schema: TIntersect, key: string): TSchema { - const resolved = schema.allOf.reduce((acc, schema) => { - const indexed = Visit(schema, key) - return indexed[Kind] === 'Never' ? acc : [...acc, indexed] - }, [] as TSchema[]) - return ResolveOptional(Type.Intersect(resolved)) - } - function TUnion(schema: TUnion, key: string): TSchema { - const resolved = schema.anyOf.map((schema) => Visit(schema, key)) - return ResolveOptional(Type.Union(resolved)) - } - function TObject(schema: TObject, key: string): TSchema { - const property = schema.properties[key] - return ValueGuard.IsUndefined(property) ? Type.Never() : Type.Union([property]) - } - function TTuple(schema: TTuple, key: string): TSchema { - const items = schema.items - if (ValueGuard.IsUndefined(items)) return Type.Never() - const element = items[key as any as number] // - if (ValueGuard.IsUndefined(element)) return Type.Never() - return element - } - function Visit(schema: TSchema, key: string): TSchema { - // prettier-ignore - return schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, key) : - schema[Kind] === 'Union' ? TUnion(schema as TUnion, key) : - schema[Kind] === 'Object' ? TObject(schema as TObject, key) : - schema[Kind] === 'Tuple' ? TTuple(schema as TTuple, key) : - Type.Never() - } - export function Resolve(schema: TSchema, keys: TPropertyKey[], options: SchemaOptions = {}): TSchema { - const resolved = keys.map((key) => Visit(schema, key.toString())) - return ResolveOptional(Type.Union(resolved, options)) - } -} -// -------------------------------------------------------------------------- -// Intrinsic -// -------------------------------------------------------------------------- -export namespace Intrinsic { - function Uncapitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return `${first.toLowerCase()}${rest}` - } - function Capitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return `${first.toUpperCase()}${rest}` - } - function Uppercase(value: string): string { - return value.toUpperCase() - } - function Lowercase(value: string): string { - return value.toLowerCase() - } - function IntrinsicTemplateLiteral(schema: TTemplateLiteral, mode: TIntrinsicMode) { - // note: template literals require special runtime handling as they are encoded in string patterns. - // This diverges from the mapped type which would otherwise map on the template literal kind. - const expression = TemplateLiteralParser.ParseExact(schema.pattern) - const finite = TemplateLiteralFinite.Check(expression) - if (!finite) return { ...schema, pattern: IntrinsicLiteral(schema.pattern, mode) } as any - const strings = [...TemplateLiteralGenerator.Generate(expression)] - const literals = strings.map((value) => Type.Literal(value)) - const mapped = IntrinsicRest(literals as any, mode) - const union = Type.Union(mapped) - return Type.TemplateLiteral([union]) - } - function IntrinsicLiteral(value: TLiteralValue, mode: TIntrinsicMode) { - // prettier-ignore - return typeof value === 'string' ? ( - mode === 'Uncapitalize' ? Uncapitalize(value) : - mode === 'Capitalize' ? Capitalize(value) : - mode === 'Uppercase' ? Uppercase(value) : - mode === 'Lowercase' ? Lowercase(value) : - value) : value.toString() - } - function IntrinsicRest(schema: TSchema[], mode: TIntrinsicMode): TSchema[] { - if (schema.length === 0) return [] - const [L, ...R] = schema - return [Map(L, mode), ...IntrinsicRest(R, mode)] - } - function Visit(schema: TSchema, mode: TIntrinsicMode) { - // prettier-ignore - return TypeGuard.TTemplateLiteral(schema) ? IntrinsicTemplateLiteral(schema, mode) : - TypeGuard.TUnion(schema) ? Type.Union(IntrinsicRest(schema.anyOf, mode)) : - TypeGuard.TLiteral(schema) ? Type.Literal(IntrinsicLiteral(schema.const, mode)) : - schema - } - /** Applies an intrinsic string manipulation to the given type. */ - export function Map(schema: T, mode: M): TIntrinsic { - return Visit(schema, mode) - } -} -// -------------------------------------------------------------------------- -// ObjectMap -// -------------------------------------------------------------------------- -export namespace ObjectMap { - function TIntersect(schema: TIntersect, callback: (object: TObject) => TObject) { - // prettier-ignore - return Type.Intersect(schema.allOf.map((inner) => Visit(inner, callback)), { ...schema }) - } - function TUnion(schema: TUnion, callback: (object: TObject) => TObject) { - // prettier-ignore - return Type.Union(schema.anyOf.map((inner) => Visit(inner, callback)), { ...schema }) - } - function TObject(schema: TObject, callback: (object: TObject) => TObject) { - return callback(schema) - } - function Visit(schema: TSchema, callback: (object: TObject) => TObject): TSchema { - // There are cases where users need to map objects with unregistered kinds. Using a TypeGuard here would - // prevent sub schema mapping as unregistered kinds will not pass TSchema checks. This is notable in the - // case of TObject where unregistered property kinds cause the TObject check to fail. As mapping is only - // used for composition, we use explicit checks instead. - // prettier-ignore - return ( - schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, callback) : - schema[Kind] === 'Union' ? TUnion(schema as TUnion, callback) : - schema[Kind] === 'Object' ? TObject(schema as TObject, callback) : - schema - ) - } - export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { - return { ...Visit(TypeClone.Type(schema), callback), ...options } as unknown as T - } -} -// -------------------------------------------------------------------------- -// KeyResolver -// -------------------------------------------------------------------------- -export interface KeyResolverOptions { - includePatterns: boolean -} -export namespace KeyResolver { - function UnwrapPattern(key: string) { - return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key - } - function TIntersect(schema: TIntersect, options: KeyResolverOptions): string[] { - return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) - } - function TUnion(schema: TUnion, options: KeyResolverOptions): string[] { - const sets = schema.anyOf.map((inner) => Visit(inner, options)) - return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] - } - function TObject(schema: TObject, options: KeyResolverOptions): string[] { - return Object.getOwnPropertyNames(schema.properties) - } - function TRecord(schema: TRecord, options: KeyResolverOptions): string[] { - return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] - } - function Visit(schema: TSchema, options: KeyResolverOptions): string[] { - // prettier-ignore - return ( - TypeGuard.TIntersect(schema) ? TIntersect(schema, options) : - TypeGuard.TUnion(schema) ? TUnion(schema, options) : - TypeGuard.TObject(schema) ? TObject(schema, options) : - TypeGuard.TRecord(schema) ? TRecord(schema, options) : - [] - ) - } - /** Resolves an array of keys in this schema */ - export function ResolveKeys(schema: TSchema, options: KeyResolverOptions): string[] { - return [...new Set(Visit(schema, options))] - } - /** Resolves a regular expression pattern matching all keys in this schema */ - export function ResolvePattern(schema: TSchema): string { - const keys = ResolveKeys(schema, { includePatterns: true }) - const pattern = keys.map((key) => `(${UnwrapPattern(key)})`) - return `^(${pattern.join('|')})$` - } -} -// -------------------------------------------------------------------------- -// KeyArrayResolver -// -------------------------------------------------------------------------- -export class KeyArrayResolverError extends TypeBoxError {} -export namespace KeyArrayResolver { - /** Resolves an array of string[] keys from the given schema or array type. */ - export function Resolve(schema: TSchema | string[]): string[] { - // prettier-ignore - return Array.isArray(schema) ? schema : - TypeGuard.TUnionLiteral(schema) ? schema.anyOf.map((schema) => schema.const.toString()) : - TypeGuard.TLiteral(schema) ? [schema.const as string] : - TypeGuard.TTemplateLiteral(schema) ? (() => { - const expression = TemplateLiteralParser.ParseExact(schema.pattern) - if (!TemplateLiteralFinite.Check(expression)) throw new KeyArrayResolverError('Cannot resolve keys from infinite template expression') - return [...TemplateLiteralGenerator.Generate(expression)] - })() : [] - } -} -// -------------------------------------------------------------------------- -// UnionResolver -// -------------------------------------------------------------------------- -export namespace UnionResolver { - function* TUnion(union: TUnion): IterableIterator { - for (const schema of union.anyOf) { - if (schema[Kind] === 'Union') { - yield* TUnion(schema as TUnion) - } else { - yield schema - } - } - } - /** Returns a resolved union with interior unions flattened */ - export function Resolve(union: TUnion): TUnion { - return Type.Union([...TUnion(union)], { ...union }) - } -} -// -------------------------------------------------------------------------- -// TemplateLiteralPattern -// -------------------------------------------------------------------------- -export class TemplateLiteralPatternError extends TypeBoxError {} -export namespace TemplateLiteralPattern { - function Throw(message: string): never { - throw new TemplateLiteralPatternError(message) - } - function Escape(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - } - function Visit(schema: TSchema, acc: string): string { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : - TypeGuard.TUnion(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : - TypeGuard.TNumber(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TInteger(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TBigInt(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TString(schema) ? `${acc}${PatternString}` : - TypeGuard.TLiteral(schema) ? `${acc}${Escape(schema.const.toString())}` : - TypeGuard.TBoolean(schema) ? `${acc}${PatternBoolean}` : - Throw(`Unexpected Kind '${schema[Kind]}'`) - ) - } - export function Create(kinds: TTemplateLiteralKind[]): string { - return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralResolver -// -------------------------------------------------------------------------------------- -export namespace TemplateLiteralResolver { - /** Resolves a template literal as a TUnion */ - export function Resolve(template: TTemplateLiteral): TString | TUnion | TLiteral { - const expression = TemplateLiteralParser.ParseExact(template.pattern) - if (!TemplateLiteralFinite.Check(expression)) return Type.String() - const literals = [...TemplateLiteralGenerator.Generate(expression)].map((value) => Type.Literal(value)) - return Type.Union(literals) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralParser -// -------------------------------------------------------------------------------------- -export class TemplateLiteralParserError extends TypeBoxError {} -export namespace TemplateLiteralParser { - export type Expression = And | Or | Const - export type Const = { type: 'const'; const: string } - export type And = { type: 'and'; expr: Expression[] } - export type Or = { type: 'or'; expr: Expression[] } - function IsNonEscaped(pattern: string, index: number, char: string) { - return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 - } - function IsOpenParen(pattern: string, index: number) { - return IsNonEscaped(pattern, index, '(') - } - function IsCloseParen(pattern: string, index: number) { - return IsNonEscaped(pattern, index, ')') - } - function IsSeparator(pattern: string, index: number) { - return IsNonEscaped(pattern, index, '|') - } - function IsGroup(pattern: string) { - if (!(IsOpenParen(pattern, 0) && IsCloseParen(pattern, pattern.length - 1))) return false - let count = 0 - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (count === 0 && index !== pattern.length - 1) return false - } - return true - } - function InGroup(pattern: string) { - return pattern.slice(1, pattern.length - 1) - } - function IsPrecedenceOr(pattern: string) { - let count = 0 - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (IsSeparator(pattern, index) && count === 0) return true - } - return false - } - function IsPrecedenceAnd(pattern: string) { - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) return true - } - return false - } - function Or(pattern: string): Expression { - let [count, start] = [0, 0] - const expressions: Expression[] = [] - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (IsSeparator(pattern, index) && count === 0) { - const range = pattern.slice(start, index) - if (range.length > 0) expressions.push(Parse(range)) - start = index + 1 - } - } - const range = pattern.slice(start) - if (range.length > 0) expressions.push(Parse(range)) - if (expressions.length === 0) return { type: 'const', const: '' } - if (expressions.length === 1) return expressions[0] - return { type: 'or', expr: expressions } - } - function And(pattern: string): Expression { - function Group(value: string, index: number): [number, number] { - if (!IsOpenParen(value, index)) throw new TemplateLiteralParserError(`TemplateLiteralParser: Index must point to open parens`) - let count = 0 - for (let scan = index; scan < value.length; scan++) { - if (IsOpenParen(value, scan)) count += 1 - if (IsCloseParen(value, scan)) count -= 1 - if (count === 0) return [index, scan] - } - throw new TemplateLiteralParserError(`TemplateLiteralParser: Unclosed group parens in expression`) - } - function Range(pattern: string, index: number): [number, number] { - for (let scan = index; scan < pattern.length; scan++) { - if (IsOpenParen(pattern, scan)) return [index, scan] - } - return [index, pattern.length] - } - const expressions: Expression[] = [] - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) { - const [start, end] = Group(pattern, index) - const range = pattern.slice(start, end + 1) - expressions.push(Parse(range)) - index = end - } else { - const [start, end] = Range(pattern, index) - const range = pattern.slice(start, end) - if (range.length > 0) expressions.push(Parse(range)) - index = end - 1 - } - } - // prettier-ignore - return (expressions.length === 0) ? { type: 'const', const: '' } : - (expressions.length === 1) ? expressions[0] : - { type: 'and', expr: expressions } - } - /** Parses a pattern and returns an expression tree */ - export function Parse(pattern: string): Expression { - // prettier-ignore - return IsGroup(pattern) ? Parse(InGroup(pattern)) : - IsPrecedenceOr(pattern) ? Or(pattern) : - IsPrecedenceAnd(pattern) ? And(pattern) : - { type: 'const', const: pattern } - } - /** Parses a pattern and strips forward and trailing ^ and $ */ - export function ParseExact(pattern: string): Expression { - return Parse(pattern.slice(1, pattern.length - 1)) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralFinite -// -------------------------------------------------------------------------------------- -export class TemplateLiteralFiniteError extends TypeBoxError {} -export namespace TemplateLiteralFinite { - function Throw(message: string): never { - throw new TemplateLiteralFiniteError(message) - } - function IsNumber(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return ( - expression.type === 'or' && - expression.expr.length === 2 && - expression.expr[0].type === 'const' && - expression.expr[0].const === '0' && - expression.expr[1].type === 'const' && - expression.expr[1].const === '[1-9][0-9]*' - ) - } - function IsBoolean(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return ( - expression.type === 'or' && - expression.expr.length === 2 && - expression.expr[0].type === 'const' && - expression.expr[0].const === 'true' && - expression.expr[1].type === 'const' && - expression.expr[1].const === 'false' - ) - } - function IsString(expression: TemplateLiteralParser.Expression) { - return expression.type === 'const' && expression.const === '.*' - } - export function Check(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return IsBoolean(expression) ? true : - IsNumber(expression) || IsString(expression) ? false : - (expression.type === 'and') ? expression.expr.every((expr) => Check(expr)) : - (expression.type === 'or') ? expression.expr.every((expr) => Check(expr)) : - (expression.type === 'const') ? true : - Throw(`Unknown expression type`) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralGenerator -// -------------------------------------------------------------------------------------- -export class TemplateLiteralGeneratorError extends TypeBoxError {} -export namespace TemplateLiteralGenerator { - function* Reduce(buffer: string[][]): IterableIterator { - if (buffer.length === 1) return yield* buffer[0] - for (const left of buffer[0]) { - for (const right of Reduce(buffer.slice(1))) { - yield `${left}${right}` - } - } - } - function* And(expression: TemplateLiteralParser.And): IterableIterator { - return yield* Reduce(expression.expr.map((expr) => [...Generate(expr)])) - } - function* Or(expression: TemplateLiteralParser.Or): IterableIterator { - for (const expr of expression.expr) yield* Generate(expr) - } - function* Const(expression: TemplateLiteralParser.Const): IterableIterator { - return yield expression.const - } - export function* Generate(expression: TemplateLiteralParser.Expression): IterableIterator { - // prettier-ignore - return ( - expression.type === 'and' ? yield* And(expression) : - expression.type === 'or' ? yield* Or(expression) : - expression.type === 'const' ? yield* Const(expression) : - (() => { throw new TemplateLiteralGeneratorError('Unknown expression') })() - ) - } -} -// --------------------------------------------------------------------- -// TemplateLiteralDslParser -// --------------------------------------------------------------------- -export namespace TemplateLiteralDslParser { - function* ParseUnion(template: string): IterableIterator { - const trim = template.trim().replace(/"|'/g, '') - // prettier-ignore - return ( - trim === 'boolean' ? yield Type.Boolean() : - trim === 'number' ? yield Type.Number() : - trim === 'bigint' ? yield Type.BigInt() : - trim === 'string' ? yield Type.String() : - yield (() => { - const literals = trim.split('|').map((literal) => Type.Literal(literal.trim())) - return ( - literals.length === 0 ? Type.Never() : - literals.length === 1 ? literals[0] : - Type.Union(literals) - ) - })() - ) - } - function* ParseTerminal(template: string): IterableIterator { - if (template[1] !== '{') { - const L = Type.Literal('$') - const R = ParseLiteral(template.slice(1)) - return yield* [L, ...R] - } - for (let i = 2; i < template.length; i++) { - if (template[i] === '}') { - const L = ParseUnion(template.slice(2, i)) - const R = ParseLiteral(template.slice(i + 1)) - return yield* [...L, ...R] - } - } - yield Type.Literal(template) - } - function* ParseLiteral(template: string): IterableIterator { - for (let i = 0; i < template.length; i++) { - if (template[i] === '$') { - const L = Type.Literal(template.slice(0, i)) - const R = ParseTerminal(template.slice(i)) - return yield* [L, ...R] - } - } - yield Type.Literal(template) - } - export function Parse(template_dsl: string): TTemplateLiteralKind[] { - return [...ParseLiteral(template_dsl)] - } -} -// --------------------------------------------------------------------- -// TransformBuilder -// --------------------------------------------------------------------- -export class TransformDecodeBuilder { - constructor(private readonly schema: T) {} - public Decode, U>>(decode: D): TransformEncodeBuilder { - return new TransformEncodeBuilder(this.schema, decode) - } -} -export class TransformEncodeBuilder { - constructor(private readonly schema: T, private readonly decode: D) {} - public Encode, StaticDecode>>(encode: E): TTransform> { - const schema = TypeClone.Type(this.schema) - // prettier-ignore - return ( - TypeGuard.TTransform(schema) ? (() => { - const Encode = (value: unknown) => schema[Transform].Encode(encode(value as any)) - const Decode = (value: unknown) => this.decode(schema[Transform].Decode(value)) - const Codec = { Encode: Encode, Decode: Decode } - return { ...schema, [Transform]: Codec } - })() : (() => { - const Codec = { Decode: this.decode, Encode: encode } - return { ...schema, [Transform]: Codec } - })() - ) as TTransform> - } -} -// -------------------------------------------------------------------------- -// TypeOrdinal: Used for auto $id generation -// -------------------------------------------------------------------------- -let TypeOrdinal = 0 -// -------------------------------------------------------------------------- -// TypeBuilder -// -------------------------------------------------------------------------- -export class TypeBuilderError extends TypeBoxError {} -export class TypeBuilder { - /** `[Internal]` Creates a schema without `static` and `params` types */ - protected Create(schema: Omit): T { - return schema as any - } - /** `[Internal]` Throws a TypeBuilder error with the given message */ - protected Throw(message: string): never { - throw new TypeBuilderError(message) - } - /** `[Internal]` Discards property keys from the given record type */ - protected Discard(record: Record, keys: PropertyKey[]) { - return keys.reduce((acc, key) => { - const { [key as any]: _, ...rest } = acc - return rest - }, record) as any - } - /** `[Json]` Omits compositing symbols from this schema */ - public Strict(schema: T): T { - return JSON.parse(JSON.stringify(schema)) - } -} -// -------------------------------------------------------------------------- -// JsonTypeBuilder -// -------------------------------------------------------------------------- -export class JsonTypeBuilder extends TypeBuilder { - // ------------------------------------------------------------------------ - // Modifiers - // ------------------------------------------------------------------------ - /** `[Json]` Creates a Readonly and Optional property */ - public ReadonlyOptional(schema: T): TReadonly> { - return this.Readonly(this.Optional(schema)) - } - /** `[Json]` Creates a Readonly property */ - public Readonly(schema: T): TReadonly { - return { ...TypeClone.Type(schema), [Readonly]: 'Readonly' } - } - /** `[Json]` Creates an Optional property */ - public Optional(schema: T): TOptional { - return { ...TypeClone.Type(schema), [Optional]: 'Optional' } - } - // ------------------------------------------------------------------------ - // Types - // ------------------------------------------------------------------------ - /** `[Json]` Creates an Any type */ - public Any(options: SchemaOptions = {}): TAny { - return this.Create({ ...options, [Kind]: 'Any' }) - } - /** `[Json]` Creates an Array type */ - public Array(schema: T, options: ArrayOptions = {}): TArray { - return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Type(schema) }) - } - /** `[Json]` Creates a Boolean type */ - public Boolean(options: SchemaOptions = {}): TBoolean { - return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) - } - /** `[Json]` Intrinsic function to Capitalize LiteralString types */ - public Capitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Capitalize'), ...options } - } - /** `[Json]` Creates a Composite object type */ - public Composite(objects: [...T], options?: ObjectOptions): TComposite { - const intersect: any = Type.Intersect(objects, {}) - const keys = KeyResolver.ResolveKeys(intersect, { includePatterns: false }) - const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Type.Index(intersect, [key]) }), {} as TProperties) - return Type.Object(properties, options) as TComposite - } - /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { - if (ValueGuard.IsUndefined(item)) return this.Throw('Enum undefined or empty') - // prettier-ignore - const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const values2 = [...new Set(values1)] - const anyOf = values2.map((value) => Type.Literal(value)) - return this.Union(anyOf, { ...options, [Hint]: 'Enum' }) as TEnum - } - /** `[Json]` Creates a Conditional type */ - public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { - switch (TypeExtends.Extends(left, right)) { - case TypeExtendsResult.Union: - return this.Union([TypeClone.Type(trueType, options), TypeClone.Type(falseType, options)]) as any as TExtends - case TypeExtendsResult.True: - return TypeClone.Type(trueType, options) as unknown as TExtends - case TypeExtendsResult.False: - return TypeClone.Type(falseType, options) as unknown as TExtends - } - } - /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ - public Exclude(unionType: L, excludedMembers: R, options: SchemaOptions = {}): TExclude { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(unionType) ? this.Exclude(TemplateLiteralResolver.Resolve(unionType), excludedMembers, options) : - TypeGuard.TTemplateLiteral(excludedMembers) ? this.Exclude(unionType, TemplateLiteralResolver.Resolve(excludedMembers), options) : - TypeGuard.TUnion(unionType) ? (() => { - const narrowed = unionType.anyOf.filter((inner) => TypeExtends.Extends(inner, excludedMembers) === TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) as TExclude - })() : - TypeExtends.Extends(unionType, excludedMembers) !== TypeExtendsResult.False ? this.Never(options) : - TypeClone.Type(unionType, options) - ) as TExclude - } - /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ - public Extract(type: L, union: R, options: SchemaOptions = {}): TExtract { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(type) ? this.Extract(TemplateLiteralResolver.Resolve(type), union, options) : - TypeGuard.TTemplateLiteral(union) ? this.Extract(type, TemplateLiteralResolver.Resolve(union), options) : - TypeGuard.TUnion(type) ? (() => { - const narrowed = type.anyOf.filter((inner) => TypeExtends.Extends(inner, union) !== TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) - })() : - TypeExtends.Extends(type, union) !== TypeExtendsResult.False ? TypeClone.Type(type, options) : - this.Never(options) - ) as TExtract - } - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): AssertType - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): TSchema - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - // prettier-ignore - return ( - TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved) ? (() => { - return TypeClone.Type(schema.items, options) - })() : - TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved) ? (() => { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const cloned = items.map((schema) => TypeClone.Type(schema)) - return this.Union(cloned, options) - })() : (() => { - const keys = KeyArrayResolver.Resolve(unresolved) - const clone = TypeClone.Type(schema) - return IndexedAccessor.Resolve(clone, keys, options) - })() - ) - } - /** `[Json]` Creates an Integer type */ - public Integer(options: NumericOptions = {}): TInteger { - return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) - } - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [], options?: SchemaOptions): TNever - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [...T], options?: SchemaOptions): T[0] - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [...T], options?: IntersectOptions): TIntersect - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: TSchema[], options: IntersectOptions = {}) { - if (allOf.length === 0) return Type.Never() - if (allOf.length === 1) return TypeClone.Type(allOf[0], options) - if (allOf.some((schema) => TypeGuard.TTransform(schema))) this.Throw('Cannot intersect transform types') - const objects = allOf.every((schema) => TypeGuard.TObject(schema)) - const cloned = TypeClone.Rest(allOf) - // prettier-ignore - const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) - ? { unevaluatedProperties: TypeClone.Type(options.unevaluatedProperties) } - : {} - return options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects - ? this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) - : this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: cloned }) - } - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { - // prettier-ignore - return ( - TypeGuard.TRecord(schema) ? (() => { - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - return ( - pattern === PatternNumberExact ? this.Number(options) : - pattern === PatternStringExact ? this.String(options) : - this.Throw('Unable to resolve key type from Record key pattern') - ) - })() : - TypeGuard.TTuple(schema) ? (() => { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const literals = items.map((_, index) => Type.Literal(index.toString())) - return this.Union(literals, options) - })() : - TypeGuard.TArray(schema) ? (() => { - return this.Number(options) - })() : (() => { - const keys = KeyResolver.ResolveKeys(schema, { includePatterns: false }) - if (keys.length === 0) return this.Never(options) as TKeyOf - const literals = keys.map((key) => this.Literal(key)) - return this.Union(literals, options) - })() - ) as unknown as TKeyOf - } - /** `[Json]` Creates a Literal type */ - public Literal(value: T, options: SchemaOptions = {}): TLiteral { - return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) - } - /** `[Json]` Intrinsic function to Lowercase LiteralString types */ - public Lowercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Lowercase'), ...options } - } - /** `[Json]` Creates a Never type */ - public Never(options: SchemaOptions = {}): TNever { - return this.Create({ ...options, [Kind]: 'Never', not: {} }) - } - /** `[Json]` Creates a Not type */ - public Not(schema: T, options?: SchemaOptions): TNot { - return this.Create({ ...options, [Kind]: 'Not', not: TypeClone.Type(schema) }) - } - /** `[Json]` Creates a Null type */ - public Null(options: SchemaOptions = {}): TNull { - return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) - } - /** `[Json]` Creates a Number type */ - public Number(options: NumericOptions = {}): TNumber { - return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) - } - /** `[Json]` Creates an Object type */ - public Object(properties: T, options: ObjectOptions = {}): TObject { - const propertyKeys = Object.getOwnPropertyNames(properties) - const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key])) - const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) - const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Type(options.additionalProperties) } : {} - const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(properties[key]) }), {} as TProperties) - return requiredKeys.length > 0 - ? this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) - : this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) - } - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - const keys = KeyArrayResolver.Resolve(unresolved) - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - if (ValueGuard.IsArray(object.required)) { - object.required = object.required.filter((key: string) => !keys.includes(key as any)) - if (object.required.length === 0) delete object.required - } - for (const key of Object.getOwnPropertyNames(object.properties)) { - if (keys.includes(key as any)) delete object.properties[key] - } - return this.Create(object) - }, options) - } - /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: T, options: ObjectOptions = {}): TPartial { - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Optional(object.properties[key]) } - }, {} as TProperties) - return this.Object(properties, this.Discard(object, ['required']) /* object used as options to retain other constraints */) - }, options) - } - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick>(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - const keys = KeyArrayResolver.Resolve(unresolved) - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - if (ValueGuard.IsArray(object.required)) { - object.required = object.required.filter((key: any) => keys.includes(key)) - if (object.required.length === 0) delete object.required - } - for (const key of Object.getOwnPropertyNames(object.properties)) { - if (!keys.includes(key as any)) delete object.properties[key] - } - return this.Create(object) - }, options) - } - /** `[Json]` Creates a Record type */ - public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordResolve { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(key) ? (() => { - const expression = TemplateLiteralParser.ParseExact(key.pattern) - // prettier-ignore - return TemplateLiteralFinite.Check(expression) - ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(schema) }), {} as TProperties), options)) - : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Type(schema) }}) - })() : - TypeGuard.TUnion(key) ? (() => { - const union = UnionResolver.Resolve(key) - if (TypeGuard.TUnionLiteral(union)) { - const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Type(schema) }), {} as TProperties) - return this.Object(properties, { ...options, [Hint]: 'Record' }) - } else this.Throw('Record key of type union contains non-literal types') - })() : - TypeGuard.TLiteral(key) ? (() => { - // prettier-ignore - return (ValueGuard.IsString(key.const) || ValueGuard.IsNumber(key.const)) - ? this.Object({ [key.const]: TypeClone.Type(schema) }, options) - : this.Throw('Record key of type literal is not of type string or number') - })() : - TypeGuard.TInteger(key) || TypeGuard.TNumber(key) ? (() => { - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [PatternNumberExact]: TypeClone.Type(schema) } }) - })() : - TypeGuard.TString(key) ? (() => { - const pattern = ValueGuard.IsUndefined(key.pattern) ? PatternStringExact : key.pattern - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Type(schema) } }) - })() : - this.Never() - ) - } - /** `[Json]` Creates a Recursive type */ - public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { - if (ValueGuard.IsUndefined(options.$id)) (options as any).$id = `T${TypeOrdinal++}` - const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) - thisType.$id = options.$id - return this.Create({ ...options, [Hint]: 'Recursive', ...thisType } as any) - } - /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ - public Ref(schema: T, options?: SchemaOptions): TRef - /** `[Json]` Creates a Ref type. */ - public Ref($ref: string, options?: SchemaOptions): TRef - /** `[Json]` Creates a Ref type. */ - public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { - if (ValueGuard.IsString(unresolved)) return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved }) - if (ValueGuard.IsUndefined(unresolved.$id)) this.Throw('Reference target type must specify an $id') - return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved.$id! }) - } - /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: T, options: SchemaOptions = {}): TRequired { - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Discard(object.properties[key], [Optional]) as TSchema } - }, {} as TProperties) - return this.Object(properties, object /* object used as options to retain other constraints */) - }, options) - } - /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ - public Rest(schema: T): TRest { - return ( - TypeGuard.TTuple(schema) && !ValueGuard.IsUndefined(schema.items) ? TypeClone.Rest(schema.items) : TypeGuard.TIntersect(schema) ? TypeClone.Rest(schema.allOf) : TypeGuard.TUnion(schema) ? TypeClone.Rest(schema.anyOf) : [] - ) as TRest - } - /** `[Json]` Creates a String type */ - public String(options: StringOptions = {}): TString { - return this.Create({ ...options, [Kind]: 'String', type: 'string' }) - } - /** `[Json]` Creates a TemplateLiteral type from template dsl string */ - public TemplateLiteral(templateDsl: T, options?: SchemaOptions): TTemplateLiteralDslParser - /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral - /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(unresolved: unknown, options: SchemaOptions = {}) { - // prettier-ignore - const pattern = ValueGuard.IsString(unresolved) - ? TemplateLiteralPattern.Create(TemplateLiteralDslParser.Parse(unresolved)) - : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) - return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) - } - /** `[Json]` Creates a Transform type */ - public Transform(schema: I): TransformDecodeBuilder { - return new TransformDecodeBuilder(schema) - } - /** `[Json]` Creates a Tuple type */ - public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { - const [additionalItems, minItems, maxItems] = [false, items.length, items.length] - const clonedItems = TypeClone.Rest(items) - // prettier-ignore - const schema = (items.length > 0 ? - { ...options, [Kind]: 'Tuple', type: 'array', items: clonedItems, additionalItems, minItems, maxItems } : - { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any - return this.Create(schema) - } - /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ - public Uncapitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uncapitalize'), ...options } - } - /** `[Json]` Creates a Union type */ - public Union(anyOf: [], options?: SchemaOptions): TNever - /** `[Json]` Creates a Union type */ - public Union(anyOf: [...T], options?: SchemaOptions): T[0] - /** `[Json]` Creates a Union type */ - public Union(anyOf: [...T], options?: SchemaOptions): TUnion - /** `[Json-Experimental]` Converts a TemplateLiteral into a Union */ - public Union(template: T): TUnionTemplateLiteral - /** `[Json]` Creates a Union type */ - public Union(union: TSchema[] | TTemplateLiteral, options: SchemaOptions = {}) { - // prettier-ignore - return TypeGuard.TTemplateLiteral(union) - ? TemplateLiteralResolver.Resolve(union) - : (() => { - const anyOf = union - if (anyOf.length === 0) return this.Never(options) - if (anyOf.length === 1) return this.Create(TypeClone.Type(anyOf[0], options)) - const clonedAnyOf = TypeClone.Rest(anyOf) - return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) - })() - } - /** `[Json]` Creates an Unknown type */ - public Unknown(options: SchemaOptions = {}): TUnknown { - return this.Create({ ...options, [Kind]: 'Unknown' }) - } - /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ - public Unsafe(options: UnsafeOptions = {}): TUnsafe { - return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) - } - /** `[Json]` Intrinsic function to Uppercase LiteralString types */ - public Uppercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uppercase'), ...options } - } -} -// -------------------------------------------------------------------------- -// JavaScriptTypeBuilder -// -------------------------------------------------------------------------- -export class JavaScriptTypeBuilder extends JsonTypeBuilder { - /** `[JavaScript]` Creates a AsyncIterator type */ - public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { - return this.Create({ ...options, [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: TypeClone.Type(items) }) - } - /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ - public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { - // prettier-ignore - const Unwrap = (rest: TSchema[]): TSchema[] => rest.length > 0 ? (() => { - const [L, ...R] = rest - return [this.Awaited(L), ...Unwrap(R)] - })() : rest - // prettier-ignore - return ( - TypeGuard.TIntersect(schema) ? Type.Intersect(Unwrap(schema.allOf)) : - TypeGuard.TUnion(schema) ? Type.Union(Unwrap(schema.anyOf)) : - TypeGuard.TPromise(schema) ? this.Awaited(schema.item) : - TypeClone.Type(schema, options) - ) as TAwaited - } - /** `[JavaScript]` Creates a BigInt type */ - public BigInt(options: NumericOptions = {}): TBigInt { - return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) - } - /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { - return this.Tuple([...schema.parameters], { ...options }) - } - /** `[JavaScript]` Creates a Constructor type */ - public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Constructor', type: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) - } - /** `[JavaScript]` Creates a Date type */ - public Date(options: DateOptions = {}): TDate { - return this.Create({ ...options, [Kind]: 'Date', type: 'Date' }) - } - /** `[JavaScript]` Creates a Function type */ - public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { - const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Function', type: 'Function', parameters: clonedParameters, returns: clonedReturns }) - } - /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ - public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { - return TypeClone.Type(schema.returns, options) - } - /** `[JavaScript]` Creates an Iterator type */ - public Iterator(items: T, options: SchemaOptions = {}): TIterator { - return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Type(items) }) - } - /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { - return this.Tuple(schema.parameters, { ...options }) - } - /** `[JavaScript]` Creates a Promise type */ - public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'Promise', item: TypeClone.Type(item) }) - } - /** `[JavaScript]` Creates a String type from a Regular Expression pattern */ - public RegExp(pattern: string, options?: SchemaOptions): TString - /** `[JavaScript]` Creates a String type from a Regular Expression */ - public RegExp(regex: RegExp, options?: SchemaOptions): TString - /** `[Extended]` Creates a String type */ - public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { - const pattern = ValueGuard.IsString(unresolved) ? unresolved : unresolved.source - return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern }) - } - /** - * @deprecated Use `Type.RegExp` - */ - public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { - return this.RegExp(regex, options) - } - /** `[JavaScript]` Extracts the ReturnType from the given Function type */ - public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { - return TypeClone.Type(schema.returns, options) - } - /** `[JavaScript]` Creates a Symbol type */ - public Symbol(options?: SchemaOptions): TSymbol { - return this.Create({ ...options, [Kind]: 'Symbol', type: 'symbol' }) - } - /** `[JavaScript]` Creates a Undefined type */ - public Undefined(options: SchemaOptions = {}): TUndefined { - return this.Create({ ...options, [Kind]: 'Undefined', type: 'undefined' }) - } - /** `[JavaScript]` Creates a Uint8Array type */ - public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' }) - } - /** `[JavaScript]` Creates a Void type */ - public Void(options: SchemaOptions = {}): TVoid { - return this.Create({ ...options, [Kind]: 'Void', type: 'void' }) - } -} -/** Json Type Builder with Static Resolution for TypeScript */ -export const JsonType = new JsonTypeBuilder() -/** JavaScript Type Builder with Static Resolution for TypeScript */ -export const Type = new JavaScriptTypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast/cast.ts similarity index 51% rename from src/value/cast.ts rename to src/value/cast/cast.ts index 16e3ec9cf..8ac9e79df 100644 --- a/src/value/cast.ts +++ b/src/value/cast/cast.ts @@ -26,93 +26,92 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from './guard' -import { Create } from './create' -import { Check } from './check' -import { Clone } from './clone' -import { Deref } from './deref' -import * as Types from '../typebox' +import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index' +import { TypeBoxError } from '../../type/error/index' +import { Kind } from '../../type/symbols/index' +import { Create } from '../create/index' +import { Check } from '../check/index' +import { Clone } from '../clone/index' +import { Deref } from '../deref/index' -// -------------------------------------------------------------------------- +import type { TSchema } from '../../type/schema/index' +import type { Static } from '../../type/static/index' +import type { TArray } from '../../type/array/index' +import type { TConstructor } from '../../type/constructor/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' +import type { TNever } from '../../type/never/index' + +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueCastArrayUniqueItemsTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown) { - super('Array cast produced invalid data due to uniqueItems constraint') - } -} -export class ValueCastNeverTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Never types cannot be cast') - } -} -export class ValueCastRecursiveTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Cannot cast recursive schemas') +// ------------------------------------------------------------------ +export class ValueCastError extends TypeBoxError { + constructor(public readonly schema: TSchema, message: string) { + super(message) } } -export class ValueCastUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Unknown type') +// ------------------------------------------------------------------ +// The following will score a schema against a value. For objects, +// the score is the tally of points awarded for each property of +// the value. Property points are (1.0 / propertyCount) to prevent +// large property counts biasing results. Properties that match +// literal values are maximally awarded as literals are typically +// used as union discriminator fields. +// ------------------------------------------------------------------ +function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number { + if (schema[Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) { + const object = schema as TObject + const keys = Object.getOwnPropertyNames(value) + const entries = Object.entries(object.properties) + const [point, max] = [1 / entries.length, entries.length] + return entries.reduce((acc, [key, schema]) => { + const literal = schema[Kind] === 'Literal' && schema.const === value[key] ? max : 0 + const checks = Check(schema, references, value[key]) ? point : 0 + const exists = keys.includes(key) ? point : 0 + return acc + (literal + checks + exists) + }, 0) + } else { + return Check(schema, references, value) ? 1 : 0 } } -// -------------------------------------------------------------------------- -// The following will score a schema against a value. For objects, the score -// is the tally of points awarded for each property of the value. Property -// points are (1.0 / propertyCount) to prevent large property counts biasing -// results. Properties that match literal values are maximally awarded as -// literals are typically used as union discriminator fields. -// -------------------------------------------------------------------------- -namespace UnionCastCreate { - function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number { - if (schema[Types.Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) { - const object = schema as Types.TObject - const keys = Object.getOwnPropertyNames(value) - const entries = Object.entries(object.properties) - const [point, max] = [1 / entries.length, entries.length] - return entries.reduce((acc, [key, schema]) => { - const literal = schema[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0 - const checks = Check(schema, references, value[key]) ? point : 0 - const exists = keys.includes(key) ? point : 0 - return acc + (literal + checks + exists) - }, 0) - } else { - return Check(schema, references, value) ? 1 : 0 +function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema { + let [select, best] = [union.anyOf[0], 0] + for (const schema of union.anyOf) { + const score = ScoreUnion(schema, references, value) + if (score > best) { + select = schema + best = score } } - function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { - let [select, best] = [union.anyOf[0], 0] - for (const schema of union.anyOf) { - const score = Score(schema, references, value) - if (score > best) { - select = schema - best = score - } - } - return select - } - export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { - if ('default' in union) { - return union.default - } else { - const schema = Select(union, references, value) - return Cast(schema, references, value) - } + return select +} +function CastUnion(union: TUnion, references: TSchema[], value: any) { + if ('default' in union) { + return union.default + } else { + const schema = SelectUnion(union, references, value) + return Cast(schema, references, value) } } -// -------------------------------------------------------------------------- + +// ------------------------------------------------------------------ // Default -// -------------------------------------------------------------------------- -export function DefaultClone(schema: Types.TSchema, references: Types.TSchema[], value: any): any { +// ------------------------------------------------------------------ +function DefaultClone(schema: TSchema, references: TSchema[], value: any): any { return Check(schema, references, value) ? Clone(value) : Create(schema, references) } -export function Default(schema: Types.TSchema, references: Types.TSchema[], value: any): any { +function Default(schema: TSchema, references: TSchema[], value: any): any { return Check(schema, references, value) ? value : Create(schema, references) } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Cast -// -------------------------------------------------------------------------- -function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { +// ------------------------------------------------------------------ +function FromArray(schema: TArray, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Clone(value) const created = IsArray(value) ? Clone(value) : Create(schema, references) const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created @@ -120,28 +119,28 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): const casted = maximum.map((value: unknown) => Visit(schema.items, references, value)) if (schema.uniqueItems !== true) return casted const unique = [...new Set(casted)] - if (!Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) + if (!Check(schema, references, unique)) throw new ValueCastError(schema, 'Array cast produced invalid data due to uniqueItems constraint') return unique } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { +function FromConstructor(schema: TConstructor, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Create(schema, references) const required = new Set(schema.returns.required || []) const result = function () {} for (const [key, property] of Object.entries(schema.returns.properties)) { if (!required.has(key) && value.prototype[key] === undefined) continue - result.prototype[key] = Visit(property as Types.TSchema, references, value.prototype[key]) + result.prototype[key] = Visit(property as TSchema, references, value.prototype[key]) } return result } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any { +function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { const created = Create(schema, references) const mapped = IsPlainObject(created) && IsPlainObject(value) ? { ...(created as any), ...value } : value return Check(schema, references, mapped) ? mapped : Create(schema, references) } -function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): any { - throw new ValueCastNeverTypeError(schema) +function FromNever(schema: TNever, references: TSchema[], value: any): any { + throw new ValueCastError(schema, 'Never types cannot be cast') } -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): any { +function FromObject(schema: TObject, references: TSchema[], value: any): any { if (Check(schema, references, value)) return value if (value === null || typeof value !== 'object') return Create(schema, references) const required = new Set(schema.required || []) @@ -160,7 +159,7 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) } return result } -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): any { +function FromRecord(schema: TRecord, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Clone(value) if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return Create(schema, references) const subschemaPropertyName = Object.getOwnPropertyNames(schema.patternProperties)[0] @@ -171,88 +170,69 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v } return result } -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): any { +function FromRef(schema: TRef, references: TSchema[], value: any): any { return Visit(Deref(schema, references), references, value) } -function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): any { +function FromThis(schema: TThis, references: TSchema[], value: any): any { return Visit(Deref(schema, references), references, value) } -function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): any { +function FromTuple(schema: TTuple, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Clone(value) if (!IsArray(value)) return Create(schema, references) if (schema.items === undefined) return [] return schema.items.map((schema, index) => Visit(schema, references, value[index])) } -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): any { - return Check(schema, references, value) ? Clone(value) : UnionCastCreate.Create(schema, references, value) +function FromUnion(schema: TUnion, references: TSchema[], value: any): any { + return Check(schema, references, value) ? Clone(value) : CastUnion(schema, references, value) } -function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { +function Visit(schema: TSchema, references: TSchema[], value: any): any { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - switch (schema[Types.Kind]) { - // ------------------------------------------------------ + switch (schema[Kind]) { + // -------------------------------------------------------------- // Structural - // ------------------------------------------------------ + // -------------------------------------------------------------- case 'Array': - return TArray(schema_, references_, value) + return FromArray(schema_, references_, value) case 'Constructor': - return TConstructor(schema_, references_, value) + return FromConstructor(schema_, references_, value) case 'Intersect': - return TIntersect(schema_, references_, value) + return FromIntersect(schema_, references_, value) case 'Never': - return TNever(schema_, references_, value) + return FromNever(schema_, references_, value) case 'Object': - return TObject(schema_, references_, value) + return FromObject(schema_, references_, value) case 'Record': - return TRecord(schema_, references_, value) + return FromRecord(schema_, references_, value) case 'Ref': - return TRef(schema_, references_, value) + return FromRef(schema_, references_, value) case 'This': - return TThis(schema_, references_, value) + return FromThis(schema_, references_, value) case 'Tuple': - return TTuple(schema_, references_, value) + return FromTuple(schema_, references_, value) case 'Union': - return TUnion(schema_, references_, value) - // ------------------------------------------------------ + return FromUnion(schema_, references_, value) + // -------------------------------------------------------------- // DefaultClone - // ------------------------------------------------------ + // -------------------------------------------------------------- case 'Date': case 'Symbol': case 'Uint8Array': return DefaultClone(schema, references, value) - // ------------------------------------------------------ + // -------------------------------------------------------------- // Default - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'BigInt': - case 'Boolean': - case 'Function': - case 'Integer': - case 'Iterator': - case 'Literal': - case 'Not': - case 'Null': - case 'Number': - case 'Promise': - case 'String': - case 'TemplateLiteral': - case 'Undefined': - case 'Unknown': - case 'Void': - return Default(schema_, references_, value) + // -------------------------------------------------------------- default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) return Default(schema_, references_, value) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Cast -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Casts a value into a given type and references. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static +export function Cast(schema: T, references: TSchema[], value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, value: unknown): Types.Static +export function Cast(schema: T, value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/cast/index.ts b/src/value/cast/index.ts new file mode 100644 index 000000000..5d403f74a --- /dev/null +++ b/src/value/cast/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './cast' diff --git a/src/value/check.ts b/src/value/check/check.ts similarity index 58% rename from src/value/check.ts rename to src/value/check/check.ts index 7e987708c..c2cd892db 100644 --- a/src/value/check.ts +++ b/src/value/check/check.ts @@ -26,39 +26,84 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from './guard' -import { TypeSystemPolicy } from '../system/index' -import { Deref } from './deref' -import { Hash } from './hash' -import * as Types from '../typebox' +import { TypeSystemPolicy } from '../../system/index' +import { Deref } from '../deref/index' +import { Hash } from '../hash/index' +import { Kind } from '../../type/symbols/index' +import { KeyOfPattern } from '../../type/keyof/index' +import { ExtendsUndefinedCheck } from '../../type/extends/index' +import { TypeRegistry, FormatRegistry } from '../../type/registry/index' +import { TypeBoxError } from '../../type/error/index' -// -------------------------------------------------------------------------- +import type { TSchema } from '../../type/schema/index' +import type { TAsyncIterator } from '../../type/async-iterator/index' +import type { TAny } from '../../type/any/index' +import type { TArray } from '../../type/array/index' +import type { TBigInt } from '../../type/bigint/index' +import type { TBoolean } from '../../type/boolean/index' +import type { TDate } from '../../type/date/index' +import type { TConstructor } from '../../type/constructor/index' +import type { TFunction } from '../../type/function/index' +import type { TInteger } from '../../type/integer/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TIterator } from '../../type/iterator/index' +import type { TLiteral } from '../../type/literal/index' +import { Never, type TNever } from '../../type/never/index' +import type { TNot } from '../../type/not/index' +import type { TNull } from '../../type/null/index' +import type { TNumber } from '../../type/number/index' +import type { TObject } from '../../type/object/index' +import type { TPromise } from '../../type/promise/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TRegExp } from '../../type/regexp/index' +import type { TTemplateLiteral } from '../../type/template-literal/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' +import type { TUnknown } from '../../type/unknown/index' +import type { Static } from '../../type/static/index' +import type { TString } from '../../type/string/index' +import type { TSymbol } from '../../type/symbol/index' +import type { TUndefined } from '../../type/undefined/index' +import type { TUint8Array } from '../../type/uint8array/index' +import type { TVoid } from '../../type/void/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsSchema } from '../../type/guard/type' +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueCheckUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +// ------------------------------------------------------------------ +export class ValueCheckUnknownTypeError extends TypeBoxError { + constructor(public readonly schema: TSchema) { super(`Unknown type`) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeGuards -// -------------------------------------------------------------------------- -function IsAnyOrUnknown(schema: Types.TSchema) { - return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' +// ------------------------------------------------------------------ +function IsAnyOrUnknown(schema: TSchema) { + return schema[Kind] === 'Any' || schema[Kind] === 'Unknown' } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Guards -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsDefined(value: unknown): value is T { return value !== undefined } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Types -// -------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { +// ------------------------------------------------------------------ +function FromAny(schema: TAny, references: TSchema[], value: any): boolean { return true } -function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { +function FromArray(schema: TArray, references: TSchema[], value: any): boolean { if (!IsArray(value)) return false if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { return false @@ -77,7 +122,7 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): if (!(IsDefined(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains))) { return true // exit } - const containsSchema = IsDefined(schema.contains) ? schema.contains : Types.Type.Never() + const containsSchema = IsDefined(schema.contains) ? schema.contains : Never() const containsCount = value.reduce((acc: number, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0) if (containsCount === 0) { return false @@ -90,10 +135,10 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): } return true } -function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: any): boolean { +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: any): boolean { return IsAsyncIterator(value) } -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { +function FromBigInt(schema: TBigInt, references: TSchema[], value: any): boolean { if (!IsBigInt(value)) return false if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false @@ -112,13 +157,13 @@ function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any) } return true } -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): boolean { +function FromBoolean(schema: TBoolean, references: TSchema[], value: any): boolean { return IsBoolean(value) } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { +function FromConstructor(schema: TConstructor, references: TSchema[], value: any): boolean { return Visit(schema.returns, references, value.prototype) } -function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): boolean { +function FromDate(schema: TDate, references: TSchema[], value: any): boolean { if (!IsDate(value)) return false if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { return false @@ -137,10 +182,10 @@ function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): bo } return true } -function TFunction(schema: Types.TFunction, references: Types.TSchema[], value: any): boolean { +function FromFunction(schema: TFunction, references: TSchema[], value: any): boolean { return IsFunction(value) } -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { +function FromInteger(schema: TInteger, references: TSchema[], value: any): boolean { if (!IsInteger(value)) { return false } @@ -161,36 +206,36 @@ function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: an } return true } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): boolean { +function FromIntersect(schema: TIntersect, references: TSchema[], value: any): boolean { const check1 = schema.allOf.every((schema) => Visit(schema, references, value)) if (schema.unevaluatedProperties === false) { - const keyPattern = new RegExp(Types.KeyResolver.ResolvePattern(schema)) + const keyPattern = new RegExp(KeyOfPattern(schema)) const check2 = Object.getOwnPropertyNames(value).every((key) => keyPattern.test(key)) return check1 && check2 - } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const keyCheck = new RegExp(Types.KeyResolver.ResolvePattern(schema)) - const check2 = Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as Types.TSchema, references, value[key])) + } else if (IsSchema(schema.unevaluatedProperties)) { + const keyCheck = new RegExp(KeyOfPattern(schema)) + const check2 = Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties as TSchema, references, value[key])) return check1 && check2 } else { return check1 } } -function TIterator(schema: Types.TIterator, references: Types.TSchema[], value: any): boolean { +function FromIterator(schema: TIterator, references: TSchema[], value: any): boolean { return IsIterator(value) } -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { +function FromLiteral(schema: TLiteral, references: TSchema[], value: any): boolean { return value === schema.const } -function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { +function FromNever(schema: TNever, references: TSchema[], value: any): boolean { return false } -function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { +function FromNot(schema: TNot, references: TSchema[], value: any): boolean { return !Visit(schema.not, references, value) } -function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): boolean { +function FromNull(schema: TNull, references: TSchema[], value: any): boolean { return IsNull(value) } -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { +function FromNumber(schema: TNumber, references: TSchema[], value: any): boolean { if (!TypeSystemPolicy.IsNumberLike(value)) return false if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false @@ -209,7 +254,7 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any) } return true } -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): boolean { +function FromObject(schema: TObject, references: TSchema[], value: any): boolean { if (!TypeSystemPolicy.IsObjectLike(value)) return false if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) { return false @@ -224,7 +269,7 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) if (!Visit(property, references, value[knownKey])) { return false } - if ((Types.ExtendsUndefined.Check(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) { + if ((ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) { return false } } else { @@ -243,15 +288,15 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) } } else if (typeof schema.additionalProperties === 'object') { const valueKeys = Object.getOwnPropertyNames(value) - return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as Types.TSchema, references, value[key])) + return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties as TSchema, references, value[key])) } else { return true } } -function TPromise(schema: Types.TPromise, references: Types.TSchema[], value: any): boolean { +function FromPromise(schema: TPromise, references: TSchema[], value: any): boolean { return IsPromise(value) } -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { +function FromRecord(schema: TRecord, references: TSchema[], value: any): boolean { if (!TypeSystemPolicy.IsRecordLike(value)) { return false } @@ -269,7 +314,7 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v }) // prettier-ignore const check2 = typeof schema.additionalProperties === 'object' ? Object.entries(value).every(([key, value]) => { - return (!regex.test(key)) ? Visit(schema.additionalProperties as Types.TSchema, references, value) : true + return (!regex.test(key)) ? Visit(schema.additionalProperties as TSchema, references, value) : true }) : true const check3 = schema.additionalProperties === false @@ -279,10 +324,14 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v : true return check1 && check2 && check3 } -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): boolean { +function FromRef(schema: TRef, references: TSchema[], value: any): boolean { return Visit(Deref(schema, references), references, value) } -function TString(schema: Types.TString, references: Types.TSchema[], value: any): boolean { +function FromRegExp(schema: TRegExp, references: TSchema[], value: any): boolean { + const regex = new RegExp(schema.source, schema.flags) + return regex.test(value) +} +function FromString(schema: TString, references: TSchema[], value: any): boolean { if (!IsString(value)) { return false } @@ -297,22 +346,22 @@ function TString(schema: Types.TString, references: Types.TSchema[], value: any) if (!regex.test(value)) return false } if (IsDefined(schema.format)) { - if (!Types.FormatRegistry.Has(schema.format)) return false - const func = Types.FormatRegistry.Get(schema.format)! + if (!FormatRegistry.Has(schema.format)) return false + const func = FormatRegistry.Get(schema.format)! return func(value) } return true } -function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): boolean { +function FromSymbol(schema: TSymbol, references: TSchema[], value: any): boolean { return IsSymbol(value) } -function TTemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { +function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], value: any): boolean { return IsString(value) && new RegExp(schema.pattern).test(value) } -function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): boolean { +function FromThis(schema: TThis, references: TSchema[], value: any): boolean { return Visit(Deref(schema, references), references, value) } -function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): boolean { +function FromTuple(schema: TTuple, references: TSchema[], value: any): boolean { if (!IsArray(value)) { return false } @@ -330,13 +379,13 @@ function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: } return true } -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { +function FromUndefined(schema: TUndefined, references: TSchema[], value: any): boolean { return IsUndefined(value) } -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { +function FromUnion(schema: TUnion, references: TSchema[], value: any): boolean { return schema.anyOf.some((inner) => Visit(inner, references, value)) } -function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: any): boolean { +function FromUint8Array(schema: TUint8Array, references: TSchema[], value: any): boolean { if (!IsUint8Array(value)) { return false } @@ -348,93 +397,95 @@ function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], val } return true } -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { +function FromUnknown(schema: TUnknown, references: TSchema[], value: any): boolean { return true } -function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { +function FromVoid(schema: TVoid, references: TSchema[], value: any): boolean { return TypeSystemPolicy.IsVoidLike(value) } -function TKind(schema: Types.TSchema, references: Types.TSchema[], value: unknown): boolean { - if (!Types.TypeRegistry.Has(schema[Types.Kind])) return false - const func = Types.TypeRegistry.Get(schema[Types.Kind])! +function FromKind(schema: TSchema, references: TSchema[], value: unknown): boolean { + if (!TypeRegistry.Has(schema[Kind])) return false + const func = TypeRegistry.Get(schema[Kind])! return func(schema, value) } -function Visit(schema: T, references: Types.TSchema[], value: any): boolean { +function Visit(schema: T, references: TSchema[], value: any): boolean { const references_ = IsDefined(schema.$id) ? [...references, schema] : references const schema_ = schema as any - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': - return TAny(schema_, references_, value) + return FromAny(schema_, references_, value) case 'Array': - return TArray(schema_, references_, value) + return FromArray(schema_, references_, value) case 'AsyncIterator': - return TAsyncIterator(schema_, references_, value) + return FromAsyncIterator(schema_, references_, value) case 'BigInt': - return TBigInt(schema_, references_, value) + return FromBigInt(schema_, references_, value) case 'Boolean': - return TBoolean(schema_, references_, value) + return FromBoolean(schema_, references_, value) case 'Constructor': - return TConstructor(schema_, references_, value) + return FromConstructor(schema_, references_, value) case 'Date': - return TDate(schema_, references_, value) + return FromDate(schema_, references_, value) case 'Function': - return TFunction(schema_, references_, value) + return FromFunction(schema_, references_, value) case 'Integer': - return TInteger(schema_, references_, value) + return FromInteger(schema_, references_, value) case 'Intersect': - return TIntersect(schema_, references_, value) + return FromIntersect(schema_, references_, value) case 'Iterator': - return TIterator(schema_, references_, value) + return FromIterator(schema_, references_, value) case 'Literal': - return TLiteral(schema_, references_, value) + return FromLiteral(schema_, references_, value) case 'Never': - return TNever(schema_, references_, value) + return FromNever(schema_, references_, value) case 'Not': - return TNot(schema_, references_, value) + return FromNot(schema_, references_, value) case 'Null': - return TNull(schema_, references_, value) + return FromNull(schema_, references_, value) case 'Number': - return TNumber(schema_, references_, value) + return FromNumber(schema_, references_, value) case 'Object': - return TObject(schema_, references_, value) + return FromObject(schema_, references_, value) case 'Promise': - return TPromise(schema_, references_, value) + return FromPromise(schema_, references_, value) case 'Record': - return TRecord(schema_, references_, value) + return FromRecord(schema_, references_, value) case 'Ref': - return TRef(schema_, references_, value) + return FromRef(schema_, references_, value) + case 'RegExp': + return FromRegExp(schema_, references_, value) case 'String': - return TString(schema_, references_, value) + return FromString(schema_, references_, value) case 'Symbol': - return TSymbol(schema_, references_, value) + return FromSymbol(schema_, references_, value) case 'TemplateLiteral': - return TTemplateLiteral(schema_, references_, value) + return FromTemplateLiteral(schema_, references_, value) case 'This': - return TThis(schema_, references_, value) + return FromThis(schema_, references_, value) case 'Tuple': - return TTuple(schema_, references_, value) + return FromTuple(schema_, references_, value) case 'Undefined': - return TUndefined(schema_, references_, value) + return FromUndefined(schema_, references_, value) case 'Union': - return TUnion(schema_, references_, value) + return FromUnion(schema_, references_, value) case 'Uint8Array': - return TUint8Array(schema_, references_, value) + return FromUint8Array(schema_, references_, value) case 'Unknown': - return TUnknown(schema_, references_, value) + return FromUnknown(schema_, references_, value) case 'Void': - return TVoid(schema_, references_, value) + return FromVoid(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCheckUnknownTypeError(schema_) - return TKind(schema_, references_, value) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCheckUnknownTypeError(schema_) + return FromKind(schema_, references_, value) } } // -------------------------------------------------------------------------- // Check // -------------------------------------------------------------------------- /** Returns true if the value matches the given type. */ -export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static +export function Check(schema: T, references: TSchema[], value: unknown): value is Static /** Returns true if the value matches the given type. */ -export function Check(schema: T, value: unknown): value is Types.Static +export function Check(schema: T, value: unknown): value is Static /** Returns true if the value matches the given type. */ export function Check(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/check/index.ts b/src/value/check/index.ts new file mode 100644 index 000000000..e318a6581 --- /dev/null +++ b/src/value/check/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './check' diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts new file mode 100644 index 000000000..29c52d32d --- /dev/null +++ b/src/value/clean/clean.ts @@ -0,0 +1,184 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { Check } from '../check/index' +import { Clone } from '../clone/index' +import { Deref } from '../deref/index' +import { Kind } from '../../type/symbols/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsString, + IsObject, + IsArray, + IsUndefined +} from '../guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +// prettier-ignore +import { + IsSchema +} from '../../type/guard/type' +// ------------------------------------------------------------------ +// IsCheckable +// ------------------------------------------------------------------ +function IsCheckable(schema: unknown): boolean { + return IsSchema(schema) && schema[Kind] !== 'Unsafe' +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +function FromArray(schema: TArray, references: TSchema[], value: unknown): any { + if (!IsArray(value)) return value + return value.map((value) => Visit(schema.items, references, value)) +} +function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { + const unevaluatedProperties = schema.unevaluatedProperties as TSchema + const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value))) + const composite = intersections.reduce((acc: any, value: any) => (IsObject(value) ? { ...acc, ...value } : value), {}) + if (!IsObject(value) || !IsObject(composite) || !IsSchema(unevaluatedProperties)) return composite + const knownkeys = KeyOfPropertyKeys(schema) as string[] + for (const key of Object.getOwnPropertyNames(value)) { + if (knownkeys.includes(key)) continue + if (Check(unevaluatedProperties, references, value[key])) { + composite[key] = Visit(unevaluatedProperties, references, value[key]) + } + } + return composite +} +function FromObject(schema: TObject, references: TSchema[], value: unknown): any { + if (!IsObject(value) || IsArray(value)) return value // Check IsArray for AllowArrayObject configuration + const additionalProperties = schema.additionalProperties as TSchema + for (const key of Object.getOwnPropertyNames(value)) { + if (key in schema.properties) { + value[key] = Visit(schema.properties[key], references, value[key]) + continue + } + if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { + value[key] = Visit(additionalProperties, references, value[key]) + continue + } + delete value[key] + } + return value +} +function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any { + if (!IsObject(value)) return value + const additionalProperties = schema.additionalProperties as TSchema + const propertyKeys = Object.keys(value) + const [propertyKey, propertySchema] = Object.entries(schema.patternProperties)[0] + const propertyKeyTest = new RegExp(propertyKey) + for (const key of propertyKeys) { + if (propertyKeyTest.test(key)) { + value[key] = Visit(propertySchema, references, value[key]) + continue + } + if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { + value[key] = Visit(additionalProperties, references, value[key]) + continue + } + delete value[key] + } + return value +} +function FromRef(schema: TRef, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function FromThis(schema: TThis, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any { + if (!IsArray(value)) return value + if (IsUndefined(schema.items)) return [] + const length = Math.min(value.length, schema.items.length) + for (let i = 0; i < length; i++) { + value[i] = Visit(schema.items[i], references, value[i]) + } + // prettier-ignore + return value.length > length + ? value.slice(0, length) + : value +} +function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { + for (const inner of schema.anyOf) { + if (IsCheckable(inner) && Check(inner, value)) { + return Visit(inner, references, value) + } + } + return value +} +function Visit(schema: TSchema, references: TSchema[], value: unknown): unknown { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Kind]) { + case 'Array': + return FromArray(schema_, references_, value) + case 'Intersect': + return FromIntersect(schema_, references_, value) + case 'Object': + return FromObject(schema_, references_, value) + case 'Record': + return FromRecord(schema_, references_, value) + case 'Ref': + return FromRef(schema_, references_, value) + case 'This': + return FromThis(schema_, references_, value) + case 'Tuple': + return FromTuple(schema_, references_, value) + case 'Union': + return FromUnion(schema_, references_, value) + default: + return value + } +} +// ------------------------------------------------------------------ +// Clean +// ------------------------------------------------------------------ +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/clean/index.ts b/src/value/clean/index.ts new file mode 100644 index 000000000..4e55b279e --- /dev/null +++ b/src/value/clean/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './clean' diff --git a/src/value/clone.ts b/src/value/clone/clone.ts similarity index 92% rename from src/value/clone.ts rename to src/value/clone/clone.ts index a2e2d1f30..4c337929b 100644 --- a/src/value/clone.ts +++ b/src/value/clone/clone.ts @@ -26,12 +26,15 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from './guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from '../guard/index' +// ------------------------------------------------------------------ // Clonable -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function ObjectType(value: ObjectType): any { const keys = [...Object.getOwnPropertyNames(value), ...Object.getOwnPropertySymbols(value)] return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) @@ -48,9 +51,9 @@ function DateType(value: Date): any { function ValueType(value: ValueType): any { return value } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Clone -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Returns a clone of the given value */ export function Clone(value: T): T { if (IsArray(value)) return ArrayType(value) diff --git a/src/value/clone/index.ts b/src/value/clone/index.ts new file mode 100644 index 000000000..4294f9957 --- /dev/null +++ b/src/value/clone/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './clone' diff --git a/src/value/convert.ts b/src/value/convert/convert.ts similarity index 53% rename from src/value/convert.ts rename to src/value/convert/convert.ts index 60ba2e1f3..8a0de37b2 100644 --- a/src/value/convert.ts +++ b/src/value/convert/convert.ts @@ -26,23 +26,41 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from './guard' -import { Clone } from './clone' -import { Check } from './check' -import { Deref } from './deref' -import * as Types from '../typebox' +import { Clone } from '../clone/index' +import { Check } from '../check/index' +import { Deref } from '../deref/index' -// -------------------------------------------------------------------------- -// Errors -// -------------------------------------------------------------------------- -export class ValueConvertUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Unknown type') - } -} -// -------------------------------------------------------------------------- +import { IsObject as IsObjectType } from '../../type/guard/type' +import { Kind } from '../../type/symbols/index' +import { Composite } from '../../type/composite/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TBigInt } from '../../type/bigint/index' +import type { TBoolean } from '../../type/boolean/index' +import type { TDate } from '../../type/date/index' +import type { TInteger } from '../../type/integer/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TLiteral } from '../../type/literal/index' +import type { TNull } from '../../type/null/index' +import type { TNumber } from '../../type/number/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' +import type { TString } from '../../type/string/index' +import type { TSymbol } from '../../type/symbol/index' +import type { TUndefined } from '../../type/undefined/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from '../guard/index' +// ------------------------------------------------------------------ // Conversions -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsStringNumeric(value: unknown): value is string { return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) } @@ -70,9 +88,9 @@ function IsDateTimeStringWithoutTimeZone(value: unknown): value is string { function IsDateString(value: unknown): value is string { return IsString(value) && /^\d\d\d\d-[0-1]\d-[0-3]\d$/i.test(value) } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Convert -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function TryConvertLiteralString(value: unknown, target: string) { const conversion = TryConvertString(value) return conversion === target ? conversion : value @@ -85,16 +103,14 @@ function TryConvertLiteralBoolean(value: unknown, target: boolean) { const conversion = TryConvertBoolean(value) return conversion === target ? conversion : value } -function TryConvertLiteral(schema: Types.TLiteral, value: unknown) { - if (typeof schema.const === 'string') { - return TryConvertLiteralString(value, schema.const) - } else if (typeof schema.const === 'number') { - return TryConvertLiteralNumber(value, schema.const) - } else if (typeof schema.const === 'boolean') { - return TryConvertLiteralBoolean(value, schema.const) - } else { - return Clone(value) - } +// prettier-ignore +function TryConvertLiteral(schema: TLiteral, value: unknown) { + return ( + IsString(schema.const) ? TryConvertLiteralString(value, schema.const) : + IsNumber(schema.const) ? TryConvertLiteralNumber(value, schema.const) : + IsBoolean(schema.const) ? TryConvertLiteralBoolean(value, schema.const) : + Clone(value) + ) } function TryConvertBoolean(value: unknown) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value @@ -117,85 +133,82 @@ function TryConvertNull(value: unknown) { function TryConvertUndefined(value: unknown) { return IsString(value) && value === 'undefined' ? undefined : value } +// ------------------------------------------------------------------ +// note: this function may return an invalid dates for the regex +// tests above. Invalid dates will however be checked during the +// casting function and will return a epoch date if invalid. +// Consider better string parsing for the iso dates in future +// revisions. +// ------------------------------------------------------------------ +// prettier-ignore function TryConvertDate(value: unknown) { - // -------------------------------------------------------------------------- - // note: this function may return an invalid dates for the regex tests - // above. Invalid dates will however be checked during the casting function - // and will return a epoch date if invalid. Consider better string parsing - // for the iso dates in future revisions. - // -------------------------------------------------------------------------- - return IsDate(value) - ? value - : IsNumber(value) - ? new Date(value) - : IsValueTrue(value) - ? new Date(1) - : IsValueFalse(value) - ? new Date(0) - : IsStringNumeric(value) - ? new Date(parseInt(value)) - : IsTimeStringWithoutTimeZone(value) - ? new Date(`1970-01-01T${value}.000Z`) - : IsTimeStringWithTimeZone(value) - ? new Date(`1970-01-01T${value}`) - : IsDateTimeStringWithoutTimeZone(value) - ? new Date(`${value}.000Z`) - : IsDateTimeStringWithTimeZone(value) - ? new Date(value) - : IsDateString(value) - ? new Date(`${value}T00:00:00.000Z`) - : value -} -// -------------------------------------------------------------------------- + return ( + IsDate(value) ? value : + IsNumber(value) ? new Date(value) : + IsValueTrue(value) ? new Date(1) : + IsValueFalse(value) ? new Date(0) : + IsStringNumeric(value) ? new Date(parseInt(value)) : + IsTimeStringWithoutTimeZone(value) ? new Date(`1970-01-01T${value}.000Z`) : + IsTimeStringWithTimeZone(value) ? new Date(`1970-01-01T${value}`) : + IsDateTimeStringWithoutTimeZone(value) ? new Date(`${value}.000Z`) : + IsDateTimeStringWithTimeZone(value) ? new Date(value) : + IsDateString(value) ? new Date(`${value}T00:00:00.000Z`) : + value + ) +} +// ------------------------------------------------------------------ // Default -// -------------------------------------------------------------------------- -export function Default(value: any) { +// ------------------------------------------------------------------ +function Default(value: any) { return value } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Convert -// -------------------------------------------------------------------------- -function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { +// ------------------------------------------------------------------ +function FromArray(schema: TArray, references: TSchema[], value: any): any { if (IsArray(value)) { return value.map((value) => Visit(schema.items, references, value)) } return value } -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): unknown { +function FromBigInt(schema: TBigInt, references: TSchema[], value: any): unknown { return TryConvertBigInt(value) } -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { +function FromBoolean(schema: TBoolean, references: TSchema[], value: any): unknown { return TryConvertBoolean(value) } -function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { +function FromDate(schema: TDate, references: TSchema[], value: any): unknown { return TryConvertDate(value) } -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { +function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown { return TryConvertInteger(value) } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): unknown { - // prettier-ignore - return (schema.allOf.every(schema => Types.TypeGuard.TObject(schema))) - ? Visit(Types.Type.Composite(schema.allOf as Types.TObject[]), references, value) - : Visit(schema.allOf[0], references, value) +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[], value: any): unknown { + const allObjects = schema.allOf.every(schema => IsObjectType(schema)) + if(allObjects) return Visit(Composite(schema.allOf as TObject[]), references, value) + return Visit(schema.allOf[0], references, value) // todo: fix this } -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): unknown { +function FromLiteral(schema: TLiteral, references: TSchema[], value: any): unknown { return TryConvertLiteral(schema, value) } -function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { +function FromNull(schema: TNull, references: TSchema[], value: any): unknown { return TryConvertNull(value) } -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): unknown { +function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown { return TryConvertNumber(value) } -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { - if (IsObject(value)) - return Object.getOwnPropertyNames(schema.properties).reduce((acc, key) => { - return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } - }, value) - return value -} -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[], value: any): unknown { + const isConvertable = IsObject(value) + if(!isConvertable) return value + return Object.getOwnPropertyNames(schema.properties).reduce((value, key) => { + return !IsUndefined(value[key]) + ? ({ ...value, [key]: Visit(schema.properties[key], references, value[key]) }) + : ({ ...value }) + }, value) +} +function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown { const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] const result = {} as Record @@ -204,30 +217,32 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v } return result } -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { +function FromRef(schema: TRef, references: TSchema[], value: any): unknown { return Visit(Deref(schema, references), references, value) } -function TString(schema: Types.TString, references: Types.TSchema[], value: any): unknown { +function FromString(schema: TString, references: TSchema[], value: any): unknown { return TryConvertString(value) } -function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { +function FromSymbol(schema: TSymbol, references: TSchema[], value: any): unknown { return IsString(value) || IsNumber(value) ? Symbol(value) : value } -function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): unknown { +function FromThis(schema: TThis, references: TSchema[], value: any): unknown { return Visit(Deref(schema, references), references, value) } -function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { - if (IsArray(value) && !IsUndefined(schema.items)) { - return value.map((value, index) => { - return index < schema.items!.length ? Visit(schema.items![index], references, value) : value - }) - } - return value -} -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[], value: any): unknown { + const isConvertable = IsArray(value) && !IsUndefined(schema.items) + if(!isConvertable) return value + return value.map((value, index) => { + return (index < schema.items!.length) + ? Visit(schema.items![index], references, value) + : value + }) +} +function FromUndefined(schema: TUndefined, references: TSchema[], value: any): unknown { return TryConvertUndefined(value) } -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { +function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { for (const subschema of schema.anyOf) { const converted = Visit(subschema, references, value) if (Check(subschema, references, converted)) { @@ -236,77 +251,61 @@ function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): } return value } -function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): unknown { +function Visit(schema: TSchema, references: TSchema[], value: any): unknown { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - switch (schema[Types.Kind]) { - // ------------------------------------------------------ - // Structural - // ------------------------------------------------------ + switch (schema[Kind]) { case 'Array': - return TArray(schema_, references_, value) + return FromArray(schema_, references_, value) case 'BigInt': - return TBigInt(schema_, references_, value) + return FromBigInt(schema_, references_, value) case 'Boolean': - return TBoolean(schema_, references_, value) + return FromBoolean(schema_, references_, value) case 'Date': - return TDate(schema_, references_, value) + return FromDate(schema_, references_, value) case 'Integer': - return TInteger(schema_, references_, value) + return FromInteger(schema_, references_, value) case 'Intersect': - return TIntersect(schema_, references_, value) + return FromIntersect(schema_, references_, value) case 'Literal': - return TLiteral(schema_, references_, value) + return FromLiteral(schema_, references_, value) case 'Null': - return TNull(schema_, references_, value) + return FromNull(schema_, references_, value) case 'Number': - return TNumber(schema_, references_, value) + return FromNumber(schema_, references_, value) case 'Object': - return TObject(schema_, references_, value) + return FromObject(schema_, references_, value) case 'Record': - return TRecord(schema_, references_, value) + return FromRecord(schema_, references_, value) case 'Ref': - return TRef(schema_, references_, value) + return FromRef(schema_, references_, value) case 'String': - return TString(schema_, references_, value) + return FromString(schema_, references_, value) case 'Symbol': - return TSymbol(schema_, references_, value) + return FromSymbol(schema_, references_, value) case 'This': - return TThis(schema_, references_, value) + return FromThis(schema_, references_, value) case 'Tuple': - return TTuple(schema_, references_, value) + return FromTuple(schema_, references_, value) case 'Undefined': - return TUndefined(schema_, references_, value) + return FromUndefined(schema_, references_, value) case 'Union': - return TUnion(schema_, references_, value) - // ------------------------------------------------------ - // Default - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'Constructor': - case 'Function': - case 'Iterator': - case 'Never': - case 'Promise': - case 'TemplateLiteral': - case 'Uint8Array': - case 'Unknown': - case 'Void': - return Default(value) + return FromUnion(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) return Default(value) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Convert -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown +export function Convert(schema: T, references: TSchema[], value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, value: unknown): unknown +export function Convert(schema: T, value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +// prettier-ignore export function Convert(...args: any[]) { - return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) + return args.length === 3 + ? Visit(args[0], args[1], args[2]) + : Visit(args[0], [], args[1]) } diff --git a/src/value/convert/index.ts b/src/value/convert/index.ts new file mode 100644 index 000000000..0e642e6ac --- /dev/null +++ b/src/value/convert/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './convert' diff --git a/src/value/create.ts b/src/value/create/create.ts similarity index 52% rename from src/value/create.ts rename to src/value/create/create.ts index b36b42f41..b632947bd 100644 --- a/src/value/create.ts +++ b/src/value/create/create.ts @@ -26,59 +26,72 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { HasPropertyKey, IsString } from './guard' -import { Check } from './check' -import { Deref } from './deref' -import * as Types from '../typebox' +import { HasPropertyKey, IsString } from '../guard/index' +import { Check } from '../check/index' +import { Deref } from '../deref/index' +import { TemplateLiteralGenerate, IsTemplateLiteralFinite } from '../../type/template-literal/index' +import { PatternStringExact, PatternNumberExact } from '../../type/patterns/index' +import { TypeRegistry } from '../../type/registry/index' +import { Kind } from '../../type/symbols/index' +import { TypeBoxError } from '../../type/error/index' -// -------------------------------------------------------------------------- +import type { TSchema } from '../../type/schema/index' +import type { TAsyncIterator } from '../../type/async-iterator/index' +import type { TAny } from '../../type/any/index' +import type { TArray } from '../../type/array/index' +import type { TBigInt } from '../../type/bigint/index' +import type { TBoolean } from '../../type/boolean/index' +import type { TDate } from '../../type/date/index' +import type { TConstructor } from '../../type/constructor/index' +import type { TFunction } from '../../type/function/index' +import type { TInteger } from '../../type/integer/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TIterator } from '../../type/iterator/index' +import type { TLiteral } from '../../type/literal/index' +import type { TNever } from '../../type/never/index' +import type { TNot } from '../../type/not/index' +import type { TNull } from '../../type/null/index' +import type { TNumber } from '../../type/number/index' +import type { TObject } from '../../type/object/index' +import type { TPromise } from '../../type/promise/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TRegExp } from '../../type/regexp/index' +import type { TTemplateLiteral } from '../../type/template-literal/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' +import type { TUnknown } from '../../type/unknown/index' +import type { Static } from '../../type/static/index' +import type { TString } from '../../type/string/index' +import type { TSymbol } from '../../type/symbol/index' +import type { TUndefined } from '../../type/undefined/index' +import type { TUint8Array } from '../../type/uint8array/index' +import type { TVoid } from '../../type/void/index' + +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueCreateUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Unknown type') - } -} -export class ValueCreateNeverTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Never types cannot be created') - } -} -export class ValueCreateNotTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Not types must have a default value') - } -} -export class ValueCreateIntersectTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Intersect produced invalid value. Consider using a default value.') - } -} -export class ValueCreateTempateLiteralTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Can only create template literal values from patterns that produce finite sequences. Consider using a default value.') - } -} -export class ValueCreateRecursiveInstantiationError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly recursiveMaxDepth: number) { - super('Value cannot be created as recursive type may produce value of infinite size. Consider using a default.') +// ------------------------------------------------------------------ +export class ValueCreateError extends TypeBoxError { + constructor(public readonly schema: TSchema, message: string) { + super(message) } } -// -------------------------------------------------------------------------- -// Types -// -------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[]): any { +// ------------------------------------------------------------------ +// Create +// ------------------------------------------------------------------ +function FromAny(schema: TAny, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } -function TArray(schema: Types.TArray, references: Types.TSchema[]): any { +function FromArray(schema: TArray, references: TSchema[]): any { if (schema.uniqueItems === true && !HasPropertyKey(schema, 'default')) { - throw new Error('ValueCreate.Array: Array with the uniqueItems constraint requires a default value') + throw new ValueCreateError(schema, 'Array with the uniqueItems constraint requires a default value') } else if ('contains' in schema && !HasPropertyKey(schema, 'default')) { - throw new Error('ValueCreate.Array: Array with the contains constraint requires a default value') + throw new ValueCreateError(schema, 'Array with the contains constraint requires a default value') } else if ('default' in schema) { return schema.default } else if (schema.minItems !== undefined) { @@ -89,28 +102,28 @@ function TArray(schema: Types.TArray, references: Types.TSchema[]): any { return [] } } -function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]) { +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return (async function* () {})() } } -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[]): any { +function FromBigInt(schema: TBigInt, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return BigInt(0) } } -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[]): any { +function FromBoolean(schema: TBoolean, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return false } } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): any { +function FromConstructor(schema: TConstructor, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { @@ -129,7 +142,7 @@ function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): } } } -function TDate(schema: Types.TDate, references: Types.TSchema[]): any { +function FromDate(schema: TDate, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimumTimestamp !== undefined) { @@ -138,14 +151,14 @@ function TDate(schema: Types.TDate, references: Types.TSchema[]): any { return new Date() } } -function TFunction(schema: Types.TFunction, references: Types.TSchema[]): any { +function FromFunction(schema: TFunction, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return () => Visit(schema.returns, references) } } -function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { +function FromInteger(schema: TInteger, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { @@ -154,53 +167,61 @@ function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { return 0 } } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any { +function FromIntersect(schema: TIntersect, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { - // Note: The best we can do here is attempt to instance each sub type and apply through object assign. For non-object - // sub types, we just escape the assignment and just return the value. In the latter case, this is typically going to + // -------------------------------------------------------------- + // Note: The best we can do here is attempt to instance each + // sub type and apply through object assign. For non-object + // sub types, we just escape the assignment and just return + // the value. In the latter case, this is typically going to // be a consequence of an illogical intersection. + // -------------------------------------------------------------- const value = schema.allOf.reduce((acc, schema) => { const next = Visit(schema, references) as any return typeof next === 'object' ? { ...acc, ...next } : next }, {}) - if (!Check(schema, references, value)) throw new ValueCreateIntersectTypeError(schema) + if (!Check(schema, references, value)) throw new ValueCreateError(schema, 'Intersect produced invalid value. Consider using a default value.') return value } } -function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { +function FromIterator(schema: TIterator, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return (function* () {})() } } -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[]): any { +function FromLiteral(schema: TLiteral, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return schema.const } } -function TNever(schema: Types.TNever, references: Types.TSchema[]): any { - throw new ValueCreateNeverTypeError(schema) +function FromNever(schema: TNever, references: TSchema[]): any { + if (HasPropertyKey(schema, 'default')) { + return schema.default + } else { + throw new ValueCreateError(schema, 'Never types cannot be created. Consider using a default value.') + } } -function TNot(schema: Types.TNot, references: Types.TSchema[]): any { +function FromNot(schema: TNot, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { - throw new ValueCreateNotTypeError(schema) + throw new ValueCreateError(schema, 'Not types must have a default value') } } -function TNull(schema: Types.TNull, references: Types.TSchema[]): any { +function FromNull(schema: TNull, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return null } } -function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { +function FromNumber(schema: TNumber, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { @@ -209,7 +230,7 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { return 0 } } -function TObject(schema: Types.TObject, references: Types.TSchema[]): any { +function FromObject(schema: TObject, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { @@ -222,18 +243,18 @@ function TObject(schema: Types.TObject, references: Types.TSchema[]): any { ) } } -function TPromise(schema: Types.TPromise, references: Types.TSchema[]): any { +function FromPromise(schema: TPromise, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return Promise.resolve(Visit(schema.item, references)) } } -function TRecord(schema: Types.TRecord, references: Types.TSchema[]): any { +function FromRecord(schema: TRecord, references: TSchema[]): any { const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0] if (HasPropertyKey(schema, 'default')) { return schema.default - } else if (!(keyPattern === Types.PatternStringExact || keyPattern === Types.PatternNumberExact)) { + } else if (!(keyPattern === PatternStringExact || keyPattern === PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') return propertyKeys.reduce((acc, key) => { return { ...acc, [key]: Visit(valueSchema, references) } @@ -242,23 +263,30 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[]): return {} } } -function TRef(schema: Types.TRef, references: Types.TSchema[]): any { +function FromRef(schema: TRef, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return Visit(Deref(schema, references), references) } } -function TString(schema: Types.TString, references: Types.TSchema[]): any { +function FromRegExp(schema: TRegExp, references: TSchema[]): any { + if (HasPropertyKey(schema, 'default')) { + return schema.default + } else { + throw new ValueCreateError(schema, 'RegExp types cannot be created. Consider using a default value.') + } +} +function FromString(schema: TString, references: TSchema[]): any { if (schema.pattern !== undefined) { if (!HasPropertyKey(schema, 'default')) { - throw new Error('ValueCreate.String: String types with patterns must specify a default value') + throw new ValueCreateError(schema, 'String types with patterns must specify a default value') } else { return schema.default } } else if (schema.format !== undefined) { if (!HasPropertyKey(schema, 'default')) { - throw new Error('ValueCreate.String: String types with formats must specify a default value') + throw new ValueCreateError(schema, 'String types with formats must specify a default value') } else { return schema.default } @@ -266,15 +294,14 @@ function TString(schema: Types.TString, references: Types.TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minLength !== undefined) { - return Array.from({ length: schema.minLength }) - .map(() => '.') - .join('') + // prettier-ignore + return Array.from({ length: schema.minLength }).map(() => ' ').join('') } else { return '' } } } -function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { +function FromSymbol(schema: TSymbol, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if ('value' in schema) { @@ -283,24 +310,23 @@ function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { return Symbol() } } -function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { +function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { return schema.default } - const expression = Types.TemplateLiteralParser.ParseExact(schema.pattern) - if (!Types.TemplateLiteralFinite.Check(expression)) throw new ValueCreateTempateLiteralTypeError(schema) - const sequence = Types.TemplateLiteralGenerator.Generate(expression) - return sequence.next().value + if (!IsTemplateLiteralFinite(schema)) throw new ValueCreateError(schema, 'Can only create template literals that produce a finite variants. Consider using a default value.') + const generated = TemplateLiteralGenerate(schema) as string[] + return generated[0] } -function TThis(schema: Types.TThis, references: Types.TSchema[]): any { - if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) +function FromThis(schema: TThis, references: TSchema[]): any { + if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateError(schema, 'Cannot create recursive type as it appears possibly infinite. Consider using a default.') if (HasPropertyKey(schema, 'default')) { return schema.default } else { return Visit(Deref(schema, references), references) } } -function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { +function FromTuple(schema: TTuple, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } @@ -310,14 +336,14 @@ function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { return Array.from({ length: schema.minItems }).map((_, index) => Visit((schema.items as any[])[index], references)) } } -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[]): any { +function FromUndefined(schema: TUndefined, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return undefined } } -function TUnion(schema: Types.TUnion, references: Types.TSchema[]): any { +function FromUnion(schema: TUnion, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.anyOf.length === 0) { @@ -326,7 +352,7 @@ function TUnion(schema: Types.TUnion, references: Types.TSchema[]): any { return Visit(schema.anyOf[0], references) } } -function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): any { +function FromUint8Array(schema: TUint8Array, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minByteLength !== undefined) { @@ -335,108 +361,110 @@ function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): an return new Uint8Array(0) } } -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[]): any { +function FromUnknown(schema: TUnknown, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } -function TVoid(schema: Types.TVoid, references: Types.TSchema[]): any { +function FromVoid(schema: TVoid, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return void 0 } } -function TKind(schema: Types.TSchema, references: Types.TSchema[]): any { +function FromKind(schema: TSchema, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { throw new Error('User defined types must specify a default value') } } -function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { +function Visit(schema: TSchema, references: TSchema[]): unknown { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': - return TAny(schema_, references_) + return FromAny(schema_, references_) case 'Array': - return TArray(schema_, references_) + return FromArray(schema_, references_) case 'AsyncIterator': - return TAsyncIterator(schema_, references_) + return FromAsyncIterator(schema_, references_) case 'BigInt': - return TBigInt(schema_, references_) + return FromBigInt(schema_, references_) case 'Boolean': - return TBoolean(schema_, references_) + return FromBoolean(schema_, references_) case 'Constructor': - return TConstructor(schema_, references_) + return FromConstructor(schema_, references_) case 'Date': - return TDate(schema_, references_) + return FromDate(schema_, references_) case 'Function': - return TFunction(schema_, references_) + return FromFunction(schema_, references_) case 'Integer': - return TInteger(schema_, references_) + return FromInteger(schema_, references_) case 'Intersect': - return TIntersect(schema_, references_) + return FromIntersect(schema_, references_) case 'Iterator': - return TIterator(schema_, references_) + return FromIterator(schema_, references_) case 'Literal': - return TLiteral(schema_, references_) + return FromLiteral(schema_, references_) case 'Never': - return TNever(schema_, references_) + return FromNever(schema_, references_) case 'Not': - return TNot(schema_, references_) + return FromNot(schema_, references_) case 'Null': - return TNull(schema_, references_) + return FromNull(schema_, references_) case 'Number': - return TNumber(schema_, references_) + return FromNumber(schema_, references_) case 'Object': - return TObject(schema_, references_) + return FromObject(schema_, references_) case 'Promise': - return TPromise(schema_, references_) + return FromPromise(schema_, references_) case 'Record': - return TRecord(schema_, references_) + return FromRecord(schema_, references_) case 'Ref': - return TRef(schema_, references_) + return FromRef(schema_, references_) + case 'RegExp': + return FromRegExp(schema_, references_) case 'String': - return TString(schema_, references_) + return FromString(schema_, references_) case 'Symbol': - return TSymbol(schema_, references_) + return FromSymbol(schema_, references_) case 'TemplateLiteral': - return TTemplateLiteral(schema_, references_) + return FromTemplateLiteral(schema_, references_) case 'This': - return TThis(schema_, references_) + return FromThis(schema_, references_) case 'Tuple': - return TTuple(schema_, references_) + return FromTuple(schema_, references_) case 'Undefined': - return TUndefined(schema_, references_) + return FromUndefined(schema_, references_) case 'Union': - return TUnion(schema_, references_) + return FromUnion(schema_, references_) case 'Uint8Array': - return TUint8Array(schema_, references_) + return FromUint8Array(schema_, references_) case 'Unknown': - return TUnknown(schema_, references_) + return FromUnknown(schema_, references_) case 'Void': - return TVoid(schema_, references_) + return FromVoid(schema_, references_) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCreateUnknownTypeError(schema_) - return TKind(schema_, references_) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCreateError(schema_, 'Unknown type') + return FromKind(schema_, references_) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // State -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ const recursiveMaxDepth = 512 let recursiveDepth = 0 -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Create -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates a value from the given schema and references */ -export function Create(schema: T, references: Types.TSchema[]): Types.Static +export function Create(schema: T, references: TSchema[]): Static /** Creates a value from the given schema */ -export function Create(schema: T): Types.Static +export function Create(schema: T): Static /** Creates a value from the given schema */ export function Create(...args: any[]) { recursiveDepth = 0 diff --git a/src/value/create/index.ts b/src/value/create/index.ts new file mode 100644 index 000000000..62543e72b --- /dev/null +++ b/src/value/create/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './create' diff --git a/src/value/default/default.ts b/src/value/default/default.ts new file mode 100644 index 000000000..6a310d259 --- /dev/null +++ b/src/value/default/default.ts @@ -0,0 +1,185 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Check } from '../check/index' +import { Deref } from '../deref/index' +import { Kind } from '../../type/symbols/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsString, IsObject, IsArray, IsUndefined } from '../guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsSchema } from '../../type/guard/type' +// ------------------------------------------------------------------ +// ValueOrDefault +// ------------------------------------------------------------------ +function ValueOrDefault(schema: TSchema, value: unknown) { + return !(value === undefined) || !('default' in schema) ? value : schema.default +} +// ------------------------------------------------------------------ +// IsCheckable +// ------------------------------------------------------------------ +function IsCheckable(schema: unknown): boolean { + return IsSchema(schema) && schema[Kind] !== 'Unsafe' +} +// ------------------------------------------------------------------ +// IsDefaultSchema +// ------------------------------------------------------------------ +function IsDefaultSchema(value: unknown): value is TSchema { + return IsSchema(value) && 'default' in value +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +function FromArray(schema: TArray, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + if (!IsArray(defaulted)) return defaulted + for (let i = 0; i < defaulted.length; i++) { + defaulted[i] = Visit(schema.items, references, defaulted[i]) + } + return defaulted +} +function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + return schema.allOf.reduce((acc, schema) => { + const next = Visit(schema, references, defaulted) + return IsObject(next) ? { ...acc, ...next } : next + }, {}) +} +function FromObject(schema: TObject, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + if (!IsObject(defaulted)) return defaulted + const additionalPropertiesSchema = schema.additionalProperties as TSchema + const knownPropertyKeys = Object.getOwnPropertyNames(schema.properties) + // properties + for (const key of knownPropertyKeys) { + if (!IsDefaultSchema(schema.properties[key])) continue + defaulted[key] = Visit(schema.properties[key], references, defaulted[key]) + } + // return if not additional properties + if (!IsDefaultSchema(additionalPropertiesSchema)) return defaulted + // additional properties + for (const key of Object.getOwnPropertyNames(defaulted)) { + if (knownPropertyKeys.includes(key)) continue + defaulted[key] = Visit(additionalPropertiesSchema, references, defaulted[key]) + } + return defaulted +} +function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + if (!IsObject(defaulted)) return defaulted + const additionalPropertiesSchema = schema.additionalProperties as TSchema + const [propertyKeyPattern, propertySchema] = Object.entries(schema.patternProperties)[0] + const knownPropertyKey = new RegExp(propertyKeyPattern) + // properties + for (const key of Object.getOwnPropertyNames(defaulted)) { + if (!(knownPropertyKey.test(key) && IsDefaultSchema(propertySchema))) continue + defaulted[key] = Visit(propertySchema, references, defaulted[key]) + } + // return if not additional properties + if (!IsDefaultSchema(additionalPropertiesSchema)) return defaulted + // additional properties + for (const key of Object.getOwnPropertyNames(defaulted)) { + if (knownPropertyKey.test(key)) continue + defaulted[key] = Visit(additionalPropertiesSchema, references, defaulted[key]) + } + return defaulted +} +function FromRef(schema: TRef, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, ValueOrDefault(schema, value)) +} +function FromThis(schema: TThis, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + if (!IsArray(defaulted) || IsUndefined(schema.items)) return defaulted + const [items, max] = [schema.items!, Math.max(schema.items!.length, defaulted.length)] + for (let i = 0; i < max; i++) { + if (i < items.length) defaulted[i] = Visit(items[i], references, defaulted[i]) + } + return defaulted +} +function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { + const defaulted = ValueOrDefault(schema, value) + for (const inner of schema.anyOf) { + const result = Visit(inner, references, defaulted) + if (IsCheckable(inner) && Check(inner, result)) { + return result + } + } + return defaulted +} +function Visit(schema: TSchema, references: TSchema[], value: unknown): any { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + switch (schema_[Kind]) { + case 'Array': + return FromArray(schema_, references_, value) + case 'Intersect': + return FromIntersect(schema_, references_, value) + case 'Object': + return FromObject(schema_, references_, value) + case 'Record': + return FromRecord(schema_, references_, value) + case 'Ref': + return FromRef(schema_, references_, value) + case 'This': + return FromThis(schema_, references_, value) + case 'Tuple': + return FromTuple(schema_, references_, value) + case 'Union': + return FromUnion(schema_, references_, value) + default: + return ValueOrDefault(schema_, value) + } +} +// ------------------------------------------------------------------ +// Default +// ------------------------------------------------------------------ +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/default/index.ts b/src/value/default/index.ts new file mode 100644 index 000000000..fb74557fd --- /dev/null +++ b/src/value/default/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './default' diff --git a/src/value/delta.ts b/src/value/delta/delta.ts similarity index 71% rename from src/value/delta.ts rename to src/value/delta/delta.ts index 5a2092dc0..d7ae9fa89 100644 --- a/src/value/delta.ts +++ b/src/value/delta/delta.ts @@ -26,50 +26,57 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from './guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' -import { Type, Static } from '../typebox' -import { ValuePointer } from './pointer' -import { Clone } from './clone' +import { IsPlainObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from '../guard/index' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' +import type { Static } from '../../type/static/index' +import { ValuePointer } from '../pointer/index' +import { Clone } from '../clone/index' +import { TypeBoxError } from '../../type/error/index' -// -------------------------------------------------------------------------- +import { Literal as CreateLiteral } from '../../type/literal/index' +import { Object as CreateObject } from '../../type/object/index' +import { String as CreateString } from '../../type/string/index' +import { Unknown as CreateUnknown } from '../../type/unknown/index' +import { Union as CreateUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ // Commands -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export type Insert = Static -export const Insert = Type.Object({ - type: Type.Literal('insert'), - path: Type.String(), - value: Type.Unknown(), +export const Insert = CreateObject({ + type: CreateLiteral('insert'), + path: CreateString(), + value: CreateUnknown(), }) export type Update = Static -export const Update = Type.Object({ - type: Type.Literal('update'), - path: Type.String(), - value: Type.Unknown(), +export const Update = CreateObject({ + type: CreateLiteral('update'), + path: CreateString(), + value: CreateUnknown(), }) export type Delete = Static -export const Delete = Type.Object({ - type: Type.Literal('delete'), - path: Type.String(), +export const Delete = CreateObject({ + type: CreateLiteral('delete'), + path: CreateString(), }) export type Edit = Static -export const Edit = Type.Union([Insert, Update, Delete]) -// -------------------------------------------------------------------------- +export const Edit = CreateUnion([Insert, Update, Delete]) +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueDeltaObjectWithSymbolKeyError extends Error { - constructor(public readonly key: unknown) { - super('Cannot diff objects with symbol keys') +// ------------------------------------------------------------------ +export class ValueDeltaError extends TypeBoxError { + constructor(public readonly value: unknown, message: string) { + super(message) } } -export class ValueDeltaUnableToDiffUnknownValue extends Error { +export class ValueDeltaSymbolError extends ValueDeltaError { constructor(public readonly value: unknown) { - super('Unable to create diff edits for unknown value') + super(value, 'Cannot diff objects with symbol keys') } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Command Factory -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function CreateUpdate(path: string, value: unknown): Edit { return { type: 'update', path, value } } @@ -79,29 +86,29 @@ function CreateInsert(path: string, value: unknown): Edit { function CreateDelete(path: string): Edit { return { type: 'delete', path } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Diffing Generators -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function* ObjectType(path: string, current: ObjectType, next: unknown): IterableIterator { if (!IsPlainObject(next)) return yield CreateUpdate(path, next) - const currentKeys = [...Object.keys(current), ...Object.getOwnPropertySymbols(current)] - const nextKeys = [...Object.keys(next), ...Object.getOwnPropertySymbols(next)] + const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] + const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] for (const key of currentKeys) { - if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${String(key)}`, undefined) + if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) + if (IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${globalThis.String(key)}`, undefined) } for (const key of nextKeys) { if (IsUndefined(current[key]) || IsUndefined(next[key])) continue - if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - yield* Visit(`${path}/${String(key)}`, current[key], next[key]) + if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) + yield* Visit(`${path}/${globalThis.String(key)}`, current[key], next[key]) } for (const key of nextKeys) { - if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (IsUndefined(current[key])) yield CreateInsert(`${path}/${String(key)}`, next[key]) + if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) + if (IsUndefined(current[key])) yield CreateInsert(`${path}/${globalThis.String(key)}`, next[key]) } for (const key of currentKeys.reverse()) { - if (IsSymbol(key)) throw new ValueDeltaObjectWithSymbolKeyError(key) - if (IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${String(key)}`) + if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) + if (IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${globalThis.String(key)}`) } } function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIterator { @@ -119,7 +126,7 @@ function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIt } } function* TypedArrayType(path: string, current: TypedArrayType, next: unknown): IterableIterator { - if (!IsTypedArray(next) || current.length !== next.length || Object.getPrototypeOf(current).constructor.name !== Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next) + if (!IsTypedArray(next) || current.length !== next.length || globalThis.Object.getPrototypeOf(current).constructor.name !== globalThis.Object.getPrototypeOf(next).constructor.name) return yield CreateUpdate(path, next) for (let i = 0; i < Math.min(current.length, next.length); i++) { yield* Visit(`${path}/${i}`, current[i], next[i]) } @@ -133,17 +140,17 @@ function* Visit(path: string, current: unknown, next: unknown): IterableIterator if (IsArray(current)) return yield* ArrayType(path, current, next) if (IsTypedArray(current)) return yield* TypedArrayType(path, current, next) if (IsValueType(current)) return yield* ValueType(path, current, next) - throw new ValueDeltaUnableToDiffUnknownValue(current) + throw new ValueDeltaError(current, 'Unable to create diff edits for unknown value') } -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ // Diff -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ export function Diff(current: unknown, next: unknown): Edit[] { return [...Visit('', current, next)] } -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ // Patch -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsRootUpdate(edits: Edit[]): edits is [Update] { return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' } diff --git a/src/value/delta/index.ts b/src/value/delta/index.ts new file mode 100644 index 000000000..3eca3782f --- /dev/null +++ b/src/value/delta/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './delta' diff --git a/src/value/deref.ts b/src/value/deref/deref.ts similarity index 89% rename from src/value/deref.ts rename to src/value/deref/deref.ts index cf9ebd798..e7c0926af 100644 --- a/src/value/deref.ts +++ b/src/value/deref/deref.ts @@ -26,7 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TypeBoxError, TSchema, TRef, TThis } from '../typebox' +import type { TSchema } from '../../type/schema/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import { TypeBoxError } from '../../type/error/index' export class TypeDereferenceError extends TypeBoxError { constructor(public readonly schema: TRef | TThis) { diff --git a/src/value/deref/index.ts b/src/value/deref/index.ts new file mode 100644 index 000000000..d0bf7e45b --- /dev/null +++ b/src/value/deref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './deref' diff --git a/src/value/equal.ts b/src/value/equal/equal.ts similarity index 97% rename from src/value/equal.ts rename to src/value/equal/equal.ts index 558d96f8b..264093959 100644 --- a/src/value/equal.ts +++ b/src/value/equal/equal.ts @@ -26,12 +26,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from './guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' +import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Equality Checks -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function ObjectType(left: ObjectType, right: unknown): boolean { if (!IsPlainObject(right)) return false const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] @@ -53,9 +53,9 @@ function TypedArrayType(left: TypedArrayType, right: unknown): any { function ValueType(left: ValueType, right: unknown): any { return left === right } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Equal -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Returns true if the left value deep-equals the right */ export function Equal(left: T, right: unknown): right is T { if (IsPlainObject(left)) return ObjectType(left, right) diff --git a/src/value/equal/index.ts b/src/value/equal/index.ts new file mode 100644 index 000000000..cf0e42f72 --- /dev/null +++ b/src/value/equal/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './equal' diff --git a/src/value/guard.ts b/src/value/guard/guard.ts similarity index 99% rename from src/value/guard.ts rename to src/value/guard/guard.ts index 5d5bb17ec..92aa187dd 100644 --- a/src/value/guard.ts +++ b/src/value/guard/guard.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Types -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export type ObjectType = Record export type ArrayType = unknown[] export type ValueType = null | undefined | symbol | bigint | number | boolean | string diff --git a/src/value/guard/index.ts b/src/value/guard/index.ts new file mode 100644 index 000000000..36d7e3fa8 --- /dev/null +++ b/src/value/guard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './guard' diff --git a/src/value/hash.ts b/src/value/hash/hash.ts similarity index 95% rename from src/value/hash.ts rename to src/value/hash/hash.ts index cd8935ead..6964af0f5 100644 --- a/src/value/hash.ts +++ b/src/value/hash/hash.ts @@ -26,20 +26,21 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsPlainObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from './guard' +import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsPlainObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index' +import { TypeBoxError } from '../../type/error/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueHashError extends Error { +// ------------------------------------------------------------------ +export class ValueHashError extends TypeBoxError { constructor(public readonly value: unknown) { super(`Unable to hash value`) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // ByteMarker -// -------------------------------------------------------------------------- -export enum ByteMarker { +// ------------------------------------------------------------------ +enum ByteMarker { Undefined, Null, Boolean, @@ -52,27 +53,27 @@ export enum ByteMarker { Symbol, BigInt, } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // State -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ let Accumulator = BigInt('14695981039346656037') const [Prime, Size] = [BigInt('1099511628211'), BigInt('2') ** BigInt('64')] const Bytes = Array.from({ length: 256 }).map((_, i) => BigInt(i)) const F64 = new Float64Array(1) const F64In = new DataView(F64.buffer) const F64Out = new Uint8Array(F64.buffer) -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // NumberToBytes -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function* NumberToBytes(value: number): IterableIterator { const byteCount = value === 0 ? 1 : Math.ceil(Math.floor(Math.log2(value) + 1) / 8) for (let i = 0; i < byteCount; i++) { yield (value >> (8 * (byteCount - 1 - i))) & 0xff } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Hashing Functions -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function ArrayType(value: Array) { FNV1A64(ByteMarker.Array) for (const item of value) { @@ -150,9 +151,9 @@ function FNV1A64(byte: number) { Accumulator = Accumulator ^ Bytes[byte] Accumulator = (Accumulator * Prime) % Size } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Hash -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates a FNV1A-64 non cryptographic hash of the given value */ export function Hash(value: unknown) { Accumulator = BigInt('14695981039346656037') diff --git a/src/value/hash/index.ts b/src/value/hash/index.ts new file mode 100644 index 000000000..a2607845e --- /dev/null +++ b/src/value/hash/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './hash' diff --git a/src/value/index.ts b/src/value/index.ts index e8beb3bbd..eecc44779 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -26,8 +26,56 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +// ------------------------------------------------------------------ +// Value Errors (re-export) +// ------------------------------------------------------------------ export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' -export { Edit, Insert, Update, Delete } from './delta' -export { Mutable } from './mutate' -export { ValuePointer } from './pointer' -export { Value } from './value' +// ------------------------------------------------------------------ +// Value Operators +// ------------------------------------------------------------------ +export { Cast, ValueCastError } from './cast/index' +export { Check } from './check/index' +export { Clean } from './clean/index' +export { Clone } from './clone/index' +export { Convert } from './convert/index' +export { Create, ValueCreateError } from './create/index' +export { Default } from './default/index' +export { Diff, Patch, Edit, Delete, Insert, Update, ValueDeltaError } from './delta/index' +export { Equal } from './equal/index' +export { Hash, ValueHashError } from './hash/index' +export { Mutate, ValueMutateError, type Mutable } from './mutate/index' +export { ValuePointer } from './pointer/index' +export { TransformDecode, TransformEncode, HasTransform, TransformDecodeCheckError, TransformDecodeError, TransformEncodeCheckError, TransformEncodeError } from './transform/index' +// ------------------------------------------------------------------ +// Value Guards +// ------------------------------------------------------------------ +export { + ArrayType, + HasPropertyKey, + IsArray, + IsAsyncIterator, + IsBigInt, + IsBoolean, + IsDate, + IsFunction, + IsInteger, + IsIterator, + IsNull, + IsNumber, + IsObject, + IsPlainObject, + IsPromise, + IsString, + IsSymbol, + IsTypedArray, + IsUint8Array, + IsUndefined, + IsValueType, + type ObjectType, + type TypedArrayType, + type ValueType, +} from './guard/index' +// ------------------------------------------------------------------ +// Value Namespace +// ------------------------------------------------------------------ +export { Value } from './value/index' diff --git a/src/value/mutate/index.ts b/src/value/mutate/index.ts new file mode 100644 index 000000000..cf8bd9248 --- /dev/null +++ b/src/value/mutate/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './mutate' diff --git a/src/value/mutate.ts b/src/value/mutate/mutate.ts similarity index 85% rename from src/value/mutate.ts rename to src/value/mutate/mutate.ts index 42d563c4a..670a02e5d 100644 --- a/src/value/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -26,26 +26,22 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from './guard' -import { ValuePointer } from './pointer' -import { Clone } from './clone' +import { IsPlainObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index' +import { ValuePointer } from '../pointer/index' +import { Clone } from '../clone/index' +import { TypeBoxError } from '../../type/error/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueMutateTypeMismatchError extends Error { - constructor() { - super('Cannot assign due type mismatch of assignable values') +// ------------------------------------------------------------------ +export class ValueMutateError extends TypeBoxError { + constructor(message: string) { + super(message) } } -export class ValueMutateInvalidRootMutationError extends Error { - constructor() { - super('Only object and array types can be mutated at the root level') - } -} -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Mutators -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export type Mutable = { [key: string]: unknown } | unknown[] function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { if (!IsPlainObject(current)) { @@ -97,9 +93,9 @@ function Visit(root: Mutable, path: string, current: unknown, next: unknown) { if (IsPlainObject(next)) return ObjectType(root, path, current, next) if (IsValueType(next)) return ValueType(root, path, current, next) } -// -------------------------------------------------------------------------- -// Mutate -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ +// IsNonMutableValue +// ------------------------------------------------------------------ function IsNonMutableValue(value: unknown): value is Mutable { return IsTypedArray(value) || IsValueType(value) } @@ -110,12 +106,12 @@ function IsMismatchedValue(current: unknown, next: unknown) { (IsArray(current) && IsPlainObject(next)) ) } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Mutate -// -------------------------------------------------------------------------- -/** Performs a deep mutable value assignment while retaining internal references */ +// ------------------------------------------------------------------ +/** `[Mutable]` Performs a deep mutable value assignment while retaining internal references */ export function Mutate(current: Mutable, next: Mutable): void { - if (IsNonMutableValue(current) || IsNonMutableValue(next)) throw new ValueMutateInvalidRootMutationError() - if (IsMismatchedValue(current, next)) throw new ValueMutateTypeMismatchError() + if (IsNonMutableValue(current) || IsNonMutableValue(next)) throw new ValueMutateError('Only object and array types can be mutated at the root level') + if (IsMismatchedValue(current, next)) throw new ValueMutateError('Cannot assign due type mismatch of assignable values') Visit(current, '', current, next) } diff --git a/src/value/pointer.ts b/src/value/pointer.ts deleted file mode 100644 index 995ce8129..000000000 --- a/src/value/pointer.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -// -------------------------------------------------------------------------- -// Errors -// -------------------------------------------------------------------------- -export class ValuePointerRootSetError extends Error { - constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { - super('Cannot set root value') - } -} -export class ValuePointerRootDeleteError extends Error { - constructor(public readonly value: unknown, public readonly path: string) { - super('Cannot delete root value') - } -} -// -------------------------------------------------------------------------- -// ValuePointer -// -------------------------------------------------------------------------- -/** Provides functionality to update values through RFC6901 string pointers */ -export namespace ValuePointer { - function Escape(component: string) { - return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') - } - /** Formats the given pointer into navigable key components */ - export function* Format(pointer: string): IterableIterator { - if (pointer === '') return - let [start, end] = [0, 0] - for (let i = 0; i < pointer.length; i++) { - const char = pointer.charAt(i) - if (char === '/') { - if (i === 0) { - start = i + 1 - } else { - end = i - yield Escape(pointer.slice(start, end)) - start = i + 1 - } - } else { - end = i - } - } - yield Escape(pointer.slice(start)) - } - /** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ - export function Set(value: any, pointer: string, update: unknown): void { - if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) - let [owner, next, key] = [null as any, value, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined) next[component] = {} - owner = next - next = next[component] - key = component - } - owner[key] = update - } - /** Deletes a value at the given pointer */ - export function Delete(value: any, pointer: string): void { - if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) - let [owner, next, key] = [null as any, value as any, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined || next[component] === null) return - owner = next - next = next[component] - key = component - } - if (Array.isArray(owner)) { - const index = parseInt(key) - owner.splice(index, 1) - } else { - delete owner[key] - } - } - /** Returns true if a value exists at the given pointer */ - export function Has(value: any, pointer: string): boolean { - if (pointer === '') return true - let [owner, next, key] = [null as any, value as any, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined) return false - owner = next - next = next[component] - key = component - } - return Object.getOwnPropertyNames(owner).includes(key) - } - /** Gets the value at the given pointer */ - export function Get(value: any, pointer: string): any { - if (pointer === '') return value - let current = value - for (const component of Format(pointer)) { - if (current[component] === undefined) return undefined - current = current[component] - } - return current - } -} diff --git a/src/value/pointer/index.ts b/src/value/pointer/index.ts new file mode 100644 index 000000000..d4071966d --- /dev/null +++ b/src/value/pointer/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as ValuePointer from './pointer' diff --git a/src/value/pointer/pointer.ts b/src/value/pointer/pointer.ts new file mode 100644 index 000000000..e0c463927 --- /dev/null +++ b/src/value/pointer/pointer.ts @@ -0,0 +1,127 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeBoxError } from '../../type/error/index' + +// ------------------------------------------------------------------ +// Errors +// ------------------------------------------------------------------ +export class ValuePointerRootSetError extends TypeBoxError { + constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { + super('Cannot set root value') + } +} +export class ValuePointerRootDeleteError extends TypeBoxError { + constructor(public readonly value: unknown, public readonly path: string) { + super('Cannot delete root value') + } +} +// ------------------------------------------------------------------ +// ValuePointer +// ------------------------------------------------------------------ +/** Provides functionality to update values through RFC6901 string pointers */ +// prettier-ignore +function Escape(component: string) { + return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') +} +/** Formats the given pointer into navigable key components */ +// prettier-ignore +export function* Format(pointer: string): IterableIterator { + if (pointer === '') return + let [start, end] = [0, 0] + for (let i = 0; i < pointer.length; i++) { + const char = pointer.charAt(i) + if (char === '/') { + if (i === 0) { + start = i + 1 + } else { + end = i + yield Escape(pointer.slice(start, end)) + start = i + 1 + } + } else { + end = i + } + } + yield Escape(pointer.slice(start)) +} +/** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ +// prettier-ignore +export function Set(value: any, pointer: string, update: unknown): void { + if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) + let [owner, next, key] = [null as any, value, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) next[component] = {} + owner = next + next = next[component] + key = component + } + owner[key] = update +} +/** Deletes a value at the given pointer */ +// prettier-ignore +export function Delete(value: any, pointer: string): void { + if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined || next[component] === null) return + owner = next + next = next[component] + key = component + } + if (Array.isArray(owner)) { + const index = parseInt(key) + owner.splice(index, 1) + } else { + delete owner[key] + } +} +/** Returns true if a value exists at the given pointer */ +// prettier-ignore +export function Has(value: any, pointer: string): boolean { + if (pointer === '') return true + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) return false + owner = next + next = next[component] + key = component + } + return Object.getOwnPropertyNames(owner).includes(key) +} +/** Gets the value at the given pointer */ +// prettier-ignore +export function Get(value: any, pointer: string): any { + if (pointer === '') return value + let current = value + for (const component of Format(pointer)) { + if (current[component] === undefined) return undefined + current = current[component] + } + return current +} diff --git a/src/value/transform.ts b/src/value/transform.ts deleted file mode 100644 index bb377f9a1..000000000 --- a/src/value/transform.ts +++ /dev/null @@ -1,439 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { IsString, IsPlainObject, IsArray, IsValueType, IsUndefined } from './guard' -import { ValueError } from '../errors/errors' -import { Deref } from './deref' -import { Check } from './check' -import * as Types from '../typebox' - -// ------------------------------------------------------------------------- -// Errors -// ------------------------------------------------------------------------- -export class TransformDecodeCheckError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to decode due to invalid value`) - } -} -export class TransformEncodeCheckError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to encode due to invalid value`) - } -} -export class TransformDecodeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown error'}`) - } -} -export class TransformEncodeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown error'}`) - } -} -// ------------------------------------------------------------------ -// HasTransform -// ------------------------------------------------------------------ -/** Recursively checks a schema for transform codecs */ -export namespace HasTransform { - function TArray(schema: Types.TArray, references: Types.TSchema[]): boolean { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]): boolean { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) - } - function TFunction(schema: Types.TFunction, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) - } - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Types.TypeGuard.TTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) - } - function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TNot(schema: Types.TNot, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.not, references) - } - function TObject(schema: Types.TObject, references: Types.TSchema[]) { - // prettier-ignore - return (Types.TypeGuard.TTransform(schema) || Object.values(schema.properties).some((schema) => Visit(schema, references)) || Types.TypeGuard.TSchema(schema.additionalProperties) && Visit(schema.additionalProperties, references) - ) - } - function TPromise(schema: Types.TPromise, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.item, references) - } - function TRecord(schema: Types.TRecord, references: Types.TSchema[]) { - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[pattern] - return Types.TypeGuard.TTransform(schema) || Visit(property, references) || (Types.TypeGuard.TSchema(schema.additionalProperties) && Types.TypeGuard.TTransform(schema.additionalProperties)) - } - function TRef(schema: Types.TRef, references: Types.TSchema[]) { - if (Types.TypeGuard.TTransform(schema)) return true - return Visit(Deref(schema, references), references) - } - function TThis(schema: Types.TThis, references: Types.TSchema[]) { - if (Types.TypeGuard.TTransform(schema)) return true - return Visit(Deref(schema, references), references) - } - function TTuple(schema: Types.TTuple, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references))) - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) - } - function Visit(schema: Types.TSchema, references: Types.TSchema[]): boolean { - const references_ = IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - if (schema.$id && visited.has(schema.$id)) return false - if (schema.$id) visited.add(schema.$id) - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_) - case 'AsyncIterator': - return TAsyncIterator(schema_, references_) - case 'Constructor': - return TConstructor(schema_, references_) - case 'Function': - return TFunction(schema_, references_) - case 'Intersect': - return TIntersect(schema_, references_) - case 'Iterator': - return TIterator(schema_, references_) - case 'Not': - return TNot(schema_, references_) - case 'Object': - return TObject(schema_, references_) - case 'Promise': - return TPromise(schema_, references_) - case 'Record': - return TRecord(schema_, references_) - case 'Ref': - return TRef(schema_, references_) - case 'This': - return TThis(schema_, references_) - case 'Tuple': - return TTuple(schema_, references_) - case 'Union': - return TUnion(schema_, references_) - default: - return Types.TypeGuard.TTransform(schema) - } - } - const visited = new Set() - /** Returns true if this schema contains a transform codec */ - export function Has(schema: Types.TSchema, references: Types.TSchema[]): boolean { - visited.clear() - return Visit(schema, references) - } -} -// ------------------------------------------------------------------ -// DecodeTransform -// ------------------------------------------------------------------ -/** Decodes a value using transform decoders if available. Does not ensure correct results. */ -export namespace DecodeTransform { - function Default(schema: Types.TSchema, value: any) { - try { - return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Decode(value) : value - } catch (error) { - throw new TransformDecodeError(schema, value, error) - } - } - // prettier-ignore - function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - return (IsArray(value)) - ? Default(schema, value.map((value: any) => Visit(schema.items, references, value))) - : Default(schema, value) - } - // prettier-ignore - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - return Default(schema, Visit(schema.not, references, value)) - } - // prettier-ignore - function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - // prettier-ignore - function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) - } - function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) - } - // prettier-ignore - function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { - return (IsArray(value) && IsArray(schema.items)) - ? Default(schema, schema.items.map((schema, index) => Visit(schema, references, value[index]))) - : Default(schema, value) - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - for (const subschema of schema.anyOf) { - if (!Check(subschema, references, defaulted)) continue - return Visit(subschema, references, defaulted) - } - return defaulted - } - function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_, value) - case 'Intersect': - return TIntersect(schema_, references_, value) - case 'Not': - return TNot(schema_, references_, value) - case 'Object': - return TObject(schema_, references_, value) - case 'Record': - return TRecord(schema_, references_, value) - case 'Ref': - return TRef(schema_, references_, value) - case 'Symbol': - return Default(schema_, value) - case 'This': - return TThis(schema_, references_, value) - case 'Tuple': - return TTuple(schema_, references_, value) - case 'Union': - return TUnion(schema_, references_, value) - default: - return Default(schema_, value) - } - } - export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { - return Visit(schema, references, value) - } -} -// ------------------------------------------------------------------ -// DecodeTransform -// ------------------------------------------------------------------ -/** Encodes a value using transform encoders if available. Does not ensure correct results. */ -export namespace EncodeTransform { - function Default(schema: Types.TSchema, value: any) { - try { - return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Encode(value) : value - } catch (error) { - throw new TransformEncodeError(schema, value, error) - } - } - // prettier-ignore - function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - const defaulted = Default(schema, value) - return IsArray(defaulted) - ? defaulted.map((value: any) => Visit(schema.items, references, value)) - : defaulted - } - // prettier-ignore - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - if (!IsPlainObject(value) || IsValueType(value)) return defaulted - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return key in defaulted - ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } - : value - }, knownProperties) - } - function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - return Default(schema.not, Default(schema, value)) - } - // prettier-ignore - function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - if (!IsPlainObject(value)) return defaulted - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return key in value - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return knownProperties - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - } - // prettier-ignore - function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) as Record - if (!IsPlainObject(value)) return defaulted - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - } - function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) - } - function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) - } - function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { - const value1 = Default(schema, value) - return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { - // test value against union variants - for (const subschema of schema.anyOf) { - if (!Check(subschema, references, value)) continue - const value1 = Visit(subschema, references, value) - return Default(schema, value1) - } - // test transformed value against union variants - for (const subschema of schema.anyOf) { - const value1 = Visit(subschema, references, value) - if (!Check(schema, references, value1)) continue - return Default(schema, value1) - } - return Default(schema, value) - } - function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_, value) - case 'Intersect': - return TIntersect(schema_, references_, value) - case 'Not': - return TNot(schema_, references_, value) - case 'Object': - return TObject(schema_, references_, value) - case 'Record': - return TRecord(schema_, references_, value) - case 'Ref': - return TRef(schema_, references_, value) - case 'This': - return TThis(schema_, references_, value) - case 'Tuple': - return TTuple(schema_, references_, value) - case 'Union': - return TUnion(schema_, references_, value) - default: - return Default(schema_, value) - } - } - export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { - return Visit(schema, references, value) - } -} diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts new file mode 100644 index 000000000..8908c9d9a --- /dev/null +++ b/src/value/transform/decode.ts @@ -0,0 +1,216 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, TransformKind } from '../../type/symbols/index' +import { TypeBoxError } from '../../type/error/index' +import { ValueError } from '../../errors/index' +import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { Index } from '../../type/indexed/index' +import { Deref } from '../deref/index' +import { Check } from '../check/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TNot } from '../../type/not/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTransform, IsSchema } from '../../type/guard/type' +// ------------------------------------------------------------------ +// Errors +// ------------------------------------------------------------------ +// thrown externally +export class TransformDecodeCheckError extends TypeBoxError { + constructor(public readonly schema: TSchema, public readonly value: unknown, public readonly error: ValueError) { + super(`Unable to decode due to invalid value`) + } +} +export class TransformDecodeError extends TypeBoxError { + constructor(public readonly schema: TSchema, public readonly value: unknown, error: any) { + super(`${error instanceof Error ? error.message : 'Unknown error'}`) + } +} +// ------------------------------------------------------------------ +// Decode +// ------------------------------------------------------------------ +// prettier-ignore +function Default(schema: TSchema, value: any) { + try { + return IsTransform(schema) ? schema[TransformKind].Decode(value) : value + } catch (error) { + throw new TransformDecodeError(schema, value, error) + } +} +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[], value: any): any { + return (IsArray(value)) + ? Default(schema, value.map((value: any) => Visit(schema.items, references, value))) + : Default(schema, value) +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { + if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) + const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(Index(schema, [key]), references, value[key]) } + : value + }, value) + if (!IsTransform(schema.unevaluatedProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const unevaluatedProperties = schema.unevaluatedProperties as TSchema + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) +} +function FromNot(schema: TNot, references: TSchema[], value: any) { + return Default(schema, Visit(schema.not, references, value)) +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[], value: any) { + if (!IsPlainObject(value)) return Default(schema, value) + const knownKeys = KeyOfPropertyKeys(schema) + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + : value + }, value) + if (!IsSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const additionalProperties = schema.additionalProperties as TSchema + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) +} +// prettier-ignore +function FromRecord(schema: TRecord, references: TSchema[], value: any) { + if (!IsPlainObject(value)) return Default(schema, value) + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const knownKeys = new RegExp(pattern) + const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { + return knownKeys.test(key) + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + : value + }, value) + if (!IsSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const additionalProperties = schema.additionalProperties as TSchema + const unknownProperties = unknownKeys.reduce((value, key) => { + return !knownKeys.test(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) + return Default(schema, unknownProperties) +} +// prettier-ignore +function FromRef(schema: TRef, references: TSchema[], value: any) { + const target = Deref(schema, references) + return Default(schema, Visit(target, references, value)) +} +// prettier-ignore +function FromThis(schema: TThis, references: TSchema[], value: any) { + const target = Deref(schema, references) + return Default(schema, Visit(target, references, value)) +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[], value: any) { + return (IsArray(value) && IsArray(schema.items)) + ? Default(schema, schema.items.map((schema, index) => Visit(schema, references, value[index]))) + : Default(schema, value) +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[], value: any) { + for (const subschema of schema.anyOf) { + if (!Check(subschema, references, value)) continue + // note: ensure interior is decoded first + const decoded = Visit(subschema, references, value) + return Default(schema, decoded) + } + return Default(schema, value) +} +// prettier-ignore +function Visit(schema: TSchema, references: TSchema[], value: any): any { + const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Kind]) { + case 'Array': + return FromArray(schema_, references_, value) + case 'Intersect': + return FromIntersect(schema_, references_, value) + case 'Not': + return FromNot(schema_, references_, value) + case 'Object': + return FromObject(schema_, references_, value) + case 'Record': + return FromRecord(schema_, references_, value) + case 'Ref': + return FromRef(schema_, references_, value) + case 'Symbol': + return Default(schema_, value) + case 'This': + return FromThis(schema_, references_, value) + case 'Tuple': + return FromTuple(schema_, references_, value) + case 'Union': + return FromUnion(schema_, references_, value) + default: + return Default(schema_, value) + } +} +/** + * `[Internal]` Decodes the value and returns the result. This function requires that + * the caller `Check` the value before use. Passing unchecked values may result in + * undefined behavior. Refer to the `Value.Decode()` for implementation details. + */ +export function TransformDecode(schema: TSchema, references: TSchema[], value: unknown): unknown { + return Visit(schema, references, value) +} diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts new file mode 100644 index 000000000..e91057e94 --- /dev/null +++ b/src/value/transform/encode.ts @@ -0,0 +1,223 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Kind, TransformKind } from '../../type/symbols/index' +import { TypeBoxError } from '../../type/error/index' +import { ValueError } from '../../errors/index' +import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { Index } from '../../type/indexed/index' +import { Deref } from '../deref/index' +import { Check } from '../check/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TNot } from '../../type/not/index' +import type { TObject } from '../../type/object/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTransform, IsSchema } from '../../type/guard/type' +// ------------------------------------------------------------------ +// Errors +// ------------------------------------------------------------------ +export class TransformEncodeCheckError extends TypeBoxError { + constructor(public readonly schema: TSchema, public readonly value: unknown, public readonly error: ValueError) { + super(`Unable to encode due to invalid value`) + } +} +export class TransformEncodeError extends TypeBoxError { + constructor(public readonly schema: TSchema, public readonly value: unknown, error: any) { + super(`${error instanceof Error ? error.message : 'Unknown error'}`) + } +} +// ------------------------------------------------------------------ +// Encode +// ------------------------------------------------------------------ +// prettier-ignore +function Default(schema: TSchema, value: any) { + try { + return IsTransform(schema) ? schema[TransformKind].Encode(value) : value + } catch (error) { + throw new TransformEncodeError(schema, value, error) + } +} +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[], value: any): any { + const defaulted = Default(schema, value) + return IsArray(defaulted) + ? defaulted.map((value: any) => Visit(schema.items, references, value)) + : defaulted +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { + const defaulted = Default(schema, value) + if (!IsPlainObject(value) || IsValueType(value)) return defaulted + const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownProperties = knownKeys.reduce((value, key) => { + return key in defaulted + ? { ...value, [key]: Visit(Index(schema, [key]), references, value[key]) } + : value + }, defaulted) + if (!IsTransform(schema.unevaluatedProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const unevaluatedProperties = schema.unevaluatedProperties as TSchema + return unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + : value + }, knownProperties) +} +// prettier-ignore +function FromNot(schema: TNot, references: TSchema[], value: any) { + return Default(schema.not, Default(schema, value)) +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[], value: any) { + const defaulted = Default(schema, value) + if (!IsPlainObject(value)) return defaulted + const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownProperties = knownKeys.reduce((value, key) => { + return key in value + ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + : value + }, defaulted) + if (!IsSchema(schema.additionalProperties)) { + return knownProperties + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const additionalProperties = schema.additionalProperties as TSchema + return unknownKeys.reduce((value, key) => { + return !knownKeys.includes(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) +} +// prettier-ignore +function FromRecord(schema: TRecord, references: TSchema[], value: any) { + const defaulted = Default(schema, value) as Record + if (!IsPlainObject(value)) return defaulted + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const knownKeys = new RegExp(pattern) + const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { + return knownKeys.test(key) + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + : value + }, defaulted) + if (!IsSchema(schema.additionalProperties)) { + return Default(schema, knownProperties) + } + const unknownKeys = Object.getOwnPropertyNames(knownProperties) + const additionalProperties = schema.additionalProperties as TSchema + return unknownKeys.reduce((value, key) => { + return !knownKeys.test(key) + ? { ...value, [key]: Default(additionalProperties, value[key]) } + : value + }, knownProperties) +} +// prettier-ignore +function FromRef(schema: TRef, references: TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) +} +// prettier-ignore +function FromThis(schema: TThis, references: TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[], value: any) { + const value1 = Default(schema, value) + return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[], value: any) { + // test value against union variants + for (const subschema of schema.anyOf) { + if (!Check(subschema, references, value)) continue + const value1 = Visit(subschema, references, value) + return Default(schema, value1) + } + // test transformed value against union variants + for (const subschema of schema.anyOf) { + const value1 = Visit(subschema, references, value) + if (!Check(schema, references, value1)) continue + return Default(schema, value1) + } + return Default(schema, value) +} +// prettier-ignore +function Visit(schema: TSchema, references: TSchema[], value: any): any { + const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const schema_ = schema as any + switch (schema[Kind]) { + case 'Array': + return FromArray(schema_, references_, value) + case 'Intersect': + return FromIntersect(schema_, references_, value) + case 'Not': + return FromNot(schema_, references_, value) + case 'Object': + return FromObject(schema_, references_, value) + case 'Record': + return FromRecord(schema_, references_, value) + case 'Ref': + return FromRef(schema_, references_, value) + case 'This': + return FromThis(schema_, references_, value) + case 'Tuple': + return FromTuple(schema_, references_, value) + case 'Union': + return FromUnion(schema_, references_, value) + default: + return Default(schema_, value) + } +} +/** + * `[Internal]` Encodes the value and returns the result. This function expects the + * caller to pass a statically checked value. This function does not check the encoded + * result, meaning the result should be passed to `Check` before use. Refer to the + * `Value.Encode()` function for implementation details. + */ +export function TransformEncode(schema: TSchema, references: TSchema[], value: unknown): unknown { + return Visit(schema, references, value) +} diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts new file mode 100644 index 000000000..437fad1d6 --- /dev/null +++ b/src/value/transform/has.ts @@ -0,0 +1,167 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Deref } from '../deref/index' +import { Kind } from '../../type/symbols/index' + +import type { TSchema } from '../../type/schema/index' +import type { TArray } from '../../type/array/index' +import type { TAsyncIterator } from '../../type/async-iterator/index' +import type { TConstructor } from '../../type/constructor/index' +import type { TFunction } from '../../type/function/index' +import type { TIntersect } from '../../type/intersect/index' +import type { TIterator } from '../../type/iterator/index' +import type { TNot } from '../../type/not/index' +import type { TObject } from '../../type/object/index' +import type { TPromise } from '../../type/promise/index' +import type { TRecord } from '../../type/record/index' +import type { TRef } from '../../type/ref/index' +import type { TThis } from '../../type/recursive/index' +import type { TTuple } from '../../type/tuple/index' +import type { TUnion } from '../../type/union/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsTransform, IsSchema } from '../../type/guard/type' +// ------------------------------------------------------------------ +// ValueGuard +// ------------------------------------------------------------------ +import { IsString, IsUndefined } from '../guard/index' + +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[]): boolean { + return IsTransform(schema) || Visit(schema.items, references) +} +// prettier-ignore +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]): boolean { + return IsTransform(schema) || Visit(schema.items, references) +} +// prettier-ignore +function FromConstructor(schema: TConstructor, references: TSchema[]) { + return IsTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function FromFunction(schema: TFunction, references: TSchema[]) { + return IsTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[]) { + return IsTransform(schema) || IsTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function FromIterator(schema: TIterator, references: TSchema[]) { + return IsTransform(schema) || Visit(schema.items, references) +} +// prettier-ignore +function FromNot(schema: TNot, references: TSchema[]) { + return IsTransform(schema) || Visit(schema.not, references) +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[]) { + return ( + IsTransform(schema) || + Object.values(schema.properties).some((schema) => Visit(schema, references)) || + ( + IsSchema(schema.additionalProperties) && Visit(schema.additionalProperties, references) + ) + ) +} +// prettier-ignore +function FromPromise(schema: TPromise, references: TSchema[]) { + return IsTransform(schema) || Visit(schema.item, references) +} +// prettier-ignore +function FromRecord(schema: TRecord, references: TSchema[]) { + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[pattern] + return IsTransform(schema) || Visit(property, references) || (IsSchema(schema.additionalProperties) && IsTransform(schema.additionalProperties)) +} +// prettier-ignore +function FromRef(schema: TRef, references: TSchema[]) { + if (IsTransform(schema)) return true + return Visit(Deref(schema, references), references) +} +// prettier-ignore +function FromThis(schema: TThis, references: TSchema[]) { + if (IsTransform(schema)) return true + return Visit(Deref(schema, references), references) +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[]) { + return IsTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references))) +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[]) { + return IsTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function Visit(schema: TSchema, references: TSchema[]): boolean { + const references_ = IsString(schema.$id) ? [...references, schema] : references + const schema_ = schema as any + if (schema.$id && visited.has(schema.$id)) return false + if (schema.$id) visited.add(schema.$id) + switch (schema[Kind]) { + case 'Array': + return FromArray(schema_, references_) + case 'AsyncIterator': + return FromAsyncIterator(schema_, references_) + case 'Constructor': + return FromConstructor(schema_, references_) + case 'Function': + return FromFunction(schema_, references_) + case 'Intersect': + return FromIntersect(schema_, references_) + case 'Iterator': + return FromIterator(schema_, references_) + case 'Not': + return FromNot(schema_, references_) + case 'Object': + return FromObject(schema_, references_) + case 'Promise': + return FromPromise(schema_, references_) + case 'Record': + return FromRecord(schema_, references_) + case 'Ref': + return FromRef(schema_, references_) + case 'This': + return FromThis(schema_, references_) + case 'Tuple': + return FromTuple(schema_, references_) + case 'Union': + return FromUnion(schema_, references_) + default: + return IsTransform(schema) + } +} +const visited = new Set() +/** Returns true if this schema contains a transform codec */ +export function HasTransform(schema: TSchema, references: TSchema[]): boolean { + visited.clear() + return Visit(schema, references) +} diff --git a/src/value/transform/index.ts b/src/value/transform/index.ts new file mode 100644 index 000000000..f821f6d29 --- /dev/null +++ b/src/value/transform/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './decode' +export * from './encode' +export * from './has' diff --git a/src/value/value.ts b/src/value/value.ts deleted file mode 100644 index 9cf27168b..000000000 --- a/src/value/value.ts +++ /dev/null @@ -1,129 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as ValueErrors from '../errors/index' -import * as ValueMutate from './mutate' -import * as ValueHash from './hash' -import * as ValueEqual from './equal' -import * as ValueCast from './cast' -import * as ValueClone from './clone' -import * as ValueConvert from './convert' -import * as ValueCreate from './create' -import * as ValueCheck from './check' -import * as ValueDelta from './delta' -import * as ValueTransform from './transform' -import * as Types from '../typebox' - -/** Functions to perform structural operations on JavaScript values */ -export namespace Value { - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(schema: T, value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(...args: any[]) { - return ValueCast.Cast.apply(ValueCast, args as any) - } - /** Creates a value from the given type and references */ - export function Create(schema: T, references: Types.TSchema[]): Types.Static - /** Creates a value from the given type */ - export function Create(schema: T): Types.Static - /** Creates a value from the given type */ - export function Create(...args: any[]) { - return ValueCreate.Create.apply(ValueCreate, args as any) - } - /** Returns true if the value matches the given type and references */ - export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static - /** Returns true if the value matches the given type */ - export function Check(schema: T, value: unknown): value is Types.Static - /** Returns true if the value matches the given type */ - export function Check(...args: any[]) { - return ValueCheck.Check.apply(ValueCheck, args as any) - } - /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ - export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown - /** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ - export function Convert(schema: T, value: unknown): unknown - /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ - export function Convert(...args: any[]) { - return ValueConvert.Convert.apply(ValueConvert, args as any) - } - /** Returns a structural clone of the given value */ - export function Clone(value: T): T { - return ValueClone.Clone(value) - } - /** Decodes a value or throws if error */ - export function Decode>(schema: T, references: Types.TSchema[], value: unknown): R - /** Decodes a value or throws if error */ - export function Decode>(schema: T, value: unknown): R - /** Decodes a value or throws if error */ - export function Decode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - if (!Check(schema, references, value)) throw new ValueTransform.TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return ValueTransform.DecodeTransform.Decode(schema, references, value) - } - /** Encodes a value or throws if error */ - export function Encode>(schema: T, references: Types.TSchema[], value: unknown): R - /** Encodes a value or throws if error */ - export function Encode>(schema: T, value: unknown): R - /** Encodes a value or throws if error */ - export function Encode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value) - if (!Check(schema, references, encoded)) throw new ValueTransform.TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) - return encoded - } - /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrors.ValueErrorIterator - /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, value: unknown): ValueErrors.ValueErrorIterator - /** Returns an iterator for each error in this value. */ - export function Errors(...args: any[]) { - return ValueErrors.Errors.apply(ValueErrors, args as any) - } - /** Returns true if left and right values are structurally equal */ - export function Equal(left: T, right: unknown): right is T { - return ValueEqual.Equal(left, right) - } - /** Returns edits to transform the current value into the next value */ - export function Diff(current: unknown, next: unknown): ValueDelta.Edit[] { - return ValueDelta.Diff(current, next) - } - /** Returns a FNV1A-64 non cryptographic hash of the given value */ - export function Hash(value: unknown): bigint { - return ValueHash.Hash(value) - } - /** Returns a new value with edits applied to the given value */ - export function Patch(current: unknown, edits: ValueDelta.Edit[]): T { - return ValueDelta.Patch(current, edits) as T - } - /** Performs a deep mutable value assignment while retaining internal references. */ - export function Mutate(current: ValueMutate.Mutable, next: ValueMutate.Mutable): void { - ValueMutate.Mutate(current, next) - } -} diff --git a/src/value/value/index.ts b/src/value/value/index.ts new file mode 100644 index 000000000..3af158d90 --- /dev/null +++ b/src/value/value/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Value from './value' diff --git a/src/value/value/value.ts b/src/value/value/value.ts new file mode 100644 index 000000000..6be317787 --- /dev/null +++ b/src/value/value/value.ts @@ -0,0 +1,146 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' +import { Mutate as MutateValue, type Mutable } from '../mutate/index' +import { Hash as HashValue } from '../hash/index' +import { Equal as EqualValue } from '../equal/index' +import { Cast as CastValue } from '../cast/index' +import { Clone as CloneValue } from '../clone/index' +import { Convert as ConvertValue } from '../convert/index' +import { Create as CreateValue } from '../create/index' +import { Clean as CleanValue } from '../clean/index' +import { Check as CheckValue } from '../check/index' +import { Default as DefaultValue } from '../default/index' +import { Diff as DiffValue, Patch as PatchValue, Edit } from '../delta/index' +import { Errors as ValueErrors, ValueErrorIterator } from '../../errors/index' + +import type { TSchema } from '../../type/schema/index' +import type { Static, StaticDecode, StaticEncode } from '../../type/static/index' + +/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ +export function Cast(schema: T, references: TSchema[], value: unknown): Static +/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ +export function Cast(schema: T, value: unknown): Static +/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ +export function Cast(...args: any[]) { + return CastValue.apply(CastValue, args as any) +} +/** Creates a value from the given type and references */ +export function Create(schema: T, references: TSchema[]): Static +/** Creates a value from the given type */ +export function Create(schema: T): Static +/** Creates a value from the given type */ +export function Create(...args: any[]) { + return CreateValue.apply(CreateValue, args as any) +} +/** Returns true if the value matches the given type and references */ +export function Check(schema: T, references: TSchema[], value: unknown): value is Static +/** Returns true if the value matches the given type */ +export function Check(schema: T, value: unknown): value is Static +/** Returns true if the value matches the given type */ +export function Check(...args: any[]) { + return CheckValue.apply(CheckValue, args as any) +} +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T, value: unknown): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(...args: any[]) { + return CleanValue.apply(CleanValue, args as any) +} +/** Converts any type mismatched values to their target type if a reasonable conversion is possible */ +export function Convert(schema: T, references: TSchema[], value: unknown): unknown +/** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ +export function Convert(schema: T, value: unknown): unknown +/** Converts any type mismatched values to their target type if a reasonable conversion is possible */ +export function Convert(...args: any[]) { + return ConvertValue.apply(ConvertValue, args as any) +} +/** Returns a structural clone of the given value */ +export function Clone(value: T): T { + return CloneValue(value) +} +/** Decodes a value or throws if error */ +export function Decode>(schema: T, references: TSchema[], value: unknown): R +/** Decodes a value or throws if error */ +export function Decode>(schema: T, value: unknown): R +/** Decodes a value or throws if error */ +export function Decode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) + return TransformDecode(schema, references, value) +} +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(...args: any[]) { + return DefaultValue.apply(DefaultValue, args as any) +} +/** Encodes a value or throws if error */ +export function Encode>(schema: T, references: TSchema[], value: unknown): R +/** Encodes a value or throws if error */ +export function Encode>(schema: T, value: unknown): R +/** Encodes a value or throws if error */ +export function Encode(...args: any[]) { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + const encoded = TransformEncode(schema, references, value) + if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) + return encoded +} +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, references: TSchema[], value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(...args: any[]) { + return ValueErrors.apply(ValueErrors, args as any) +} +/** Returns true if left and right values are structurally equal */ +export function Equal(left: T, right: unknown): right is T { + return EqualValue(left, right) +} +/** Returns edits to transform the current value into the next value */ +export function Diff(current: unknown, next: unknown): Edit[] { + return DiffValue(current, next) +} +/** Returns a FNV1A-64 non cryptographic hash of the given value */ +export function Hash(value: unknown): bigint { + return HashValue(value) +} +/** Returns a new value with edits applied to the given value */ +export function Patch(current: unknown, edits: Edit[]): T { + return PatchValue(current, edits) as T +} +/** `[Mutable]` Performs a deep mutable value assignment while retaining internal references. */ +export function Mutate(current: Mutable, next: Mutable): void { + MutateValue(current, next) +} diff --git a/benchmark/compression/index.ts b/task/benchmark/compression/index.ts similarity index 71% rename from benchmark/compression/index.ts rename to task/benchmark/compression/index.ts index 521031315..546145c70 100644 --- a/benchmark/compression/index.ts +++ b/task/benchmark/compression/index.ts @@ -3,9 +3,9 @@ import { statSync, readdirSync } from 'fs' import { basename, extname } from 'path' export async function measure(test: string) { - await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression`) + await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression`) const compiled = statSync(`target/benchmark/compression/${test}.js`) - await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`) + await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`) const minified = statSync(`target/benchmark/compression/${test}.js`) return { test: test.padEnd(20), @@ -16,7 +16,7 @@ export async function measure(test: string) { } export async function compression() { - const tests = readdirSync('benchmark/compression/module').map((name) => basename(name, extname(name))) + const tests = readdirSync('task/benchmark/compression/module').map((name) => basename(name, extname(name))) const results = await Promise.all(tests.map((test) => measure(test))) const present = results.reduce((acc, c) => { return { ...acc, [c.test.replace(/-/g, '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } } diff --git a/benchmark/compression/module/typebox-compiler.ts b/task/benchmark/compression/module/typebox-compiler.ts similarity index 100% rename from benchmark/compression/module/typebox-compiler.ts rename to task/benchmark/compression/module/typebox-compiler.ts diff --git a/benchmark/compression/module/typebox-errors.ts b/task/benchmark/compression/module/typebox-errors.ts similarity index 100% rename from benchmark/compression/module/typebox-errors.ts rename to task/benchmark/compression/module/typebox-errors.ts diff --git a/benchmark/compression/module/typebox-system.ts b/task/benchmark/compression/module/typebox-system.ts similarity index 100% rename from benchmark/compression/module/typebox-system.ts rename to task/benchmark/compression/module/typebox-system.ts diff --git a/benchmark/compression/module/typebox-value.ts b/task/benchmark/compression/module/typebox-value.ts similarity index 100% rename from benchmark/compression/module/typebox-value.ts rename to task/benchmark/compression/module/typebox-value.ts diff --git a/benchmark/compression/module/typebox.ts b/task/benchmark/compression/module/typebox.ts similarity index 100% rename from benchmark/compression/module/typebox.ts rename to task/benchmark/compression/module/typebox.ts diff --git a/benchmark/index.ts b/task/benchmark/index.ts similarity index 100% rename from benchmark/index.ts rename to task/benchmark/index.ts diff --git a/task/benchmark/measurement/index.ts b/task/benchmark/measurement/index.ts new file mode 100644 index 000000000..5976be49d --- /dev/null +++ b/task/benchmark/measurement/index.ts @@ -0,0 +1,5 @@ +import { shell } from '@sinclair/hammer' + +export async function measurement() { + await shell(`hammer run task/benchmark/measurement/module/index.ts --dist target/benchmark/measurement`) +} diff --git a/benchmark/measurement/module/benchmark.ts b/task/benchmark/measurement/module/benchmark.ts similarity index 100% rename from benchmark/measurement/module/benchmark.ts rename to task/benchmark/measurement/module/benchmark.ts diff --git a/benchmark/measurement/module/cases.ts b/task/benchmark/measurement/module/cases.ts similarity index 97% rename from benchmark/measurement/module/cases.ts rename to task/benchmark/measurement/module/cases.ts index 8325c80ba..fe2ff6a20 100644 --- a/benchmark/measurement/module/cases.ts +++ b/task/benchmark/measurement/module/cases.ts @@ -11,7 +11,7 @@ export namespace Cases { export const Primitive_String = Type.String() - export const Primitive_String_Pattern = Type.RegExp(/foo/, { default: 'foo' }) + export const Primitive_String_Pattern = Type.String({ pattern: 'foo', default: 'foo' }) export const Primitive_Boolean = Type.Boolean() diff --git a/benchmark/measurement/module/check.ts b/task/benchmark/measurement/module/check.ts similarity index 93% rename from benchmark/measurement/module/check.ts rename to task/benchmark/measurement/module/check.ts index a6e2e4324..99d3ced51 100644 --- a/benchmark/measurement/module/check.ts +++ b/task/benchmark/measurement/module/check.ts @@ -34,7 +34,7 @@ export namespace CheckBenchmark { export function* Execute() { for (const [type, schema] of Object.entries(Cases)) { - if (!TypeGuard.TSchema(schema)) throw Error('Invalid TypeBox schema') + if (!TypeGuard.IsSchema(schema)) throw Error('Invalid TypeBox schema') yield Measure(type, schema) } } diff --git a/benchmark/measurement/module/compile.ts b/task/benchmark/measurement/module/compile.ts similarity index 95% rename from benchmark/measurement/module/compile.ts rename to task/benchmark/measurement/module/compile.ts index 98b61c006..ee0ade89c 100644 --- a/benchmark/measurement/module/compile.ts +++ b/task/benchmark/measurement/module/compile.ts @@ -21,7 +21,7 @@ export namespace CompileBenchmark { export function* Execute() { for (const [type, schema] of Object.entries(Cases)) { - if (!TypeGuard.TSchema(schema)) throw Error('Invalid TypeBox schema') + if (!TypeGuard.IsSchema(schema)) throw Error('Invalid TypeBox schema') // ------------------------------------------------------------------------------- // Note: it is not possible to benchmark recursive schemas as ajv will cache and // track duplicate $id (resulting in compile error). It is not possible to ammend diff --git a/benchmark/measurement/module/index.ts b/task/benchmark/measurement/module/index.ts similarity index 100% rename from benchmark/measurement/module/index.ts rename to task/benchmark/measurement/module/index.ts diff --git a/benchmark/measurement/module/result.ts b/task/benchmark/measurement/module/result.ts similarity index 100% rename from benchmark/measurement/module/result.ts rename to task/benchmark/measurement/module/result.ts diff --git a/task/build/common/remove-notices.ts b/task/build/common/remove-notices.ts new file mode 100644 index 000000000..15c9688eb --- /dev/null +++ b/task/build/common/remove-notices.ts @@ -0,0 +1,82 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Path from 'node:path' +import * as Fs from 'node:fs' + +// ---------------------------------------------------------------------- +// Remove Module Level MIT Notice on Package Distribution +// +// The MIT copyright notice the unnecessarily increases the distribution +// size of the package, this code removes it. The MIT license is available +// in the package root. +// +// ---------------------------------------------------------------------- +// prettier-ignore +function escape(content: string) { + return content.split('').map((c) => `\\${c}`).join('') +} +// prettier-ignore +function removeNotice(content: string): string { + const open = escape('/*--------------------------------------------------------------------------') + const close = escape('---------------------------------------------------------------------------*/') + const critera = 'Permission is hereby granted, free of charge' + const pattern = new RegExp(`${open}[\\s\\S]*?${close}`, 'gm') + while (true) { + const match = pattern.exec(content) + if (match === null) return content.trimStart() + if (!match[0].includes(critera)) continue + content = content.replace(match[0], '') + } +} +// ------------------------------------------------------------------ +// Directory Enumeration +// ------------------------------------------------------------------ +// prettier-ignore +function processFile(sourcePath: string) { + const sourceContent = Fs.readFileSync(sourcePath, 'utf-8') + const targetContent = removeNotice(sourceContent) + Fs.writeFileSync(sourcePath, targetContent) +} +// prettier-ignore +function processSourcePath(sourcePath: string) { + const stat = Fs.statSync(sourcePath) + if(stat.isDirectory()) return readDirectory(sourcePath) + if(stat.isFile()) return processFile(sourcePath) +} +// prettier-ignore +function readDirectory(sourceDirectory: string) { + for(const entry of Fs.readdirSync(sourceDirectory)) { + const sourcePath = Path.join(sourceDirectory, entry) + processSourcePath(sourcePath) + } +} +/** Removes the MIT copyright notices from each source file in the given directory */ +export function removeNotices(sourceDirectory: string) { + readDirectory(sourceDirectory) +} diff --git a/task/build/import/build.ts b/task/build/import/build.ts new file mode 100644 index 000000000..5a423c1cc --- /dev/null +++ b/task/build/import/build.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { removeNotices } from '../common/remove-notices' +import { convertToEsm } from './convert-to-esm' +import { compile } from './compile' + +/** Builds the ESM version of this package */ +export async function build(target: string) { + console.log('building...import') + const buildTarget = `${target}/build/import` + await compile(buildTarget) + await convertToEsm(buildTarget) + await removeNotices(buildTarget) +} diff --git a/task/build/import/compile.ts b/task/build/import/compile.ts new file mode 100644 index 000000000..05a0a10b8 --- /dev/null +++ b/task/build/import/compile.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +declare function shell(command: string): Promise + +// prettier-ignore +export async function compile(target: string) { + const options = [ + `--outDir ${target}`, + '--target ESNext', + '--module ESNext', + '--declaration', + ].join(' ') + await shell(`tsc -p ./src/tsconfig.json ${options}`) +} diff --git a/task/build/import/convert-to-esm.ts b/task/build/import/convert-to-esm.ts new file mode 100644 index 000000000..1db93a61f --- /dev/null +++ b/task/build/import/convert-to-esm.ts @@ -0,0 +1,110 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Path from 'node:path' +import * as Fs from 'node:fs' + +// ------------------------------------------------------------------ +// Specifier Rewrite +// ------------------------------------------------------------------ + +// prettier-ignore +function replaceInlineImportSpecifiers(content: string): string { + const pattern = /import\((.*?)\)/g + while (true) { + const match = pattern.exec(content) + if (match === null) return content + const captured = match[1] + if(captured.includes('.mjs')) continue + const specifier = captured.slice(1, captured.length - 1) + content = content.replace(captured, `"${specifier}.mjs"`) + } +} +// prettier-ignore +function replaceExportSpecifiers(content: string): string { + const pattern = /(export|import)(.*) from ('(.*)');/g + while(true) { + const match = pattern.exec(content) + if(match === null) return content + const captured = match[3] + const specifier = captured.slice(1, captured.length - 1) + content = content.replace(captured, `'${specifier}.mjs'`) + } +} +function replaceSpecifiers(content: string): string { + const pass1 = replaceExportSpecifiers(content) + const pass2 = replaceInlineImportSpecifiers(pass1) + return pass2 +} + +// ------------------------------------------------------------------ +// ConvertToEsm +// ------------------------------------------------------------------ +// prettier-ignore +function shouldProcess(sourcePath: string) { + const extname = Path.extname(sourcePath) + return ['.js', '.ts'].includes(extname) +} +// prettier-ignore +function newExtension(extname: string) { + return ( + extname === '.js' ? '.mjs' : + extname === '.ts' ? '.mts' : + extname + ) +} +// prettier-ignore +function processFile(sourcePath: string) { + if(!shouldProcess(sourcePath)) return + const extname = Path.extname(sourcePath) + const dirname = Path.dirname(sourcePath) + const basename = Path.basename(sourcePath, extname) + const new_extname = newExtension(extname) + const sourceContent = Fs.readFileSync(sourcePath, 'utf-8') + const targetContent = replaceSpecifiers(sourceContent) + const targetPath = `${Path.join(dirname, basename)}${new_extname}` + Fs.writeFileSync(sourcePath, targetContent) + Fs.renameSync(sourcePath, targetPath) +} +// prettier-ignore +function processSourcePath(sourcePath: string) { + const stat = Fs.statSync(sourcePath) + if(stat.isDirectory()) return readDirectory(sourcePath) + if(stat.isFile()) return processFile(sourcePath) +} +// prettier-ignore +function readDirectory(sourceDirectory: string) { + for(const entry of Fs.readdirSync(sourceDirectory)) { + const sourcePath = Path.join(sourceDirectory, entry) + processSourcePath(sourcePath) + } +} +/** Converts the JavaScript and TypeScript declaration modules in the given source directory to use .mjs extensions */ +export function convertToEsm(sourceDirectory: string) { + readDirectory(sourceDirectory) +} diff --git a/task/build/index.ts b/task/build/index.ts new file mode 100644 index 000000000..fd7cf77da --- /dev/null +++ b/task/build/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Import from './import/build' +export * as Require from './require/build' +export * as Redirect from './redirect/build' diff --git a/task/build/redirect/build.ts b/task/build/redirect/build.ts new file mode 100644 index 000000000..9d34cc8cb --- /dev/null +++ b/task/build/redirect/build.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { createPackageJson } from './create-package-json' +import { createRedirectPaths } from './create-redirect-paths' + +/** Builds package.json and redirect directories */ +export async function build(target: string) { + console.log('building...redirect') + const submodules = ['compiler', 'errors', 'system', 'type', 'value'] + await createPackageJson(target, submodules) + await createRedirectPaths(target, submodules) +} diff --git a/task/build/redirect/create-package-json.ts b/task/build/redirect/create-package-json.ts new file mode 100644 index 000000000..5b36ce927 --- /dev/null +++ b/task/build/redirect/create-package-json.ts @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Fs from 'node:fs' +import * as Path from 'node:path' + +// prettier-ignore +export function createPackageJson(target: string, submodules: string[]) { + const content = JSON.stringify(resolvePackageJson(submodules), null, 2) + const targetPath = Path.join(target, 'package.json') + const targetDir = Path.dirname(targetPath) + Fs.mkdirSync(targetDir, { recursive: true }) + Fs.writeFileSync(targetPath, content, 'utf-8') +} +// prettier-ignore +function resolvePackageJson(submodules: string[]) { + return { + ...resolveMetadata(), + ...resolveExports(submodules) + } +} +// prettier-ignore +function resolveSubmoduleExports(submodule: string) { + return { + require: { + default: `./build/require/${submodule}/index.js`, + types: `./build/require/${submodule}/index.d.ts` + }, + import: { + default: `./build/import/${submodule}/index.mjs`, + types: `./build/import/${submodule}/index.d.mts`, + } + } +} +// prettier-ignore +function resolveExports(submodules: string[]) { + const exports = submodules.reduce((acc, submodule) => { + return { ...acc, [`./${submodule}`]: resolveSubmoduleExports(submodule) } + }, { + // ... and root module + ".": { + "require": { + "default": "./build/require/index.js", + "types": "./build/require/index.d.ts" + }, + "import": { + "default": "./build/import/index.mjs", + "types": "./build/import/index.d.mts" + } + } + }) + return { exports } +} +// prettier-ignore +function resolveMetadata() { + const packagePath = Path.join(process.cwd(), 'package.json') + const packageJson = JSON.parse(Fs.readFileSync(packagePath, 'utf-8')) + return { + name: packageJson.name, + version: packageJson.version, + description: packageJson.description, + keywords: packageJson.keywords, + author: packageJson.author, + license: packageJson.license, + repository: packageJson.repository, + scripts: { test: 'echo test' }, // flagged by socket.dev + module: "./build/import/index.mjs", + types: "./build/require/index.d.ts", + main: "./build/require/index.js" + } +} diff --git a/task/build/redirect/create-redirect-paths.ts b/task/build/redirect/create-redirect-paths.ts new file mode 100644 index 000000000..66549ed3b --- /dev/null +++ b/task/build/redirect/create-redirect-paths.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Fs from 'node:fs' + +// -------------------------------------------------------------------------------------------------------------------------- +// Builds redirect directories for earlier versions of Node. Note that TypeScript will use these directories to +// resolve types when tsconfig.json is configured for `moduleResolution: 'node'`. This approach is referred to as +// `package-json-redirect` and enables correct type resolution in lieu of a correct end user configuration. +// +// https://github.com/andrewbranch/example-subpath-exports-ts-compat/tree/main/examples/node_modules/package-json-redirects +// -------------------------------------------------------------------------------------------------------------------------- + +// prettier-ignore +export function createRedirectPaths(target: string, submodules: string[]) { + submodules.forEach((submodule) => writeRedirect(target, submodule)) +} + +// prettier-ignore +function writeRedirect(target: string, submodule: string) { + Fs.mkdirSync(`${target}/${submodule}`, { recursive: true }) + Fs.writeFileSync(`${target}/${submodule}/package.json`,JSON.stringify({ + main: `../build/require/${submodule}/index.js`, + types: `../build/require/${submodule}/index.d.ts`, + }, null, 2)) +} diff --git a/task/build/require/build.ts b/task/build/require/build.ts new file mode 100644 index 000000000..a3305a146 --- /dev/null +++ b/task/build/require/build.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { removeNotices } from '../common/remove-notices' +import { compile } from './compile' + +/** Builds the CommonJS version of this package */ +export async function build(target: string) { + console.log('building...require') + const buildTarget = `${target}/build/require` + await compile(buildTarget) + await removeNotices(buildTarget) +} diff --git a/task/build/require/compile.ts b/task/build/require/compile.ts new file mode 100644 index 000000000..f033f2228 --- /dev/null +++ b/task/build/require/compile.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +declare function shell(command: string): Promise + +// prettier-ignore +export async function compile(target: string) { + const options = [ + `--outDir ${target}`, + '--target ES2020', + '--module CommonJS', + '--declaration', + ].join(' ') + await shell(`tsc -p ./src/tsconfig.json ${options}`) +} diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index 2e9a92944..315eec216 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -1,6 +1,10 @@ import * as assert from 'assert' export namespace Assert { + export function HasProperty(value: unknown, key: K): asserts value is Record { + if (typeof value === 'object' && value !== null && key in value) return + throw new Error(`Expected value to have property '${key as string}'`) + } export function IsTrue(value: boolean): asserts value is true { return assert.strictEqual(value, true) } @@ -46,7 +50,7 @@ export namespace Assert { if (value instanceof constructor) return throw Error(`Value is not instance of ${constructor}`) } - export function IsTypeOf(value: any, type: any) { + export function IsTypeOf(value: any, type: T) { if (typeof value === type) return throw Error(`Value is not typeof ${type}`) } diff --git a/test/runtime/compiler-ajv/const.ts b/test/runtime/compiler-ajv/const.ts new file mode 100644 index 000000000..21705e276 --- /dev/null +++ b/test/runtime/compiler-ajv/const.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('compiler-ajv/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Ok(T, 1) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Ok(T, 'hello') + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Ok(T, true) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Ok(T, { x: 1, y: 2 }) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Ok(T, [1, 2, 3]) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Ok(T, [1, true, 'hello']) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Ok(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + }) +}) diff --git a/test/runtime/compiler-ajv/index.ts b/test/runtime/compiler-ajv/index.ts index 0c6e11f6a..ae974b96d 100644 --- a/test/runtime/compiler-ajv/index.ts +++ b/test/runtime/compiler-ajv/index.ts @@ -2,6 +2,7 @@ import './any' import './array' import './boolean' import './composite' +import './const' import './date' import './enum' import './integer' @@ -22,8 +23,8 @@ import './readonly' import './recursive' import './record' import './ref' -import './regexp' import './required' +import './string-pattern' import './string' import './template-literal' import './tuple' diff --git a/test/runtime/compiler-ajv/partial.ts b/test/runtime/compiler-ajv/partial.ts index 5e56d704a..5be5691a3 100644 --- a/test/runtime/compiler-ajv/partial.ts +++ b/test/runtime/compiler-ajv/partial.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok } from './validate' import { Assert } from '../assert' @@ -29,12 +29,12 @@ describe('compiler-ajv/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - Assert.IsEqual(T.properties.x[Readonly], 'Readonly') - Assert.IsEqual(T.properties.x[Optional], 'Optional') - Assert.IsEqual(T.properties.y[Readonly], 'Readonly') - Assert.IsEqual(T.properties.y[Optional], 'Optional') - Assert.IsEqual(T.properties.z[Optional], 'Optional') - Assert.IsEqual(T.properties.w[Optional], 'Optional') + Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.x[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.y[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.z[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.w[OptionalKind], 'Optional') }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler-ajv/required.ts b/test/runtime/compiler-ajv/required.ts index 7df4b1875..73820cfb1 100644 --- a/test/runtime/compiler-ajv/required.ts +++ b/test/runtime/compiler-ajv/required.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { Assert } from '../assert' @@ -26,10 +26,10 @@ describe('compiler-ajv/Required', () => { w: Type.Number(), }) const T = Type.Required(A) - Assert.IsEqual(T.properties.x[Readonly], 'Readonly') - Assert.IsEqual(T.properties.y[Readonly], 'Readonly') - Assert.IsEqual(T.properties.z[Optional], undefined) - Assert.IsEqual(T.properties.w[Optional], undefined) + Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.z[OptionalKind], undefined) + Assert.IsEqual(T.properties.w[OptionalKind], undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler-ajv/string-pattern.ts b/test/runtime/compiler-ajv/string-pattern.ts new file mode 100644 index 000000000..71419b1dc --- /dev/null +++ b/test/runtime/compiler-ajv/string-pattern.ts @@ -0,0 +1,65 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('compiler-ajv/StringPattern', () => { + //----------------------------------------------------- + // Regular Expression + //----------------------------------------------------- + it('Should validate regular expression 1', () => { + const T = Type.String({ pattern: /[012345]/.source }) + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate regular expression 2', () => { + const T = Type.String({ pattern: /true|false/.source }) + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate regular expression 3', () => { + const T = Type.String({ pattern: /true|false/.source }) + Fail(T, 'unknown') + }) + it('Should validate regular expression 4', () => { + const T = Type.String({ pattern: /[\d]{5}/.source }) + Ok(T, '12345') + }) + //----------------------------------------------------- + // Regular Pattern + //----------------------------------------------------- + it('Should validate pattern 1', () => { + const T = Type.String({ pattern: '[012345]' }) + Ok(T, '0') + Ok(T, '1') + Ok(T, '2') + Ok(T, '3') + Ok(T, '4') + Ok(T, '5') + }) + it('Should validate pattern 2', () => { + const T = Type.String({ pattern: 'true|false' }) + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'true') + Ok(T, 'false') + Ok(T, 'false') + Ok(T, 'false') + Fail(T, '6') + }) + it('Should validate pattern 3', () => { + const T = Type.String({ pattern: 'true|false' }) + Fail(T, 'unknown') + }) + it('Should validate pattern 4', () => { + const T = Type.String({ pattern: '[\\d]{5}' }) + Ok(T, '12345') + }) +}) diff --git a/test/runtime/compiler-ajv/validate.ts b/test/runtime/compiler-ajv/validate.ts index 12648225b..baf279db8 100644 --- a/test/runtime/compiler-ajv/validate.ts +++ b/test/runtime/compiler-ajv/validate.ts @@ -8,19 +8,19 @@ import Ajv, { AnySchema } from 'ajv/dist/2019' function schemaOf(schemaOf: string, value: unknown, schema: unknown) { switch (schemaOf) { case 'Constructor': - return TypeGuard.TConstructor(schema) && Value.Check(schema, value) + return TypeGuard.IsConstructor(schema) && Value.Check(schema, value) case 'Function': - return TypeGuard.TFunction(schema) && Value.Check(schema, value) + return TypeGuard.IsFunction(schema) && Value.Check(schema, value) case 'Date': - return TypeGuard.TDate(schema) && Value.Check(schema, value) + return TypeGuard.IsDate(schema) && Value.Check(schema, value) case 'Promise': - return TypeGuard.TPromise(schema) && Value.Check(schema, value) + return TypeGuard.IsPromise(schema) && Value.Check(schema, value) case 'Uint8Array': - return TypeGuard.TUint8Array(schema) && Value.Check(schema, value) + return TypeGuard.IsUint8Array(schema) && Value.Check(schema, value) case 'Undefined': - return TypeGuard.TUndefined(schema) && Value.Check(schema, value) + return TypeGuard.IsUndefined(schema) && Value.Check(schema, value) case 'Void': - return TypeGuard.TVoid(schema) && Value.Check(schema, value) + return TypeGuard.IsVoid(schema) && Value.Check(schema, value) default: return false } diff --git a/test/runtime/compiler/const.ts b/test/runtime/compiler/const.ts new file mode 100644 index 000000000..a8526757e --- /dev/null +++ b/test/runtime/compiler/const.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('compiler/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Ok(T, 1) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Ok(T, 'hello') + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Ok(T, true) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Ok(T, { x: 1, y: 2 }) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Ok(T, [1, 2, 3]) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Ok(T, [1, true, 'hello']) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Ok(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index db87a1e15..487c94a60 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -4,6 +4,7 @@ import './async-iterator' import './bigint' import './boolean' import './composite' +import './const' import './constructor' import './date' import './unicode' @@ -31,6 +32,7 @@ import './record' import './ref' import './regexp' import './required' +import './string-pattern' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 870e6979b..74b71aa60 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -1,5 +1,5 @@ import { TypeSystem } from '@sinclair/typebox/system' -import { Type, Kind, Optional, Readonly } from '@sinclair/typebox' +import { Type, OptionalKind, ReadonlyKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -30,12 +30,12 @@ describe('compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.x[Optional], 'Optional') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.y[Optional], 'Optional') - strictEqual(T.properties.z[Optional], 'Optional') - strictEqual(T.properties.w[Optional], 'Optional') + strictEqual(T.properties.x[ReadonlyKind], 'Readonly') + strictEqual(T.properties.x[OptionalKind], 'Optional') + strictEqual(T.properties.y[ReadonlyKind], 'Readonly') + strictEqual(T.properties.y[OptionalKind], 'Optional') + strictEqual(T.properties.z[OptionalKind], 'Optional') + strictEqual(T.properties.w[OptionalKind], 'Optional') }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler/regexp.ts b/test/runtime/compiler/regexp.ts index 8d5d9ea39..5a9bf09b8 100644 --- a/test/runtime/compiler/regexp.ts +++ b/test/runtime/compiler/regexp.ts @@ -2,64 +2,15 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('compiler/RegExp', () => { - //----------------------------------------------------- - // Regular Expression - //----------------------------------------------------- it('Should validate regular expression 1', () => { - const T = Type.RegExp(/[012345]/) - Ok(T, '0') - Ok(T, '1') - Ok(T, '2') - Ok(T, '3') - Ok(T, '4') - Ok(T, '5') + const T = Type.RegExp(/foo/i) + Ok(T, 'foo') + Ok(T, 'Foo') + Ok(T, 'fOO') + Fail(T, 'bar') }) it('Should validate regular expression 2', () => { - const T = Type.RegExp(/true|false/) - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'false') - Ok(T, 'false') - Ok(T, 'false') - Fail(T, '6') - }) - it('Should validate regular expression 3', () => { - const T = Type.RegExp(/true|false/) - Fail(T, 'unknown') - }) - it('Should validate regular expression 4', () => { - const T = Type.RegExp(/[\d]{5}/) - Ok(T, '12345') - }) - //----------------------------------------------------- - // Regular Pattern - //----------------------------------------------------- - it('Should validate pattern 1', () => { - const T = Type.RegExp('[012345]') - Ok(T, '0') - Ok(T, '1') - Ok(T, '2') - Ok(T, '3') - Ok(T, '4') - Ok(T, '5') - }) - it('Should validate pattern 2', () => { - const T = Type.RegExp('true|false') - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'true') - Ok(T, 'false') - Ok(T, 'false') - Ok(T, 'false') - Fail(T, '6') - }) - it('Should validate pattern 3', () => { - const T = Type.RegExp('true|false') - Fail(T, 'unknown') - }) - it('Should validate pattern 4', () => { - const T = Type.RegExp('[\\d]{5}') - Ok(T, '12345') + const T = Type.RegExp(/|\p{Extended_Pictographic}/gu) + Ok(T, '♥️♦️♠️♣️') }) }) diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index 14ca1bf5f..fb071fecc 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -26,10 +26,10 @@ describe('compiler/Required', () => { w: Type.Number(), }) const T = Type.Required(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.z[Optional], undefined) - strictEqual(T.properties.w[Optional], undefined) + strictEqual(T.properties.x[ReadonlyKind], 'Readonly') + strictEqual(T.properties.y[ReadonlyKind], 'Readonly') + strictEqual(T.properties.z[OptionalKind], undefined) + strictEqual(T.properties.w[OptionalKind], undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler-ajv/regexp.ts b/test/runtime/compiler/string-pattern.ts similarity index 72% rename from test/runtime/compiler-ajv/regexp.ts rename to test/runtime/compiler/string-pattern.ts index dcfae6e8d..6ee16cbb0 100644 --- a/test/runtime/compiler-ajv/regexp.ts +++ b/test/runtime/compiler/string-pattern.ts @@ -1,12 +1,12 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' -describe('compiler-ajv/RegExp', () => { +describe('compiler/StringPattern', () => { //----------------------------------------------------- // Regular Expression //----------------------------------------------------- it('Should validate regular expression 1', () => { - const T = Type.RegExp(/[012345]/) + const T = Type.String({ pattern: /[012345]/.source }) Ok(T, '0') Ok(T, '1') Ok(T, '2') @@ -15,7 +15,7 @@ describe('compiler-ajv/RegExp', () => { Ok(T, '5') }) it('Should validate regular expression 2', () => { - const T = Type.RegExp(/true|false/) + const T = Type.String({ pattern: /true|false/.source }) Ok(T, 'true') Ok(T, 'true') Ok(T, 'true') @@ -25,18 +25,18 @@ describe('compiler-ajv/RegExp', () => { Fail(T, '6') }) it('Should validate regular expression 3', () => { - const T = Type.RegExp(/true|false/) + const T = Type.String({ pattern: /true|false/.source }) Fail(T, 'unknown') }) it('Should validate regular expression 4', () => { - const T = Type.RegExp(/[\d]{5}/) + const T = Type.String({ pattern: /[\d]{5}/.source }) Ok(T, '12345') }) //----------------------------------------------------- // Regular Pattern //----------------------------------------------------- it('Should validate pattern 1', () => { - const T = Type.RegExp('[012345]') + const T = Type.String({ pattern: '[012345]' }) Ok(T, '0') Ok(T, '1') Ok(T, '2') @@ -45,7 +45,7 @@ describe('compiler-ajv/RegExp', () => { Ok(T, '5') }) it('Should validate pattern 2', () => { - const T = Type.RegExp('true|false') + const T = Type.String({ pattern: 'true|false' }) Ok(T, 'true') Ok(T, 'true') Ok(T, 'true') @@ -55,11 +55,11 @@ describe('compiler-ajv/RegExp', () => { Fail(T, '6') }) it('Should validate pattern 3', () => { - const T = Type.RegExp('true|false') + const T = Type.String({ pattern: 'true|false' }) Fail(T, 'unknown') }) it('Should validate pattern 4', () => { - const T = Type.RegExp('[\\d]{5}') + const T = Type.String({ pattern: '[\\d]{5}' }) Ok(T, '12345') }) }) diff --git a/test/runtime/errors/types/index.ts b/test/runtime/errors/types/index.ts index 44730ba30..3329f6b54 100644 --- a/test/runtime/errors/types/index.ts +++ b/test/runtime/errors/types/index.ts @@ -48,6 +48,7 @@ import './object-required-property' import './object' import './promise' import './record-pointer-property' +import './regexp' import './string-format-unknown' import './string-format' import './string-max-length' diff --git a/test/runtime/errors/types/regexp.ts b/test/runtime/errors/types/regexp.ts new file mode 100644 index 000000000..e6f1c69b8 --- /dev/null +++ b/test/runtime/errors/types/regexp.ts @@ -0,0 +1,17 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/RegExp', () => { + const T = Type.RegExp(/123/) + it('Should pass 0', () => { + const R = Resolve(T, '123') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, '321') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.RegExp) + }) +}) diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts index 3dd3e83ee..6373c9ae0 100644 --- a/test/runtime/type/extends/any.ts +++ b/test/runtime/type/extends/any.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Any', () => { it('Should extend Any', () => { type T = any extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = any extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = any extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.String()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Boolean', () => { type T = any extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Number', () => { type T = any extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Integer', () => { type T = any extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Array 1', () => { type T = any extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Array 2', () => { type T = any extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Tuple', () => { type T = any extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 1', () => { type T = any extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 2', () => { type T = any extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 3', () => { type T = any extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 1', () => { type T = any extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 2', () => { type T = any extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = any extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Undefined', () => { type T = any extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Void', () => { type T = any extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Date', () => { type T = any extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.Union) }) }) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts index b734bc8ab..2e468c80c 100644 --- a/test/runtime/type/extends/array.ts +++ b/test/runtime/type/extends/array.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Array', () => { @@ -8,258 +7,258 @@ describe('type/extends/Array', () => { // ---------------------------------------------- it('Should extend Array Varying 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 3', () => { type T = Array extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 4', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Any // ---------------------------------------------- it('Should extend Any', () => { type T = Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 3', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple', () => { type T = Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Array extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = Array extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = Array extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Constrained // ---------------------------------------------- it('Should extend constrained Any', () => { type T = Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained String', () => { type T = Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Array 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Array 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Array 3', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Tuple', () => { type T = Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Array extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 3', () => { type T = Array extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 5', () => { type T = Array extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 3', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 4', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 5', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Null', () => { type T = Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Void', () => { type T = Array extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Array extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/async-iterator.ts b/test/runtime/type/extends/async-iterator.ts index 4eba4c020..9cc5392a8 100644 --- a/test/runtime/type/extends/async-iterator.ts +++ b/test/runtime/type/extends/async-iterator.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/AsyncIterator', () => { @@ -8,65 +7,65 @@ describe('type/extends/AsyncIterator', () => { // ---------------------------------------------- it('Should extend AsyncIterator 1', () => { type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Any()), Type.AsyncIterator(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Any()), Type.AsyncIterator(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 2', () => { type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 3', () => { type T = AsyncIterableIterator<'hello'> extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Literal('hello')), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Literal('hello')), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 4', () => { type T = AsyncIterableIterator extends AsyncIterableIterator<'hello'> ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Literal('hello'))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Literal('hello'))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend AsyncIterator 5', () => { type T = AsyncIterableIterator extends AsyncIterableIterator<'hello' | number> ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend AsyncIterator 6', () => { type T = AsyncIterableIterator<'hello' | number> extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // -------------------------------------------------------------------- // Structural // -------------------------------------------------------------------- it('Should extends Any 1', () => { type T = AsyncIterableIterator extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Any 2', () => { type T = any extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extends Unknown 1', () => { type T = AsyncIterableIterator extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Unknown 2', () => { type T = unknown extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 1', () => { type T = AsyncIterableIterator extends never ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Never()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 2', () => { type T = never extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Never(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Never(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/bigint.ts b/test/runtime/type/extends/bigint.ts index 5346cfa39..f965eab3d 100644 --- a/test/runtime/type/extends/bigint.ts +++ b/test/runtime/type/extends/bigint.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/BigInt', () => { it('Should extend Any', () => { type T = bigint extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = bigint extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = bigint extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = bigint extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = bigint extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = bigint extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = bigint extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = bigint extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = bigint extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = bigint extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = bigint extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = bigint extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = bigint extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = bigint extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = bigint extends boolean | bigint ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = bigint extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = bigint extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = bigint extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = bigint extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts index 1d56e21eb..6e336f331 100644 --- a/test/runtime/type/extends/boolean.ts +++ b/test/runtime/type/extends/boolean.ts @@ -1,91 +1,90 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Boolean', () => { it('Should extend Any', () => { type T = boolean extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = boolean extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = boolean extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Number', () => { type T = boolean extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = boolean extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = boolean extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = boolean extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = boolean extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = boolean extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = boolean extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = boolean extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = boolean extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = boolean extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = boolean extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = boolean extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = boolean extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = boolean extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts index f5b6151d9..a590c842f 100644 --- a/test/runtime/type/extends/constructor.ts +++ b/test/runtime/type/extends/constructor.ts @@ -1,211 +1,210 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Constructor', () => { it('Should extend Function', () => { type T = (new () => number) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 2', () => { type T = (new () => any) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 3', () => { type T = (new () => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 4', () => { type T = (new (a: number) => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 5', () => { type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 6', () => { type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 7', () => { type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 8', () => { type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => any) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 10', () => { type T = (new () => Array) extends new () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 11', () => { type T = (new () => Array) extends new () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 12', () => { type T = (new () => object) extends new () => Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 13', () => { type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 14', () => { type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 15', () => { type T = (new () => any) extends new () => unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 16', () => { type T = (new () => unknown) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Any', () => { type T = (new () => number) extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = (new () => number) extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = (new () => number) extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = (new () => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = (new () => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = (new () => number) extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = (new () => number) extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = (new () => number) extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = (new () => number) extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = (new () => number) extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = (new () => number) extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = (new () => number) extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = (new () => number) extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = (new () => number) extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = (new () => number) extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = (new () => number) extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts index 04df6c661..2bcfa8864 100644 --- a/test/runtime/type/extends/date.ts +++ b/test/runtime/type/extends/date.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Date', () => { it('Should extend Any', () => { type T = Date extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Date extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Date extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Date extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Date extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Date extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Date extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Date extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Date extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Date extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Date extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Date extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Date extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Date extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Date extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Date extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Date extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Date extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts index 0b334087d..e889555a3 100644 --- a/test/runtime/type/extends/function.ts +++ b/test/runtime/type/extends/function.ts @@ -1,211 +1,210 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Function', () => { it('Should extend Constructor 1', () => { type T = (() => number) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 1', () => { type T = (() => number) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 2', () => { type T = (() => any) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 3', () => { type T = (() => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 4', () => { type T = ((a: number) => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 5', () => { type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 6', () => { type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 7', () => { type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 8', () => { type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Function([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => any) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 10', () => { type T = (() => Array) extends () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 11', () => { type T = (() => Array) extends () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 12', () => { type T = (() => object) extends () => Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 13', () => { type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 14', () => { type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 15', () => { type T = (() => any) extends () => unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 16', () => { type T = (() => unknown) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Any', () => { type T = (() => number) extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = (() => number) extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = (() => number) extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = (() => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = (() => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = (() => number) extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = (() => number) extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = (() => number) extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = (() => number) extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = (() => number) extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = (() => number) extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = (() => number) extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = (() => number) extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = (() => number) extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = (() => number) extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = (() => number) extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/index.ts b/test/runtime/type/extends/index.ts index c826cc0fe..033113275 100644 --- a/test/runtime/type/extends/index.ts +++ b/test/runtime/type/extends/index.ts @@ -15,6 +15,7 @@ import './number' import './object' import './promise' import './record' +import './regexp' import './string' import './symbol' import './template-literal' diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts index 1c5d00995..6abd57529 100644 --- a/test/runtime/type/extends/integer.ts +++ b/test/runtime/type/extends/integer.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Integer', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/iterator.ts b/test/runtime/type/extends/iterator.ts index 1cf5b6bfd..d685dfab1 100644 --- a/test/runtime/type/extends/iterator.ts +++ b/test/runtime/type/extends/iterator.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Iterator', () => { @@ -8,65 +7,65 @@ describe('type/extends/Iterator', () => { // ---------------------------------------------- it('Should extend Iterator 1', () => { type T = IterableIterator extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Any()), Type.Iterator(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Any()), Type.Iterator(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 2', () => { type T = IterableIterator extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 3', () => { type T = IterableIterator<'hello'> extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Literal('hello')), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Literal('hello')), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 4', () => { type T = IterableIterator extends IterableIterator<'hello'> ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Literal('hello'))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.Literal('hello'))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Iterator 5', () => { type T = IterableIterator extends IterableIterator<'hello' | number> ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Iterator 6', () => { type T = IterableIterator<'hello' | number> extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // -------------------------------------------------------------------- // Structural // -------------------------------------------------------------------- it('Should extends Any 1', () => { type T = IterableIterator extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Any 2', () => { type T = any extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extends Unknown 1', () => { type T = IterableIterator extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Unknown 2', () => { type T = unknown extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 1', () => { type T = IterableIterator extends never ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Never()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 2', () => { type T = never extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Never(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Never(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts index 7c23890fc..ca2c952b6 100644 --- a/test/runtime/type/extends/literal.ts +++ b/test/runtime/type/extends/literal.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Literal', () => { @@ -8,258 +7,258 @@ describe('type/extends/Literal', () => { // ------------------------------------------------------------------- it('Should extend Any (String)', () => { type T = 'hello' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (String)', () => { type T = 'hello' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (String)', () => { type T = 'hello' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (String)', () => { type T = 'hello' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (String)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (String)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (String)', () => { type T = 'hello' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (String)', () => { type T = 'hello' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (String)', () => { type T = 'hello' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (String)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (String)', () => { type T = 'hello' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (String)', () => { type T = 'hello' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (String)', () => { type T = 'hello' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (String)', () => { type T = 'hello' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (String)', () => { type T = 'hello' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // Number Literal // ------------------------------------------------------------------- it('Should extend Any (Number)', () => { type T = 10 extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (Number)', () => { type T = 10 extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (Number)', () => { type T = 10 extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean (Number)', () => { type T = 10 extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (Number)', () => { type T = 10 extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer (Number)', () => { type T = 10 extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array (Number)', () => { type T = 10 extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (Number)', () => { type T = 10 extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (Number)', () => { type T = 10 extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (Number)', () => { type T = 10 extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (Number)', () => { type T = 10 extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (Number)', () => { type T = 10 extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (Number)', () => { type T = 10 extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null (Number)', () => { type T = 10 extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (Number)', () => { type T = 10 extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // Boolean Literal // ------------------------------------------------------------------- it('Should extend Any (Boolean)', () => { type T = true extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (Boolean)', () => { type T = true extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (Boolean)', () => { type T = true extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean (Boolean)', () => { type T = true extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Number (Boolean)', () => { type T = true extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (Boolean)', () => { type T = true extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (Boolean)', () => { type T = true extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (Boolean)', () => { type T = true extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = 'hello' extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = 10 extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 3', () => { type T = true extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (Boolean)', () => { type T = true extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (Boolean)', () => { type T = true extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (Boolean)', () => { type T = true extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2 (Boolean)', () => { type T = true extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (Boolean)', () => { type T = true extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null (Boolean)', () => { type T = true extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (Boolean)', () => { type T = true extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = true extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = true extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/not.ts b/test/runtime/type/extends/not.ts index 53f8d1e40..fd3a97ab8 100644 --- a/test/runtime/type/extends/not.ts +++ b/test/runtime/type/extends/not.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' // --------------------------------------------------------------------------- @@ -18,8 +17,8 @@ describe('type/extends/Not', () => { it('Should extend with unknown assignability check', () => { const A = Type.Number() const B = Type.Not(Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) // we would expect false + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) // we would expect false }) // --------------------------------------------------------------------------- // Nested @@ -31,17 +30,17 @@ describe('type/extends/Not', () => { const T4 = Type.Not(T3) const T5 = Type.Not(T4) - const R1 = TypeExtends.Extends(T1, Type.String()) - const R2 = TypeExtends.Extends(T2, Type.String()) - const R3 = TypeExtends.Extends(T3, Type.String()) - const R4 = TypeExtends.Extends(T4, Type.String()) - const R5 = TypeExtends.Extends(T5, Type.String()) + const R1 = ExtendsCheck(T1, Type.String()) + const R2 = ExtendsCheck(T2, Type.String()) + const R3 = ExtendsCheck(T3, Type.String()) + const R4 = ExtendsCheck(T4, Type.String()) + const R5 = ExtendsCheck(T5, Type.String()) - Assert.IsEqual(R1, TypeExtendsResult.True) - Assert.IsEqual(R2, TypeExtendsResult.False) - Assert.IsEqual(R3, TypeExtendsResult.True) - Assert.IsEqual(R4, TypeExtendsResult.False) - Assert.IsEqual(R5, TypeExtendsResult.True) + Assert.IsEqual(R1, ExtendsResult.True) + Assert.IsEqual(R2, ExtendsResult.False) + Assert.IsEqual(R3, ExtendsResult.True) + Assert.IsEqual(R4, ExtendsResult.False) + Assert.IsEqual(R5, ExtendsResult.True) }) // --------------------------------------------------------------------------- @@ -49,97 +48,97 @@ describe('type/extends/Not', () => { // --------------------------------------------------------------------------- it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts index 702e16271..157d7d264 100644 --- a/test/runtime/type/extends/null.ts +++ b/test/runtime/type/extends/null.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Null', () => { it('Should extend Any', () => { type T = null extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = null extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = null extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = null extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = null extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = null extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = null extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = null extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = null extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = null extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = null extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = null extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = null extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = null extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = null extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = null extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Undefined', () => { type T = null extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = null extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = null extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts index aa647eca6..ce9c4189d 100644 --- a/test/runtime/type/extends/number.ts +++ b/test/runtime/type/extends/number.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Number', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts index 8bc3a6e63..76e405818 100644 --- a/test/runtime/type/extends/object.ts +++ b/test/runtime/type/extends/object.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Object', () => { @@ -10,43 +9,43 @@ describe('type/extends/Object', () => { type T = { x: number; y: number } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.Number() }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = { x: number } extends { x: number; y: number } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = { x: number; y: string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.String() }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 4', () => { type T = { x: number } extends { x: number; y: string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.String() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = { x: number | string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 6', () => { type T = { x: number } extends { x: number | string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ---------------------------------------------------------- // Record @@ -55,124 +54,124 @@ describe('type/extends/Object', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 5', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 6', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ---------------------------------------------------------- // Standard // ---------------------------------------------------------- it('Should extend Any', () => { type T = { a: number } extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = { a: number } extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = { a: number } extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = { a: number } extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = { a: number } extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = { a: number } extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = { a: number } extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = { a: number } extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = { a: number } extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = { a: number } extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = { a: number } extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = { a: number } extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = { a: number } extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = { a: number } extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = { a: number } extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = { a: number } extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = { a: number } extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------------------------- // Optional @@ -180,14 +179,14 @@ describe('type/extends/Object', () => { it('Should extend optional 1', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Optional(Type.Number()) }) - const C = TypeExtends.Extends(A, B) - Assert.IsEqual(C, TypeExtendsResult.True) + const C = ExtendsCheck(A, B) + Assert.IsEqual(C, ExtendsResult.True) }) it('Should extend optional 2', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Optional(Type.Number()) }) - const C = TypeExtends.Extends(B, A) - Assert.IsEqual(C, TypeExtendsResult.False) + const C = ExtendsCheck(B, A) + Assert.IsEqual(C, ExtendsResult.False) }) it('Should extend optional 3', () => { const A = Type.Object({ @@ -198,8 +197,8 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Number(), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend optional 4', () => { const A = Type.Object({ @@ -210,8 +209,8 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Number(), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend optional 5', () => { const A = Type.Object({ @@ -222,7 +221,7 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Optional(Type.Number()), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts index 843786a64..2313c8738 100644 --- a/test/runtime/type/extends/promise.ts +++ b/test/runtime/type/extends/promise.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Promise', () => { @@ -8,203 +7,203 @@ describe('type/extends/Promise', () => { // ---------------------------------------------- it('Should extend Promise Varying 1', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Promise(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 2', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.String()), Type.Promise(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 3', () => { type T = Promise extends Promise ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Promise(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 4', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Promise(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Any // ---------------------------------------------- it('Should extend Any', () => { type T = Promise extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Promise extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Promise extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Promise extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Promise extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Promise extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Promise extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = Promise extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = Promise extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Promise extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Promise extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Promise extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Constrained // ---------------------------------------------- it('Should extend constrained Any', () => { type T = Promise extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Promise extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained String', () => { type T = Promise extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Promise extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Array', () => { type T = Promise extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Promise extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 3', () => { type T = Promise extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Promise extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Promise extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Null', () => { type T = Promise extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Promise extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Promise extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Promise extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/record.ts b/test/runtime/type/extends/record.ts index 7ac3301c2..905a96fcf 100644 --- a/test/runtime/type/extends/record.ts +++ b/test/runtime/type/extends/record.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Record', () => { @@ -7,194 +6,194 @@ describe('type/extends/Record', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ----- it('Should extend Record 12', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ------------------------------------------------------------------- // Standard // ------------------------------------------------------------------- it('Should extend Any', () => { type T = Record extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Record extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Record extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = Record extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Record extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/regexp.ts b/test/runtime/type/extends/regexp.ts new file mode 100644 index 000000000..2298acd23 --- /dev/null +++ b/test/runtime/type/extends/regexp.ts @@ -0,0 +1,113 @@ +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// ------------------------------------------------------------------ +// Note: RegExp infers as type String +// ------------------------------------------------------------------ +describe('type/extends/RegExp', () => { + it('Should extend Any', () => { + type T = string extends any ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Unknown', () => { + type T = string extends unknown ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend String', () => { + type T = string extends string ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.RegExp(/xyz/)) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Boolean', () => { + type T = string extends boolean ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Number', () => { + type T = string extends number ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Integer', () => { + type T = string extends number ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Array', () => { + type T = string extends Array ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Tuple', () => { + type T = string extends [number, number] ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Record 1', () => { + type T = string extends Record ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Record 2', () => { + type T = string extends Record ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Record(Type.Number(), Type.Unknown())) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Record 3', () => { + type T = string extends Record ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Record(Type.Number(), Type.RegExp(/xyz/))) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Record 4', () => { + type T = string extends Record ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Record(Type.Number(), Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Object 1', () => { + type T = string extends {} ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Object 2', () => { + type T = string extends { a: 10 } ? 1 : 2 + const R = ExtendsCheck(Type.RegExp(/xyz/), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Union 1', () => { + type T = number extends number | string ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Number(), Type.RegExp(/xyz/)])) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Union 2', () => { + type T = number extends any | number ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Union 3', () => { + type T = number extends boolean | number ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) + }) + it('Should extend Null', () => { + type T = number extends null ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Undefined', () => { + type T = number extends undefined ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Void', () => { + type T = number extends void ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) + }) + it('Should extend Date', () => { + type T = number extends Date ? 1 : 2 + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) + }) +}) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts index 0dd95ecbe..cdb3e216c 100644 --- a/test/runtime/type/extends/string.ts +++ b/test/runtime/type/extends/string.ts @@ -1,111 +1,110 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/String', () => { it('Should extend Any', () => { type T = string extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = string extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = string extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean', () => { type T = string extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = string extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = string extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = string extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = string extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/symbol.ts b/test/runtime/type/extends/symbol.ts index 03dc7aaf0..9412f4fc8 100644 --- a/test/runtime/type/extends/symbol.ts +++ b/test/runtime/type/extends/symbol.ts @@ -1,106 +1,105 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Symbol', () => { it('Should extend Any', () => { type T = symbol extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = symbol extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = symbol extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = symbol extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = symbol extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = symbol extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = symbol extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = symbol extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = symbol extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = symbol extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = symbol extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = symbol extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = symbol extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = symbol extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = symbol extends boolean | symbol ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = symbol extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = symbol extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = symbol extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = symbol extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol extends symbol ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Symbol()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Symbol()) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/template-literal.ts b/test/runtime/type/extends/template-literal.ts index 3dd6ff3d2..1e8aeab12 100644 --- a/test/runtime/type/extends/template-literal.ts +++ b/test/runtime/type/extends/template-literal.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/TemplateLiteral', () => { @@ -8,155 +7,155 @@ describe('type/extends/TemplateLiteral', () => { // ------------------------------------------------------------------- it('Should extend Any (hello)', () => { type T = 'hello' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (hello)', () => { type T = 'hello' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (hello)', () => { type T = 'hello' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (hello)', () => { type T = 'hello' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (hello)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (hello)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (hello)', () => { type T = 'hello' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (hello)', () => { type T = 'hello' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (hello)', () => { type T = 'hello' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (hello)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (hello)', () => { type T = 'hello' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (hello)', () => { type T = 'hello' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (hello)', () => { type T = 'hello' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (hello)', () => { type T = 'hello' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (hello)', () => { type T = 'hello' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // String Literal 'hello' | 'world' // ------------------------------------------------------------------- it('Should extend Any (hello | world)', () => { type T = 'hello' | 'world' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (hello | world)', () => { type T = 'hello' | 'world' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (hello | world)', () => { type T = 'hello' | 'world' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (hello | world)', () => { type T = 'hello' | 'world' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (hello | world)', () => { type T = 'hello' | 'world' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (hello | world)', () => { type T = 'hello' | 'world' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (hello | world)', () => { type T = 'hello' | 'world' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (hello | world)', () => { type T = 'hello' | 'world' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (hello | world)', () => { type T = 'hello' | 'world' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (hello | world)', () => { type T = 'hello' | 'world' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (hello | world)', () => { type T = 'hello' | 'world' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (hello | world)', () => { type T = 'hello' | 'world' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (hello | world)', () => { type T = 'hello' | 'world' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/tuple.ts b/test/runtime/type/extends/tuple.ts index b297655ab..99bc36033 100644 --- a/test/runtime/type/extends/tuple.ts +++ b/test/runtime/type/extends/tuple.ts @@ -1,161 +1,160 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Tuple', () => { it('Should extend Any', () => { type T = [string, number] extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = [string, number] extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = [string, number] extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = [string, number] extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = [string, number] extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 2', () => { type T = [string, number] extends Array ? 1 : 2 // 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 4', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 1', () => { type T = [string, number] extends [string, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 2', () => { type T = [string, number] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 3', () => { type T = [string, any] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 4', () => { type T = [string, number] extends [string, any] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 5', () => { type T = [string, unknown] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 6', () => { type T = [string, number] extends [string, unknown] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 7', () => { type T = [] extends [string, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 8', () => { type T = [string, number] extends [] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = [string, number] extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = [string, number] extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = [string, number] extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = [string, number] extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = [string, number] extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = [string, number] extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = [string, number] extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = [string, number] extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = [string, number] extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = [string, number] extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts index 828163dd1..0128fb0cb 100644 --- a/test/runtime/type/extends/uint8array.ts +++ b/test/runtime/type/extends/uint8array.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Uint8Array', () => { it('Should extend Any', () => { type T = Uint8Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Uint8Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Uint8Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Uint8Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Uint8Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Uint8Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Uint8Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Uint8Array extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 1', () => { type T = Uint8Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Uint8Array extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Uint8Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Uint8Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Uint8Array extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Uint8Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Uint8Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Uint8Array extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Uint8Array extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts index 664126124..4996a1054 100644 --- a/test/runtime/type/extends/undefined.ts +++ b/test/runtime/type/extends/undefined.ts @@ -1,91 +1,90 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Undefined', () => { it('Should extend Any', () => { type T = undefined extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = undefined extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = undefined extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = undefined extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = undefined extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = undefined extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = undefined extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = undefined extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = undefined extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = undefined extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = undefined extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = undefined extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = undefined extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = undefined extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = undefined extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Date', () => { type T = undefined extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/union.ts b/test/runtime/type/extends/union.ts index e9eb05cef..39783cff9 100644 --- a/test/runtime/type/extends/union.ts +++ b/test/runtime/type/extends/union.ts @@ -1,136 +1,135 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Union', () => { it('Should extend Any', () => { type T = number | string extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number | string extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number | string extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number | string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = number | string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = number | string extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number | string extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number | string extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number | string extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number | string extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number | string extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = number | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = any | boolean extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 5', () => { type T = any | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 6', () => { type T = any | {} extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 7', () => { type T = any extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 8', () => { type T = unknown | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 9', () => { type T = unknown extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = number | string extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number | string extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number | string extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void 2', () => { type T = number | string | void extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date 2', () => { type T = Date | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend BigInt', () => { type T = bigint | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts index 85dbf8654..7622dbfdd 100644 --- a/test/runtime/type/extends/unknown.ts +++ b/test/runtime/type/extends/unknown.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Unknown', () => { it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts index 106e1759e..48efc644e 100644 --- a/test/runtime/type/extends/void.ts +++ b/test/runtime/type/extends/void.ts @@ -1,106 +1,105 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Void', () => { it('Should extend Any', () => { type T = void extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = void extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = void extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = void extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = void extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = void extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = void extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = void extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = void extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = void extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = void extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = void extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = void extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = void extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = void extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = void extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = void extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = void extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = void extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Date', () => { type T = void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/guard/any.ts b/test/runtime/type/guard/any.ts index bb35ccc03..ecc4cc92b 100644 --- a/test/runtime/type/guard/any.ts +++ b/test/runtime/type/guard/any.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TAny', () => { it('Should guard for TAny', () => { - const R = TypeGuard.TAny(Type.Any()) + const R = TypeGuard.IsAny(Type.Any()) Assert.IsTrue(R) }) it('Should not guard for TAny', () => { - const R = TypeGuard.TAny(null) + const R = TypeGuard.IsAny(null) Assert.IsFalse(R) }) it('Should not guard for TAny with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TAny(Type.Any({ $id: 1 })) + const R = TypeGuard.IsAny(Type.Any({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/array.ts b/test/runtime/type/guard/array.ts index 95b0063aa..12f0a9e20 100644 --- a/test/runtime/type/guard/array.ts +++ b/test/runtime/type/guard/array.ts @@ -4,15 +4,15 @@ import { Assert } from '../../assert/index' describe('type/guard/TArray', () => { it('Should guard for TArray', () => { - const R = TypeGuard.TArray(Type.Array(Type.Number())) + const R = TypeGuard.IsArray(Type.Array(Type.Number())) Assert.IsTrue(R) }) it('Should not guard for TArray', () => { - const R = TypeGuard.TArray(null) + const R = TypeGuard.IsArray(null) Assert.IsFalse(R) }) it('Should guard for nested object TArray', () => { - const R = TypeGuard.TArray( + const R = TypeGuard.IsArray( Type.Array( Type.Object({ x: Type.Number(), @@ -23,7 +23,7 @@ describe('type/guard/TArray', () => { Assert.IsTrue(R) }) it('Should not guard for nested object TArray', () => { - const R = TypeGuard.TArray( + const R = TypeGuard.IsArray( Type.Array( Type.Object({ x: Type.Number(), @@ -35,22 +35,22 @@ describe('type/guard/TArray', () => { }) it('Should not guard for TArray with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TArray(Type.Array(Type.Number(), { $id: 1 })) + const R = TypeGuard.IsArray(Type.Array(Type.Number(), { $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TArray with invalid minItems', () => { // @ts-ignore - const R = TypeGuard.TArray(Type.Array(Type.String(), { minItems: '1' })) + const R = TypeGuard.IsArray(Type.Array(Type.String(), { minItems: '1' })) Assert.IsFalse(R) }) it('Should not guard for TArray with invalid maxItems', () => { // @ts-ignore - const R = TypeGuard.TArray(Type.Array(Type.String(), { maxItems: '1' })) + const R = TypeGuard.IsArray(Type.Array(Type.String(), { maxItems: '1' })) Assert.IsFalse(R) }) it('Should not guard for TArray with invalid uniqueItems', () => { // @ts-ignore - const R = TypeGuard.TArray(Type.Array(Type.String(), { uniqueItems: '1' })) + const R = TypeGuard.IsArray(Type.Array(Type.String(), { uniqueItems: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/async-iterator.ts b/test/runtime/type/guard/async-iterator.ts index 8b3567e5b..55a52da32 100644 --- a/test/runtime/type/guard/async-iterator.ts +++ b/test/runtime/type/guard/async-iterator.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('type/guard/TAsyncIterator', () => { it('Should guard for TAsyncIterator', () => { const T = Type.AsyncIterator(Type.Any()) - const R = TypeGuard.TAsyncIterator(T) + const R = TypeGuard.IsAsyncIterator(T) Assert.IsTrue(R) }) it('Should not guard for TAsyncIterator', () => { const T = null - const R = TypeGuard.TAsyncIterator(T) + const R = TypeGuard.IsAsyncIterator(T) Assert.IsFalse(R) }) it('Should not guard for TAsyncIterator with invalid $id', () => { //@ts-ignore const T = Type.AsyncIterator(Type.Any(), { $id: 1 }) - const R = TypeGuard.TAsyncIterator(T) + const R = TypeGuard.IsAsyncIterator(T) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/awaited.ts b/test/runtime/type/guard/awaited.ts index ef535a9ef..7515107a7 100644 --- a/test/runtime/type/guard/awaited.ts +++ b/test/runtime/type/guard/awaited.ts @@ -5,37 +5,37 @@ import { Assert } from '../../assert/index' describe('type/guard/Awaited', () => { it('Should guard for Awaited 1', () => { const T = Type.Awaited(Type.String()) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Should guard for Awaited 2', () => { const T = Type.Awaited(Type.Promise(Type.String())) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Should guard for Awaited 3', () => { const T = Type.Awaited(Type.Awaited(Type.Promise(Type.String()))) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Should guard for Awaited 4', () => { const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String()))])) - Assert.IsTrue(TypeGuard.TString(T)) + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should guard for Awaited 5', () => { const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()])) - Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsTrue(TypeGuard.TString(T.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(T.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[1])) }) it('Should guard for Awaited 6', () => { const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String()))])) - Assert.IsTrue(TypeGuard.TString(T)) + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should guard for Awaited 7', () => { const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()])) - Assert.IsTrue(TypeGuard.TIntersect(T)) - Assert.IsTrue(TypeGuard.TString(T.allOf[0])) - Assert.IsTrue(TypeGuard.TNumber(T.allOf[1])) + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(TypeGuard.IsString(T.allOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(T.allOf[1])) }) }) diff --git a/test/runtime/type/guard/bigint.ts b/test/runtime/type/guard/bigint.ts index bc16722a0..ae7edab9f 100644 --- a/test/runtime/type/guard/bigint.ts +++ b/test/runtime/type/guard/bigint.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TBigInt', () => { it('Should guard for TBigInt', () => { - const R = TypeGuard.TBigInt(Type.BigInt()) + const R = TypeGuard.IsBigInt(Type.BigInt()) Assert.IsTrue(R) }) it('Should not guard for TBigInt', () => { - const R = TypeGuard.TBigInt(null) + const R = TypeGuard.IsBigInt(null) Assert.IsFalse(R) }) it('Should not guard for BigInt with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TBigInt(Type.BigInt({ $id: 1 })) + const R = TypeGuard.IsBigInt(Type.BigInt({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/boolean.ts b/test/runtime/type/guard/boolean.ts index ecc50b90d..e74af2e16 100644 --- a/test/runtime/type/guard/boolean.ts +++ b/test/runtime/type/guard/boolean.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TBoolean', () => { it('Should guard for TBoolean', () => { - const R = TypeGuard.TBoolean(Type.Boolean()) + const R = TypeGuard.IsBoolean(Type.Boolean()) Assert.IsTrue(R) }) it('Should not guard for TBoolean', () => { - const R = TypeGuard.TBoolean(null) + const R = TypeGuard.IsBoolean(null) Assert.IsFalse(R) }) it('Should not guard for TBoolean with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TBoolean(Type.Boolean({ $id: 1 })) + const R = TypeGuard.IsBoolean(Type.Boolean({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/capitalize.ts b/test/runtime/type/guard/capitalize.ts index 33ed2e9a8..def31e727 100644 --- a/test/runtime/type/guard/capitalize.ts +++ b/test/runtime/type/guard/capitalize.ts @@ -4,30 +4,30 @@ import { Assert } from '../../assert/index' describe('type/guard/Capitalize', () => { it('Should guard for Capitalize 1', () => { const T = Type.Capitalize(Type.Literal('hello'), { $id: 'hello', foo: 1 }) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'Hello') Assert.IsEqual(T.$id, 'hello') Assert.IsEqual(T.foo, 1) }) it('Should guard for Capitalize 2', () => { const T = Type.Capitalize(Type.Literal('hello')) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'Hello') }) it('Should guard for Capitalize 3', () => { const T = Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')])) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'Hello') Assert.IsEqual(T.anyOf[1].const, 'World') }) it('Should guard for Capitalize 4', () => { const T = Type.Capitalize(Type.TemplateLiteral('hello${0|1}')) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') }) it('Should guard for Capitalize 5', () => { const T = Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') }) }) diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index 7d4fc1f4b..e8a3f21ac 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -5,20 +5,20 @@ import { Assert } from '../../assert/index' describe('type/guard/TComposite', () => { it('Should guard for distinct properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) }) it('Should guard for overlapping properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) - Assert.IsTrue(TypeGuard.TIntersect(T.properties.x)) + Assert.IsTrue(TypeGuard.IsIntersect(T.properties.x)) // @ts-ignore - Assert.IsTrue(TypeGuard.TNumber(T.properties.x.allOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x.allOf[0])) // @ts-ignore - Assert.IsTrue(TypeGuard.TNumber(T.properties.x.allOf[1])) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x.allOf[1])) }) it('Should not produce optional property if all properties are not optional', () => { const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) - Assert.IsFalse(TypeGuard.TOptional(T.properties.x)) + Assert.IsFalse(TypeGuard.IsOptional(T.properties.x)) }) // Note for: https://github.com/sinclairzx81/typebox/issues/419 // Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access @@ -33,7 +33,7 @@ describe('type/guard/TComposite', () => { Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Optional(Type.Number()) }) ]) - Assert.IsTrue(TypeGuard.TOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) Assert.IsEqual(T.required, undefined) }) it('Should produce required property if some composited properties are not optional', () => { @@ -42,7 +42,7 @@ describe('type/guard/TComposite', () => { Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() }) ]) - Assert.IsFalse(TypeGuard.TOptional(T.properties.x)) + Assert.IsFalse(TypeGuard.IsOptional(T.properties.x)) Assert.IsTrue(T.required!.includes('x')) }) it('Should preserve single optional property', () => { @@ -50,7 +50,7 @@ describe('type/guard/TComposite', () => { const T = Type.Composite([ Type.Object({ x: Type.Optional(Type.Number()) }), ]) - Assert.IsTrue(TypeGuard.TOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) Assert.IsEqual(T.required, undefined) }) }) diff --git a/test/runtime/type/guard/const.ts b/test/runtime/type/guard/const.ts new file mode 100644 index 000000000..0e5ad4288 --- /dev/null +++ b/test/runtime/type/guard/const.ts @@ -0,0 +1,124 @@ +import { TypeGuard, ValueGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TConstT', () => { + // ---------------------------------------------------------------- + // Identity Types + // ---------------------------------------------------------------- + it('Should guard for TConst 1', () => { + const T = Type.Const(undefined) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsUndefined(T)) + }) + it('Should guard for TConst 2', () => { + const T = Type.Const(null) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsNull(T)) + }) + it('Should guard for TConst 3', () => { + const T = Type.Const(Symbol()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsSymbol(T)) + }) + it('Should guard for TConst 4', () => { + const T = Type.Const(1 as const) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 1) + }) + it('Should guard for TConst 5', () => { + const T = Type.Const('hello' as const) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should guard for TConst 6', () => { + const T = Type.Const(true as const) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) + Assert.IsEqual(T.const, true) + }) + // ---------------------------------------------------------------- + // Complex Types + // ---------------------------------------------------------------- + it('Should guard for TConst 7', () => { + const T = Type.Const(100n as const) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + // TS disparity because TLiteral does not support Bigint + Assert.IsTrue(TypeGuard.IsBigInt(T)) + }) + it('Should guard for TConst 8', () => { + const T = Type.Const(new Date()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsDate(T)) + }) + it('Should guard for TConst 9', () => { + const T = Type.Const(new Uint8Array()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsUint8Array(T)) + }) + it('Should guard for TConst 10', () => { + const T = Type.Const(function () {}) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsFunction(T)) + Assert.IsTrue(T.parameters.length === 0) + Assert.IsTrue(TypeGuard.IsUnknown(T.returns)) + }) + it('Should guard for TConst 11', () => { + const T = Type.Const(new (class {})()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsObject(T)) + // Object types that are neither Date or Uint8Array evaluate as empty objects + Assert.IsEqual(T.properties, {}) + }) + it('Should guard for TConst 12', () => { + const T = Type.Const((function* (): any {})()) + const R = ValueGuard.IsIterator((function* (): any {})()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + it('Should guard for TConst 13', () => { + const T = Type.Const((async function* (): any {})()) + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + it('Should guard for TConst 14', () => { + const T = Type.Const({ + x: 1, + y: { + z: 2, + }, + } as const) + // root + Assert.IsFalse(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsObject(T)) + // x + Assert.IsTrue(TypeGuard.IsLiteral(T.properties.x)) + Assert.IsEqual(T.properties.x.const, 1) + // y + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) + Assert.IsTrue(TypeGuard.IsObject(T.properties.y)) + // y.z + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y.properties.z)) + Assert.IsTrue(TypeGuard.IsLiteral(T.properties.y.properties.z)) + Assert.IsEqual(T.properties.y.properties.z.const, 2) + }) + it('Should guard for TConst 15', () => { + const T = Type.Const([1, 2, 3] as const) + // root (arrays are always readonly as root) + Assert.IsTrue(TypeGuard.IsReadonly(T)) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(T.items?.length === 3) + // 0 + Assert.IsFalse(TypeGuard.IsReadonly(T.items![0])) + Assert.IsTrue(TypeGuard.IsLiteral(T.items![0])) + // 1 + Assert.IsFalse(TypeGuard.IsReadonly(T.items![1])) + Assert.IsTrue(TypeGuard.IsLiteral(T.items![1])) + // 2 + Assert.IsFalse(TypeGuard.IsReadonly(T.items![2])) + Assert.IsTrue(TypeGuard.IsLiteral(T.items![2])) + }) +}) diff --git a/test/runtime/type/guard/constructor.ts b/test/runtime/type/guard/constructor.ts index 967d04c1b..bcbb78aa6 100644 --- a/test/runtime/type/guard/constructor.ts +++ b/test/runtime/type/guard/constructor.ts @@ -4,32 +4,32 @@ import { Assert } from '../../assert/index' describe('type/guard/TConstructor', () => { it('Should guard for TConstructor', () => { - const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number())) + const R = TypeGuard.IsConstructor(Type.Constructor([], Type.Number())) Assert.IsTrue(R) }) it('Should not guard for TConstructor', () => { - const R = TypeGuard.TConstructor(null) + const R = TypeGuard.IsConstructor(null) Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) + const R = TypeGuard.IsConstructor(Type.Constructor([], Type.Number(), { $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid Params', () => { - const R = TypeGuard.TConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) + const R = TypeGuard.IsConstructor(Type.Constructor([{} as any, {} as any], Type.Number())) Assert.IsFalse(R) }) it('Should not guard for TConstructor with invalid Return', () => { - const R = TypeGuard.TConstructor(Type.Constructor([], {} as any)) + const R = TypeGuard.IsConstructor(Type.Constructor([], {} as any)) Assert.IsFalse(R) }) it('Should guard for TConstructor with empty Rest Tuple', () => { - const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([])), Type.Number())) + const R = TypeGuard.IsConstructor(Type.Constructor(Type.Rest(Type.Tuple([])), Type.Number())) Assert.IsTrue(R) }) it('Should guard for TConstructor with Rest Tuple', () => { - const R = TypeGuard.TConstructor(Type.Constructor(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) + const R = TypeGuard.IsConstructor(Type.Constructor(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/date.ts b/test/runtime/type/guard/date.ts index a9534a4ab..fdfc720e2 100644 --- a/test/runtime/type/guard/date.ts +++ b/test/runtime/type/guard/date.ts @@ -4,41 +4,41 @@ import { Assert } from '../../assert/index' describe('type/guard/TDate', () => { it('Should guard for TDate', () => { - const R = TypeGuard.TDate(Type.Date()) + const R = TypeGuard.IsDate(Type.Date()) Assert.IsTrue(R) }) it('Should not guard for TDate', () => { - const R = TypeGuard.TDate(null) + const R = TypeGuard.IsDate(null) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ $id: 1 })) + const R = TypeGuard.IsDate(Type.Date({ $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid exclusiveMaximumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ exclusiveMaximumTimestamp: '1' })) + const R = TypeGuard.IsDate(Type.Date({ exclusiveMaximumTimestamp: '1' })) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid exclusiveMinimumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ exclusiveMinimumTimestamp: '1' })) + const R = TypeGuard.IsDate(Type.Date({ exclusiveMinimumTimestamp: '1' })) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid maximumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ maximumTimestamp: '1' })) + const R = TypeGuard.IsDate(Type.Date({ maximumTimestamp: '1' })) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid minimumTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ minimumTimestamp: '1' })) + const R = TypeGuard.IsDate(Type.Date({ minimumTimestamp: '1' })) Assert.IsFalse(R) }) it('Should not guard for TDate with invalid multipleOfTimestamp', () => { // @ts-ignore - const R = TypeGuard.TDate(Type.Date({ multipleOfTimestamp: '1' })) + const R = TypeGuard.IsDate(Type.Date({ multipleOfTimestamp: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/deref.ts b/test/runtime/type/guard/deref.ts new file mode 100644 index 000000000..eedd3a652 --- /dev/null +++ b/test/runtime/type/guard/deref.ts @@ -0,0 +1,110 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TDeref', () => { + it('Should should deref 1', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const D = Type.Deref(R, [T]) + Assert.IsTrue(TypeGuard.IsString(D)) + Assert.IsFalse('$id' in D) + }) + it('Should should deref 2', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Object({ + x: R, + y: R, + }) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsObject(D)) + Assert.IsTrue(TypeGuard.IsString(D.properties.x)) + Assert.IsTrue(TypeGuard.IsString(D.properties.y)) + Assert.IsFalse('$id' in D.properties.x) + Assert.IsFalse('$id' in D.properties.y) + }) + it('Should should deref 3', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Array(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsArray(D)) + Assert.IsTrue(TypeGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 4', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.AsyncIterator(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsAsyncIterator(D)) + Assert.IsTrue(TypeGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 5', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Iterator(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsIterator(D)) + Assert.IsTrue(TypeGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 6', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Function([R], R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsFunction(D)) + Assert.IsTrue(TypeGuard.IsString(D.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(D.returns)) + Assert.IsFalse('$id' in D.parameters[0]) + Assert.IsFalse('$id' in D.returns) + }) + it('Should should deref 7', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Constructor([R], R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsConstructor(D)) + Assert.IsTrue(TypeGuard.IsString(D.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(D.returns)) + Assert.IsFalse('$id' in D.parameters[0]) + Assert.IsFalse('$id' in D.returns) + }) + it('Should should deref 8', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Promise(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(TypeGuard.IsPromise(D)) + Assert.IsTrue(TypeGuard.IsString(D.item)) + Assert.IsFalse('$id' in D.item) + }) + it('Should should deref 9', () => { + const T = Type.String({ $id: 'T' }) + const R1 = Type.Ref(T, { $id: 'R1' }) + const R2 = Type.Ref(R1, { $id: 'R2' }) + const R3 = Type.Ref(R2, { $id: 'R3' }) + const R4 = Type.Ref(R3, { $id: 'R4' }) + const R5 = Type.Ref(R4, { $id: 'R5' }) + const R6 = Type.Ref(R5, { $id: 'R6' }) + const O = Type.Array(R6) + const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T]) + Assert.IsTrue(TypeGuard.IsArray(D)) + Assert.IsTrue(TypeGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 10', () => { + const T = Type.String({ $id: 'T' }) + const R1 = Type.Ref(T, { $id: 'R1' }) + const R2 = Type.Ref(R1, { $id: 'R2' }) + const R3 = Type.Ref(R2, { $id: 'R3' }) + const R4 = Type.Ref(R3, { $id: 'R4' }) + const R5 = Type.Ref(R4, { $id: 'R5' }) + const R6 = Type.Ref(R5, { $id: 'R6' }) + const O = Type.Array(R6) + Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T + }) +}) diff --git a/test/runtime/type/guard/enum.ts b/test/runtime/type/guard/enum.ts index fd4ee99e1..dc092e838 100644 --- a/test/runtime/type/guard/enum.ts +++ b/test/runtime/type/guard/enum.ts @@ -35,12 +35,12 @@ describe('type/guard/TEnum', () => { // ---------------------------------------------------------------- it('Should guard for Empty 1', () => { const T = Type.Enum({}) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should guard for Empty 2', () => { enum E {} const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) // ---------------------------------------------------------------- @@ -49,14 +49,14 @@ describe('type/guard/TEnum', () => { it('Should guard for TEnum Enum 0', () => { enum E {} const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should guard for TEnum Enum 1', () => { enum E { A, } const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, E.A) }) it('Should guard for TEnum Enum 2', () => { @@ -66,7 +66,7 @@ describe('type/guard/TEnum', () => { C = 3, } const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, E.A) Assert.IsEqual(T.anyOf[1].const, E.B) Assert.IsEqual(T.anyOf[2].const, E.C) @@ -78,7 +78,7 @@ describe('type/guard/TEnum', () => { C = 'Z', } const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, E.A) Assert.IsEqual(T.anyOf[1].const, E.B) Assert.IsEqual(T.anyOf[2].const, E.C) @@ -90,7 +90,7 @@ describe('type/guard/TEnum', () => { C = 'X', } const T = Type.Enum(E) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, E.A) Assert.IsEqual(T.anyOf[1].const, E.B) Assert.IsEqual(T.anyOf.length, 2) @@ -100,11 +100,11 @@ describe('type/guard/TEnum', () => { // ---------------------------------------------------------------- it('Should guard for TEnum Object Literal 0', () => { const T = Type.Enum({}) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should guard for TEnum Object Literal 1', () => { const T = Type.Enum({ A: 1 }) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 1) }) it('Should guard for TEnum Object Literal 2', () => { @@ -113,7 +113,7 @@ describe('type/guard/TEnum', () => { B: 2, C: 3, }) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 1) Assert.IsEqual(T.anyOf[1].const, 2) Assert.IsEqual(T.anyOf[2].const, 3) @@ -124,7 +124,7 @@ describe('type/guard/TEnum', () => { B: 'Y', C: 'Z', }) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'X') Assert.IsEqual(T.anyOf[1].const, 'Y') Assert.IsEqual(T.anyOf[2].const, 'Z') @@ -135,7 +135,7 @@ describe('type/guard/TEnum', () => { B: 'Y', C: 'X', }) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'X') Assert.IsEqual(T.anyOf[1].const, 'Y') Assert.IsEqual(T.anyOf.length, 2) diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts index fe4f06ded..78fbdfe32 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/exclude.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExclude', () => { it('Should extract string from number', () => { const T = Type.Exclude(Type.String(), Type.Number()) - Assert.IsTrue(TypeGuard.TString(T)) + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should extract string from string', () => { const T = Type.Exclude(Type.String(), Type.String()) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should extract string | number | boolean from string', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsTrue(TypeGuard.TNumber(T.anyOf[0])) - Assert.IsTrue(TypeGuard.TBoolean(T.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.IsTrue(TypeGuard.TNumber(T)) + Assert.IsTrue(TypeGuard.IsNumber(T)) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral @@ -28,7 +28,7 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should extract TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) @@ -50,7 +50,7 @@ describe('type/guard/TExclude', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should extract TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) @@ -72,7 +72,7 @@ describe('type/guard/TExclude', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should extract Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts index 9fe5f35ea..b3524d1e0 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/extract.ts @@ -5,21 +5,21 @@ import { Assert } from '../../assert/index' describe('type/guard/TExtract', () => { it('Should extract string from number', () => { const T = Type.Extract(Type.String(), Type.Number()) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should extract string from string', () => { const T = Type.Extract(Type.String(), Type.String()) - Assert.IsTrue(TypeGuard.TString(T)) + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should extract string | number | boolean from string', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - Assert.IsTrue(TypeGuard.TString(T)) + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should extract string | number | boolean from string | boolean', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) - Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsTrue(TypeGuard.TString(T.anyOf[0])) - Assert.IsTrue(TypeGuard.TBoolean(T.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsString(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral diff --git a/test/runtime/type/guard/function.ts b/test/runtime/type/guard/function.ts index 79e66ba6f..400c83004 100644 --- a/test/runtime/type/guard/function.ts +++ b/test/runtime/type/guard/function.ts @@ -4,32 +4,32 @@ import { Assert } from '../../assert/index' describe('type/guard/TFunction', () => { it('Should guard for TFunction', () => { - const R = TypeGuard.TFunction(Type.Function([], Type.Number())) + const R = TypeGuard.IsFunction(Type.Function([], Type.Number())) Assert.IsTrue(R) }) it('Should not guard for TFunction', () => { - const R = TypeGuard.TFunction(null) + const R = TypeGuard.IsFunction(null) Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TFunction(Type.Function([], Type.Number(), { $id: 1 })) + const R = TypeGuard.IsFunction(Type.Function([], Type.Number(), { $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid Params', () => { - const R = TypeGuard.TFunction(Type.Function([{} as any, {} as any], Type.Number())) + const R = TypeGuard.IsFunction(Type.Function([{} as any, {} as any], Type.Number())) Assert.IsFalse(R) }) it('Should not guard for TFunction with invalid Return', () => { - const R = TypeGuard.TFunction(Type.Function([], {} as any)) + const R = TypeGuard.IsFunction(Type.Function([], {} as any)) Assert.IsFalse(R) }) it('Should guard for TFunction with empty Rest Tuple', () => { - const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([])), Type.Number())) + const R = TypeGuard.IsFunction(Type.Function(Type.Rest(Type.Tuple([])), Type.Number())) Assert.IsTrue(R) }) it('Should guard for TFunction with Rest Tuple', () => { - const R = TypeGuard.TFunction(Type.Function(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) + const R = TypeGuard.IsFunction(Type.Function(Type.Rest(Type.Tuple([Type.Number(), Type.String()])), Type.Number())) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 3533d6d53..271b53f88 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -6,8 +6,10 @@ import './bigint' import './boolean' import './capitalize' import './composite' +import './const' import './constructor' import './date' +import './deref' import './enum' import './exclude' import './extract' @@ -20,6 +22,7 @@ import './keyof' import './kind' import './literal' import './lowercase' +import './mapped' import './not' import './null' import './number' @@ -31,6 +34,7 @@ import './promise' import './record' import './recursive' import './ref' +import './regexp' import './required' import './rest' import './string' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 2a680f843..0e29896a9 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -9,7 +9,7 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 2', () => { const T = Type.Object({ @@ -17,9 +17,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, ['x', 'y']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Should Index 3', () => { const T = Type.Object({ @@ -27,9 +27,9 @@ describe('type/guard/TIndex', () => { y: Type.String(), }) const I = Type.Index(T, Type.KeyOf(T)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Should Index 4', () => { const T = Type.Object({ @@ -37,59 +37,59 @@ describe('type/guard/TIndex', () => { ac: Type.String(), }) const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Should Index 5', () => { const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Should Index 6', () => { const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Should Index 7', () => { const T = Type.Array(Type.Null()) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TNull(I)) + Assert.IsTrue(TypeGuard.IsNull(I)) }) it('Should Index 6', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0]) - Assert.IsTrue(TypeGuard.TLiteralString(I)) + Assert.IsTrue(TypeGuard.IsLiteralString(I)) Assert.IsEqual(I.const, 'hello') }) it('Should Index 8', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1]) - Assert.IsTrue(TypeGuard.TLiteralString(I)) + Assert.IsTrue(TypeGuard.IsLiteralString(I)) Assert.IsEqual(I.const, 'world') }) it('Should Index 9', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 1]) - Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) Assert.IsEqual(I.anyOf[0].const, 'hello') Assert.IsEqual(I.anyOf[1].const, 'world') }) it('Should Index 10', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [1, 0]) - Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) Assert.IsEqual(I.anyOf[0].const, 'world') Assert.IsEqual(I.anyOf[1].const, 'hello') }) it('Should Index 11', () => { const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) const I = Type.Index(T, [0, 0, 0, 1]) - Assert.IsTrue(TypeGuard.TUnion(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) Assert.IsEqual(I.anyOf[0].const, 'hello') Assert.IsEqual(I.anyOf[1].const, 'hello') Assert.IsEqual(I.anyOf[2].const, 'hello') @@ -98,47 +98,47 @@ describe('type/guard/TIndex', () => { it('Should Index 12', () => { const T = Type.Tuple([Type.String(), Type.Boolean()]) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TBoolean(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(I.anyOf[1])) }) it('Should Index 13', () => { const T = Type.Tuple([Type.String()]) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) it('Should Index 14', () => { const T = Type.Tuple([]) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.IsNever(I)) }) it('Should Index 15', () => { const T = Type.Object({ 0: Type.Number(), }) const I = Type.Index(T, Type.Literal(0)) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 16', () => { const T = Type.Object({ 0: Type.Number(), }) const I = Type.Index(T, Type.Literal('0')) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 17', () => { const T = Type.Object({ '0': Type.Number(), }) const I = Type.Index(T, Type.Literal(0)) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 18', () => { const T = Type.Object({ '0': Type.Number(), }) const I = Type.Index(T, Type.Literal('0')) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 19', () => { const T = Type.Object({ @@ -147,9 +147,9 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)])) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TBoolean(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsBoolean(I.anyOf[1])) }) it('Should Index 20', () => { const T = Type.Object({ @@ -158,7 +158,7 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.BigInt()) - Assert.IsTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.IsNever(I)) }) it('Should Index 21', () => { const T = Type.Object({ @@ -167,7 +167,7 @@ describe('type/guard/TIndex', () => { 2: Type.Boolean(), }) const I = Type.Index(T, Type.Object({})) - Assert.IsTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.IsNever(I)) }) it('Should Index 22', () => { const A = Type.Object({ x: Type.Literal('A') }) @@ -176,11 +176,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TIntersect(I)) - Assert.IsTrue(TypeGuard.TLiteral(I.allOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(I.allOf[1])) - Assert.IsTrue(TypeGuard.TLiteral(I.allOf[2])) - Assert.IsTrue(TypeGuard.TLiteral(I.allOf[3])) + Assert.IsTrue(TypeGuard.IsIntersect(I)) + Assert.IsTrue(TypeGuard.IsLiteral(I.allOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(I.allOf[1])) + Assert.IsTrue(TypeGuard.IsLiteral(I.allOf[2])) + Assert.IsTrue(TypeGuard.IsLiteral(I.allOf[3])) }) it('Should Index 23', () => { const A = Type.Object({ x: Type.Literal('A') }) @@ -189,11 +189,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Union([A, B, C, D]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[1])) - Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[2])) - Assert.IsTrue(TypeGuard.TLiteral(I.anyOf[3])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsLiteral(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsLiteral(I.anyOf[2])) + Assert.IsTrue(TypeGuard.IsLiteral(I.anyOf[3])) }) it('Should Index 24', () => { const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) @@ -202,9 +202,9 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x', 'y']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsIntersect(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Should Index 25', () => { const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) @@ -213,11 +213,11 @@ describe('type/guard/TIndex', () => { const D = Type.Object({ x: Type.Literal('D') }) const T = Type.Intersect([A, B, C, D]) const I = Type.Index(T, ['x', 'y']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TIntersect(I.anyOf[1])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1].allOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1].allOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsIntersect(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsIntersect(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1].allOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1].allOf[1])) }) it('Should Index 26', () => { const T = Type.Recursive((This) => @@ -228,10 +228,10 @@ describe('type/guard/TIndex', () => { }), ) const I = Type.Index(T, ['x', 'y', 'z']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) - Assert.IsTrue(TypeGuard.TThis(I.anyOf[2])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsThis(I.anyOf[2])) }) it('Should Index 27', () => { const T = Type.Object({ @@ -239,9 +239,9 @@ describe('type/guard/TIndex', () => { 1: Type.Number(), }) const I = Type.Index(T, [0, 1]) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Should Index 28', () => { const T = Type.Object({ @@ -249,9 +249,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, [0, '1']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Should Index 29', () => { const T = Type.Object({ @@ -259,9 +259,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal('1')])) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Should Index 30', () => { const T = Type.Object({ @@ -269,9 +269,9 @@ describe('type/guard/TIndex', () => { '1': Type.Number(), }) const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) // Note: Expect TNever for anyOf[1] but permit for TNumber due to IndexedAccess // Resolve() which currently cannot differentiate between string and numeric keys // on the object. This may be resolvable in later revisions, but test for this @@ -286,8 +286,8 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) it('Should Index 32', () => { const T = Type.Object({ @@ -295,8 +295,8 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['y']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) it('Should Index 33', () => { const T = Type.Object({ @@ -304,15 +304,24 @@ describe('type/guard/TIndex', () => { y: Type.Number(), }) const I = Type.Index(T, ['x', 'y']) - Assert.IsTrue(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Should Index 34', () => { const T = Type.String() - // @ts-ignore const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TNever(I)) + Assert.IsTrue(TypeGuard.IsNever(I)) + }) + it('Should Index 35', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(TypeGuard.IsString(I)) + }) + it('Should Index 36', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, ['[number]']) + Assert.IsTrue(TypeGuard.IsString(I)) }) }) diff --git a/test/runtime/type/guard/integer.ts b/test/runtime/type/guard/integer.ts index 849aabdca..42a8c6120 100644 --- a/test/runtime/type/guard/integer.ts +++ b/test/runtime/type/guard/integer.ts @@ -4,41 +4,41 @@ import { Assert } from '../../assert/index' describe('type/guard/TInteger', () => { it('Should guard for TInteger', () => { - const R = TypeGuard.TInteger(Type.Integer()) + const R = TypeGuard.IsInteger(Type.Integer()) Assert.IsTrue(R) }) it('Should not guard for TInteger', () => { - const R = TypeGuard.TInteger(null) + const R = TypeGuard.IsInteger(null) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ $id: 1 })) + const R = TypeGuard.IsInteger(Type.Integer({ $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid multipleOf', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ multipleOf: '1' })) + const R = TypeGuard.IsInteger(Type.Integer({ multipleOf: '1' })) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid minimum', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ minimum: '1' })) + const R = TypeGuard.IsInteger(Type.Integer({ minimum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid maximum', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ maximum: '1' })) + const R = TypeGuard.IsInteger(Type.Integer({ maximum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid exclusiveMinimum', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ exclusiveMinimum: '1' })) + const R = TypeGuard.IsInteger(Type.Integer({ exclusiveMinimum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TInteger with invalid exclusiveMaximum', () => { // @ts-ignore - const R = TypeGuard.TInteger(Type.Integer({ exclusiveMaximum: '1' })) + const R = TypeGuard.IsInteger(Type.Integer({ exclusiveMaximum: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/intersect.ts index 25c069c7b..ab2b58466 100644 --- a/test/runtime/type/guard/intersect.ts +++ b/test/runtime/type/guard/intersect.ts @@ -4,7 +4,7 @@ import { Assert } from '../../assert/index' describe('type/guard/TIntersect', () => { it('Should guard for TIntersect', () => { - const R = TypeGuard.TIntersect( + const R = TypeGuard.IsIntersect( Type.Intersect([ Type.Object({ x: Type.Number(), @@ -17,7 +17,7 @@ describe('type/guard/TIntersect', () => { Assert.IsTrue(R) }) it('Should not guard for TIntersect', () => { - const R = TypeGuard.TIntersect( + const R = TypeGuard.IsIntersect( Type.Union([ Type.Object({ x: Type.Number(), diff --git a/test/runtime/type/guard/iterator.ts b/test/runtime/type/guard/iterator.ts index b0dd08c4a..df549edd1 100644 --- a/test/runtime/type/guard/iterator.ts +++ b/test/runtime/type/guard/iterator.ts @@ -5,18 +5,18 @@ import { Assert } from '../../assert/index' describe('type/guard/TIterator', () => { it('Should guard for TIterator', () => { const T = Type.Iterator(Type.Any()) - const R = TypeGuard.TIterator(T) + const R = TypeGuard.IsIterator(T) Assert.IsTrue(R) }) it('Should not guard for TIterator', () => { const T = null - const R = TypeGuard.TIterator(T) + const R = TypeGuard.IsIterator(T) Assert.IsFalse(R) }) it('Should not guard for TIterator with invalid $id', () => { //@ts-ignore const T = Type.Iterator(Type.Any(), { $id: 1 }) - const R = TypeGuard.TIterator(T) + const R = TypeGuard.IsIterator(T) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/keyof.ts index d8b24ad74..1b066c4ed 100644 --- a/test/runtime/type/guard/keyof.ts +++ b/test/runtime/type/guard/keyof.ts @@ -9,9 +9,9 @@ describe('type/guard/TKeyOf', () => { y: Type.Number(), }) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TUnion(K)) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(K)) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[1])) }) it('Should KeyOf 2', () => { const T = Type.Recursive((Self) => @@ -21,9 +21,9 @@ describe('type/guard/TKeyOf', () => { }), ) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TUnion(K)) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(K)) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[1])) }) it('Should KeyOf 3', () => { const T = Type.Intersect([ @@ -35,9 +35,9 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TUnion(K)) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(K.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(K)) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(K.anyOf[1])) }) it('Should KeyOf 4', () => { const T = Type.Union([ @@ -49,27 +49,27 @@ describe('type/guard/TKeyOf', () => { }), ]) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TNever(K)) + Assert.IsTrue(TypeGuard.IsNever(K)) }) it('Should KeyOf 5', () => { const T = Type.Null() const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TNever(K)) + Assert.IsTrue(TypeGuard.IsNever(K)) }) it('Should KeyOf 6', () => { const T = Type.Array(Type.Number()) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TNumber(K)) + Assert.IsTrue(TypeGuard.IsNumber(K)) }) it('Should KeyOf 7', () => { const T = Type.Tuple([]) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TNever(K)) + Assert.IsTrue(TypeGuard.IsNever(K)) }) it('Should KeyOf 8', () => { const T = Type.Tuple([Type.Number(), Type.Null()]) const K = Type.KeyOf(T) - Assert.IsTrue(TypeGuard.TUnion(K)) + Assert.IsTrue(TypeGuard.IsUnion(K)) Assert.IsEqual(K.anyOf[0].const, '0') Assert.IsEqual(K.anyOf[1].const, '1') }) diff --git a/test/runtime/type/guard/kind.ts b/test/runtime/type/guard/kind.ts index 52c87f883..26006958f 100644 --- a/test/runtime/type/guard/kind.ts +++ b/test/runtime/type/guard/kind.ts @@ -4,10 +4,10 @@ import { Assert } from '../../assert/index' describe('type/guard/TKind', () => { it('Should guard 1', () => { const T = { [Kind]: 'Kind' } - Assert.IsTrue(TypeGuard.TKind(T)) + Assert.IsTrue(TypeGuard.IsKind(T)) }) it('Should guard 2', () => { const T = {} - Assert.IsFalse(TypeGuard.TKind(T)) + Assert.IsFalse(TypeGuard.IsKind(T)) }) }) diff --git a/test/runtime/type/guard/literal.ts b/test/runtime/type/guard/literal.ts index b982a5e7e..c1e6b21f5 100644 --- a/test/runtime/type/guard/literal.ts +++ b/test/runtime/type/guard/literal.ts @@ -4,29 +4,29 @@ import { Assert } from '../../assert/index' describe('type/guard/TLiteral', () => { it('Should guard for TLiteral of String', () => { - const R = TypeGuard.TLiteral(Type.Literal('hello')) + const R = TypeGuard.IsLiteral(Type.Literal('hello')) Assert.IsTrue(R) }) it('Should guard for TLiteral of Number', () => { - const R = TypeGuard.TLiteral(Type.Literal(42)) + const R = TypeGuard.IsLiteral(Type.Literal(42)) Assert.IsTrue(R) }) it('Should guard for TLiteral of Boolean', () => { - const R = TypeGuard.TLiteral(Type.Literal(true)) + const R = TypeGuard.IsLiteral(Type.Literal(true)) Assert.IsTrue(R) }) it('Should not guard for TLiteral of Null', () => { // @ts-ignore - const R = TypeGuard.TLiteral(Type.Literal(null)) + const R = TypeGuard.IsLiteral(Type.Literal(null)) Assert.IsFalse(R) }) it('Should not guard for TLiteral', () => { - const R = TypeGuard.TLiteral(null) + const R = TypeGuard.IsLiteral(null) Assert.IsFalse(R) }) it('Should not guard for TLiteral with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TLiteral(Type.Literal(42, { $id: 1 })) + const R = TypeGuard.IsLiteral(Type.Literal(42, { $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/lowercase.ts b/test/runtime/type/guard/lowercase.ts index 5a07e5d41..be9e797cd 100644 --- a/test/runtime/type/guard/lowercase.ts +++ b/test/runtime/type/guard/lowercase.ts @@ -4,30 +4,30 @@ import { Assert } from '../../assert/index' describe('type/guard/Lowercase', () => { it('Should guard for Lowercase 1', () => { const T = Type.Lowercase(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hello') Assert.IsEqual(T.$id, 'hello') Assert.IsEqual(T.foo, 1) }) it('Should guard for Lowercase 2', () => { const T = Type.Lowercase(Type.Literal('HELLO')) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hello') }) it('Should guard for Lowercase 3', () => { const T = Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') }) it('Should guard for Lowercase 4', () => { const T = Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}')) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello0|hello1)$') }) it('Should guard for Lowercase 5', () => { const T = Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello0|hello1)$') }) }) diff --git a/test/runtime/type/guard/mapped.ts b/test/runtime/type/guard/mapped.ts new file mode 100644 index 000000000..1ab3daf9f --- /dev/null +++ b/test/runtime/type/guard/mapped.ts @@ -0,0 +1,609 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('type/guard/Mapped', () => { + it('Should guard mapped 1', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), _ => Type.Number(), { custom: 1 }) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + }, { custom: 1 })) + }) + it('Should guard mapped 2', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), _ => Type.Number()) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + })) + }) + it('Should guard mapped 3', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), K => K) + Assert.IsEqual(T, Type.Object({ + x: Type.Literal('x'), + y: Type.Literal('y'), + z: Type.Literal('z'), + })) + }) + it('Should guard mapped 4', () => { + const T = Type.Mapped(Type.TemplateLiteral('${0|1}${0|1}'), K => Type.Number()) + Assert.IsEqual(T, Type.Object({ + '00': Type.Number(), + '01': Type.Number(), + '10': Type.Number(), + '11': Type.Number(), + })) + }) + it('Should guard mapped 5', () => { + const T = Type.Mapped(Type.TemplateLiteral('${a|b}'), X => + Type.Mapped(Type.TemplateLiteral('${c|d}'), Y => + Type.Mapped(Type.TemplateLiteral('${e|f}'), Z => + Type.Tuple([X, Y, Z]) + ) + ) + ) + Assert.IsEqual(T, Type.Object({ + a: Type.Object({ + c: Type.Object({ + e: Type.Tuple([Type.Literal("a"), Type.Literal("c"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("a"), Type.Literal("c"), Type.Literal("f")]) + }), + d: Type.Object({ + e: Type.Tuple([Type.Literal("a"), Type.Literal("d"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("a"), Type.Literal("d"), Type.Literal("f")]) + }), + }), + b: Type.Object({ + c: Type.Object({ + e: Type.Tuple([Type.Literal("b"), Type.Literal("c"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("b"), Type.Literal("c"), Type.Literal("f")]) + }), + d: Type.Object({ + e: Type.Tuple([Type.Literal("b"), Type.Literal("d"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("b"), Type.Literal("d"), Type.Literal("f")]) + }), + }), + })) + }) + it('Should guard mapped 6', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => K) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal('x'), + y: Type.Literal('y'), + z: Type.Literal('z') + })) + }) + it('Should guard mapped 7', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + })) + }) + it('Should guard mapped 8', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K, { custom: 1 })) + Assert.IsEqual(M, Type.Object({ + x: Type.Number({ custom: 1 }), + y: Type.String({ custom: 1 }), + z: Type.Boolean({ custom: 1 }) + })) + }) + // ---------------------------------------------------------------- + // Extract + // ---------------------------------------------------------------- + it('Should guard mapped 9', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.String()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.String() + })) + }) + it('Should guard mapped 10', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.String(), Type.Number()]) + })) + }) + it('Should guard mapped 11', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Null()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Never() + })) + }) + // ---------------------------------------------------------------- + // Extends + // ---------------------------------------------------------------- + it('Should guard mapped 12', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(K, Type.Literal('x'), Type.Literal(1), + Type.Extends(K, Type.Literal('y'), Type.Literal(2), + Type.Extends(K, Type.Literal('z'), Type.Literal(3), Type.Never()))) + ) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal(1), + y: Type.Literal(2), + z: Type.Literal(3), + })) + }) + it('Should guard mapped 13', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(Type.Index(T, K), Type.Number(), Type.Literal(3), + Type.Extends(Type.Index(T, K), Type.String(), Type.Literal(2), + Type.Extends(Type.Index(T, K), Type.Boolean(), Type.Literal(1), Type.Never()))) + ) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal(3), + y: Type.Literal(2), + z: Type.Literal(1), + })) + }) + // ---------------------------------------------------------------- + // Exclude + // ---------------------------------------------------------------- + it('Should guard mapped 14', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.String()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.Number(), Type.Boolean()]) + })) + }) + it('Should guard mapped 15', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Boolean() + })) + }) + it('Should guard mapped 16', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Null()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + })) + }) + // ---------------------------------------------------------------- + // Non-Evaluated + // ---------------------------------------------------------------- + it('Should guard mapped 17', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Array(Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Array(Type.Number()) })) + }) + it('Should guard mapped 18', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Promise(Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Promise(Type.Number()) })) + }) + it('Should guard mapped 19', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Function([Type.Index(T, K)], Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Function([Type.Number()], Type.Number())})) + }) + it('Should guard mapped 20', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Tuple([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Tuple([Type.Number(), Type.Number()]) })) + }) + it('Should guard mapped 21', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Union([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Union([Type.Number(), Type.Number()]) })) + }) + it('Should guard mapped 22', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Intersect([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Intersect([Type.Number(), Type.Number()]) })) + }) + // ---------------------------------------------------------------- + // Numeric Keys + // ---------------------------------------------------------------- + it('Should guard mapped 23', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => K) + Assert.IsEqual(M, Type.Object({ + 0: Type.Literal('0'), + 1: Type.Literal('1'), + 2: Type.Literal('2'), + })) + }) + it('Should guard mapped 24', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Assert.IsEqual(M, Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number(), + })) + }) + it('Should guard mapped 25', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.String()) + Assert.IsEqual(M, Type.Object({ + 0: Type.String(), + 1: Type.String(), + 2: Type.String(), + })) + }) + it('Should guard mapped 26', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Extends(K, Type.Literal('1'), Type.String(), Type.Number())) + Assert.IsEqual(M, Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Number(), + })) + }) + // ---------------------------------------------------------------- + // Modifiers: Optional + // ---------------------------------------------------------------- + it('Should guard mapped 27', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number() + }) + // subtractive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), false)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.Number() + })) + }) + it('Should guard mapped 28', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number() + }) + // additive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), true)) + Assert.IsEqual(M, Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()) + })) + }) + // ---------------------------------------------------------------- + // Modifiers: Readonly + // ---------------------------------------------------------------- + it('Should guard mapped 27', () => { + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Number() + }) + // subtractive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), false)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.Number() + })) + }) + it('Should guard mapped 28', () => { + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Number() + }) + // additive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), true)) + Assert.IsEqual(M, Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Readonly(Type.Number()) + })) + }) + // ---------------------------------------------------------------- + // Finite Boolean + // ---------------------------------------------------------------- + it('Should guard mapped 29', () => { + const T = Type.TemplateLiteral('${boolean}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + true: Type.Literal('true'), + false: Type.Literal('false'), + })) + }) + it('Should guard mapped 30', () => { + const T = Type.TemplateLiteral('${0|1}${boolean}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + '0true': Type.Literal('0true'), + '0false': Type.Literal('0false'), + '1true': Type.Literal('1true'), + '1false': Type.Literal('1false'), + })) + }) + it('Should guard mapped 31', () => { + const T = Type.TemplateLiteral('${boolean}${0|1}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + 'true0': Type.Literal('true0'), + 'true1': Type.Literal('true1'), + 'false0': Type.Literal('false0'), + 'false1': Type.Literal('false1'), + })) + }) + // ---------------------------------------------------------------- + // Numeric Mapping + // ---------------------------------------------------------------- + it('Should guard mapped 32', () => { + const T = Type.TemplateLiteral([ + Type.Union([Type.Literal(0), Type.Literal(1)]), + Type.Union([Type.Literal(0), Type.Literal(1)]), + ]) + const M = Type.Mapped(T, (K) => K) + Assert.IsEqual(M, Type.Object({ + '00': Type.Literal('00'), + '01': Type.Literal('01'), + '10': Type.Literal('10'), + '11': Type.Literal('11'), + })) + }) + // ---------------------------------------------------------------- + // Indexed Key Remap + // ---------------------------------------------------------------- + it('Should guard mapped 33', () => { + const T = Type.Object({ + hello: Type.Number(), + world: Type.String(), + }) + const M = Type.Mapped(Type.Uppercase(Type.KeyOf(T)), (K) => { + return Type.Index(T, Type.Lowercase(K)) + }) + Assert.IsEqual(M, Type.Object({ + HELLO: Type.Number(), + WORLD: Type.String() + })) + }) + // ---------------------------------------------------------------- + // Partial + // ---------------------------------------------------------------- + it('Should guard mapped 34', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Partial(Type.Index(T, K)) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Partial(Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + })), + y: Type.Partial(Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + })), + })) + }) + // ---------------------------------------------------------------- + // Required + // ---------------------------------------------------------------- + it('Should guard mapped 35', () => { + const T = Type.Object({ + x: Type.Partial(Type.Object({ + x: Type.Number(), + y: Type.Number(), + })), + y: Type.Partial(Type.Object({ + x: Type.Number(), + y: Type.Number(), + })), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Required(Type.Index(T, K)) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + })) + }) + // ------------------------------------------------------------------ + // Pick With Key + // ------------------------------------------------------------------ + it('Should guard mapped 36', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Pick(T, K) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + y: Type.Object({ + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + })) + }) + // ------------------------------------------------------------------ + // Pick With Result + // ------------------------------------------------------------------ + it('Should guard mapped 37', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Pick(Type.Index(T, K), ['x']) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ x: Type.Number() }), + y: Type.Object({ x: Type.Number() }) + })) + }) + // ------------------------------------------------------------------ + // Omit With Key + // ------------------------------------------------------------------ + it('Should guard mapped 36', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Omit(T, K) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + y: Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + })) + }) + // ------------------------------------------------------------------ + // Omit With Result + // ------------------------------------------------------------------ + it('Should guard mapped 37', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Omit(Type.Index(T, K), ['x']) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ y: Type.Number() }), + y: Type.Object({ y: Type.Number() }) + })) + }) +}) diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/not.ts index 71e5695e4..b21cb5d7c 100644 --- a/test/runtime/type/guard/not.ts +++ b/test/runtime/type/guard/not.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TNot', () => { it('Should guard for TNot', () => { - const R = TypeGuard.TNot(Type.Not(Type.String())) + const R = TypeGuard.IsNot(Type.Not(Type.String())) Assert.IsTrue(R) }) it('Should not guard for TNot 1', () => { - const R = TypeGuard.TNot(Type.Not(null as any)) + const R = TypeGuard.IsNot(Type.Not(null as any)) Assert.IsFalse(R) }) it('Should not guard for TNot with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TNot(Type.Not(Type.String()), { $id: 1 }) + const R = TypeGuard.IsNot(Type.Not(Type.String()), { $id: 1 }) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/null.ts b/test/runtime/type/guard/null.ts index dbeca23ae..0ab379276 100644 --- a/test/runtime/type/guard/null.ts +++ b/test/runtime/type/guard/null.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TNull', () => { it('Should guard for TNull', () => { - const R = TypeGuard.TNull(Type.Null()) + const R = TypeGuard.IsNull(Type.Null()) Assert.IsTrue(R) }) it('Should not guard for TNull', () => { - const R = TypeGuard.TNull(null) + const R = TypeGuard.IsNull(null) Assert.IsFalse(R) }) it('Should not guard for TNull with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TNull(Type.Null({ $id: 1 })) + const R = TypeGuard.IsNull(Type.Null({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/number.ts b/test/runtime/type/guard/number.ts index 02b911665..02ba9d6a7 100644 --- a/test/runtime/type/guard/number.ts +++ b/test/runtime/type/guard/number.ts @@ -4,41 +4,41 @@ import { Assert } from '../../assert/index' describe('type/guard/TNumber', () => { it('Should guard for TNumber', () => { - const R = TypeGuard.TNumber(Type.Number()) + const R = TypeGuard.IsNumber(Type.Number()) Assert.IsTrue(R) }) it('Should not guard for TNumber', () => { - const R = TypeGuard.TNumber(null) + const R = TypeGuard.IsNumber(null) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ $id: 1 })) + const R = TypeGuard.IsNumber(Type.Number({ $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid multipleOf', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ multipleOf: '1' })) + const R = TypeGuard.IsNumber(Type.Number({ multipleOf: '1' })) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid minimum', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ minimum: '1' })) + const R = TypeGuard.IsNumber(Type.Number({ minimum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid maximum', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ maximum: '1' })) + const R = TypeGuard.IsNumber(Type.Number({ maximum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid exclusiveMinimum', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ exclusiveMinimum: '1' })) + const R = TypeGuard.IsNumber(Type.Number({ exclusiveMinimum: '1' })) Assert.IsFalse(R) }) it('Should not guard for TNumber with invalid exclusiveMaximum', () => { // @ts-ignore - const R = TypeGuard.TNumber(Type.Number({ exclusiveMaximum: '1' })) + const R = TypeGuard.IsNumber(Type.Number({ exclusiveMaximum: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/object.ts b/test/runtime/type/guard/object.ts index 4f7cfcddf..ff5b2f0e2 100644 --- a/test/runtime/type/guard/object.ts +++ b/test/runtime/type/guard/object.ts @@ -4,7 +4,7 @@ import { Assert } from '../../assert/index' describe('type/guard/TObject', () => { it('Should guard for TObject', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object({ x: Type.Number(), y: Type.Number(), @@ -13,11 +13,11 @@ describe('type/guard/TObject', () => { Assert.IsTrue(R) }) it('Should not guard for TObject', () => { - const R = TypeGuard.TObject(null) + const R = TypeGuard.IsObject(null) Assert.IsFalse(R) }) it('Should not guard for TObject with escape characters in property key', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object({ 'hello\nworld': Type.Number(), }), @@ -25,7 +25,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with invalid property values', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object({ x: Type.Number(), y: {} as any, @@ -34,7 +34,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with invalid additionalProperties', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), @@ -48,7 +48,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with invalid $id', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), @@ -62,7 +62,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with invalid minProperties', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), @@ -76,7 +76,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with invalid maxProperties', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), @@ -90,7 +90,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should guard for TObject with invalid additional properties', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), @@ -105,7 +105,7 @@ describe('type/guard/TObject', () => { Assert.IsFalse(R) }) it('Should not guard for TObject with valid additional properties schema', () => { - const R = TypeGuard.TObject( + const R = TypeGuard.IsObject( Type.Object( { x: Type.Number(), diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 7b19d146c..c449cc7e6 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -1,4 +1,4 @@ -import { TypeGuard, Type, Kind, Transform } from '@sinclair/typebox' +import { TypeGuard, Type, Kind, TransformKind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TOmit', () => { @@ -24,7 +24,7 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsEqual(T.required, ['y']) }) it('Should Omit 2', () => { @@ -35,7 +35,7 @@ describe('type/guard/TOmit', () => { }), ['x'], ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsEqual(T.required, undefined) }) it('Should Omit 3', () => { @@ -47,13 +47,13 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsEqual(T.required, ['y']) }) it('Should Omit 4', () => { const L = Type.Literal('x') const T = Type.Omit(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.IsEqual(TypeGuard.TNumber(T.allOf[1].properties.y), true) + Assert.IsEqual(TypeGuard.IsNumber(T.allOf[1].properties.y), true) // @ts-ignore Assert.IsEqual(T.allOf[1].properties.x, undefined) }) @@ -99,7 +99,7 @@ describe('type/guard/TOmit', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.ad)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.ad)) Assert.IsEqual(T.required, ['ad']) }) // ---------------------------------------------------------------- @@ -124,6 +124,6 @@ describe('type/guard/TOmit', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Omit(S, ['x']) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index cd248bb31..6c419f451 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -1,10 +1,10 @@ -import { TypeGuard, TypeRegistry, Type, Kind, Transform } from '@sinclair/typebox' +import { TypeGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TPartial', () => { it('Should produce a valid TSchema', () => { const T = Type.Partial(Type.Object({ x: Type.Number() })) - Assert.IsTrue(TypeGuard.TSchema(T)) + Assert.IsTrue(TypeGuard.IsSchema(T)) }) // ------------------------------------------------------------------------- // case: https://github.com/sinclairzx81/typebox/issues/364 @@ -62,6 +62,6 @@ describe('type/guard/TPartial', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Partial(S) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 0fcd90573..7b53d4da9 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -1,4 +1,4 @@ -import { TypeGuard, Type, Kind, Transform } from '@sinclair/typebox' +import { TypeGuard, Type, Kind, TransformKind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TPick', () => { @@ -30,7 +30,7 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsEqual(T.required, ['x']) }) it('Should Pick 2', () => { @@ -41,7 +41,7 @@ describe('type/guard/TPick', () => { }), ['x'], ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsEqual(T.required, undefined) }) it('Should Pick 3', () => { @@ -53,14 +53,14 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsEqual(T.required, ['x']) }) it('Should Pick 4', () => { const L = Type.Literal('x') const T = Type.Pick(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) - Assert.IsTrue(TypeGuard.TNumber(T.allOf[0].properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.allOf[0].properties.x)) // @ts-ignore Assert.IsEqual(T.allOf[1].properties.y, undefined) }) @@ -73,8 +73,8 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsEqual(T.required, ['x', 'y']) }) it('Should Pick 6', () => { @@ -86,8 +86,8 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.x)) - Assert.IsTrue(TypeGuard.TNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsEqual(T.required, ['x', 'y']) }) it('Should Pick 7', () => { @@ -100,8 +100,8 @@ describe('type/guard/TPick', () => { }), L, ) - Assert.IsTrue(TypeGuard.TNumber(T.properties.ab)) - Assert.IsTrue(TypeGuard.TNumber(T.properties.ac)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.ab)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.ac)) Assert.IsEqual(T.required, ['ab', 'ac']) }) // ---------------------------------------------------------------- @@ -126,6 +126,6 @@ describe('type/guard/TPick', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Pick(S, ['x']) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/promise.ts b/test/runtime/type/guard/promise.ts index f00ecc78a..7fab077b8 100644 --- a/test/runtime/type/guard/promise.ts +++ b/test/runtime/type/guard/promise.ts @@ -4,20 +4,20 @@ import { Assert } from '../../assert/index' describe('type/guard/TPromise', () => { it('Should guard for TPromise', () => { - const R = TypeGuard.TPromise(Type.Promise(Type.Number())) + const R = TypeGuard.IsPromise(Type.Promise(Type.Number())) Assert.IsTrue(R) }) it('Should not guard for TPromise', () => { - const R = TypeGuard.TPromise(null) + const R = TypeGuard.IsPromise(null) Assert.IsFalse(R) }) it('Should not guard for TPromise with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TPromise(Type.Promise(Type.Number(), { $id: 1 })) + const R = TypeGuard.IsPromise(Type.Promise(Type.Number(), { $id: 1 })) Assert.IsFalse(R) }) it('Should guard for TPromise with nested TObject', () => { - const R = TypeGuard.TPromise( + const R = TypeGuard.IsPromise( Type.Promise( Type.Object({ x: Type.Number(), @@ -28,7 +28,7 @@ describe('type/guard/TPromise', () => { Assert.IsTrue(R) }) it('Should not guard for TPromise with nested TObject', () => { - const R = TypeGuard.TPromise( + const R = TypeGuard.IsPromise( Type.Promise( Type.Object({ x: Type.Number(), diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 987d8bd84..89ec3dfae 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -8,73 +8,73 @@ describe('type/guard/TRecord', () => { // ------------------------------------------------------------- it('Should guard overload 1', () => { const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TObject(T)) - Assert.IsTrue(TypeGuard.TString(T.properties.A)) - Assert.IsTrue(TypeGuard.TString(T.properties.B)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsString(T.properties.B)) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 2', () => { const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal - Assert.IsTrue(TypeGuard.TObject(T)) - Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 3', () => { // @ts-ignore const T = Type.Record(Type.Union([]), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should guard overload 4', () => { // @ts-ignore const T = Type.Record(Type.BigInt(), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TNever(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should guard overload 5', () => { const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TObject(T)) - Assert.IsTrue(TypeGuard.TString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 6', () => { const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TObject(T)) - Assert.IsTrue(TypeGuard.TString(T.properties.helloA)) - Assert.IsTrue(TypeGuard.TString(T.properties.helloB)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.helloA)) + Assert.IsTrue(TypeGuard.IsString(T.properties.helloB)) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 7', () => { const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TRecord(T)) - Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNumberExact])) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 8', () => { const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TRecord(T)) - Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternNumberExact])) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNumberExact])) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 9', () => { const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TRecord(T)) - Assert.IsTrue(TypeGuard.TString(T.patternProperties[PatternStringExact])) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternStringExact])) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 10', () => { const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) const T = Type.Record(L, Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.TRecord(T)) - Assert.IsTrue(TypeGuard.TString(T.patternProperties[`^${PatternString}_foo$`])) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[`^${PatternString}_foo$`])) Assert.IsEqual(T.extra, 1) }) it('Should guard overload 11', () => { const L = Type.Union([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) const T = Type.Record(L, Type.String()) - Assert.IsTrue(TypeGuard.TObject(T)) - Assert.IsTrue(TypeGuard.TString(T.properties.A)) - Assert.IsTrue(TypeGuard.TString(T.properties.B)) - Assert.IsTrue(TypeGuard.TString(T.properties.C)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsString(T.properties.B)) + Assert.IsTrue(TypeGuard.IsString(T.properties.C)) }) it('Should guard overload 12', () => { enum E { @@ -84,20 +84,20 @@ describe('type/guard/TRecord', () => { } const T = Type.Enum(E) const R = Type.Record(T, Type.Null()) - Assert.IsTrue(TypeGuard.TObject(R)) - Assert.IsTrue(TypeGuard.TNull(R.properties.X)) - Assert.IsTrue(TypeGuard.TNull(R.properties.Y)) - Assert.IsTrue(TypeGuard.TNull(R.properties.Z)) + Assert.IsTrue(TypeGuard.IsObject(R)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.X)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.Y)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.Z)) }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- it('Should guard for TRecord', () => { - const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number())) + const R = TypeGuard.IsRecord(Type.Record(Type.String(), Type.Number())) Assert.IsTrue(R) }) it('Should guard for TRecord with TObject value', () => { - const R = TypeGuard.TRecord( + const R = TypeGuard.IsRecord( Type.Record( Type.String(), Type.Object({ @@ -109,16 +109,16 @@ describe('type/guard/TRecord', () => { Assert.IsTrue(R) }) it('Should not guard for TRecord', () => { - const R = TypeGuard.TRecord(null) + const R = TypeGuard.IsRecord(null) Assert.IsFalse(R) }) it('Should not guard for TRecord with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) + const R = TypeGuard.IsRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TRecord with TObject value with invalid Property', () => { - const R = TypeGuard.TRecord( + const R = TypeGuard.IsRecord( Type.Record( Type.String(), Type.Object({ @@ -131,12 +131,12 @@ describe('type/guard/TRecord', () => { }) it('Normalize: Should should normalize to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) - const R = TypeGuard.TObject(Type.Record(K, Type.Number())) + const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) it('Normalize: Should should normalize to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) - const R = TypeGuard.TObject(Type.Record(K, Type.Number())) + const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/guard/recursive.ts b/test/runtime/type/guard/recursive.ts index 8585e17b1..57c6fe775 100644 --- a/test/runtime/type/guard/recursive.ts +++ b/test/runtime/type/guard/recursive.ts @@ -5,12 +5,12 @@ import { Assert } from '../../assert/index' describe('type/guard/TRecursive', () => { it('Should guard 1', () => { const T = Type.Recursive((This) => Type.Object({ nodes: This })) - Assert.IsTrue(TypeGuard.TRecursive(T)) - Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.IsRecursive(T)) + Assert.IsTrue(TypeGuard.IsObject(T)) }) it('Should guard 2', () => { const T = Type.Recursive((This) => Type.Tuple([This])) - Assert.IsTrue(TypeGuard.TRecursive(T)) - Assert.IsTrue(TypeGuard.TTuple(T)) + Assert.IsTrue(TypeGuard.IsRecursive(T)) + Assert.IsTrue(TypeGuard.IsTuple(T)) }) }) diff --git a/test/runtime/type/guard/ref.ts b/test/runtime/type/guard/ref.ts index 83682d29d..e2b5a74b8 100644 --- a/test/runtime/type/guard/ref.ts +++ b/test/runtime/type/guard/ref.ts @@ -5,11 +5,11 @@ import { Assert } from '../../assert/index' describe('type/guard/TRef', () => { it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) - const R = TypeGuard.TRef(Type.Ref(T)) + const R = TypeGuard.IsRef(Type.Ref(T)) Assert.IsTrue(R) }) it('Should not guard for TRef', () => { - const R = TypeGuard.TRef(null) + const R = TypeGuard.IsRef(null) Assert.IsFalse(R) }) it('Should not guard for TRef with invalid $ref', () => { @@ -17,7 +17,7 @@ describe('type/guard/TRef', () => { const S = Type.Ref(T) // @ts-ignore S.$ref = 1 - const R = TypeGuard.TRef(S) + const R = TypeGuard.IsRef(S) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/regexp.ts b/test/runtime/type/guard/regexp.ts new file mode 100644 index 000000000..845c1753d --- /dev/null +++ b/test/runtime/type/guard/regexp.ts @@ -0,0 +1,23 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/guard/TRegExp', () => { + it('Should guard for TRegExp 1', () => { + const T = Type.RegExp(/foo/, { $id: 'T' }) + Assert.IsTrue(TypeGuard.IsSchema(T)) + }) + it('Should guard for TRegExp 1', () => { + const T = Type.RegExp(/foo/, { $id: 'T' }) + Assert.IsTrue(TypeGuard.IsRegExp(T)) + }) + it('Should guard for TRegExp 2', () => { + const T = Type.RegExp('foo', { $id: 'T' }) + Assert.IsTrue(TypeGuard.IsRegExp(T)) + }) + it('Should not guard for TRegExp 1', () => { + // @ts-ignore + const T = Type.RegExp('foo', { $id: 1 }) + Assert.IsFalse(TypeGuard.IsRegExp(T)) + }) +}) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/required.ts index 5af8134d5..e4315e912 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/required.ts @@ -1,10 +1,10 @@ -import { TypeGuard, TypeRegistry, Type, Kind, Transform } from '@sinclair/typebox' +import { TypeGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TRequired', () => { it('Should produce a valid TSchema', () => { const T = Type.Required(Type.Object({ x: Type.Number() })) - Assert.IsTrue(TypeGuard.TSchema(T)) + Assert.IsTrue(TypeGuard.IsSchema(T)) }) it('Should support TUnsafe required properties with no Kind', () => { const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ x: 1 })) })) @@ -59,6 +59,6 @@ describe('type/guard/TRequired', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Required(S) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/rest.ts b/test/runtime/type/guard/rest.ts index 4c22803d5..dc018d6e7 100644 --- a/test/runtime/type/guard/rest.ts +++ b/test/runtime/type/guard/rest.ts @@ -6,29 +6,29 @@ describe('type/guard/TRest', () => { // union never const A = Type.String() const B = Type.Union(Type.Rest(A)) - Assert.IsTrue(TypeGuard.TNever(B)) + Assert.IsTrue(TypeGuard.IsNever(B)) }) it('Should guard 2', () => { // intersect never const A = Type.String() const B = Type.Intersect(Type.Rest(A)) - Assert.IsTrue(TypeGuard.TNever(B)) + Assert.IsTrue(TypeGuard.IsNever(B)) }) it('Should guard 3', () => { // tuple const A = Type.Tuple([Type.Number(), Type.String()]) const B = Type.Union(Type.Rest(A)) - Assert.IsTrue(TypeGuard.TUnion(B)) + Assert.IsTrue(TypeGuard.IsUnion(B)) Assert.IsEqual(B.anyOf.length, 2) - Assert.IsTrue(TypeGuard.TNumber(B.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(B.anyOf[1])) + Assert.IsTrue(TypeGuard.IsNumber(B.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(B.anyOf[1])) }) it('Should guard 4', () => { // tuple spread const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) - Assert.IsTrue(TypeGuard.TTuple(C)) + Assert.IsTrue(TypeGuard.IsTuple(C)) Assert.IsEqual(C.items!.length, 4) Assert.IsEqual(C.items![0].const, 1) Assert.IsEqual(C.items![1].const, 2) @@ -41,10 +41,10 @@ describe('type/guard/TRest', () => { const B = Type.Object({ y: Type.String() }) const C = Type.Union([A, B]) const D = Type.Intersect(Type.Rest(C)) - Assert.IsTrue(TypeGuard.TIntersect(D)) + Assert.IsTrue(TypeGuard.IsIntersect(D)) Assert.IsEqual(D.allOf.length, 2) - Assert.IsTrue(TypeGuard.TObject(D.allOf[0])) - Assert.IsTrue(TypeGuard.TObject(D.allOf[1])) + Assert.IsTrue(TypeGuard.IsObject(D.allOf[0])) + Assert.IsTrue(TypeGuard.IsObject(D.allOf[1])) }) it('Should guard 6', () => { // intersect to composite @@ -52,8 +52,8 @@ describe('type/guard/TRest', () => { const B = Type.Object({ y: Type.String() }) const C = Type.Intersect([A, B]) const D = Type.Composite(Type.Rest(C)) - Assert.IsTrue(TypeGuard.TObject(D)) - Assert.IsTrue(TypeGuard.TNumber(D.properties.x)) - Assert.IsTrue(TypeGuard.TString(D.properties.y)) + Assert.IsTrue(TypeGuard.IsObject(D)) + Assert.IsTrue(TypeGuard.IsNumber(D.properties.x)) + Assert.IsTrue(TypeGuard.IsString(D.properties.y)) }) }) diff --git a/test/runtime/type/guard/string.ts b/test/runtime/type/guard/string.ts index 50205a1da..5eb862683 100644 --- a/test/runtime/type/guard/string.ts +++ b/test/runtime/type/guard/string.ts @@ -4,31 +4,31 @@ import { Assert } from '../../assert/index' describe('type/guard/TString', () => { it('Should guard for TString', () => { - const R = TypeGuard.TString(Type.String()) + const R = TypeGuard.IsString(Type.String()) Assert.IsTrue(R) }) it('Should not guard for TString', () => { - const R = TypeGuard.TString(null) + const R = TypeGuard.IsString(null) Assert.IsFalse(R) }) it('Should not guard for TString with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TString(Type.String({ $id: 1 })) + const R = TypeGuard.IsString(Type.String({ $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TString with invalid minLength', () => { // @ts-ignore - const R = TypeGuard.TString(Type.String({ minLength: '1' })) + const R = TypeGuard.IsString(Type.String({ minLength: '1' })) Assert.IsFalse(R) }) it('Should not guard for TString with invalid maxLength', () => { // @ts-ignore - const R = TypeGuard.TString(Type.String({ maxLength: '1' })) + const R = TypeGuard.IsString(Type.String({ maxLength: '1' })) Assert.IsFalse(R) }) it('Should not guard for TString with invalid pattern', () => { // @ts-ignore - const R = TypeGuard.TString(Type.String({ pattern: 1 })) + const R = TypeGuard.IsString(Type.String({ pattern: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/symbol.ts b/test/runtime/type/guard/symbol.ts index 28041bfe2..8723b2151 100644 --- a/test/runtime/type/guard/symbol.ts +++ b/test/runtime/type/guard/symbol.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TSymbol', () => { it('Should guard for TSymbol', () => { - const R = TypeGuard.TSymbol(Type.Symbol()) + const R = TypeGuard.IsSymbol(Type.Symbol()) Assert.IsTrue(R) }) it('Should not guard for TSymbol', () => { - const R = TypeGuard.TSymbol(null) + const R = TypeGuard.IsSymbol(null) Assert.IsFalse(R) }) it('Should not guard for TSymbol with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TSymbol(Type.Symbol({ $id: 1 })) + const R = TypeGuard.IsSymbol(Type.Symbol({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/template-literal.ts b/test/runtime/type/guard/template-literal.ts index 7637133c6..b24518e08 100644 --- a/test/runtime/type/guard/template-literal.ts +++ b/test/runtime/type/guard/template-literal.ts @@ -4,44 +4,44 @@ import { Assert } from '../../assert/index' describe('type/guard/TTemplateLiteral', () => { it('Should guard for empty TemplateLiteral', () => { - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([])) Assert.IsTrue(R) }) it('Should guard for TSchema', () => { - const R = TypeGuard.TSchema(Type.TemplateLiteral([])) + const R = TypeGuard.IsSchema(Type.TemplateLiteral([])) Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TTemplateLiteral)', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TLiteral)', () => { - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TString)', () => { - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.String()])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.String()])) Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TNumber)', () => { - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Number()])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Number()])) Assert.IsTrue(R) }) it('Should guard for TemplateLiteral (TBoolean)', () => { - const R = TypeGuard.TTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) + const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) Assert.IsTrue(R) }) it('Should not guard for missing ^ expression prefix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(1) - Assert.IsFalse(TypeGuard.TTemplateLiteral(T)) + Assert.IsFalse(TypeGuard.IsTemplateLiteral(T)) }) it('Should not guard for missing $ expression postfix', () => { const T = Type.TemplateLiteral([Type.Literal('hello')]) // @ts-ignore T.pattern = T.pattern.slice(0, T.pattern.length - 1) - Assert.IsFalse(TypeGuard.TTemplateLiteral(T)) + Assert.IsFalse(TypeGuard.IsTemplateLiteral(T)) }) }) diff --git a/test/runtime/type/guard/this.ts b/test/runtime/type/guard/this.ts index 93508d53c..8ca3a7e0d 100644 --- a/test/runtime/type/guard/this.ts +++ b/test/runtime/type/guard/this.ts @@ -5,7 +5,7 @@ import { Assert } from '../../assert/index' describe('type/guard/TThis', () => { it('Should guard for TThis', () => { Type.Recursive((This) => { - const R = TypeGuard.TThis(This) + const R = TypeGuard.IsThis(This) Assert.IsTrue(R) return Type.Object({ nodes: Type.Array(This) }) }) @@ -14,7 +14,7 @@ describe('type/guard/TThis', () => { Type.Recursive((This) => { // @ts-ignore This.$ref = 1 - const R = TypeGuard.TThis(This) + const R = TypeGuard.IsThis(This) Assert.IsFalse(R) return Type.Object({ nodes: Type.Array(This) }) }) diff --git a/test/runtime/type/guard/tuple.ts b/test/runtime/type/guard/tuple.ts index 9326c23d2..31a15c280 100644 --- a/test/runtime/type/guard/tuple.ts +++ b/test/runtime/type/guard/tuple.ts @@ -4,20 +4,20 @@ import { Assert } from '../../assert/index' describe('type/guard/TTuple', () => { it('Should guard for TTuple', () => { - const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()])) + const R = TypeGuard.IsTuple(Type.Tuple([Type.Number(), Type.Number()])) Assert.IsTrue(R) }) it('Should not guard for TTuple', () => { - const R = TypeGuard.TTuple(null) + const R = TypeGuard.IsTuple(null) Assert.IsFalse(R) }) it('Should not guard for TTuple with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) + const R = TypeGuard.IsTuple(Type.Tuple([Type.Number(), Type.Number()], { $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TTuple with invalid Items', () => { - const R = TypeGuard.TTuple(Type.Tuple([Type.Number(), {} as any])) + const R = TypeGuard.IsTuple(Type.Tuple([Type.Number(), {} as any])) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uint8array.ts b/test/runtime/type/guard/uint8array.ts index cc058d8b3..de668fd9f 100644 --- a/test/runtime/type/guard/uint8array.ts +++ b/test/runtime/type/guard/uint8array.ts @@ -4,26 +4,26 @@ import { Assert } from '../../assert/index' describe('type/guard/TUint8Array', () => { it('Should guard for TUint8Array', () => { - const R = TypeGuard.TUint8Array(Type.Uint8Array()) + const R = TypeGuard.IsUint8Array(Type.Uint8Array()) Assert.IsTrue(R) }) it('Should not guard for TUint8Array', () => { - const R = TypeGuard.TUint8Array(null) + const R = TypeGuard.IsUint8Array(null) Assert.IsFalse(R) }) it('Should not guard for TUint8Array with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TUint8Array(Type.Uint8Array({ $id: 1 })) + const R = TypeGuard.IsUint8Array(Type.Uint8Array({ $id: 1 })) Assert.IsFalse(R) }) it('Should not guard for TUint8Array with invalid minByteLength', () => { // @ts-ignore - const R = TypeGuard.TUint8Array(Type.Uint8Array({ minByteLength: '1' })) + const R = TypeGuard.IsUint8Array(Type.Uint8Array({ minByteLength: '1' })) Assert.IsFalse(R) }) it('Should not guard for TUint8Array with invalid maxByteLength', () => { // @ts-ignore - const R = TypeGuard.TUint8Array(Type.Uint8Array({ maxByteLength: '1' })) + const R = TypeGuard.IsUint8Array(Type.Uint8Array({ maxByteLength: '1' })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uncapitalize.ts b/test/runtime/type/guard/uncapitalize.ts index d54b4e584..ff412676a 100644 --- a/test/runtime/type/guard/uncapitalize.ts +++ b/test/runtime/type/guard/uncapitalize.ts @@ -4,30 +4,30 @@ import { Assert } from '../../assert/index' describe('type/guard/Uncapitalize', () => { it('Should guard for Uncapitalize 1', () => { const T = Type.Uncapitalize(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hELLO') Assert.IsEqual(T.$id, 'hello') Assert.IsEqual(T.foo, 1) }) it('Should guard for Uncapitalize 2', () => { const T = Type.Uncapitalize(Type.Literal('HELLO')) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hELLO') }) it('Should guard for Uncapitalize 3', () => { const T = Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hELLO') Assert.IsEqual(T.anyOf[1].const, 'wORLD') }) it('Should guard for Uncapitalize 4', () => { const T = Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}')) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') }) it('Should guard for Uncapitalize 5', () => { const T = Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') }) }) diff --git a/test/runtime/type/guard/undefined.ts b/test/runtime/type/guard/undefined.ts index 8393bff76..a6389f4e6 100644 --- a/test/runtime/type/guard/undefined.ts +++ b/test/runtime/type/guard/undefined.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TUndefined', () => { it('Should guard for TUndefined', () => { - const R = TypeGuard.TUndefined(Type.Undefined()) + const R = TypeGuard.IsUndefined(Type.Undefined()) Assert.IsTrue(R) }) it('Should not guard for TUndefined', () => { - const R = TypeGuard.TUndefined(null) + const R = TypeGuard.IsUndefined(null) Assert.IsFalse(R) }) it('Should not guard for TUndefined with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TUndefined(Type.Undefined({ $id: 1 })) + const R = TypeGuard.IsUndefined(Type.Undefined({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/union.ts b/test/runtime/type/guard/union.ts index 62b485e40..edcf34043 100644 --- a/test/runtime/type/guard/union.ts +++ b/test/runtime/type/guard/union.ts @@ -1,10 +1,10 @@ -import { TypeGuard } from '@sinclair/typebox' +import { TSchema, TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TUnion', () => { it('Should guard for TUnion', () => { - const R = TypeGuard.TUnion( + const R = TypeGuard.IsUnion( Type.Union([ Type.Object({ x: Type.Number(), @@ -17,11 +17,11 @@ describe('type/guard/TUnion', () => { Assert.IsTrue(R) }) it('Should not guard for TUnion', () => { - const R = TypeGuard.TUnion(null) + const R = TypeGuard.IsUnion(null) Assert.IsFalse(R) }) it('Should guard for TUnion with invalid $id', () => { - const R = TypeGuard.TUnion( + const R = TypeGuard.IsUnion( // @ts-ignore Type.Union( [ @@ -41,18 +41,18 @@ describe('type/guard/TUnion', () => { Assert.IsFalse(R) }) it('Should not guard for TUnion with invalid variant', () => { - const R = TypeGuard.TUnion( + const R = TypeGuard.IsUnion( Type.Union([ Type.Object({ x: Type.Number(), }), - {} as any, + {} as TSchema, ]), ) Assert.IsFalse(R) }) it('Should not guard for TUnion with invalid object variant', () => { - const R = TypeGuard.TUnion( + const R = TypeGuard.IsUnion( Type.Union([ Type.Object({ x: Type.Number(), @@ -66,19 +66,19 @@ describe('type/guard/TUnion', () => { }) it('Transform: Should transform to never for zero length union', () => { const T = Type.Union([]) - const R = TypeGuard.TNever(T) + const R = TypeGuard.IsNever(T) Assert.IsTrue(R) }) it('Transform: Should unwrap union type for array of length === 1', () => { const T = Type.Union([Type.String()]) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Transform: Should retain union if array length > 1', () => { const T = Type.Union([Type.String(), Type.Number()]) - const R1 = TypeGuard.TUnion(T) - const R2 = TypeGuard.TString(T.anyOf[0]) - const R3 = TypeGuard.TNumber(T.anyOf[1]) + const R1 = TypeGuard.IsUnion(T) + const R2 = TypeGuard.IsString(T.anyOf[0]) + const R3 = TypeGuard.IsNumber(T.anyOf[1]) Assert.IsTrue(R1) Assert.IsTrue(R2) Assert.IsTrue(R3) diff --git a/test/runtime/type/guard/unknown.ts b/test/runtime/type/guard/unknown.ts index bd5cfcd03..cc488fd74 100644 --- a/test/runtime/type/guard/unknown.ts +++ b/test/runtime/type/guard/unknown.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TUnknown', () => { it('Should guard for TUnknown', () => { - const R = TypeGuard.TUnknown(Type.Unknown()) + const R = TypeGuard.IsUnknown(Type.Unknown()) Assert.IsTrue(R) }) it('Should not guard for TUnknown', () => { - const R = TypeGuard.TUnknown(null) + const R = TypeGuard.IsUnknown(null) Assert.IsFalse(R) }) it('Should not guard for TUnknown with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TUnknown(Type.Unknown({ $id: 1 })) + const R = TypeGuard.IsUnknown(Type.Unknown({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/unsafe.ts b/test/runtime/type/guard/unsafe.ts index f43a2d78d..5691abea8 100644 --- a/test/runtime/type/guard/unsafe.ts +++ b/test/runtime/type/guard/unsafe.ts @@ -5,29 +5,29 @@ import { Assert } from '../../assert/index' describe('type/guard/TUnsafe', () => { it('Should guard raw TUnsafe', () => { const T = Type.Unsafe({ x: 1 }) - const R = TypeGuard.TUnsafe(T) + const R = TypeGuard.IsUnsafe(T) Assert.IsTrue(R) }) it('Should guard raw TUnsafe as TSchema', () => { const T = Type.Unsafe({ x: 1 }) - const R = TypeGuard.TSchema(T) + const R = TypeGuard.IsSchema(T) Assert.IsTrue(R) }) it('Should guard override TUnsafe as TSchema when registered', () => { TypeRegistry.Set('UnsafeType', () => true) const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) - const R = TypeGuard.TSchema(T) + const R = TypeGuard.IsSchema(T) Assert.IsTrue(R) TypeRegistry.Delete('UnsafeType') }) it('Should not guard TUnsafe with unregistered kind', () => { const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) - const R = TypeGuard.TUnsafe(T) + const R = TypeGuard.IsUnsafe(T) Assert.IsFalse(R) }) it('Should not guard for TString', () => { const T = Type.String() - const R = TypeGuard.TUnsafe(T) + const R = TypeGuard.IsUnsafe(T) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/guard/uppercase.ts b/test/runtime/type/guard/uppercase.ts index 602ddf400..539a93755 100644 --- a/test/runtime/type/guard/uppercase.ts +++ b/test/runtime/type/guard/uppercase.ts @@ -4,30 +4,30 @@ import { Assert } from '../../assert/index' describe('type/guard/Uppercase', () => { it('Should guard for Uppercase 1', () => { const T = Type.Uppercase(Type.Literal('hello'), { $id: 'hello', foo: 1 }) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'HELLO') Assert.IsEqual(T.$id, 'hello') Assert.IsEqual(T.foo, 1) }) it('Should guard for Uppercase 2', () => { const T = Type.Uppercase(Type.Literal('hello')) - Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'HELLO') }) it('Should guard for Uppercase 3', () => { const T = Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')])) - Assert.IsTrue(TypeGuard.TUnion(T)) + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'HELLO') Assert.IsEqual(T.anyOf[1].const, 'WORLD') }) it('Should guard for Uppercase 4', () => { const T = Type.Uppercase(Type.TemplateLiteral('hello${0|1}')) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') }) it('Should guard for Uppercase 5', () => { const T = Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') }) }) diff --git a/test/runtime/type/guard/void.ts b/test/runtime/type/guard/void.ts index fb60e97a7..9c1f52beb 100644 --- a/test/runtime/type/guard/void.ts +++ b/test/runtime/type/guard/void.ts @@ -4,16 +4,16 @@ import { Assert } from '../../assert/index' describe('type/guard/TVoid', () => { it('Should guard for TVoid', () => { - const R = TypeGuard.TVoid(Type.Void()) + const R = TypeGuard.IsVoid(Type.Void()) Assert.IsTrue(R) }) it('Should not guard for TVoid', () => { - const R = TypeGuard.TVoid(null) + const R = TypeGuard.IsVoid(null) Assert.IsFalse(R) }) it('Should not guard for TVoid with invalid $id', () => { // @ts-ignore - const R = TypeGuard.TVoid(Type.Void({ $id: 1 })) + const R = TypeGuard.IsVoid(Type.Void({ $id: 1 })) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index bc394a539..f5cfcd709 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -4,5 +4,5 @@ import './guard/index' import './intrinsic/index' import './normalize/index' import './registry/index' -import './template/index' +import './template-literal/index' import './value/index' diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts index 3ff191aee..a6a18f34e 100644 --- a/test/runtime/type/intrinsic/intrinsic.ts +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -1,27 +1,28 @@ -import { TypeGuard, Intrinsic } from '@sinclair/typebox' +import { TypeGuard } from '@sinclair/typebox' +import { Intrinsic } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/intrinsic/Map', () => { +describe('type/intrinsic/Intrinsic', () => { // ---------------------------------------------------- // Passthrough // ---------------------------------------------------- it('Should passthrough 1', () => { - const T = Intrinsic.Map(Type.String(), 'Capitalize') - Assert.IsTrue(TypeGuard.TString(T)) + const T = Intrinsic(Type.String(), 'Capitalize') + Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should passthrough 2', () => { - const T = Intrinsic.Map(Type.Number(), 'Capitalize') - Assert.IsTrue(TypeGuard.TNumber(T)) + const T = Intrinsic(Type.Number(), 'Capitalize') + Assert.IsTrue(TypeGuard.IsNumber(T)) }) it('Should passthrough 3', () => { - const T = Intrinsic.Map( + const T = Intrinsic( Type.Object({ x: Type.Number(), }), 'Capitalize', ) - Assert.IsTrue(TypeGuard.TObject(T)) + Assert.IsTrue(TypeGuard.IsObject(T)) }) // ---------------------------------------------------- // Partial Passthrough @@ -30,59 +31,59 @@ describe('type/intrinsic/Map', () => { const A = Type.Object({ x: Type.Number() }) const B = Type.Literal('hello') const U = Type.Union([A, B]) - const T = Intrinsic.Map(U, 'Capitalize') - Assert.IsTrue(TypeGuard.TUnion(T)) - Assert.IsTrue(TypeGuard.TObject(T.anyOf[0])) - Assert.IsTrue(TypeGuard.TLiteral(T.anyOf[1])) + const T = Intrinsic(U, 'Capitalize') + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsObject(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(T.anyOf[1])) Assert.IsEqual(T.anyOf[1].const, 'Hello') }) // ---------------------------------------------------- // Mode: Literal // ---------------------------------------------------- it('Should map literal: Capitalize', () => { - const T = Intrinsic.Map(Type.Literal('hello'), 'Capitalize') - Assert.IsTrue(TypeGuard.TLiteral(T)) + const T = Intrinsic(Type.Literal('hello'), 'Capitalize') + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'Hello') }) it('Should map literal: Uncapitalize', () => { - const T = Intrinsic.Map(Type.Literal('HELLO'), 'Uncapitalize') - Assert.IsTrue(TypeGuard.TLiteral(T)) + const T = Intrinsic(Type.Literal('HELLO'), 'Uncapitalize') + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hELLO') }) it('Should map literal: Uppercase', () => { - const T = Intrinsic.Map(Type.Literal('hello'), 'Uppercase') - Assert.IsTrue(TypeGuard.TLiteral(T)) + const T = Intrinsic(Type.Literal('hello'), 'Uppercase') + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'HELLO') }) it('Should map literal: Lowercase', () => { - const T = Intrinsic.Map(Type.Literal('HELLO'), 'Lowercase') - Assert.IsTrue(TypeGuard.TLiteral(T)) + const T = Intrinsic(Type.Literal('HELLO'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsLiteral(T)) Assert.IsEqual(T.const, 'hello') }) // ---------------------------------------------------- // Mode: Literal Union // ---------------------------------------------------- it('Should map literal union: Capitalize', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') - Assert.IsTrue(TypeGuard.TUnion(T)) + const T = Intrinsic(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'Hello') Assert.IsEqual(T.anyOf[1].const, 'World') }) it('Should map literal union: Uncapitalize', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') - Assert.IsTrue(TypeGuard.TUnion(T)) + const T = Intrinsic(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') }) it('Should map literal union: Uppercase', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') - Assert.IsTrue(TypeGuard.TUnion(T)) + const T = Intrinsic(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'HELLO') Assert.IsEqual(T.anyOf[1].const, 'WORLD') }) it('Should map literal union: Lowercase', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') - Assert.IsTrue(TypeGuard.TUnion(T)) + const T = Intrinsic(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') + Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') }) @@ -90,74 +91,74 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral // ---------------------------------------------------- it('Should map template literal: Capitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1world|Hello2world)$') }) it('Should map template literal: Uncapitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1WORLD|hELLO2WORLD)$') }) it('Should map template literal: Uppercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1WORLD|HELLO2WORLD)$') }) it('Should map template literal: Lowercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1world|hello2world)$') }) // ---------------------------------------------------- // Mode: TemplateLiteral Numeric // ---------------------------------------------------- it('Should map template literal numeric: Capitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1|Hello2)$') }) it('Should map template literal numeric: Uncapitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1|hELLO2)$') }) it('Should map template literal numeric: Uppercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1|HELLO2)$') }) it('Should map template literal numeric: Lowercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1|hello2)$') }) // ---------------------------------------------------- // Mode: TemplateLiteral Patterns // ---------------------------------------------------- it('Should map template literal patterns 1', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(.*)world$') }) it('Should map template literal patterns 2', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(0|[1-9][0-9]*)world$') }) it('Should map template literal patterns 3', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('${number}${string}'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('${number}${string}'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)(.*)$') }) it('Should map template literal patterns 3', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)hello(.*)$') }) it('Should map template literal patterns 3', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') - Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) + const T = Intrinsic(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') + Assert.IsTrue(TypeGuard.IsTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(stringhello|numberhello)$') }) }) diff --git a/test/runtime/type/normalize/exclude.ts b/test/runtime/type/normalize/exclude.ts index 908f16145..53fad9c4d 100644 --- a/test/runtime/type/normalize/exclude.ts +++ b/test/runtime/type/normalize/exclude.ts @@ -5,17 +5,17 @@ import { Assert } from '../../assert/index' describe('type/normalize/Exclude', () => { it('Normalize 1', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - const R = TypeGuard.TUnion(T) + const R = TypeGuard.IsUnion(T) Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number()]), Type.String()) - const R = TypeGuard.TNumber(T) + const R = TypeGuard.IsNumber(T) Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Exclude(Type.Union([Type.String()]), Type.String()) - const R = TypeGuard.TNever(T) + const R = TypeGuard.IsNever(T) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/extract.ts b/test/runtime/type/normalize/extract.ts index e1dc98195..196ee0f4a 100644 --- a/test/runtime/type/normalize/extract.ts +++ b/test/runtime/type/normalize/extract.ts @@ -5,27 +5,27 @@ import { Assert } from '../../assert/index' describe('type/normalize/Extract', () => { it('Normalize 1', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Number()])) - const R = TypeGuard.TUnion(T) + const R = TypeGuard.IsUnion(T) Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Extract(Type.Union([Type.String(), Type.Number()]), Type.String()) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Normalize 4', () => { const T = Type.Extract(Type.Union([Type.String()]), Type.String()) - const R = TypeGuard.TString(T) + const R = TypeGuard.IsString(T) Assert.IsTrue(R) }) it('Normalize 5', () => { const T = Type.Extract(Type.Union([]), Type.String()) - const R = TypeGuard.TNever(T) + const R = TypeGuard.IsNever(T) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/indexed.ts b/test/runtime/type/normalize/indexed.ts index c149c66ae..543f9b580 100644 --- a/test/runtime/type/normalize/indexed.ts +++ b/test/runtime/type/normalize/indexed.ts @@ -9,7 +9,7 @@ describe('type/normalize/Index', () => { it('Normalize Array 1', () => { const T = Type.Array(Type.String()) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) // --------------------------------------------------------- // Tuple @@ -17,33 +17,33 @@ describe('type/normalize/Index', () => { it('Normalize Tuple 1', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, Type.Number()) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Normalize Tuple 2', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, [0, 1]) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Normalize Tuple 3', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['0', '1']) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TNumber(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsNumber(I.anyOf[1])) }) it('Normalize Tuple 4', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['0']) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) it('Normalize Tuple 5', () => { const T = Type.Tuple([Type.String(), Type.Number()]) const I = Type.Index(T, ['1']) - Assert.IsTrue(TypeGuard.TNumber(I)) + Assert.IsTrue(TypeGuard.IsNumber(I)) }) // --------------------------------------------------------- // Intersect @@ -51,47 +51,47 @@ describe('type/normalize/Index', () => { it('Normalize Intersect 1', () => { const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TIntersect(I)) - Assert.IsTrue(TypeGuard.TString(I.allOf[0])) - Assert.IsTrue(TypeGuard.TString(I.allOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsIntersect(I)) + Assert.IsTrue(TypeGuard.IsString(I.allOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.allOf[1])) }) it('Normalize Intersect 2', () => { const T = Type.Intersect([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TIntersect(I)) - Assert.IsTrue(TypeGuard.TString(I.allOf[0])) - Assert.IsTrue(TypeGuard.TString(I.allOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsIntersect(I)) + Assert.IsTrue(TypeGuard.IsString(I.allOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.allOf[1])) }) it('Normalize Intersect 3', () => { const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TIntersect(I)) - Assert.IsTrue(TypeGuard.TString(I.allOf[0])) - Assert.IsTrue(TypeGuard.TString(I.allOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsIntersect(I)) + Assert.IsTrue(TypeGuard.IsString(I.allOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.allOf[1])) }) it('Normalize Intersect 4', () => { const T = Type.Intersect([Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) it('Normalize Intersect 5', () => { const T = Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) const I = Type.Index(T, ['x', 'y']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Normalize Intersect 6', () => { const T = Type.Recursive(() => Type.Intersect([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) const I = Type.Index(T, ['x', 'y']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) // --------------------------------------------------------- // Union @@ -99,42 +99,42 @@ describe('type/normalize/Index', () => { it('Normalize Union 1', () => { const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.Optional(Type.String()) })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Normalize Union 2', () => { const T = Type.Union([Type.Object({ x: Type.Optional(Type.String()) }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Normalize Union 3', () => { const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsFalse(TypeGuard.TOptional(I)) - Assert.IsTrue(TypeGuard.TUnion(I)) - Assert.IsTrue(TypeGuard.TString(I.anyOf[0])) - Assert.IsTrue(TypeGuard.TString(I.anyOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(I)) + Assert.IsTrue(TypeGuard.IsUnion(I)) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[0])) + Assert.IsTrue(TypeGuard.IsString(I.anyOf[1])) }) it('Normalize Union 4', () => { const T = Type.Union([Type.Object({ x: Type.String() })]) const I = Type.Index(T, ['x']) - Assert.IsTrue(TypeGuard.TString(I)) + Assert.IsTrue(TypeGuard.IsString(I)) }) it('Normalize Union 5', () => { const T = Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })]) // @ts-ignore const I = Type.Index(T, ['x', 'y']) - Assert.IsFalse(TypeGuard.TNever(I)) + Assert.IsFalse(TypeGuard.IsNever(I)) }) it('Normalize Union 6', () => { const T = Type.Recursive(() => Type.Union([Type.Object({ x: Type.String() }), Type.Object({ y: Type.String() })])) // @ts-ignore const I = Type.Index(T, ['x', 'y']) - Assert.IsFalse(TypeGuard.TNever(I)) + Assert.IsFalse(TypeGuard.IsNever(I)) }) }) diff --git a/test/runtime/type/normalize/intersect.ts b/test/runtime/type/normalize/intersect.ts index 34837db79..cf73898a8 100644 --- a/test/runtime/type/normalize/intersect.ts +++ b/test/runtime/type/normalize/intersect.ts @@ -5,17 +5,17 @@ import { Assert } from '../../assert/index' describe('type/normalize/Intersect', () => { it('Normalize 1', () => { const T = Type.Intersect([Type.Number(), Type.String()]) - const R = TypeGuard.TIntersect(T) + const R = TypeGuard.IsIntersect(T) Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Intersect([Type.Number()]) - const R = TypeGuard.TNumber(T) + const R = TypeGuard.IsNumber(T) Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Intersect([]) - const R = TypeGuard.TNever(T) + const R = TypeGuard.IsNever(T) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/record.ts b/test/runtime/type/normalize/record.ts index 9f419429d..ef463f5ce 100644 --- a/test/runtime/type/normalize/record.ts +++ b/test/runtime/type/normalize/record.ts @@ -6,7 +6,7 @@ describe('type/normalize/Record', () => { it('Normalize 1', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Record(K, Type.String()) - const R = TypeGuard.TObject(T) + const R = TypeGuard.IsObject(T) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/normalize/union.ts b/test/runtime/type/normalize/union.ts index 3c5d468a4..2ab700fb0 100644 --- a/test/runtime/type/normalize/union.ts +++ b/test/runtime/type/normalize/union.ts @@ -5,17 +5,17 @@ import { Assert } from '../../assert/index' describe('type/normalize/Union', () => { it('Normalize 1', () => { const T = Type.Union([Type.Number(), Type.String()]) - const R = TypeGuard.TUnion(T) + const R = TypeGuard.IsUnion(T) Assert.IsTrue(R) }) it('Normalize 2', () => { const T = Type.Union([Type.Number()]) - const R = TypeGuard.TNumber(T) + const R = TypeGuard.IsNumber(T) Assert.IsTrue(R) }) it('Normalize 3', () => { const T = Type.Union([]) - const R = TypeGuard.TNever(T) + const R = TypeGuard.IsNever(T) Assert.IsTrue(R) }) }) diff --git a/test/runtime/type/template-literal/finite.ts b/test/runtime/type/template-literal/finite.ts new file mode 100644 index 000000000..a6a97efa3 --- /dev/null +++ b/test/runtime/type/template-literal/finite.ts @@ -0,0 +1,71 @@ +import { TemplateLiteralParse, IsTemplateLiteralExpressionFinite, PatternString, PatternBoolean, PatternNumber } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/template-literal/IsTemplateLiteralExpressionFinite', () => { + // --------------------------------------------------------------- + // Finite + // --------------------------------------------------------------- + it('Finite 1', () => { + const E = TemplateLiteralParse(`A`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsTrue(R) + }) + it('Finite 2', () => { + const E = TemplateLiteralParse(`A|B`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsTrue(R) + }) + it('Finite 3', () => { + const E = TemplateLiteralParse(`A(B|C)`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsTrue(R) + }) + it('Finite 4', () => { + const E = TemplateLiteralParse(`${PatternBoolean}`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsTrue(R) + }) + it('Finite 5', () => { + const E = TemplateLiteralParse(`\\.\\*`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsTrue(R) + }) + // --------------------------------------------------------------- + // Infinite + // --------------------------------------------------------------- + it('Infinite 1', () => { + const E = TemplateLiteralParse(`${PatternString}`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 2', () => { + const E = TemplateLiteralParse(`${PatternNumber}`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 3', () => { + const E = TemplateLiteralParse(`A|${PatternString}`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 4', () => { + const E = TemplateLiteralParse(`A|${PatternNumber}`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 5', () => { + const E = TemplateLiteralParse(`A(${PatternString})`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 6', () => { + const E = TemplateLiteralParse(`A(${PatternNumber})`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) + it('Infinite 7', () => { + const E = TemplateLiteralParse(`${PatternString}_foo`) + const R = IsTemplateLiteralExpressionFinite(E) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/template-literal/generate.ts b/test/runtime/type/template-literal/generate.ts new file mode 100644 index 000000000..2178571bc --- /dev/null +++ b/test/runtime/type/template-literal/generate.ts @@ -0,0 +1,199 @@ +import { TemplateLiteralParse, TemplateLiteralExpressionGenerate } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/template-literal/TemplateLiteralExpressionGenerate', () => { + // --------------------------------------------------------------- + // Exact (No Default Unwrap) + // --------------------------------------------------------------- + it('Exact 1', () => { + const E = TemplateLiteralParse('^$') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['^$']) + }) + it('Exact 2', () => { + const E = TemplateLiteralParse('^A$') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['^A$']) + }) + // --------------------------------------------------------------- + // Patterns + // --------------------------------------------------------------- + it('Pattern 1', () => { + const E = TemplateLiteralParse('(true|false)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['true', 'false']) + }) + it('Pattern 2', () => { + const E = TemplateLiteralParse('(0|[1-9][0-9]*)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['0', '[1-9][0-9]*']) + }) + it('Pattern 3', () => { + const E = TemplateLiteralParse('.*') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['.*']) + }) + // --------------------------------------------------------------- + // Expression + // --------------------------------------------------------------- + it('Expression 1', () => { + const E = TemplateLiteralParse(')') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, [')']) + }) + it('Expression 2', () => { + const E = TemplateLiteralParse('\\)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['\\)']) + }) + it('Expression 3', () => { + const E = TemplateLiteralParse('\\(') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['\\(']) + }) + it('Expression 4', () => { + const E = TemplateLiteralParse('') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['']) + }) + it('Expression 5', () => { + const E = TemplateLiteralParse('\\') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['\\']) + }) + it('Expression 6', () => { + const E = TemplateLiteralParse('()') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['']) + }) + it('Expression 7', () => { + const E = TemplateLiteralParse('(a)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['a']) + }) + it('Expression 8', () => { + const E = TemplateLiteralParse('()))') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['))']) + }) + it('Expression 9', () => { + const E = TemplateLiteralParse('())') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, [')']) + }) + it('Expression 10', () => { + const E = TemplateLiteralParse('A|B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', 'B']) + }) + it('Expression 11', () => { + const E = TemplateLiteralParse('A|(B)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', 'B']) + }) + it('Expression 12', () => { + const E = TemplateLiteralParse('A(B)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['AB']) + }) + it('Expression 13', () => { + const E = TemplateLiteralParse('(A)B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['AB']) + }) + it('Expression 14', () => { + const E = TemplateLiteralParse('(A)|B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', 'B']) + }) + it('Expression 15', () => { + const E = TemplateLiteralParse('|') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['']) + }) + it('Expression 16', () => { + const E = TemplateLiteralParse('||') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['']) + }) + it('Expression 17', () => { + const E = TemplateLiteralParse('||A') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A']) + }) + it('Expression 18', () => { + const E = TemplateLiteralParse('A||') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A']) + }) + it('Expression 19', () => { + const E = TemplateLiteralParse('A||B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', 'B']) + }) + it('Expression 20', () => { + const E = TemplateLiteralParse('A|()|B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', '', 'B']) + }) + it('Expression 21', () => { + const E = TemplateLiteralParse('A|(|)|B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', '', 'B']) + }) + it('Expression 22', () => { + const E = TemplateLiteralParse('A|(||)|B') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', '', 'B']) + }) + it('Expression 23', () => { + const E = TemplateLiteralParse('|A(||)B|') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['AB']) + }) + it('Expression 24', () => { + const E = TemplateLiteralParse('A(B)(C)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['ABC']) + }) + it('Expression 25', () => { + const E = TemplateLiteralParse('A(B)|(C)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['AB', 'C']) + }) + it('Expression 26', () => { + const E = TemplateLiteralParse('A(B|C)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['AB', 'AC']) + }) + it('Expression 27', () => { + const E = TemplateLiteralParse('A|(B|C)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['A', 'B', 'C']) + }) + it('Expression 28', () => { + const E = TemplateLiteralParse('((A)B)C') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['ABC']) + }) + it('Expression 29', () => { + const E = TemplateLiteralParse('(0|1)(0|1)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['00', '01', '10', '11']) + }) + it('Expression 30', () => { + const E = TemplateLiteralParse('(0|1)|(0|1)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['0', '1', '0', '1']) + }) + it('Expression 31', () => { + const E = TemplateLiteralParse('(0|(1|0)|1)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['0', '1', '0', '1']) + }) + it('Expression 32', () => { + const E = TemplateLiteralParse('(0(1|0)1)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['011', '001']) + }) +}) diff --git a/test/runtime/type/template/index.ts b/test/runtime/type/template-literal/index.ts similarity index 76% rename from test/runtime/type/template/index.ts rename to test/runtime/type/template-literal/index.ts index 032be5db6..483e67965 100644 --- a/test/runtime/type/template/index.ts +++ b/test/runtime/type/template-literal/index.ts @@ -1,4 +1,4 @@ import './finite' import './generate' -import './parser' +import './parse' import './pattern' diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template-literal/parse.ts similarity index 82% rename from test/runtime/type/template/parser.ts rename to test/runtime/type/template-literal/parse.ts index 5ab50afbf..57db98fc6 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template-literal/parse.ts @@ -1,28 +1,28 @@ -import { TemplateLiteralParser } from '@sinclair/typebox' +import { TemplateLiteralParse } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/templateliteral/TemplateLiteralParser', () => { +describe('type/template-literal/TemplateLiteralParser', () => { // --------------------------------------------------------------- // Throws // --------------------------------------------------------------- it('Throw 1', () => { - Assert.Throws(() => TemplateLiteralParser.Parse('(')) + Assert.Throws(() => TemplateLiteralParse('(')) }) it('Throw 2', () => { - Assert.Throws(() => TemplateLiteralParser.Parse('(')) + Assert.Throws(() => TemplateLiteralParse('(')) }) // --------------------------------------------------------------- // Exact (No Default Unwrap) // --------------------------------------------------------------- it('Exact 1', () => { - const E = TemplateLiteralParser.Parse('^$') + const E = TemplateLiteralParse('^$') Assert.IsEqual(E, { type: 'const', const: '^$', }) }) it('Exact 2', () => { - const E = TemplateLiteralParser.Parse('^A$') + const E = TemplateLiteralParse('^A$') Assert.IsEqual(E, { type: 'const', const: '^A$', @@ -32,7 +32,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { // Patterns // --------------------------------------------------------------- it('Pattern 1', () => { - const E = TemplateLiteralParser.Parse('(true|false)') + const E = TemplateLiteralParse('(true|false)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -48,7 +48,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Pattern 2', () => { - const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') + const E = TemplateLiteralParse('(0|[1-9][0-9]*)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -64,7 +64,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Pattern 3', () => { - const E = TemplateLiteralParser.Parse('.*') + const E = TemplateLiteralParse('.*') Assert.IsEqual(E, { type: 'const', const: '.*', @@ -74,56 +74,56 @@ describe('type/templateliteral/TemplateLiteralParser', () => { // Expression // --------------------------------------------------------------- it('Expression 1', () => { - const E = TemplateLiteralParser.Parse(')') + const E = TemplateLiteralParse(')') Assert.IsEqual(E, { type: 'const', const: ')', }) }) it('Expression 2', () => { - const E = TemplateLiteralParser.Parse('\\)') + const E = TemplateLiteralParse('\\)') Assert.IsEqual(E, { type: 'const', const: '\\)', }) }) it('Expression 3', () => { - const E = TemplateLiteralParser.Parse('\\(') + const E = TemplateLiteralParse('\\(') Assert.IsEqual(E, { type: 'const', const: '\\(', }) }) it('Expression 4', () => { - const E = TemplateLiteralParser.Parse('') + const E = TemplateLiteralParse('') Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 5', () => { - const E = TemplateLiteralParser.Parse('\\') + const E = TemplateLiteralParse('\\') Assert.IsEqual(E, { type: 'const', const: '\\', }) }) it('Expression 6', () => { - const E = TemplateLiteralParser.Parse('()') + const E = TemplateLiteralParse('()') Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 7', () => { - const E = TemplateLiteralParser.Parse('(a)') + const E = TemplateLiteralParse('(a)') Assert.IsEqual(E, { type: 'const', const: 'a', }) }) it('Expression 8', () => { - const E = TemplateLiteralParser.Parse('()))') + const E = TemplateLiteralParse('()))') Assert.IsEqual(E, { type: 'and', expr: [ @@ -139,7 +139,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 9', () => { - const E = TemplateLiteralParser.Parse('())') + const E = TemplateLiteralParse('())') Assert.IsEqual(E, { type: 'and', expr: [ @@ -155,7 +155,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 10', () => { - const E = TemplateLiteralParser.Parse('A|B') + const E = TemplateLiteralParse('A|B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -171,7 +171,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 11', () => { - const E = TemplateLiteralParser.Parse('A|(B)') + const E = TemplateLiteralParse('A|(B)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -187,7 +187,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 12', () => { - const E = TemplateLiteralParser.Parse('A(B)') + const E = TemplateLiteralParse('A(B)') Assert.IsEqual(E, { type: 'and', expr: [ @@ -203,7 +203,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 13', () => { - const E = TemplateLiteralParser.Parse('(A)B') + const E = TemplateLiteralParse('(A)B') Assert.IsEqual(E, { type: 'and', expr: [ @@ -219,7 +219,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 14', () => { - const E = TemplateLiteralParser.Parse('(A)|B') + const E = TemplateLiteralParse('(A)|B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -235,35 +235,35 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 15', () => { - const E = TemplateLiteralParser.Parse('|') + const E = TemplateLiteralParse('|') Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 16', () => { - const E = TemplateLiteralParser.Parse('||') + const E = TemplateLiteralParse('||') Assert.IsEqual(E, { type: 'const', const: '', }) }) it('Expression 17', () => { - const E = TemplateLiteralParser.Parse('||A') + const E = TemplateLiteralParse('||A') Assert.IsEqual(E, { type: 'const', const: 'A', }) }) it('Expression 18', () => { - const E = TemplateLiteralParser.Parse('A||') + const E = TemplateLiteralParse('A||') Assert.IsEqual(E, { type: 'const', const: 'A', }) }) it('Expression 19', () => { - const E = TemplateLiteralParser.Parse('A||B') + const E = TemplateLiteralParse('A||B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -279,7 +279,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 20', () => { - const E = TemplateLiteralParser.Parse('A|()|B') + const E = TemplateLiteralParse('A|()|B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -299,7 +299,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 21', () => { - const E = TemplateLiteralParser.Parse('A|(|)|B') + const E = TemplateLiteralParse('A|(|)|B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -319,7 +319,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 22', () => { - const E = TemplateLiteralParser.Parse('A|(||)|B') + const E = TemplateLiteralParse('A|(||)|B') Assert.IsEqual(E, { type: 'or', expr: [ @@ -339,7 +339,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 23', () => { - const E = TemplateLiteralParser.Parse('|A(||)B|') + const E = TemplateLiteralParse('|A(||)B|') Assert.IsEqual(E, { type: 'and', expr: [ @@ -359,7 +359,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 24', () => { - const E = TemplateLiteralParser.Parse('A(B)(C)') + const E = TemplateLiteralParse('A(B)(C)') Assert.IsEqual(E, { type: 'and', expr: [ @@ -379,7 +379,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 25', () => { - const E = TemplateLiteralParser.Parse('A(B)|(C)') + const E = TemplateLiteralParse('A(B)|(C)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -404,7 +404,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 26', () => { - const E = TemplateLiteralParser.Parse('A(B|C)') + const E = TemplateLiteralParse('A(B|C)') Assert.IsEqual(E, { type: 'and', expr: [ @@ -429,7 +429,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 27', () => { - const E = TemplateLiteralParser.Parse('A|(B|C)') + const E = TemplateLiteralParse('A|(B|C)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -454,7 +454,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 28', () => { - const E = TemplateLiteralParser.Parse('((A)B)C') + const E = TemplateLiteralParse('((A)B)C') Assert.IsEqual(E, { type: 'and', expr: [ @@ -479,7 +479,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 29', () => { - const E = TemplateLiteralParser.Parse('(0|1)(0|1)') + const E = TemplateLiteralParse('(0|1)(0|1)') Assert.IsEqual(E, { type: 'and', expr: [ @@ -513,7 +513,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 30', () => { - const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') + const E = TemplateLiteralParse('(0|1)|(0|1)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -547,7 +547,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 31', () => { - const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') + const E = TemplateLiteralParse('(0|(1|0)|1)') Assert.IsEqual(E, { type: 'or', expr: [ @@ -576,7 +576,7 @@ describe('type/templateliteral/TemplateLiteralParser', () => { }) }) it('Expression 32', () => { - const E = TemplateLiteralParser.Parse('(0(1|0)1)') + const E = TemplateLiteralParse('(0(1|0)1)') Assert.IsEqual(E, { type: 'and', expr: [ diff --git a/test/runtime/type/template/pattern.ts b/test/runtime/type/template-literal/pattern.ts similarity index 98% rename from test/runtime/type/template/pattern.ts rename to test/runtime/type/template-literal/pattern.ts index 6fdf85ca7..1a8b13fa8 100644 --- a/test/runtime/type/template/pattern.ts +++ b/test/runtime/type/template-literal/pattern.ts @@ -1,7 +1,7 @@ import { Type, TTemplateLiteral, PatternNumber, PatternString, PatternBoolean } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/templateliteral/TemplateLiteralPattern', () => { +describe('type/template-literal/TemplateLiteralPattern', () => { const Equal = (template: TTemplateLiteral, expect: string) => { const pattern = template.pattern.slice(1, template.pattern.length - 1) Assert.IsEqual(pattern, expect) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts deleted file mode 100644 index 53028dff9..000000000 --- a/test/runtime/type/template/finite.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { PatternString, PatternBoolean, PatternNumber, TemplateLiteralParser, TemplateLiteralFinite } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('type/templateliteral/TemplateLiteralFinite', () => { - // --------------------------------------------------------------- - // Finite - // --------------------------------------------------------------- - it('Finite 1', () => { - const E = TemplateLiteralParser.Parse(`A`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsTrue(R) - }) - it('Finite 2', () => { - const E = TemplateLiteralParser.Parse(`A|B`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsTrue(R) - }) - it('Finite 3', () => { - const E = TemplateLiteralParser.Parse(`A(B|C)`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsTrue(R) - }) - it('Finite 4', () => { - const E = TemplateLiteralParser.Parse(`${PatternBoolean}`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsTrue(R) - }) - it('Finite 5', () => { - const E = TemplateLiteralParser.Parse(`\\.\\*`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsTrue(R) - }) - // --------------------------------------------------------------- - // Infinite - // --------------------------------------------------------------- - it('Infinite 1', () => { - const E = TemplateLiteralParser.Parse(`${PatternString}`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 2', () => { - const E = TemplateLiteralParser.Parse(`${PatternNumber}`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 3', () => { - const E = TemplateLiteralParser.Parse(`A|${PatternString}`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 4', () => { - const E = TemplateLiteralParser.Parse(`A|${PatternNumber}`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 5', () => { - const E = TemplateLiteralParser.Parse(`A(${PatternString})`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 6', () => { - const E = TemplateLiteralParser.Parse(`A(${PatternNumber})`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) - it('Infinite 7', () => { - const E = TemplateLiteralParser.Parse(`${PatternString}_foo`) - const R = TemplateLiteralFinite.Check(E) - Assert.IsFalse(R) - }) -}) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts deleted file mode 100644 index 73aa9cfc3..000000000 --- a/test/runtime/type/template/generate.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { TemplateLiteralParser, TemplateLiteralGenerator } from '@sinclair/typebox' -import { Assert } from '../../assert/index' - -describe('type/templateliteral/TemplateLiteralGenerator', () => { - // --------------------------------------------------------------- - // Exact (No Default Unwrap) - // --------------------------------------------------------------- - it('Exact 1', () => { - const E = TemplateLiteralParser.Parse('^$') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['^$']) - }) - it('Exact 2', () => { - const E = TemplateLiteralParser.Parse('^A$') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['^A$']) - }) - // --------------------------------------------------------------- - // Patterns - // --------------------------------------------------------------- - it('Pattern 1', () => { - const E = TemplateLiteralParser.Parse('(true|false)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['true', 'false']) - }) - it('Pattern 2', () => { - const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['0', '[1-9][0-9]*']) - }) - it('Pattern 3', () => { - const E = TemplateLiteralParser.Parse('.*') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['.*']) - }) - // --------------------------------------------------------------- - // Expression - // --------------------------------------------------------------- - it('Expression 1', () => { - const E = TemplateLiteralParser.Parse(')') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, [')']) - }) - it('Expression 2', () => { - const E = TemplateLiteralParser.Parse('\\)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['\\)']) - }) - it('Expression 3', () => { - const E = TemplateLiteralParser.Parse('\\(') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['\\(']) - }) - it('Expression 4', () => { - const E = TemplateLiteralParser.Parse('') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['']) - }) - it('Expression 5', () => { - const E = TemplateLiteralParser.Parse('\\') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['\\']) - }) - it('Expression 6', () => { - const E = TemplateLiteralParser.Parse('()') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['']) - }) - it('Expression 7', () => { - const E = TemplateLiteralParser.Parse('(a)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['a']) - }) - it('Expression 8', () => { - const E = TemplateLiteralParser.Parse('()))') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['))']) - }) - it('Expression 9', () => { - const E = TemplateLiteralParser.Parse('())') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, [')']) - }) - it('Expression 10', () => { - const E = TemplateLiteralParser.Parse('A|B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', 'B']) - }) - it('Expression 11', () => { - const E = TemplateLiteralParser.Parse('A|(B)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', 'B']) - }) - it('Expression 12', () => { - const E = TemplateLiteralParser.Parse('A(B)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['AB']) - }) - it('Expression 13', () => { - const E = TemplateLiteralParser.Parse('(A)B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['AB']) - }) - it('Expression 14', () => { - const E = TemplateLiteralParser.Parse('(A)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', 'B']) - }) - it('Expression 15', () => { - const E = TemplateLiteralParser.Parse('|') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['']) - }) - it('Expression 16', () => { - const E = TemplateLiteralParser.Parse('||') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['']) - }) - it('Expression 17', () => { - const E = TemplateLiteralParser.Parse('||A') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A']) - }) - it('Expression 18', () => { - const E = TemplateLiteralParser.Parse('A||') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A']) - }) - it('Expression 19', () => { - const E = TemplateLiteralParser.Parse('A||B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', 'B']) - }) - it('Expression 20', () => { - const E = TemplateLiteralParser.Parse('A|()|B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', '', 'B']) - }) - it('Expression 21', () => { - const E = TemplateLiteralParser.Parse('A|(|)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', '', 'B']) - }) - it('Expression 22', () => { - const E = TemplateLiteralParser.Parse('A|(||)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', '', 'B']) - }) - it('Expression 23', () => { - const E = TemplateLiteralParser.Parse('|A(||)B|') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['AB']) - }) - it('Expression 24', () => { - const E = TemplateLiteralParser.Parse('A(B)(C)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['ABC']) - }) - it('Expression 25', () => { - const E = TemplateLiteralParser.Parse('A(B)|(C)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['AB', 'C']) - }) - it('Expression 26', () => { - const E = TemplateLiteralParser.Parse('A(B|C)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['AB', 'AC']) - }) - it('Expression 27', () => { - const E = TemplateLiteralParser.Parse('A|(B|C)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['A', 'B', 'C']) - }) - it('Expression 28', () => { - const E = TemplateLiteralParser.Parse('((A)B)C') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['ABC']) - }) - it('Expression 29', () => { - const E = TemplateLiteralParser.Parse('(0|1)(0|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['00', '01', '10', '11']) - }) - it('Expression 30', () => { - const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['0', '1', '0', '1']) - }) - it('Expression 31', () => { - const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['0', '1', '0', '1']) - }) - it('Expression 32', () => { - const E = TemplateLiteralParser.Parse('(0(1|0)1)') - const R = [...TemplateLiteralGenerator.Generate(E)] - Assert.IsEqual(R, ['011', '001']) - }) -}) diff --git a/test/runtime/type/value/guard.ts b/test/runtime/type/value/guard.ts index 51652350b..d68d18952 100644 --- a/test/runtime/type/value/guard.ts +++ b/test/runtime/type/value/guard.ts @@ -2,6 +2,17 @@ import { Assert } from '../../assert/index' import { ValueGuard } from '@sinclair/typebox' describe('type/ValueGuard', () => { + // ----------------------------------------------------- + // IsSymbol + // ----------------------------------------------------- + it('Should guard symbol 1', () => { + const R = ValueGuard.IsSymbol(Symbol()) + Assert.IsTrue(R) + }) + it('Should guard symbol 2', () => { + const R = ValueGuard.IsSymbol({}) + Assert.IsFalse(R) + }) // ----------------------------------------------------- // IsNull // ----------------------------------------------------- @@ -106,4 +117,71 @@ describe('type/ValueGuard', () => { const R = ValueGuard.IsArray({}) Assert.IsFalse(R) }) + // ----------------------------------------------------- + // IsFunction + // ----------------------------------------------------- + it('Should guard function 1', () => { + const R = ValueGuard.IsFunction(function () {}) + Assert.IsTrue(R) + }) + it('Should guard function 2', () => { + const R = ValueGuard.IsFunction({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsAsyncIterator + // ----------------------------------------------------- + it('Should guard async iterator 1', () => { + const R = ValueGuard.IsAsyncIterator((async function* (): any {})()) + Assert.IsTrue(R) + }) + it('Should guard async iterator 2', () => { + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsAsyncIterator + // ----------------------------------------------------- + it('Should guard iterator 1', () => { + const R = ValueGuard.IsIterator((function* (): any {})()) + Assert.IsTrue(R) + }) + it('Should guard iterator 2', () => { + const R = ValueGuard.IsIterator((async function* (): any {})()) + Assert.IsFalse(R) + }) + it('Should guard iterator 3', () => { + const R = ValueGuard.IsIterator([]) + Assert.IsFalse(R) + }) + it('Should guard iterator 4', () => { + const R = ValueGuard.IsIterator(new Uint8Array()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // Date + // ----------------------------------------------------- + it('Should guard date 1', () => { + const R = ValueGuard.IsDate(new Date()) + Assert.IsTrue(R) + }) + it('Should guard date 2', () => { + const R = ValueGuard.IsDate({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // Date + // ----------------------------------------------------- + it('Should guard uint8array 1', () => { + const R = ValueGuard.IsUint8Array(new Uint8Array()) + Assert.IsTrue(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array({}) + Assert.IsFalse(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array([]) + Assert.IsFalse(R) + }) }) diff --git a/test/runtime/value/cast/regexp.ts b/test/runtime/value/cast/regexp.ts index 6b4e05950..6c036fa9a 100644 --- a/test/runtime/value/cast/regexp.ts +++ b/test/runtime/value/cast/regexp.ts @@ -50,4 +50,11 @@ describe('value/cast/RegExp', () => { const result = Value.Cast(T, value) Assert.IsEqual(result, value) }) + // ---------------------------------------------------------------- + // Throw + // ---------------------------------------------------------------- + it('Should throw with no default', () => { + const T = Type.RegExp(/foo/) + Assert.Throws(() => Value.Cast(T, null)) + }) }) diff --git a/test/runtime/value/check/const.ts b/test/runtime/value/check/const.ts new file mode 100644 index 000000000..4b3db9f44 --- /dev/null +++ b/test/runtime/value/check/const.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Assert.IsTrue(Value.Check(T, 1)) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Assert.IsTrue(Value.Check(T, 'hello')) + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Assert.IsTrue(Value.Check(T, true)) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Assert.IsTrue(Value.Check(T, { x: 1, y: 2 })) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Assert.IsTrue(Value.Check(T, [1, 2, 3])) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Assert.IsTrue(Value.Check(T, [1, true, 'hello'])) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Assert.IsTrue( + Value.Check(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }), + ) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 7b51cf3bf..9012725f2 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -4,6 +4,7 @@ import './async-iterator' import './bigint' import './boolean' import './composite' +import './const' import './constructor' import './date' import './enum' diff --git a/test/runtime/value/clean/any.ts b/test/runtime/value/clean/any.ts new file mode 100644 index 000000000..9a49a2562 --- /dev/null +++ b/test/runtime/value/clean/any.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Any', () => { + it('Should clean 1', () => { + const T = Type.Any() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/array.ts b/test/runtime/value/clean/array.ts new file mode 100644 index 000000000..6e8a21844 --- /dev/null +++ b/test/runtime/value/clean/array.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Array', () => { + it('Should clean 1', () => { + const T = Type.Any() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + const R = Value.Clean(T, [undefined, null, { x: 1 }, { x: 1, y: 2 }, { x: 1, y: 2, z: 3 }]) + Assert.IsEqual(R, [undefined, null, { x: 1 }, { x: 1, y: 2 }, { x: 1, y: 2 }]) + }) +}) diff --git a/test/runtime/value/clean/async-iterator.ts b/test/runtime/value/clean/async-iterator.ts new file mode 100644 index 000000000..a067233b3 --- /dev/null +++ b/test/runtime/value/clean/async-iterator.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/AsyncIterator', () => { + it('Should clean 1', () => { + const T = Type.AsyncIterator(Type.Number()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/bigint.ts b/test/runtime/value/clean/bigint.ts new file mode 100644 index 000000000..cdd77d4de --- /dev/null +++ b/test/runtime/value/clean/bigint.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/BigInt', () => { + it('Should clean 1', () => { + const T = Type.BigInt() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/boolean.ts b/test/runtime/value/clean/boolean.ts new file mode 100644 index 000000000..7697d25c3 --- /dev/null +++ b/test/runtime/value/clean/boolean.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Boolean', () => { + it('Should clean 1', () => { + const T = Type.Boolean() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/composite.ts b/test/runtime/value/clean/composite.ts new file mode 100644 index 000000000..035f335a9 --- /dev/null +++ b/test/runtime/value/clean/composite.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Composite', () => { + it('Should clean 1', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/constructor.ts b/test/runtime/value/clean/constructor.ts new file mode 100644 index 000000000..4a4be492c --- /dev/null +++ b/test/runtime/value/clean/constructor.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Constructor', () => { + it('Should clean 1', () => { + const T = Type.Constructor([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], Type.Object({ z: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/date.ts b/test/runtime/value/clean/date.ts new file mode 100644 index 000000000..8f777e4c3 --- /dev/null +++ b/test/runtime/value/clean/date.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Date', () => { + it('Should clean 1', () => { + const T = Type.Date() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/enum.ts b/test/runtime/value/clean/enum.ts new file mode 100644 index 000000000..88902047b --- /dev/null +++ b/test/runtime/value/clean/enum.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Enum', () => { + it('Should clean 1', () => { + const T = Type.Enum({ x: 1, y: 2 }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/function.ts b/test/runtime/value/clean/function.ts new file mode 100644 index 000000000..2bc5a4ee5 --- /dev/null +++ b/test/runtime/value/clean/function.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Function', () => { + it('Should clean 1', () => { + const T = Type.Function([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], Type.Object({ z: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/index.ts b/test/runtime/value/clean/index.ts new file mode 100644 index 000000000..b290dd224 --- /dev/null +++ b/test/runtime/value/clean/index.ts @@ -0,0 +1,34 @@ +import './any' +import './array' +import './async-iterator' +import './bigint' +import './boolean' +import './composite' +import './constructor' +import './date' +import './enum' +import './function' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './never' +import './not' +import './null' +import './object' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './string' +import './symbol' +import './template-literal' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/clean/integer.ts b/test/runtime/value/clean/integer.ts new file mode 100644 index 000000000..ff377049c --- /dev/null +++ b/test/runtime/value/clean/integer.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Integer', () => { + it('Should clean 1', () => { + const T = Type.Integer() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/intersect.ts b/test/runtime/value/clean/intersect.ts new file mode 100644 index 000000000..14f5d5221 --- /dev/null +++ b/test/runtime/value/clean/intersect.ts @@ -0,0 +1,346 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/clean/Intersect', () => { + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clean 4', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Intersect Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clean discard 4', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Intersect Deep + // ---------------------------------------------------------------- + it('Should clear intersect deep 1', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clear intersect deep 2', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clear intersect deep 3', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clear intersect deep 4', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clear intersect deep 5', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should clear intersect deep 6', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + it('Should clear intersect deep 7', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + // ---------------------------------------------------------------- + // Intersect Deep Discard + // ---------------------------------------------------------------- + it('Should clear intersect discard deep 1', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clear intersect discard deep 2', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, {}) + }) + it('Should clear intersect discard deep 3', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clear intersect discard deep 4', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clear intersect discard deep 5', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should clear intersect discard deep 6', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + it('Should clear intersect discard deep 7', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3, w: 3, a: 1 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + // ---------------------------------------------------------------- + // Intersect Invalid + // ---------------------------------------------------------------- + it('Should clean intersect invalid 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.String() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) // types are ignored + }) + // ---------------------------------------------------------------- + // Intersect Unevaluted Properties + // ---------------------------------------------------------------- + it('Should clean intersect unevaluated properties 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.String() }) + ], { + unevaluatedProperties: Type.String() + }) + const R = Value.Clean(T, { x: 1, y: 2, a: 1, b: '' }) + Assert.IsEqual(R, { x: 1, y: 2, b: '' }) + }) + // ---------------------------------------------------------------- + // Intersect Illogical + // ---------------------------------------------------------------- + it('Should clean illogical 1', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean illogical 2', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, 1) + Assert.IsEqual(R, 1) + }) + it('Should clean illogical 3', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean illogical 4', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean illogical 5', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }), + ]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { u: null, x: 1 }) // u retained from Number + }) +}) diff --git a/test/runtime/value/clean/iterator.ts b/test/runtime/value/clean/iterator.ts new file mode 100644 index 000000000..ec19feb07 --- /dev/null +++ b/test/runtime/value/clean/iterator.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Iterator', () => { + it('Should clean 1', () => { + const T = Type.Iterator(Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/keyof.ts b/test/runtime/value/clean/keyof.ts new file mode 100644 index 000000000..911912992 --- /dev/null +++ b/test/runtime/value/clean/keyof.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/KeyOf', () => { + it('Should clean 1', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number(), y: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/kind.ts b/test/runtime/value/clean/kind.ts new file mode 100644 index 000000000..8a4b836d5 --- /dev/null +++ b/test/runtime/value/clean/kind.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Kind', () => { + it('Should clean 1', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown' }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/literal.ts b/test/runtime/value/clean/literal.ts new file mode 100644 index 000000000..bf424b784 --- /dev/null +++ b/test/runtime/value/clean/literal.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Literal', () => { + it('Should clean 1', () => { + const T = Type.Literal(1) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/never.ts b/test/runtime/value/clean/never.ts new file mode 100644 index 000000000..2945598e4 --- /dev/null +++ b/test/runtime/value/clean/never.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Never', () => { + it('Should clean 1', () => { + const T = Type.Never() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/not.ts b/test/runtime/value/clean/not.ts new file mode 100644 index 000000000..cb60a05d0 --- /dev/null +++ b/test/runtime/value/clean/not.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Not', () => { + it('Should clean 1', () => { + const T = Type.Not(Type.Any()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/null.ts b/test/runtime/value/clean/null.ts new file mode 100644 index 000000000..a52d81a4d --- /dev/null +++ b/test/runtime/value/clean/null.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Null', () => { + it('Should clean 1', () => { + const T = Type.Null() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/number.ts b/test/runtime/value/clean/number.ts new file mode 100644 index 000000000..aafd93083 --- /dev/null +++ b/test/runtime/value/clean/number.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Number', () => { + it('Should clean 1', () => { + const T = Type.Number() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/object.ts b/test/runtime/value/clean/object.ts new file mode 100644 index 000000000..805665a7e --- /dev/null +++ b/test/runtime/value/clean/object.ts @@ -0,0 +1,178 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Object', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean 4', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Nested + // ---------------------------------------------------------------- + it('Should clean nested 1', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean nested 2', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean nested 3', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + it('Should clean nested 4', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, { x: { y: null } }) + Assert.IsEqual(R, { x: { y: null } }) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should clean additional properties 1', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties 2', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties 3', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean additional properties 4', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Additional Properties Discard + // ---------------------------------------------------------------- + it('Should clean additional properties discard 1', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties discard 2', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null }) + Assert.IsEqual(R, { k: '' }) + }) + it('Should clean additional properties discard 3', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null, x: 1 }) + Assert.IsEqual(R, { k: '', x: 1 }) + }) + it('Should clean additional properties discard 4', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null, x: 1, y: 2 }) + Assert.IsEqual(R, { k: '', x: 1, y: 2 }) + }) +}) diff --git a/test/runtime/value/clean/promise.ts b/test/runtime/value/clean/promise.ts new file mode 100644 index 000000000..0d2d7e4ff --- /dev/null +++ b/test/runtime/value/clean/promise.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Promise', () => { + it('Should clean 1', () => { + const T = Type.Promise(Type.Any()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/record.ts b/test/runtime/value/clean/record.ts new file mode 100644 index 000000000..6ef341c15 --- /dev/null +++ b/test/runtime/value/clean/record.ts @@ -0,0 +1,114 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Record', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { a: 1 }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { a: 1, 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should clean additional properties 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties Discard + // ---------------------------------------------------------------- + it('Should clean additional properties discard 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties discard 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties discard 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: null, 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties Keep + // ---------------------------------------------------------------- + it('Should clean additional properties keep 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties keep 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true }) + Assert.IsEqual(R, { a: true }) + }) + it('Should clean additional properties keep 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true, 0: null }) + Assert.IsEqual(R, { a: true, 0: null }) + }) +}) diff --git a/test/runtime/value/clean/recursive.ts b/test/runtime/value/clean/recursive.ts new file mode 100644 index 000000000..1a6bb6e82 --- /dev/null +++ b/test/runtime/value/clean/recursive.ts @@ -0,0 +1,112 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Recursive', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [{ id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean discard 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean discard 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean discard 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [{ u: null, id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) +}) diff --git a/test/runtime/value/clean/ref.ts b/test/runtime/value/clean/ref.ts new file mode 100644 index 000000000..b06556159 --- /dev/null +++ b/test/runtime/value/clean/ref.ts @@ -0,0 +1,78 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Ref', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null, x: null }) + Assert.IsEqual(R, { x: null }) + }) +}) diff --git a/test/runtime/value/clean/regexp.ts b/test/runtime/value/clean/regexp.ts new file mode 100644 index 000000000..6b1d57421 --- /dev/null +++ b/test/runtime/value/clean/regexp.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/RegExp', () => { + it('Should clean 1', () => { + const T = Type.RegExp('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/string.ts b/test/runtime/value/clean/string.ts new file mode 100644 index 000000000..aa5d75727 --- /dev/null +++ b/test/runtime/value/clean/string.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/String', () => { + it('Should clean 1', () => { + const T = Type.String() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/symbol.ts b/test/runtime/value/clean/symbol.ts new file mode 100644 index 000000000..62047d28e --- /dev/null +++ b/test/runtime/value/clean/symbol.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Symbol', () => { + it('Should clean 1', () => { + const T = Type.Symbol() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/template-literal.ts b/test/runtime/value/clean/template-literal.ts new file mode 100644 index 000000000..f76730548 --- /dev/null +++ b/test/runtime/value/clean/template-literal.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/TemplateLiteral', () => { + it('Should clean 1', () => { + const T = Type.TemplateLiteral('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/tuple.ts b/test/runtime/value/clean/tuple.ts new file mode 100644 index 000000000..81618f0f9 --- /dev/null +++ b/test/runtime/value/clean/tuple.ts @@ -0,0 +1,110 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Tuple', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean 3', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should clean 4', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2, 3]) + Assert.IsEqual(R, [1, 2]) + }) + // ---------------------------------------------------------------- + // Clean Deep + // ---------------------------------------------------------------- + it('Should clean deep 1', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean deep 2', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean deep 3', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, [1]) + }) + it('Should clean deep 4', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, null]) + Assert.IsEqual(R, [1, null]) + }) + it('Should clean deep 5', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + it('Should clean deep 6', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { u: null, x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + // ---------------------------------------------------------------- + // Clean Empty + // ---------------------------------------------------------------- + it('Should clean empty 1', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean empty 2', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean empty 3', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, []) + }) +}) diff --git a/test/runtime/value/clean/uint8array.ts b/test/runtime/value/clean/uint8array.ts new file mode 100644 index 000000000..732eb5b71 --- /dev/null +++ b/test/runtime/value/clean/uint8array.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Uint8Array', () => { + it('Should clean 1', () => { + const T = Type.Uint8Array() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/undefined.ts b/test/runtime/value/clean/undefined.ts new file mode 100644 index 000000000..4b590c41b --- /dev/null +++ b/test/runtime/value/clean/undefined.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Undefined', () => { + it('Should clean 1', () => { + const T = Type.Undefined() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/union.ts b/test/runtime/value/clean/union.ts new file mode 100644 index 000000000..443e3d651 --- /dev/null +++ b/test/runtime/value/clean/union.ts @@ -0,0 +1,134 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Union', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, 1) + Assert.IsEqual(R, 1) + }) + it('Should clean 2', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, true) + Assert.IsEqual(R, true) + }) + // ---------------------------------------------------------------- + // Clean Select + // ---------------------------------------------------------------- + it('Should clean select 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean select 3', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + it('Should clean select 4', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { y: null }) + Assert.IsEqual(R, { y: null }) + }) + // ---------------------------------------------------------------- + // Clean Select Discard + // ---------------------------------------------------------------- + it('Should clean select discard 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select discard 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, { u: null }) // no match + }) + it('Should clean select discard 3', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean select discard 4', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { y: 1 }) + }) + // ---------------------------------------------------------------- + // Clean Select Retain + // ---------------------------------------------------------------- + it('Should clean select retain 1', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select retain 2', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, { u: null }) + }) + it('Should clean select retain 3', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { u: null, x: 1 }) + }) + it('Should clean select retain 4', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { u: null, y: 1 }) + }) + // ---------------------------------------------------------------- + // Clean Select First and Discard + // ---------------------------------------------------------------- + it('Should clean select first and discard 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean select first and discard 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { u: null, y: 1 }) + }) +}) diff --git a/test/runtime/value/clean/unknown.ts b/test/runtime/value/clean/unknown.ts new file mode 100644 index 000000000..edba3ebfc --- /dev/null +++ b/test/runtime/value/clean/unknown.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Unknown', () => { + it('Should clean 1', () => { + const T = Type.Unknown() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/void.ts b/test/runtime/value/clean/void.ts new file mode 100644 index 000000000..cd4c20db0 --- /dev/null +++ b/test/runtime/value/clean/void.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Void', () => { + it('Should clean 1', () => { + const T = Type.Void() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/create/regexp.ts b/test/runtime/value/create/regexp.ts index f1415fd6a..cc424393c 100644 --- a/test/runtime/value/create/regexp.ts +++ b/test/runtime/value/create/regexp.ts @@ -13,4 +13,11 @@ describe('value/create/RegEx', () => { const T = Type.RegExp(/foo/, { default: 'foo' }) Assert.IsEqual(Value.Create(T), 'foo') }) + // ---------------------------------------------------------------- + // Throw + // ---------------------------------------------------------------- + it('Should throw with no default', () => { + const T = Type.RegExp(/foo/) + Assert.Throws(() => Value.Create(T)) + }) }) diff --git a/test/runtime/value/default/any.ts b/test/runtime/value/default/any.ts new file mode 100644 index 000000000..15b12e979 --- /dev/null +++ b/test/runtime/value/default/any.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Any', () => { + it('Should use default', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/array.ts b/test/runtime/value/default/array.ts new file mode 100644 index 000000000..be385e603 --- /dev/null +++ b/test/runtime/value/default/array.ts @@ -0,0 +1,24 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Array', () => { + it('Should use default', () => { + const T = Type.Array(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Array(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Elements + // ---------------------------------------------------------------- + it('Should use default on elements', () => { + const T = Type.Array(Type.Number({ default: 2 })) + const R = Value.Default(T, [1, undefined, 3]) + Assert.IsEqual(R, [1, 2, 3]) + }) +}) diff --git a/test/runtime/value/default/async-iterator.ts b/test/runtime/value/default/async-iterator.ts new file mode 100644 index 000000000..dd26f579c --- /dev/null +++ b/test/runtime/value/default/async-iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/AsyncIterator', () => { + it('Should use default', () => { + const T = Type.AsyncIterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.AsyncIterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/bigint.ts b/test/runtime/value/default/bigint.ts new file mode 100644 index 000000000..b7e8aa710 --- /dev/null +++ b/test/runtime/value/default/bigint.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/BigInt', () => { + it('Should use default', () => { + const T = Type.BigInt({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.BigInt({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/boolean.ts b/test/runtime/value/default/boolean.ts new file mode 100644 index 000000000..ee05bb06b --- /dev/null +++ b/test/runtime/value/default/boolean.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Boolean', () => { + it('Should use default', () => { + const T = Type.Boolean({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Boolean({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/composite.ts b/test/runtime/value/default/composite.ts new file mode 100644 index 000000000..8a73e4106 --- /dev/null +++ b/test/runtime/value/default/composite.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Composite', () => { + it('Should use default', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/constructor.ts b/test/runtime/value/default/constructor.ts new file mode 100644 index 000000000..76cb2343f --- /dev/null +++ b/test/runtime/value/default/constructor.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Constructor', () => { + it('Should use default', () => { + const T = Type.Constructor([], Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Constructor([], Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/date.ts b/test/runtime/value/default/date.ts new file mode 100644 index 000000000..2522cf20d --- /dev/null +++ b/test/runtime/value/default/date.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Date', () => { + it('Should use default', () => { + const T = Type.Date({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Date({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/enum.ts b/test/runtime/value/default/enum.ts new file mode 100644 index 000000000..ae6f9ece2 --- /dev/null +++ b/test/runtime/value/default/enum.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Enum', () => { + it('Should use default', () => { + const T = Type.Enum({}, { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Enum({}, { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/function.ts b/test/runtime/value/default/function.ts new file mode 100644 index 000000000..959c28284 --- /dev/null +++ b/test/runtime/value/default/function.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Function', () => { + it('Should use default', () => { + const T = Type.Function([], Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Function([], Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/index.ts b/test/runtime/value/default/index.ts new file mode 100644 index 000000000..7395f9fce --- /dev/null +++ b/test/runtime/value/default/index.ts @@ -0,0 +1,35 @@ +import './any' +import './array' +import './async-iterator' +import './bigint' +import './boolean' +import './composite' +import './constructor' +import './date' +import './enum' +import './function' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './never' +import './not' +import './null' +import './number' +import './object' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './string' +import './symbol' +import './template-literal' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/default/integer.ts b/test/runtime/value/default/integer.ts new file mode 100644 index 000000000..c0ce64b4a --- /dev/null +++ b/test/runtime/value/default/integer.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Integer', () => { + it('Should use default', () => { + const T = Type.Integer({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Integer({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/intersect.ts b/test/runtime/value/default/intersect.ts new file mode 100644 index 000000000..0e72b3e6e --- /dev/null +++ b/test/runtime/value/default/intersect.ts @@ -0,0 +1,106 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Intersect', () => { + it('Should use default', () => { + const T = Type.Intersect([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Intersect([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Intersected + // ---------------------------------------------------------------- + it('Should use default intersected 1', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1, b: 2 }) + }) + it('Should use default intersected 2', () => { + const A = Type.Object({ + a: Type.Number(), + }) + const B = Type.Object({ + b: Type.Number(), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should use default intersected 3', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, { a: 3 }) + Assert.IsEqual(R, { a: 3, b: 2 }) + }) + it('Should use default intersected 4', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, { a: 4, b: 5 }) + Assert.IsEqual(R, { a: 4, b: 5 }) + }) + it('Should use default intersected 5', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Number({ default: 2 }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1 }) + }) + it('Should use default intersected 6', () => { + const A = Type.Number({ default: 2 }) + const B = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1 }) + }) + // ---------------------------------------------------------------- + // Intersected Deep + // ---------------------------------------------------------------- + it('Should use default intersected deep 1', () => { + const A = Type.Object({ a: Type.Number({ default: 1 }) }) + const B = Type.Object({ b: Type.Number({ default: 2 }) }) + const C = Type.Object({ c: Type.Number({ default: 3 }) }) + const D = Type.Object({ d: Type.Number({ default: 4 }) }) + const T1 = Type.Intersect([A, B]) + const T2 = Type.Intersect([C, D]) + const T = Type.Intersect([T1, T2]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1, b: 2, c: 3, d: 4 }) + }) + it('Should use default intersected deep 2', () => { + const A = Type.Object({ a: Type.Number({}) }) + const B = Type.Object({ b: Type.Number({}) }) + const C = Type.Object({ c: Type.Number({}) }) + const D = Type.Object({ d: Type.Number({}) }) + const T1 = Type.Intersect([A, B]) + const T2 = Type.Intersect([C, D]) + const T = Type.Intersect([T1, T2]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) +}) diff --git a/test/runtime/value/default/iterator.ts b/test/runtime/value/default/iterator.ts new file mode 100644 index 000000000..d49b7f296 --- /dev/null +++ b/test/runtime/value/default/iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Iterator', () => { + it('Should use default', () => { + const T = Type.Iterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Iterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/keyof.ts b/test/runtime/value/default/keyof.ts new file mode 100644 index 000000000..2cb0bb26b --- /dev/null +++ b/test/runtime/value/default/keyof.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/KeyOf', () => { + it('Should use default', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/kind.ts b/test/runtime/value/default/kind.ts new file mode 100644 index 000000000..1f8a96fcc --- /dev/null +++ b/test/runtime/value/default/kind.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Kind', () => { + it('Should use default', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown', default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown', default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/literal.ts b/test/runtime/value/default/literal.ts new file mode 100644 index 000000000..fa01d55b9 --- /dev/null +++ b/test/runtime/value/default/literal.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Literal', () => { + it('Should use default', () => { + const T = Type.Literal('foo', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Literal('foo', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/never.ts b/test/runtime/value/default/never.ts new file mode 100644 index 000000000..a39542739 --- /dev/null +++ b/test/runtime/value/default/never.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Never', () => { + it('Should use default', () => { + const T = Type.Never({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Never({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/not.ts b/test/runtime/value/default/not.ts new file mode 100644 index 000000000..148756d5b --- /dev/null +++ b/test/runtime/value/default/not.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Not', () => { + it('Should use default', () => { + const T = Type.Not(Type.String(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Not(Type.String(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/null.ts b/test/runtime/value/default/null.ts new file mode 100644 index 000000000..9246fe893 --- /dev/null +++ b/test/runtime/value/default/null.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Null', () => { + it('Should use default', () => { + const T = Type.Null({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Null({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/number.ts b/test/runtime/value/default/number.ts new file mode 100644 index 000000000..6dcaa7035 --- /dev/null +++ b/test/runtime/value/default/number.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Number', () => { + it('Should use default', () => { + const T = Type.Number({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Number({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/object.ts b/test/runtime/value/default/object.ts new file mode 100644 index 000000000..ab4dda473 --- /dev/null +++ b/test/runtime/value/default/object.ts @@ -0,0 +1,180 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Object', () => { + it('Should use default', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------- + it('Should should fully construct object 1', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, { x: { x: 1, y: 2 }, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 2', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, { x: null }) + Assert.IsEqual(R, { x: null, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 3', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, { x: { x: null, y: null } }) + Assert.IsEqual(R, { x: { x: null, y: null }, y: { x: 3, y: 4 } }) + }) + // ---------------------------------------------------------------- + // Properties + // ---------------------------------------------------------------- + it('Should use property defaults 1', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: 1 }, + ) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use property defaults 2', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should use property defaults 3', () => { + const T = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should use property defaults 4', () => { + const T = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }) + const R = Value.Default(T, { x: 3 }) + Assert.IsEqual(R, { x: 3 }) + }) + // ---------------------------------------------------------------- + // AdditionalProperties + // ---------------------------------------------------------------- + it('Should use additional property defaults 1', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use additional property defaults 2', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ) + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: 3 }) + }) + it('Should use additional property defaults 3', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number(), + }, + ) + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: undefined }) + }) +}) diff --git a/test/runtime/value/default/promise.ts b/test/runtime/value/default/promise.ts new file mode 100644 index 000000000..659e052cb --- /dev/null +++ b/test/runtime/value/default/promise.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Promise', () => { + it('Should use default', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/record.ts b/test/runtime/value/default/record.ts new file mode 100644 index 000000000..4b300ddc2 --- /dev/null +++ b/test/runtime/value/default/record.ts @@ -0,0 +1,66 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Record', () => { + it('Should use default', () => { + const T = Type.Record(Type.String(), Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Record(Type.String(), Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // // ---------------------------------------------------------------- + // // Properties + // // ---------------------------------------------------------------- + it('Should use property defaults 1', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: 1 }) + }) + it('Should use property defaults 2', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + it('Should use property defaults 3', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { a: undefined }) + Assert.IsEqual(R, { a: undefined }) + }) + it('Should use property defaults 4', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: 1 }) + }) + it('Should use property defaults 5', () => { + const T = Type.Record(Type.Number(), Type.Number()) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: undefined }) + }) + it('Should use property defaults 6', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should use additional property defaults 1', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 }), { + additionalProperties: Type.Number({ default: 3 }), + }) + const R = Value.Default(T, { 0: undefined, a: undefined }) + Assert.IsEqual(R, { 0: 1, a: 3 }) + }) + it('Should use additional property defaults 2', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 }), { + additionalProperties: Type.Number(), + }) + const R = Value.Default(T, { 0: undefined, a: undefined }) + Assert.IsEqual(R, { 0: 1, a: undefined }) + }) +}) diff --git a/test/runtime/value/default/recursive.ts b/test/runtime/value/default/recursive.ts new file mode 100644 index 000000000..417727bda --- /dev/null +++ b/test/runtime/value/default/recursive.ts @@ -0,0 +1,58 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/default/Recursive', () => { + it('Should use default', () => { + const T = Type.Recursive((This) => Type.Object({ + nodes: Type.Array(This) + }, { default: 1 })) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Recursive((This) => Type.Object({ + nodes: Type.Array(This) + }, { default: 1 })) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Recursive + // ---------------------------------------------------------------- + it('Should use default recursive values', () => { + const T = Type.Recursive((This) => Type.Object({ + id: Type.String({ default: 1 }), + nodes: Type.Array(This, { default: [] }) // need this + })) + const R = Value.Default(T, { + nodes: [{ + nodes: [{ + nodes: [{ id: null }] + }, { + nodes: [{ id: null }] + }] + }] + }) + Assert.IsEqual(R, { + nodes: [{ + nodes: [{ + nodes: [{ + id: null, + nodes: [] + }], + id: 1 + }, { + nodes: [{ + id: null, + nodes: [] + }], + id: 1 + }], + id: 1 + }], + id: 1 + }) + }) +}) diff --git a/test/runtime/value/default/ref.ts b/test/runtime/value/default/ref.ts new file mode 100644 index 000000000..634059573 --- /dev/null +++ b/test/runtime/value/default/ref.ts @@ -0,0 +1,33 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Ref', () => { + it('Should use default', () => { + const A = Type.String({ $id: 'A' }) + const T = Type.Ref('A', { default: 1 }) + const R = Value.Default(T, [A], undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const A = Type.String({ $id: 'A' }) + const T = Type.Ref('A', { default: 1 }) + const R = Value.Default(T, [A], null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Foreign + // ---------------------------------------------------------------- + it('Should use default on foreign value', () => { + const A = Type.String({ $id: 'A', default: 1 }) + const T = Type.Ref('A') + const R = Value.Default(T, [A], undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value on foreign value', () => { + const A = Type.String({ $id: 'A', default: 1 }) + const T = Type.Ref('A') + const R = Value.Default(T, [A], null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/regexp.ts b/test/runtime/value/default/regexp.ts new file mode 100644 index 000000000..9c32c7ab2 --- /dev/null +++ b/test/runtime/value/default/regexp.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/RegExp', () => { + it('Should use default', () => { + const T = Type.RegExp('', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.RegExp('', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/string.ts b/test/runtime/value/default/string.ts new file mode 100644 index 000000000..af833332e --- /dev/null +++ b/test/runtime/value/default/string.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/String', () => { + it('Should use default', () => { + const T = Type.String({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.String({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/symbol.ts b/test/runtime/value/default/symbol.ts new file mode 100644 index 000000000..d593fb7b8 --- /dev/null +++ b/test/runtime/value/default/symbol.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Symbol', () => { + it('Should use default', () => { + const T = Type.Symbol({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Symbol({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/template-literal.ts b/test/runtime/value/default/template-literal.ts new file mode 100644 index 000000000..2dd6b7e83 --- /dev/null +++ b/test/runtime/value/default/template-literal.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/TemplateLiteral', () => { + it('Should use default', () => { + const T = Type.TemplateLiteral('hello', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.TemplateLiteral('hello', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/tuple.ts b/test/runtime/value/default/tuple.ts new file mode 100644 index 000000000..c7acdc99b --- /dev/null +++ b/test/runtime/value/default/tuple.ts @@ -0,0 +1,44 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Tuple', () => { + it('Should use default', () => { + const T = Type.Tuple([Type.Number(), Type.Number()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Tuple([Type.Number(), Type.Number()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Elements + // ---------------------------------------------------------------- + it('Should use default elements 1', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + it('Should use default elements 2', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, [1, 2]) + }) + it('Should use default elements 3', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, [4, 5, 6]) + Assert.IsEqual(R, [4, 5, 6]) + }) + it('Should use default elements 4', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })]) + const R = Value.Default(T, [4, 5, 6]) + Assert.IsEqual(R, [4, 5, 6]) + }) + it('Should use default elements 5', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })]) + const R = Value.Default(T, [4]) + Assert.IsEqual(R, [4, 2]) + }) +}) diff --git a/test/runtime/value/default/uint8array.ts b/test/runtime/value/default/uint8array.ts new file mode 100644 index 000000000..5c3cfa77d --- /dev/null +++ b/test/runtime/value/default/uint8array.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Uint8Array', () => { + it('Should use default', () => { + const T = Type.Uint8Array({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Uint8Array({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/undefined.ts b/test/runtime/value/default/undefined.ts new file mode 100644 index 000000000..d622cd1c6 --- /dev/null +++ b/test/runtime/value/default/undefined.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Undefined', () => { + it('Should use default', () => { + const T = Type.Undefined({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Undefined({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/union.ts b/test/runtime/value/default/union.ts new file mode 100644 index 000000000..56b572caf --- /dev/null +++ b/test/runtime/value/default/union.ts @@ -0,0 +1,110 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Union', () => { + it('Should use default', () => { + const T = Type.Union([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Union([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Interior + // ---------------------------------------------------------------- + it('Should default interior 1', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + it('Should default interior 2', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 'hello') + }) + it('Should default interior 3', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, 'world') + Assert.IsEqual(R, 'world') + }) + it('Should default interior 4', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should default interior 5', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, { x: 3 }) + Assert.IsEqual(R, { x: 3, y: 2 }) + }) + it('Should default interior 6', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, { x: 3, y: 4 }) + Assert.IsEqual(R, { x: 3, y: 4 }) + }) + // ---------------------------------------------------------------- + // Interior Unsafe + // ---------------------------------------------------------------- + it('Should default interior unsafe 1', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.Unsafe({ default: 'hello' }), + ]) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should default interior unsafe 2', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.Unsafe({ default: 'hello' }), + ]) + const R = Value.Default(T, 'world') + Assert.IsEqual(R, 'world') + }) +}) diff --git a/test/runtime/value/default/unknown.ts b/test/runtime/value/default/unknown.ts new file mode 100644 index 000000000..97236570d --- /dev/null +++ b/test/runtime/value/default/unknown.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Unknown', () => { + it('Should use default', () => { + const T = Type.Unknown({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Unknown({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/void.ts b/test/runtime/value/default/void.ts new file mode 100644 index 000000000..03d03c1e3 --- /dev/null +++ b/test/runtime/value/default/void.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Void', () => { + it('Should use default', () => { + const T = Type.Void({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Void({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/guard/guard.ts b/test/runtime/value/guard/guard.ts index d51511184..ee8d3839f 100644 --- a/test/runtime/value/guard/guard.ts +++ b/test/runtime/value/guard/guard.ts @@ -1,5 +1,5 @@ import { Assert } from '../../assert/index' -import * as ValueGuard from '@sinclair/typebox/value/guard' +import * as ValueGuard from '@sinclair/typebox/value' describe('value/guard/ValueGuard', () => { // ----------------------------------------------------- diff --git a/test/runtime/value/hash/hash.ts b/test/runtime/value/hash/hash.ts index 8d06d509f..aceb06480 100644 --- a/test/runtime/value/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -1,122 +1,122 @@ -import * as ValueHash from '@sinclair/typebox/value/hash' +import { Hash } from '@sinclair/typebox/value' import { Assert } from '../../assert/index' describe('value/hash/Hash', () => { it('Should hash number', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(1)) - const A = ValueHash.Hash(1) - const B = ValueHash.Hash(2) + Assert.IsEqual('bigint', typeof Hash(1)) + const A = Hash(1) + const B = Hash(2) Assert.NotEqual(A, B) }) it('Should hash string', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash('hello')) - const A = ValueHash.Hash('hello') - const B = ValueHash.Hash('world') + Assert.IsEqual('bigint', typeof Hash('hello')) + const A = Hash('hello') + const B = Hash('world') Assert.NotEqual(A, B) }) it('Should hash boolean', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(true)) - Assert.IsEqual('bigint', typeof ValueHash.Hash(false)) - const A = ValueHash.Hash(true) - const B = ValueHash.Hash(false) + Assert.IsEqual('bigint', typeof Hash(true)) + Assert.IsEqual('bigint', typeof Hash(false)) + const A = Hash(true) + const B = Hash(false) Assert.NotEqual(A, B) }) it('Should hash null', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(null)) - const A = ValueHash.Hash(null) - const B = ValueHash.Hash(undefined) + Assert.IsEqual('bigint', typeof Hash(null)) + const A = Hash(null) + const B = Hash(undefined) Assert.NotEqual(A, B) }) it('Should hash array', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash([0, 1, 2, 3])) - const A = ValueHash.Hash([0, 1, 2, 3]) - const B = ValueHash.Hash([0, 2, 2, 3]) + Assert.IsEqual('bigint', typeof Hash([0, 1, 2, 3])) + const A = Hash([0, 1, 2, 3]) + const B = Hash([0, 2, 2, 3]) Assert.NotEqual(A, B) }) it('Should hash object 1', () => { // prettier-ignore - Assert.IsEqual('bigint', typeof ValueHash.Hash({ x: 1, y: 2 })) - const A = ValueHash.Hash({ x: 1, y: 2 }) - const B = ValueHash.Hash({ x: 2, y: 2 }) + Assert.IsEqual('bigint', typeof Hash({ x: 1, y: 2 })) + const A = Hash({ x: 1, y: 2 }) + const B = Hash({ x: 2, y: 2 }) Assert.NotEqual(A, B) }) it('Should hash object 2', () => { - const A = ValueHash.Hash({ x: 1, y: [1, 2] }) - const B = ValueHash.Hash({ x: 1, y: [1, 3] }) + const A = Hash({ x: 1, y: [1, 2] }) + const B = Hash({ x: 1, y: [1, 3] }) Assert.NotEqual(A, B) }) it('Should hash object 3', () => { - const A = ValueHash.Hash({ x: 1, y: undefined }) - const B = ValueHash.Hash({ x: 1 }) + const A = Hash({ x: 1, y: undefined }) + const B = Hash({ x: 1 }) Assert.NotEqual(A, B) }) it('Should hash object 4', () => { - const A = ValueHash.Hash({ x: 1, y: new Uint8Array([0, 1, 2]) }) - const B = ValueHash.Hash({ x: 1, y: [0, 1, 2] }) + const A = Hash({ x: 1, y: new Uint8Array([0, 1, 2]) }) + const B = Hash({ x: 1, y: [0, 1, 2] }) Assert.NotEqual(A, B) }) it('Should hash object 5', () => { - const A = ValueHash.Hash({ x: 1, y: undefined }) - const B = ValueHash.Hash({ x: 2, y: undefined }) + const A = Hash({ x: 1, y: undefined }) + const B = Hash({ x: 2, y: undefined }) Assert.NotEqual(A, B) }) it('Should hash Date', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(new Date())) - const A = ValueHash.Hash(new Date(1)) - const B = ValueHash.Hash(new Date(2)) + Assert.IsEqual('bigint', typeof Hash(new Date())) + const A = Hash(new Date(1)) + const B = Hash(new Date(2)) Assert.NotEqual(A, B) }) it('Should hash Uint8Array', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(new Uint8Array([0, 1, 2, 3]))) - const A = ValueHash.Hash(new Uint8Array([0, 1, 2, 3])) - const B = ValueHash.Hash(new Uint8Array([0, 2, 2, 3])) + Assert.IsEqual('bigint', typeof Hash(new Uint8Array([0, 1, 2, 3]))) + const A = Hash(new Uint8Array([0, 1, 2, 3])) + const B = Hash(new Uint8Array([0, 2, 2, 3])) Assert.NotEqual(A, B) }) it('Should hash undefined', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(undefined)) - const A = ValueHash.Hash(undefined) - const B = ValueHash.Hash(null) + Assert.IsEqual('bigint', typeof Hash(undefined)) + const A = Hash(undefined) + const B = Hash(null) Assert.NotEqual(A, B) }) it('Should hash symbol 1', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol()) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol()) Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol(2)) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol(2)) Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol(1)) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol(1)) Assert.IsEqual(A, B) }) it('Should hash bigint 1', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) - const A = ValueHash.Hash(BigInt(1)) - const B = ValueHash.Hash(BigInt(2)) + Assert.IsEqual('bigint', typeof Hash(BigInt(1))) + const A = Hash(BigInt(1)) + const B = Hash(BigInt(2)) Assert.NotEqual(A, B) }) it('Should hash bigint 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) - const A = ValueHash.Hash(BigInt(1)) - const B = ValueHash.Hash(BigInt(1)) + Assert.IsEqual('bigint', typeof Hash(BigInt(1))) + const A = Hash(BigInt(1)) + const B = Hash(BigInt(1)) Assert.IsEqual(A, B) }) // ---------------------------------------------------------------- // Unicode // ---------------------------------------------------------------- it('Should hash unicode 1 (retain single byte hash)', () => { - const hash = ValueHash.Hash('a') + const hash = Hash('a') Assert.IsEqual(hash, 586962220959696054n) }) it('Should hash unicode 2', () => { - const hash = ValueHash.Hash('안녕 세계') + const hash = Hash('안녕 세계') Assert.IsEqual(hash, 11219208047802711777n) }) }) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index de126d75d..4e6ce996e 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -1,8 +1,10 @@ import './cast' import './check' +import './clean' import './clone' import './convert' import './create' +import './default' import './delta' import './equal' import './guard' diff --git a/test/runtime/value/transform/_encoder.ts b/test/runtime/value/transform/_encoder.ts index 06d82410b..22a2b1d1d 100644 --- a/test/runtime/value/transform/_encoder.ts +++ b/test/runtime/value/transform/_encoder.ts @@ -1,4 +1,4 @@ -import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol, IsDate } from '@sinclair/typebox/value/guard' +import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol, IsDate } from '@sinclair/typebox/value' import { TSchema, StaticDecode, StaticEncode } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value } from '@sinclair/typebox/value' diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts index 93eeff81c..43152417f 100644 --- a/test/runtime/value/transform/union.ts +++ b/test/runtime/value/transform/union.ts @@ -1,7 +1,6 @@ import * as Encoder from './_encoder' import { Assert } from '../../assert' -import { Value } from '@sinclair/typebox/value' -import { Type, TSchema } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' describe('value/transform/Union', () => { // -------------------------------------------------------- @@ -199,4 +198,44 @@ describe('value/transform/Union', () => { it('Should throw on interior union encode', () => { Assert.Throws(() => Encoder.Encode(T52, 1)) }) + // prettier-ignore + { // https://github.com/sinclairzx81/typebox/issues/676 + // interior-type + const S = Type.Transform(Type.String()) + .Decode((value: string) => new globalThis.Date(value)) + .Encode((value: Date) => value.toISOString()) + // union-type + const U = Type.Union([ + Type.Object({ date: S }), + Type.Number() + ]) + // expect date on decode + const T1 = Type.Transform(U) + .Decode((value) => { + Assert.IsTypeOf(value, 'object') + Assert.HasProperty(value, 'date') + Assert.IsInstanceOf(value.date, globalThis.Date); + return value + }) + .Encode((value) => value) + // expect number on decode + const T2 = Type.Transform(U) + .Decode((value) => { + Assert.IsTypeOf(value, 'number') + return value + }) + .Encode((value) => value) + + it('Should decode interior union 1', () => { + const R = Encoder.Decode(T1, { date: new globalThis.Date().toISOString() }) + Assert.IsTypeOf(R, 'object') + Assert.HasProperty(R, 'date') + Assert.IsInstanceOf(R.date, globalThis.Date); + }) + it('Should decode interior union 2', () => { + const R = Encoder.Decode(T2, 123) + Assert.IsTypeOf(R, 'number') + Assert.IsEqual(R, 123) + }) + } }) diff --git a/test/static/assert.ts b/test/static/assert.ts index 9a10c8c07..862731dbf 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -27,22 +27,24 @@ export type CircularHelper = [T] extends U ? T : Expected // See https://github.com/Microsoft/TypeScript/issues/27024 export type ConstrainEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? T : Expected export type ConstraintMutuallyExtend = CircularHelper + +// Circular Error on TS 5.4.0 // If U is never, there's nothing we can do -export type ComplexConstraint = If< - // If U is any, we can't use Expect or it would satisfy the constraint - And>, IsAny>, - never, - If< - Or< - // If they are both any we are happy - And, IsAny>, - // If T extends U, but not because it's any, we are happy - And, Not>> - >, - T, - Expected - > -> +// export type ComplexConstraint = If< +// // If U is any, we can't use Expect or it would satisfy the constraint +// And>, IsAny>, +// never, +// If< +// Or< +// // If they are both any we are happy +// And, IsAny>, +// // If T extends U, but not because it's any, we are happy +// And, Not>> +// >, +// T, +// Expected +// > +// > // ------------------------------------------------------------------ // Expect // ------------------------------------------------------------------ @@ -50,9 +52,12 @@ export type ExpectResult = If< IsNever>, { ToStaticNever(): void }, { - ToStatic, U>>(): void - ToStaticDecode, U>>(): void - ToStaticEncode, U>>(): void + ToStatic, U>>(): void + ToStaticDecode, U>>(): void + ToStaticEncode, U>>(): void + // ToStatic>(): void + // ToStaticDecode>(): void + // ToStaticEncode>(): void } > export function Expect(schema: T) { diff --git a/test/static/const.ts b/test/static/const.ts new file mode 100644 index 000000000..d81d0fed7 --- /dev/null +++ b/test/static/const.ts @@ -0,0 +1,41 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Identity Types +// ------------------------------------------------------------------ +// prettier-ignore +Expect(Type.Const(undefined)).ToStatic() +// prettier-ignore +Expect(Type.Const(null)).ToStatic() +// prettier-ignore +Expect(Type.Const(Symbol())).ToStatic() +// prettier-ignore +Expect(Type.Const(1 as const)).ToStatic<1>() +// prettier-ignore +Expect(Type.Const('hello' as const)).ToStatic<'hello'>() +// prettier-ignore +Expect(Type.Const(true as const)).ToStatic() + +// ------------------------------------------------------------------ +// Complex Types +// ------------------------------------------------------------------ +// prettier-ignore +Expect(Type.Const(100n)).ToStatic() +// prettier-ignore +Expect(Type.Const(new Date())).ToStatic() +// prettier-ignore +Expect(Type.Const(new Uint8Array())).ToStatic() +// prettier-ignore +Expect(Type.Const(function () {})).ToStatic<() => unknown>() +// prettier-ignore +Expect(Type.Const((function *(): any {})())).ToStatic() +// prettier-ignore +Expect(Type.Const((async function *(): any {})())).ToStatic() +// todo: remove when dropping TS 4.0 +// prettier-ignore +Expect(Type.Const({ x: 1, y: { z: 2 } })).ToStatic<{ readonly x: number, readonly y: { readonly z: number }}>() +// prettier-ignore +Expect(Type.Const({ x: 1, y: { z: 2 } } as const)).ToStatic<{ readonly x: 1, readonly y: { readonly z: 2 }}>() +// prettier-ignore +Expect(Type.Const([1, 2, 3] as const)).ToStatic<[1, 2, 3]>() diff --git a/test/static/deref.ts b/test/static/deref.ts new file mode 100644 index 000000000..4df0737ef --- /dev/null +++ b/test/static/deref.ts @@ -0,0 +1,56 @@ +import { Expect } from './assert' +import { Type, TRef, TObject, TNumber } from '@sinclair/typebox' + +// prettier-ignore +const Vector: TObject<{ + x: TNumber; + y: TNumber; +}> = Type.Object({ + x: Type.Number(), + y: Type.Number(), +}, { $id: 'Vector' }) + +// prettier-ignore +const VectorRef: TRef> = Type.Ref(Vector) + +// prettier-ignore +const Vertex: TObject<{ + position: TRef>; + texcoord: TRef>; +}> = Type.Object({ + position: VectorRef, + texcoord: VectorRef, +}) + +// prettier-ignore +const VertexDeref: TObject<{ + position: TObject<{ + x: TNumber; + y: TNumber; + }>; + texcoord: TObject<{ + x: TNumber; + y: TNumber; + }>; +}> = Type.Deref(Vertex, [Vector]) + +// prettier-ignore +Expect(VertexDeref).ToStatic<{ + position: { + x: number; + y: number; + }; + texcoord: { + x: number; + y: number; + }; +}> diff --git a/test/static/index.ts b/test/static/index.ts index 911f5ec19..df90d857c 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -6,9 +6,11 @@ import './bigint' import './boolean' import './capitalize' import './composite' +import './const' import './constructor-parameters' import './constructor' import './date' +import './deref' import './enum' import './extract' import './exclude' @@ -20,6 +22,7 @@ import './iterator' import './keyof' import './literal' import './lowercase' +import './mapped' import './modifier' import './namespace' import './never' diff --git a/test/static/indexed.ts b/test/static/indexed.ts index a33af5130..fe5875b7b 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -54,13 +54,13 @@ import { Type, Static } from '@sinclair/typebox' const A = Type.Object({}) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } { const A = Type.Array(Type.Number()) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } // ------------------------------------------------------------------ // Intersections @@ -174,6 +174,9 @@ import { Type, Static } from '@sinclair/typebox' const D = Type.Object({ x: Type.String() }) const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) + + // TUnion<[TIntersect<[TIntersect<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TIntersect<[TLiteral<...>, TNumber]>, TNumber]>]> + // TUnion<[TUnion<[TString, TNumber, TString, TString]>, TUnion<[TLiteral<1>, TNumber, TNumber]>]> type O = Static Expect(R).ToStatic<1>() } @@ -191,6 +194,10 @@ import { Type, Static } from '@sinclair/typebox' const D = Type.Object({ x: Type.String() }) const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) + + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TIntersect<[TString, TIntersect<[]>]>, TIntersect<[]>]>]>, TIntersect<...>]> + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]> + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TString, TString]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]> type O = Static Expect(R).ToStatic() } @@ -207,7 +214,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Object({ x: Type.Literal('C'), y: Type.Number() }) const D = Type.Object({ x: Type.Literal('D') }) const I = Type.Union([A, B, C, D]) - const R = Type.Index(I, Type.Union([Type.Literal('x')])) + const R = Type.Index(I, ['x']) type O = Static Expect(R).ToStatic<'A' | 'B' | 'C' | 'D'>() } @@ -243,7 +250,7 @@ import { Type, Static } from '@sinclair/typebox' } { const T = Type.Object({ - 0: Type.Number(), + '0': Type.Number(), '1': Type.String(), }) const R = Type.Index(T, Type.KeyOf(T)) @@ -257,3 +264,13 @@ import { Type, Static } from '@sinclair/typebox' }) Expect(R).ToStatic<{ x: number | string }>() } +{ + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Expect(I).ToStatic() +} +{ + const T = Type.Array(Type.String()) + const I = Type.Index(T, ['[number]']) + Expect(I).ToStatic() +} diff --git a/test/static/mapped.ts b/test/static/mapped.ts new file mode 100644 index 000000000..9f29b5716 --- /dev/null +++ b/test/static/mapped.ts @@ -0,0 +1,404 @@ +import { Expect } from './assert' +import { Static, Type } from '@sinclair/typebox' + +// prettier-ignore +{ // Generative + const A = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), K => Type.Number()) + Expect(A).ToStatic<{ + x: number, + y: number, + z: number + }> + const B = Type.Mapped(Type.TemplateLiteral('${0|1}${0|1}'), K => Type.Number()) + Expect(B).ToStatic<{ + '00': number, + '01': number, + '10': number, + '11': number, + }> +} +// prettier-ignore +{ // Generative Nested +const T = Type.Mapped(Type.TemplateLiteral('${a|b}'), X => + Type.Mapped(Type.TemplateLiteral('${c|d}'), Y => + Type.Mapped(Type.TemplateLiteral('${e|f}'), Z => + Type.Tuple([X, Y, Z]) + ) + ) +) +type E = { + [X in `${'a' | 'b'}`]: { + [Y in `${'c' | 'd'}`]: { + [Z in `${'e' | 'f'}`]: [X, Y, Z] + } + } +} +Expect(T).ToStatic // ok +} +// prettier-ignore +{ // Identity + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + + const A = Type.Mapped(Type.KeyOf(T), K => K) + Expect(A).ToStatic<{ + x: 'x', + y: 'y', + z: 'z' + }>() + + const B = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Expect(B).ToStatic<{ + x: number, + y: string, + z: boolean + }>() +} +// prettier-ignore +{ // Extract + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const A = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.String()) + }) + Expect(A).ToStatic<{ + x: string + }> + const B = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Expect(B).ToStatic<{ + x: string | number + }> + const C = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Null()) + }) + Expect(C).ToStatic<{ + x: never + }> +} +// prettier-ignore +{ // Numeric Keys + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const A = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Expect(A).ToStatic<{ + 0: number, + 1: number, + 2: number + }> +} +// prettier-ignore +{ // Extends + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const A = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(K, Type.Literal('x'), Type.Literal(1), + Type.Extends(K, Type.Literal('y'), Type.Literal(2), + Type.Extends(K, Type.Literal('z'), Type.Literal(3), Type.Never()))) + ) + }) + Expect(A).ToStatic<{ + x: 1, + y: 2, + z: 3 + }> + const B = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(Type.Index(T, K), Type.Number(), Type.Literal(3), + Type.Extends(Type.Index(T, K), Type.String(), Type.Literal(2), + Type.Extends(Type.Index(T, K), Type.Boolean(), Type.Literal(1), Type.Never()))) + ) + }) + Expect(B).ToStatic<{ + x: 3, + y: 2, + z: 1 + }> +} +// prettier-ignore +{ // Exclude + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const A = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.String()) + }) + Expect(A).ToStatic<{ + x: number | boolean + }> + const B = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Expect(B).ToStatic<{ + x: boolean + }> + const C = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Null()) + }) + Expect(C).ToStatic<{ + x: string | number | boolean + }> +} +// prettier-ignore +{ // Non-Evaluated Indexed + const T = Type.Object({ + x: Type.Number() + }) + const A = Type.Mapped(Type.KeyOf(T), K => Type.Array(Type.Index(T, K))) + Expect(A).ToStatic<{ x: number[] }> + + const B = Type.Mapped(Type.KeyOf(T), K => Type.Promise(Type.Index(T, K))) + Expect(B).ToStatic<{ x: Promise }> + + const C = Type.Mapped(Type.KeyOf(T), K => Type.Function([Type.Index(T, K)], Type.Index(T, K))) + Expect(C).ToStatic<{ x: (x: number) => number }> + + const D = Type.Mapped(Type.KeyOf(T), K => Type.Tuple([Type.Index(T, K), Type.Index(T, K)])) + Expect(D).ToStatic<{ x: [number, number] }> + + const E = Type.Mapped(Type.KeyOf(T), K => Type.Union([Type.Index(T, K)])) + Expect(E).ToStatic<{ x: number }> + + const F = Type.Mapped(Type.KeyOf(T), K => Type.Intersect([Type.Index(T, K)])) + Expect(F).ToStatic<{ x: number }> +} +// prettier-ignore +{ // Modifiers + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number() + }) + // Additive + const A = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), true)) + Expect(A).ToStatic<{ x?: number, y?: number}>() + // Subtractive + const S = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), false)) + Expect(S).ToStatic<{ x: number, y: number}>() +} +// prettier-ignore +{ // Modifiers + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Number() + }) + // Additive + const A = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), true)) + Expect(A).ToStatic<{ readonly x: number, readonly y: number}>() + // Subtractive + const S = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), false)) + Expect(S).ToStatic<{ x: number, y: number}>() +} +// ------------------------------------------------------------------ +// Finite Boolean +// ------------------------------------------------------------------ +{ + const T = Type.TemplateLiteral('${boolean}') + const M = Type.Mapped(T, (K) => K) + Expect(M).ToStatic<{ + true: 'true' + false: 'false' + }> +} +{ + const T = Type.TemplateLiteral('${0|1}${boolean}') + const M = Type.Mapped(T, (K) => K) + Expect(M).ToStatic<{ + '0true': '0true' + '0false': '0false' + '1true': '1true' + '1false': '1false' + }> +} +{ + const T = Type.TemplateLiteral('${boolean}${0|1}') + const M = Type.Mapped(T, (K) => K) + Expect(M).ToStatic<{ + true0: 'true0' + false0: 'false0' + true1: 'true1' + false1: 'false1' + }> +} +{ + const T = Type.TemplateLiteral([Type.Union([Type.Literal(0), Type.Literal(1)]), Type.Union([Type.Literal(0), Type.Literal(1)])]) + const M = Type.Mapped(T, (K) => K) + Expect(M).ToStatic<{ + '00': '00' + '01': '01' + '10': '10' + '11': '11' + }> +} +{ + const T = Type.Object({ + hello: Type.Number(), + world: Type.String(), + }) + const M = Type.Mapped(Type.Uppercase(Type.KeyOf(T)), (K) => { + return Type.Index(T, Type.Lowercase(K)) + }) + Expect(M).ToStatic<{ + HELLO: number + WORLD: string + }> +} +// ------------------------------------------------------------------ +// Interior Partial +// ------------------------------------------------------------------ +{ + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Partial(Type.Index(T, K)) + }) + Expect(M).ToStatic<{ + x: { x?: number; y?: number } + y: { x?: number; y?: number } + }> +} +// ------------------------------------------------------------------ +// Interior Required +// ------------------------------------------------------------------ +{ + const T = Type.Object({ + x: Type.Partial( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + y: Type.Partial( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Required(Type.Index(T, K)) + }) + Expect(M).ToStatic<{ + x: { x: number; y: number } + y: { x: number; y: number } + }> +} +// ------------------------------------------------------------------ +// Pick With Key +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Pick(T, K) + }) + Expect(M).ToStatic<{ + x: { x: { x: number; y: number; }; }; + y: { y: { x: number; y: number; }; }; + }> +} +// ------------------------------------------------------------------ +// Pick With Result +// ------------------------------------------------------------------ +{ + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Pick(Type.Index(T, K), ['x']) + }) + Expect(M).ToStatic<{ + x: { x: number } + y: { x: number } + }> +} +// ------------------------------------------------------------------ +// Omit With Key +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Omit(T, K) + }) + Expect(M).ToStatic<{ + x: { y: { x: number; y: number; }; }; + y: { x: { x: number; y: number; }; }; + }> +} +// ------------------------------------------------------------------ +// Omit With Result +// ------------------------------------------------------------------ +{ + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Omit(Type.Index(T, K), ['x']) + }) + Expect(M).ToStatic<{ + x: { y: number } + y: { y: number } + }> +} diff --git a/test/static/optional.ts b/test/static/optional.ts index b3342692a..619930dc5 100644 --- a/test/static/optional.ts +++ b/test/static/optional.ts @@ -5,10 +5,45 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Object({ A: Type.Optional(Type.String()), }) + type T = Static + + Expect(T).ToStatic<{ + A?: string + }>() +} +// Noop +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Optional(Type.String(), false), + }) + type T = Static + Expect(T).ToStatic<{ + A: string + }>() +} +// Additive +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Optional(Type.String(), true), + }) type T = Static Expect(T).ToStatic<{ A?: string }>() } +// Subtractive +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Optional(Type.Optional(Type.String()), false) + }) + type T = Static + + Expect(T).ToStatic<{ + A: string + }>() +} diff --git a/test/static/readonly.ts b/test/static/readonly.ts index 4d1668346..1d6224fc5 100644 --- a/test/static/readonly.ts +++ b/test/static/readonly.ts @@ -12,3 +12,39 @@ import { Type, Static } from '@sinclair/typebox' readonly A: string }>() } +// Noop +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Readonly(Type.String(), false), + }) + type T = Static + + Expect(T).ToStatic<{ + A: string + }>() +} +// Additive +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Readonly(Type.String(), true), + }) + type T = Static + + Expect(T).ToStatic<{ + readonly A: string + }>() +} +// Subtractive +// prettier-ignore +{ + const T = Type.Object({ + A: Type.Readonly(Type.Readonly(Type.String()), false) + }) + type T = Static + + Expect(T).ToStatic<{ + A: string + }>() +} diff --git a/test/static/record.ts b/test/static/record.ts index e21356595..e284ce053 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -20,7 +20,6 @@ import { Type, Static } from '@sinclair/typebox' const K = Type.Number() const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToStatic>() Expect(T).ToStatic>() } { @@ -174,5 +173,5 @@ import { Type, Static } from '@sinclair/typebox' // expect T to support named properties enum E {} const T = Type.Record(Type.Enum(E), Type.Number()) - Expect(T).ToStatic<{}> + Expect(T).ToStatic<{ [x: string]: number }> } diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts index f0ec8444e..670b0b694 100644 --- a/test/static/template-literal.ts +++ b/test/static/template-literal.ts @@ -27,6 +27,13 @@ import { Type } from '@sinclair/typebox' const T = Type.TemplateLiteral([Type.Literal('hello'), A]) Expect(T).ToStatic<'hello1' | 'hello2'>() } +{ + // TemplateLiteral Composition + const A = Type.TemplateLiteral('${A|B}') + const B = Type.TemplateLiteral('${C|D}') + const T = Type.TemplateLiteral([A, B]) + Expect(T).ToStatic<'AC' | 'AD' | 'BC' | 'BD'>() +} { // String const T = Type.TemplateLiteral([Type.String()]) diff --git a/test/static/transform.ts b/test/static/transform.ts index 917628a67..1f4176289 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -215,12 +215,12 @@ import { Expect } from './assert' // should decode within generic function context // https://github.com/sinclairzx81/typebox/issues/554 // prettier-ignore - const ArrayOrSingle = (schema: T) => - Type.Transform(Type.Union([schema, Type.Array(schema)])) - .Decode((value) => (Array.isArray(value) ? value : [value])) - .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); - const T = ArrayOrSingle(Type.String()) - Expect(T).ToStaticDecode() + // const ArrayOrSingle = (schema: T) => + // Type.Transform(Type.Union([schema, Type.Array(schema)])[0]) + // .Decode((value) => (Array.isArray(value) ? value : [value])) + // .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); + // const T = ArrayOrSingle(Type.String()) + // Expect(T).ToStaticDecode() } { // should correctly decode record keys diff --git a/test/static/union.ts b/test/static/union.ts index 9c9895ee0..3decc9e08 100644 --- a/test/static/union.ts +++ b/test/static/union.ts @@ -71,3 +71,36 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Union([]) Expect(T).ToStaticNever() } +// prettier-ignore +{ // Scalable Union + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const Z = Type.Object({ z: Type.Number() }) + const W = Type.Object({ w: Type.Number() }) + + const T = Type.Union([ + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + X, Y, Z, W, X, Y, Z, W, + ]) + Expect(T).ToStatic< + { x: number } | + { y: number } | + { z: number } | + { w: number } + >() +} diff --git a/tsconfig.json b/tsconfig.json index ec148af39..6a47a31ec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,63 +1,17 @@ { "compilerOptions": { "strict": true, - "target": "ES2020", - "module": "CommonJS", - "moduleResolution": "node", - "declaration": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", "baseUrl": ".", "paths": { - "@sinclair/typebox/compiler": [ - "src/compiler/index.ts" - ], - "@sinclair/typebox/errors": [ - "src/errors/index.ts" - ], - "@sinclair/typebox/system": [ - "src/system/index.ts" - ], - "@sinclair/typebox/value/cast": [ - "src/value/cast.ts" - ], - "@sinclair/typebox/value/check": [ - "src/value/check.ts" - ], - "@sinclair/typebox/value/clone": [ - "src/value/clone.ts" - ], - "@sinclair/typebox/value/convert": [ - "src/value/convert.ts" - ], - "@sinclair/typebox/value/create": [ - "src/value/create.ts" - ], - "@sinclair/typebox/value/delta": [ - "src/value/delta.ts" - ], - "@sinclair/typebox/value/equal": [ - "src/value/equal.ts" - ], - "@sinclair/typebox/value/guard": [ - "src/value/guard.ts" - ], - "@sinclair/typebox/value/hash": [ - "src/value/hash.ts" - ], - "@sinclair/typebox/value/mutate": [ - "src/value/mutate.ts" - ], - "@sinclair/typebox/value/pointer": [ - "src/value/pointer.ts" - ], - "@sinclair/typebox/value/transform": [ - "src/value/transform/index.ts" - ], - "@sinclair/typebox/value": [ - "src/value/index.ts" - ], - "@sinclair/typebox": [ - "src/typebox.ts" - ], + "@sinclair/typebox/compiler": ["src/compiler/index.ts"], + "@sinclair/typebox/errors": ["src/errors/index.ts"], + "@sinclair/typebox/system": ["src/system/index.ts"], + "@sinclair/typebox/type": ["src/type/index.ts"], + "@sinclair/typebox/value": ["src/value/index.ts"], + "@sinclair/typebox": ["src/index.ts"], } } } \ No newline at end of file From ca4d771b87ee1f8e953036c95a21da7150786d3e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 23 Dec 2023 13:42:12 +0900 Subject: [PATCH 217/369] Revision 0.32.1 (#701) * Ensure Package Specifies Default Import as Last Key * TypeScript 5.3.3 --- package-lock.json | 39 +++++++++++++++++----- package.json | 4 +-- task/build/redirect/create-package-json.ts | 14 ++++---- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ce4d3fae..f5cea1b24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.0", + "version": "0.32.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.0", + "version": "0.32.1", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.3.2" + "typescript": "^5.3.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -63,6 +63,19 @@ "node": ">=18" } }, + "node_modules/@arethetypeswrong/core/node_modules/typescript": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -1631,9 +1644,9 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1826,6 +1839,14 @@ "semver": "^7.5.4", "typescript": "5.3.2", "validate-npm-package-name": "^5.0.0" + }, + "dependencies": { + "typescript": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "dev": true + } } }, "@colors/colors": { @@ -2853,9 +2874,9 @@ "dev": true }, "typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 342d3bc79..2f9b49118 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.0", + "version": "0.32.1", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.3.2" + "typescript": "^5.3.3" } } diff --git a/task/build/redirect/create-package-json.ts b/task/build/redirect/create-package-json.ts index 5b36ce927..068041c5a 100644 --- a/task/build/redirect/create-package-json.ts +++ b/task/build/redirect/create-package-json.ts @@ -48,12 +48,13 @@ function resolvePackageJson(submodules: string[]) { function resolveSubmoduleExports(submodule: string) { return { require: { + types: `./build/require/${submodule}/index.d.ts`, default: `./build/require/${submodule}/index.js`, - types: `./build/require/${submodule}/index.d.ts` }, import: { - default: `./build/import/${submodule}/index.mjs`, types: `./build/import/${submodule}/index.d.mts`, + default: `./build/import/${submodule}/index.mjs`, + } } } @@ -65,12 +66,13 @@ function resolveExports(submodules: string[]) { // ... and root module ".": { "require": { + "types": "./build/require/index.d.ts", "default": "./build/require/index.js", - "types": "./build/require/index.d.ts" + }, "import": { + "types": "./build/import/index.d.mts", "default": "./build/import/index.mjs", - "types": "./build/import/index.d.mts" } } }) @@ -89,8 +91,8 @@ function resolveMetadata() { license: packageJson.license, repository: packageJson.repository, scripts: { test: 'echo test' }, // flagged by socket.dev - module: "./build/import/index.mjs", types: "./build/require/index.d.ts", - main: "./build/require/index.js" + main: "./build/require/index.js", + module: "./build/import/index.mjs", } } From d0c9840157896888119a85cd7b87a4838a26f3b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Strgar?= Date: Sun, 24 Dec 2023 13:06:59 +0100 Subject: [PATCH 218/369] Export TLiteralValue (#702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Žiga Strgar --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 6c243906b..fbf63ae51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,7 +63,7 @@ export { Intersect, IntersectEvaluated, type TIntersect, type TIntersectEvaluate export { Iterator, type TIterator } from './type/iterator/index' export { Intrinsic, IntrinsicFromMappedKey, type TIntrinsic, Capitalize, type TCapitalize, Lowercase, type TLowercase, Uncapitalize, type TUncapitalize, Uppercase, type TUppercase } from './type/intrinsic/index' export { KeyOf, type TKeyOf, type KeyOfFromMappedResult, KeyOfPropertyKeys, KeyOfPattern } from './type/keyof/index' -export { Literal, type TLiteral } from './type/literal/index' +export { Literal, type TLiteral, type TLiteralValue } from './type/literal/index' export { Mapped, MappedKey, MappedResult, type TMapped, type TMappedKey, type TMappedResult, type TMappedFunction } from './type/mapped/index' export { Never, type TNever } from './type/never/index' export { Not, type TNot } from './type/not/index' From 64c1e16d924d448a535693869fcc313bb96e3713 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 24 Dec 2023 21:14:02 +0900 Subject: [PATCH 219/369] Revision 0.32.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5cea1b24..4c2d79a0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.1", + "version": "0.32.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.1", + "version": "0.32.2", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 2f9b49118..73e35ab0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.1", + "version": "0.32.2", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From 483040c3bfd69041715cce066ed015c4b1efa45d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 25 Dec 2023 00:09:12 +0900 Subject: [PATCH 220/369] Revision 0.32.3 (#703) * Evaluate Inferred Record Type * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/record/record.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4c2d79a0a..1b0be7a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.2", + "version": "0.32.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.2", + "version": "0.32.3", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 73e35ab0d..5c4cd123d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.2", + "version": "0.32.3", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/record/record.ts b/src/type/record/record.ts index ec724bd30..042d4b5d6 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -181,7 +181,7 @@ function FromNumberKey(_: K, T: T, options // ------------------------------------------------------------------ // prettier-ignore type RecordStatic = ( - Record, PropertyKey>, Static> + Evaluate, PropertyKey>, Static>> ) // prettier-ignore export interface TRecord extends TSchema { From 0a988cdb6c6422b78a05c5cca1dd1315faa35110 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 29 Dec 2023 12:39:52 +0900 Subject: [PATCH 221/369] Revision 0.32.4 (#708) * Export ErrorFunctionParameter * Revision --- package-lock.json | 4 ++-- package.json | 2 +- src/errors/index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b0be7a86..4c2b4a5e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.3", + "version": "0.32.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.3", + "version": "0.32.4", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 5c4cd123d..6f84b9e31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.3", + "version": "0.32.4", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/index.ts b/src/errors/index.ts index b5a10ef10..2c0c08936 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -27,4 +27,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export { Errors, ValueError, ValueErrorIterator, ValueErrorType, ValueErrorsUnknownTypeError } from './errors' -export { DefaultErrorFunction, GetErrorFunction, SetErrorFunction, type ErrorFunction } from './function' +export { DefaultErrorFunction, GetErrorFunction, SetErrorFunction, type ErrorFunction, type ErrorFunctionParameter } from './function' From 01ea89e324eb6d100a237cfeafdf2b2d37d709e3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 5 Jan 2024 12:34:07 +0900 Subject: [PATCH 222/369] Resolve Overload for Value Clean (#717) --- src/value/clean/clean.ts | 4 ++-- src/value/convert/convert.ts | 4 ++-- src/value/default/default.ts | 4 ++-- src/value/value/value.ts | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 29c52d32d..0d0f20fa7 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -175,9 +175,9 @@ function Visit(schema: TSchema, references: TSchema[], value: unknown): unknown // Clean // ------------------------------------------------------------------ /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: T, references: TSchema[], value: unknown): unknown +export function Clean(schema: TSchema, references: TSchema[], value: unknown): unknown /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: T): unknown +export function Clean(schema: TSchema, value: unknown): unknown /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ export function Clean(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 8a0de37b2..4360e4fbb 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -299,9 +299,9 @@ function Visit(schema: TSchema, references: TSchema[], value: any): unknown { // Convert // ------------------------------------------------------------------ /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, references: TSchema[], value: unknown): unknown +export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, value: unknown): unknown +export function Convert(schema: TSchema, value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ // prettier-ignore export function Convert(...args: any[]) { diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 6a310d259..a089f7600 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -176,9 +176,9 @@ function Visit(schema: TSchema, references: TSchema[], value: unknown): any { // Default // ------------------------------------------------------------------ /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: T, references: TSchema[], value: unknown): unknown +export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: T, value: unknown): unknown +export function Default(schema: TSchema, value: unknown): unknown /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ export function Default(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 6be317787..62a0f04d1 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -68,17 +68,17 @@ export function Check(...args: any[]) { return CheckValue.apply(CheckValue, args as any) } /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: T, references: TSchema[], value: unknown): unknown +export function Clean(schema: TSchema, references: TSchema[], value: unknown): unknown /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: T, value: unknown): unknown +export function Clean(schema: TSchema, value: unknown): unknown /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ export function Clean(...args: any[]) { return CleanValue.apply(CleanValue, args as any) } /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ -export function Convert(schema: T, references: TSchema[], value: unknown): unknown +export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ -export function Convert(schema: T, value: unknown): unknown +export function Convert(schema: TSchema, value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ export function Convert(...args: any[]) { return ConvertValue.apply(ConvertValue, args as any) @@ -98,9 +98,9 @@ export function Decode(...args: any[]) { return TransformDecode(schema, references, value) } /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: T, references: TSchema[], value: unknown): unknown +export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: T, value: unknown): unknown +export function Default(schema: TSchema, value: unknown): unknown /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ export function Default(...args: any[]) { return DefaultValue.apply(DefaultValue, args as any) From 767ff8726ef87eb6340565934e4dec365e1f8003 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 5 Jan 2024 12:47:20 +0900 Subject: [PATCH 223/369] Revision 0.32.5 (#718) --- example/annotation/annotation.ts | 2 +- example/collections/array.ts | 2 +- example/collections/index.ts | 2 +- example/collections/map.ts | 2 +- example/collections/set.ts | 2 +- example/formats/index.ts | 2 +- example/prototypes/evaluate.ts | 2 +- example/prototypes/index.ts | 2 +- example/prototypes/partial-deep.ts | 2 +- example/prototypes/union-enum.ts | 2 +- example/prototypes/union-oneof.ts | 2 +- example/typedef/index.ts | 2 +- example/typedef/typedef.ts | 2 +- license | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 2 +- src/compiler/index.ts | 2 +- src/errors/errors.ts | 2 +- src/errors/function.ts | 2 +- src/errors/index.ts | 2 +- src/index.ts | 2 +- src/system/index.ts | 2 +- src/system/policy.ts | 2 +- src/system/system.ts | 2 +- src/type/any/any.ts | 2 +- src/type/any/index.ts | 2 +- src/type/array/array.ts | 2 +- src/type/array/index.ts | 2 +- src/type/async-iterator/async-iterator.ts | 2 +- src/type/async-iterator/index.ts | 2 +- src/type/awaited/awaited.ts | 2 +- src/type/awaited/index.ts | 2 +- src/type/bigint/bigint.ts | 2 +- src/type/bigint/index.ts | 2 +- src/type/boolean/boolean.ts | 2 +- src/type/boolean/index.ts | 2 +- src/type/clone/index.ts | 2 +- src/type/clone/type.ts | 2 +- src/type/clone/value.ts | 2 +- src/type/composite/composite.ts | 2 +- src/type/composite/index.ts | 2 +- src/type/const/const.ts | 2 +- src/type/const/index.ts | 2 +- src/type/constructor-parameters/constructor-parameters.ts | 2 +- src/type/constructor-parameters/index.ts | 2 +- src/type/constructor/constructor.ts | 2 +- src/type/constructor/index.ts | 2 +- src/type/date/date.ts | 2 +- src/type/date/index.ts | 2 +- src/type/deref/deref.ts | 2 +- src/type/deref/index.ts | 2 +- src/type/discard/discard.ts | 2 +- src/type/discard/index.ts | 2 +- src/type/enum/enum.ts | 2 +- src/type/enum/index.ts | 2 +- src/type/error/error.ts | 2 +- src/type/error/index.ts | 2 +- src/type/exclude/exclude-from-mapped-result.ts | 2 +- src/type/exclude/exclude.ts | 2 +- src/type/exclude/index.ts | 2 +- src/type/extends/extends-check.ts | 2 +- src/type/extends/extends-from-mapped-key.ts | 2 +- src/type/extends/extends-from-mapped-result.ts | 2 +- src/type/extends/extends-undefined.ts | 2 +- src/type/extends/extends.ts | 2 +- src/type/extends/index.ts | 2 +- src/type/extract/extract-from-mapped-result.ts | 2 +- src/type/extract/extract.ts | 2 +- src/type/extract/index.ts | 2 +- src/type/function/function.ts | 2 +- src/type/function/index.ts | 2 +- src/type/guard/index.ts | 2 +- src/type/guard/type.ts | 2 +- src/type/guard/value.ts | 2 +- src/type/helpers/helpers.ts | 2 +- src/type/helpers/index.ts | 2 +- src/type/index.ts | 2 +- src/type/indexed/index.ts | 2 +- src/type/indexed/indexed-from-mapped-key.ts | 2 +- src/type/indexed/indexed-from-mapped-result.ts | 2 +- src/type/indexed/indexed-property-keys.ts | 2 +- src/type/indexed/indexed.ts | 2 +- src/type/instance-type/index.ts | 2 +- src/type/instance-type/instance-type.ts | 2 +- src/type/integer/index.ts | 2 +- src/type/integer/integer.ts | 2 +- src/type/intersect/index.ts | 2 +- src/type/intersect/intersect-create.ts | 2 +- src/type/intersect/intersect-evaluated.ts | 2 +- src/type/intersect/intersect-type.ts | 2 +- src/type/intersect/intersect.ts | 2 +- src/type/intrinsic/capitalize.ts | 2 +- src/type/intrinsic/index.ts | 2 +- src/type/intrinsic/intrinsic-from-mapped-key.ts | 2 +- src/type/intrinsic/intrinsic.ts | 2 +- src/type/intrinsic/lowercase.ts | 2 +- src/type/intrinsic/uncapitalize.ts | 2 +- src/type/intrinsic/uppercase.ts | 2 +- src/type/iterator/index.ts | 2 +- src/type/iterator/iterator.ts | 2 +- src/type/keyof/index.ts | 2 +- src/type/keyof/keyof-from-mapped-result.ts | 2 +- src/type/keyof/keyof-property-keys.ts | 2 +- src/type/keyof/keyof.ts | 2 +- src/type/literal/index.ts | 2 +- src/type/literal/literal.ts | 2 +- src/type/mapped/index.ts | 2 +- src/type/mapped/mapped-key.ts | 2 +- src/type/mapped/mapped-result.ts | 2 +- src/type/mapped/mapped.ts | 2 +- src/type/never/index.ts | 2 +- src/type/never/never.ts | 2 +- src/type/not/index.ts | 2 +- src/type/not/not.ts | 2 +- src/type/null/index.ts | 2 +- src/type/null/null.ts | 2 +- src/type/number/index.ts | 2 +- src/type/number/number.ts | 2 +- src/type/object/index.ts | 2 +- src/type/object/object.ts | 2 +- src/type/omit/index.ts | 2 +- src/type/omit/omit-from-mapped-key.ts | 2 +- src/type/omit/omit-from-mapped-result.ts | 2 +- src/type/omit/omit.ts | 2 +- src/type/optional/index.ts | 2 +- src/type/optional/optional-from-mapped-result.ts | 2 +- src/type/optional/optional.ts | 2 +- src/type/parameters/index.ts | 2 +- src/type/parameters/parameters.ts | 2 +- src/type/partial/index.ts | 2 +- src/type/partial/partial-from-mapped-result.ts | 2 +- src/type/partial/partial.ts | 2 +- src/type/patterns/index.ts | 2 +- src/type/patterns/patterns.ts | 2 +- src/type/pick/index.ts | 2 +- src/type/pick/pick-from-mapped-key.ts | 2 +- src/type/pick/pick-from-mapped-result.ts | 2 +- src/type/pick/pick.ts | 2 +- src/type/promise/index.ts | 2 +- src/type/promise/promise.ts | 2 +- src/type/readonly-optional/index.ts | 2 +- src/type/readonly-optional/readonly-optional.ts | 2 +- src/type/readonly/index.ts | 2 +- src/type/readonly/readonly-from-mapped-result.ts | 2 +- src/type/readonly/readonly.ts | 2 +- src/type/record/index.ts | 2 +- src/type/record/record.ts | 2 +- src/type/recursive/index.ts | 2 +- src/type/recursive/recursive.ts | 2 +- src/type/ref/index.ts | 2 +- src/type/ref/ref.ts | 2 +- src/type/regexp/index.ts | 2 +- src/type/regexp/regexp.ts | 2 +- src/type/registry/format.ts | 2 +- src/type/registry/index.ts | 2 +- src/type/registry/type.ts | 2 +- src/type/required/index.ts | 2 +- src/type/required/required-from-mapped-result.ts | 2 +- src/type/required/required.ts | 2 +- src/type/rest/index.ts | 2 +- src/type/rest/rest.ts | 2 +- src/type/return-type/index.ts | 2 +- src/type/return-type/return-type.ts | 2 +- src/type/schema/anyschema.ts | 2 +- src/type/schema/index.ts | 2 +- src/type/schema/schema.ts | 2 +- src/type/sets/index.ts | 2 +- src/type/sets/set.ts | 2 +- src/type/static/index.ts | 2 +- src/type/static/static.ts | 2 +- src/type/strict/index.ts | 2 +- src/type/strict/strict.ts | 2 +- src/type/string/index.ts | 2 +- src/type/string/string.ts | 2 +- src/type/symbol/index.ts | 2 +- src/type/symbol/symbol.ts | 2 +- src/type/symbols/index.ts | 2 +- src/type/symbols/symbols.ts | 2 +- src/type/template-literal/finite.ts | 2 +- src/type/template-literal/generate.ts | 2 +- src/type/template-literal/index.ts | 2 +- src/type/template-literal/parse.ts | 2 +- src/type/template-literal/pattern.ts | 2 +- src/type/template-literal/syntax.ts | 2 +- src/type/template-literal/template-literal.ts | 2 +- src/type/template-literal/union.ts | 2 +- src/type/transform/index.ts | 2 +- src/type/transform/transform.ts | 2 +- src/type/tuple/index.ts | 2 +- src/type/tuple/tuple.ts | 2 +- src/type/type/index.ts | 2 +- src/type/type/javascript.ts | 2 +- src/type/type/json.ts | 2 +- src/type/type/type.ts | 2 +- src/type/uint8array/index.ts | 2 +- src/type/uint8array/uint8array.ts | 2 +- src/type/undefined/index.ts | 2 +- src/type/undefined/undefined.ts | 2 +- src/type/union/index.ts | 2 +- src/type/union/union-create.ts | 2 +- src/type/union/union-evaluated.ts | 2 +- src/type/union/union-type.ts | 2 +- src/type/union/union.ts | 2 +- src/type/unknown/index.ts | 2 +- src/type/unknown/unknown.ts | 2 +- src/type/unsafe/index.ts | 2 +- src/type/unsafe/unsafe.ts | 2 +- src/type/void/index.ts | 2 +- src/type/void/void.ts | 2 +- src/value/cast/cast.ts | 2 +- src/value/cast/index.ts | 2 +- src/value/check/check.ts | 2 +- src/value/check/index.ts | 2 +- src/value/clean/clean.ts | 2 +- src/value/clean/index.ts | 2 +- src/value/clone/clone.ts | 2 +- src/value/clone/index.ts | 2 +- src/value/convert/convert.ts | 2 +- src/value/convert/index.ts | 2 +- src/value/create/create.ts | 2 +- src/value/create/index.ts | 2 +- src/value/default/default.ts | 2 +- src/value/default/index.ts | 2 +- src/value/delta/delta.ts | 2 +- src/value/delta/index.ts | 2 +- src/value/deref/deref.ts | 2 +- src/value/deref/index.ts | 2 +- src/value/equal/equal.ts | 2 +- src/value/equal/index.ts | 2 +- src/value/guard/guard.ts | 2 +- src/value/guard/index.ts | 2 +- src/value/hash/hash.ts | 2 +- src/value/hash/index.ts | 2 +- src/value/index.ts | 2 +- src/value/mutate/index.ts | 2 +- src/value/mutate/mutate.ts | 2 +- src/value/pointer/index.ts | 2 +- src/value/pointer/pointer.ts | 2 +- src/value/transform/decode.ts | 2 +- src/value/transform/encode.ts | 2 +- src/value/transform/has.ts | 2 +- src/value/transform/index.ts | 2 +- src/value/value/index.ts | 2 +- src/value/value/value.ts | 2 +- task/build/common/remove-notices.ts | 2 +- task/build/import/build.ts | 2 +- task/build/import/compile.ts | 2 +- task/build/import/convert-to-esm.ts | 2 +- task/build/index.ts | 2 +- task/build/redirect/build.ts | 2 +- task/build/redirect/create-package-json.ts | 2 +- task/build/redirect/create-redirect-paths.ts | 2 +- task/build/require/build.ts | 2 +- task/build/require/compile.ts | 2 +- 255 files changed, 256 insertions(+), 256 deletions(-) diff --git a/example/annotation/annotation.ts b/example/annotation/annotation.ts index 26bebaf0d..9e38f70b6 100644 --- a/example/annotation/annotation.ts +++ b/example/annotation/annotation.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/array.ts b/example/collections/array.ts index 90f5d73b4..be13f9284 100644 --- a/example/collections/array.ts +++ b/example/collections/array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/index.ts b/example/collections/index.ts index 49183354a..6069c8c37 100644 --- a/example/collections/index.ts +++ b/example/collections/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/map.ts b/example/collections/map.ts index 796140fe6..47957795b 100644 --- a/example/collections/map.ts +++ b/example/collections/map.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/set.ts b/example/collections/set.ts index 8cf312a3e..730583ae3 100644 --- a/example/collections/set.ts +++ b/example/collections/set.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/formats/index.ts b/example/formats/index.ts index 0b20821fe..2ac3f6f51 100644 --- a/example/formats/index.ts +++ b/example/formats/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/evaluate.ts b/example/prototypes/evaluate.ts index 530972fa7..460d86773 100644 --- a/example/prototypes/evaluate.ts +++ b/example/prototypes/evaluate.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index 4a70931c8..c0011265b 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/partial-deep.ts b/example/prototypes/partial-deep.ts index c0896186c..7ee36f445 100644 --- a/example/prototypes/partial-deep.ts +++ b/example/prototypes/partial-deep.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/union-enum.ts b/example/prototypes/union-enum.ts index ee55d202c..91c768c52 100644 --- a/example/prototypes/union-enum.ts +++ b/example/prototypes/union-enum.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/union-oneof.ts b/example/prototypes/union-oneof.ts index 5129d457a..818d3e457 100644 --- a/example/prototypes/union-oneof.ts +++ b/example/prototypes/union-oneof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/typedef/index.ts b/example/typedef/index.ts index 495e365c3..a23b035fe 100644 --- a/example/typedef/index.ts +++ b/example/typedef/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 233ac5c7c..7a86b77d7 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/license b/license index 971ec5da8..7c58af806 100644 --- a/license +++ b/license @@ -4,7 +4,7 @@ Json Schema Type Builder with Static Type Resolution for TypeScript The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package-lock.json b/package-lock.json index 4c2b4a5e4..78a0102ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.4", + "version": "0.32.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.4", + "version": "0.32.5", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 6f84b9e31..930e2f331 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.4", + "version": "0.32.5", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 7f7368c19..e05257958 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 48b4c10e8..b732cb855 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/errors.ts b/src/errors/errors.ts index c84ebab9e..7bd658f3e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/function.ts b/src/errors/function.ts index bba4ff53b..8bb1605dd 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/index.ts b/src/errors/index.ts index 2c0c08936..6fc0d608a 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/index.ts b/src/index.ts index fbf63ae51..779964fef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/index.ts b/src/system/index.ts index fe79f6d08..7ed15c5dc 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/policy.ts b/src/system/policy.ts index 04c4a4ec2..e2d1a08e7 100644 --- a/src/system/policy.ts +++ b/src/system/policy.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/system.ts b/src/system/system.ts index 0c1535b05..2715ee4e6 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/any/any.ts b/src/type/any/any.ts index e462db9f5..6916cef60 100644 --- a/src/type/any/any.ts +++ b/src/type/any/any.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/any/index.ts b/src/type/any/index.ts index 29688e122..fe7f59848 100644 --- a/src/type/any/index.ts +++ b/src/type/any/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/array/array.ts b/src/type/array/array.ts index 1165e6268..09ca326ae 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/array/index.ts b/src/type/array/index.ts index ae02b63c0..d19c2e2be 100644 --- a/src/type/array/index.ts +++ b/src/type/array/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts index 9ee5f8755..5861f9625 100644 --- a/src/type/async-iterator/async-iterator.ts +++ b/src/type/async-iterator/async-iterator.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/async-iterator/index.ts b/src/type/async-iterator/index.ts index d6bc7cb22..9a61d47dc 100644 --- a/src/type/async-iterator/index.ts +++ b/src/type/async-iterator/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index 8eec616c4..7a17df3af 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/awaited/index.ts b/src/type/awaited/index.ts index 26a23a571..186c08da5 100644 --- a/src/type/awaited/index.ts +++ b/src/type/awaited/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts index 53b0fb28d..eb0ca248e 100644 --- a/src/type/bigint/bigint.ts +++ b/src/type/bigint/bigint.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/bigint/index.ts b/src/type/bigint/index.ts index ce8f4fbf9..8d4c4b5d6 100644 --- a/src/type/bigint/index.ts +++ b/src/type/bigint/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts index 7d3fe1aed..68a2c26a5 100644 --- a/src/type/boolean/boolean.ts +++ b/src/type/boolean/boolean.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/boolean/index.ts b/src/type/boolean/index.ts index aad153cb3..4de112e06 100644 --- a/src/type/boolean/index.ts +++ b/src/type/boolean/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/index.ts b/src/type/clone/index.ts index 44bd8cb28..4ac4e6f01 100644 --- a/src/type/clone/index.ts +++ b/src/type/clone/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts index 08d1c3f43..f6c3dd54e 100644 --- a/src/type/clone/type.ts +++ b/src/type/clone/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/value.ts b/src/type/clone/value.ts index 03e880482..548cef442 100644 --- a/src/type/clone/value.ts +++ b/src/type/clone/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index 0bfe80434..ae994017f 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/composite/index.ts b/src/type/composite/index.ts index 2fe294b71..141f3e337 100644 --- a/src/type/composite/index.ts +++ b/src/type/composite/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/const/const.ts b/src/type/const/const.ts index b3ce2ac58..ce13dc352 100644 --- a/src/type/const/const.ts +++ b/src/type/const/const.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/const/index.ts b/src/type/const/index.ts index f13d0f549..90b0ba9ca 100644 --- a/src/type/const/index.ts +++ b/src/type/const/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts index e2cb70e19..9b00791ab 100644 --- a/src/type/constructor-parameters/constructor-parameters.ts +++ b/src/type/constructor-parameters/constructor-parameters.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor-parameters/index.ts b/src/type/constructor-parameters/index.ts index 23f9c265b..dbc9c6c0a 100644 --- a/src/type/constructor-parameters/index.ts +++ b/src/type/constructor-parameters/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index c4e37e8fc..948d0aab6 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor/index.ts b/src/type/constructor/index.ts index 8e75965f7..3269a4e78 100644 --- a/src/type/constructor/index.ts +++ b/src/type/constructor/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/date/date.ts b/src/type/date/date.ts index 9e45ec214..3a7534217 100644 --- a/src/type/date/date.ts +++ b/src/type/date/date.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/date/index.ts b/src/type/date/index.ts index 932ee5def..d73b06d48 100644 --- a/src/type/date/index.ts +++ b/src/type/date/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts index 84fa78421..2be22d90d 100644 --- a/src/type/deref/deref.ts +++ b/src/type/deref/deref.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/deref/index.ts b/src/type/deref/index.ts index 496b4d6f0..28c03fc50 100644 --- a/src/type/deref/index.ts +++ b/src/type/deref/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/discard/discard.ts b/src/type/discard/discard.ts index f531f4d49..9e4b98dc3 100644 --- a/src/type/discard/discard.ts +++ b/src/type/discard/discard.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/discard/index.ts b/src/type/discard/index.ts index 43fb9af56..54c7051b2 100644 --- a/src/type/discard/index.ts +++ b/src/type/discard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts index 4a92aeed2..2406cc027 100644 --- a/src/type/enum/enum.ts +++ b/src/type/enum/enum.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/enum/index.ts b/src/type/enum/index.ts index d5dfd4a6f..a77209132 100644 --- a/src/type/enum/index.ts +++ b/src/type/enum/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/error/error.ts b/src/type/error/error.ts index 652352632..f72140b3d 100644 --- a/src/type/error/error.ts +++ b/src/type/error/error.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/error/index.ts b/src/type/error/index.ts index 277d5e011..4cf885dad 100644 --- a/src/type/error/index.ts +++ b/src/type/error/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/exclude-from-mapped-result.ts b/src/type/exclude/exclude-from-mapped-result.ts index 858bbfd9b..a8934686f 100644 --- a/src/type/exclude/exclude-from-mapped-result.ts +++ b/src/type/exclude/exclude-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index 751e9e154..e69ef1b9a 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/index.ts b/src/type/exclude/index.ts index b83e080b5..335e87a04 100644 --- a/src/type/exclude/index.ts +++ b/src/type/exclude/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-check.ts b/src/type/extends/extends-check.ts index d726d91ea..dc461eff4 100644 --- a/src/type/extends/extends-check.ts +++ b/src/type/extends/extends-check.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-from-mapped-key.ts b/src/type/extends/extends-from-mapped-key.ts index acc955ee5..7e3620ff2 100644 --- a/src/type/extends/extends-from-mapped-key.ts +++ b/src/type/extends/extends-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-from-mapped-result.ts b/src/type/extends/extends-from-mapped-result.ts index d34c5dc80..c202aa356 100644 --- a/src/type/extends/extends-from-mapped-result.ts +++ b/src/type/extends/extends-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-undefined.ts b/src/type/extends/extends-undefined.ts index f6c275c6a..23c51776d 100644 --- a/src/type/extends/extends-undefined.ts +++ b/src/type/extends/extends-undefined.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts index e91a9d629..53049c4d5 100644 --- a/src/type/extends/extends.ts +++ b/src/type/extends/extends.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/index.ts b/src/type/extends/index.ts index 330381d55..aa0bc3d6c 100644 --- a/src/type/extends/index.ts +++ b/src/type/extends/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/extract-from-mapped-result.ts b/src/type/extract/extract-from-mapped-result.ts index 96191929c..c21ed3567 100644 --- a/src/type/extract/extract-from-mapped-result.ts +++ b/src/type/extract/extract-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index 2ef499831..7e6dc05f9 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/index.ts b/src/type/extract/index.ts index 3fd310772..baa79009e 100644 --- a/src/type/extract/index.ts +++ b/src/type/extract/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/function/function.ts b/src/type/function/function.ts index 7c97429a4..6f594dfb3 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/function/index.ts b/src/type/function/index.ts index 3399c7270..21b4ad556 100644 --- a/src/type/function/index.ts +++ b/src/type/function/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/index.ts b/src/type/guard/index.ts index 2e7cc05ad..a8f31501a 100644 --- a/src/type/guard/index.ts +++ b/src/type/guard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index 9794a4fcb..98eada8ce 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts index e09fb9bbc..c3ecc85fe 100644 --- a/src/type/guard/value.ts +++ b/src/type/guard/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts index 84263c6d5..c241ef343 100644 --- a/src/type/helpers/helpers.ts +++ b/src/type/helpers/helpers.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/helpers/index.ts b/src/type/helpers/index.ts index 4d6559c8a..8e6b80464 100644 --- a/src/type/helpers/index.ts +++ b/src/type/helpers/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/index.ts b/src/type/index.ts index d47dbf308..b69a1fa72 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/index.ts b/src/type/indexed/index.ts index bdd973af4..ed02a2814 100644 --- a/src/type/indexed/index.ts +++ b/src/type/indexed/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index 5b1567c49..76dc90e70 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index f685717ca..ce4745886 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts index 58f689811..48a45cd92 100644 --- a/src/type/indexed/indexed-property-keys.ts +++ b/src/type/indexed/indexed-property-keys.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index bea542a69..7b6e4dcf3 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/instance-type/index.ts b/src/type/instance-type/index.ts index aa85b717a..a16ba2e50 100644 --- a/src/type/instance-type/index.ts +++ b/src/type/instance-type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts index f26503419..358ff7040 100644 --- a/src/type/instance-type/instance-type.ts +++ b/src/type/instance-type/instance-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/integer/index.ts b/src/type/integer/index.ts index 73513ebc8..82c3e2cce 100644 --- a/src/type/integer/index.ts +++ b/src/type/integer/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts index 23ed802af..e74ec75ea 100644 --- a/src/type/integer/integer.ts +++ b/src/type/integer/integer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/index.ts b/src/type/intersect/index.ts index c2f3d3ba4..e9ec6eaea 100644 --- a/src/type/intersect/index.ts +++ b/src/type/intersect/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-create.ts b/src/type/intersect/intersect-create.ts index 815c27e9a..eb179e770 100644 --- a/src/type/intersect/intersect-create.ts +++ b/src/type/intersect/intersect-create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts index 5f6f25ba4..2647e1b0d 100644 --- a/src/type/intersect/intersect-evaluated.ts +++ b/src/type/intersect/intersect-evaluated.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-type.ts b/src/type/intersect/intersect-type.ts index 991c3674e..1b1f6a5de 100644 --- a/src/type/intersect/intersect-type.ts +++ b/src/type/intersect/intersect-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts index b06c1dd80..e677da624 100644 --- a/src/type/intersect/intersect.ts +++ b/src/type/intersect/intersect.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/capitalize.ts b/src/type/intrinsic/capitalize.ts index 0bb053da8..48acdc23d 100644 --- a/src/type/intrinsic/capitalize.ts +++ b/src/type/intrinsic/capitalize.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/index.ts b/src/type/intrinsic/index.ts index d5c9aedc3..8a67cea42 100644 --- a/src/type/intrinsic/index.ts +++ b/src/type/intrinsic/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/intrinsic-from-mapped-key.ts b/src/type/intrinsic/intrinsic-from-mapped-key.ts index b1c1b5f3d..cd370ac1d 100644 --- a/src/type/intrinsic/intrinsic-from-mapped-key.ts +++ b/src/type/intrinsic/intrinsic-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index 5080aa60f..5d4442494 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/lowercase.ts b/src/type/intrinsic/lowercase.ts index 835d39b4f..745c4ff22 100644 --- a/src/type/intrinsic/lowercase.ts +++ b/src/type/intrinsic/lowercase.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/uncapitalize.ts b/src/type/intrinsic/uncapitalize.ts index d69502253..3024f9672 100644 --- a/src/type/intrinsic/uncapitalize.ts +++ b/src/type/intrinsic/uncapitalize.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/uppercase.ts b/src/type/intrinsic/uppercase.ts index 52ee4fa67..bde519cc6 100644 --- a/src/type/intrinsic/uppercase.ts +++ b/src/type/intrinsic/uppercase.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/iterator/index.ts b/src/type/iterator/index.ts index 8c41f54e3..1a6c6fd01 100644 --- a/src/type/iterator/index.ts +++ b/src/type/iterator/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts index 74a37a236..e89816fa7 100644 --- a/src/type/iterator/iterator.ts +++ b/src/type/iterator/iterator.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index 03a32c098..ecaa5e169 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 97709295c..8e10c66aa 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 63cb1a49e..34a16956a 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 1c6192136..5fcd4e51f 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/literal/index.ts b/src/type/literal/index.ts index d4cb53ef2..a3102cc94 100644 --- a/src/type/literal/index.ts +++ b/src/type/literal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts index 8754ffd7c..fe3c7b64b 100644 --- a/src/type/literal/literal.ts +++ b/src/type/literal/literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/index.ts b/src/type/mapped/index.ts index 5e1f19bb1..9999cfd84 100644 --- a/src/type/mapped/index.ts +++ b/src/type/mapped/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped-key.ts b/src/type/mapped/mapped-key.ts index 24ad79526..d7eba3634 100644 --- a/src/type/mapped/mapped-key.ts +++ b/src/type/mapped/mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped-result.ts b/src/type/mapped/mapped-result.ts index 84908ea3b..d2aade75c 100644 --- a/src/type/mapped/mapped-result.ts +++ b/src/type/mapped/mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 49ab254e7..0c6588b8b 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/never/index.ts b/src/type/never/index.ts index 913504405..d8937194f 100644 --- a/src/type/never/index.ts +++ b/src/type/never/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/never/never.ts b/src/type/never/never.ts index 7fc22f7fe..d3113d216 100644 --- a/src/type/never/never.ts +++ b/src/type/never/never.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/not/index.ts b/src/type/not/index.ts index 49cf9dca8..34f540ae0 100644 --- a/src/type/not/index.ts +++ b/src/type/not/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/not/not.ts b/src/type/not/not.ts index df0a93a74..d01ac2b3a 100644 --- a/src/type/not/not.ts +++ b/src/type/not/not.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/null/index.ts b/src/type/null/index.ts index 281bef4f4..fe04d45b7 100644 --- a/src/type/null/index.ts +++ b/src/type/null/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/null/null.ts b/src/type/null/null.ts index 6dad0c7d8..09142d299 100644 --- a/src/type/null/null.ts +++ b/src/type/null/null.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/number/index.ts b/src/type/number/index.ts index c43f623c7..2cb1382f0 100644 --- a/src/type/number/index.ts +++ b/src/type/number/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/number/number.ts b/src/type/number/number.ts index 1ae3ed8fd..573eb403a 100644 --- a/src/type/number/number.ts +++ b/src/type/number/number.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/object/index.ts b/src/type/object/index.ts index 5bebd5824..ac0958f80 100644 --- a/src/type/object/index.ts +++ b/src/type/object/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/object/object.ts b/src/type/object/object.ts index a68c53f9e..ed705aa6e 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/index.ts b/src/type/omit/index.ts index c5e44415a..fc10b7329 100644 --- a/src/type/omit/index.ts +++ b/src/type/omit/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index efac50d8f..041101a9e 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index c6dd8b9a7..d08f83f05 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index e0afd1060..5de3f15f6 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/index.ts b/src/type/optional/index.ts index b861f8e73..ef2545c59 100644 --- a/src/type/optional/index.ts +++ b/src/type/optional/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/optional-from-mapped-result.ts b/src/type/optional/optional-from-mapped-result.ts index 28ad4eaf6..813349da4 100644 --- a/src/type/optional/optional-from-mapped-result.ts +++ b/src/type/optional/optional-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts index 867117b4d..70703f101 100644 --- a/src/type/optional/optional.ts +++ b/src/type/optional/optional.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/parameters/index.ts b/src/type/parameters/index.ts index 195fafbb0..fc04a3a98 100644 --- a/src/type/parameters/index.ts +++ b/src/type/parameters/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts index e2d4bb2f0..6e5bcbe5d 100644 --- a/src/type/parameters/parameters.ts +++ b/src/type/parameters/parameters.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/index.ts b/src/type/partial/index.ts index af7c8ba58..953dfdbbb 100644 --- a/src/type/partial/index.ts +++ b/src/type/partial/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts index 23fb89d14..2681232e3 100644 --- a/src/type/partial/partial-from-mapped-result.ts +++ b/src/type/partial/partial-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index d49dad8ce..b46ee4af9 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/patterns/index.ts b/src/type/patterns/index.ts index 7dbf0a210..240e6e9ca 100644 --- a/src/type/patterns/index.ts +++ b/src/type/patterns/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/patterns/patterns.ts b/src/type/patterns/patterns.ts index d9b9e8d79..016d60424 100644 --- a/src/type/patterns/patterns.ts +++ b/src/type/patterns/patterns.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/index.ts b/src/type/pick/index.ts index 1fa9844ab..ac52efa52 100644 --- a/src/type/pick/index.ts +++ b/src/type/pick/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index b5e30fa1e..963ba8557 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 4da4a6108..4238fd3dc 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index 336d47585..b746bf13b 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/promise/index.ts b/src/type/promise/index.ts index 55150952e..ee5555e88 100644 --- a/src/type/promise/index.ts +++ b/src/type/promise/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts index 48b99d0ce..bebb00171 100644 --- a/src/type/promise/promise.ts +++ b/src/type/promise/promise.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly-optional/index.ts b/src/type/readonly-optional/index.ts index 3e44a84c0..594463f54 100644 --- a/src/type/readonly-optional/index.ts +++ b/src/type/readonly-optional/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly-optional/readonly-optional.ts b/src/type/readonly-optional/readonly-optional.ts index 788ede112..d32212777 100644 --- a/src/type/readonly-optional/readonly-optional.ts +++ b/src/type/readonly-optional/readonly-optional.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/index.ts b/src/type/readonly/index.ts index f39c08474..c21de9d35 100644 --- a/src/type/readonly/index.ts +++ b/src/type/readonly/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/readonly-from-mapped-result.ts b/src/type/readonly/readonly-from-mapped-result.ts index 9f303bb40..29857793b 100644 --- a/src/type/readonly/readonly-from-mapped-result.ts +++ b/src/type/readonly/readonly-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts index c5bc936c9..594170fff 100644 --- a/src/type/readonly/readonly.ts +++ b/src/type/readonly/readonly.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/record/index.ts b/src/type/record/index.ts index 10eb1ceb9..6b0a78848 100644 --- a/src/type/record/index.ts +++ b/src/type/record/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 042d4b5d6..53c4011d0 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/recursive/index.ts b/src/type/recursive/index.ts index 9bb19d9f4..f23634a44 100644 --- a/src/type/recursive/index.ts +++ b/src/type/recursive/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts index cff19af46..d1e14e264 100644 --- a/src/type/recursive/recursive.ts +++ b/src/type/recursive/recursive.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/ref/index.ts b/src/type/ref/index.ts index 1db9fa4e0..606ede084 100644 --- a/src/type/ref/index.ts +++ b/src/type/ref/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index 1df55fe2d..97fffe26f 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/regexp/index.ts b/src/type/regexp/index.ts index 00fab30b4..578ac9777 100644 --- a/src/type/regexp/index.ts +++ b/src/type/regexp/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts index 3029d4240..f43257710 100644 --- a/src/type/regexp/regexp.ts +++ b/src/type/regexp/regexp.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/format.ts b/src/type/registry/format.ts index ebcf3d74a..99b893bbe 100644 --- a/src/type/registry/format.ts +++ b/src/type/registry/format.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/index.ts b/src/type/registry/index.ts index a0fb0e043..f16716fd9 100644 --- a/src/type/registry/index.ts +++ b/src/type/registry/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/type.ts b/src/type/registry/type.ts index 78e313b4a..35078a856 100644 --- a/src/type/registry/type.ts +++ b/src/type/registry/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/index.ts b/src/type/required/index.ts index 428fd821c..e6d6901ff 100644 --- a/src/type/required/index.ts +++ b/src/type/required/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts index 2f6da2807..b73554f1d 100644 --- a/src/type/required/required-from-mapped-result.ts +++ b/src/type/required/required-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/required.ts b/src/type/required/required.ts index e26e8f857..1066289b3 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/rest/index.ts b/src/type/rest/index.ts index b1442f480..e62da1430 100644 --- a/src/type/rest/index.ts +++ b/src/type/rest/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts index 8ca450780..e12d787b8 100644 --- a/src/type/rest/rest.ts +++ b/src/type/rest/rest.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/return-type/index.ts b/src/type/return-type/index.ts index 610d3adc0..799891a64 100644 --- a/src/type/return-type/index.ts +++ b/src/type/return-type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts index fd31f8d59..f3f843355 100644 --- a/src/type/return-type/return-type.ts +++ b/src/type/return-type/return-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/anyschema.ts b/src/type/schema/anyschema.ts index 44fa18e5f..908295fc1 100644 --- a/src/type/schema/anyschema.ts +++ b/src/type/schema/anyschema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/index.ts b/src/type/schema/index.ts index bfd714bb5..d3ce4acee 100644 --- a/src/type/schema/index.ts +++ b/src/type/schema/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/schema.ts b/src/type/schema/schema.ts index 287088f98..6cba033d7 100644 --- a/src/type/schema/schema.ts +++ b/src/type/schema/schema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/sets/index.ts b/src/type/sets/index.ts index e672ba02e..c793d0851 100644 --- a/src/type/sets/index.ts +++ b/src/type/sets/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/sets/set.ts b/src/type/sets/set.ts index c478204ff..f2456a728 100644 --- a/src/type/sets/set.ts +++ b/src/type/sets/set.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/static/index.ts b/src/type/static/index.ts index 8f4f0c9dc..94f485cd1 100644 --- a/src/type/static/index.ts +++ b/src/type/static/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/static/static.ts b/src/type/static/static.ts index e38b3765f..25a9083d1 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/strict/index.ts b/src/type/strict/index.ts index caf80eab1..24555f714 100644 --- a/src/type/strict/index.ts +++ b/src/type/strict/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/strict/strict.ts b/src/type/strict/strict.ts index de47e425b..ddcf88836 100644 --- a/src/type/strict/strict.ts +++ b/src/type/strict/strict.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/string/index.ts b/src/type/string/index.ts index 1c188b4fd..a21b1efbe 100644 --- a/src/type/string/index.ts +++ b/src/type/string/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/string/string.ts b/src/type/string/string.ts index 5bcda7176..77d1c10ee 100644 --- a/src/type/string/string.ts +++ b/src/type/string/string.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbol/index.ts b/src/type/symbol/index.ts index abd306265..0ba6f5542 100644 --- a/src/type/symbol/index.ts +++ b/src/type/symbol/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts index 4c5adc9ab..02712500c 100644 --- a/src/type/symbol/symbol.ts +++ b/src/type/symbol/symbol.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbols/index.ts b/src/type/symbols/index.ts index bac16a322..7ee40afb8 100644 --- a/src/type/symbols/index.ts +++ b/src/type/symbols/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbols/symbols.ts b/src/type/symbols/symbols.ts index dbb90a99a..11530fedb 100644 --- a/src/type/symbols/symbols.ts +++ b/src/type/symbols/symbols.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/finite.ts b/src/type/template-literal/finite.ts index 43a03ac33..194e15f93 100644 --- a/src/type/template-literal/finite.ts +++ b/src/type/template-literal/finite.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts index 671dd40e2..23a56790b 100644 --- a/src/type/template-literal/generate.ts +++ b/src/type/template-literal/generate.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/index.ts b/src/type/template-literal/index.ts index 17ae27505..8f6a05c3e 100644 --- a/src/type/template-literal/index.ts +++ b/src/type/template-literal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/parse.ts b/src/type/template-literal/parse.ts index f856a1eac..7a2730f44 100644 --- a/src/type/template-literal/parse.ts +++ b/src/type/template-literal/parse.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts index 6af7e3f9d..099ee1b5f 100644 --- a/src/type/template-literal/pattern.ts +++ b/src/type/template-literal/pattern.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts index f70df69f8..1250ee651 100644 --- a/src/type/template-literal/syntax.ts +++ b/src/type/template-literal/syntax.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts index c3436b8f2..2003e68a2 100644 --- a/src/type/template-literal/template-literal.ts +++ b/src/type/template-literal/template-literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/union.ts b/src/type/template-literal/union.ts index 9de4dbc83..a5a1f9240 100644 --- a/src/type/template-literal/union.ts +++ b/src/type/template-literal/union.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/transform/index.ts b/src/type/transform/index.ts index 4e6ab0218..949f78cfe 100644 --- a/src/type/transform/index.ts +++ b/src/type/transform/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts index cb7099544..bdd575e5b 100644 --- a/src/type/transform/transform.ts +++ b/src/type/transform/transform.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/tuple/index.ts b/src/type/tuple/index.ts index c1eb788d2..54d2b14ab 100644 --- a/src/type/tuple/index.ts +++ b/src/type/tuple/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts index 32119f78d..bd4f8400f 100644 --- a/src/type/tuple/tuple.ts +++ b/src/type/tuple/tuple.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/index.ts b/src/type/type/index.ts index 081b459d1..dbe0febc8 100644 --- a/src/type/type/index.ts +++ b/src/type/type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index 7975887fd..a5c170153 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/json.ts b/src/type/type/json.ts index d4e6214f6..145d93e5b 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/type.ts b/src/type/type/type.ts index 53f275419..4f0fc5550 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/uint8array/index.ts b/src/type/uint8array/index.ts index 4c2296a42..fc3256037 100644 --- a/src/type/uint8array/index.ts +++ b/src/type/uint8array/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts index 1c1732ced..fa0fb5501 100644 --- a/src/type/uint8array/uint8array.ts +++ b/src/type/uint8array/uint8array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/undefined/index.ts b/src/type/undefined/index.ts index cbc1c28e3..728a1232d 100644 --- a/src/type/undefined/index.ts +++ b/src/type/undefined/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts index a069fe06f..69614443a 100644 --- a/src/type/undefined/undefined.ts +++ b/src/type/undefined/undefined.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/index.ts b/src/type/union/index.ts index 17dd10d72..b49ca2d45 100644 --- a/src/type/union/index.ts +++ b/src/type/union/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-create.ts b/src/type/union/union-create.ts index c2f261933..48df85256 100644 --- a/src/type/union/union-create.ts +++ b/src/type/union/union-create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts index 248d4e600..5bac5539f 100644 --- a/src/type/union/union-evaluated.ts +++ b/src/type/union/union-evaluated.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-type.ts b/src/type/union/union-type.ts index 6eb5ba4d3..68f0cb19e 100644 --- a/src/type/union/union-type.ts +++ b/src/type/union/union-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union.ts b/src/type/union/union.ts index 5c5265e97..2024ad8bb 100644 --- a/src/type/union/union.ts +++ b/src/type/union/union.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unknown/index.ts b/src/type/unknown/index.ts index 7d9c7e293..80a7b68e7 100644 --- a/src/type/unknown/index.ts +++ b/src/type/unknown/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts index f5a0d9a3d..974e4ade1 100644 --- a/src/type/unknown/unknown.ts +++ b/src/type/unknown/unknown.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unsafe/index.ts b/src/type/unsafe/index.ts index 78141b392..395fd75bf 100644 --- a/src/type/unsafe/index.ts +++ b/src/type/unsafe/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts index 26fb987af..7faf6154a 100644 --- a/src/type/unsafe/unsafe.ts +++ b/src/type/unsafe/unsafe.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/void/index.ts b/src/type/void/index.ts index 3b9bf5199..e6bc946ae 100644 --- a/src/type/void/index.ts +++ b/src/type/void/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/void/void.ts b/src/type/void/void.ts index 867f33f47..4cf4ae501 100644 --- a/src/type/void/void.ts +++ b/src/type/void/void.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 8ac9e79df..afe8bd74f 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/cast/index.ts b/src/value/cast/index.ts index 5d403f74a..575c46a49 100644 --- a/src/value/cast/index.ts +++ b/src/value/cast/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/check/check.ts b/src/value/check/check.ts index c2cd892db..e05fee2d7 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/check/index.ts b/src/value/check/index.ts index e318a6581..04975a6ba 100644 --- a/src/value/check/index.ts +++ b/src/value/check/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 0d0f20fa7..7f48119b7 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clean/index.ts b/src/value/clean/index.ts index 4e55b279e..81d0b0910 100644 --- a/src/value/clean/index.ts +++ b/src/value/clean/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index 4c337929b..7f748cbba 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clone/index.ts b/src/value/clone/index.ts index 4294f9957..22d2dd12b 100644 --- a/src/value/clone/index.ts +++ b/src/value/clone/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 4360e4fbb..35afea3c8 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/convert/index.ts b/src/value/convert/index.ts index 0e642e6ac..3a88a32f7 100644 --- a/src/value/convert/index.ts +++ b/src/value/convert/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/create/create.ts b/src/value/create/create.ts index b632947bd..697a37234 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/create/index.ts b/src/value/create/index.ts index 62543e72b..706ec0023 100644 --- a/src/value/create/index.ts +++ b/src/value/create/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/default/default.ts b/src/value/default/default.ts index a089f7600..afeea9cd2 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/default/index.ts b/src/value/default/index.ts index fb74557fd..62ca5b16f 100644 --- a/src/value/default/index.ts +++ b/src/value/default/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/delta/delta.ts b/src/value/delta/delta.ts index d7ae9fa89..616d5e949 100644 --- a/src/value/delta/delta.ts +++ b/src/value/delta/delta.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/delta/index.ts b/src/value/delta/index.ts index 3eca3782f..0ea44bee9 100644 --- a/src/value/delta/index.ts +++ b/src/value/delta/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/deref/deref.ts b/src/value/deref/deref.ts index e7c0926af..c51fc23f7 100644 --- a/src/value/deref/deref.ts +++ b/src/value/deref/deref.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/deref/index.ts b/src/value/deref/index.ts index d0bf7e45b..99003df72 100644 --- a/src/value/deref/index.ts +++ b/src/value/deref/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/equal/equal.ts b/src/value/equal/equal.ts index 264093959..fdfca521e 100644 --- a/src/value/equal/equal.ts +++ b/src/value/equal/equal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/equal/index.ts b/src/value/equal/index.ts index cf0e42f72..7a4ad2de7 100644 --- a/src/value/equal/index.ts +++ b/src/value/equal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 92aa187dd..83a3c1b66 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/guard/index.ts b/src/value/guard/index.ts index 36d7e3fa8..439925ae1 100644 --- a/src/value/guard/index.ts +++ b/src/value/guard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index 6964af0f5..c87e102bc 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/hash/index.ts b/src/value/hash/index.ts index a2607845e..8f83cb1ac 100644 --- a/src/value/hash/index.ts +++ b/src/value/hash/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/index.ts b/src/value/index.ts index eecc44779..8b17d674f 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/mutate/index.ts b/src/value/mutate/index.ts index cf8bd9248..ef0e67394 100644 --- a/src/value/mutate/index.ts +++ b/src/value/mutate/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index 670a02e5d..b8e5c61ca 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/pointer/index.ts b/src/value/pointer/index.ts index d4071966d..c64016d47 100644 --- a/src/value/pointer/index.ts +++ b/src/value/pointer/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/pointer/pointer.ts b/src/value/pointer/pointer.ts index e0c463927..6b8d7aa6f 100644 --- a/src/value/pointer/pointer.ts +++ b/src/value/pointer/pointer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 8908c9d9a..9580e7663 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index e91057e94..35d724811 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 437fad1d6..9aff3d25e 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/index.ts b/src/value/transform/index.ts index f821f6d29..e3bd75d2a 100644 --- a/src/value/transform/index.ts +++ b/src/value/transform/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/value/index.ts b/src/value/value/index.ts index 3af158d90..eabf61e7b 100644 --- a/src/value/value/index.ts +++ b/src/value/value/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 62a0f04d1..6b5febc8b 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/common/remove-notices.ts b/task/build/common/remove-notices.ts index 15c9688eb..96b4dfb2d 100644 --- a/task/build/common/remove-notices.ts +++ b/task/build/common/remove-notices.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/import/build.ts b/task/build/import/build.ts index 5a423c1cc..bb2637876 100644 --- a/task/build/import/build.ts +++ b/task/build/import/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/import/compile.ts b/task/build/import/compile.ts index 05a0a10b8..e8beb64a5 100644 --- a/task/build/import/compile.ts +++ b/task/build/import/compile.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/import/convert-to-esm.ts b/task/build/import/convert-to-esm.ts index 1db93a61f..9878a4fe6 100644 --- a/task/build/import/convert-to-esm.ts +++ b/task/build/import/convert-to-esm.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/index.ts b/task/build/index.ts index fd7cf77da..948330f61 100644 --- a/task/build/index.ts +++ b/task/build/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/redirect/build.ts b/task/build/redirect/build.ts index 9d34cc8cb..f3eba27d7 100644 --- a/task/build/redirect/build.ts +++ b/task/build/redirect/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/redirect/create-package-json.ts b/task/build/redirect/create-package-json.ts index 068041c5a..d25cc61c4 100644 --- a/task/build/redirect/create-package-json.ts +++ b/task/build/redirect/create-package-json.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/redirect/create-redirect-paths.ts b/task/build/redirect/create-redirect-paths.ts index 66549ed3b..3915b08e5 100644 --- a/task/build/redirect/create-redirect-paths.ts +++ b/task/build/redirect/create-redirect-paths.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/require/build.ts b/task/build/require/build.ts index a3305a146..52bba8179 100644 --- a/task/build/require/build.ts +++ b/task/build/require/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/require/compile.ts b/task/build/require/compile.ts index f033f2228..bd089ec21 100644 --- a/task/build/require/compile.ts +++ b/task/build/require/compile.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2023 Haydn Paterson (sinclair) +Copyright (c) 2017-2024 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 4c0b764cf92e51413e71bc054618dab2bf349096 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 Jan 2024 11:25:14 +0900 Subject: [PATCH 224/369] Revision 0.32.6 (#724) * Export TTemplateLiteralSyntax Infrastructure * Export TDecodeType, TDecodeRest and TDecodeProperties * Normalize Mapped Types, Drop TCompositeResolve * Export KeyOf Infrastructure * Export Awaited, Exclude, Extract, Pick, Omit and Mapped ReturnType --- package-lock.json | 4 +- package.json | 2 +- src/index.ts | 18 ++--- src/type/awaited/awaited.ts | 22 +++---- src/type/composite/composite.ts | 23 +++---- src/type/exclude/exclude.ts | 65 ++++++++----------- src/type/extract/extract.ts | 65 ++++++++----------- src/type/indexed/indexed-from-mapped-key.ts | 4 +- src/type/keyof/keyof-from-mapped-result.ts | 5 +- src/type/keyof/keyof.ts | 14 ++-- src/type/mapped/mapped.ts | 14 ++-- src/type/omit/omit-from-mapped-result.ts | 5 +- .../partial/partial-from-mapped-result.ts | 5 +- src/type/pick/pick-from-mapped-result.ts | 5 +- src/type/pick/pick.ts | 14 ++-- .../required/required-from-mapped-result.ts | 5 +- src/type/static/static.ts | 44 ++++++------- test/runtime/type/guard/exclude.ts | 32 +++++---- test/runtime/type/guard/extract.ts | 6 ++ test/static/extract.ts | 2 +- 20 files changed, 175 insertions(+), 179 deletions(-) diff --git a/package-lock.json b/package-lock.json index 78a0102ed..154b955ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.5", + "version": "0.32.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.5", + "version": "0.32.6", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 930e2f331..afe9e95fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.5", + "version": "0.32.6", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/index.ts b/src/index.ts index 779964fef..4f582c5ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,7 @@ export { TypeBoxError } from './type/error/index' export { Any, type TAny } from './type/any/index' export { Array, type TArray, type ArrayOptions } from './type/array/index' export { AsyncIterator, type TAsyncIterator } from './type/async-iterator/index' -export { Awaited, type TAwaited } from './type/awaited/index' +export { Awaited, type TAwaited, type TAwaitedResolve } from './type/awaited/index' export { BigInt, type TBigInt, type BigIntOptions } from './type/bigint/index' export { Boolean, type TBoolean } from './type/boolean/index' export { Composite, type TComposite } from './type/composite/index' @@ -52,7 +52,7 @@ export { Date, type TDate, type DateOptions } from './type/date/index' export { Deref, type TDeref } from './type/deref/index' export { Enum, type TEnum } from './type/enum/index' export { Exclude, type TExclude, type TExcludeFromMappedResult } from './type/exclude/index' -export { Extends, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck } from './type/extends/index' +export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey } from './type/extends/index' export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index' export { Function, type TFunction } from './type/function/index' export { Increment, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' @@ -62,23 +62,23 @@ export { Integer, type TInteger, type IntegerOptions } from './type/integer/inde export { Intersect, IntersectEvaluated, type TIntersect, type TIntersectEvaluated, type IntersectOptions } from './type/intersect/index' export { Iterator, type TIterator } from './type/iterator/index' export { Intrinsic, IntrinsicFromMappedKey, type TIntrinsic, Capitalize, type TCapitalize, Lowercase, type TLowercase, Uncapitalize, type TUncapitalize, Uppercase, type TUppercase } from './type/intrinsic/index' -export { KeyOf, type TKeyOf, type KeyOfFromMappedResult, KeyOfPropertyKeys, KeyOfPattern } from './type/keyof/index' +export { KeyOf, KeyOfPropertyKeys, KeyOfPropertyKeysToRest, KeyOfFromMappedResult, KeyOfPattern, type TKeyOf, type TKeyOfPropertyKeys, type TKeyOfPropertyKeysToRest, type TKeyOfFromMappedResult } from './type/keyof/index' export { Literal, type TLiteral, type TLiteralValue } from './type/literal/index' -export { Mapped, MappedKey, MappedResult, type TMapped, type TMappedKey, type TMappedResult, type TMappedFunction } from './type/mapped/index' +export { Mapped, MappedKey, MappedResult, MappedFunctionReturnType, type TMapped, type TMappedKey, type TMappedResult, type TMappedFunction, type TMappedFunctionReturnType } from './type/mapped/index' export { Never, type TNever } from './type/never/index' export { Not, type TNot } from './type/not/index' export { Null, type TNull } from './type/null/index' export { Number, type TNumber, type NumberOptions } from './type/number/index' export { Object, type TObject, type TProperties, type ObjectOptions } from './type/object/index' -export { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' +export { Omit, type TOmit, type TOmitResolve, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' export { Optional, OptionalFromMappedResult, type TOptional, type TOptionalWithFlag, type TOptionalFromMappedResult } from './type/optional/index' export { Parameters, type TParameters } from './type/parameters/index' export { Partial, PartialFromMappedResult, type TPartial, type TPartialFromMappedResult } from './type/partial/index' -export { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' +export { Pick, type TPick, type TPickResolve, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' export { Promise, type TPromise } from './type/promise/index' export { Readonly, ReadonlyFromMappedResult, type TReadonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from './type/readonly/index' export { ReadonlyOptional, type TReadonlyOptional } from './type/readonly-optional/index' -export { Record, type TRecord } from './type/record/index' +export { Record, type TRecord, type TRecordOrObject } from './type/record/index' export { Recursive, type TRecursive, type TThis } from './type/recursive/index' export { Ref, type TRef } from './type/ref/index' export { RegExp, type TRegExp } from './type/regexp/index' @@ -86,7 +86,7 @@ export { Required, type TRequired, type TRequiredFromMappedResult } from './type export { Rest, type TRest } from './type/rest/index' export { ReturnType, type TReturnType } from './type/return-type/index' export { type TSchema, type TKind, type SchemaOptions, type TAnySchema } from './type/schema/index' -export { type Static, type StaticDecode, type StaticEncode } from './type/static/index' +export { type Static, type StaticDecode, type StaticEncode, type TDecodeType, type TDecodeRest, type TDecodeProperties } from './type/static/index' export { Strict } from './type/strict/index' export { String, type TString, type StringOptions, type StringFormatOption, type StringContentEncodingOption } from './type/string/index' export { Symbol, type TSymbol, type TSymbolValue as SymbolValue } from './type/symbol/index' @@ -98,6 +98,8 @@ export { TemplateLiteralParseExact, TemplateLiteralGenerate, TemplateLiteralExpressionGenerate, + TemplateLiteralSyntax, + type TTemplateLiteralSyntax, type TTemplateLiteral, type TIsTemplateLiteralFinite, type TTemplateLiteralGenerate, diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index 7a17df3af..c782487fe 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -42,11 +42,11 @@ import { IsIntersect, IsUnion, IsPromise } from '../guard/type' // prettier-ignore type TFromRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> + ? TFromRest]> : Acc // prettier-ignore function FromRest(T: [...T]) : TFromRest { - return T.map(L => FromSchema(L)) as TFromRest + return T.map(L => AwaitedResolve(L)) as TFromRest } // ---------------------------------------------------------------- // FromIntersect @@ -69,37 +69,37 @@ function FromUnion(T: [...T]): TFromUnion { // ---------------------------------------------------------------- // Promise // ---------------------------------------------------------------- -type TFromPromise = TFromSchema +type TFromPromise = TAwaitedResolve // prettier-ignore function FromPromise(T: T): TFromPromise { - return FromSchema(T) as TFromPromise + return AwaitedResolve(T) as TFromPromise } // ---------------------------------------------------------------- // FromSchema // ---------------------------------------------------------------- // prettier-ignore -type TFromSchema = +export type TAwaitedResolve = T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : - T extends TPromise ? TFromSchema : + T extends TPromise ? TAwaitedResolve : T // prettier-ignore -function FromSchema(T: T): TFromSchema { +export function AwaitedResolve(T: T): TAwaitedResolve { return ( IsIntersect(T) ? FromIntersect(T.allOf) : IsUnion(T) ? FromUnion(T.anyOf) : IsPromise(T) ? FromPromise(T.item) : T - ) as TFromSchema + ) as TAwaitedResolve } // ------------------------------------------------------------------ // TAwaited // ------------------------------------------------------------------ // prettier-ignore export type TAwaited = ( - TFromSchema + TAwaitedResolve ) /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ -export function Awaited(T: T, options: SchemaOptions = {}): TFromSchema { - return CloneType(FromSchema(T), options) +export function Awaited(T: T, options: SchemaOptions = {}): TAwaitedResolve { + return CloneType(AwaitedResolve(T), options) } diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index ae994017f..b4dc847fa 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -27,12 +27,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema } from '../schema/index' -import type { UnionToTuple, Assert, Evaluate } from '../helpers/index' +import type { UnionToTuple, Assert, Ensure, Evaluate } from '../helpers/index' import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' import { Intersect, type TIntersect } from '../intersect/index' import { Index, type TIndex } from '../indexed/index' import { KeyOfPropertyKeys } from '../keyof/index' -import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // TCompositeKeys @@ -54,22 +53,18 @@ type TCompositeIndex, K extends string[], Acc ex type TCompositeReduce = UnionToTuple> extends infer K ? Evaluate, Assert>> : {} // ^ indexed via intersection of T -// prettier-ignore -type TCompositeResolve = TIntersect extends TIntersect - ? TObject> - : TObject<{}> -function CompositeResolve(T: [...T]): TCompositeResolve { - const intersect: TSchema = Intersect(T, {}) - const keys = KeyOfPropertyKeys(intersect) as string[] - const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties) - return Object(properties) as TCompositeResolve -} // ------------------------------------------------------------------ // TComposite // ------------------------------------------------------------------ -export type TComposite = TCompositeResolve +// prettier-ignore +export type TComposite = TIntersect extends TIntersect + ? Ensure>> + : Ensure> /** `[Json]` Creates a Composite object type */ export function Composite(T: [...T], options?: ObjectOptions): TComposite { - return CloneType(CompositeResolve(T) as TObject, options) as TComposite + const intersect: TSchema = Intersect(T, {}) + const keys = KeyOfPropertyKeys(intersect) as string[] + const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties) + return Object(properties, options) as TComposite } diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index e69ef1b9a..d82bdf33a 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -44,53 +44,44 @@ import { ExcludeFromMappedResult, type TExcludeFromMappedResult } from './exclud // ------------------------------------------------------------------ import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' // ------------------------------------------------------------------ -// ExcludeResolve +// ExcludeTemplateLiteral // ------------------------------------------------------------------ // prettier-ignore type TExcludeTemplateLiteralResult = TUnionEvaluated }[T]>>> // prettier-ignore -type TExcludeTemplateLiteral = ( - Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never +type TExcludeTemplateLiteral = ( + Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never ) +// ------------------------------------------------------------------ +// ExcludeRest +// ------------------------------------------------------------------ // prettier-ignore -type TExcludeArray = AssertRest> extends Static ? never : T[K] +type TExcludeRest = AssertRest> extends Static ? never : L[K] }[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never -// prettier-ignore -type TExcludeResolve = - T extends TTemplateLiteral ? TExcludeTemplateLiteral : - T extends TUnion ? TExcludeArray : - T extends U - ? TNever - : T -// prettier-ignore -function ExcludeResolve(L: L, R: R): TExcludeResolve { - return ( - IsTemplateLiteral(L) ? ExcludeResolve(TemplateLiteralToUnion(L), R) : - IsTemplateLiteral(R) ? ExcludeResolve(L, TemplateLiteralToUnion(R)) : - IsUnion(L) ? (() => { - const narrowed = L.anyOf.filter((inner) => ExtendsCheck(inner, R) === ExtendsResult.False) - return (narrowed.length === 1 ? narrowed[0] : Union(narrowed)) - })() : - ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : - L - ) as TExcludeResolve + +function ExcludeRest(L: [...L], R: R) { + const excluded = L.filter((inner) => ExtendsCheck(inner, R) === ExtendsResult.False) + return excluded.length === 1 ? excluded[0] : Union(excluded) } // ------------------------------------------------------------------ // TExclude // ------------------------------------------------------------------ -export type TExclude = TExcludeResolve - -/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ -export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult -/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ -export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude +// prettier-ignore +export type TExclude = ( + L extends TMappedResult ? TExcludeFromMappedResult : + L extends TTemplateLiteral ? TExcludeTemplateLiteral : + L extends TUnion ? TExcludeRest : + L extends R ? TNever : L +) /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ -export function Exclude(unionType: TSchema, excludedMembers: TSchema, options: SchemaOptions = {}) { - if (IsMappedResult(unionType)) { - return ExcludeFromMappedResult(unionType, excludedMembers, options) - } else { - const E = ExcludeResolve(unionType, excludedMembers) as any - return CloneType(E, options) - } +export function Exclude(L: L, R: R, options: SchemaOptions = {}): TExclude { + // prettier-ignore + return CloneType(( + IsMappedResult(L) ? ExcludeFromMappedResult(L, R, options) : + IsTemplateLiteral(L) ? Exclude(TemplateLiteralToUnion(L), R) : + IsTemplateLiteral(R) ? Exclude(L, TemplateLiteralToUnion(R)) : + IsUnion(L) ? ExcludeRest(L.anyOf, R) : + ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : L + ), options) as never } diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index 7e6dc05f9..a1f6f4d54 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -33,7 +33,7 @@ import { TemplateLiteralToUnion, type TTemplateLiteral } from '../template-liter import { type TLiteral } from '../literal/index' import { Union, type TUnion } from '../union/index' import { type Static } from '../static/index' -import { Never } from '../never/index' +import { Never, type TNever } from '../never/index' import { type TUnionEvaluated } from '../union/index' import { ExtendsCheck, ExtendsResult } from '../extends/index' import { CloneType } from '../clone/type' @@ -44,51 +44,42 @@ import { ExtractFromMappedResult, type TExtractFromMappedResult } from './extrac // ------------------------------------------------------------------ import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' // ------------------------------------------------------------------ -// ExtractResolve +// ExtractTemplateLiteral // ------------------------------------------------------------------ // prettier-ignore -type TFromTemplateLiteralResult = TUnionEvaluated }[T]>>> +type TExtractTemplateLiteralResult = TUnionEvaluated }[T]>>> // prettier-ignore -type TFromTemplateLiteral = Extract, Static> extends infer S ? TFromTemplateLiteralResult> : never +type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never +// ------------------------------------------------------------------ +// ExtractRest +// ------------------------------------------------------------------ // prettier-ignore -type TFromArray = AssertRest> extends Static ? T[K] : never +type TExtractRest = AssertRest> extends Static ? L[K] : never }[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never -// prettier-ignore -type TExtractResolve = ( - T extends TTemplateLiteral ? TFromTemplateLiteral : - T extends TUnion ? TFromArray : - T -) -// prettier-ignore -function ExtractResolve(L: L, R: R): TExtractResolve { - return ( - IsTemplateLiteral(L) ? ExtractResolve(TemplateLiteralToUnion(L), R) : - IsTemplateLiteral(R) ? ExtractResolve(L, TemplateLiteralToUnion(R) as any) : - IsUnion(L) ? (() => { - const narrowed = L.anyOf.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False) - return (narrowed.length === 1 ? narrowed[0] : Union(narrowed)) - })() : - ExtendsCheck(L, R) !== ExtendsResult.False ? L : - Never() - ) as TExtractResolve + +function ExtractRest(L: [...L], R: R) { + const extracted = L.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False) + return extracted.length === 1 ? extracted[0] : Union(extracted) } // ------------------------------------------------------------------ // TExtract // ------------------------------------------------------------------ // prettier-ignore -export type TExtract = TExtractResolve - -/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ -export function Extract(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult -/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ -export function Extract(type: L, union: R, options?: SchemaOptions): TExtract +export type TExtract = ( + L extends TMappedResult ? TExtractFromMappedResult : + L extends TTemplateLiteral ? TExtractTemplateLiteral : + L extends TUnion ? TExtractRest : + L extends U ? L : TNever +) /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ -export function Extract(type: TSchema, union: TSchema, options: SchemaOptions = {}) { - if (IsMappedResult(type)) { - return ExtractFromMappedResult(type, union, options) - } else { - const E = ExtractResolve(type, union) - return CloneType(E, options) - } +export function Extract(L: L, R: R, options: SchemaOptions = {}): TExtract { + // prettier-ignore + return CloneType(( + IsMappedResult(L) ? ExtractFromMappedResult(L, R, options) : + IsTemplateLiteral(L) ? Extract(TemplateLiteralToUnion(L), R) : + IsTemplateLiteral(R) ? Extract(L, TemplateLiteralToUnion(R) as any) : + IsUnion(L) ? ExtractRest(L.anyOf, R) : + ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never() + ), options) as never } diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index 76dc90e70..d32a11056 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -27,7 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' -import type { Evaluate } from '../helpers/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { Index, type TIndex } from './indexed' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' @@ -90,7 +90,7 @@ export type TIndexFromMappedKey< K extends TMappedKey, P extends TProperties = TMappedIndexProperties > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function IndexFromMappedKey< diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 8e10c66aa..d7edcd9ef 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { SchemaOptions } from '../schema/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { KeyOf, type TKeyOf } from './keyof' @@ -55,7 +56,7 @@ function FromProperties< type TFromMappedResult< R extends TMappedResult > = ( - TFromProperties + Evaluate> ) // prettier-ignore function FromMappedResult< @@ -71,7 +72,7 @@ export type TKeyOfFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function KeyOfFromMappedResult< diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 5fcd4e51f..8e3b8fcde 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -45,16 +45,16 @@ import { IsMappedResult } from '../guard/type' // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKeys = ( +export type TKeyOfPropertyKeysToRest = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? L extends '[number]' - ? TFromPropertyKeys - : TFromPropertyKeys>]> + ? TKeyOfPropertyKeysToRest + : TKeyOfPropertyKeysToRest>]> : Acc ) // prettier-ignore -function FromPropertyKeys(T: [...T]): TFromPropertyKeys { - return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as TFromPropertyKeys +export function KeyOfPropertyKeysToRest(T: [...T]): TKeyOfPropertyKeysToRest { + return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as TKeyOfPropertyKeysToRest } // ------------------------------------------------------------------ // KeyOfTypeResolve @@ -63,7 +63,7 @@ function FromPropertyKeys(T: [...T]): TFromPropertyKeys export type TKeyOf< T extends TSchema, K extends PropertyKey[] = TKeyOfPropertyKeys, - S extends TSchema[] = TFromPropertyKeys, + S extends TSchema[] = TKeyOfPropertyKeysToRest, U = TUnionEvaluated > = ( Ensure @@ -78,7 +78,7 @@ export function KeyOf(T: TSchema, options: SchemaOptions = {}): any { return KeyOfFromMappedResult(T, options) } else { const K = KeyOfPropertyKeys(T) - const S = FromPropertyKeys(K) + const S = KeyOfPropertyKeysToRest(K) const U = UnionEvaluated(S) return CloneType(U, options) } diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 0c6588b8b..6ab806bd0 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -241,19 +241,19 @@ function FromSchemaType(K: K, T: T): F ) as FromSchemaType } // ------------------------------------------------------------------ -// FromMappedFunctionReturnType +// MappedFunctionReturnType // ------------------------------------------------------------------ // prettier-ignore -type FromMappedFunctionReturnType = ( +export type TMappedFunctionReturnType = ( K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? FromMappedFunctionReturnType }> + ? TMappedFunctionReturnType }> : Acc ) // prettier-ignore -function FromMappedFunctionReturnType(K: [...K], T: T, Acc: TProperties = {}): FromMappedFunctionReturnType { +export function MappedFunctionReturnType(K: [...K], T: T, Acc: TProperties = {}): TMappedFunctionReturnType { return K.reduce((Acc, L) => { return { ...Acc, [L]: FromSchemaType(L, T) } - }, {} as TProperties) as FromMappedFunctionReturnType + }, {} as TProperties) as TMappedFunctionReturnType } // ------------------------------------------------------------------ // TMappedFunction @@ -267,7 +267,7 @@ export type TMappedFunction> = (T: I) export type TMapped< K extends PropertyKey[], F extends TMappedFunction, - R extends TProperties = Evaluate>>, + R extends TProperties = Evaluate>>, > = Ensure> /** `[Json]` Creates a Mapped object type */ @@ -278,6 +278,6 @@ export function Mapped = T export function Mapped(key: any, map: Function, options: ObjectOptions = {}) { const K = IsSchema(key) ? IndexPropertyKeys(key) : (key as PropertyKey[]) const RT = map({ [Kind]: 'MappedKey', keys: K } as TMappedKey) - const R = FromMappedFunctionReturnType(K, RT) + const R = MappedFunctionReturnType(K, RT) return CloneType(Object(R), options) } diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index d08f83f05..01327fe21 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { SchemaOptions } from '../schema/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Omit, type TOmit } from './omit' @@ -58,7 +59,7 @@ type TFromMappedResult< R extends TMappedResult, K extends PropertyKey[], > = ( - TFromProperties + Evaluate> ) // prettier-ignore function FromMappedResult< @@ -76,7 +77,7 @@ export type TOmitFromMappedResult< K extends PropertyKey[], P extends TProperties = TFromMappedResult > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function OmitFromMappedResult< diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts index 2681232e3..00f7338ac 100644 --- a/src/type/partial/partial-from-mapped-result.ts +++ b/src/type/partial/partial-from-mapped-result.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { SchemaOptions } from '../schema/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Partial, type TPartial } from './partial' @@ -55,7 +56,7 @@ function FromProperties< type TFromMappedResult< R extends TMappedResult > = ( - TFromProperties + Evaluate> ) // prettier-ignore function FromMappedResult< @@ -71,7 +72,7 @@ export type TPartialFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function PartialFromMappedResult< diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 4238fd3dc..bfa997a64 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { SchemaOptions } from '../schema/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Pick, type TPick } from './pick' @@ -58,7 +59,7 @@ type TFromMappedResult< R extends TMappedResult, K extends PropertyKey[], > = ( - TFromProperties + Evaluate> ) // prettier-ignore function FromMappedResult< @@ -76,7 +77,7 @@ export type TPickFromMappedResult< K extends PropertyKey[], P extends TProperties = TFromMappedResult > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function PickFromMappedResult< diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index b746bf13b..976cba58e 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -50,7 +50,7 @@ import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } // prettier-ignore type FromIntersect = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromIntersect]> + ? FromIntersect]> : Acc function FromIntersect(T: T, K: K) { return T.map((T) => PickResolve(T, K)) as FromIntersect @@ -61,7 +61,7 @@ function FromIntersect(T: T, K: K) // prettier-ignore type FromUnion = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromUnion]> + ? FromUnion]> : Acc // prettier-ignore function FromUnion(T: T, K: K) { @@ -82,25 +82,25 @@ function FromProperties(T: T, K: // PickResolve // ------------------------------------------------------------------ // prettier-ignore -export type PickResolve = - T extends TRecursive ? TRecursive> : +export type TPickResolve = + T extends TRecursive ? TRecursive> : T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TObject> : TObject<{}> // prettier-ignore -export function PickResolve(T: T, K: [...K]): PickResolve { +export function PickResolve(T: T, K: [...K]): TPickResolve { return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : IsObject(T) ? Object(FromProperties(T.properties, K)) : Object({}) - ) as PickResolve + ) as TPickResolve } // ------------------------------------------------------------------ // TPick // ------------------------------------------------------------------ -export type TPick = PickResolve +export type TPick = TPickResolve /** `[Json]` Constructs a type whose keys are picked from the given type */ export function Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts index b73554f1d..bcf5780bf 100644 --- a/src/type/required/required-from-mapped-result.ts +++ b/src/type/required/required-from-mapped-result.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { SchemaOptions } from '../schema/index' +import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Required, type TRequired } from './required' @@ -55,7 +56,7 @@ function FromProperties< type TFromMappedResult< R extends TMappedResult > = ( - TFromProperties + Evaluate> ) // prettier-ignore function FromMappedResult< @@ -71,7 +72,7 @@ export type TRequiredFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult > = ( - TMappedResult

      + Ensure> ) // prettier-ignore export function RequiredFromMappedResult< diff --git a/src/type/static/static.ts b/src/type/static/static.ts index 25a9083d1..488055c76 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -52,41 +52,41 @@ import type { TTransform } from '../transform/index' // DecodeType // ------------------------------------------------------------------ // prettier-ignore -export type DecodeProperties = { - [K in keyof T]: DecodeType +export type TDecodeProperties = { + [K in keyof T]: TDecodeType } // prettier-ignore -export type DecodeRest = +export type TDecodeRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? DecodeRest]> + ? TDecodeRest]> : Acc // prettier-ignore -export type DecodeType = ( - T extends TOptional ? TOptional> : - T extends TReadonly ? TReadonly> : +export type TDecodeType = ( + T extends TOptional ? TOptional> : + T extends TReadonly ? TReadonly> : T extends TTransform ? TUnsafe : - T extends TArray ? TArray> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor, DecodeType> : + T extends TArray ? TArray> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TConstructor ? TConstructor, TDecodeType> : T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction, DecodeType> : - T extends TIntersect ? TIntersect> : - T extends TIterator ? TIterator> : - T extends TNot ? TNot> : - T extends TObject ? TObject>> : - T extends TPromise ? TPromise> : - T extends TRecord ? TRecord> : - T extends TRecursive ? TRecursive> : - T extends TRef ? TRef> : - T extends TTuple ? TTuple> : - T extends TUnion ? TUnion> : + T extends TFunction ? TFunction, TDecodeType> : + T extends TIntersect ? TIntersect> : + T extends TIterator ? TIterator> : + T extends TNot ? TNot> : + T extends TObject ? TObject>> : + T extends TPromise ? TPromise> : + T extends TRecord ? TRecord> : + T extends TRecursive ? TRecursive> : + T extends TRef ? TRef> : + T extends TTuple ? TTuple> : + T extends TUnion ? TUnion> : T ) // ------------------------------------------------------------------ // Static // ------------------------------------------------------------------ /** Creates an decoded static type from a TypeBox type */ -export type StaticDecode = Static, P> +export type StaticDecode = Static, P> /** Creates an encoded static type from a TypeBox type */ export type StaticEncode = Static /** Creates a static type from a TypeBox type */ diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/exclude.ts index 78fbdfe32..88366678c 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/exclude.ts @@ -3,40 +3,40 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/guard/TExclude', () => { - it('Should extract string from number', () => { + it('Should exclude string from number', () => { const T = Type.Exclude(Type.String(), Type.Number()) Assert.IsTrue(TypeGuard.IsString(T)) }) - it('Should extract string from string', () => { + it('Should exclude string from string', () => { const T = Type.Exclude(Type.String(), Type.String()) Assert.IsTrue(TypeGuard.IsNever(T)) }) - it('Should extract string | number | boolean from string', () => { + it('Should exclude string | number | boolean from string', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0])) Assert.IsTrue(TypeGuard.IsBoolean(T.anyOf[1])) }) - it('Should extract string | number | boolean from string | boolean', () => { + it('Should exclude string | number | boolean from string | boolean', () => { const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) Assert.IsTrue(TypeGuard.IsNumber(T)) }) // ------------------------------------------------------------------------ // TemplateLiteral | TemplateLiteral // ------------------------------------------------------------------------ - it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) Assert.IsTrue(TypeGuard.IsNever(T)) }) - it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) Assert.IsTrue(['C'].includes(T.const)) }) - it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) @@ -46,19 +46,19 @@ describe('type/guard/TExclude', () => { // ------------------------------------------------------------------------ // TemplateLiteral | Union 1 // ------------------------------------------------------------------------ - it('Should extract TemplateLiteral | Union 1', () => { + it('Should exclude TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const T = Type.Exclude(A, B) Assert.IsTrue(TypeGuard.IsNever(T)) }) - it('Should extract TemplateLiteral | Union 1', () => { + it('Should exclude TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) const T = Type.Exclude(A, B) Assert.IsTrue(['C'].includes(T.const)) }) - it('Should extract TemplateLiteral | Union 1', () => { + it('Should exclude TemplateLiteral | Union 1', () => { const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const B = Type.Union([Type.Literal('A')]) const T = Type.Exclude(A, B) @@ -68,23 +68,29 @@ describe('type/guard/TExclude', () => { // ------------------------------------------------------------------------ // Union | TemplateLiteral 1 // ------------------------------------------------------------------------ - it('Should extract Union | TemplateLiteral 1', () => { + it('Should exclude Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) const T = Type.Exclude(A, B) Assert.IsTrue(TypeGuard.IsNever(T)) }) - it('Should extract Union | TemplateLiteral 1', () => { + it('Should exclude Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) const T = Type.Exclude(A, B) Assert.IsTrue(['C'].includes(T.const)) }) - it('Should extract Union | TemplateLiteral 1', () => { + it('Should exclude Union | TemplateLiteral 1', () => { const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) const T = Type.Exclude(A, B) Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) }) + it('Should exclude with options', () => { + const A = Type.String() + const B = Type.String() + const T = Type.Exclude(A, B, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) }) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/extract.ts index b3524d1e0..ffa7d8871 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/extract.ts @@ -93,4 +93,10 @@ describe('type/guard/TExtract', () => { const T = Type.Extract(A, B) Assert.IsTrue(['A'].includes(T.const)) }) + it('Should extract with options', () => { + const A = Type.String() + const B = Type.String() + const T = Type.Extract(A, B, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) }) diff --git a/test/static/extract.ts b/test/static/extract.ts index 4bd3dc833..0ed6a2a96 100644 --- a/test/static/extract.ts +++ b/test/static/extract.ts @@ -7,7 +7,7 @@ import { Expect } from './assert' } { const T = Type.Extract(Type.String(), Type.Number()) - Expect(T).ToStatic() + Expect(T).ToStaticNever() } { const T = Type.Extract(Type.Union([Type.Number(), Type.String(), Type.Boolean()]), Type.Number()) From ed234d02a3476177b962ed48e0d209483fbbdfbf Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 Jan 2024 12:51:02 +0900 Subject: [PATCH 225/369] Revision 0.32.6 (#725) * Export Deref Infrastructure * Remove Resolve type for Deref, Omit, Pick and Awaited * Export Index Infrastructure * Readme * Reorder Template Literal Exports --- readme.md | 22 +++++++++++----------- src/index.ts | 33 +++++++++++++++++++++++---------- src/type/awaited/awaited.ts | 26 +++++++++++--------------- src/type/deref/deref.ts | 36 +++++++++++++++++------------------- src/type/indexed/indexed.ts | 24 ++++++++++++------------ src/type/omit/omit.ts | 25 +++++++++++-------------- src/type/pick/pick.ts | 26 +++++++++++--------------- 7 files changed, 96 insertions(+), 96 deletions(-) diff --git a/readme.md b/readme.md index d6671201b..26fd763b9 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates in-memory Json Schema objects that infer as TypeScript types. The schematics produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime asserted using standard Json Schema validation. -This library is built to be a runtime type system offering similar capabilities to TypeScript's static type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to be a runtime type system providing similar capabilities to TypeScript's programmable type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. License MIT @@ -648,7 +648,7 @@ TypeBox provides an extended type set that can be used to create schematics for ### Import -Import the Type namespace to bring in TypeBox's full type system. This is recommended for most users. +Import the Type namespace to bring in the full TypeBox type system. This is recommended for most users. ```typescript import { Type, type Static } from '@sinclair/typebox' @@ -870,7 +870,7 @@ function test(node: Node) { ### Template Literal Types -TypeBox supports template literal types with TemplateLiteral. This type can be created using a syntax similar to the TypeScript template literal syntax or composed from exterior types. TypeBox encodes template literals as regular expressions which enables the template to be checked by Json Schema validators. This type also supports regular expression parsing that enables template patterns to be used for generative types. The following shows both TypeScript and TypeBox usage. +TypeBox supports template literal types with the TemplateLiteral function. This type can be created using a syntax similar to the TypeScript template literal syntax or composed from exterior types. TypeBox encodes template literals as regular expressions which enables the template to be checked by Json Schema validators. This type also supports regular expression parsing that enables template patterns to be used for generative types. The following shows both TypeScript and TypeBox usage. ```typescript // TypeScript @@ -905,7 +905,7 @@ const R = Type.Record(K, Type.String()) // const R: TObject<{ ### Indexed Access Types -TypeBox supports indexed access types with Index. This type enables uniform access to interior property and element types without having to extract them from the underlying schema representation. This type is supported for Object, Array, Tuple, Union and Intersect types. +TypeBox supports indexed access types with the Index function. This function enables uniform access to interior property and element types without having to extract them from the underlying schema representation. Index types are supported for Object, Array, Tuple, Union and Intersect types. ```typescript const T = Type.Object({ // type T = { @@ -944,7 +944,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // type C = T[keyof T] ### Mapped Types -TypeBox supports mapped object types with Mapped. This type accepts two arguments, the first is a union type typically derived from KeyOf, the second is a mapping function that receives a mapping key `K` that can be used to index properties of a type. The following implements Partial using mapped types. +TypeBox supports mapped types with the Mapped function. This function accepts two arguments, the first is a union type typically derived from KeyOf, the second is a mapping function that receives a mapping key `K` that can be used to index properties of a type. The following implements a mapped type that remaps each property to be `T | null` ```typescript const T = Type.Object({ // type T = { @@ -953,14 +953,14 @@ const T = Type.Object({ // type T = { z: Type.Boolean() // z: boolean }) // } -const M = Type.Mapped(Type.KeyOf(T), K => { // type M = { [K in keyof T]?: T[K] } - return Type.Optional(Type.Index(T, K)) // +const M = Type.Mapped(Type.KeyOf(T), K => { // type M = { [K in keyof T]: T[K] | null } + return Type.Union([Type.Index(T, K), Type.Null()]) // }) // ... evaluated as // // const M: TObject<{ - // x: TOptional, - // y: TOptional, - // z: TOptional + // x: TUnion<[TNumber, TNull]>, + // y: TUnion<[TString, TNull]>, + // z: TUnion<[TBoolean, TNull]> // }> ``` @@ -968,7 +968,7 @@ const M = Type.Mapped(Type.KeyOf(T), K => { // type M = { [K in keyof T ### Conditional Types -TypeBox supports runtime conditional types with Extends. This type performs a structural assignability check against the first (`left`) and second (`right`) arguments and will return either the third (`true`) or fourth (`false`) argument based on the result. The conditional types Exclude and Extract are also supported. The following shows both TypeScript and TypeBox examples of conditional types. +TypeBox supports runtime conditional types with the Extends function. This function performs a structural assignability check against the first (`left`) and second (`right`) arguments and will return either the third (`true`) or fourth (`false`) argument based on the result. The conditional types Exclude and Extract are also supported. The following shows both TypeScript and TypeBox examples of conditional types. ```typescript // Extends diff --git a/src/index.ts b/src/index.ts index 4f582c5ca..b1b6ea791 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,7 +41,7 @@ export { TypeBoxError } from './type/error/index' export { Any, type TAny } from './type/any/index' export { Array, type TArray, type ArrayOptions } from './type/array/index' export { AsyncIterator, type TAsyncIterator } from './type/async-iterator/index' -export { Awaited, type TAwaited, type TAwaitedResolve } from './type/awaited/index' +export { Awaited, type TAwaited } from './type/awaited/index' export { BigInt, type TBigInt, type BigIntOptions } from './type/bigint/index' export { Boolean, type TBoolean } from './type/boolean/index' export { Composite, type TComposite } from './type/composite/index' @@ -56,7 +56,20 @@ export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExte export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index' export { Function, type TFunction } from './type/function/index' export { Increment, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' -export { Index, IndexPropertyKeys, IndexFromMappedKey, IndexFromMappedResult, type TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult } from './type/indexed/index' +export { + Index, + IndexPropertyKeys, + IndexFromPropertyKeys, + IndexFromPropertyKey, + IndexFromMappedKey, + IndexFromMappedResult, + type TIndex, + type TIndexPropertyKeys, + type TIndexFromPropertyKeys, + type TIndexFromPropertyKey, + type TIndexFromMappedKey, + type TIndexFromMappedResult, +} from './type/indexed/index' export { InstanceType, type TInstanceType } from './type/instance-type/index' export { Integer, type TInteger, type IntegerOptions } from './type/integer/index' export { Intersect, IntersectEvaluated, type TIntersect, type TIntersectEvaluated, type IntersectOptions } from './type/intersect/index' @@ -70,11 +83,11 @@ export { Not, type TNot } from './type/not/index' export { Null, type TNull } from './type/null/index' export { Number, type TNumber, type NumberOptions } from './type/number/index' export { Object, type TObject, type TProperties, type ObjectOptions } from './type/object/index' -export { Omit, type TOmit, type TOmitResolve, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' +export { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' export { Optional, OptionalFromMappedResult, type TOptional, type TOptionalWithFlag, type TOptionalFromMappedResult } from './type/optional/index' export { Parameters, type TParameters } from './type/parameters/index' export { Partial, PartialFromMappedResult, type TPartial, type TPartialFromMappedResult } from './type/partial/index' -export { Pick, type TPick, type TPickResolve, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' +export { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' export { Promise, type TPromise } from './type/promise/index' export { Readonly, ReadonlyFromMappedResult, type TReadonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from './type/readonly/index' export { ReadonlyOptional, type TReadonlyOptional } from './type/readonly-optional/index' @@ -92,18 +105,18 @@ export { String, type TString, type StringOptions, type StringFormatOption, type export { Symbol, type TSymbol, type TSymbolValue as SymbolValue } from './type/symbol/index' export { TemplateLiteral, - IsTemplateLiteralFinite, - IsTemplateLiteralExpressionFinite, + TemplateLiteralSyntax, + TemplateLiteralGenerate, TemplateLiteralParse, TemplateLiteralParseExact, - TemplateLiteralGenerate, + IsTemplateLiteralFinite, TemplateLiteralExpressionGenerate, - TemplateLiteralSyntax, - type TTemplateLiteralSyntax, + IsTemplateLiteralExpressionFinite, type TTemplateLiteral, - type TIsTemplateLiteralFinite, + type TTemplateLiteralSyntax, type TTemplateLiteralGenerate, type TTemplateLiteralKind, + type TIsTemplateLiteralFinite, } from './type/template-literal/index' export { Transform, TransformDecodeBuilder, TransformEncodeBuilder, type TTransform, type TransformOptions, type TransformFunction } from './type/transform/index' export { Tuple, type TTuple } from './type/tuple/index' diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index c782487fe..5c3fbb5a2 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -42,7 +42,7 @@ import { IsIntersect, IsUnion, IsPromise } from '../guard/type' // prettier-ignore type TFromRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> + ? TFromRest]> : Acc // prettier-ignore function FromRest(T: [...T]) : TFromRest { @@ -69,37 +69,33 @@ function FromUnion(T: [...T]): TFromUnion { // ---------------------------------------------------------------- // Promise // ---------------------------------------------------------------- -type TFromPromise = TAwaitedResolve +type TFromPromise = TAwaited // prettier-ignore function FromPromise(T: T): TFromPromise { return AwaitedResolve(T) as TFromPromise } // ---------------------------------------------------------------- -// FromSchema +// AwaitedResolve // ---------------------------------------------------------------- // prettier-ignore -export type TAwaitedResolve = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TPromise ? TAwaitedResolve : - T -// prettier-ignore -export function AwaitedResolve(T: T): TAwaitedResolve { +function AwaitedResolve(T: T): TAwaited { return ( IsIntersect(T) ? FromIntersect(T.allOf) : IsUnion(T) ? FromUnion(T.anyOf) : IsPromise(T) ? FromPromise(T.item) : T - ) as TAwaitedResolve + ) as TAwaited } // ------------------------------------------------------------------ // TAwaited // ------------------------------------------------------------------ // prettier-ignore -export type TAwaited = ( - TAwaitedResolve -) +export type TAwaited = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TPromise ? TAwaited : + T /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ -export function Awaited(T: T, options: SchemaOptions = {}): TAwaitedResolve { +export function Awaited(T: T, options: SchemaOptions = {}): TAwaited { return CloneType(AwaitedResolve(T), options) } diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts index 2be22d90d..117de7564 100644 --- a/src/type/deref/deref.ts +++ b/src/type/deref/deref.ts @@ -53,7 +53,7 @@ import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsOb // prettier-ignore export type TFromRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> + ? TFromRest]> : Acc ) function FromRest(schema: TSchema[], references: TSchema[]) { @@ -64,7 +64,7 @@ function FromRest(schema: TSchema[], references: TSchema[]) { // ------------------------------------------------------------------ // prettier-ignore type FromProperties = Evaluate<{ - [K in keyof T]: DerefResolve + [K in keyof T]: TDeref }> // prettier-ignore function FromProperties(properties: TProperties, references: TSchema[]) { @@ -133,21 +133,7 @@ function FromRef(schema: TRef, references: TSchema[]) { return Deref(discard, references) } // prettier-ignore -export type DerefResolve = - T extends TConstructor ? TConstructor, DerefResolve> : - T extends TFunction ? TFunction, DerefResolve> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TTuple ? TTuple> : - T extends TObject ? TObject> : - T extends TArray ? TArray> : - T extends TPromise ? TPromise> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TIterator ? TIterator> : - T extends TRef ? DerefResolve : - T -// prettier-ignore -export function DerefResolve(schema: T, references: TSchema[]): TDeref { +function DerefResolve(schema: T, references: TSchema[]): TDeref { return ( IsConstructor(schema) ? FromConstructor(schema, references) : IsFunction(schema) ? FromFunction(schema, references) : @@ -163,11 +149,23 @@ export function DerefResolve(schema: T, references: TSchema[] schema ) as TDeref } +// prettier-ignore +export type TDeref = + T extends TConstructor ? TConstructor, TDeref> : + T extends TFunction ? TFunction, TDeref> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TTuple ? TTuple> : + T extends TObject ? TObject> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TIterator ? TIterator> : + T extends TRef ? TDeref : + T // ------------------------------------------------------------------ // TDeref // ------------------------------------------------------------------ -export type TDeref = DerefResolve - /** `[Json]` Creates a dereferenced type */ export function Deref(schema: T, references: TSchema[]): TDeref { return DerefResolve(CloneType(schema), CloneRest(references)) diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index 7b6e4dcf3..2589aeaf6 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -54,12 +54,12 @@ import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, I // prettier-ignore type TFromRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest, TSchema>]> + ? TFromRest, TSchema>]> : Acc ) // prettier-ignore function FromRest(T: [...T], K: K): TFromRest { - return T.map(L => FromKey(L, K)) as TFromRest + return T.map(L => IndexFromPropertyKey(L, K)) as TFromRest } // ------------------------------------------------------------------ // FromIntersectRest @@ -168,8 +168,8 @@ function FromProperty(T: T, K: K): // FromKey // ------------------------------------------------------------------ // prettier-ignore -type TFromKey = ( - T extends TRecursive ? TFromKey : +export type TIndexFromPropertyKey = ( + T extends TRecursive ? TIndexFromPropertyKey : T extends TIntersect ? TFromIntersect : T extends TUnion ? TFromUnion : T extends TTuple ? TFromTuple : @@ -178,7 +178,7 @@ type TFromKey = ( TNever ) // prettier-ignore -function FromKey(T: T, K: K): TFromKey { +export function IndexFromPropertyKey(T: T, K: K): TIndexFromPropertyKey { return ( IsIntersect(T) ? FromIntersect(T.allOf, K) : IsUnion(T) ? FromUnion(T.anyOf, K) : @@ -186,32 +186,32 @@ function FromKey(T: T, K: K): TFromKey IsArray(T) ? FromArray(T.items, K) : IsObject(T) ? FromProperty(T.properties, K) : Never() - ) as TFromKey + ) as TIndexFromPropertyKey } // ------------------------------------------------------------------ // FromKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromKeys = ( +export type TIndexFromPropertyKeys = ( K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? TFromKeys, TSchema>]> + ? TIndexFromPropertyKeys, TSchema>]> : Acc ) // prettier-ignore -function FromKeys(T: T, K: [...K]): TFromKeys { - return K.map(L => FromKey(T, L)) as TFromKeys +export function IndexFromPropertyKeys(T: T, K: [...K]): TIndexFromPropertyKeys { + return K.map(L => IndexFromPropertyKey(T, L)) as TIndexFromPropertyKeys } // ------------------------------------------------------------------ // FromSchema // ------------------------------------------------------------------ // prettier-ignore type FromSchema = ( - TUnionEvaluated> + TUnionEvaluated> ) // prettier-ignore function FromSchema(T: T, K: [...K]): FromSchema { return ( - UnionEvaluated(FromKeys(T, K as PropertyKey[])) + UnionEvaluated(IndexFromPropertyKeys(T, K as PropertyKey[])) ) as FromSchema } // ------------------------------------------------------------------ diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 5de3f15f6..5c5ae73b2 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -51,7 +51,7 @@ import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } // prettier-ignore type TFromIntersect = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromIntersect]> + ? TFromIntersect]> : Acc ) // prettier-ignore @@ -64,7 +64,7 @@ function FromIntersect(T: T, K: K) // prettier-ignore type TFromUnion = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromUnion]> + ? TFromUnion]> : Acc ) // prettier-ignore @@ -91,27 +91,24 @@ function FromProperties(T: T, K: // OmitResolve // ------------------------------------------------------------------ // prettier-ignore -export type TOmitResolve = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - TObject<{}> -// prettier-ignore -export function OmitResolve(T: T, K: [...K]): TOmitResolve { +function OmitResolve(T: T, K: [...K]): TOmit { return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : IsObject(T) ? Object(FromProperties(T.properties, K)) : Object({}) - ) as TOmitResolve + ) as TOmit } +// prettier-ignore +export type TOmit = + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> // ------------------------------------------------------------------ // TOmit // ------------------------------------------------------------------ -// prettier-ignore -export type TOmit = TOmitResolve - /** `[Json]` Constructs a type whose keys are omitted from the given type */ export function Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult /** `[Json]` Constructs a type whose keys are omitted from the given type */ diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index 976cba58e..c35b401ce 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -50,7 +50,7 @@ import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } // prettier-ignore type FromIntersect = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromIntersect]> + ? FromIntersect]> : Acc function FromIntersect(T: T, K: K) { return T.map((T) => PickResolve(T, K)) as FromIntersect @@ -61,7 +61,7 @@ function FromIntersect(T: T, K: K) // prettier-ignore type FromUnion = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromUnion]> + ? FromUnion]> : Acc // prettier-ignore function FromUnion(T: T, K: K) { @@ -82,25 +82,21 @@ function FromProperties(T: T, K: // PickResolve // ------------------------------------------------------------------ // prettier-ignore -export type TPickResolve = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - TObject<{}> -// prettier-ignore -export function PickResolve(T: T, K: [...K]): TPickResolve { +function PickResolve(T: T, K: [...K]): TPick { return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : IsObject(T) ? Object(FromProperties(T.properties, K)) : Object({}) - ) as TPickResolve + ) as TPick } -// ------------------------------------------------------------------ -// TPick -// ------------------------------------------------------------------ -export type TPick = TPickResolve +// prettier-ignore +export type TPick = + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> /** `[Json]` Constructs a type whose keys are picked from the given type */ export function Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult From 8e1bc047fbfb015004ccdf32f6b1f87718bdab4d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 Jan 2024 20:02:08 +0900 Subject: [PATCH 226/369] Revision 0.32.7 (#727) - Clone Default Annotation on Assignment --- package-lock.json | 4 ++-- package.json | 2 +- src/value/default/default.ts | 3 ++- test/runtime/value/default/object.ts | 34 +++++++++++++++++++++++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 154b955ce..f0b5b839d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.6", + "version": "0.32.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.6", + "version": "0.32.7", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index afe9e95fc..ba8a22890 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.6", + "version": "0.32.7", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/default/default.ts b/src/value/default/default.ts index afeea9cd2..b84ce005f 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { Check } from '../check/index' +import { Clone } from '../clone/index' import { Deref } from '../deref/index' import { Kind } from '../../type/symbols/index' @@ -52,7 +53,7 @@ import { IsSchema } from '../../type/guard/type' // ValueOrDefault // ------------------------------------------------------------------ function ValueOrDefault(schema: TSchema, value: unknown) { - return !(value === undefined) || !('default' in schema) ? value : schema.default + return value === undefined && 'default' in schema ? Clone(schema.default) : value } // ------------------------------------------------------------------ // IsCheckable diff --git a/test/runtime/value/default/object.ts b/test/runtime/value/default/object.ts index ab4dda473..1accf40ee 100644 --- a/test/runtime/value/default/object.ts +++ b/test/runtime/value/default/object.ts @@ -1,5 +1,5 @@ import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { Type, CloneType } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/default/Object', () => { @@ -177,4 +177,36 @@ describe('value/default/Object', () => { const R = Value.Default(T, { x: null, y: null, z: undefined }) Assert.IsEqual(R, { x: null, y: null, z: undefined }) }) + // ---------------------------------------------------------------- + // Mutation + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should retain defaults on operation', () => { + const A = Type.Object({ + a: Type.Object( + { + b: Type.Array(Type.String(), { default: [] }), + }, + { default: {} }, + ), + }) + const value = Value.Default(A, {}) + Assert.IsEqual(value, { a: { b: [] } }) + Assert.IsEqual(A.properties.a.default, {}) + Assert.IsEqual(A.properties.a.properties.b.default, []) + }) + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should retain schematics on operation', () => { + const A = Type.Object({ + a: Type.Object( + { + b: Type.Array(Type.String(), { default: [] }), + }, + { default: {} }, + ), + }) + const B = CloneType(A) + Value.Default(A, {}) + Assert.IsEqual(A, B) + }) }) From 7ea295676350b286b23be213936340e95118d92d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 10 Jan 2024 21:01:39 +0900 Subject: [PATCH 227/369] Revision 0.32.8 (#728) - Clone Default Annotation on Assignment --- package-lock.json | 4 +- package.json | 2 +- src/value/cast/cast.ts | 2 +- src/value/create/create.ts | 77 ++++++++++++++++------------- test/runtime/value/create/object.ts | 15 ++++++ 5 files changed, 61 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0b5b839d..2fabd94e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.7", + "version": "0.32.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.7", + "version": "0.32.8", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index ba8a22890..b291d5ecb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.7", + "version": "0.32.8", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index afe8bd74f..cadbaf72a 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -92,7 +92,7 @@ function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema } function CastUnion(union: TUnion, references: TSchema[], value: any) { if ('default' in union) { - return union.default + return typeof value === 'function' ? union.default : Clone(union.default) } else { const schema = SelectUnion(union, references, value) return Cast(schema, references, value) diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 697a37234..2b2ef63ea 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import { HasPropertyKey, IsString } from '../guard/index' import { Check } from '../check/index' +import { Clone } from '../clone/index' import { Deref } from '../deref/index' import { TemplateLiteralGenerate, IsTemplateLiteralFinite } from '../../type/template-literal/index' import { PatternStringExact, PatternNumberExact } from '../../type/patterns/index' @@ -78,11 +79,17 @@ export class ValueCreateError extends TypeBoxError { } } // ------------------------------------------------------------------ +// Default +// ------------------------------------------------------------------ +function FromDefault(value: unknown) { + return typeof value === 'function' ? value : Clone(value) +} +// ------------------------------------------------------------------ // Create // ------------------------------------------------------------------ function FromAny(schema: TAny, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return {} } @@ -93,7 +100,7 @@ function FromArray(schema: TArray, references: TSchema[]): any { } else if ('contains' in schema && !HasPropertyKey(schema, 'default')) { throw new ValueCreateError(schema, 'Array with the contains constraint requires a default value') } else if ('default' in schema) { - return schema.default + return FromDefault(schema.default) } else if (schema.minItems !== undefined) { return Array.from({ length: schema.minItems }).map((item) => { return Visit(schema.items, references) @@ -104,28 +111,28 @@ function FromArray(schema: TArray, references: TSchema[]): any { } function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return (async function* () {})() } } function FromBigInt(schema: TBigInt, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return BigInt(0) } } function FromBoolean(schema: TBoolean, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return false } } function FromConstructor(schema: TConstructor, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { const value = Visit(schema.returns, references) as any if (typeof value === 'object' && !Array.isArray(value)) { @@ -144,7 +151,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[]): any { } function FromDate(schema: TDate, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.minimumTimestamp !== undefined) { return new Date(schema.minimumTimestamp) } else { @@ -153,14 +160,14 @@ function FromDate(schema: TDate, references: TSchema[]): any { } function FromFunction(schema: TFunction, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return () => Visit(schema.returns, references) } } function FromInteger(schema: TInteger, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.minimum !== undefined) { return schema.minimum } else { @@ -169,7 +176,7 @@ function FromInteger(schema: TInteger, references: TSchema[]): any { } function FromIntersect(schema: TIntersect, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { // -------------------------------------------------------------- // Note: The best we can do here is attempt to instance each @@ -188,42 +195,42 @@ function FromIntersect(schema: TIntersect, references: TSchema[]): any { } function FromIterator(schema: TIterator, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return (function* () {})() } } function FromLiteral(schema: TLiteral, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return schema.const } } function FromNever(schema: TNever, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { throw new ValueCreateError(schema, 'Never types cannot be created. Consider using a default value.') } } function FromNot(schema: TNot, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { throw new ValueCreateError(schema, 'Not types must have a default value') } } function FromNull(schema: TNull, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return null } } function FromNumber(schema: TNumber, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.minimum !== undefined) { return schema.minimum } else { @@ -232,11 +239,11 @@ function FromNumber(schema: TNumber, references: TSchema[]): any { } function FromObject(schema: TObject, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { const required = new Set(schema.required) return ( - schema.default || + FromDefault(schema.default) || Object.entries(schema.properties).reduce((acc, [key, schema]) => { return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc } }, {}) @@ -245,7 +252,7 @@ function FromObject(schema: TObject, references: TSchema[]): any { } function FromPromise(schema: TPromise, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return Promise.resolve(Visit(schema.item, references)) } @@ -253,7 +260,7 @@ function FromPromise(schema: TPromise, references: TSchema[]): any { function FromRecord(schema: TRecord, references: TSchema[]): any { const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0] if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (!(keyPattern === PatternStringExact || keyPattern === PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') return propertyKeys.reduce((acc, key) => { @@ -265,14 +272,14 @@ function FromRecord(schema: TRecord, references: TSchema[]): any { } function FromRef(schema: TRef, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return Visit(Deref(schema, references), references) } } function FromRegExp(schema: TRegExp, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { throw new ValueCreateError(schema, 'RegExp types cannot be created. Consider using a default value.') } @@ -282,17 +289,17 @@ function FromString(schema: TString, references: TSchema[]): any { if (!HasPropertyKey(schema, 'default')) { throw new ValueCreateError(schema, 'String types with patterns must specify a default value') } else { - return schema.default + return FromDefault(schema.default) } } else if (schema.format !== undefined) { if (!HasPropertyKey(schema, 'default')) { throw new ValueCreateError(schema, 'String types with formats must specify a default value') } else { - return schema.default + return FromDefault(schema.default) } } else { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.minLength !== undefined) { // prettier-ignore return Array.from({ length: schema.minLength }).map(() => ' ').join('') @@ -303,7 +310,7 @@ function FromString(schema: TString, references: TSchema[]): any { } function FromSymbol(schema: TSymbol, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if ('value' in schema) { return Symbol.for(schema.value) } else { @@ -312,7 +319,7 @@ function FromSymbol(schema: TSymbol, references: TSchema[]): any { } function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } if (!IsTemplateLiteralFinite(schema)) throw new ValueCreateError(schema, 'Can only create template literals that produce a finite variants. Consider using a default value.') const generated = TemplateLiteralGenerate(schema) as string[] @@ -321,14 +328,14 @@ function FromTemplateLiteral(schema: TTemplateLiteral, references: TSchema[]) { function FromThis(schema: TThis, references: TSchema[]): any { if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateError(schema, 'Cannot create recursive type as it appears possibly infinite. Consider using a default.') if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return Visit(Deref(schema, references), references) } } function FromTuple(schema: TTuple, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } if (schema.items === undefined) { return [] @@ -338,14 +345,14 @@ function FromTuple(schema: TTuple, references: TSchema[]): any { } function FromUndefined(schema: TUndefined, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return undefined } } function FromUnion(schema: TUnion, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.anyOf.length === 0) { throw new Error('ValueCreate.Union: Cannot create Union with zero variants') } else { @@ -354,7 +361,7 @@ function FromUnion(schema: TUnion, references: TSchema[]): any { } function FromUint8Array(schema: TUint8Array, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else if (schema.minByteLength !== undefined) { return new Uint8Array(schema.minByteLength) } else { @@ -363,21 +370,21 @@ function FromUint8Array(schema: TUint8Array, references: TSchema[]): any { } function FromUnknown(schema: TUnknown, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return {} } } function FromVoid(schema: TVoid, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { return void 0 } } function FromKind(schema: TSchema, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { - return schema.default + return FromDefault(schema.default) } else { throw new Error('User defined types must specify a default value') } diff --git a/test/runtime/value/create/object.ts b/test/runtime/value/create/object.ts index 0d2ed1cad..e915a142b 100644 --- a/test/runtime/value/create/object.ts +++ b/test/runtime/value/create/object.ts @@ -68,4 +68,19 @@ describe('value/create/Object', () => { z: 3, }) }) + // ---------------------------------------------------------------- + // Mutation + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should clone defaults on assignment - no mutation', () => { + const T = Type.Object( + { + x: Type.Number(), + }, + { default: { x: 1 } }, + ) + const V = Value.Create(T) + V.x = 123 + Assert.IsEqual(T.default, { x: 1 }) + }) }) From 839967d4399ec9a0480e41d4020e1bddd6c838ae Mon Sep 17 00:00:00 2001 From: karanssj4 Date: Thu, 11 Jan 2024 19:27:59 +0530 Subject: [PATCH 228/369] Documentation (#730) - Update readme.md - minor rephrasing --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 26fd763b9..c25440666 100644 --- a/readme.md +++ b/readme.md @@ -1814,4 +1814,4 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ## Contribute -TypeBox is open to community contribution. Please ensure you submit an open issue before submitting your pull request. The TypeBox project preferences open community discussion prior to accepting new features. +TypeBox is open to community contribution. Please ensure you submit an open issue before submitting your pull request. The TypeBox project prefers open community discussion before accepting new features. From 9adcb58f22eb77d6c86963bbb680d960f5f271fd Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 12 Jan 2024 13:28:06 +0900 Subject: [PATCH 229/369] Documentation --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index c25440666..9dc77a5cc 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates in-memory Json Schema objects that infer as TypeScript types. The schematics produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime asserted using standard Json Schema validation. -This library is designed to be a runtime type system providing similar capabilities to TypeScript's programmable type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to allow Json Schema to compose with the same flexibility as TypeScript's programmable type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. License MIT From dcf48f3232a93ea7d8c8224a2ba0a8789e9a8cf7 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 13 Jan 2024 00:01:44 +0900 Subject: [PATCH 230/369] Revision 0.32.9 (#731) * Optimize Composite * Set and Composite Tests * Version --- example/typedef/typedef.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- src/index.ts | 33 ++++- src/type/composite/composite.ts | 101 +++++++++++---- src/type/helpers/helpers.ts | 6 +- src/type/keyof/keyof-property-keys.ts | 20 +-- src/type/sets/set.ts | 50 +++++--- src/type/type/json.ts | 4 +- test/runtime/compiler-ajv/composite.ts | 17 +++ test/runtime/compiler/composite.ts | 17 +++ test/runtime/type/guard/composite.ts | 111 +++++++++++++++- test/runtime/type/index.ts | 1 + test/runtime/type/sets/index.ts | 1 + test/runtime/type/sets/sets.ts | 167 +++++++++++++++++++++++++ test/static/composite.ts | 59 ++++++++- 16 files changed, 528 insertions(+), 67 deletions(-) create mode 100644 test/runtime/type/sets/index.ts create mode 100644 test/runtime/type/sets/sets.ts diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 7a86b77d7..6682e5e38 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -56,7 +56,7 @@ export interface TBoolean extends Types.TSchema { // -------------------------------------------------------------------------- export type InferUnion = T extends [infer L extends TStruct, ...infer R extends TStruct[]] - ? Types.Evaluate<{ [_ in D]: Index } & Types.Static> | InferUnion>> + ? Types.Evaluate<{ [_ in D]: Index } & Types.Static> | InferUnion>> : never export interface TUnion extends Types.TSchema { diff --git a/package-lock.json b/package-lock.json index 2fabd94e0..79f26077e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.8", + "version": "0.32.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.8", + "version": "0.32.9", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index b291d5ecb..6a27860cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.8", + "version": "0.32.9", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/index.ts b/src/index.ts index b1b6ea791..cfcd03abe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,9 +34,37 @@ export { PatternBoolean, PatternBooleanExact, PatternNumber, PatternNumberExact, export { TypeRegistry, FormatRegistry } from './type/registry/index' export { TypeGuard, ValueGuard } from './type/guard/index' export { CloneType, CloneRest } from './type/clone/type' +// ------------------------------------------------------------------ +// Error +// ------------------------------------------------------------------ export { TypeBoxError } from './type/error/index' // ------------------------------------------------------------------ -// Type +// Sets +// ------------------------------------------------------------------ +export { + SetComplement, + SetDistinct, + SetIncludes, + SetIntersect, + SetIntersectMany, + SetIsSubset, + SetUnion, + SetUnionMany, + type TSetComplement, + type TSetDistinct, + type TSetIncludes, + type TSetIntersect, + type TSetIntersectMany, + type TSetIsSubset, + type TSetUnion, + type TSetUnionMany, +} from './type/sets/index' +// ------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------ +export { Increment, type TIncrement, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' +// ------------------------------------------------------------------ +// Types // ------------------------------------------------------------------ export { Any, type TAny } from './type/any/index' export { Array, type TArray, type ArrayOptions } from './type/array/index' @@ -55,7 +83,6 @@ export { Exclude, type TExclude, type TExcludeFromMappedResult } from './type/ex export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey } from './type/extends/index' export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index' export { Function, type TFunction } from './type/function/index' -export { Increment, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' export { Index, IndexPropertyKeys, @@ -102,7 +129,7 @@ export { type TSchema, type TKind, type SchemaOptions, type TAnySchema } from '. export { type Static, type StaticDecode, type StaticEncode, type TDecodeType, type TDecodeRest, type TDecodeProperties } from './type/static/index' export { Strict } from './type/strict/index' export { String, type TString, type StringOptions, type StringFormatOption, type StringContentEncodingOption } from './type/string/index' -export { Symbol, type TSymbol, type TSymbolValue as SymbolValue } from './type/symbol/index' +export { Symbol, type TSymbol, type TSymbolValue } from './type/symbol/index' export { TemplateLiteral, TemplateLiteralSyntax, diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index b4dc847fa..b37ccb613 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -27,44 +27,95 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema } from '../schema/index' -import type { UnionToTuple, Assert, Ensure, Evaluate } from '../helpers/index' +import type { Evaluate } from '../helpers/index' +import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index' +import { IndexFromPropertyKeys, type TIndexFromPropertyKeys } from '../indexed/index' +import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from '../keyof/index' +import { type TNever } from '../never/index' import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' -import { Intersect, type TIntersect } from '../intersect/index' -import { Index, type TIndex } from '../indexed/index' -import { KeyOfPropertyKeys } from '../keyof/index' +import { SetDistinct, TSetDistinct } from '../sets/index' // ------------------------------------------------------------------ -// TCompositeKeys +// TypeGuard +// ------------------------------------------------------------------ +import { IsNever } from '../guard/type' +// ------------------------------------------------------------------ +// CompositeKeys // ------------------------------------------------------------------ // prettier-ignore -type TCompositeKeys = - T extends [infer L extends TObject, ...infer R extends TObject[]] - ? TCompositeKeys +type TCompositeKeys = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TCompositeKeys]>> : Acc +) +// prettier-ignore +function CompositeKeys(T: [...T]): TCompositeKeys { + return T.reduce((Acc, L) => { + return SetDistinct([...Acc, ...KeyOfPropertyKeys(L)]) as never + }, []) as never +} +// ------------------------------------------------------------------ +// FilterNever +// ------------------------------------------------------------------ +// prettier-ignore +type TFilterNever = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TNever + ? Acc + : TFilterNever + : Acc +) +// prettier-ignore +function FilterNever(T: [...T]): TFilterNever { + return T.filter(L => !IsNever(L)) as never +} // ------------------------------------------------------------------ -// TCompositeIndex +// CompositeProperty // ------------------------------------------------------------------ // prettier-ignore -type TCompositeIndex, K extends string[], Acc extends TProperties = {}> = - K extends [infer L extends string, ...infer R extends string[]] - ? TCompositeIndex }> +type TCompositeProperty = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TCompositeProperty]>> + : Acc +) +// prettier-ignore +function CompositeProperty(T: [...T], K: K): TCompositeProperty { + return T.reduce((Acc, L) => { + return FilterNever([...Acc, ...IndexFromPropertyKeys(L, [K])]) + }, []) as never +} +// ------------------------------------------------------------------ +// CompositeProperties +// ------------------------------------------------------------------ +// prettier-ignore +type TCompositeProperties = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? TCompositeProperties> }> : Acc +) // prettier-ignore -type TCompositeReduce = UnionToTuple> extends infer K - ? Evaluate, Assert>> - : {} // ^ indexed via intersection of T +function CompositeProperties(T: [...T], K: [...K]): TCompositeProperties { + return K.reduce((Acc, L) => { + return { ...Acc, [L]: IntersectEvaluated(CompositeProperty(T, L)) } + }, {}) as never +} // ------------------------------------------------------------------ -// TComposite +// Composite // ------------------------------------------------------------------ // prettier-ignore -export type TComposite = TIntersect extends TIntersect - ? Ensure>> - : Ensure> +type TCompositeEvaluate< + T extends TSchema[], + K extends PropertyKey[] = TCompositeKeys, + P extends TProperties = Evaluate>, + R extends TObject = TObject

      +> = R +// prettier-ignore +export type TComposite = TCompositeEvaluate -/** `[Json]` Creates a Composite object type */ -export function Composite(T: [...T], options?: ObjectOptions): TComposite { - const intersect: TSchema = Intersect(T, {}) - const keys = KeyOfPropertyKeys(intersect) as string[] - const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties) - return Object(properties, options) as TComposite +// prettier-ignore +export function Composite(T: [...T], options: ObjectOptions = {}): TComposite { + const K = CompositeKeys(T) + const P = CompositeProperties(T, K) + const R = Object(P, options) + return R as TComposite } diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts index c241ef343..807e10c07 100644 --- a/src/type/helpers/helpers.ts +++ b/src/type/helpers/helpers.ts @@ -56,10 +56,10 @@ type IncrementStep = T extends IncrementBase['m'] : `${IncrementTake}${R}` : never type IncrementReverse = T extends `${infer L}${infer R}` ? `${IncrementReverse}${L}` : T -export type Increment = IncrementReverse>> +export type TIncrement = IncrementReverse>> /** Increments the given string value + 1 */ -export function Increment(T: T): Increment { - return (parseInt(T) + 1).toString() as Increment +export function Increment(T: T): TIncrement { + return (parseInt(T) + 1).toString() as TIncrement } // ------------------------------------------------------------------ // Helper: Type Asserts diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 34a16956a..8f7c17e3b 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -27,7 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema } from '../schema/index' -import { type ZeroString, type UnionToTuple, Increment } from '../helpers/index' +import { type ZeroString, type UnionToTuple, type TIncrement } from '../helpers/index' import type { TRecursive } from '../recursive/index' import type { TIntersect } from '../intersect/index' import type { TUnion } from '../union/index' @@ -59,31 +59,31 @@ function FromRest(T: [...T]): TFromRest { // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type FromIntersect< +type TFromIntersect< T extends TSchema[], C extends PropertyKey[][] = TFromRest, R extends PropertyKey[] = TSetUnionMany > = R // prettier-ignore -function FromIntersect(T: [...T]): FromIntersect { +function FromIntersect(T: [...T]): TFromIntersect { const C = FromRest(T) as PropertyKey[][] const R = SetUnionMany(C) - return R as FromIntersect + return R as TFromIntersect } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type FromUnion< +type TFromUnion< T extends TSchema[], C extends PropertyKey[][] = TFromRest, R extends PropertyKey[] = TSetIntersectMany > = R // prettier-ignore -function FromUnion(T: [...T]): FromUnion { +function FromUnion(T: [...T]): TFromUnion { const C = FromRest(T) as PropertyKey[][] const R = SetIntersectMany(C) - return R as FromUnion + return R as TFromUnion } // ------------------------------------------------------------------ // FromTuple @@ -91,7 +91,7 @@ function FromUnion(T: [...T]): FromUnion { // prettier-ignore type TFromTuple = T extends [infer _ extends TSchema, ...infer R extends TSchema[]] - ? TFromTuple, [...Acc, I]> + ? TFromTuple, [...Acc, I]> : Acc // prettier-ignore function FromTuple(T: [...T]): TFromTuple { @@ -142,8 +142,8 @@ function FromPatternProperties(patternProperties: Record): // prettier-ignore export type TKeyOfPropertyKeys = ( T extends TRecursive ? TKeyOfPropertyKeys : - T extends TIntersect ? FromIntersect : - T extends TUnion ? FromUnion : + T extends TIntersect ? TFromIntersect : + T extends TUnion ? TFromUnion : T extends TTuple ? TFromTuple : T extends TArray ? TFromArray : T extends TObject ? TFromProperties : diff --git a/src/type/sets/set.ts b/src/type/sets/set.ts index f2456a728..70b1e02ee 100644 --- a/src/type/sets/set.ts +++ b/src/type/sets/set.ts @@ -37,7 +37,7 @@ export type TSetIncludes = ( : TSetIncludes : false ) -/** Returns true if element S is in the set of T */ +/** Returns true if element right is in the set of left */ // prettier-ignore export function SetIncludes(T: [...T], S: S): TSetIncludes { return T.includes(S) as TSetIncludes @@ -46,16 +46,16 @@ export function SetIncludes(T: [ // SetIsSubset // ------------------------------------------------------------------ // prettier-ignore -export type SetIsSubset = ( +export type TSetIsSubset = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? TSetIncludes extends true - ? SetIsSubset + ? TSetIsSubset : false : true ) -/** Returns true if T is a subset of S */ -export function SetIsSubset(T: [...T], S: [...S]): SetIsSubset { - return T.every((L) => SetIncludes(S, L)) as SetIsSubset +/** Returns true if left is a subset of right */ +export function SetIsSubset(T: [...T], S: [...S]): TSetIsSubset { + return T.every((L) => SetIncludes(S, L)) as TSetIsSubset } // ------------------------------------------------------------------ // SetDistinct @@ -78,7 +78,7 @@ export function SetDistinct(T: [...T]): TSetDistinct export type TSetIntersect = ( T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? TSetIncludes extends true - ? TSetIntersect + ? TSetIntersect : TSetIntersect : Acc ) @@ -90,14 +90,12 @@ export function SetIntersect(T // SetUnion // ------------------------------------------------------------------ // prettier-ignore -export type TSetUnion = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? TSetUnion - : Acc +export type TSetUnion = ( + [...T, ...S] ) /** Returns the Union of the given sets */ export function SetUnion(T: [...T], S: [...S]): TSetUnion { - return [...T, ...S] as never + return [...T, ...S] } // ------------------------------------------------------------------ // SetComplement @@ -119,17 +117,35 @@ export function SetComplement( // SetIntersectMany // ------------------------------------------------------------------ // prettier-ignore -export type TSetIntersectMany = ( - T extends [infer L extends PropertyKey[]] ? L : +type TSetIntersectManyResolve = ( T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] - ? TSetIntersectMany> + ? TSetIntersectManyResolve> : Acc ) -/** Returns the Intersect of multiple sets */ +// prettier-ignore +function SetIntersectManyResolve(T: [...T], Init: Acc): TSetIntersectManyResolve { + return T.reduce((Acc: PropertyKey[], L: PropertyKey[]) => { + return SetIntersect(Acc, L) + }, Init) as never +} +// prettier-ignore +export type TSetIntersectMany = ( + T extends [infer L extends PropertyKey[]] + ? L + // Use left to initialize the accumulator for resolve + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? TSetIntersectManyResolve + : [] +) // prettier-ignore export function SetIntersectMany(T: [...T]): TSetIntersectMany { return ( - T.length === 1 ? T[0] : T.reduce((Acc, L) => [...SetIntersect(Acc, L)], []) + T.length === 1 + ? T[0] + // Use left to initialize the accumulator for resolve + : T.length > 1 + ? SetIntersectManyResolve(T.slice(1), T[0]) + : [] ) as TSetIntersectMany } // ------------------------------------------------------------------ diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 145d93e5b..772ff0ee9 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -130,8 +130,8 @@ export class JsonTypeBuilder { return Capitalize(schema, options) } /** `[Json]` Creates a Composite object type */ - public Composite(objects: [...T], options?: ObjectOptions): TComposite { - return Composite(objects, options) as any // (error) TS 5.4.0-dev - review TComposite implementation + public Composite(schemas: [...T], options?: ObjectOptions): TComposite { + return Composite(schemas, options) // (error) TS 5.4.0-dev - review TComposite implementation } /** `[JavaScript]` Creates a readonly const type from the given value. */ public Const(value: T, options: SchemaOptions = {}): TConst { diff --git a/test/runtime/compiler-ajv/composite.ts b/test/runtime/compiler-ajv/composite.ts index 41e8a8ac9..3d1d41a62 100644 --- a/test/runtime/compiler-ajv/composite.ts +++ b/test/runtime/compiler-ajv/composite.ts @@ -91,4 +91,21 @@ describe('compiler-ajv/Composite', () => { const B = Type.Composite([T]) Assert.IsEqual(A, B) }) + // prettier-ignore + it('Should composite intersection', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }) + ]), + ]) + Ok(T, { x: 1, y: 2, z: 3 }) + Fail(T, { x: 1, y: 2, z: '3' }) + Fail(T, { x: 1, y: 2 }) + }) }) diff --git a/test/runtime/compiler/composite.ts b/test/runtime/compiler/composite.ts index 3186c8302..960a1d7f1 100644 --- a/test/runtime/compiler/composite.ts +++ b/test/runtime/compiler/composite.ts @@ -81,4 +81,21 @@ describe('compiler/Composite', () => { Fail(T, { x: { x: '1' }, y: { x: '' } }) Fail(T, { x: { x: 1 }, y: { x: 1 } }) }) + // prettier-ignore + it('Should composite intersection', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }) + ]), + ]) + Ok(T, { x: 1, y: 2, z: 3 }) + Fail(T, { x: 1, y: 2, z: '3' }) + Fail(T, { x: 1, y: 2 }) + }) }) diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index e8a3f21ac..d3e9557a2 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -36,8 +36,8 @@ describe('type/guard/TComposite', () => { Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) Assert.IsEqual(T.required, undefined) }) + // prettier-ignore it('Should produce required property if some composited properties are not optional', () => { - // prettier-ignore const T = Type.Composite([ Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() }) @@ -45,12 +45,119 @@ describe('type/guard/TComposite', () => { Assert.IsFalse(TypeGuard.IsOptional(T.properties.x)) Assert.IsTrue(T.required!.includes('x')) }) + // prettier-ignore it('Should preserve single optional property', () => { - // prettier-ignore const T = Type.Composite([ Type.Object({ x: Type.Optional(Type.Number()) }), ]) Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) Assert.IsEqual(T.required, undefined) }) + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + // prettier-ignore + it('Should composite Intersect 1', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + })) + }) + // prettier-ignore + it('Should composite Intersect 2', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ x: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.Intersect([Type.Number(), Type.Number()]), Type.Number()]) + })) + }) + // prettier-ignore + it('Should composite Intersect 3', () => { + const T = Type.Composite([ + Type.Number(), + Type.Boolean() + ]) + Assert.IsEqual(T, Type.Object({})) + }) + // prettier-ignore + it('Should composite Intersect 4', () => { + const T = Type.Composite([ + Type.Number(), + Type.Boolean(), + Type.Object({ x: Type.String() }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.String() + })) + }) + // prettier-ignore + it('Should composite Intersect 5', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.String()) }), + Type.Object({ x: Type.String() }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.String(), Type.String()]) + })) + }) + // prettier-ignore + it('Should composite Intersect 6', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.String()) }), + Type.Object({ x: Type.Optional(Type.String()) }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Optional(Type.Intersect([Type.String(), Type.String()])) + })) + }) + // ---------------------------------------------------------------- + // Union + // ---------------------------------------------------------------- + // prettier-ignore + it('Should composite Union 1', () => { + const T = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Union([ + Type.Object({ z: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + z: Type.Intersect([Type.Union([Type.Never(), Type.Never()]), Type.Number()]) + })) + }) + // prettier-ignore + it('Should composite Union 2', () => { + const T = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }), + ]), + Type.Union([ + Type.Object({ x: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.Union([Type.Number(), Type.Number()]), Type.Number()]) + })) + }) }) diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index f5cfcd709..d05528481 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -4,5 +4,6 @@ import './guard/index' import './intrinsic/index' import './normalize/index' import './registry/index' +import './sets/index' import './template-literal/index' import './value/index' diff --git a/test/runtime/type/sets/index.ts b/test/runtime/type/sets/index.ts new file mode 100644 index 000000000..2b3525728 --- /dev/null +++ b/test/runtime/type/sets/index.ts @@ -0,0 +1 @@ +import './sets' diff --git a/test/runtime/type/sets/sets.ts b/test/runtime/type/sets/sets.ts new file mode 100644 index 000000000..6bfef3c09 --- /dev/null +++ b/test/runtime/type/sets/sets.ts @@ -0,0 +1,167 @@ +import * as Type from '@sinclair/typebox' +import { Assert } from '../../assert' + +describe('type/sets', () => { + // ---------------------------------------------------------------- + // Distinct + // ---------------------------------------------------------------- + it('Should Distinct', () => { + const R = Type.SetDistinct([1, 1, 2, 2]) + Assert.IsEqual(R, [1, 2]) + }) + // ---------------------------------------------------------------- + // Includes + // ---------------------------------------------------------------- + it('Should Includes 1', () => { + const R = Type.SetIncludes([1, 2, 3, 4], 1) + Assert.IsTrue(R) + }) + it('Should Includes 2', () => { + const R = Type.SetIncludes([1, 2, 3, 4], 7) + Assert.IsFalse(R) + }) + // ---------------------------------------------------------------- + // IsSubset + // ---------------------------------------------------------------- + it('Should IsSubset 1', () => { + const R = Type.SetIsSubset([1, 2], [1, 2]) + Assert.IsTrue(R) + }) + it('Should IsSubset 2', () => { + const R = Type.SetIsSubset([1, 2], [1, 2, 3]) + Assert.IsTrue(R) + }) + it('Should IsSubset 3', () => { + const R = Type.SetIsSubset([1, 2], [1]) + Assert.IsFalse(R) + }) + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + it('Should Intersect 1', () => { + const R = Type.SetIntersect([1, 2], [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should Intersect 2', () => { + const R = Type.SetIntersect([1], [1, 2]) + Assert.IsEqual(R, [1]) + }) + it('Should Intersect 3', () => { + const R = Type.SetIntersect([1, 2], [1]) + Assert.IsEqual(R, [1]) + }) + it('Should Intersect 4', () => { + const R = Type.SetIntersect([], [1]) + Assert.IsEqual(R, []) + }) + it('Should Intersect 5', () => { + const R = Type.SetIntersect([1], []) + Assert.IsEqual(R, []) + }) + it('Should Intersect 6', () => { + const R = Type.SetIntersect([1], [2]) + Assert.IsEqual(R, []) + }) + // ---------------------------------------------------------------- + // Union + // ---------------------------------------------------------------- + it('Should Union 1', () => { + const R = Type.SetUnion([1, 2], [1, 2]) + Assert.IsEqual(R, [1, 2, 1, 2]) + }) + it('Should Union 2', () => { + const R = Type.SetUnion([1], [1, 2]) + Assert.IsEqual(R, [1, 1, 2]) + }) + it('Should Union 3', () => { + const R = Type.SetUnion([1, 2], [1]) + Assert.IsEqual(R, [1, 2, 1]) + }) + it('Should Union 4', () => { + const R = Type.SetUnion([], [1]) + Assert.IsEqual(R, [1]) + }) + it('Should Union 5', () => { + const R = Type.SetUnion([1], []) + Assert.IsEqual(R, [1]) + }) + // ---------------------------------------------------------------- + // Complement + // ---------------------------------------------------------------- + it('Should Complement 1', () => { + const R = Type.SetComplement([1, 2, 3, 4], [2, 3]) + Assert.IsEqual(R, [1, 4]) + }) + it('Should Complement 2', () => { + const R = Type.SetComplement([2, 3], [1, 2, 3, 4]) + Assert.IsEqual(R, []) + }) + // ---------------------------------------------------------------- + // IntersectMany + // ---------------------------------------------------------------- + it('Should IntersectMany 1', () => { + const R = Type.SetIntersectMany([[1, 2, 3], [1, 2], [1]] as const) + Assert.IsEqual(R, [1]) + }) + it('Should IntersectMany 2', () => { + const R = Type.SetIntersectMany([[1], [1, 2], [1, 2, 3]] as const) + Assert.IsEqual(R, [1]) + }) + it('Should IntersectMany 3', () => { + const R = Type.SetIntersectMany([ + [1, 2], + [1, 2], + ] as const) + Assert.IsEqual(R, [1, 2]) + }) + it('Should IntersectMany 4', () => { + const R = Type.SetIntersectMany([[1], [2]] as const) + Assert.IsEqual(R, []) + }) + it('Should IntersectMany 5', () => { + const R = Type.SetIntersectMany([[1], []] as const) + Assert.IsEqual(R, []) + }) + it('Should IntersectMany 6', () => { + const R = Type.SetIntersectMany([[], [1]] as const) + Assert.IsEqual(R, []) + }) + it('Should IntersectMany 7', () => { + const R = Type.SetIntersectMany([[1], [1], [1], [1], []] as const) + Assert.IsEqual(R, []) + }) + it('Should IntersectMany 8', () => { + const R = Type.SetIntersectMany([[], [1], [1], [1], [1]] as const) + Assert.IsEqual(R, []) + }) + // ---------------------------------------------------------------- + // UnionMany + // ---------------------------------------------------------------- + it('Should UnionMany 1', () => { + const R = Type.SetUnionMany([[1, 2, 3], [1, 2], [1]] as const) + Assert.IsEqual(R, [1, 2, 3, 1, 2, 1]) + }) + it('Should UnionMany 2', () => { + const R = Type.SetUnionMany([[1], [1, 2], [1, 2, 3]] as const) + Assert.IsEqual(R, [1, 1, 2, 1, 2, 3]) + }) + it('Should UnionMany 3', () => { + const R = Type.SetUnionMany([ + [1, 2], + [1, 2], + ] as const) + Assert.IsEqual(R, [1, 2, 1, 2]) + }) + it('Should UnionMany 4', () => { + const R = Type.SetUnionMany([[1], [2]] as const) + Assert.IsEqual(R, [1, 2]) + }) + it('Should UnionMany 5', () => { + const R = Type.SetUnionMany([[1], []] as const) + Assert.IsEqual(R, [1]) + }) + it('Should UnionMany 6', () => { + const R = Type.SetUnionMany([[], [1]] as const) + Assert.IsEqual(R, [1]) + }) +}) diff --git a/test/static/composite.ts b/test/static/composite.ts index 70751619e..e2b440f41 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' +import { Type, TOptional, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' // ---------------------------------------------------------------------------- // Overlapping - Non Varying @@ -127,3 +127,60 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' Type.Object({ x: Type.Boolean() }) ]) } +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T: TObject<{ + x: TNumber; + y: TNumber; + z: TNumber; + }> = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }) + ]) + ]) +} +// prettier-ignore +{ + const T: TObject<{ + x: TIntersect<[TNumber, TNumber]>; + y: TIntersect<[TNumber, TNumber]>; + }> = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]) + ]) +} +// prettier-ignore +{ + const T: TObject<{ + x: TIntersect<[TNumber, TNumber]>; + }> = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }), + ]) + ]) +} +// prettier-ignore +{ + const T: TObject<{ + x: TOptional>; + }> = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }), + ]) + ]) +} From 0c0da45394e5ecb043a6fc6fee7d24a13bc9e4fa Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 18 Jan 2024 01:58:45 +0900 Subject: [PATCH 231/369] Revision 0.32.10 (#734) * Export Partial and Required Type Infrastructure * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/partial/partial.ts | 26 ++++++++++++-------------- src/type/required/required.ts | 25 ++++++++++++------------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79f26077e..87202da08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.9", + "version": "0.32.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.9", + "version": "0.32.10", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 6a27860cb..0f1575d2e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.9", + "version": "0.32.10", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index b46ee4af9..961d36f25 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -52,7 +52,7 @@ import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' // prettier-ignore type TFromRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> + ? TFromRest]> : Acc ) // prettier-ignore @@ -80,27 +80,25 @@ function FromProperties(T: T) { // PartialResolve // ------------------------------------------------------------------ // prettier-ignore -type PartialResolve = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - TObject<{}> -) -// prettier-ignore -function PartialResolve(T: T): PartialResolve { +function PartialResolve(T: T): TPartial { return ( IsIntersect(T) ? Intersect(FromRest(T.allOf)) : IsUnion(T) ? Union(FromRest(T.anyOf)) : IsObject(T) ? Object(FromProperties(T.properties)) : Object({}) - ) as PartialResolve + ) as TPartial } // ------------------------------------------------------------------ // TPartial // ------------------------------------------------------------------ -export type TPartial = PartialResolve - +// prettier-ignore +export type TPartial = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) /** `[Json]` Constructs a type where all properties are optional */ export function Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ @@ -110,5 +108,5 @@ export function Partial(T: TSchema, options: SchemaOptions = {}): any { if (IsMappedResult(T)) return PartialFromMappedResult(T, options) const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema const R = CloneType(PartialResolve(T), options) - return { ...D, ...R } as any + return { ...D, ...R } as never } diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 1066289b3..56220759b 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -53,7 +53,7 @@ import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' // prettier-ignore type TFromRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> + ? TFromRest]> : Acc ) // prettier-ignore @@ -80,28 +80,27 @@ function FromProperties(T: T) { // ------------------------------------------------------------------ // RequiredResolve // ------------------------------------------------------------------ + // prettier-ignore -type TRequiredResolve = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - TObject<{}> -) -// prettier-ignore -function RequiredResolve(T: T): TRequiredResolve { +function RequiredResolve(T: T): TRequired { return ( IsIntersect(T) ? Intersect(FromRest(T.allOf)) : IsUnion(T) ? Union(FromRest(T.anyOf)) : IsObject(T) ? Object(FromProperties(T.properties)) : Object({}) - ) as TRequiredResolve + ) as TRequired } // ------------------------------------------------------------------ // TRequired // ------------------------------------------------------------------ -export type TRequired = TRequiredResolve - +// prettier-ignore +export type TRequired = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) /** `[Json]` Constructs a type where all properties are required */ export function Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ From cc068713a5d6bd060617b01de1f30c3dee6f266d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 21 Jan 2024 03:23:24 +0900 Subject: [PATCH 232/369] Revision 0.32.11 (#738) * Optimize Extract and Exclude * Version --- package-lock.json | 4 +- package.json | 2 +- src/index.ts | 6 ++- .../exclude/exclude-from-mapped-result.ts | 14 +++---- .../exclude/exclude-from-template-literal.ts | 39 +++++++++++++++++++ src/type/exclude/exclude.ts | 37 ++++++++---------- src/type/exclude/index.ts | 1 + .../extract/extract-from-mapped-result.ts | 14 +++---- .../extract/extract-from-template-literal.ts | 39 +++++++++++++++++++ src/type/extract/extract.ts | 34 ++++++++-------- src/type/extract/index.ts | 1 + src/type/template-literal/union.ts | 22 ++++++++--- src/type/type/json.ts | 8 +++- test/static/exclude.ts | 15 +++++++ test/static/extract.ts | 17 ++++++++ 15 files changed, 188 insertions(+), 65 deletions(-) create mode 100644 src/type/exclude/exclude-from-template-literal.ts create mode 100644 src/type/extract/extract-from-template-literal.ts diff --git a/package-lock.json b/package-lock.json index 87202da08..292708f95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.10", + "version": "0.32.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.10", + "version": "0.32.11", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 0f1575d2e..26107da3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.10", + "version": "0.32.11", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/index.ts b/src/index.ts index cfcd03abe..2108648d5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -79,9 +79,9 @@ export { ConstructorParameters, type TConstructorParameters } from './type/const export { Date, type TDate, type DateOptions } from './type/date/index' export { Deref, type TDeref } from './type/deref/index' export { Enum, type TEnum } from './type/enum/index' -export { Exclude, type TExclude, type TExcludeFromMappedResult } from './type/exclude/index' +export { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from './type/exclude/index' export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey } from './type/extends/index' -export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index' +export { Extract, type TExtract, type TExtractFromMappedResult, type TExtractFromTemplateLiteral } from './type/extract/index' export { Function, type TFunction } from './type/function/index' export { Index, @@ -136,6 +136,7 @@ export { TemplateLiteralGenerate, TemplateLiteralParse, TemplateLiteralParseExact, + TemplateLiteralToUnion, IsTemplateLiteralFinite, TemplateLiteralExpressionGenerate, IsTemplateLiteralExpressionFinite, @@ -143,6 +144,7 @@ export { type TTemplateLiteralSyntax, type TTemplateLiteralGenerate, type TTemplateLiteralKind, + type TTemplateLiteralToUnion, type TIsTemplateLiteralFinite, } from './type/template-literal/index' export { Transform, TransformDecodeBuilder, TransformEncodeBuilder, type TTransform, type TransformOptions, type TransformFunction } from './type/transform/index' diff --git a/src/type/exclude/exclude-from-mapped-result.ts b/src/type/exclude/exclude-from-mapped-result.ts index a8934686f..3fe927fde 100644 --- a/src/type/exclude/exclude-from-mapped-result.ts +++ b/src/type/exclude/exclude-from-mapped-result.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TSchema, SchemaOptions } from '../schema/index' +import type { TSchema } from '../schema/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Exclude, type TExclude } from './exclude' @@ -45,9 +45,9 @@ type TFromProperties< function FromProperties< P extends TProperties, T extends TSchema ->(P: P, U: T, options: SchemaOptions): TFromProperties { +>(P: P, U: T): TFromProperties { return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Exclude(P[K2], U, options) } + return {...Acc, [K2]: Exclude(P[K2], U) } }, {}) as TFromProperties } // ------------------------------------------------------------------ @@ -64,8 +64,8 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult, T extends TSchema ->(R: R, T: T, options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, T, options) as TFromMappedResult +>(R: R, T: T): TFromMappedResult { + return FromProperties(R.properties, T) as TFromMappedResult } // ------------------------------------------------------------------ // ExcludeFromMappedResult @@ -83,7 +83,7 @@ export function ExcludeFromMappedResult< R extends TMappedResult, T extends TSchema, P extends TProperties = TFromMappedResult ->(R: R, T: T, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, T, options) as unknown as P +>(R: R, T: T): TMappedResult

      { + const P = FromMappedResult(R, T) as unknown as P return MappedResult(P) } diff --git a/src/type/exclude/exclude-from-template-literal.ts b/src/type/exclude/exclude-from-template-literal.ts new file mode 100644 index 000000000..707a6c254 --- /dev/null +++ b/src/type/exclude/exclude-from-template-literal.ts @@ -0,0 +1,39 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { TExclude, Exclude } from './exclude' +import { TemplateLiteralToUnion, type TTemplateLiteral, type TTemplateLiteralToUnion } from '../template-literal/index' + +// prettier-ignore +export type TExcludeFromTemplateLiteral = ( + TExclude, R> +) +export function ExcludeFromTemplateLiteral(L: L, R: R): TExcludeFromTemplateLiteral { + return Exclude(TemplateLiteralToUnion(L), R) as never +} diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index d82bdf33a..021caf156 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -27,32 +27,23 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' -import type { UnionToTuple, AssertRest, AssertType, Assert } from '../helpers/index' +import type { UnionToTuple, AssertRest, AssertType } from '../helpers/index' import type { TMappedResult } from '../mapped/index' -import { TemplateLiteralToUnion, type TTemplateLiteral } from '../template-literal/index' +import { type TTemplateLiteral } from '../template-literal/index' import { Union, type TUnion } from '../union/index' import { Never, type TNever } from '../never/index' -import { type TLiteral } from '../literal/index' import { type Static } from '../static/index' import { type TUnionEvaluated } from '../union/index' import { ExtendsCheck, ExtendsResult } from '../extends/index' import { CloneType } from '../clone/type' import { ExcludeFromMappedResult, type TExcludeFromMappedResult } from './exclude-from-mapped-result' +import { ExcludeFromTemplateLiteral, type TExcludeFromTemplateLiteral } from './exclude-from-template-literal' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' // ------------------------------------------------------------------ -// ExcludeTemplateLiteral -// ------------------------------------------------------------------ -// prettier-ignore -type TExcludeTemplateLiteralResult = TUnionEvaluated }[T]>>> -// prettier-ignore -type TExcludeTemplateLiteral = ( - Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never -) -// ------------------------------------------------------------------ // ExcludeRest // ------------------------------------------------------------------ // prettier-ignore @@ -69,19 +60,23 @@ function ExcludeRest(L: [...L], R: R) { // ------------------------------------------------------------------ // prettier-ignore export type TExclude = ( - L extends TMappedResult ? TExcludeFromMappedResult : - L extends TTemplateLiteral ? TExcludeTemplateLiteral : L extends TUnion ? TExcludeRest : L extends R ? TNever : L ) /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ -export function Exclude(L: L, R: R, options: SchemaOptions = {}): TExclude { +export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromTemplateLiteral +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude +/** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ +export function Exclude(L: TSchema, R: TSchema, options: SchemaOptions = {}): any { + // overloads + if (IsTemplateLiteral(L)) return CloneType(ExcludeFromTemplateLiteral(L, R), options) + if (IsMappedResult(L)) return CloneType(ExcludeFromMappedResult(L, R), options) // prettier-ignore - return CloneType(( - IsMappedResult(L) ? ExcludeFromMappedResult(L, R, options) : - IsTemplateLiteral(L) ? Exclude(TemplateLiteralToUnion(L), R) : - IsTemplateLiteral(R) ? Exclude(L, TemplateLiteralToUnion(R)) : - IsUnion(L) ? ExcludeRest(L.anyOf, R) : + return CloneType( + IsUnion(L) ? ExcludeRest(L.anyOf, R) : ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : L - ), options) as never + , options) } diff --git a/src/type/exclude/index.ts b/src/type/exclude/index.ts index 335e87a04..610ff43a2 100644 --- a/src/type/exclude/index.ts +++ b/src/type/exclude/index.ts @@ -27,4 +27,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './exclude-from-mapped-result' +export * from './exclude-from-template-literal' export * from './exclude' diff --git a/src/type/extract/extract-from-mapped-result.ts b/src/type/extract/extract-from-mapped-result.ts index c21ed3567..65d244162 100644 --- a/src/type/extract/extract-from-mapped-result.ts +++ b/src/type/extract/extract-from-mapped-result.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TSchema, SchemaOptions } from '../schema/index' +import type { TSchema } from '../schema/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Extract, type TExtract } from './extract' @@ -45,9 +45,9 @@ type TFromProperties< function FromProperties< P extends TProperties, T extends TSchema ->(P: P, T: T, options: SchemaOptions): TFromProperties { +>(P: P, T: T): TFromProperties { return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Extract(P[K2], T, options) } + return {...Acc, [K2]: Extract(P[K2], T) } }, {}) as TFromProperties } // ------------------------------------------------------------------ @@ -64,8 +64,8 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult, T extends TSchema ->(R: R, T: T, options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, T, options) as TFromMappedResult +>(R: R, T: T): TFromMappedResult { + return FromProperties(R.properties, T) as TFromMappedResult } // ------------------------------------------------------------------ // ExtractFromMappedResult @@ -83,7 +83,7 @@ export function ExtractFromMappedResult< R extends TMappedResult, T extends TSchema, P extends TProperties = TFromMappedResult ->(R: R, T: T, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, T, options) as unknown as P +>(R: R, T: T): TMappedResult

      { + const P = FromMappedResult(R, T) as unknown as P return MappedResult(P) } diff --git a/src/type/extract/extract-from-template-literal.ts b/src/type/extract/extract-from-template-literal.ts new file mode 100644 index 000000000..82cc52dd9 --- /dev/null +++ b/src/type/extract/extract-from-template-literal.ts @@ -0,0 +1,39 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import { Extract, type TExtract } from './extract' +import { TemplateLiteralToUnion, type TTemplateLiteral, type TTemplateLiteralToUnion } from '../template-literal/index' + +// prettier-ignore +export type TExtractFromTemplateLiteral = ( + TExtract, R> +) +export function ExtractFromTemplateLiteral(L: L, R: R): TExtractFromTemplateLiteral { + return Extract(TemplateLiteralToUnion(L), R) as never +} diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index a1f6f4d54..40d7e2d3d 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -27,29 +27,23 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' -import type { Assert, AssertRest, AssertType, UnionToTuple } from '../helpers/index' +import type { AssertRest, AssertType, UnionToTuple } from '../helpers/index' import type { TMappedResult } from '../mapped/index' -import { TemplateLiteralToUnion, type TTemplateLiteral } from '../template-literal/index' -import { type TLiteral } from '../literal/index' import { Union, type TUnion } from '../union/index' import { type Static } from '../static/index' import { Never, type TNever } from '../never/index' import { type TUnionEvaluated } from '../union/index' +import { type TTemplateLiteral } from '../template-literal/index' import { ExtendsCheck, ExtendsResult } from '../extends/index' import { CloneType } from '../clone/type' import { ExtractFromMappedResult, type TExtractFromMappedResult } from './extract-from-mapped-result' +import { ExtractFromTemplateLiteral, type TExtractFromTemplateLiteral } from './extract-from-template-literal' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' -// ------------------------------------------------------------------ -// ExtractTemplateLiteral -// ------------------------------------------------------------------ -// prettier-ignore -type TExtractTemplateLiteralResult = TUnionEvaluated }[T]>>> -// prettier-ignore -type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never + // ------------------------------------------------------------------ // ExtractRest // ------------------------------------------------------------------ @@ -67,19 +61,23 @@ function ExtractRest(L: [...L], R: R) { // ------------------------------------------------------------------ // prettier-ignore export type TExtract = ( - L extends TMappedResult ? TExtractFromMappedResult : - L extends TTemplateLiteral ? TExtractTemplateLiteral : L extends TUnion ? TExtractRest : L extends U ? L : TNever ) /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ -export function Extract(L: L, R: R, options: SchemaOptions = {}): TExtract { +export function Extract(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(type: L, union: R, options?: SchemaOptions): TExtractFromTemplateLiteral +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(type: L, union: R, options?: SchemaOptions): TExtract +/** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ +export function Extract(L: TSchema, R: TSchema, options: SchemaOptions = {}): any { + // overloads + if (IsTemplateLiteral(L)) return CloneType(ExtractFromTemplateLiteral(L, R), options) + if (IsMappedResult(L)) return CloneType(ExtractFromMappedResult(L, R), options) // prettier-ignore - return CloneType(( - IsMappedResult(L) ? ExtractFromMappedResult(L, R, options) : - IsTemplateLiteral(L) ? Extract(TemplateLiteralToUnion(L), R) : - IsTemplateLiteral(R) ? Extract(L, TemplateLiteralToUnion(R) as any) : + return CloneType( IsUnion(L) ? ExtractRest(L.anyOf, R) : ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never() - ), options) as never + , options) } diff --git a/src/type/extract/index.ts b/src/type/extract/index.ts index baa79009e..38bc0a813 100644 --- a/src/type/extract/index.ts +++ b/src/type/extract/index.ts @@ -27,4 +27,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './extract-from-mapped-result' +export * from './extract-from-template-literal' export * from './extract' diff --git a/src/type/template-literal/union.ts b/src/type/template-literal/union.ts index a5a1f9240..dba883805 100644 --- a/src/type/template-literal/union.ts +++ b/src/type/template-literal/union.ts @@ -26,19 +26,31 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import type { Static } from '../static/index' import type { TTemplateLiteral } from './template-literal' -import { Union, type TUnion } from '../union/index' +import type { UnionToTuple } from '../helpers/index' +import { UnionEvaluated, type TUnionEvaluated } from '../union/index' import { Literal, type TLiteral } from '../literal/index' -import { type TString } from '../string/index' -import { type TNever } from '../never/index' import { TemplateLiteralGenerate } from './generate' // ------------------------------------------------------------------ // TemplateLiteralToUnion // ------------------------------------------------------------------ +// prettier-ignore +export type TTemplateLiteralToUnionLiteralArray = ( + T extends [infer L extends string, ...infer R extends string[]] + ? TTemplateLiteralToUnionLiteralArray]> + : Acc +) +// prettier-ignore +export type TTemplateLiteralToUnion< + T extends TTemplateLiteral, + U extends string[] = UnionToTuple> +> = TUnionEvaluated> + /** Returns a Union from the given TemplateLiteral */ -export function TemplateLiteralToUnion(schema: TTemplateLiteral): TNever | TString | TUnion { +export function TemplateLiteralToUnion(schema: TTemplateLiteral): TTemplateLiteralToUnion { const R = TemplateLiteralGenerate(schema) as string[] const L = R.map((S) => Literal(S)) - return Union(L) + return UnionEvaluated(L) } diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 772ff0ee9..af79c93a4 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -33,9 +33,9 @@ import { Composite, type TComposite } from '../composite/index' import { Const, type TConst } from '../const/index' import { Deref, type TDeref } from '../deref/index' import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index' -import { Exclude, type TExclude, type TExcludeFromMappedResult } from '../exclude/index' +import { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from '../exclude/index' import { Extends, type TExtends, type TExtendsFromMappedKey, type TExtendsFromMappedResult } from '../extends/index' -import { Extract, type TExtract, type TExtractFromMappedResult } from '../extract/index' +import { Extract, type TExtract, type TExtractFromMappedResult, type TExtractFromTemplateLiteral } from '../extract/index' import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult } from '../indexed/index' import { Integer, type IntegerOptions, type TInteger } from '../integer/index' import { Intersect, type IntersectOptions } from '../intersect/index' @@ -148,6 +148,8 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromMappedResult /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ + public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExcludeFromTemplateLiteral + /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ public Exclude(unionType: TSchema, excludedMembers: TSchema, options: SchemaOptions = {}): any { @@ -166,6 +168,8 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ public Extract(type: L, union: R, options?: SchemaOptions): TExtractFromMappedResult /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ + public Extract(type: L, union: R, options?: SchemaOptions): TExtractFromTemplateLiteral + /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ public Extract(type: L, union: R, options?: SchemaOptions): TExtract /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ public Extract(type: TSchema, union: TSchema, options: SchemaOptions = {}): any { diff --git a/test/static/exclude.ts b/test/static/exclude.ts index 14e1a5104..a63c6c2f4 100644 --- a/test/static/exclude.ts +++ b/test/static/exclude.ts @@ -82,3 +82,18 @@ import { Expect } from './assert' const T = Type.Exclude(A, B) Expect(T).ToStatic<'C' | 'B'>() } +// https://github.com/sinclairzx81/typebox/issues/737 +{ + const U = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Object({ + type: Type.Exclude(U, Type.Literal('A')), + }) + Expect(T).ToStatic<{ type: 'B' }>() +} +{ + const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Object({ + type: Type.Exclude(U, Type.Literal('A')), + }) + Expect(T).ToStatic<{ type: 'B' | 'C' }>() +} diff --git a/test/static/extract.ts b/test/static/extract.ts index 0ed6a2a96..b9e621e95 100644 --- a/test/static/extract.ts +++ b/test/static/extract.ts @@ -81,3 +81,20 @@ import { Expect } from './assert' const T = Type.Extract(A, B) Expect(T).ToStatic<'A'>() } +// ------------------------------------------------------------------------ +// Nested (Inference Test) +// ------------------------------------------------------------------------ +{ + const U = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Object({ + type: Type.Extract(U, Type.Literal('A')), + }) + Expect(T).ToStatic<{ type: 'A' }>() +} +{ + const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Object({ + type: Type.Extract(U, Type.Union([Type.Literal('A'), Type.Literal('B')])), + }) + Expect(T).ToStatic<{ type: 'A' | 'B' }>() +} From 28164b12252a1a6e8b77bd7f27a56fc5fb6312b0 Mon Sep 17 00:00:00 2001 From: Gabriel Halle Date: Sun, 21 Jan 2024 00:18:04 -0500 Subject: [PATCH 233/369] Recursive Fix: Evaluate Record and Array (#735) * Fix some infinite recursion with static types * Adds test for #336 * Fix import path * Rename unused type parameter --- src/type/array/array.ts | 8 +++++--- src/type/record/record.ts | 2 +- test/static/recursive.ts | 23 ++++++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/type/array/array.ts b/src/type/array/array.ts index 09ca326ae..912a5ed37 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -26,10 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TSchema, SchemaOptions } from '../schema/index' +import { CloneType } from '../clone/type' +import { Ensure } from '../helpers/index' +import type { SchemaOptions, TSchema } from '../schema/index' import type { Static } from '../static/index' import { Kind } from '../symbols/index' -import { CloneType } from '../clone/type' export interface ArrayOptions extends SchemaOptions { /** The minimum number of items in this array */ @@ -45,9 +46,10 @@ export interface ArrayOptions extends SchemaOptions { /** A maximum number of contains schema matches */ maxContains?: number } +type ArrayStatic = Ensure[]> export interface TArray extends TSchema, ArrayOptions { [Kind]: 'Array' - static: Array> + static: ArrayStatic type: 'array' items: T } diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 53c4011d0..b4ff98460 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -181,7 +181,7 @@ function FromNumberKey(_: K, T: T, options // ------------------------------------------------------------------ // prettier-ignore type RecordStatic = ( - Evaluate, PropertyKey>, Static>> + Evaluate<{ [_ in Assert, PropertyKey>]: Static; }> ) // prettier-ignore export interface TRecord extends TSchema { diff --git a/test/static/recursive.ts b/test/static/recursive.ts index cfd5a76c5..c1e8bcd26 100644 --- a/test/static/recursive.ts +++ b/test/static/recursive.ts @@ -1,5 +1,5 @@ +import { Static, Type } from '@sinclair/typebox' import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' { // identity @@ -78,3 +78,24 @@ import { Type, Static } from '@sinclair/typebox' nodes: Static[] }>() } +{ + // issue: https://github.com/sinclairzx81/typebox/issues/336 + type JSONValue = + | string + | number + | null + | boolean + | { [x: string]: JSONValue } + | JSONValue[] + const R = Type.Recursive((Node) => + Type.Union([ + Type.Null(), + Type.String(), + Type.Number(), + Type.Boolean(), + Type.Record(Type.String(), Node), + Type.Array(Node), + ]), + ) + Expect(R).ToStatic() +} From 22ee96ad0431d3cf612d2b13ba1897eb4da11b4b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 21 Jan 2024 15:09:01 +0900 Subject: [PATCH 234/369] Revision 0.32.12 (#740) * Branch * Record Options | Option Asssignment Tests * Version --- package-lock.json | 4 +- package.json | 2 +- src/type/type/json.ts | 2 +- test/runtime/type/index.ts | 1 + test/runtime/type/options/assign-builder.ts | 266 ++++++++++++++++++++ test/runtime/type/options/assign.ts | 263 +++++++++++++++++++ test/runtime/type/options/index.ts | 2 + test/static/recursive.ts | 31 ++- 8 files changed, 551 insertions(+), 20 deletions(-) create mode 100644 test/runtime/type/options/assign-builder.ts create mode 100644 test/runtime/type/options/assign.ts create mode 100644 test/runtime/type/options/index.ts diff --git a/package-lock.json b/package-lock.json index 292708f95..db5bd313b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.11", + "version": "0.32.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.11", + "version": "0.32.12", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 26107da3d..f54449430 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.11", + "version": "0.32.12", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/type/json.ts b/src/type/type/json.ts index af79c93a4..3d1854005 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -273,7 +273,7 @@ export class JsonTypeBuilder { } /** `[Json]` Creates a Record type */ public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordOrObject { - return Record(key, schema) + return Record(key, schema, options) } /** `[Json]` Creates a Recursive type */ public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index d05528481..120c570f6 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -3,6 +3,7 @@ import './extends/index' import './guard/index' import './intrinsic/index' import './normalize/index' +import './options/index' import './registry/index' import './sets/index' import './template-literal/index' diff --git a/test/runtime/type/options/assign-builder.ts b/test/runtime/type/options/assign-builder.ts new file mode 100644 index 000000000..f24e04fa3 --- /dev/null +++ b/test/runtime/type/options/assign-builder.ts @@ -0,0 +1,266 @@ +import { JavaScriptTypeBuilder } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +const Type = new JavaScriptTypeBuilder() + +// TypeBuilder will proxy calls through to the raw function API +describe('type/options/AssignTypeBuilder', () => { + it('Should assign options for Any', () => { + const T = Type.Any({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Array', () => { + const T = Type.Array(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for AsyncIterator', () => { + const T = Type.AsyncIterator(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Awaited', () => { + const T = Type.Awaited(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for BigInt', () => { + const T = Type.BigInt({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Boolean', () => { + const T = Type.Boolean({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Capitalize', () => { + const T = Type.Capitalize(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Composite', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Const', () => { + const T = Type.Const(1, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Constructor', () => { + const T = Type.Constructor([], Type.Any(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Date', () => { + const T = Type.Date({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Enum', () => { + const T = Type.Enum({}, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Exclude', () => { + const T = Type.Exclude(Type.Union([Type.Literal(1), Type.Literal(2)]), Type.Literal(2), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Extends', () => { + const T = Type.Extends(Type.String(), Type.String(), Type.Number(), Type.Null(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Extract', () => { + const T = Type.Extract(Type.Union([Type.Literal(1), Type.Literal(2)]), Type.Literal(2), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Function', () => { + const T = Type.Function([], Type.Any(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Integer', () => { + const T = Type.Integer({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 1', () => { + const T = Type.Intersect([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 2', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 3', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Iterator', () => { + const T = Type.Iterator(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for KeyOf', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Literal', () => { + const T = Type.Literal(1, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Lowercase', () => { + const T = Type.Lowercase(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Mapped 1', () => { + const M = Type.Mapped(Type.TemplateLiteral('${1|2|3}'), (K) => K, { foo: 'bar' }) + Assert.IsEqual(M.foo, 'bar') + }) + it('Should assign options for Mapped 2', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), (K) => Type.Index(T, K), { foo: 'bar' }) + Assert.IsEqual(M.foo, 'bar') + }) + it('Should assign options for Never', () => { + const T = Type.Never({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Not', () => { + const T = Type.Not(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Null', () => { + const T = Type.Null({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Number', () => { + const T = Type.Number({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Object', () => { + const T = Type.Object( + { + x: Type.String(), + }, + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Omit', () => { + const T = Type.Omit( + Type.Object({ + x: Type.String(), + y: Type.String(), + }), + ['x'], + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Partial', () => { + const T = Type.Partial( + Type.Object({ + x: Type.String(), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Pick', () => { + const T = Type.Pick( + Type.Object({ + x: Type.String(), + y: Type.String(), + }), + ['x'], + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Record', () => { + const T = Type.Record(Type.String(), Type.Number(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Recursive', () => { + const T = Type.Recursive( + (This) => + Type.Object({ + x: Type.Number(), + y: Type.Array(This), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Ref 1', () => { + const T = Type.Object({ x: Type.Number() }, { $id: 'T' }) + const R = Type.Ref(T, { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for Ref 2', () => { + const R = Type.Ref('T', { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for RegExp', () => { + const R = Type.RegExp(/xyz/, { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for Partial', () => { + const T = Type.Required( + Type.Object({ + x: Type.String(), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for String', () => { + const T = Type.String({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Symbol', () => { + const T = Type.Symbol({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for TemplateLiteral 1', () => { + const T = Type.TemplateLiteral('hello${1|2|3}', { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for TemplateLiteral 2', () => { + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('1'), Type.Literal('2'), Type.Literal('3')])], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Tuple 1', () => { + const T = Type.Tuple([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Tuple 2', () => { + const T = Type.Tuple([Type.String(), Type.Number()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uint8Array', () => { + const T = Type.Uint8Array({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uncapitalize', () => { + const T = Type.Uncapitalize(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Undefined', () => { + const T = Type.Undefined({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([Type.String()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([Type.String(), Type.Boolean()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Unknown', () => { + const T = Type.Unknown({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uppercase', () => { + const T = Type.Uppercase(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Void', () => { + const T = Type.Void({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) +}) diff --git a/test/runtime/type/options/assign.ts b/test/runtime/type/options/assign.ts new file mode 100644 index 000000000..274d2f4bb --- /dev/null +++ b/test/runtime/type/options/assign.ts @@ -0,0 +1,263 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('type/options/Assign', () => { + it('Should assign options for Any', () => { + const T = Type.Any({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Array', () => { + const T = Type.Array(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for AsyncIterator', () => { + const T = Type.AsyncIterator(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Awaited', () => { + const T = Type.Awaited(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for BigInt', () => { + const T = Type.BigInt({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Boolean', () => { + const T = Type.Boolean({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Capitalize', () => { + const T = Type.Capitalize(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Composite', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Const', () => { + const T = Type.Const(1, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Constructor', () => { + const T = Type.Constructor([], Type.Any(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Date', () => { + const T = Type.Date({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Enum', () => { + const T = Type.Enum({}, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Exclude', () => { + const T = Type.Exclude(Type.Union([Type.Literal(1), Type.Literal(2)]), Type.Literal(2), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Extends', () => { + const T = Type.Extends(Type.String(), Type.String(), Type.Number(), Type.Null(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Extract', () => { + const T = Type.Extract(Type.Union([Type.Literal(1), Type.Literal(2)]), Type.Literal(2), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Function', () => { + const T = Type.Function([], Type.Any(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Integer', () => { + const T = Type.Integer({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 1', () => { + const T = Type.Intersect([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 2', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Intersect 3', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Iterator', () => { + const T = Type.Iterator(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for KeyOf', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Literal', () => { + const T = Type.Literal(1, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Lowercase', () => { + const T = Type.Lowercase(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Mapped 1', () => { + const M = Type.Mapped(Type.TemplateLiteral('${1|2|3}'), (K) => K, { foo: 'bar' }) + Assert.IsEqual(M.foo, 'bar') + }) + it('Should assign options for Mapped 2', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), (K) => Type.Index(T, K), { foo: 'bar' }) + Assert.IsEqual(M.foo, 'bar') + }) + it('Should assign options for Never', () => { + const T = Type.Never({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Not', () => { + const T = Type.Not(Type.String(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Null', () => { + const T = Type.Null({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Number', () => { + const T = Type.Number({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Object', () => { + const T = Type.Object( + { + x: Type.String(), + }, + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Omit', () => { + const T = Type.Omit( + Type.Object({ + x: Type.String(), + y: Type.String(), + }), + ['x'], + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Partial', () => { + const T = Type.Partial( + Type.Object({ + x: Type.String(), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Pick', () => { + const T = Type.Pick( + Type.Object({ + x: Type.String(), + y: Type.String(), + }), + ['x'], + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Record', () => { + const T = Type.Record(Type.String(), Type.Number(), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Recursive', () => { + const T = Type.Recursive( + (This) => + Type.Object({ + x: Type.Number(), + y: Type.Array(This), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Ref 1', () => { + const T = Type.Object({ x: Type.Number() }, { $id: 'T' }) + const R = Type.Ref(T, { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for Ref 2', () => { + const R = Type.Ref('T', { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for RegExp', () => { + const R = Type.RegExp(/xyz/, { foo: 'bar' }) + Assert.IsEqual(R.foo, 'bar') + }) + it('Should assign options for Partial', () => { + const T = Type.Required( + Type.Object({ + x: Type.String(), + }), + { foo: 'bar' }, + ) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for String', () => { + const T = Type.String({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Symbol', () => { + const T = Type.Symbol({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for TemplateLiteral 1', () => { + const T = Type.TemplateLiteral('hello${1|2|3}', { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for TemplateLiteral 2', () => { + const T = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('1'), Type.Literal('2'), Type.Literal('3')])], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Tuple 1', () => { + const T = Type.Tuple([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Tuple 2', () => { + const T = Type.Tuple([Type.String(), Type.Number()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uint8Array', () => { + const T = Type.Uint8Array({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uncapitalize', () => { + const T = Type.Uncapitalize(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Undefined', () => { + const T = Type.Undefined({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([Type.String()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Union 1', () => { + const T = Type.Union([Type.String(), Type.Boolean()], { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Unknown', () => { + const T = Type.Unknown({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Uppercase', () => { + const T = Type.Uppercase(Type.Literal('hello'), { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) + it('Should assign options for Void', () => { + const T = Type.Void({ foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) +}) diff --git a/test/runtime/type/options/index.ts b/test/runtime/type/options/index.ts new file mode 100644 index 000000000..2b378f7fd --- /dev/null +++ b/test/runtime/type/options/index.ts @@ -0,0 +1,2 @@ +import './assign-builder' +import './assign' diff --git a/test/static/recursive.ts b/test/static/recursive.ts index c1e8bcd26..3c4cc9aee 100644 --- a/test/static/recursive.ts +++ b/test/static/recursive.ts @@ -78,24 +78,23 @@ import { Expect } from './assert' nodes: Static[] }>() } +// prettier-ignore { // issue: https://github.com/sinclairzx81/typebox/issues/336 - type JSONValue = - | string - | number - | null - | boolean - | { [x: string]: JSONValue } + type JSONValue = + | string + | number + | null + | boolean + | { [x: string]: JSONValue } | JSONValue[] - const R = Type.Recursive((Node) => - Type.Union([ - Type.Null(), - Type.String(), - Type.Number(), - Type.Boolean(), - Type.Record(Type.String(), Node), - Type.Array(Node), - ]), - ) + const R = Type.Recursive((Node) => Type.Union([ + Type.Null(), + Type.String(), + Type.Number(), + Type.Boolean(), + Type.Record(Type.String(), Node), + Type.Array(Node) + ])) Expect(R).ToStatic() } From 52c71ed6df7187f6ad93460a548de1bfad9893e8 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 26 Jan 2024 06:50:39 +0900 Subject: [PATCH 235/369] Revision 0.32.13 (#744) * Add String Length Contraints to RegExp * Version * Guard Test --- package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 2 ++ src/errors/errors.ts | 7 +++++++ src/index.ts | 2 +- src/type/guard/type.ts | 4 +++- src/type/regexp/regexp.ts | 12 +++++++++--- src/type/type/javascript.ts | 8 ++++---- src/value/check/check.ts | 6 ++++++ test/runtime/compiler/regexp.ts | 14 ++++++++++++++ test/runtime/errors/types/index.ts | 2 ++ .../runtime/errors/types/regexp-max-length.ts | 19 +++++++++++++++++++ .../runtime/errors/types/regexp-min-length.ts | 19 +++++++++++++++++++ test/runtime/type/guard/regexp.ts | 10 ++++++++++ test/runtime/value/check/regexp.ts | 14 ++++++++++++++ 15 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 test/runtime/errors/types/regexp-max-length.ts create mode 100644 test/runtime/errors/types/regexp-min-length.ts diff --git a/package-lock.json b/package-lock.json index db5bd313b..20bbc610d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.12", + "version": "0.32.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.12", + "version": "0.32.13", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f54449430..ce36002d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.12", + "version": "0.32.13", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index e05257958..39cb2f4c2 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -391,6 +391,8 @@ export namespace TypeCompiler { function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator { const variable = CreateVariable(`${new RegExp(schema.source, schema.flags)};`) yield `(typeof ${value} === 'string')` + if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` + if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` yield `${variable}.test(${value})` } function* FromString(schema: TString, references: TSchema[], value: string): IterableIterator { diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 7bd658f3e..f81de1a69 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -444,6 +444,13 @@ function* FromRef(schema: TRef, references: TSchema[], path: string, value: any) yield* Visit(Deref(schema, references), references, path, value) } function* FromRegExp(schema: TRegExp, references: TSchema[], path: string, value: any): IterableIterator { + if (!IsString(value)) return yield Create(ValueErrorType.String, schema, path, value) + if (IsDefined(schema.minLength) && !(value.length >= schema.minLength)) { + yield Create(ValueErrorType.StringMinLength, schema, path, value) + } + if (IsDefined(schema.maxLength) && !(value.length <= schema.maxLength)) { + yield Create(ValueErrorType.StringMaxLength, schema, path, value) + } const regex = new RegExp(schema.source, schema.flags) if (!regex.test(value)) { return yield Create(ValueErrorType.RegExp, schema, path, value) diff --git a/src/index.ts b/src/index.ts index 2108648d5..393709386 100644 --- a/src/index.ts +++ b/src/index.ts @@ -121,7 +121,7 @@ export { ReadonlyOptional, type TReadonlyOptional } from './type/readonly-option export { Record, type TRecord, type TRecordOrObject } from './type/record/index' export { Recursive, type TRecursive, type TThis } from './type/recursive/index' export { Ref, type TRef } from './type/ref/index' -export { RegExp, type TRegExp } from './type/regexp/index' +export { RegExp, type TRegExp, type RegExpOptions } from './type/regexp/index' export { Required, type TRequired, type TRequiredFromMappedResult } from './type/required/index' export { Rest, type TRest } from './type/rest/index' export { ReturnType, type TReturnType } from './type/return-type/index' diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index 98eada8ce..5282c5713 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -444,7 +444,9 @@ export function IsRegExp(value: unknown): value is TRegExp { IsKindOf(value, 'RegExp') && IsOptionalString(value.$id) && ValueGuard.IsString(value.source) && - ValueGuard.IsString(value.flags) + ValueGuard.IsString(value.flags) && + IsOptionalNumber(value.maxLength) && + IsOptionalNumber(value.minLength) ) } /** Returns true if the given value is TString */ diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts index f43257710..c4d0030d0 100644 --- a/src/type/regexp/regexp.ts +++ b/src/type/regexp/regexp.ts @@ -31,6 +31,12 @@ import type { TSchema } from '../schema/index' import { IsString } from '../guard/value' import { Kind } from '../symbols/index' +export interface RegExpOptions extends SchemaOptions { + /** The maximum length of the string */ + maxLength?: number + /** The minimum length of the string */ + minLength?: number +} export interface TRegExp extends TSchema { [Kind]: 'RegExp' static: `${string}` @@ -40,11 +46,11 @@ export interface TRegExp extends TSchema { } /** `[JavaScript]` Creates a RegExp type */ -export function RegExp(pattern: string, options?: SchemaOptions): TRegExp +export function RegExp(pattern: string, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ -export function RegExp(regex: RegExp, options?: SchemaOptions): TRegExp +export function RegExp(regex: RegExp, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ -export function RegExp(unresolved: RegExp | string, options: SchemaOptions = {}) { +export function RegExp(unresolved: RegExp | string, options: RegExpOptions = {}) { const expr = IsString(unresolved) ? new globalThis.RegExp(unresolved) : unresolved return { ...options, [Kind]: 'RegExp', type: 'RegExp', source: expr.source, flags: expr.flags } as never } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index a5c170153..ec87ac728 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -38,7 +38,7 @@ import { InstanceType, type TInstanceType } from '../instance-type/index' import { Iterator, type TIterator } from '../iterator/index' import { Parameters, type TParameters } from '../parameters/index' import { Promise, type TPromise } from '../promise/index' -import { RegExp, type TRegExp } from '../regexp/index' +import { RegExp, type TRegExp, RegExpOptions } from '../regexp/index' import { ReturnType, type TReturnType } from '../return-type/index' import { type TSchema, type SchemaOptions } from '../schema/index' import { Symbol, type TSymbol } from '../symbol/index' @@ -93,11 +93,11 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return Promise(item, options) } /** `[JavaScript]` Creates a RegExp type */ - public RegExp(pattern: string, options?: SchemaOptions): TRegExp + public RegExp(pattern: string, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ - public RegExp(regex: RegExp, options?: SchemaOptions): TRegExp + public RegExp(regex: RegExp, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ - public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { + public RegExp(unresolved: string | RegExp, options: RegExpOptions = {}) { return RegExp(unresolved as any, options) } /** `[JavaScript]` Extracts the ReturnType from the given Function type */ diff --git a/src/value/check/check.ts b/src/value/check/check.ts index e05fee2d7..436c0f047 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -329,6 +329,12 @@ function FromRef(schema: TRef, references: TSchema[], value: any): boolean { } function FromRegExp(schema: TRegExp, references: TSchema[], value: any): boolean { const regex = new RegExp(schema.source, schema.flags) + if (IsDefined(schema.minLength)) { + if (!(value.length >= schema.minLength)) return false + } + if (IsDefined(schema.maxLength)) { + if (!(value.length <= schema.maxLength)) return false + } return regex.test(value) } function FromString(schema: TString, references: TSchema[], value: any): boolean { diff --git a/test/runtime/compiler/regexp.ts b/test/runtime/compiler/regexp.ts index 5a9bf09b8..d4dda52e6 100644 --- a/test/runtime/compiler/regexp.ts +++ b/test/runtime/compiler/regexp.ts @@ -13,4 +13,18 @@ describe('compiler/RegExp', () => { const T = Type.RegExp(/|\p{Extended_Pictographic}/gu) Ok(T, '♥️♦️♠️♣️') }) + it('Should validate with minLength constraint', () => { + const T = Type.RegExp(/(.*)/, { + minLength: 3, + }) + Ok(T, 'xxx') + Fail(T, 'xx') + }) + it('Should validate with maxLength constraint', () => { + const T = Type.RegExp(/(.*)/, { + maxLength: 3, + }) + Ok(T, 'xxx') + Fail(T, 'xxxx') + }) }) diff --git a/test/runtime/errors/types/index.ts b/test/runtime/errors/types/index.ts index 3329f6b54..18084a348 100644 --- a/test/runtime/errors/types/index.ts +++ b/test/runtime/errors/types/index.ts @@ -48,6 +48,8 @@ import './object-required-property' import './object' import './promise' import './record-pointer-property' +import './regexp-max-length' +import './regexp-min-length' import './regexp' import './string-format-unknown' import './string-format' diff --git a/test/runtime/errors/types/regexp-max-length.ts b/test/runtime/errors/types/regexp-max-length.ts new file mode 100644 index 000000000..22f8fd7c9 --- /dev/null +++ b/test/runtime/errors/types/regexp-max-length.ts @@ -0,0 +1,19 @@ +import { Type, FormatRegistry } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/RegExpMaxLength', () => { + const T = Type.RegExp(/(.*)/, { + maxLength: 4, + }) + it('Should pass 0', () => { + const R = Resolve(T, 'xxxx') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 'xxxxx') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringMaxLength) + }) +}) diff --git a/test/runtime/errors/types/regexp-min-length.ts b/test/runtime/errors/types/regexp-min-length.ts new file mode 100644 index 000000000..3bd6227bb --- /dev/null +++ b/test/runtime/errors/types/regexp-min-length.ts @@ -0,0 +1,19 @@ +import { Type } from '@sinclair/typebox' +import { ValueErrorType } from '@sinclair/typebox/errors' +import { Resolve } from './resolve' +import { Assert } from '../../assert' + +describe('errors/type/RegExpMinLength', () => { + const T = Type.RegExp(/(.*)/, { + minLength: 4, + }) + it('Should pass 0', () => { + const R = Resolve(T, 'xxxx') + Assert.IsEqual(R.length, 0) + }) + it('Should pass 1', () => { + const R = Resolve(T, 'xxx') + Assert.IsEqual(R.length, 1) + Assert.IsEqual(R[0].type, ValueErrorType.StringMinLength) + }) +}) diff --git a/test/runtime/type/guard/regexp.ts b/test/runtime/type/guard/regexp.ts index 845c1753d..558329cba 100644 --- a/test/runtime/type/guard/regexp.ts +++ b/test/runtime/type/guard/regexp.ts @@ -20,4 +20,14 @@ describe('type/guard/TRegExp', () => { const T = Type.RegExp('foo', { $id: 1 }) Assert.IsFalse(TypeGuard.IsRegExp(T)) }) + it('Should guard for RegExp constraint 1', () => { + // @ts-ignore + const T = Type.RegExp('foo', { maxLength: '1' }) + Assert.IsFalse(TypeGuard.IsRegExp(T)) + }) + it('Should guard for RegExp constraint 2', () => { + // @ts-ignore + const T = Type.RegExp('foo', { minLength: '1' }) + Assert.IsFalse(TypeGuard.IsRegExp(T)) + }) }) diff --git a/test/runtime/value/check/regexp.ts b/test/runtime/value/check/regexp.ts index ce843038e..f25958126 100644 --- a/test/runtime/value/check/regexp.ts +++ b/test/runtime/value/check/regexp.ts @@ -33,4 +33,18 @@ describe('value/check/RegExp', () => { const result = Value.Check(T, value) Assert.IsEqual(result, false) }) + it('Should validate with minLength constraint', () => { + const T = Type.RegExp(/(.*)/, { + minLength: 3, + }) + Assert.IsTrue(Value.Check(T, 'xxx')) + Assert.IsFalse(Value.Check(T, 'xx')) + }) + it('Should validate with maxLength constraint', () => { + const T = Type.RegExp(/(.*)/, { + maxLength: 3, + }) + Assert.IsTrue(Value.Check(T, 'xxx')) + Assert.IsFalse(Value.Check(T, 'xxxx')) + }) }) From fd1056b367479c7a9925143641d272b4a238ffad Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 11 Feb 2024 12:16:13 +0900 Subject: [PATCH 236/369] Revision 0.32.14 (#753) * Simplify Exports * Version --- package-lock.json | 4 +- package.json | 2 +- src/index.ts | 193 ++++++++++++++------------------------ src/type/clone/index.ts | 4 +- src/type/const/const.ts | 4 +- src/type/object/object.ts | 2 +- 6 files changed, 77 insertions(+), 132 deletions(-) diff --git a/package-lock.json b/package-lock.json index 20bbc610d..afcf1b69a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.13", + "version": "0.32.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.13", + "version": "0.32.14", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index ce36002d0..1ab2c08c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.13", + "version": "0.32.14", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/index.ts b/src/index.ts index 393709386..dbbe67127 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,133 +29,78 @@ THE SOFTWARE. // ------------------------------------------------------------------ // Infrastructure // ------------------------------------------------------------------ -export { Kind, Hint, ReadonlyKind, OptionalKind, TransformKind } from './type/symbols/index' -export { PatternBoolean, PatternBooleanExact, PatternNumber, PatternNumberExact, PatternString, PatternStringExact } from './type/patterns/index' -export { TypeRegistry, FormatRegistry } from './type/registry/index' -export { TypeGuard, ValueGuard } from './type/guard/index' -export { CloneType, CloneRest } from './type/clone/type' -// ------------------------------------------------------------------ -// Error -// ------------------------------------------------------------------ -export { TypeBoxError } from './type/error/index' -// ------------------------------------------------------------------ -// Sets -// ------------------------------------------------------------------ -export { - SetComplement, - SetDistinct, - SetIncludes, - SetIntersect, - SetIntersectMany, - SetIsSubset, - SetUnion, - SetUnionMany, - type TSetComplement, - type TSetDistinct, - type TSetIncludes, - type TSetIntersect, - type TSetIntersectMany, - type TSetIsSubset, - type TSetUnion, - type TSetUnionMany, -} from './type/sets/index' -// ------------------------------------------------------------------ -// Helpers -// ------------------------------------------------------------------ -export { Increment, type TIncrement, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index' +export * from './type/clone/index' +export * from './type/error/index' +export * from './type/guard/index' +export * from './type/helpers/index' +export * from './type/patterns/index' +export * from './type/registry/index' +export * from './type/sets/index' +export * from './type/symbols/index' // ------------------------------------------------------------------ // Types // ------------------------------------------------------------------ -export { Any, type TAny } from './type/any/index' -export { Array, type TArray, type ArrayOptions } from './type/array/index' -export { AsyncIterator, type TAsyncIterator } from './type/async-iterator/index' -export { Awaited, type TAwaited } from './type/awaited/index' -export { BigInt, type TBigInt, type BigIntOptions } from './type/bigint/index' -export { Boolean, type TBoolean } from './type/boolean/index' -export { Composite, type TComposite } from './type/composite/index' -export { Const, type TConst } from './type/const/index' -export { Constructor, type TConstructor } from './type/constructor/index' -export { ConstructorParameters, type TConstructorParameters } from './type/constructor-parameters/index' -export { Date, type TDate, type DateOptions } from './type/date/index' -export { Deref, type TDeref } from './type/deref/index' -export { Enum, type TEnum } from './type/enum/index' -export { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from './type/exclude/index' -export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey } from './type/extends/index' -export { Extract, type TExtract, type TExtractFromMappedResult, type TExtractFromTemplateLiteral } from './type/extract/index' -export { Function, type TFunction } from './type/function/index' -export { - Index, - IndexPropertyKeys, - IndexFromPropertyKeys, - IndexFromPropertyKey, - IndexFromMappedKey, - IndexFromMappedResult, - type TIndex, - type TIndexPropertyKeys, - type TIndexFromPropertyKeys, - type TIndexFromPropertyKey, - type TIndexFromMappedKey, - type TIndexFromMappedResult, -} from './type/indexed/index' -export { InstanceType, type TInstanceType } from './type/instance-type/index' -export { Integer, type TInteger, type IntegerOptions } from './type/integer/index' -export { Intersect, IntersectEvaluated, type TIntersect, type TIntersectEvaluated, type IntersectOptions } from './type/intersect/index' -export { Iterator, type TIterator } from './type/iterator/index' -export { Intrinsic, IntrinsicFromMappedKey, type TIntrinsic, Capitalize, type TCapitalize, Lowercase, type TLowercase, Uncapitalize, type TUncapitalize, Uppercase, type TUppercase } from './type/intrinsic/index' -export { KeyOf, KeyOfPropertyKeys, KeyOfPropertyKeysToRest, KeyOfFromMappedResult, KeyOfPattern, type TKeyOf, type TKeyOfPropertyKeys, type TKeyOfPropertyKeysToRest, type TKeyOfFromMappedResult } from './type/keyof/index' -export { Literal, type TLiteral, type TLiteralValue } from './type/literal/index' -export { Mapped, MappedKey, MappedResult, MappedFunctionReturnType, type TMapped, type TMappedKey, type TMappedResult, type TMappedFunction, type TMappedFunctionReturnType } from './type/mapped/index' -export { Never, type TNever } from './type/never/index' -export { Not, type TNot } from './type/not/index' -export { Null, type TNull } from './type/null/index' -export { Number, type TNumber, type NumberOptions } from './type/number/index' -export { Object, type TObject, type TProperties, type ObjectOptions } from './type/object/index' -export { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from './type/omit/index' -export { Optional, OptionalFromMappedResult, type TOptional, type TOptionalWithFlag, type TOptionalFromMappedResult } from './type/optional/index' -export { Parameters, type TParameters } from './type/parameters/index' -export { Partial, PartialFromMappedResult, type TPartial, type TPartialFromMappedResult } from './type/partial/index' -export { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from './type/pick/index' -export { Promise, type TPromise } from './type/promise/index' -export { Readonly, ReadonlyFromMappedResult, type TReadonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from './type/readonly/index' -export { ReadonlyOptional, type TReadonlyOptional } from './type/readonly-optional/index' -export { Record, type TRecord, type TRecordOrObject } from './type/record/index' -export { Recursive, type TRecursive, type TThis } from './type/recursive/index' -export { Ref, type TRef } from './type/ref/index' -export { RegExp, type TRegExp, type RegExpOptions } from './type/regexp/index' -export { Required, type TRequired, type TRequiredFromMappedResult } from './type/required/index' -export { Rest, type TRest } from './type/rest/index' -export { ReturnType, type TReturnType } from './type/return-type/index' -export { type TSchema, type TKind, type SchemaOptions, type TAnySchema } from './type/schema/index' -export { type Static, type StaticDecode, type StaticEncode, type TDecodeType, type TDecodeRest, type TDecodeProperties } from './type/static/index' -export { Strict } from './type/strict/index' -export { String, type TString, type StringOptions, type StringFormatOption, type StringContentEncodingOption } from './type/string/index' -export { Symbol, type TSymbol, type TSymbolValue } from './type/symbol/index' -export { - TemplateLiteral, - TemplateLiteralSyntax, - TemplateLiteralGenerate, - TemplateLiteralParse, - TemplateLiteralParseExact, - TemplateLiteralToUnion, - IsTemplateLiteralFinite, - TemplateLiteralExpressionGenerate, - IsTemplateLiteralExpressionFinite, - type TTemplateLiteral, - type TTemplateLiteralSyntax, - type TTemplateLiteralGenerate, - type TTemplateLiteralKind, - type TTemplateLiteralToUnion, - type TIsTemplateLiteralFinite, -} from './type/template-literal/index' -export { Transform, TransformDecodeBuilder, TransformEncodeBuilder, type TTransform, type TransformOptions, type TransformFunction } from './type/transform/index' -export { Tuple, type TTuple } from './type/tuple/index' -export { Uint8Array, type TUint8Array, type Uint8ArrayOptions } from './type/uint8array/index' -export { Undefined, type TUndefined } from './type/undefined/index' -export { Union, UnionEvaluated, type TUnion, type TUnionEvaluated } from './type/union/index' -export { Unknown, type TUnknown } from './type/unknown/index' -export { Unsafe, type TUnsafe } from './type/unsafe/index' -export { Void, type TVoid } from './type/void/index' +export * from './type/any/index' +export * from './type/array/index' +export * from './type/async-iterator/index' +export * from './type/awaited/index' +export * from './type/bigint/index' +export * from './type/boolean/index' +export * from './type/composite/index' +export * from './type/const/index' +export * from './type/constructor/index' +export * from './type/constructor-parameters/index' +export * from './type/date/index' +export * from './type/deref/index' +export * from './type/enum/index' +export * from './type/exclude/index' +export * from './type/extends/index' +export * from './type/extract/index' +export * from './type/function/index' +export * from './type/indexed/index' +export * from './type/instance-type/index' +export * from './type/integer/index' +export * from './type/intersect/index' +export * from './type/iterator/index' +export * from './type/intrinsic/index' +export * from './type/keyof/index' +export * from './type/literal/index' +export * from './type/mapped/index' +export * from './type/never/index' +export * from './type/not/index' +export * from './type/null/index' +export * from './type/number/index' +export * from './type/object/index' +export * from './type/omit/index' +export * from './type/optional/index' +export * from './type/parameters/index' +export * from './type/partial/index' +export * from './type/pick/index' +export * from './type/promise/index' +export * from './type/readonly/index' +export * from './type/readonly-optional/index' +export * from './type/record/index' +export * from './type/recursive/index' +export * from './type/ref/index' +export * from './type/regexp/index' +export * from './type/required/index' +export * from './type/rest/index' +export * from './type/return-type/index' +export * from './type/schema/index' +export * from './type/static/index' +export * from './type/strict/index' +export * from './type/string/index' +export * from './type/symbol/index' +export * from './type/template-literal/index' +export * from './type/transform/index' +export * from './type/tuple/index' +export * from './type/uint8array/index' +export * from './type/undefined/index' +export * from './type/union/index' +export * from './type/unknown/index' +export * from './type/unsafe/index' +export * from './type/void/index' // ------------------------------------------------------------------ // Namespace // ------------------------------------------------------------------ -export { Type, JsonTypeBuilder, JavaScriptTypeBuilder } from './type/type/index' +export * from './type/type/index' diff --git a/src/type/clone/index.ts b/src/type/clone/index.ts index 4ac4e6f01..ee3c7bfec 100644 --- a/src/type/clone/index.ts +++ b/src/type/clone/index.ts @@ -26,5 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * as TypeClone from './type' -export * as ValueClone from './value' +export * from './type' +export * from './value' diff --git a/src/type/const/const.ts b/src/type/const/const.ts index ce13dc352..3eecebc0b 100644 --- a/src/type/const/const.ts +++ b/src/type/const/const.ts @@ -44,7 +44,7 @@ import { Readonly, type TReadonly } from '../readonly/index' import { Undefined, type TUndefined } from '../undefined/index' import { Uint8Array, type TUint8Array } from '../uint8array/index' import { Unknown, type TUnknown } from '../unknown/index' -import { TypeClone } from '../clone/index' +import { CloneType } from '../clone/index' // ------------------------------------------------------------------ // ValueGuard @@ -131,5 +131,5 @@ export type TConst = FromValue /** `[JavaScript]` Creates a readonly const type from the given value. */ export function Const(T: T, options: SchemaOptions = {}): TConst { - return TypeClone.CloneType(FromValue(T, true), options) as TConst + return CloneType(FromValue(T, true), options) as TConst } diff --git a/src/type/object/object.ts b/src/type/object/object.ts index ed705aa6e..0cda73dfe 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -84,7 +84,7 @@ export interface TObject extends TSchema, O required?: string[] } /** `[Json]` Creates an Object type */ -export function _Object(properties: T, options: ObjectOptions = {}): TObject { +function _Object(properties: T, options: ObjectOptions = {}): TObject { const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) const optionalKeys = propertyKeys.filter((key) => IsOptional(properties[key])) const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) From 3db65cd0585c56c8ac563dcd094b5ed73fb484bb Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 1 Mar 2024 16:42:52 +0900 Subject: [PATCH 237/369] Revision 0.32.15 (#774) * Add Additional Guards for Typed Arrays * Map and Set | Additional Narrow for InstanceObject * Version --- package-lock.json | 4 +- package.json | 2 +- src/compiler/index.ts | 2 +- src/errors/index.ts | 4 +- src/system/index.ts | 4 +- src/value/cast/cast.ts | 4 +- src/value/clone/clone.ts | 4 +- src/value/delta/delta.ts | 6 +- src/value/equal/equal.ts | 6 +- src/value/guard/guard.ts | 83 ++++++-- src/value/hash/hash.ts | 4 +- src/value/index.ts | 61 ++---- src/value/mutate/mutate.ts | 10 +- src/value/transform/decode.ts | 8 +- src/value/transform/encode.ts | 8 +- test/runtime/value/guard/guard.ts | 305 +++++++++++++++++++++++++----- 16 files changed, 377 insertions(+), 138 deletions(-) diff --git a/package-lock.json b/package-lock.json index afcf1b69a..2b11a6af0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.14", + "version": "0.32.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.14", + "version": "0.32.15", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 1ab2c08c5..32692ddaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.14", + "version": "0.32.15", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/index.ts b/src/compiler/index.ts index b732cb855..d1a93e868 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -27,4 +27,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' -export { TypeCompiler, TypeCheck, type TypeCompilerCodegenOptions, type TypeCompilerLanguageOption, TypeCompilerTypeGuardError, TypeCompilerUnknownTypeError } from './compiler' +export * from './compiler' diff --git a/src/errors/index.ts b/src/errors/index.ts index 6fc0d608a..288d17757 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -26,5 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { Errors, ValueError, ValueErrorIterator, ValueErrorType, ValueErrorsUnknownTypeError } from './errors' -export { DefaultErrorFunction, GetErrorFunction, SetErrorFunction, type ErrorFunction, type ErrorFunctionParameter } from './function' +export * from './errors' +export * from './function' diff --git a/src/system/index.ts b/src/system/index.ts index 7ed15c5dc..70c91ccf2 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -26,5 +26,5 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export { TypeSystemPolicy } from './policy' -export { TypeSystem, TypeSystemDuplicateFormat, TypeSystemDuplicateTypeKind } from './system' +export * from './policy' +export * from './system' diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index cadbaf72a..999e9379a 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index' +import { IsStandardObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index' import { TypeBoxError } from '../../type/error/index' import { Kind } from '../../type/symbols/index' import { Create } from '../create/index' @@ -134,7 +134,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any } function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { const created = Create(schema, references) - const mapped = IsPlainObject(created) && IsPlainObject(value) ? { ...(created as any), ...value } : value + const mapped = IsStandardObject(created) && IsStandardObject(value) ? { ...(created as any), ...value } : value return Check(schema, references, mapped) ? mapped : Create(schema, references) } function FromNever(schema: TNever, references: TSchema[], value: any): any { diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index 7f748cbba..f9f64f4cd 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -31,7 +31,7 @@ import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/ // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from '../guard/index' +import { IsArray, IsDate, IsStandardObject, IsTypedArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // Clonable // ------------------------------------------------------------------ @@ -58,7 +58,7 @@ function ValueType(value: ValueType): any { export function Clone(value: T): T { if (IsArray(value)) return ArrayType(value) if (IsDate(value)) return DateType(value) - if (IsPlainObject(value)) return ObjectType(value) + if (IsStandardObject(value)) return ObjectType(value) if (IsTypedArray(value)) return TypedArrayType(value) if (IsValueType(value)) return ValueType(value) throw new Error('ValueClone: Unable to clone value') diff --git a/src/value/delta/delta.ts b/src/value/delta/delta.ts index 616d5e949..daf47fa1e 100644 --- a/src/value/delta/delta.ts +++ b/src/value/delta/delta.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from '../guard/index' +import { IsStandardObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from '../guard/index' import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' import type { Static } from '../../type/static/index' import { ValuePointer } from '../pointer/index' @@ -90,7 +90,7 @@ function CreateDelete(path: string): Edit { // Diffing Generators // ------------------------------------------------------------------ function* ObjectType(path: string, current: ObjectType, next: unknown): IterableIterator { - if (!IsPlainObject(next)) return yield CreateUpdate(path, next) + if (!IsStandardObject(next)) return yield CreateUpdate(path, next) const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] for (const key of currentKeys) { @@ -136,7 +136,7 @@ function* ValueType(path: string, current: ValueType, next: unknown): IterableIt yield CreateUpdate(path, next) } function* Visit(path: string, current: unknown, next: unknown): IterableIterator { - if (IsPlainObject(current)) return yield* ObjectType(path, current, next) + if (IsStandardObject(current)) return yield* ObjectType(path, current, next) if (IsArray(current)) return yield* ArrayType(path, current, next) if (IsTypedArray(current)) return yield* TypedArrayType(path, current, next) if (IsValueType(current)) return yield* ValueType(path, current, next) diff --git a/src/value/equal/equal.ts b/src/value/equal/equal.ts index fdfca521e..ce64ea993 100644 --- a/src/value/equal/equal.ts +++ b/src/value/equal/equal.ts @@ -26,14 +26,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' +import { IsStandardObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' // ------------------------------------------------------------------ // Equality Checks // ------------------------------------------------------------------ function ObjectType(left: ObjectType, right: unknown): boolean { - if (!IsPlainObject(right)) return false + if (!IsStandardObject(right)) return false const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] const rightKeys = [...Object.keys(right), ...Object.getOwnPropertySymbols(right)] if (leftKeys.length !== rightKeys.length) return false @@ -58,7 +58,7 @@ function ValueType(left: ValueType, right: unknown): any { // ------------------------------------------------------------------ /** Returns true if the left value deep-equals the right */ export function Equal(left: T, right: unknown): right is T { - if (IsPlainObject(left)) return ObjectType(left, right) + if (IsStandardObject(left)) return ObjectType(left, right) if (IsDate(left)) return DateType(left, right) if (IsTypedArray(left)) return TypedArrayType(left, right) if (IsArray(left)) return ArrayType(left, right) diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 83a3c1b66..8e45b1bd8 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -57,24 +57,87 @@ export function IsIterator(value: unknown): value is IterableIterator { return IsObject(value) && Symbol.iterator in value } // -------------------------------------------------------------------------- -// Nominal +// Object Instances // -------------------------------------------------------------------------- -/** Returns true if this value is a typed array */ -export function IsTypedArray(value: unknown): value is TypedArrayType { - return ArrayBuffer.isView(value) +/** Returns true if this value is not an instance of a class */ +export function IsStandardObject(value: unknown): value is ObjectType { + return IsObject(value) && !IsArray(value) && IsFunction(value.constructor) && value.constructor.name === 'Object' +} +/** Returns true if this value is an instance of a class */ +export function IsInstanceObject(value: unknown): value is ObjectType { + return IsObject(value) && !IsArray(value) && IsFunction(value.constructor) && value.constructor.name !== 'Object' } +// -------------------------------------------------------------------------- +// JavaScript +// -------------------------------------------------------------------------- /** Returns true if this value is a Promise */ export function IsPromise(value: unknown): value is Promise { return value instanceof Promise } -/** Returns true if the value is a Uint8Array */ -export function IsUint8Array(value: unknown): value is Uint8Array { - return value instanceof Uint8Array -} /** Returns true if this value is a Date */ export function IsDate(value: unknown): value is Date { return value instanceof Date && Number.isFinite(value.getTime()) } +/** Returns true if this value is an instance of Map */ +export function IsMap(value: unknown): value is Map { + return value instanceof globalThis.Map +} +/** Returns true if this value is an instance of Set */ +export function IsSet(value: unknown): value is Set { + return value instanceof globalThis.Set +} +/** Returns true if this value is RegExp */ +export function IsRegExp(value: unknown): value is RegExp { + return value instanceof globalThis.RegExp +} +/** Returns true if this value is a typed array */ +export function IsTypedArray(value: unknown): value is TypedArrayType { + return ArrayBuffer.isView(value) +} +/** Returns true if the value is a Int8Array */ +export function IsInt8Array(value: unknown): value is Int8Array { + return value instanceof globalThis.Int8Array +} +/** Returns true if the value is a Uint8Array */ +export function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof globalThis.Uint8Array +} +/** Returns true if the value is a Uint8ClampedArray */ +export function IsUint8ClampedArray(value: unknown): value is Uint8ClampedArray { + return value instanceof globalThis.Uint8ClampedArray +} +/** Returns true if the value is a Int16Array */ +export function IsInt16Array(value: unknown): value is Int16Array { + return value instanceof globalThis.Int16Array +} +/** Returns true if the value is a Uint16Array */ +export function IsUint16Array(value: unknown): value is Uint16Array { + return value instanceof globalThis.Uint16Array +} +/** Returns true if the value is a Int32Array */ +export function IsInt32Array(value: unknown): value is Int32Array { + return value instanceof globalThis.Int32Array +} +/** Returns true if the value is a Uint32Array */ +export function IsUint32Array(value: unknown): value is Uint32Array { + return value instanceof globalThis.Uint32Array +} +/** Returns true if the value is a Float32Array */ +export function IsFloat32Array(value: unknown): value is Float32Array { + return value instanceof globalThis.Float32Array +} +/** Returns true if the value is a Float64Array */ +export function IsFloat64Array(value: unknown): value is Float64Array { + return value instanceof globalThis.Float64Array +} +/** Returns true if the value is a BigInt64Array */ +export function IsBigInt64Array(value: unknown): value is BigInt64Array { + return value instanceof globalThis.BigInt64Array +} +/** Returns true if the value is a BigUint64Array */ +export function IsBigUint64Array(value: unknown): value is BigUint64Array { + return value instanceof globalThis.BigUint64Array +} // -------------------------------------------------------------------------- // Standard // -------------------------------------------------------------------------- @@ -82,10 +145,6 @@ export function IsDate(value: unknown): value is Date { export function HasPropertyKey(value: Record, key: K): value is ObjectType & Record { return key in value } -/** Returns true if this object is not an instance of any other type */ -export function IsPlainObject(value: unknown): value is ObjectType { - return IsObject(value) && IsFunction(value.constructor) && value.constructor.name === 'Object' -} /** Returns true of this value is an object type */ export function IsObject(value: unknown): value is ObjectType { return value !== null && typeof value === 'object' diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index c87e102bc..beb159d09 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsPlainObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index' +import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsStandardObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index' import { TypeBoxError } from '../../type/error/index' // ------------------------------------------------------------------ @@ -140,7 +140,7 @@ function Visit(value: any) { if (IsDate(value)) return DateType(value) if (IsNull(value)) return NullType(value) if (IsNumber(value)) return NumberType(value) - if (IsPlainObject(value)) return ObjectType(value) + if (IsStandardObject(value)) return ObjectType(value) if (IsString(value)) return StringType(value) if (IsSymbol(value)) return SymbolType(value) if (IsUint8Array(value)) return Uint8ArrayType(value) diff --git a/src/value/index.ts b/src/value/index.ts index 8b17d674f..445581ad3 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -27,55 +27,30 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ // ------------------------------------------------------------------ -// Value Errors (re-export) +// Errors (re-export) // ------------------------------------------------------------------ export { ValueError, ValueErrorType, ValueErrorIterator } from '../errors/index' // ------------------------------------------------------------------ -// Value Operators +// Guards // ------------------------------------------------------------------ -export { Cast, ValueCastError } from './cast/index' -export { Check } from './check/index' -export { Clean } from './clean/index' -export { Clone } from './clone/index' -export { Convert } from './convert/index' -export { Create, ValueCreateError } from './create/index' -export { Default } from './default/index' -export { Diff, Patch, Edit, Delete, Insert, Update, ValueDeltaError } from './delta/index' -export { Equal } from './equal/index' -export { Hash, ValueHashError } from './hash/index' -export { Mutate, ValueMutateError, type Mutable } from './mutate/index' -export { ValuePointer } from './pointer/index' -export { TransformDecode, TransformEncode, HasTransform, TransformDecodeCheckError, TransformDecodeError, TransformEncodeCheckError, TransformEncodeError } from './transform/index' +export * from './guard/index' // ------------------------------------------------------------------ -// Value Guards +// Operators // ------------------------------------------------------------------ -export { - ArrayType, - HasPropertyKey, - IsArray, - IsAsyncIterator, - IsBigInt, - IsBoolean, - IsDate, - IsFunction, - IsInteger, - IsIterator, - IsNull, - IsNumber, - IsObject, - IsPlainObject, - IsPromise, - IsString, - IsSymbol, - IsTypedArray, - IsUint8Array, - IsUndefined, - IsValueType, - type ObjectType, - type TypedArrayType, - type ValueType, -} from './guard/index' +export * from './cast/index' +export * from './check/index' +export * from './clean/index' +export * from './clone/index' +export * from './convert/index' +export * from './create/index' +export * from './default/index' +export * from './delta/index' +export * from './equal/index' +export * from './hash/index' +export * from './mutate/index' +export * from './pointer/index' +export * from './transform/index' // ------------------------------------------------------------------ -// Value Namespace +// Namespace // ------------------------------------------------------------------ export { Value } from './value/index' diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index b8e5c61ca..45791bc24 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index' +import { IsStandardObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index' import { ValuePointer } from '../pointer/index' import { Clone } from '../clone/index' import { TypeBoxError } from '../../type/error/index' @@ -44,7 +44,7 @@ export class ValueMutateError extends TypeBoxError { // ------------------------------------------------------------------ export type Mutable = { [key: string]: unknown } | unknown[] function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { - if (!IsPlainObject(current)) { + if (!IsStandardObject(current)) { ValuePointer.Set(root, path, Clone(next)) } else { const currentKeys = Object.keys(current) @@ -90,7 +90,7 @@ function ValueType(root: Mutable, path: string, current: unknown, next: unknown) function Visit(root: Mutable, path: string, current: unknown, next: unknown) { if (IsArray(next)) return ArrayType(root, path, current, next) if (IsTypedArray(next)) return TypedArrayType(root, path, current, next) - if (IsPlainObject(next)) return ObjectType(root, path, current, next) + if (IsStandardObject(next)) return ObjectType(root, path, current, next) if (IsValueType(next)) return ValueType(root, path, current, next) } // ------------------------------------------------------------------ @@ -102,8 +102,8 @@ function IsNonMutableValue(value: unknown): value is Mutable { function IsMismatchedValue(current: unknown, next: unknown) { // prettier-ignore return ( - (IsPlainObject(current) && IsArray(next)) || - (IsArray(current) && IsPlainObject(next)) + (IsStandardObject(current) && IsArray(next)) || + (IsArray(current) && IsStandardObject(next)) ) } // ------------------------------------------------------------------ diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 9580e7663..3169293ac 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -48,7 +48,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +import { IsStandardObject, IsArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -86,7 +86,7 @@ function FromArray(schema: TArray, references: TSchema[], value: any): any { } // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { - if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) + if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, value) const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return (key in value) @@ -110,7 +110,7 @@ function FromNot(schema: TNot, references: TSchema[], value: any) { } // prettier-ignore function FromObject(schema: TObject, references: TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) + if (!IsStandardObject(value)) return Default(schema, value) const knownKeys = KeyOfPropertyKeys(schema) const knownProperties = knownKeys.reduce((value, key) => { return (key in value) @@ -131,7 +131,7 @@ function FromObject(schema: TObject, references: TSchema[], value: any) { } // prettier-ignore function FromRecord(schema: TRecord, references: TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) + if (!IsStandardObject(value)) return Default(schema, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 35d724811..695a7ea7c 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -48,7 +48,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +import { IsStandardObject, IsArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -87,7 +87,7 @@ function FromArray(schema: TArray, references: TSchema[], value: any): any { // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { const defaulted = Default(schema, value) - if (!IsPlainObject(value) || IsValueType(value)) return defaulted + if (!IsStandardObject(value) || IsValueType(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return key in defaulted @@ -112,7 +112,7 @@ function FromNot(schema: TNot, references: TSchema[], value: any) { // prettier-ignore function FromObject(schema: TObject, references: TSchema[], value: any) { const defaulted = Default(schema, value) - if (!IsPlainObject(value)) return defaulted + if (!IsStandardObject(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return key in value @@ -133,7 +133,7 @@ function FromObject(schema: TObject, references: TSchema[], value: any) { // prettier-ignore function FromRecord(schema: TRecord, references: TSchema[], value: any) { const defaulted = Default(schema, value) as Record - if (!IsPlainObject(value)) return defaulted + if (!IsStandardObject(value)) return defaulted const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { diff --git a/test/runtime/value/guard/guard.ts b/test/runtime/value/guard/guard.ts index ee8d3839f..cd775097c 100644 --- a/test/runtime/value/guard/guard.ts +++ b/test/runtime/value/guard/guard.ts @@ -5,118 +5,115 @@ describe('value/guard/ValueGuard', () => { // ----------------------------------------------------- // IsNull // ----------------------------------------------------- - it('Should guard null 1', () => { + it('Should guard Null 1', () => { const R = ValueGuard.IsNull(null) Assert.IsTrue(R) }) - it('Should guard null 2', () => { + it('Should guard Null 2', () => { const R = ValueGuard.IsNull({}) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsUndefined // ----------------------------------------------------- - it('Should guard undefined 1', () => { + it('Should guard Undefined 1', () => { const R = ValueGuard.IsUndefined(undefined) Assert.IsTrue(R) }) - it('Should guard undefined 2', () => { + it('Should guard Undefined 2', () => { const R = ValueGuard.IsUndefined({}) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsBigInt // ----------------------------------------------------- - it('Should guard bigint 1', () => { + it('Should guard BigInt 1', () => { const R = ValueGuard.IsBigInt(1n) Assert.IsTrue(R) }) - it('Should guard bigint 2', () => { + it('Should guard BigInt 2', () => { const R = ValueGuard.IsBigInt(1) Assert.IsFalse(R) }) - it('Should guard bigint 3', () => { + it('Should guard BigInt 3', () => { const R = ValueGuard.IsBigInt('123') Assert.IsFalse(R) }) // ----------------------------------------------------- // IsNumber // ----------------------------------------------------- - it('Should guard number 1', () => { + it('Should guard Number 1', () => { const R = ValueGuard.IsNumber(1) Assert.IsTrue(R) }) - it('Should guard number 2', () => { + it('Should guard Number 2', () => { const R = ValueGuard.IsNumber(3.14) Assert.IsTrue(R) }) - it('Should guard number 3', () => { + it('Should guard Number 3', () => { const R = ValueGuard.IsNumber('') Assert.IsFalse(R) }) - it('Should guard number 4', () => { + it('Should guard Number 4', () => { const R = ValueGuard.IsNumber(NaN) Assert.IsTrue(R) }) // ----------------------------------------------------- // IsString // ----------------------------------------------------- - it('Should guard string 1', () => { + it('Should guard String 1', () => { const R = ValueGuard.IsString('') Assert.IsTrue(R) }) - it('Should guard string 2', () => { + it('Should guard String 2', () => { const R = ValueGuard.IsString(true) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsBoolean // ----------------------------------------------------- - it('Should guard boolean 1', () => { + it('Should guard Boolean 1', () => { const R = ValueGuard.IsBoolean(true) Assert.IsTrue(R) }) - it('Should guard boolean 2', () => { + it('Should guard Boolean 2', () => { const R = ValueGuard.IsBoolean(1) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsObject // ----------------------------------------------------- - it('Should guard object 1', () => { + it('Should guard Object 1', () => { const R = ValueGuard.IsObject({}) Assert.IsTrue(R) }) - it('Should guard object 2', () => { + it('Should guard Object 2', () => { const R = ValueGuard.IsObject(1) Assert.IsFalse(R) }) - it('Should guard object 3', () => { + it('Should guard Object 3', () => { const R = ValueGuard.IsObject([]) Assert.IsTrue(R) }) // ----------------------------------------------------- // IsArray // ----------------------------------------------------- - it('Should guard array 1', () => { + it('Should guard Array 1', () => { const R = ValueGuard.IsArray([]) Assert.IsTrue(R) }) - it('Should guard array 2', () => { + it('Should guard Array 2', () => { const R = ValueGuard.IsArray({}) Assert.IsFalse(R) }) // ----------------------------------------------------- - // Specialized (Values Only) - // ----------------------------------------------------- - // ----------------------------------------------------- // IsAsyncIterator // ----------------------------------------------------- - it('Should guard async iterator 1', () => { + it('Should guard AsyncIterator 1', () => { const R = ValueGuard.IsAsyncIterator((async function* (): any {})()) Assert.IsTrue(R) }) - it('Should guard async iterator 2', () => { + it('Should guard AsyncIterator 2', () => { const R = ValueGuard.IsAsyncIterator((function* (): any {})()) Assert.IsFalse(R) }) @@ -136,119 +133,327 @@ describe('value/guard/ValueGuard', () => { // ----------------------------------------------------- // IsDate // ----------------------------------------------------- - it('Should guard date 1', () => { + it('Should guard Date 1', () => { const R = ValueGuard.IsDate(new Date()) Assert.IsTrue(R) }) - it('Should guard date 2', () => { + it('Should guard Date 2', () => { const R = ValueGuard.IsDate({}) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsFunction // ----------------------------------------------------- - it('Should guard function 1', () => { + it('Should guard Function 1', () => { const R = ValueGuard.IsFunction(function () {}) Assert.IsTrue(R) }) - it('Should guard function 2', () => { + it('Should guard Function 2', () => { const R = ValueGuard.IsFunction({}) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsInteger // ----------------------------------------------------- - it('Should guard integer 1', () => { + it('Should guard Integer 1', () => { const R = ValueGuard.IsInteger(1) Assert.IsTrue(R) }) - it('Should guard integer 2', () => { + it('Should guard Integer 2', () => { const R = ValueGuard.IsInteger(3.14) Assert.IsFalse(R) }) - it('Should guard integer 3', () => { + it('Should guard Integer 3', () => { const R = ValueGuard.IsInteger(NaN) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsIterator // ----------------------------------------------------- - it('Should guard iterator 1', () => { + it('Should guard Iterator 1', () => { const R = ValueGuard.IsIterator((function* () {})()) Assert.IsTrue(R) }) - it('Should guard iterator 2', () => { + it('Should guard Iterator 2', () => { const R = ValueGuard.IsIterator({}) Assert.IsFalse(R) }) // ----------------------------------------------------- - // IsPlainObject + // IsStandardObject // ----------------------------------------------------- - it('Should guard plain object 1', () => { - const R = ValueGuard.IsPlainObject({}) + it('Should guard StandardObject 1', () => { + const R = ValueGuard.IsStandardObject({}) Assert.IsTrue(R) }) - it('Should guard plain object 2', () => { - const R = ValueGuard.IsPlainObject(new (class {})()) + it('Should guard StandardObject 2', () => { + const R = ValueGuard.IsStandardObject(1) + Assert.IsFalse(R) + }) + it('Should guard StandardObject 3', () => { + const R = ValueGuard.IsStandardObject([]) + Assert.IsFalse(R) + }) + it('Should guard StandardObject 4', () => { + const R = ValueGuard.IsStandardObject(new (class {})()) Assert.IsFalse(R) }) // ----------------------------------------------------- + // IsInstanceObject + // ----------------------------------------------------- + it('Should guard InstanceObject 1', () => { + const R = ValueGuard.IsInstanceObject({}) + Assert.IsFalse(R) + }) + it('Should guard InstanceObject 2', () => { + const R = ValueGuard.IsInstanceObject(1) + Assert.IsFalse(R) + }) + it('Should guard InstanceObject 3', () => { + const R = ValueGuard.IsInstanceObject([]) + Assert.IsFalse(R) + }) + it('Should guard InstanceObject 4', () => { + const R = ValueGuard.IsInstanceObject(new (class {})()) + Assert.IsTrue(R) + }) + // ----------------------------------------------------- // IsPromise // ----------------------------------------------------- - it('Should guard promise 1', () => { + it('Should guard Promise 1', () => { const R = ValueGuard.IsPromise(Promise.resolve(1)) Assert.IsTrue(R) }) - it('Should guard promise 2', () => { + it('Should guard Promise 2', () => { const R = ValueGuard.IsPromise(new (class {})()) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsSymbol // ----------------------------------------------------- - it('Should guard symbol 1', () => { + it('Should guard Symbol 1', () => { const R = ValueGuard.IsSymbol(Symbol(1)) Assert.IsTrue(R) }) - it('Should guard symbol 2', () => { + it('Should guard Symbol 2', () => { const R = ValueGuard.IsSymbol(1) Assert.IsFalse(R) }) // ----------------------------------------------------- // IsTypedArray // ----------------------------------------------------- - it('Should guard typed array 1', () => { + it('Should guard TypedArray 1', () => { const R = ValueGuard.IsTypedArray(new Uint8Array(1)) Assert.IsTrue(R) }) - it('Should guard typed array 2', () => { + it('Should guard TypedArray 2', () => { const R = ValueGuard.IsTypedArray(new Float32Array(1)) Assert.IsTrue(R) }) - it('Should guard typed array 3', () => { + it('Should guard TypedArray 3', () => { const R = ValueGuard.IsTypedArray(new ArrayBuffer(1)) Assert.IsFalse(R) }) - it('Should guard typed array 4', () => { + it('Should guard TypedArray 4', () => { const R = ValueGuard.IsTypedArray(1) Assert.IsFalse(R) }) // ----------------------------------------------------- + // IsInt8Array + // ----------------------------------------------------- + it('Should guard Int8Array 1', () => { + const R = ValueGuard.IsInt8Array(new Int8Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Int8Array 2', () => { + const R = ValueGuard.IsInt8Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Int8Array 2', () => { + const R = ValueGuard.IsInt8Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- // IsUint8Array // ----------------------------------------------------- - it('Should guard uint8array 1', () => { + it('Should guard Uint8Array 1', () => { const R = ValueGuard.IsUint8Array(new Uint8Array(1)) Assert.IsTrue(R) }) - it('Should guard uint8array 2', () => { + it('Should guard Uint8Array 2', () => { const R = ValueGuard.IsUint8Array(new Float32Array(1)) Assert.IsFalse(R) }) - it('Should guard uint8array 2', () => { + it('Should guard Uint8Array 2', () => { const R = ValueGuard.IsUint8Array(1) Assert.IsFalse(R) }) // ----------------------------------------------------- + // IsUint8ClampedArray + // ----------------------------------------------------- + it('Should guard Uint8ClampedArray 1', () => { + const R = ValueGuard.IsUint8ClampedArray(new Uint8ClampedArray(1)) + Assert.IsTrue(R) + }) + it('Should guard Uint8ClampedArray 2', () => { + const R = ValueGuard.IsUint8ClampedArray(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Uint8ClampedArray 2', () => { + const R = ValueGuard.IsUint8ClampedArray(1) + Assert.IsFalse(R) + }) + + // ----------------------------------------------------- + // IsInt16Array + // ----------------------------------------------------- + it('Should guard Int16Array 1', () => { + const R = ValueGuard.IsInt16Array(new Int16Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Int16Array 2', () => { + const R = ValueGuard.IsInt16Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Int16Array 2', () => { + const R = ValueGuard.IsInt16Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsUint16Array + // ----------------------------------------------------- + it('Should guard Uint16Array 1', () => { + const R = ValueGuard.IsUint16Array(new Uint16Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Uint16Array 2', () => { + const R = ValueGuard.IsUint16Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Uint16Array 2', () => { + const R = ValueGuard.IsUint16Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsInt32Array + // ----------------------------------------------------- + it('Should guard Int32Array 1', () => { + const R = ValueGuard.IsInt32Array(new Int32Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Int32Array 2', () => { + const R = ValueGuard.IsInt32Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Int32Array 2', () => { + const R = ValueGuard.IsInt32Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsUint32Array + // ----------------------------------------------------- + it('Should guard Uint32Array 1', () => { + const R = ValueGuard.IsUint32Array(new Uint32Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Uint32Array 2', () => { + const R = ValueGuard.IsUint32Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Uint32Array 2', () => { + const R = ValueGuard.IsUint32Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsFloat32Array + // ----------------------------------------------------- + it('Should guard Float32Array 1', () => { + const R = ValueGuard.IsFloat32Array(new Float32Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Float32Array 2', () => { + const R = ValueGuard.IsFloat32Array(new Float64Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Float32Array 2', () => { + const R = ValueGuard.IsFloat32Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsFloat64Array + // ----------------------------------------------------- + it('Should guard Float64Array 1', () => { + const R = ValueGuard.IsFloat64Array(new Float64Array(1)) + Assert.IsTrue(R) + }) + it('Should guard Float64Array 2', () => { + const R = ValueGuard.IsFloat64Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Float64Array 2', () => { + const R = ValueGuard.IsFloat64Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBigInt64Array + // ----------------------------------------------------- + it('Should guard BigInt64Array 1', () => { + const R = ValueGuard.IsBigInt64Array(new BigInt64Array(1)) + Assert.IsTrue(R) + }) + it('Should guard BigInt64Array 2', () => { + const R = ValueGuard.IsBigInt64Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard BigInt64Array 2', () => { + const R = ValueGuard.IsBigInt64Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsBigUint64Array + // ----------------------------------------------------- + it('Should guard BigUint64Array 1', () => { + const R = ValueGuard.IsBigUint64Array(new BigUint64Array(1)) + Assert.IsTrue(R) + }) + it('Should guard BigUint64Array 2', () => { + const R = ValueGuard.IsBigUint64Array(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard BigUint64Array 2', () => { + const R = ValueGuard.IsBigUint64Array(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsMap + // ----------------------------------------------------- + it('Should guard Map 1', () => { + const R = ValueGuard.IsMap(new Map()) + Assert.IsTrue(R) + }) + it('Should guard Map 2', () => { + const R = ValueGuard.IsMap(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Map 2', () => { + const R = ValueGuard.IsMap(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsSet + // ----------------------------------------------------- + it('Should guard Set 1', () => { + const R = ValueGuard.IsSet(new Set()) + Assert.IsTrue(R) + }) + it('Should guard Set 2', () => { + const R = ValueGuard.IsSet(new Float32Array(1)) + Assert.IsFalse(R) + }) + it('Should guard Set 2', () => { + const R = ValueGuard.IsSet(1) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- // IsValueType // ----------------------------------------------------- it('Should guard value type 1', () => { From ea0fa093a0857cf227789829893f11ce8380d8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20S=C3=B6derlund?= Date: Thu, 7 Mar 2024 04:40:48 +0100 Subject: [PATCH 238/369] Ecosystem (#775) - Added form library Superforms --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 9dc77a5cc..0a246c134 100644 --- a/readme.md +++ b/readme.md @@ -1694,6 +1694,7 @@ The following is a list of community packages that offer general tooling, extend | [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | +| [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | From abe71c68cd7099d05a62aa78088113f021623dbc Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 7 Mar 2024 14:44:01 +0900 Subject: [PATCH 239/369] TypeScript 5.4.2 --- hammer.mjs | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 661d90a28..1326d2195 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 2b11a6af0..c70b6b36f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.3.3" + "typescript": "^5.4.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1644,9 +1644,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2874,9 +2874,9 @@ "dev": true }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 32692ddaf..6fa87a159 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.3.3" + "typescript": "^5.4.2" } } From 9fa2e2801c5f267c2624a5a39cefd7d9e3463bef Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 20 Mar 2024 02:25:53 +0900 Subject: [PATCH 240/369] Revision 0.32.16 (#791) * Update Indexed Union Evaluation * Use Reduce Strategy for Union and Intersect Value Conversion * Add Path to Transform Codec Errors * Support Control Character Escape in Template Literal Syntax * Intersect Union Convert Test Case * Revision 0.32.16 --- package-lock.json | 4 +- package.json | 2 +- src/type/indexed/indexed.ts | 33 ++++- src/type/template-literal/parse.ts | 29 ++++- src/type/template-literal/syntax.ts | 5 +- src/value/convert/convert.ts | 13 +- src/value/transform/decode.ts | 117 ++++++++++-------- src/value/transform/encode.ts | 113 +++++++++-------- src/value/value/value.ts | 2 +- test/runtime/type/guard/composite.ts | 8 +- test/runtime/type/guard/record.ts | 14 +++ .../runtime/type/template-literal/generate.ts | 9 +- test/runtime/type/template-literal/parse.ts | 4 +- test/runtime/value/convert/intersect.ts | 27 ++-- test/runtime/value/convert/union.ts | 25 +++- test/static/composite.ts | 29 ++++- test/static/record.ts | 14 +++ test/static/template-literal.ts | 35 ++++++ 18 files changed, 333 insertions(+), 150 deletions(-) diff --git a/package-lock.json b/package-lock.json index c70b6b36f..3804e5462 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.15", + "version": "0.32.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.15", + "version": "0.32.16", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 6fa87a159..e4c6fe9ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.15", + "version": "0.32.16", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index 2589aeaf6..f3df37e77 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -88,12 +88,41 @@ function FromIntersect(T: [...T], K: } // ------------------------------------------------------------------ // FromUnionRest +// +// The following accept a tuple of indexed key results. When evaluating +// these results, we check if any result evaluated to TNever. For key +// indexed unions, a TNever result indicates that the key was not +// present on the variant. In these cases, we must evaluate the indexed +// union to TNever (as given by a [] result). This logic aligns to the +// following behaviour. +// +// Non-Overlapping Union +// +// type A = { a: string } +// type B = { b: string } +// type C = (A | B) & { a: number } // C is { a: number } +// +// Overlapping Union +// +// type A = { a: string } +// type B = { a: string } +// type C = (A | B) & { a: number } // C is { a: never } +// // ------------------------------------------------------------------ // prettier-ignore -type TFromUnionRest = T +type TFromUnionRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TNever + ? [] + : TFromUnionRest + : Acc // prettier-ignore function FromUnionRest(T: [...T]): TFromUnionRest { - return T as never // review this + return ( + T.some(L => IsNever(L)) + ? [] + : T + ) as never } // ------------------------------------------------------------------ // FromUnion diff --git a/src/type/template-literal/parse.ts b/src/type/template-literal/parse.ts index 7a2730f44..6a36c013e 100644 --- a/src/type/template-literal/parse.ts +++ b/src/type/template-literal/parse.ts @@ -40,23 +40,42 @@ export type Expression = ExpressionAnd | ExpressionOr | ExpressionConst export type ExpressionConst = { type: 'const'; const: string } export type ExpressionAnd = { type: 'and'; expr: Expression[] } export type ExpressionOr = { type: 'or'; expr: Expression[] } +// ------------------------------------------------------------------- +// Unescape +// +// Unescape for these control characters specifically. Note that this +// function is only called on non union group content, and where we +// still want to allow the user to embed control characters in that +// content. For review. +// ------------------------------------------------------------------- // prettier-ignore +function Unescape(pattern: string) { + return pattern + .replace(/\\\$/g, '$') + .replace(/\\\*/g, '*') + .replace(/\\\^/g, '^') + .replace(/\\\|/g, '|') + .replace(/\\\(/g, '(') + .replace(/\\\)/g, ')') +} +// ------------------------------------------------------------------- +// Control Characters +// ------------------------------------------------------------------- function IsNonEscaped(pattern: string, index: number, char: string) { return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 } -// prettier-ignore function IsOpenParen(pattern: string, index: number) { return IsNonEscaped(pattern, index, '(') } -// prettier-ignore function IsCloseParen(pattern: string, index: number) { return IsNonEscaped(pattern, index, ')') } -// prettier-ignore function IsSeparator(pattern: string, index: number) { return IsNonEscaped(pattern, index, '|') } -// prettier-ignore +// ------------------------------------------------------------------- +// Control Groups +// ------------------------------------------------------------------- function IsGroup(pattern: string) { if (!(IsOpenParen(pattern, 0) && IsCloseParen(pattern, pattern.length - 1))) return false let count = 0 @@ -155,7 +174,7 @@ export function TemplateLiteralParse(pattern: string): Expression { IsGroup(pattern) ? TemplateLiteralParse(InGroup(pattern)) : IsPrecedenceOr(pattern) ? Or(pattern) : IsPrecedenceAnd(pattern) ? And(pattern) : - { type: 'const', const: pattern } + { type: 'const', const: Unescape(pattern) } ) } // ------------------------------------------------------------------ diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts index 1250ee651..3352b10b6 100644 --- a/src/type/template-literal/syntax.ts +++ b/src/type/template-literal/syntax.ts @@ -100,7 +100,10 @@ type FromTerminal = // prettier-ignore type FromString = T extends `{${infer L}}${infer R}` ? [FromTerminal, ...FromString] : - T extends `${infer L}$${infer R}` ? [TLiteral, ...FromString] : + // note: to correctly handle $ characters encoded in the sequence, we need to + // lookahead and test against opening and closing union groups. + T extends `${infer L}$\{${infer R1}\}${infer R2}` ? [TLiteral, ...FromString<`{${R1}}`>, ...FromString] : + T extends `${infer L}$\{${infer R1}\}` ? [TLiteral, ...FromString<`{${R1}}`>] : T extends `${infer L}` ? [TLiteral] : [] diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 35afea3c8..dd0e2d1d2 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -183,11 +183,8 @@ function FromDate(schema: TDate, references: TSchema[], value: any): unknown { function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown { return TryConvertInteger(value) } -// prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], value: any): unknown { - const allObjects = schema.allOf.every(schema => IsObjectType(schema)) - if(allObjects) return Visit(Composite(schema.allOf as TObject[]), references, value) - return Visit(schema.allOf[0], references, value) // todo: fix this + return schema.allOf.reduce((value, schema) => Visit(schema, references, value), value) } function FromLiteral(schema: TLiteral, references: TSchema[], value: any): unknown { return TryConvertLiteral(schema, value) @@ -243,13 +240,7 @@ function FromUndefined(schema: TUndefined, references: TSchema[], value: any): u return TryConvertUndefined(value) } function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { - for (const subschema of schema.anyOf) { - const converted = Visit(subschema, references, value) - if (Check(subschema, references, converted)) { - return converted - } - } - return value + return schema.anyOf.reduce((value, schema) => Visit(schema, references, value), value) } function Visit(schema: TSchema, references: TSchema[], value: any): unknown { const references_ = IsString(schema.$id) ? [...references, schema] : references diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 3169293ac..02982d0fd 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -57,153 +57,164 @@ import { IsTransform, IsSchema } from '../../type/guard/type' // Errors // ------------------------------------------------------------------ // thrown externally +// prettier-ignore export class TransformDecodeCheckError extends TypeBoxError { - constructor(public readonly schema: TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to decode due to invalid value`) + constructor( + public readonly schema: TSchema, + public readonly value: unknown, + public readonly error: ValueError + ) { + super(`Unable to decode value as it does not match the expected schema`) } } +// prettier-ignore export class TransformDecodeError extends TypeBoxError { - constructor(public readonly schema: TSchema, public readonly value: unknown, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown error'}`) + constructor( + public readonly schema: TSchema, + public readonly path: string, + public readonly value: unknown, + public readonly error: Error, + ) { + super(error instanceof Error ? error.message : 'Unknown error') } } // ------------------------------------------------------------------ // Decode // ------------------------------------------------------------------ // prettier-ignore -function Default(schema: TSchema, value: any) { +function Default(schema: TSchema, path: string, value: any) { try { return IsTransform(schema) ? schema[TransformKind].Decode(value) : value } catch (error) { - throw new TransformDecodeError(schema, value, error) + throw new TransformDecodeError(schema, path, value, error as Error) } } // prettier-ignore -function FromArray(schema: TArray, references: TSchema[], value: any): any { +function FromArray(schema: TArray, references: TSchema[], path: string, value: any): any { return (IsArray(value)) - ? Default(schema, value.map((value: any) => Visit(schema.items, references, value))) - : Default(schema, value) + ? Default(schema, path, value.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value))) + : Default(schema, path, value) } // prettier-ignore -function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { - if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, value) +function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { + if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return (key in value) - ? { ...value, [key]: Visit(Index(schema, [key]), references, value[key]) } + ? { ...value, [key]: Visit(Index(schema, [key]), references, `${path}/${key}`, value[key]) } : value }, value) if (!IsTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) + return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const unevaluatedProperties = schema.unevaluatedProperties as TSchema const unknownProperties = unknownKeys.reduce((value, key) => { return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + ? { ...value, [key]: Default(unevaluatedProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) - return Default(schema, unknownProperties) + return Default(schema, path, unknownProperties) } -function FromNot(schema: TNot, references: TSchema[], value: any) { - return Default(schema, Visit(schema.not, references, value)) +function FromNot(schema: TNot, references: TSchema[], path: string, value: any) { + return Default(schema, path, Visit(schema.not, references, path, value)) } // prettier-ignore -function FromObject(schema: TObject, references: TSchema[], value: any) { - if (!IsStandardObject(value)) return Default(schema, value) +function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { + if (!IsStandardObject(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) const knownProperties = knownKeys.reduce((value, key) => { return (key in value) - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + ? { ...value, [key]: Visit(schema.properties[key], references, `${path}/${key}`, value[key]) } : value }, value) if (!IsSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) + return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema const unknownProperties = unknownKeys.reduce((value, key) => { return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } + ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) - return Default(schema, unknownProperties) + return Default(schema, path, unknownProperties) } // prettier-ignore -function FromRecord(schema: TRecord, references: TSchema[], value: any) { - if (!IsStandardObject(value)) return Default(schema, value) +function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { + if (!IsStandardObject(value)) return Default(schema, path, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, `${path}/${key}`, value[key]) } : value }, value) if (!IsSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) + return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema const unknownProperties = unknownKeys.reduce((value, key) => { return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } + ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) - return Default(schema, unknownProperties) + return Default(schema, path, unknownProperties) } // prettier-ignore -function FromRef(schema: TRef, references: TSchema[], value: any) { +function FromRef(schema: TRef, references: TSchema[], path: string, value: any) { const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) + return Default(schema, path, Visit(target, references, path, value)) } // prettier-ignore -function FromThis(schema: TThis, references: TSchema[], value: any) { +function FromThis(schema: TThis, references: TSchema[], path: string, value: any) { const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) + return Default(schema, path, Visit(target, references, path, value)) } // prettier-ignore -function FromTuple(schema: TTuple, references: TSchema[], value: any) { +function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any) { return (IsArray(value) && IsArray(schema.items)) - ? Default(schema, schema.items.map((schema, index) => Visit(schema, references, value[index]))) - : Default(schema, value) + ? Default(schema, path, schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value[index]))) + : Default(schema, path, value) } // prettier-ignore -function FromUnion(schema: TUnion, references: TSchema[], value: any) { +function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any) { for (const subschema of schema.anyOf) { if (!Check(subschema, references, value)) continue // note: ensure interior is decoded first - const decoded = Visit(subschema, references, value) - return Default(schema, decoded) + const decoded = Visit(subschema, references, path, value) + return Default(schema, path, decoded) } - return Default(schema, value) + return Default(schema, path, value) } // prettier-ignore -function Visit(schema: TSchema, references: TSchema[], value: any): any { +function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { const references_ = typeof schema.$id === 'string' ? [...references, schema] : references const schema_ = schema as any switch (schema[Kind]) { case 'Array': - return FromArray(schema_, references_, value) + return FromArray(schema_, references_, path, value) case 'Intersect': - return FromIntersect(schema_, references_, value) + return FromIntersect(schema_, references_, path, value) case 'Not': - return FromNot(schema_, references_, value) + return FromNot(schema_, references_, path, value) case 'Object': - return FromObject(schema_, references_, value) + return FromObject(schema_, references_, path, value) case 'Record': - return FromRecord(schema_, references_, value) + return FromRecord(schema_, references_, path, value) case 'Ref': - return FromRef(schema_, references_, value) + return FromRef(schema_, references_, path, value) case 'Symbol': - return Default(schema_, value) + return Default(schema_, path, value) case 'This': - return FromThis(schema_, references_, value) + return FromThis(schema_, references_, path, value) case 'Tuple': - return FromTuple(schema_, references_, value) + return FromTuple(schema_, references_, path, value) case 'Union': - return FromUnion(schema_, references_, value) + return FromUnion(schema_, references_, path, value) default: - return Default(schema_, value) + return Default(schema_, path, value) } } /** @@ -212,5 +223,5 @@ function Visit(schema: TSchema, references: TSchema[], value: any): any { * undefined behavior. Refer to the `Value.Decode()` for implementation details. */ export function TransformDecode(schema: TSchema, references: TSchema[], value: unknown): unknown { - return Visit(schema, references, value) + return Visit(schema, references, '', value) } diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 695a7ea7c..2a2b8f465 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -56,13 +56,24 @@ import { IsTransform, IsSchema } from '../../type/guard/type' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ +// prettier-ignore export class TransformEncodeCheckError extends TypeBoxError { - constructor(public readonly schema: TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to encode due to invalid value`) + constructor( + public readonly schema: TSchema, + public readonly value: unknown, + public readonly error: ValueError + ) { + super(`The encoded value does not match the expected schema`) } } +// prettier-ignore export class TransformEncodeError extends TypeBoxError { - constructor(public readonly schema: TSchema, public readonly value: unknown, error: any) { + constructor( + public readonly schema: TSchema, + public readonly path: string, + public readonly value: unknown, + public readonly error: Error, + ) { super(`${error instanceof Error ? error.message : 'Unknown error'}`) } } @@ -70,53 +81,53 @@ export class TransformEncodeError extends TypeBoxError { // Encode // ------------------------------------------------------------------ // prettier-ignore -function Default(schema: TSchema, value: any) { +function Default(schema: TSchema, path: string, value: any) { try { return IsTransform(schema) ? schema[TransformKind].Encode(value) : value } catch (error) { - throw new TransformEncodeError(schema, value, error) + throw new TransformEncodeError(schema, path, value, error as Error) } } // prettier-ignore -function FromArray(schema: TArray, references: TSchema[], value: any): any { - const defaulted = Default(schema, value) +function FromArray(schema: TArray, references: TSchema[], path: string, value: any): any { + const defaulted = Default(schema, path, value) return IsArray(defaulted) - ? defaulted.map((value: any) => Visit(schema.items, references, value)) + ? defaulted.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value)) : defaulted } // prettier-ignore -function FromIntersect(schema: TIntersect, references: TSchema[], value: any) { - const defaulted = Default(schema, value) +function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { + const defaulted = Default(schema, path, value) if (!IsStandardObject(value) || IsValueType(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return key in defaulted - ? { ...value, [key]: Visit(Index(schema, [key]), references, value[key]) } + ? { ...value, [key]: Visit(Index(schema, [key]), references, `${path}/${key}`, value[key]) } : value }, defaulted) if (!IsTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) + return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const unevaluatedProperties = schema.unevaluatedProperties as TSchema return unknownKeys.reduce((value, key) => { return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } + ? { ...value, [key]: Default(unevaluatedProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) } // prettier-ignore -function FromNot(schema: TNot, references: TSchema[], value: any) { - return Default(schema.not, Default(schema, value)) +function FromNot(schema: TNot, references: TSchema[], path: string, value: any) { + return Default(schema.not, path, Default(schema, path, value)) } // prettier-ignore -function FromObject(schema: TObject, references: TSchema[], value: any) { - const defaulted = Default(schema, value) +function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { + const defaulted = Default(schema, path, value) if (!IsStandardObject(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = knownKeys.reduce((value, key) => { return key in value - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + ? { ...value, [key]: Visit(schema.properties[key], references, `${path}/${key}`, value[key]) } : value }, defaulted) if (!IsSchema(schema.additionalProperties)) { @@ -126,90 +137,90 @@ function FromObject(schema: TObject, references: TSchema[], value: any) { const additionalProperties = schema.additionalProperties as TSchema return unknownKeys.reduce((value, key) => { return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } + ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) } // prettier-ignore -function FromRecord(schema: TRecord, references: TSchema[], value: any) { - const defaulted = Default(schema, value) as Record +function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { + const defaulted = Default(schema, path, value) as Record if (!IsStandardObject(value)) return defaulted const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } + ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, `${path}/${key}`, value[key]) } : value }, defaulted) if (!IsSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) + return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema return unknownKeys.reduce((value, key) => { return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } + ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } : value }, knownProperties) } // prettier-ignore -function FromRef(schema: TRef, references: TSchema[], value: any) { +function FromRef(schema: TRef, references: TSchema[], path: string, value: any) { const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) + const resolved = Visit(target, references, path, value) + return Default(schema, path, resolved) } // prettier-ignore -function FromThis(schema: TThis, references: TSchema[], value: any) { +function FromThis(schema: TThis, references: TSchema[], path: string, value: any) { const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) + const resolved = Visit(target, references, path, value) + return Default(schema, path, resolved) } // prettier-ignore -function FromTuple(schema: TTuple, references: TSchema[], value: any) { - const value1 = Default(schema, value) - return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] +function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any) { + const value1 = Default(schema, path, value) + return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value1[index])) : [] } // prettier-ignore -function FromUnion(schema: TUnion, references: TSchema[], value: any) { +function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any) { // test value against union variants for (const subschema of schema.anyOf) { if (!Check(subschema, references, value)) continue - const value1 = Visit(subschema, references, value) - return Default(schema, value1) + const value1 = Visit(subschema, references, path, value) + return Default(schema, path, value1) } // test transformed value against union variants for (const subschema of schema.anyOf) { - const value1 = Visit(subschema, references, value) + const value1 = Visit(subschema, references, path, value) if (!Check(schema, references, value1)) continue - return Default(schema, value1) + return Default(schema, path, value1) } - return Default(schema, value) + return Default(schema, path, value) } // prettier-ignore -function Visit(schema: TSchema, references: TSchema[], value: any): any { +function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { const references_ = typeof schema.$id === 'string' ? [...references, schema] : references const schema_ = schema as any switch (schema[Kind]) { case 'Array': - return FromArray(schema_, references_, value) + return FromArray(schema_, references_, path, value) case 'Intersect': - return FromIntersect(schema_, references_, value) + return FromIntersect(schema_, references_, path, value) case 'Not': - return FromNot(schema_, references_, value) + return FromNot(schema_, references_, path, value) case 'Object': - return FromObject(schema_, references_, value) + return FromObject(schema_, references_, path, value) case 'Record': - return FromRecord(schema_, references_, value) + return FromRecord(schema_, references_, path, value) case 'Ref': - return FromRef(schema_, references_, value) + return FromRef(schema_, references_, path, value) case 'This': - return FromThis(schema_, references_, value) + return FromThis(schema_, references_, path, value) case 'Tuple': - return FromTuple(schema_, references_, value) + return FromTuple(schema_, references_, path, value) case 'Union': - return FromUnion(schema_, references_, value) + return FromUnion(schema_, references_, path, value) default: - return Default(schema_, value) + return Default(schema_, path, value) } } /** @@ -219,5 +230,5 @@ function Visit(schema: TSchema, references: TSchema[], value: any): any { * `Value.Encode()` function for implementation details. */ export function TransformEncode(schema: TSchema, references: TSchema[], value: unknown): unknown { - return Visit(schema, references, value) + return Visit(schema, references, '', value) } diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 6b5febc8b..f64dc18e5 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -113,7 +113,7 @@ export function Encode>(schema: T, value: export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] const encoded = TransformEncode(schema, references, value) - if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) + if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) return encoded } /** Returns an iterator for each error in this value. */ diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/composite.ts index d3e9557a2..351041bb2 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/composite.ts @@ -130,8 +130,9 @@ describe('type/guard/TComposite', () => { // ---------------------------------------------------------------- // Union // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/789 // prettier-ignore - it('Should composite Union 1', () => { + it('Should composite Union 1 (non-overlapping)', () => { const T = Type.Composite([ Type.Union([ Type.Object({ x: Type.Number() }), @@ -142,11 +143,12 @@ describe('type/guard/TComposite', () => { ]) ]) Assert.IsEqual(T, Type.Object({ - z: Type.Intersect([Type.Union([Type.Never(), Type.Never()]), Type.Number()]) + z: Type.Number() })) }) + // https://github.com/sinclairzx81/typebox/issues/789 // prettier-ignore - it('Should composite Union 2', () => { + it('Should composite Union 2 (overlapping)', () => { const T = Type.Composite([ Type.Union([ Type.Object({ x: Type.Number() }), diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/record.ts index 89ec3dfae..f0c11b6f9 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/record.ts @@ -139,4 +139,18 @@ describe('type/guard/TRecord', () => { const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) + // ------------------------------------------------------------------ + // Evaluated: Dollar Sign Escape + // https://github.com/sinclairzx81/typebox/issues/794 + // ------------------------------------------------------------------ + // prettier-ignore + { + const K = Type.TemplateLiteral('$prop${A|B|C}') // issue + const T = Type.Record(K, Type.String()) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propA)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propB)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propC)) + Assert.IsEqual(T.required, ['$propA', '$propB', '$propC']) + } }) diff --git a/test/runtime/type/template-literal/generate.ts b/test/runtime/type/template-literal/generate.ts index 2178571bc..197754750 100644 --- a/test/runtime/type/template-literal/generate.ts +++ b/test/runtime/type/template-literal/generate.ts @@ -44,12 +44,12 @@ describe('type/template-literal/TemplateLiteralExpressionGenerate', () => { it('Expression 2', () => { const E = TemplateLiteralParse('\\)') const R = [...TemplateLiteralExpressionGenerate(E)] - Assert.IsEqual(R, ['\\)']) + Assert.IsEqual(R, [')']) }) it('Expression 3', () => { const E = TemplateLiteralParse('\\(') const R = [...TemplateLiteralExpressionGenerate(E)] - Assert.IsEqual(R, ['\\(']) + Assert.IsEqual(R, ['(']) }) it('Expression 4', () => { const E = TemplateLiteralParse('') @@ -196,4 +196,9 @@ describe('type/template-literal/TemplateLiteralExpressionGenerate', () => { const R = [...TemplateLiteralExpressionGenerate(E)] Assert.IsEqual(R, ['011', '001']) }) + it('Expression 33', () => { + const E = TemplateLiteralParse('\\$prop(1|2|3)') + const R = [...TemplateLiteralExpressionGenerate(E)] + Assert.IsEqual(R, ['$prop1', '$prop2', '$prop3']) + }) }) diff --git a/test/runtime/type/template-literal/parse.ts b/test/runtime/type/template-literal/parse.ts index 57db98fc6..679cb4b21 100644 --- a/test/runtime/type/template-literal/parse.ts +++ b/test/runtime/type/template-literal/parse.ts @@ -84,14 +84,14 @@ describe('type/template-literal/TemplateLiteralParser', () => { const E = TemplateLiteralParse('\\)') Assert.IsEqual(E, { type: 'const', - const: '\\)', + const: ')', }) }) it('Expression 3', () => { const E = TemplateLiteralParse('\\(') Assert.IsEqual(E, { type: 'const', - const: '\\(', + const: '(', }) }) it('Expression 4', () => { diff --git a/test/runtime/value/convert/intersect.ts b/test/runtime/value/convert/intersect.ts index 5c2e2bb37..191150a3b 100644 --- a/test/runtime/value/convert/intersect.ts +++ b/test/runtime/value/convert/intersect.ts @@ -12,7 +12,10 @@ describe('value/convert/Intersect', () => { const R = Value.Convert(T, { x: '1', y: '2' }) Assert.IsEqual(R, { x: 1, y: 2 }) }) - it('Should not convert for non object exclusive intersect', () => { + // ---------------------------------------------------------------- + // Intersection Complex + // ---------------------------------------------------------------- + it('Should complex intersect 1', () => { // prettier-ignore const T = Type.Intersect([ Type.Number(), @@ -20,26 +23,26 @@ describe('value/convert/Intersect', () => { Type.Object({ y: Type.Number() }) ]) const R = Value.Convert(T, { x: '1', y: '2' }) - Assert.IsEqual(R, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 1, y: 2 }) }) - it('Should convert first type for object exclusive intersect 1', () => { + it('Should complex intersect 2', () => { // prettier-ignore const T = Type.Intersect([ - Type.Number(), Type.Object({ x: Type.Number() }), - Type.Object({ y: Type.Number() }) + Type.Object({ y: Type.Number() }), + Type.Number(), ]) - const R = Value.Convert(T, '123') - Assert.IsEqual(R, 123) + const R = Value.Convert(T, { x: '3', y: '4' }) + Assert.IsEqual(R, { x: 3, y: 4 }) }) - it('Should convert first type for object exclusive intersect 2', () => { + it('Should complex intersect 3', () => { // prettier-ignore const T = Type.Intersect([ - Type.Object({ x: Type.Number() }), - Type.Object({ y: Type.Number() }), Type.Number(), + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) ]) - const R = Value.Convert(T, { x: '3', y: '4' }) - Assert.IsEqual(R, { x: 3, y: '4' }) + const R = Value.Convert(T, '123') + Assert.IsEqual(R, 123) }) }) diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index 12f731b08..227e76c95 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -1,4 +1,4 @@ -import { Value } from '@sinclair/typebox/value' +import { Convert, Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' @@ -14,11 +14,30 @@ describe('value/convert/Union', () => { Assert.IsEqual(V2, { x: null }) Assert.IsEqual(V3, { x: 'hello' }) }) - it('Should convert first variant in ambiguous conversion', () => { + it('Should convert last variant in ambiguous conversion', () => { const T = Type.Object({ x: Type.Union([Type.Boolean(), Type.Number()]), }) const V1 = Value.Convert(T, { x: '1' }) - Assert.IsEqual(V1, { x: true }) + Assert.IsEqual(V1, { x: 1 }) + }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/787 + // ---------------------------------------------------------------- + // prettier-ignore + it('Should convert Intersect Union', () => { + const T = Type.Intersect([ + Type.Union([ + Type.Object({ a: Type.Number() }), + Type.Object({ b: Type.Number() }), + ]), + Type.Object({ c: Type.Number() }), + ]) + const A = Convert(T, { a: '1', c: '2' }) + const B = Convert(T, { b: '1', c: '2' }) + const C = Convert(T, { a: '1', b: '2', c: '3' }) + Assert.IsEqual(A, { a: 1, c: 2 }) + Assert.IsEqual(B, { b: 1, c: 2 }) + Assert.IsEqual(C, { a: 1, b: 2, c: 3 }) }) }) diff --git a/test/static/composite.ts b/test/static/composite.ts index e2b440f41..f124f6dbc 100644 --- a/test/static/composite.ts +++ b/test/static/composite.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type, TOptional, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox' +import { Type, TOptional, TObject, TUnion, TIntersect, TNumber, TString, TBoolean } from '@sinclair/typebox' // ---------------------------------------------------------------------------- // Overlapping - Non Varying @@ -184,3 +184,30 @@ import { Type, TOptional, TObject, TIntersect, TNumber, TBoolean } from '@sincla ]) ]) } +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T: TObject<{ + x: TNumber; + }> = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Object({ x: Type.Number() }) + ]) +} +// prettier-ignore +{ + const T: TObject<{ + x: TIntersect<[TUnion<[TString, TString]>, TNumber]>; + }> = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.String() }), + Type.Object({ x: Type.String() }) + ]), + Type.Object({ x: Type.Number() }) + ]) +} diff --git a/test/static/record.ts b/test/static/record.ts index e284ce053..da8a0288f 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -175,3 +175,17 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Record(Type.Enum(E), Type.Number()) Expect(T).ToStatic<{ [x: string]: number }> } +// ------------------------------------------------------------------ +// Dollar Sign Escape +// https://github.com/sinclairzx81/typebox/issues/794 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const K = Type.TemplateLiteral('$prop${A|B|C}') // issue + const T = Type.Record(K, Type.String()) + Expect(T).ToStatic<{ + '$propA': string, + '$propB': string, + '$propC': string + }>() +} diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts index 670b0b694..773ee1fa1 100644 --- a/test/static/template-literal.ts +++ b/test/static/template-literal.ts @@ -77,3 +77,38 @@ import { Type } from '@sinclair/typebox' const T = Type.TemplateLiteral([Type.Literal('hello'), A]) Expect(T).ToStatic<'helloA' | 'helloB'>() } +// ------------------------------------------------------------------ +// Dollar Sign Escape +// https://github.com/sinclairzx81/typebox/issues/794 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.TemplateLiteral('$prop${A|B|C}') // issue + Expect(T).ToStatic<'$propA' | '$propB' | '$propC'>() +} +// prettier-ignore +{ + const T = Type.TemplateLiteral('$prop${A|B|C}x') // trailing + Expect(T).ToStatic<'$propAx' | '$propBx' | '$propCx'>() +} +// prettier-ignore +{ + const T = Type.TemplateLiteral('$prop${A|B|C}x}') // non-greedy + Expect(T).ToStatic<'$propAx}' | '$propBx}' | '$propCx}'>() +} +// prettier-ignore +{ + const T = Type.TemplateLiteral('$prop${A|B|C}x}${X|Y}') // distributive - non-greedy + Expect(T).ToStatic< + '$propAx}X' | '$propBx}X' | '$propCx}X' | + '$propAx}Y' | '$propBx}Y' | '$propCx}Y' + >() +} +// prettier-ignore +{ + const T = Type.TemplateLiteral('$prop${A|B|C}x}${X|Y}x') // distributive - non-greedy - trailing + Expect(T).ToStatic< + '$propAx}Xx' | '$propBx}Xx' | '$propCx}Xx' | + '$propAx}Yx' | '$propBx}Yx' | '$propCx}Yx' + >() +} From ec27004427a2a669a4e962b5afb7b75bca3d20c3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 20 Mar 2024 18:45:53 +0900 Subject: [PATCH 241/369] Revision 0.32.17 (#799) * Detect Any on StaticDecode * Revision 0.32.17 --- package-lock.json | 4 ++-- package.json | 2 +- src/type/static/static.ts | 3 ++- test/static/transform.ts | 11 +++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3804e5462..5077496c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.16", + "version": "0.32.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.16", + "version": "0.32.17", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e4c6fe9ab..6ea8b8b95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.16", + "version": "0.32.17", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/static/static.ts b/src/type/static/static.ts index 488055c76..7e346b7ac 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -85,8 +85,9 @@ export type TDecodeType = ( // ------------------------------------------------------------------ // Static // ------------------------------------------------------------------ +export type StaticDecodeIsAny = boolean extends (T extends TSchema ? true : false) ? true : false /** Creates an decoded static type from a TypeBox type */ -export type StaticDecode = Static, P> +export type StaticDecode = StaticDecodeIsAny extends true ? unknown : Static, P> /** Creates an encoded static type from a TypeBox type */ export type StaticEncode = Static /** Creates a static type from a TypeBox type */ diff --git a/test/static/transform.ts b/test/static/transform.ts index 1f4176289..2f5e2411f 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -1,4 +1,7 @@ import { Type, TSchema, Static, StaticDecode, TObject, TNumber } from '@sinclair/typebox' +import { TypeCheck } from '@sinclair/typebox/compiler' +import { Value } from '@sinclair/typebox/value' + import { Expect } from './assert' { // string > number @@ -309,3 +312,11 @@ import { Expect } from './assert' Expect(T).ToStaticDecode Date>() Expect(T).ToStaticEncode number>() } +// ------------------------------------------------------------- +// https://github.com/sinclairzx81/typebox/issues/798 +// ------------------------------------------------------------- +{ + const c1: TypeCheck = {} as any + const x1 = c1.Decode({}) + const x2 = Value.Decode({} as any, {}) +} From 8a6018dec4ae287edce9f3383a7afd9d787a382e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 21 Mar 2024 16:47:16 +0900 Subject: [PATCH 242/369] Revision 0.32.18 (#801) - Explicit Return on TypeSystem Type --- package-lock.json | 4 ++-- package.json | 2 +- src/system/system.ts | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5077496c8..a651c37c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.17", + "version": "0.32.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.17", + "version": "0.32.18", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 6ea8b8b95..a6fbb94b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.17", + "version": "0.32.18", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/system/system.ts b/src/system/system.ts index 2715ee4e6..98b3647c6 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -27,7 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeRegistry, FormatRegistry } from '../type/registry/index' -import { Unsafe } from '../type/unsafe/index' +import { Unsafe, type TUnsafe } from '../type/unsafe/index' import { Kind } from '../type/symbols/index' import { TypeBoxError } from '../type/error/index' @@ -47,10 +47,12 @@ export class TypeSystemDuplicateFormat extends TypeBoxError { // ------------------------------------------------------------------ // TypeSystem // ------------------------------------------------------------------ +export type TypeFactoryFunction> = (options?: Partial) => TUnsafe + /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { /** Creates a new type */ - export function Type>(kind: string, check: (options: Options, value: unknown) => boolean) { + export function Type>(kind: string, check: (options: Options, value: unknown) => boolean): TypeFactoryFunction { if (TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) TypeRegistry.Set(kind, check) return (options: Partial = {}) => Unsafe({ ...options, [Kind]: kind }) From 9e4c67dd857e106286454e55d9182342c3cfee99 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 23 Mar 2024 08:05:19 +0900 Subject: [PATCH 243/369] Revision 0.32.19 (#805) * Revert Union Conversion Logic * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/convert/convert.ts | 7 ++++++- test/runtime/value/convert/union.ts | 6 +++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index a651c37c6..e5543b702 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.18", + "version": "0.32.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.18", + "version": "0.32.19", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index a6fbb94b2..1ecddd42e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.18", + "version": "0.32.19", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index dd0e2d1d2..a95e8228a 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -240,7 +240,12 @@ function FromUndefined(schema: TUndefined, references: TSchema[], value: any): u return TryConvertUndefined(value) } function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { - return schema.anyOf.reduce((value, schema) => Visit(schema, references, value), value) + for (const subschema of schema.anyOf) { + const converted = Visit(subschema, references, value) + if (!Check(subschema, references, converted)) continue + return converted + } + return value } function Visit(schema: TSchema, references: TSchema[], value: any): unknown { const references_ = IsString(schema.$id) ? [...references, schema] : references diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index 227e76c95..ef020b69a 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -14,12 +14,12 @@ describe('value/convert/Union', () => { Assert.IsEqual(V2, { x: null }) Assert.IsEqual(V3, { x: 'hello' }) }) - it('Should convert last variant in ambiguous conversion', () => { + it('Should convert first variant in ambiguous conversion', () => { const T = Type.Object({ x: Type.Union([Type.Boolean(), Type.Number()]), }) const V1 = Value.Convert(T, { x: '1' }) - Assert.IsEqual(V1, { x: 1 }) + Assert.IsEqual(V1, { x: true }) }) // ---------------------------------------------------------------- // https://github.com/sinclairzx81/typebox/issues/787 @@ -38,6 +38,6 @@ describe('value/convert/Union', () => { const C = Convert(T, { a: '1', b: '2', c: '3' }) Assert.IsEqual(A, { a: 1, c: 2 }) Assert.IsEqual(B, { b: 1, c: 2 }) - Assert.IsEqual(C, { a: 1, b: 2, c: 3 }) + Assert.IsEqual(C, { a: 1, b: '2', c: 3 }) // note: matching on first }) }) From 3735b3425b697ffdf131f25ea2e5c57a05efbdb1 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 23 Mar 2024 17:48:11 +0900 Subject: [PATCH 244/369] Add FromSchema Prototype --- example/prototypes/from-schema.ts | 237 ++++++++++++++++++++++++++++++ example/prototypes/index.ts | 1 + 2 files changed, 238 insertions(+) create mode 100644 example/prototypes/from-schema.ts diff --git a/example/prototypes/from-schema.ts b/example/prototypes/from-schema.ts new file mode 100644 index 000000000..94a4716b2 --- /dev/null +++ b/example/prototypes/from-schema.ts @@ -0,0 +1,237 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Type from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Schematics +// ------------------------------------------------------------------ +const IsExact = (value: unknown, expect: unknown) => value === expect +const IsSValue = (value: unknown): value is SValue => Type.ValueGuard.IsString(value) || Type.ValueGuard.IsNumber(value) || Type.ValueGuard.IsBoolean(value) +const IsSEnum = (value: unknown): value is SEnum => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.enum) && value.enum.every((value) => IsSValue(value)) +const IsSAllOf = (value: unknown): value is SAllOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.allOf) +const IsSAnyOf = (value: unknown): value is SAnyOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.anyOf) +const IsSOneOf = (value: unknown): value is SOneOf => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsArray(value.oneOf) +const IsSTuple = (value: unknown): value is STuple => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'array') && Type.ValueGuard.IsArray(value.items) +const IsSArray = (value: unknown): value is SArray => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'array') && !Type.ValueGuard.IsArray(value.items) && Type.ValueGuard.IsObject(value.items) +const IsSConst = (value: unknown): value is SConst => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsObject(value['const']) +const IsSString = (value: unknown): value is SString => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'string') +const IsSNumber = (value: unknown): value is SNumber => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'number') +const IsSInteger = (value: unknown): value is SInteger => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'integer') +const IsSBoolean = (value: unknown): value is SBoolean => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'boolean') +const IsSNull = (value: unknown): value is SBoolean => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'null') +const IsSProperties = (value: unknown): value is SProperties => Type.ValueGuard.IsObject(value) +// prettier-ignore +const IsSObject = (value: unknown): value is SObject => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'object') && IsSProperties(value.properties) && (value.required === undefined || Type.ValueGuard.IsArray(value.required) && value.required.every((value: unknown) => Type.ValueGuard.IsString(value))) +type SValue = string | number | boolean +type SEnum = Readonly<{ enum: readonly SValue[] }> +type SAllOf = Readonly<{ allOf: readonly unknown[] }> +type SAnyOf = Readonly<{ anyOf: readonly unknown[] }> +type SOneOf = Readonly<{ oneOf: readonly unknown[] }> +type SProperties = Record +type SObject = Readonly<{ type: 'object'; properties: SProperties; required?: readonly string[] }> +type STuple = Readonly<{ type: 'array'; items: readonly unknown[] }> +type SArray = Readonly<{ type: 'array'; items: unknown }> +type SConst = Readonly<{ const: SValue }> +type SString = Readonly<{ type: 'string' }> +type SNumber = Readonly<{ type: 'number' }> +type SInteger = Readonly<{ type: 'integer' }> +type SBoolean = Readonly<{ type: 'boolean' }> +type SNull = Readonly<{ type: 'null' }> +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + T extends readonly [infer L extends unknown, ...infer R extends unknown[]] + ? TFromSchema extends infer S extends Type.TSchema + ? TFromRest + : TFromRest + : Acc +) +function FromRest(T: T): TFromRest { + return T.map((L) => FromSchema(L)) as never +} +// ------------------------------------------------------------------ +// FromEnumRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromEnumRest = ( + T extends readonly [infer L extends SValue, ...infer R extends SValue[]] + ? TFromEnumRest]> + : Acc +) +function FromEnumRest(T: T): TFromEnumRest { + return T.map((L) => Type.Literal(L)) as never +} +// ------------------------------------------------------------------ +// AllOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAllOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TIntersectEvaluated + : Type.TNever +) +function FromAllOf(T: T): TFromAllOf { + return Type.IntersectEvaluated(FromRest(T.allOf), T) +} +// ------------------------------------------------------------------ +// AnyOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAnyOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromAnyOf(T: T): TFromAnyOf { + return Type.UnionEvaluated(FromRest(T.anyOf), T) +} +// ------------------------------------------------------------------ +// OneOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromOneOf = ( + TFromRest extends infer Rest extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromOneOf(T: T): TFromOneOf { + return Type.UnionEvaluated(FromRest(T.oneOf), T) +} +// ------------------------------------------------------------------ +// Enum +// ------------------------------------------------------------------ +// prettier-ignore +type TFromEnum = ( + TFromEnumRest extends infer Elements extends Type.TSchema[] + ? Type.TUnionEvaluated + : Type.TNever +) +function FromEnum(T: T): TFromEnum { + return Type.UnionEvaluated(FromEnumRest(T.enum)) +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + TFromRest extends infer Elements extends Type.TSchema[] + ? Type.TTuple + : Type.TTuple<[]> +) +// prettier-ignore +function FromTuple(T: T): TFromTuple { + return Type.Tuple(FromRest(T.items), T) as never +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = ( + TFromSchema extends infer Items extends Type.TSchema + ? Type.TArray + : Type.TArray +) +// prettier-ignore +function FromArray(T: T): TFromArray { + return Type.Array(FromSchema(T.items), T) as never +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type TFromConst = ( + Type.Ensure> +) +function FromConst(T: T) { + return Type.Literal(T.const, T) +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +type TFromPropertiesIsOptional = unknown extends R ? true : K extends R ? false : true +// prettier-ignore +type TFromProperties = Type.Evaluate<{ + -readonly [K in keyof T]: TFromPropertiesIsOptional extends true + ? Type.TOptional> + : TFromSchema +}> +// prettier-ignore +type TFromObject = ( + TFromProperties[number]> extends infer Properties extends Type.TProperties + ? Type.TObject + : Type.TObject<{}> +) +function FromObject(T: T): TFromObject { + const properties = globalThis.Object.getOwnPropertyNames(T.properties).reduce((Acc, K) => { + return { ...Acc, [K]: T.required && T.required.includes(K) ? FromSchema(T.properties[K]) : Type.Optional(FromSchema(T.properties[K])) } + }, {} as Type.TProperties) + return Type.Object(properties, T) as never +} +// ------------------------------------------------------------------ +// FromSchema +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromSchema = ( + T extends SAllOf ? TFromAllOf : + T extends SAnyOf ? TFromAnyOf : + T extends SOneOf ? TFromOneOf : + T extends SEnum ? TFromEnum : + T extends SObject ? TFromObject : + T extends STuple ? TFromTuple : + T extends SArray ? TFromArray : + T extends SConst ? TFromConst : + T extends SString ? Type.TString : + T extends SNumber ? Type.TNumber : + T extends SInteger ? Type.TInteger : + T extends SBoolean ? Type.TBoolean : + T extends SNull ? Type.TNull : + Type.TUnknown +) +/** Parses a TypeBox type from raw JsonSchema */ +export function FromSchema(T: T): TFromSchema { + // prettier-ignore + return ( + IsSAllOf(T) ? FromAllOf(T) : + IsSAnyOf(T) ? FromAnyOf(T) : + IsSOneOf(T) ? FromOneOf(T) : + IsSEnum(T) ? FromEnum(T) : + IsSObject(T) ? FromObject(T) : + IsSTuple(T) ? FromTuple(T) : + IsSArray(T) ? FromArray(T) : + IsSConst(T) ? FromConst(T) : + IsSString(T) ? Type.String(T) : + IsSNumber(T) ? Type.Number(T) : + IsSInteger(T) ? Type.Integer(T) : + IsSBoolean(T) ? Type.Boolean(T) : + IsSNull(T) ? Type.Null(T) : + Type.Unknown(T || {}) + ) as never +} diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index c0011265b..c0039fc87 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './evaluate' +export * from './from-schema' export * from './partial-deep' export * from './union-enum' export * from './union-oneof' From 480ba12942cf908fab6e2ad16b3276b035ddcb54 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 25 Mar 2024 20:20:30 +0900 Subject: [PATCH 245/369] Revision 0.32.20 (#810) * Solve TypeScript Compiler Emit Regression * Version --- package-lock.json | 18 ++++++------- package.json | 4 +-- src/value/delta/delta.ts | 55 +++++++++++++++++++++++++++------------- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5543b702..12bd65858 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.19", + "version": "0.32.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.19", + "version": "0.32.20", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.4.2" + "typescript": "^5.4.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1644,9 +1644,9 @@ } }, "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2874,9 +2874,9 @@ "dev": true }, "typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 1ecddd42e..469dd81f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.19", + "version": "0.32.20", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.4.2" + "typescript": "^5.4.3" } } diff --git a/src/value/delta/delta.ts b/src/value/delta/delta.ts index daf47fa1e..bd437acc1 100644 --- a/src/value/delta/delta.ts +++ b/src/value/delta/delta.ts @@ -33,34 +33,55 @@ import { ValuePointer } from '../pointer/index' import { Clone } from '../clone/index' import { TypeBoxError } from '../../type/error/index' -import { Literal as CreateLiteral } from '../../type/literal/index' -import { Object as CreateObject } from '../../type/object/index' -import { String as CreateString } from '../../type/string/index' -import { Unknown as CreateUnknown } from '../../type/unknown/index' -import { Union as CreateUnion } from '../../type/union/index' +import { Literal, type TLiteral } from '../../type/literal/index' +import { Object, type TObject } from '../../type/object/index' +import { String, type TString } from '../../type/string/index' +import { Unknown, type TUnknown } from '../../type/unknown/index' +import { Union, type TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // Commands // ------------------------------------------------------------------ + +// Note: A TypeScript 5.4.2 compiler regression resulted in the type +// import paths being generated incorrectly. We can resolve this by +// explicitly importing the correct TSchema types above. Note also +// that the left-side annotations are optional, but since the types +// are imported we might as well use them. We should check this +// regression in future. The regression occured between TypeScript +// versions 5.3.3 -> 5.4.2. + export type Insert = Static -export const Insert = CreateObject({ - type: CreateLiteral('insert'), - path: CreateString(), - value: CreateUnknown(), +export const Insert: TObject<{ + type: TLiteral<'insert'> + path: TString + value: TUnknown +}> = Object({ + type: Literal('insert'), + path: String(), + value: Unknown(), }) export type Update = Static -export const Update = CreateObject({ - type: CreateLiteral('update'), - path: CreateString(), - value: CreateUnknown(), +export const Update: TObject<{ + type: TLiteral<'update'> + path: TString + value: TUnknown +}> = Object({ + type: Literal('update'), + path: String(), + value: Unknown(), }) export type Delete = Static -export const Delete = CreateObject({ - type: CreateLiteral('delete'), - path: CreateString(), +export const Delete: TObject<{ + type: TLiteral<'delete'> + path: TString +}> = Object({ + type: Literal('delete'), + path: String(), }) export type Edit = Static -export const Edit = CreateUnion([Insert, Update, Delete]) +export const Edit: TUnion<[typeof Insert, typeof Update, typeof Delete]> = Union([Insert, Update, Delete]) + // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ From b91c1c1a85af980a76c5c8a3fb599f0066db86f9 Mon Sep 17 00:00:00 2001 From: m1212e <14091540+m1212e@users.noreply.github.com> Date: Tue, 16 Apr 2024 07:03:54 +0200 Subject: [PATCH 246/369] Ecosystem (#827) --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 0a246c134..2d72ff4a9 100644 --- a/readme.md +++ b/readme.md @@ -1698,6 +1698,7 @@ The following is a list of community packages that offer general tooling, extend | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | +| [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schemes matching the database models | From a169893ff7a1d6c42b2b4d3cbee0b3fde346ea68 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 16 Apr 2024 14:06:04 +0900 Subject: [PATCH 247/369] Ecosystem --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 2d72ff4a9..b6fad4d6a 100644 --- a/readme.md +++ b/readme.md @@ -1693,12 +1693,12 @@ The following is a list of community packages that offer general tooling, extend | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | | [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | +| [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schema matching the database models | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | -| [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schemes matching the database models | From 9265f4426c6cce349c4e150782cc03fed81b187f Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 17 Apr 2024 18:44:34 +0900 Subject: [PATCH 248/369] TypeScript 5.4.5 --- hammer.mjs | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 1326d2195..065e0326b 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 12bd65858..9fb943232 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.4.3" + "typescript": "^5.4.5" } }, "node_modules/@andrewbranch/untar.js": { @@ -1644,9 +1644,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2874,9 +2874,9 @@ "dev": true }, "typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 469dd81f1..cf71e410e 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^9.2.2", "prettier": "^2.7.1", - "typescript": "^5.4.3" + "typescript": "^5.4.5" } } From bcee7b5a0f9b833567f64bc6469e56d378354216 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 19 Apr 2024 17:56:06 +0900 Subject: [PATCH 249/369] Revision 0.32.21 (#836) * Composite Never Filter Fix | Support Array Conversion * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/composite/composite.ts | 22 +++++++++++----------- src/value/convert/convert.ts | 9 ++------- test/runtime/value/convert/array.ts | 24 ++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9fb943232..2dde718ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.20", + "version": "0.32.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.20", + "version": "0.32.21", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index cf71e410e..ad32abec6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.20", + "version": "0.32.21", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index b37ccb613..8254036ee 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -45,14 +45,14 @@ import { IsNever } from '../guard/type' // prettier-ignore type TCompositeKeys = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TCompositeKeys]>> - : Acc + ? TCompositeKeys]> + : TSetDistinct ) // prettier-ignore function CompositeKeys(T: [...T]): TCompositeKeys { - return T.reduce((Acc, L) => { - return SetDistinct([...Acc, ...KeyOfPropertyKeys(L)]) as never - }, []) as never + return SetDistinct(T.reduce((Acc, L) => { + return ([...Acc, ...KeyOfPropertyKeys(L)]) as never + }, [])) as never } // ------------------------------------------------------------------ // FilterNever @@ -61,7 +61,7 @@ function CompositeKeys(T: [...T]): TCompositeKeys { type TFilterNever = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? L extends TNever - ? Acc + ? TFilterNever : TFilterNever : Acc ) @@ -75,14 +75,14 @@ function FilterNever(T: [...T]): TFilterNever { // prettier-ignore type TCompositeProperty = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TCompositeProperty]>> - : Acc + ? TCompositeProperty]> + : TFilterNever ) // prettier-ignore function CompositeProperty(T: [...T], K: K): TCompositeProperty { - return T.reduce((Acc, L) => { - return FilterNever([...Acc, ...IndexFromPropertyKeys(L, [K])]) - }, []) as never + return FilterNever(T.reduce((Acc, L) => { + return [...Acc, ...IndexFromPropertyKeys(L, [K])] as never + }, [])) as never } // ------------------------------------------------------------------ // CompositeProperties diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index a95e8228a..b17256384 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -29,10 +29,7 @@ THE SOFTWARE. import { Clone } from '../clone/index' import { Check } from '../check/index' import { Deref } from '../deref/index' - -import { IsObject as IsObjectType } from '../../type/guard/type' import { Kind } from '../../type/symbols/index' -import { Composite } from '../../type/composite/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' @@ -166,10 +163,8 @@ function Default(value: any) { // Convert // ------------------------------------------------------------------ function FromArray(schema: TArray, references: TSchema[], value: any): any { - if (IsArray(value)) { - return value.map((value) => Visit(schema.items, references, value)) - } - return value + const elements = IsArray(value) ? value : [value] + return elements.map((element) => Visit(schema.items, references, element)) } function FromBigInt(schema: TBigInt, references: TSchema[], value: any): unknown { return TryConvertBigInt(value) diff --git a/test/runtime/value/convert/array.ts b/test/runtime/value/convert/array.ts index c8a040f3d..345a5564a 100644 --- a/test/runtime/value/convert/array.ts +++ b/test/runtime/value/convert/array.ts @@ -29,4 +29,28 @@ describe('value/convert/Array', () => { Assert.IsEqual(R[5].getTime(), 0) Assert.IsEqual(R[6], 'hello') }) + // ---------------------------------------------------------------- + // Array Coercion + // ---------------------------------------------------------------- + it('Should convert into array (convert interior)', () => { + const T = Type.Array(Type.Number()) + const R = Value.Convert(T, '1') + Assert.IsEqual(R, [1]) + }) + it('Should convert into array (retain interior)', () => { + const T = Type.Array(Type.Number()) + const R = Value.Convert(T, 'A') + Assert.IsEqual(R, ['A']) + }) + it('Should convert into array (object)', () => { + const T = Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + ) + const R = Value.Convert(T, { x: '1', y: true, z: null }) + Assert.IsEqual(R, [{ x: 1, y: 1, z: null }]) + }) }) From 3719b3eaff59fafe077bc47cf8ada4940f9499f2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 21 Apr 2024 01:28:28 +0900 Subject: [PATCH 250/369] Revision 0.32.22 (#840) * Support Optional and Readonly Function Arguments * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/constructor/constructor.ts | 25 +++++++++++++++++-------- src/type/function/function.ts | 26 ++++++++++++++++++-------- test/static/constructor.ts | 20 ++++++++++++++++++++ test/static/function.ts | 22 ++++++++++++++++++++++ 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2dde718ee..5249c5e1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.21", + "version": "0.32.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.21", + "version": "0.32.22", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index ad32abec6..0340aa476 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.21", + "version": "0.32.22", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index 948d0aab6..9983862ee 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -29,28 +29,37 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import type { Ensure } from '../helpers/index' +import type { TReadonlyOptional } from '../readonly-optional/index' +import type { TReadonly } from '../readonly/index' +import type { TOptional } from '../optional/index' import { CloneType, CloneRest } from '../clone/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ -// TConstructorStatic +// StaticConstructor // ------------------------------------------------------------------ -type ConstructorStaticReturnType = Static +type StaticReturnType = Static // prettier-ignore -type ConstructorStaticParameters = +type StaticParameter = + T extends TReadonlyOptional ? [Readonly>?] : + T extends TReadonly ? [Readonly>] : + T extends TOptional ? [Static?] : + [Static] +// prettier-ignore +type StaticParameters = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? ConstructorStaticParameters]> + ? StaticParameters]> : Acc -// prettier-ignore -type ConstructorStatic = ( - Ensure) => ConstructorStaticReturnType> ) +// prettier-ignore +type StaticConstructor = + Ensure) => StaticReturnType> // ------------------------------------------------------------------ // TConstructor // ------------------------------------------------------------------ export interface TConstructor extends TSchema { [Kind]: 'Constructor' - static: ConstructorStatic + static: StaticConstructor type: 'Constructor' parameters: T returns: U diff --git a/src/type/function/function.ts b/src/type/function/function.ts index 6f594dfb3..340e5ba49 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -29,28 +29,38 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import type { Ensure } from '../helpers/index' +import type { TReadonlyOptional } from '../readonly-optional/index' +import type { TReadonly } from '../readonly/index' +import type { TOptional } from '../optional/index' import { CloneType, CloneRest } from '../clone/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ -// FunctionStatic +// StaticFunction // ------------------------------------------------------------------ -type FunctionStaticReturnType = Static +type StaticReturnType = Static // prettier-ignore -type FunctionStaticParameters = +type StaticParameter = + T extends TReadonlyOptional ? [Readonly>?] : + T extends TReadonly ? [Readonly>] : + T extends TOptional ? [Static?] : + [Static] +// prettier-ignore +type StaticParameters = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FunctionStaticParameters]> + ? StaticParameters]> : Acc -// prettier-ignore -type FunctionStatic = ( - Ensure<(...param: FunctionStaticParameters) => FunctionStaticReturnType> ) +// prettier-ignore +type StaticFunction = + Ensure<(...params: StaticParameters) => StaticReturnType> + // ------------------------------------------------------------------ // TFunction // ------------------------------------------------------------------ export interface TFunction extends TSchema { [Kind]: 'Function' - static: FunctionStatic + static: StaticFunction type: 'Function' parameters: T returns: U diff --git a/test/static/constructor.ts b/test/static/constructor.ts index c91d398e7..59e435c62 100644 --- a/test/static/constructor.ts +++ b/test/static/constructor.ts @@ -13,6 +13,26 @@ import { Type } from '@sinclair/typebox' })) Expect(T).ToStatic { method: new (param_0: number, param_1: string) => boolean }>() } +{ + // readonly-optional + const T = Type.Constructor([Type.ReadonlyOptional(Type.Array(Type.Number()))], Type.Number()) + Expect(T).ToStaticDecode number>() +} +{ + // readonly + const T = Type.Constructor([Type.Readonly(Type.Array(Type.Number()))], Type.Number()) + Expect(T).ToStaticDecode number>() +} +{ + // optional 1 + const T = Type.Constructor([Type.Optional(Type.Number())], Type.Number()) + Expect(T).ToStaticDecode number>() +} +{ + // optional 2 + const T = Type.Constructor([Type.Number(), Type.Optional(Type.Number())], Type.Number()) + Expect(T).ToStaticDecode number>() +} { // decode 2 const S = Type.Transform(Type.Integer()) diff --git a/test/static/function.ts b/test/static/function.ts index a204187a6..1ae3404aa 100644 --- a/test/static/function.ts +++ b/test/static/function.ts @@ -14,6 +14,28 @@ import { Type } from '@sinclair/typebox' })) Expect(T).ToStatic<(param_0: number, param_1: string) => { method: (param_0: number, param_1: string) => boolean }>() } +{ + // readonly-optional + const T = Type.Function([Type.ReadonlyOptional(Type.Array(Type.Number()))], Type.Number()) + Expect(T).ToStaticDecode<(param_0?: readonly number[]) => number>() +} +{ + // readonly + const T = Type.Function([Type.Readonly(Type.Array(Type.Number()))], Type.Number()) + Expect(T).ToStaticDecode<(param_0: readonly number[]) => number>() +} +{ + // optional 1 + const T = Type.Function([Type.Optional(Type.Number())], Type.Number()) + Expect(T).ToStaticDecode<(param_0?: number) => number>() +} +{ + // optional 2 + const T = Type.Function([Type.Number(), Type.Optional(Type.Number())], Type.Number()) + Expect(T).ToStaticDecode<(param_0: number, param_1?: number) => number>() +} +const F = Type.Constructor([Type.Readonly(Type.Array(Type.String()))], Type.Number()) + { // decode 2 const S = Type.Transform(Type.Integer()) From 2a2b5d9dd0a86be555c164f80013635f2e2419d6 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 24 Apr 2024 16:40:39 +0900 Subject: [PATCH 251/369] Revision 0.32.23 (#847) * Clean: Ensure References Passed for Union Check * Rename Inferred Function and Constructor Parameter * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/constructor/constructor.ts | 2 +- src/type/function/function.ts | 2 +- src/value/clean/clean.ts | 2 +- test/runtime/value/clean/union.ts | 23 +++++++++++++++++++++++ 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5249c5e1c..b822b7549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.22", + "version": "0.32.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.22", + "version": "0.32.23", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 0340aa476..b2c6b0327 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.22", + "version": "0.32.23", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index 9983862ee..c68ffe992 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -53,7 +53,7 @@ type StaticParameters = - Ensure) => StaticReturnType> + Ensure) => StaticReturnType> // ------------------------------------------------------------------ // TConstructor // ------------------------------------------------------------------ diff --git a/src/type/function/function.ts b/src/type/function/function.ts index 340e5ba49..d86297985 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -53,7 +53,7 @@ type StaticParameters = - Ensure<(...params: StaticParameters) => StaticReturnType> + Ensure<(...param: StaticParameters) => StaticReturnType> // ------------------------------------------------------------------ // TFunction diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 7f48119b7..e913b4a67 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -141,7 +141,7 @@ function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any { } function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { for (const inner of schema.anyOf) { - if (IsCheckable(inner) && Check(inner, value)) { + if (IsCheckable(inner) && Check(inner, references, value)) { return Visit(inner, references, value) } } diff --git a/test/runtime/value/clean/union.ts b/test/runtime/value/clean/union.ts index 443e3d651..010bd0c72 100644 --- a/test/runtime/value/clean/union.ts +++ b/test/runtime/value/clean/union.ts @@ -131,4 +131,27 @@ describe('value/clean/Union', () => { const R = Value.Clean(T, { u: null, y: 1 }) Assert.IsEqual(R, { u: null, y: 1 }) }) + // ---------------------------------------------------------------- + // Union Recursive + // + // https://github.com/sinclairzx81/typebox/issues/845 + // ---------------------------------------------------------------- + it('Should clean recursive with union', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.Number(), + parent: Type.Union([This, Type.Null()]), + }), + ) + const R = Value.Clean(T, { + id: 1, + unknown: 1, + parent: { + id: 2, + unknown: 1, + parent: null, + }, + }) + Assert.IsEqual(R, { id: 1, parent: { id: 2, parent: null } }) + }) }) From cfc152b720faa863ae5d198d0a5fc8ea314f3864 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 25 Apr 2024 09:46:53 +0900 Subject: [PATCH 252/369] Revision 0.32.24 (#848) --- package-lock.json | 4 ++-- package.json | 2 +- src/value/convert/convert.ts | 14 ++++++++------ src/value/value/value.ts | 6 +++--- test/runtime/value/convert/object.ts | 10 +++++++++- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index b822b7549..c98535528 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.23", + "version": "0.32.24", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.23", + "version": "0.32.24", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index b2c6b0327..5015e3835 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.23", + "version": "0.32.24", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index b17256384..9d8191342 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -54,7 +54,7 @@ import type { TUndefined } from '../../type/undefined/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from '../guard/index' +import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol, HasPropertyKey } from '../guard/index' // ------------------------------------------------------------------ // Conversions // ------------------------------------------------------------------ @@ -194,11 +194,13 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown function FromObject(schema: TObject, references: TSchema[], value: any): unknown { const isConvertable = IsObject(value) if(!isConvertable) return value - return Object.getOwnPropertyNames(schema.properties).reduce((value, key) => { - return !IsUndefined(value[key]) - ? ({ ...value, [key]: Visit(schema.properties[key], references, value[key]) }) - : ({ ...value }) - }, value) + const result: Record = {} + for(const key of Object.keys(value)) { + result[key] = HasPropertyKey(schema.properties, key) + ? Visit(schema.properties[key], references, value[key]) + : value[key] + } + return result } function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown { const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] diff --git a/src/value/value/value.ts b/src/value/value/value.ts index f64dc18e5..1228fa827 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -75,11 +75,11 @@ export function Clean(schema: TSchema, value: unknown): unknown export function Clean(...args: any[]) { return CleanValue.apply(CleanValue, args as any) } -/** Converts any type mismatched values to their target type if a reasonable conversion is possible */ +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possible */ +/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(...args: any[]) { return ConvertValue.apply(ConvertValue, args as any) } diff --git a/test/runtime/value/convert/object.ts b/test/runtime/value/convert/object.ts index fa5e4ed70..042c05647 100644 --- a/test/runtime/value/convert/object.ts +++ b/test/runtime/value/convert/object.ts @@ -2,9 +2,9 @@ import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' +// prettier-ignore describe('value/convert/Object', () => { it('Should convert properties', () => { - // prettier-ignore const T = Type.Object({ x: Type.Number(), y: Type.Boolean(), @@ -13,4 +13,12 @@ describe('value/convert/Object', () => { const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) }) + it('Should convert known properties', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Boolean() + }) + const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) + Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) + }) }) From 130c52083a9ee8d181c8f609ed6898d8ae6d9518 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 25 Apr 2024 17:12:40 +0900 Subject: [PATCH 253/369] Partial Deep Prototype --- example/prototypes/partial-deep.ts | 50 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/example/prototypes/partial-deep.ts b/example/prototypes/partial-deep.ts index 7ee36f445..5fc707f80 100644 --- a/example/prototypes/partial-deep.ts +++ b/example/prototypes/partial-deep.ts @@ -29,38 +29,40 @@ THE SOFTWARE. import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TProperties, Evaluate } from '@sinclair/typebox' // ------------------------------------------------------------------------------------- -// TDeepPartial +// TPartialDeepProperties // ------------------------------------------------------------------------------------- export type TPartialDeepProperties = { - [K in keyof T]: TPartial + [K in keyof T]: TPartialDeep } -export type TPartialDeepRest = +function PartialDeepProperties(properties: T): TPartialDeepProperties { + return Object.getOwnPropertyNames(properties).reduce((acc, key) => { + return {...acc, [key]: PartialDeep(properties[key])} + }, {}) as never +} +// ------------------------------------------------------------------------------------- +// TPartialDeepRest +// ------------------------------------------------------------------------------------- +export type TPartialDeepRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [TPartial, ...TPartialDeepRest] - : [] + ? TPartialDeepRest]> + : Acc +) +function PartialDeepRest(rest: [...T]): TPartialDeepRest { + return rest.map(schema => PartialDeep(schema)) as never +} +// ------------------------------------------------------------------------------------- +// TPartialDeep +// ------------------------------------------------------------------------------------- export type TPartialDeep = T extends TIntersect ? TIntersect> : T extends TUnion ? TUnion> : T extends TObject ? TPartial>>> : T -// ------------------------------------------------------------------------------------- -// DeepPartial -// ------------------------------------------------------------------------------------- -function PartialDeepProperties(properties: T) { - return Object.getOwnPropertyNames(properties).reduce((acc, key) => { - return {...acc, [key]: Type.Partial(properties[key])} - }, {} as TProperties) -} -function PartialDeepRest(rest: [...T]): TPartialDeepRest { - const [L, ...R] = rest - return (R.length > 0) ? [Type.Partial(L), ...PartialDeepRest(R)] : [] as any -} -/** Maps the given schema as deep partial, making all properties and sub properties optional */ -export function PartialDeep(type: T): TPartialDeep { +export function PartialDeep(schema: T): TPartialDeep { return ( - TypeGuard.IsIntersect(type) ? Type.Intersect(PartialDeepRest(type.allOf)) : - TypeGuard.IsUnion(type) ? Type.Union(PartialDeepRest(type.anyOf)) : - TypeGuard.IsObject(type) ? Type.Partial(Type.Object(PartialDeepProperties(type.properties))) : - type - ) as any + TypeGuard.IsIntersect(schema) ? Type.Intersect(PartialDeepRest(schema.allOf)) : + TypeGuard.IsUnion(schema) ? Type.Union(PartialDeepRest(schema.anyOf)) : + TypeGuard.IsObject(schema) ? Type.Partial(Type.Object(PartialDeepProperties(schema.properties))) : + schema + ) as never } \ No newline at end of file From 1ba927ca2265be3385adc8585fd46c8730caecca Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 26 Apr 2024 09:46:04 +0900 Subject: [PATCH 254/369] Revision 0.32.25 (#849) * Type and Value Optimizations * Version --- example/prototypes/mutual.ts | 48 ++ package-lock.json | 4 +- package.json | 2 +- readme.md | 8 +- src/type/any/any.ts | 2 +- src/type/array/array.ts | 2 +- src/type/async-iterator/async-iterator.ts | 2 +- src/type/awaited/awaited.ts | 12 +- src/type/bigint/bigint.ts | 2 +- src/type/boolean/boolean.ts | 2 +- src/type/clone/type.ts | 2 +- src/type/clone/value.ts | 11 +- src/type/composite/composite.ts | 25 +- src/type/const/const.ts | 14 +- src/type/constructor/constructor.ts | 2 +- src/type/date/date.ts | 2 +- src/type/deref/deref.ts | 16 +- src/type/enum/enum.ts | 2 +- .../exclude/exclude-from-mapped-result.ts | 12 +- src/type/exclude/exclude.ts | 2 +- src/type/extends/extends-from-mapped-key.ts | 10 +- .../extends/extends-from-mapped-result.ts | 12 +- src/type/extends/extends.ts | 6 +- .../extract/extract-from-mapped-result.ts | 12 +- src/type/extract/extract.ts | 2 +- src/type/function/function.ts | 2 +- src/type/guard/index.ts | 1 + src/type/guard/kind.ts | 285 ++++++++ src/type/guard/type.ts | 2 +- src/type/helpers/helpers.ts | 2 +- src/type/indexed/indexed-from-mapped-key.ts | 10 +- .../indexed/indexed-from-mapped-result.ts | 14 +- src/type/indexed/indexed-property-keys.ts | 14 +- src/type/indexed/indexed.ts | 20 +- src/type/integer/integer.ts | 2 +- src/type/intersect/intersect-create.ts | 4 +- src/type/intersect/intersect-evaluated.ts | 14 +- src/type/intersect/intersect.ts | 2 +- .../intrinsic/intrinsic-from-mapped-key.ts | 10 +- src/type/intrinsic/intrinsic.ts | 6 +- src/type/iterator/iterator.ts | 2 +- src/type/keyof/keyof-from-mapped-result.ts | 12 +- src/type/keyof/keyof-property-keys.ts | 18 +- src/type/keyof/keyof.ts | 4 +- src/type/literal/literal.ts | 2 +- src/type/mapped/mapped-key.ts | 2 +- src/type/mapped/mapped.ts | 53 +- src/type/never/never.ts | 2 +- src/type/not/not.ts | 2 +- src/type/null/null.ts | 2 +- src/type/number/number.ts | 2 +- src/type/object/object.ts | 13 +- src/type/omit/omit-from-mapped-key.ts | 10 +- src/type/omit/omit-from-mapped-result.ts | 12 +- src/type/omit/omit.ts | 16 +- .../optional/optional-from-mapped-result.ts | 8 +- src/type/optional/optional.ts | 2 +- .../partial/partial-from-mapped-result.ts | 12 +- src/type/partial/partial.ts | 14 +- src/type/pick/pick-from-mapped-key.ts | 10 +- src/type/pick/pick-from-mapped-result.ts | 12 +- src/type/pick/pick.ts | 12 +- src/type/promise/promise.ts | 2 +- .../readonly-optional/readonly-optional.ts | 2 +- .../readonly/readonly-from-mapped-result.ts | 12 +- src/type/readonly/readonly.ts | 2 +- src/type/record/record.ts | 25 +- src/type/recursive/recursive.ts | 2 +- .../required/required-from-mapped-result.ts | 12 +- src/type/required/required.ts | 12 +- src/type/rest/rest.ts | 10 +- src/type/sets/set.ts | 16 +- src/type/string/string.ts | 2 +- src/type/symbol/symbol.ts | 2 +- src/type/template-literal/generate.ts | 2 +- src/type/template-literal/pattern.ts | 12 +- src/type/template-literal/template-literal.ts | 2 +- src/type/transform/transform.ts | 6 +- src/type/tuple/tuple.ts | 2 +- src/type/uint8array/uint8array.ts | 2 +- src/type/undefined/undefined.ts | 2 +- src/type/union/union-create.ts | 2 +- src/type/union/union-evaluated.ts | 12 +- src/type/unknown/unknown.ts | 2 +- src/type/unsafe/unsafe.ts | 2 +- src/type/void/void.ts | 2 +- src/value/clean/clean.ts | 2 +- src/value/clone/clone.ts | 10 +- src/value/create/create.ts | 18 +- src/value/hash/hash.ts | 2 +- src/value/mutate/mutate.ts | 4 +- src/value/transform/decode.ts | 54 +- src/value/transform/encode.ts | 57 +- test/runtime/type/guard/index.ts | 55 +- test/runtime/type/guard/kind/any.ts | 14 + test/runtime/type/guard/kind/array.ts | 14 + .../runtime/type/guard/kind/async-iterator.ts | 16 + test/runtime/type/guard/kind/awaited.ts | 41 ++ test/runtime/type/guard/kind/bigint.ts | 14 + test/runtime/type/guard/kind/boolean.ts | 14 + test/runtime/type/guard/kind/capitalize.ts | 33 + test/runtime/type/guard/kind/composite.ts | 165 +++++ test/runtime/type/guard/kind/const.ts | 124 ++++ test/runtime/type/guard/kind/constructor.ts | 14 + test/runtime/type/guard/kind/date.ts | 14 + test/runtime/type/guard/kind/deref.ts | 110 ++++ test/runtime/type/guard/kind/enum.ts | 143 ++++ test/runtime/type/guard/kind/exclude.ts | 96 +++ test/runtime/type/guard/kind/extract.ts | 102 +++ test/runtime/type/guard/kind/function.ts | 14 + test/runtime/type/guard/kind/index.ts | 52 ++ test/runtime/type/guard/kind/indexed.ts | 327 ++++++++++ test/runtime/type/guard/kind/integer.ts | 14 + test/runtime/type/guard/kind/intersect.ts | 39 ++ test/runtime/type/guard/kind/iterator.ts | 16 + test/runtime/type/guard/kind/keyof.ts | 76 +++ test/runtime/type/guard/kind/kind.ts | 13 + test/runtime/type/guard/kind/literal.ts | 18 + test/runtime/type/guard/kind/lowercase.ts | 33 + test/runtime/type/guard/{ => kind}/mapped.ts | 4 +- test/runtime/type/guard/kind/not.ts | 10 + test/runtime/type/guard/kind/null.ts | 14 + test/runtime/type/guard/kind/number.ts | 14 + test/runtime/type/guard/kind/object.ts | 19 + test/runtime/type/guard/kind/omit.ts | 129 ++++ test/runtime/type/guard/kind/partial.ts | 67 ++ test/runtime/type/guard/kind/pick.ts | 131 ++++ test/runtime/type/guard/kind/promise.ts | 14 + test/runtime/type/guard/{ => kind}/record.ts | 4 +- test/runtime/type/guard/kind/recursive.ts | 16 + test/runtime/type/guard/kind/ref.ts | 15 + test/runtime/type/guard/kind/regexp.ts | 18 + test/runtime/type/guard/kind/required.ts | 64 ++ test/runtime/type/guard/kind/rest.ts | 59 ++ test/runtime/type/guard/kind/string.ts | 14 + test/runtime/type/guard/kind/symbol.ts | 14 + .../type/guard/kind/template-literal.ts | 35 + test/runtime/type/guard/kind/this.ts | 13 + test/runtime/type/guard/kind/tuple.ts | 14 + test/runtime/type/guard/kind/uint8array.ts | 14 + test/runtime/type/guard/kind/uncapitalize.ts | 33 + test/runtime/type/guard/kind/undefined.ts | 14 + test/runtime/type/guard/kind/union.ts | 42 ++ test/runtime/type/guard/kind/unknown.ts | 14 + test/runtime/type/guard/kind/unsafe.ts | 33 + test/runtime/type/guard/kind/uppercase.ts | 33 + test/runtime/type/guard/kind/void.ts | 14 + test/runtime/type/guard/{ => type}/any.ts | 4 +- test/runtime/type/guard/{ => type}/array.ts | 4 +- .../type/guard/{ => type}/async-iterator.ts | 4 +- test/runtime/type/guard/{ => type}/awaited.ts | 4 +- test/runtime/type/guard/{ => type}/bigint.ts | 4 +- test/runtime/type/guard/{ => type}/boolean.ts | 4 +- .../type/guard/{ => type}/capitalize.ts | 4 +- .../type/guard/{ => type}/composite.ts | 4 +- test/runtime/type/guard/{ => type}/const.ts | 4 +- .../type/guard/{ => type}/constructor.ts | 4 +- test/runtime/type/guard/{ => type}/date.ts | 4 +- test/runtime/type/guard/{ => type}/deref.ts | 4 +- test/runtime/type/guard/{ => type}/enum.ts | 4 +- test/runtime/type/guard/{ => type}/exclude.ts | 4 +- test/runtime/type/guard/{ => type}/extract.ts | 4 +- .../runtime/type/guard/{ => type}/function.ts | 4 +- test/runtime/type/guard/type/index.ts | 52 ++ test/runtime/type/guard/{ => type}/indexed.ts | 4 +- test/runtime/type/guard/{ => type}/integer.ts | 4 +- .../type/guard/{ => type}/intersect.ts | 4 +- .../runtime/type/guard/{ => type}/iterator.ts | 4 +- test/runtime/type/guard/{ => type}/keyof.ts | 4 +- test/runtime/type/guard/{ => type}/kind.ts | 4 +- test/runtime/type/guard/{ => type}/literal.ts | 4 +- .../type/guard/{ => type}/lowercase.ts | 4 +- test/runtime/type/guard/type/mapped.ts | 609 ++++++++++++++++++ test/runtime/type/guard/{ => type}/not.ts | 4 +- test/runtime/type/guard/{ => type}/null.ts | 4 +- test/runtime/type/guard/{ => type}/number.ts | 4 +- test/runtime/type/guard/{ => type}/object.ts | 4 +- test/runtime/type/guard/{ => type}/omit.ts | 4 +- test/runtime/type/guard/{ => type}/partial.ts | 4 +- test/runtime/type/guard/{ => type}/pick.ts | 4 +- test/runtime/type/guard/{ => type}/promise.ts | 4 +- test/runtime/type/guard/type/record.ts | 156 +++++ .../type/guard/{ => type}/recursive.ts | 4 +- test/runtime/type/guard/{ => type}/ref.ts | 4 +- test/runtime/type/guard/{ => type}/regexp.ts | 4 +- .../runtime/type/guard/{ => type}/required.ts | 4 +- test/runtime/type/guard/{ => type}/rest.ts | 4 +- test/runtime/type/guard/{ => type}/string.ts | 4 +- test/runtime/type/guard/{ => type}/symbol.ts | 4 +- .../type/guard/{ => type}/template-literal.ts | 4 +- test/runtime/type/guard/{ => type}/this.ts | 4 +- test/runtime/type/guard/{ => type}/tuple.ts | 4 +- .../type/guard/{ => type}/uint8array.ts | 4 +- .../type/guard/{ => type}/uncapitalize.ts | 4 +- .../type/guard/{ => type}/undefined.ts | 4 +- test/runtime/type/guard/{ => type}/union.ts | 4 +- test/runtime/type/guard/{ => type}/unknown.ts | 4 +- test/runtime/type/guard/{ => type}/unsafe.ts | 4 +- .../type/guard/{ => type}/uppercase.ts | 4 +- test/runtime/type/guard/{ => type}/void.ts | 4 +- test/runtime/type/{ => guard}/value/guard.ts | 2 +- test/runtime/type/{ => guard}/value/index.ts | 0 test/runtime/type/index.ts | 1 - 203 files changed, 4014 insertions(+), 560 deletions(-) create mode 100644 example/prototypes/mutual.ts create mode 100644 src/type/guard/kind.ts create mode 100644 test/runtime/type/guard/kind/any.ts create mode 100644 test/runtime/type/guard/kind/array.ts create mode 100644 test/runtime/type/guard/kind/async-iterator.ts create mode 100644 test/runtime/type/guard/kind/awaited.ts create mode 100644 test/runtime/type/guard/kind/bigint.ts create mode 100644 test/runtime/type/guard/kind/boolean.ts create mode 100644 test/runtime/type/guard/kind/capitalize.ts create mode 100644 test/runtime/type/guard/kind/composite.ts create mode 100644 test/runtime/type/guard/kind/const.ts create mode 100644 test/runtime/type/guard/kind/constructor.ts create mode 100644 test/runtime/type/guard/kind/date.ts create mode 100644 test/runtime/type/guard/kind/deref.ts create mode 100644 test/runtime/type/guard/kind/enum.ts create mode 100644 test/runtime/type/guard/kind/exclude.ts create mode 100644 test/runtime/type/guard/kind/extract.ts create mode 100644 test/runtime/type/guard/kind/function.ts create mode 100644 test/runtime/type/guard/kind/index.ts create mode 100644 test/runtime/type/guard/kind/indexed.ts create mode 100644 test/runtime/type/guard/kind/integer.ts create mode 100644 test/runtime/type/guard/kind/intersect.ts create mode 100644 test/runtime/type/guard/kind/iterator.ts create mode 100644 test/runtime/type/guard/kind/keyof.ts create mode 100644 test/runtime/type/guard/kind/kind.ts create mode 100644 test/runtime/type/guard/kind/literal.ts create mode 100644 test/runtime/type/guard/kind/lowercase.ts rename test/runtime/type/guard/{ => kind}/mapped.ts (99%) create mode 100644 test/runtime/type/guard/kind/not.ts create mode 100644 test/runtime/type/guard/kind/null.ts create mode 100644 test/runtime/type/guard/kind/number.ts create mode 100644 test/runtime/type/guard/kind/object.ts create mode 100644 test/runtime/type/guard/kind/omit.ts create mode 100644 test/runtime/type/guard/kind/partial.ts create mode 100644 test/runtime/type/guard/kind/pick.ts create mode 100644 test/runtime/type/guard/kind/promise.ts rename test/runtime/type/guard/{ => kind}/record.ts (98%) create mode 100644 test/runtime/type/guard/kind/recursive.ts create mode 100644 test/runtime/type/guard/kind/ref.ts create mode 100644 test/runtime/type/guard/kind/regexp.ts create mode 100644 test/runtime/type/guard/kind/required.ts create mode 100644 test/runtime/type/guard/kind/rest.ts create mode 100644 test/runtime/type/guard/kind/string.ts create mode 100644 test/runtime/type/guard/kind/symbol.ts create mode 100644 test/runtime/type/guard/kind/template-literal.ts create mode 100644 test/runtime/type/guard/kind/this.ts create mode 100644 test/runtime/type/guard/kind/tuple.ts create mode 100644 test/runtime/type/guard/kind/uint8array.ts create mode 100644 test/runtime/type/guard/kind/uncapitalize.ts create mode 100644 test/runtime/type/guard/kind/undefined.ts create mode 100644 test/runtime/type/guard/kind/union.ts create mode 100644 test/runtime/type/guard/kind/unknown.ts create mode 100644 test/runtime/type/guard/kind/unsafe.ts create mode 100644 test/runtime/type/guard/kind/uppercase.ts create mode 100644 test/runtime/type/guard/kind/void.ts rename test/runtime/type/guard/{ => type}/any.ts (84%) rename test/runtime/type/guard/{ => type}/array.ts (94%) rename test/runtime/type/guard/{ => type}/async-iterator.ts (86%) rename test/runtime/type/guard/{ => type}/awaited.ts (94%) rename test/runtime/type/guard/{ => type}/bigint.ts (84%) rename test/runtime/type/guard/{ => type}/boolean.ts (84%) rename test/runtime/type/guard/{ => type}/capitalize.ts (93%) rename test/runtime/type/guard/{ => type}/composite.ts (98%) rename test/runtime/type/guard/{ => type}/const.ts (98%) rename test/runtime/type/guard/{ => type}/constructor.ts (93%) rename test/runtime/type/guard/{ => type}/date.ts (94%) rename test/runtime/type/guard/{ => type}/deref.ts (97%) rename test/runtime/type/guard/{ => type}/enum.ts (97%) rename test/runtime/type/guard/{ => type}/exclude.ts (98%) rename test/runtime/type/guard/{ => type}/extract.ts (98%) rename test/runtime/type/guard/{ => type}/function.ts (93%) create mode 100644 test/runtime/type/guard/type/index.ts rename test/runtime/type/guard/{ => type}/indexed.ts (99%) rename test/runtime/type/guard/{ => type}/integer.ts (94%) rename test/runtime/type/guard/{ => type}/intersect.ts (90%) rename test/runtime/type/guard/{ => type}/iterator.ts (86%) rename test/runtime/type/guard/{ => type}/keyof.ts (95%) rename test/runtime/type/guard/{ => type}/kind.ts (75%) rename test/runtime/type/guard/{ => type}/literal.ts (91%) rename test/runtime/type/guard/{ => type}/lowercase.ts (93%) create mode 100644 test/runtime/type/guard/type/mapped.ts rename test/runtime/type/guard/{ => type}/not.ts (85%) rename test/runtime/type/guard/{ => type}/null.ts (84%) rename test/runtime/type/guard/{ => type}/number.ts (94%) rename test/runtime/type/guard/{ => type}/object.ts (96%) rename test/runtime/type/guard/{ => type}/omit.ts (97%) rename test/runtime/type/guard/{ => type}/partial.ts (96%) rename test/runtime/type/guard/{ => type}/pick.ts (97%) rename test/runtime/type/guard/{ => type}/promise.ts (92%) create mode 100644 test/runtime/type/guard/type/record.ts rename test/runtime/type/guard/{ => type}/recursive.ts (85%) rename test/runtime/type/guard/{ => type}/ref.ts (87%) rename test/runtime/type/guard/{ => type}/regexp.ts (92%) rename test/runtime/type/guard/{ => type}/required.ts (96%) rename test/runtime/type/guard/{ => type}/rest.ts (95%) rename test/runtime/type/guard/{ => type}/string.ts (92%) rename test/runtime/type/guard/{ => type}/symbol.ts (84%) rename test/runtime/type/guard/{ => type}/template-literal.ts (94%) rename test/runtime/type/guard/{ => type}/this.ts (86%) rename test/runtime/type/guard/{ => type}/tuple.ts (89%) rename test/runtime/type/guard/{ => type}/uint8array.ts (91%) rename test/runtime/type/guard/{ => type}/uncapitalize.ts (93%) rename test/runtime/type/guard/{ => type}/undefined.ts (85%) rename test/runtime/type/guard/{ => type}/union.ts (96%) rename test/runtime/type/guard/{ => type}/unknown.ts (84%) rename test/runtime/type/guard/{ => type}/unsafe.ts (91%) rename test/runtime/type/guard/{ => type}/uppercase.ts (93%) rename test/runtime/type/guard/{ => type}/void.ts (84%) rename test/runtime/type/{ => guard}/value/guard.ts (99%) rename test/runtime/type/{ => guard}/value/index.ts (100%) diff --git a/example/prototypes/mutual.ts b/example/prototypes/mutual.ts new file mode 100644 index 000000000..c47b7c798 --- /dev/null +++ b/example/prototypes/mutual.ts @@ -0,0 +1,48 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Type, TSchema, Static } from '@sinclair/typebox' + +// Mutual Recursive Template +const __A = (reference: T) => Type.Object({ + b: Type.Union([reference, Type.Null()]) +}, { additionalProperties: false }) + +const __B = (reference: T) => Type.Object({ + a: Type.Union([reference, Type.Null()]) +}, { additionalProperties: false }) + +// .... + +// Mutual Recursive Types +const A = Type.Recursive((This) => __A(__B(This))) +const B = Type.Recursive((This) => __B(__A(This))) + +type A = Static; +type B = Static; + diff --git a/package-lock.json b/package-lock.json index c98535528..f8c489aee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.24", + "version": "0.32.25", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.24", + "version": "0.32.25", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 5015e3835..300281f8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.24", + "version": "0.32.25", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index b6fad4d6a..86aac5555 100644 --- a/readme.md +++ b/readme.md @@ -1804,11 +1804,11 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '120.6 kb' │ ' 52.9 kb' │ '2.28 x' │ -│ typebox/errors │ ' 55.7 kb' │ ' 25.5 kb' │ '2.19 x' │ +│ typebox/compiler │ '126.9 kb' │ ' 55.7 kb' │ '2.28 x' │ +│ typebox/errors │ ' 46.1 kb' │ ' 20.8 kb' │ '2.22 x' │ │ typebox/system │ ' 4.7 kb' │ ' 2.0 kb' │ '2.33 x' │ -│ typebox/value │ '146.2 kb' │ ' 62.0 kb' │ '2.36 x' │ -│ typebox │ ' 91.4 kb' │ ' 37.8 kb' │ '2.42 x' │ +│ typebox/value │ '152.2 kb' │ ' 64.5 kb' │ '2.36 x' │ +│ typebox │ ' 95.7 kb' │ ' 39.8 kb' │ '2.40 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/type/any/any.ts b/src/type/any/any.ts index 6916cef60..90eaf93ec 100644 --- a/src/type/any/any.ts +++ b/src/type/any/any.ts @@ -36,5 +36,5 @@ export interface TAny extends TSchema { /** `[Json]` Creates an Any type */ export function Any(options: SchemaOptions = {}): TAny { - return { ...options, [Kind]: 'Any' } as unknown as TAny + return { ...options, [Kind]: 'Any' } as never } diff --git a/src/type/array/array.ts b/src/type/array/array.ts index 912a5ed37..fecf073db 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -60,5 +60,5 @@ export function Array(schema: T, options: ArrayOptions = {}): [Kind]: 'Array', type: 'array', items: CloneType(schema), - } as unknown as TArray + } as never } diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts index 5861f9625..ee9417421 100644 --- a/src/type/async-iterator/async-iterator.ts +++ b/src/type/async-iterator/async-iterator.ts @@ -44,5 +44,5 @@ export function AsyncIterator(items: T, options: SchemaOption [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: CloneType(items), - } as unknown as TAsyncIterator + } as never } diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index 5c3fbb5a2..5318d98ea 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -35,7 +35,7 @@ import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsIntersect, IsUnion, IsPromise } from '../guard/type' +import { IsIntersect, IsUnion, IsPromise } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ @@ -46,7 +46,7 @@ type TFromRest = : Acc // prettier-ignore function FromRest(T: [...T]) : TFromRest { - return T.map(L => AwaitedResolve(L)) as TFromRest + return T.map(L => AwaitedResolve(L)) as never } // ---------------------------------------------------------------- // FromIntersect @@ -55,7 +55,7 @@ function FromRest(T: [...T]) : TFromRest { type TFromIntersect = TIntersect> // prettier-ignore function FromIntersect(T: [...T]): TFromIntersect { - return Intersect(FromRest(T) as TSchema[]) as unknown as TFromIntersect + return Intersect(FromRest(T) as TSchema[]) as never } // ---------------------------------------------------------------- // FromUnion @@ -64,7 +64,7 @@ function FromIntersect(T: [...T]): TFromIntersect { type TFromUnion = TUnion> // prettier-ignore function FromUnion(T: [...T]): TFromUnion { - return Union(FromRest(T) as TSchema[]) as unknown as TFromUnion + return Union(FromRest(T) as TSchema[]) as never } // ---------------------------------------------------------------- // Promise @@ -72,7 +72,7 @@ function FromUnion(T: [...T]): TFromUnion { type TFromPromise = TAwaited // prettier-ignore function FromPromise(T: T): TFromPromise { - return AwaitedResolve(T) as TFromPromise + return AwaitedResolve(T) as never } // ---------------------------------------------------------------- // AwaitedResolve @@ -84,7 +84,7 @@ function AwaitedResolve(T: T): TAwaited { IsUnion(T) ? FromUnion(T.anyOf) : IsPromise(T) ? FromPromise(T.item) : T - ) as TAwaited + ) as never } // ------------------------------------------------------------------ // TAwaited diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts index eb0ca248e..b653a09a2 100644 --- a/src/type/bigint/bigint.ts +++ b/src/type/bigint/bigint.ts @@ -47,5 +47,5 @@ export function BigInt(options: BigIntOptions = {}): TBigInt { ...options, [Kind]: 'BigInt', type: 'bigint', - } as TBigInt + } as never } diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts index 68a2c26a5..7729a0052 100644 --- a/src/type/boolean/boolean.ts +++ b/src/type/boolean/boolean.ts @@ -40,5 +40,5 @@ export function Boolean(options: SchemaOptions = {}): TBoolean { ...options, [Kind]: 'Boolean', type: 'boolean', - } as unknown as TBoolean + } as never } diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts index f6c3dd54e..89b17b2ee 100644 --- a/src/type/clone/type.ts +++ b/src/type/clone/type.ts @@ -31,7 +31,7 @@ import { Clone } from './value' /** Clones a Rest */ export function CloneRest(schemas: T): T { - return schemas.map((schema) => CloneType(schema)) as T + return schemas.map((schema) => CloneType(schema)) as never } /** Clones a Type */ export function CloneType(schema: T, options: SchemaOptions = {}): T { diff --git a/src/type/clone/value.ts b/src/type/clone/value.ts index 548cef442..0a062cb38 100644 --- a/src/type/clone/value.ts +++ b/src/type/clone/value.ts @@ -41,9 +41,14 @@ function RegExpType(value: RegExp) { return new RegExp(value.source, value.flags) } function ObjectType(value: Record) { - const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) - const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) - return { ...clonedProperties, ...clonedSymbols } + const result = {} as Record + for (const key of Object.getOwnPropertyNames(value)) { + result[key] = Visit(value[key]) + } + for (const key of Object.getOwnPropertySymbols(value)) { + result[key] = Visit(value[key]) + } + return result } // prettier-ignore function Visit(value: unknown): any { diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index 8254036ee..c3a6f8084 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -38,7 +38,7 @@ import { SetDistinct, TSetDistinct } from '../sets/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsNever } from '../guard/type' +import { IsNever } from '../guard/kind' // ------------------------------------------------------------------ // CompositeKeys // ------------------------------------------------------------------ @@ -50,9 +50,9 @@ type TCompositeKeys = ( ) // prettier-ignore function CompositeKeys(T: [...T]): TCompositeKeys { - return SetDistinct(T.reduce((Acc, L) => { - return ([...Acc, ...KeyOfPropertyKeys(L)]) as never - }, [])) as never + const Acc = [] as PropertyKey[] + for(const L of T) Acc.push(...KeyOfPropertyKeys(L)) + return SetDistinct(Acc) as never } // ------------------------------------------------------------------ // FilterNever @@ -80,9 +80,9 @@ type TCompositeProperty(T: [...T], K: K): TCompositeProperty { - return FilterNever(T.reduce((Acc, L) => { - return [...Acc, ...IndexFromPropertyKeys(L, [K])] as never - }, [])) as never + const Acc = [] as TSchema[] + for(const L of T) Acc.push(...IndexFromPropertyKeys(L, [K])) + return FilterNever(Acc) as never } // ------------------------------------------------------------------ // CompositeProperties @@ -95,9 +95,11 @@ type TCompositeProperties(T: [...T], K: [...K]): TCompositeProperties { - return K.reduce((Acc, L) => { - return { ...Acc, [L]: IntersectEvaluated(CompositeProperty(T, L)) } - }, {}) as never + const Acc = {} as never + for(const L of K) { + Acc[L] = IntersectEvaluated(CompositeProperty(T, L)) + } + return Acc } // ------------------------------------------------------------------ // Composite @@ -111,11 +113,10 @@ type TCompositeEvaluate< > = R // prettier-ignore export type TComposite = TCompositeEvaluate - // prettier-ignore export function Composite(T: [...T], options: ObjectOptions = {}): TComposite { const K = CompositeKeys(T) const P = CompositeProperties(T, K) const R = Object(P, options) - return R as TComposite + return R as never } diff --git a/src/type/const/const.ts b/src/type/const/const.ts index 3eecebc0b..1606b9930 100644 --- a/src/type/const/const.ts +++ b/src/type/const/const.ts @@ -60,7 +60,7 @@ type TFromArray = : T // prettier-ignore function FromArray(T: [...T]): TFromArray { - return T.map(L => FromValue(L, false)) as TFromArray + return T.map(L => FromValue(L, false)) as never } // ------------------------------------------------------------------ // FromProperties @@ -73,16 +73,16 @@ type TFromProperties> = { } // prettier-ignore function FromProperties>(value: T): TFromProperties { - return globalThis.Object.getOwnPropertyNames(value).reduce((acc, key) => { - return { ...acc, [key]: Readonly(FromValue(value[key], false)) } - }, {} as TProperties) as unknown as TFromProperties + const Acc = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(value)) Acc[K] = Readonly(FromValue(value[K], false)) + return Acc as never } // ------------------------------------------------------------------ // ConditionalReadonly - Only applied if not root // ------------------------------------------------------------------ type TConditionalReadonly = Root extends true ? T : TReadonly function ConditionalReadonly(T: T, root: Root): TConditionalReadonly { - return (root === true ? T : Readonly(T)) as unknown as TConditionalReadonly + return (root === true ? T : Readonly(T)) as never } // ------------------------------------------------------------------ // FromValue @@ -122,7 +122,7 @@ function FromValue(value: T, root: Root): FromValue + ) as never } // ------------------------------------------------------------------ // TConst @@ -131,5 +131,5 @@ export type TConst = FromValue /** `[JavaScript]` Creates a readonly const type from the given value. */ export function Const(T: T, options: SchemaOptions = {}): TConst { - return CloneType(FromValue(T, true), options) as TConst + return CloneType(FromValue(T, true), options) as never } diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index c68ffe992..65f440c32 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -72,5 +72,5 @@ export function Constructor(parameters: type: 'Constructor', parameters: CloneRest(parameters), returns: CloneType(returns), - } as unknown as TConstructor + } as never } diff --git a/src/type/date/date.ts b/src/type/date/date.ts index 3a7534217..192cea310 100644 --- a/src/type/date/date.ts +++ b/src/type/date/date.ts @@ -52,5 +52,5 @@ export function Date(options: DateOptions = {}): TDate { ...options, [Kind]: 'Date', type: 'Date', - } as unknown as TDate + } as never } diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts index 117de7564..76d626a29 100644 --- a/src/type/deref/deref.ts +++ b/src/type/deref/deref.ts @@ -46,7 +46,7 @@ import { IsUndefined } from '../guard/value' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsPromise, IsAsyncIterator, IsIterator, IsRef } from '../guard/type' +import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsPromise, IsAsyncIterator, IsIterator, IsRef } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ @@ -56,8 +56,8 @@ export type TFromRest = ( ? TFromRest]> : Acc ) -function FromRest(schema: TSchema[], references: TSchema[]) { - return schema.map((schema) => Deref(schema, references)) +function FromRest(schema: [...T], references: TSchema[]): TFromRest { + return schema.map((schema) => Deref(schema, references)) as never } // ------------------------------------------------------------------ // FromProperties @@ -68,9 +68,11 @@ type FromProperties = Evaluate<{ }> // prettier-ignore function FromProperties(properties: TProperties, references: TSchema[]) { - return globalThis.Object.getOwnPropertyNames(properties).reduce((acc, key) => { - return {...acc, [key]: Deref(properties[key], references) } - }, {} as TProperties) + const Acc = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(properties)) { + Acc[K] = Deref(properties[K], references) + } + return Acc as never } // prettier-ignore function FromConstructor(schema: TConstructor, references: TSchema[]) { @@ -147,7 +149,7 @@ function DerefResolve(schema: T, references: TSchema[]): TDer IsIterator(schema) ? FromIterator(schema, references) : IsRef(schema) ? FromRef(schema, references) : schema - ) as TDeref + ) as never } // prettier-ignore export type TDeref = diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts index 2406cc027..08a87b495 100644 --- a/src/type/enum/enum.ts +++ b/src/type/enum/enum.ts @@ -54,5 +54,5 @@ export function Enum>(item: .map((key) => item[key]) as T[keyof T][] const values2 = [...new Set(values1)] const anyOf = values2.map((value) => Literal(value)) - return Union(anyOf, { ...options, [Hint]: 'Enum' }) as unknown as TEnum + return Union(anyOf, { ...options, [Hint]: 'Enum' }) as never } diff --git a/src/type/exclude/exclude-from-mapped-result.ts b/src/type/exclude/exclude-from-mapped-result.ts index 3fe927fde..8985e467e 100644 --- a/src/type/exclude/exclude-from-mapped-result.ts +++ b/src/type/exclude/exclude-from-mapped-result.ts @@ -46,9 +46,9 @@ function FromProperties< P extends TProperties, T extends TSchema >(P: P, U: T): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Exclude(P[K2], U) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Exclude(P[K2], U) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -65,7 +65,7 @@ function FromMappedResult< R extends TMappedResult, T extends TSchema >(R: R, T: T): TFromMappedResult { - return FromProperties(R.properties, T) as TFromMappedResult + return FromProperties(R.properties, T) as never } // ------------------------------------------------------------------ // ExcludeFromMappedResult @@ -84,6 +84,6 @@ export function ExcludeFromMappedResult< T extends TSchema, P extends TProperties = TFromMappedResult >(R: R, T: T): TMappedResult

      { - const P = FromMappedResult(R, T) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, T) + return MappedResult(P) as never } diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index 021caf156..3140a5e10 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -42,7 +42,7 @@ import { ExcludeFromTemplateLiteral, type TExcludeFromTemplateLiteral } from './ // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' +import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // ExcludeRest // ------------------------------------------------------------------ diff --git a/src/type/extends/extends-from-mapped-key.ts b/src/type/extends/extends-from-mapped-key.ts index 7e3620ff2..ce43b6b60 100644 --- a/src/type/extends/extends-from-mapped-key.ts +++ b/src/type/extends/extends-from-mapped-key.ts @@ -54,7 +54,7 @@ function FromPropertyKey< >(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKey { return { [K]: Extends(Literal(K as TLiteralValue), U, L, R, options) as any - } as TFromPropertyKey + } as never } // ------------------------------------------------------------------ // FromPropertyKeys @@ -80,7 +80,7 @@ function FromPropertyKeys< >(K: [...K], U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(LK, U, L, R, options) } - }, {} as TProperties) as TFromPropertyKeys + }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey @@ -101,7 +101,7 @@ function FromMappedKey< L extends TSchema, R extends TSchema >(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(K.keys, U, L, R, options) as TFromMappedKey + return FromPropertyKeys(K.keys, U, L, R, options) as never } // ------------------------------------------------------------------ // ExtendsFromMappedKey @@ -124,6 +124,6 @@ export function ExtendsFromMappedKey< R extends TSchema, P extends TProperties = TFromMappedKey >(T: T, U: U, L: L, R: R, options: SchemaOptions): TMappedResult

      { - const P = FromMappedKey(T, U, L, R, options) as unknown as P - return MappedResult(P) + const P = FromMappedKey(T, U, L, R, options) + return MappedResult(P) as never } diff --git a/src/type/extends/extends-from-mapped-result.ts b/src/type/extends/extends-from-mapped-result.ts index c202aa356..2c857cf9d 100644 --- a/src/type/extends/extends-from-mapped-result.ts +++ b/src/type/extends/extends-from-mapped-result.ts @@ -50,9 +50,9 @@ function FromProperties< True extends TSchema, False extends TSchema >(P: P, Right: Right, True: True, False: False, options: SchemaOptions): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Extends(P[K2], Right, True, False, options) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extends(P[K2], Right, True, False, options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -73,7 +73,7 @@ function FromMappedResult< True extends TSchema, False extends TSchema >(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TFromMappedResult { - return FromProperties(Left.properties, Right, True, False, options) as TFromMappedResult + return FromProperties(Left.properties, Right, True, False, options) as never } // ------------------------------------------------------------------ // ExtendsFromMappedResult @@ -96,6 +96,6 @@ export function ExtendsFromMappedResult< False extends TSchema, P extends TProperties = TFromMappedResult >(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(Left, Right, True, False, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(Left, Right, True, False, options) + return MappedResult(P) as never } diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts index 53049c4d5..614722a4f 100644 --- a/src/type/extends/extends.ts +++ b/src/type/extends/extends.ts @@ -39,7 +39,7 @@ import { ExtendsFromMappedResult, type TExtendsFromMappedResult } from './extend // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsMappedResult } from '../guard/type' +import { IsMappedKey, IsMappedResult } from '../guard/kind' // prettier-ignore type TExtendsResolve = ( @@ -56,7 +56,7 @@ function ExtendsResolve + ) as never } // ------------------------------------------------------------------ // TExtends @@ -76,5 +76,5 @@ export function Extends + ) as never } diff --git a/src/type/extract/extract-from-mapped-result.ts b/src/type/extract/extract-from-mapped-result.ts index 65d244162..b49720a37 100644 --- a/src/type/extract/extract-from-mapped-result.ts +++ b/src/type/extract/extract-from-mapped-result.ts @@ -46,9 +46,9 @@ function FromProperties< P extends TProperties, T extends TSchema >(P: P, T: T): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Extract(P[K2], T) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extract(P[K2], T) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -65,7 +65,7 @@ function FromMappedResult< R extends TMappedResult, T extends TSchema >(R: R, T: T): TFromMappedResult { - return FromProperties(R.properties, T) as TFromMappedResult + return FromProperties(R.properties, T) as never } // ------------------------------------------------------------------ // ExtractFromMappedResult @@ -84,6 +84,6 @@ export function ExtractFromMappedResult< T extends TSchema, P extends TProperties = TFromMappedResult >(R: R, T: T): TMappedResult

      { - const P = FromMappedResult(R, T) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, T) + return MappedResult(P) as never } diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index 40d7e2d3d..91c8ca5e2 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -42,7 +42,7 @@ import { ExtractFromTemplateLiteral, type TExtractFromTemplateLiteral } from './ // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/type' +import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // ExtractRest diff --git a/src/type/function/function.ts b/src/type/function/function.ts index d86297985..f4333585c 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -73,5 +73,5 @@ export function Function(parameters: [.. type: 'Function', parameters: CloneRest(parameters), returns: CloneType(returns), - } as unknown as TFunction + } as never } diff --git a/src/type/guard/index.ts b/src/type/guard/index.ts index a8f31501a..feeaa6296 100644 --- a/src/type/guard/index.ts +++ b/src/type/guard/index.ts @@ -26,5 +26,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +export * as KindGuard from './kind' export * as TypeGuard from './type' export * as ValueGuard from './value' diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts new file mode 100644 index 000000000..b68f01bca --- /dev/null +++ b/src/type/guard/kind.ts @@ -0,0 +1,285 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ValueGuard from './value' +import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' +import { TransformOptions } from '../transform/index' +import { TTemplateLiteral } from '../template-literal/index' +import { TArray } from '../array/index' +import { TBoolean } from '../boolean/index' +import type { TRecord } from '../record/index' +import type { TString } from '../string/index' +import type { TUnion } from '../union/index' +import type { TAny } from '../any/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TBigInt } from '../bigint/index' +import type { TConstructor } from '../constructor/index' +import type { TFunction } from '../function/index' +import type { TInteger } from '../integer/index' +import type { TIntersect } from '../intersect/index' +import type { TIterator } from '../iterator/index' +import type { TLiteral } from '../literal/index' +import type { TMappedKey, TMappedResult } from '../mapped/index' +import type { TNever } from '../never/index' +import type { TNot } from '../not/index' +import type { TNull } from '../null/index' +import type { TNumber } from '../number/index' +import type { TObject, TProperties } from '../object/index' +import type { TOptional } from '../optional/index' +import type { TPromise } from '../promise/index' +import type { TReadonly } from '../readonly/index' +import type { TRef } from '../ref/index' +import type { TRegExp } from '../regexp/index' +import type { TSchema } from '../schema/index' +import type { TSymbol } from '../symbol/index' +import type { TTuple } from '../tuple/index' +import type { TUint8Array } from '../uint8array/index' +import type { TUndefined } from '../undefined/index' +import type { TUnknown } from '../unknown/index' +import type { TUnsafe } from '../unsafe/index' +import type { TVoid } from '../void/index' +import type { TDate } from '../date/index' +import type { TThis } from '../recursive/index' + +/** `[Kind-Only]` Returns true if this value has a Readonly symbol */ +export function IsReadonly(value: T): value is TReadonly { + return ValueGuard.IsObject(value) && value[ReadonlyKind] === 'Readonly' +} +/** `[Kind-Only]` Returns true if this value has a Optional symbol */ +export function IsOptional(value: T): value is TOptional { + return ValueGuard.IsObject(value) && value[OptionalKind] === 'Optional' +} +/** `[Kind-Only]` Returns true if the given value is TAny */ +export function IsAny(value: unknown): value is TAny { + return IsKindOf(value, 'Any') +} +/** `[Kind-Only]` Returns true if the given value is TArray */ +export function IsArray(value: unknown): value is TArray { + return IsKindOf(value, 'Array') +} +/** `[Kind-Only]` Returns true if the given value is TAsyncIterator */ +export function IsAsyncIterator(value: unknown): value is TAsyncIterator { + return IsKindOf(value, 'AsyncIterator') +} +/** `[Kind-Only]` Returns true if the given value is TBigInt */ +export function IsBigInt(value: unknown): value is TBigInt { + return IsKindOf(value, 'BigInt') +} +/** `[Kind-Only]` Returns true if the given value is TBoolean */ +export function IsBoolean(value: unknown): value is TBoolean { + return IsKindOf(value, 'Boolean') +} +/** `[Kind-Only]` Returns true if the given value is TConstructor */ +export function IsConstructor(value: unknown): value is TConstructor { + return IsKindOf(value, 'Constructor') +} +/** `[Kind-Only]` Returns true if the given value is TDate */ +export function IsDate(value: unknown): value is TDate { + return IsKindOf(value, 'Date') +} +/** `[Kind-Only]` Returns true if the given value is TFunction */ +export function IsFunction(value: unknown): value is TFunction { + return IsKindOf(value, 'Function') +} +/** `[Kind-Only]` Returns true if the given value is TInteger */ +export function IsInteger(value: unknown): value is TInteger { + return IsKindOf(value, 'Integer') +} +/** `[Kind-Only]` Returns true if the given schema is TProperties */ +export function IsProperties(value: unknown): value is TProperties { + return ValueGuard.IsObject(value) +} +/** `[Kind-Only]` Returns true if the given value is TIntersect */ +export function IsIntersect(value: unknown): value is TIntersect { + return IsKindOf(value, 'Intersect') +} +/** `[Kind-Only]` Returns true if the given value is TIterator */ +export function IsIterator(value: unknown): value is TIterator { + return IsKindOf(value, 'Iterator') +} +/** `[Kind-Only]` Returns true if the given value is a TKind with the given name. */ +export function IsKindOf(value: unknown, kind: T): value is Record & { [Kind]: T } { + return ValueGuard.IsObject(value) && Kind in value && value[Kind] === kind +} +/** `[Kind-Only]` Returns true if the given value is TLiteral */ +export function IsLiteralString(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsString(value.const) +} +/** `[Kind-Only]` Returns true if the given value is TLiteral */ +export function IsLiteralNumber(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsNumber(value.const) +} +/** `[Kind-Only]` Returns true if the given value is TLiteral */ +export function IsLiteralBoolean(value: unknown): value is TLiteral { + return IsLiteral(value) && ValueGuard.IsBoolean(value.const) +} +/** `[Kind-Only]` Returns true if the given value is TLiteral */ +export function IsLiteral(value: unknown): value is TLiteral { + return IsKindOf(value, 'Literal') +} +/** `[Kind-Only]` Returns true if the given value is a TMappedKey */ +export function IsMappedKey(value: unknown): value is TMappedKey { + return IsKindOf(value, 'MappedKey') +} +/** `[Kind-Only]` Returns true if the given value is TMappedResult */ +export function IsMappedResult(value: unknown): value is TMappedResult { + return IsKindOf(value, 'MappedResult') +} +/** `[Kind-Only]` Returns true if the given value is TNever */ +export function IsNever(value: unknown): value is TNever { + return IsKindOf(value, 'Never') +} +/** `[Kind-Only]` Returns true if the given value is TNot */ +export function IsNot(value: unknown): value is TNot { + return IsKindOf(value, 'Not') +} +/** `[Kind-Only]` Returns true if the given value is TNull */ +export function IsNull(value: unknown): value is TNull { + return IsKindOf(value, 'Null') +} +/** `[Kind-Only]` Returns true if the given value is TNumber */ +export function IsNumber(value: unknown): value is TNumber { + return IsKindOf(value, 'Number') +} +/** `[Kind-Only]` Returns true if the given value is TObject */ +export function IsObject(value: unknown): value is TObject { + return IsKindOf(value, 'Object') +} +/** `[Kind-Only]` Returns true if the given value is TPromise */ +export function IsPromise(value: unknown): value is TPromise { + return IsKindOf(value, 'Promise') +} +/** `[Kind-Only]` Returns true if the given value is TRecord */ +export function IsRecord(value: unknown): value is TRecord { + return IsKindOf(value, 'Record') +} +/** `[Kind-Only]` Returns true if this value is TRecursive */ +export function IsRecursive(value: unknown): value is { [Hint]: 'Recursive' } { + return ValueGuard.IsObject(value) && Hint in value && value[Hint] === 'Recursive' +} +/** `[Kind-Only]` Returns true if the given value is TRef */ +export function IsRef(value: unknown): value is TRef { + return IsKindOf(value, 'Ref') +} +/** `[Kind-Only]` Returns true if the given value is TRegExp */ +export function IsRegExp(value: unknown): value is TRegExp { + return IsKindOf(value, 'RegExp') +} +/** `[Kind-Only]` Returns true if the given value is TString */ +export function IsString(value: unknown): value is TString { + return IsKindOf(value, 'String') +} +/** `[Kind-Only]` Returns true if the given value is TSymbol */ +export function IsSymbol(value: unknown): value is TSymbol { + return IsKindOf(value, 'Symbol') +} +/** `[Kind-Only]` Returns true if the given value is TTemplateLiteral */ +export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral { + return IsKindOf(value, 'TemplateLiteral') +} +/** `[Kind-Only]` Returns true if the given value is TThis */ +export function IsThis(value: unknown): value is TThis { + return IsKindOf(value, 'This') +} +/** `[Kind-Only]` Returns true of this value is TTransform */ +export function IsTransform(value: unknown): value is { [TransformKind]: TransformOptions } { + return ValueGuard.IsObject(value) && TransformKind in value +} +/** `[Kind-Only]` Returns true if the given value is TTuple */ +export function IsTuple(value: unknown): value is TTuple { + return IsKindOf(value, 'Tuple') +} +/** `[Kind-Only]` Returns true if the given value is TUndefined */ +export function IsUndefined(value: unknown): value is TUndefined { + return IsKindOf(value, 'Undefined') +} +/** `[Kind-Only]` Returns true if the given value is TUnion */ +export function IsUnion(value: unknown): value is TUnion { + return IsKindOf(value, 'Union') +} +/** `[Kind-Only]` Returns true if the given value is TUint8Array */ +export function IsUint8Array(value: unknown): value is TUint8Array { + return IsKindOf(value, 'Uint8Array') +} +/** `[Kind-Only]` Returns true if the given value is TUnknown */ +export function IsUnknown(value: unknown): value is TUnknown { + return IsKindOf(value, 'Unknown') +} +/** `[Kind-Only]` Returns true if the given value is a raw TUnsafe */ +export function IsUnsafe(value: unknown): value is TUnsafe { + return IsKindOf(value, 'Unsafe') +} +/** `[Kind-Only]` Returns true if the given value is TVoid */ +export function IsVoid(value: unknown): value is TVoid { + return IsKindOf(value, 'Void') +} +/** `[Kind-Only]` Returns true if the given value is TKind */ +export function IsKind(value: unknown): value is Record & { [Kind]: string } { + return ValueGuard.IsObject(value) && Kind in value && ValueGuard.IsString(value[Kind]) +} +/** `[Kind-Only]` Returns true if the given value is TSchema */ +export function IsSchema(value: unknown): value is TSchema { + // prettier-ignore + return ( + IsAny(value) || + IsArray(value) || + IsBoolean(value) || + IsBigInt(value) || + IsAsyncIterator(value) || + IsConstructor(value) || + IsDate(value) || + IsFunction(value) || + IsInteger(value) || + IsIntersect(value) || + IsIterator(value) || + IsLiteral(value) || + IsMappedKey(value) || + IsMappedResult(value) || + IsNever(value) || + IsNot(value) || + IsNull(value) || + IsNumber(value) || + IsObject(value) || + IsPromise(value) || + IsRecord(value) || + IsRef(value) || + IsRegExp(value) || + IsString(value) || + IsSymbol(value) || + IsTemplateLiteral(value) || + IsThis(value) || + IsTuple(value) || + IsUndefined(value) || + IsUnion(value) || + IsUint8Array(value) || + IsUnknown(value) || + IsUnsafe(value) || + IsVoid(value) || + IsKind(value) + ) +} diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index 5282c5713..f80b24fe7 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -472,7 +472,7 @@ export function IsSymbol(value: unknown): value is TSymbol { ) } /** Returns true if the given value is TTemplateLiteral */ -export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral { +export function IsTemplateLiteral(value: unknown): value is TTemplateLiteral { // prettier-ignore return ( IsKindOf(value, 'TemplateLiteral') && diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts index 807e10c07..1ab44dc30 100644 --- a/src/type/helpers/helpers.ts +++ b/src/type/helpers/helpers.ts @@ -59,7 +59,7 @@ type IncrementReverse = T extends `${infer L}${infer R}` ? `${ export type TIncrement = IncrementReverse>> /** Increments the given string value + 1 */ export function Increment(T: T): TIncrement { - return (parseInt(T) + 1).toString() as TIncrement + return (parseInt(T) + 1).toString() as never } // ------------------------------------------------------------------ // Helper: Type Asserts diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index d32a11056..e82b1b5f5 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -47,7 +47,7 @@ function MappedIndexPropertyKey< T extends TSchema, K extends PropertyKey >(T: T, K: K, options: SchemaOptions): TMappedIndexPropertyKey { - return { [K]: Index(T, [K], options) } as TMappedIndexPropertyKey + return { [K]: Index(T, [K], options) } as never } // ------------------------------------------------------------------ // MappedIndexPropertyKeys @@ -65,7 +65,7 @@ function MappedIndexPropertyKeys< >(T: T, K: [...K], options: SchemaOptions): TMappedIndexPropertyKeys { return K.reduce((Acc, L) => { return { ...Acc, ...MappedIndexPropertyKey(T, L, options) } - }, {} as TProperties) as TMappedIndexPropertyKeys + }, {} as TProperties) as never } // ------------------------------------------------------------------ // MappedIndexProperties @@ -79,7 +79,7 @@ function MappedIndexProperties< T extends TSchema, K extends TMappedKey >(T: T, K: K, options: SchemaOptions): TMappedIndexProperties { - return MappedIndexPropertyKeys(T, K.keys, options) as TMappedIndexProperties + return MappedIndexPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ // TIndexFromMappedKey @@ -98,6 +98,6 @@ export function IndexFromMappedKey< K extends TMappedKey, P extends TProperties = TMappedIndexProperties >(T: T, K: K, options: SchemaOptions): TMappedResult

      { - const P = MappedIndexProperties(T, K, options) as unknown as P - return MappedResult(P) + const P = MappedIndexProperties(T, K, options) + return MappedResult(P) as never } diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index ce4745886..f51837c1f 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -47,9 +47,11 @@ function FromProperties< T extends TSchema, P extends TProperties >(T: T, P: P, options: SchemaOptions): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Index(T, IndexPropertyKeys(P[K2]), options) } - }, {}) as TFromProperties + const Acc = {} as Record + for(const K2 of Object.getOwnPropertyNames(P)) { + Acc[K2] = Index(T, IndexPropertyKeys(P[K2]), options) + } + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -66,7 +68,7 @@ function FromMappedResult< T extends TSchema, R extends TMappedResult >(T: T, R: R, options: SchemaOptions): TFromMappedResult { - return FromProperties(T, R.properties, options) as TFromMappedResult + return FromProperties(T, R.properties, options) as never } // ------------------------------------------------------------------ // TIndexFromMappedResult @@ -85,6 +87,6 @@ export function IndexFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult >(T: T, R: R, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(T, R, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(T, R, options) + return MappedResult(P) as never } diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts index 48a45cd92..dcb60fad7 100644 --- a/src/type/indexed/indexed-property-keys.ts +++ b/src/type/indexed/indexed-property-keys.ts @@ -36,7 +36,7 @@ import type { TUnion } from '../union/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../guard/type' +import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../guard/kind' // ------------------------------------------------------------------ // FromTemplateLiteral // ------------------------------------------------------------------ @@ -45,7 +45,7 @@ type TFromTemplateLiteral(T: T): TFromTemplateLiteral { const R = TemplateLiteralGenerate(T) as string[] - return R.map(S => S.toString()) as TFromTemplateLiteral + return R.map(S => S.toString()) as never } // ------------------------------------------------------------------ // FromUnion @@ -58,9 +58,9 @@ type TFromUnion = ( ) // prettier-ignore function FromUnion(T: T): TFromUnion { - return T.reduce((Acc, L) => { - return [...Acc, ...IndexPropertyKeys(L)] - }, [] as string[]) as TFromUnion + const Acc = [] as string[] + for(const L of T) Acc.push(...IndexPropertyKeys(L)) + return Acc as never } // ------------------------------------------------------------------ // FromLiteral @@ -75,7 +75,7 @@ type TFromLiteral = ( function FromLiteral(T: T): TFromLiteral { return ( [(T as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString() - ) as TFromLiteral + ) as never } // ------------------------------------------------------------------ // IndexedKeyResolve @@ -99,5 +99,5 @@ export function IndexPropertyKeys(T: T): TIndexPropertyKeys + ))] as never } diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index f3df37e77..f5519e46b 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -46,7 +46,7 @@ import { IndexFromMappedResult, type TIndexFromMappedResult } from './indexed-fr // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion } from '../guard/type' +import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // FromRest @@ -59,7 +59,7 @@ type TFromRest(T: [...T], K: K): TFromRest { - return T.map(L => IndexFromPropertyKey(L, K)) as TFromRest + return T.map(L => IndexFromPropertyKey(L, K)) as never } // ------------------------------------------------------------------ // FromIntersectRest @@ -84,7 +84,7 @@ type TFromIntersect = ( function FromIntersect(T: [...T], K: K): TFromIntersect { return ( IntersectEvaluated(FromIntersectRest(FromRest(T as TSchema[], K))) - ) as TFromIntersect + ) as never } // ------------------------------------------------------------------ // FromUnionRest @@ -135,7 +135,7 @@ type TFromUnion = ( function FromUnion(T: [...T], K: K): TFromUnion { return ( UnionEvaluated(FromUnionRest(FromRest(T as TSchema[], K))) - ) as TFromUnion + ) as never } // ------------------------------------------------------------------ // FromTuple @@ -152,7 +152,7 @@ function FromTuple(T: [...T], K: K): K in T ? T[K as number] : K === '[number]' ? UnionEvaluated(T) : Never() - ) as TFromTuple + ) as never } // ------------------------------------------------------------------ // FromArray @@ -169,7 +169,7 @@ function FromArray(T: T, K: K): TFromA K === '[number]' ? T : Never() - ) as TFromArray + ) as never } // ------------------------------------------------------------------ // FromProperty @@ -191,7 +191,7 @@ type TFromProperty< ) // prettier-ignore function FromProperty(T: T, K: K): TFromProperty { - return (K in T ? T[K as string] : Never()) as TFromProperty + return (K in T ? T[K as string] : Never()) as never } // ------------------------------------------------------------------ // FromKey @@ -215,7 +215,7 @@ export function IndexFromPropertyKey(T IsArray(T) ? FromArray(T.items, K) : IsObject(T) ? FromProperty(T.properties, K) : Never() - ) as TIndexFromPropertyKey + ) as never } // ------------------------------------------------------------------ // FromKeys @@ -228,7 +228,7 @@ export type TIndexFromPropertyKeys(T: T, K: [...K]): TIndexFromPropertyKeys { - return K.map(L => IndexFromPropertyKey(T, L)) as TIndexFromPropertyKeys + return K.map(L => IndexFromPropertyKey(T, L)) as never } // ------------------------------------------------------------------ // FromSchema @@ -241,7 +241,7 @@ type FromSchema = ( function FromSchema(T: T, K: [...K]): FromSchema { return ( UnionEvaluated(IndexFromPropertyKeys(T, K as PropertyKey[])) - ) as FromSchema + ) as never } // ------------------------------------------------------------------ // TIndex diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts index e74ec75ea..3146c25cf 100644 --- a/src/type/integer/integer.ts +++ b/src/type/integer/integer.ts @@ -47,5 +47,5 @@ export function Integer(options: IntegerOptions = {}): TInteger { ...options, [Kind]: 'Integer', type: 'integer', - } as unknown as TInteger + } as never } diff --git a/src/type/intersect/intersect-create.ts b/src/type/intersect/intersect-create.ts index eb179e770..300291aa1 100644 --- a/src/type/intersect/intersect-create.ts +++ b/src/type/intersect/intersect-create.ts @@ -34,7 +34,7 @@ import type { TIntersect, IntersectOptions } from './intersect-type' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsObject, IsSchema } from '../guard/type' +import { IsObject, IsSchema } from '../guard/kind' // ------------------------------------------------------------------ // IntersectCreate // ------------------------------------------------------------------ @@ -48,5 +48,5 @@ export function IntersectCreate(T: [...T], options: Interse (options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects ? { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: CloneRest(T) } : { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: CloneRest(T) }) - ) as TIntersect + ) as never } diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts index 2647e1b0d..f8a5c884e 100644 --- a/src/type/intersect/intersect-evaluated.ts +++ b/src/type/intersect/intersect-evaluated.ts @@ -41,11 +41,7 @@ import { IntersectCreate } from './intersect-create' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -// prettier-ignore -import { - IsOptional, - IsTransform, -} from '../guard/type' +import { IsOptional, IsTransform } from '../guard/kind' // ------------------------------------------------------------------ // IsIntersectOptional @@ -60,7 +56,7 @@ type TIsIntersectOptional = ( ) // prettier-ignore function IsIntersectOptional(T: T): TIsIntersectOptional { - return T.every(L => IsOptional(L)) as TIsIntersectOptional + return T.every(L => IsOptional(L)) as never } // ------------------------------------------------------------------ // RemoveOptionalFromType @@ -75,7 +71,7 @@ type TRemoveOptionalFromType = ( function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { return ( Discard(T, [OptionalKind]) - ) as TRemoveOptionalFromType + ) as never } // ------------------------------------------------------------------ // RemoveOptionalFromRest @@ -90,7 +86,7 @@ type TRemoveOptionalFromRest = ) // prettier-ignore function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { - return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as TRemoveOptionalFromRest + return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as never } // ------------------------------------------------------------------ // ResolveIntersect @@ -107,7 +103,7 @@ function ResolveIntersect(T: [...T], options: SchemaOptions IsIntersectOptional(T) ? Optional(IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options)) : IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options) - ) as unknown as TResolveIntersect + ) as never } // ------------------------------------------------------------------ // IntersectEvaluated diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts index e677da624..5672f4c63 100644 --- a/src/type/intersect/intersect.ts +++ b/src/type/intersect/intersect.ts @@ -36,7 +36,7 @@ import { IntersectCreate } from './intersect-create' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsTransform } from '../guard/type' +import { IsTransform } from '../guard/kind' // ------------------------------------------------------------------ // Intersect // ------------------------------------------------------------------ diff --git a/src/type/intrinsic/intrinsic-from-mapped-key.ts b/src/type/intrinsic/intrinsic-from-mapped-key.ts index cd370ac1d..51ce4c719 100644 --- a/src/type/intrinsic/intrinsic-from-mapped-key.ts +++ b/src/type/intrinsic/intrinsic-from-mapped-key.ts @@ -50,7 +50,7 @@ function MappedIntrinsicPropertyKey< >(K: K, M: M, options: SchemaOptions): TMappedIntrinsicPropertyKey { return { [K]: Intrinsic(Literal(K as TLiteralValue), M, options) - } as TMappedIntrinsicPropertyKey + } as never } // ------------------------------------------------------------------ // MappedIntrinsicPropertyKeys @@ -72,7 +72,7 @@ function MappedIntrinsicPropertyKeys< >(K: [...K], M: M, options: SchemaOptions): TMappedIntrinsicPropertyKeys { return K.reduce((Acc, L) => { return { ...Acc, ...MappedIntrinsicPropertyKey(L, M, options) } - }, {} as TProperties) as TMappedIntrinsicPropertyKeys + }, {} as TProperties) as never } // ------------------------------------------------------------------ // MappedIntrinsicProperties @@ -89,7 +89,7 @@ function MappedIntrinsicProperties< K extends TMappedKey, M extends IntrinsicMode, >(T: K, M: M, options: SchemaOptions): TMappedIntrinsicProperties { - return MappedIntrinsicPropertyKeys(T['keys'], M, options) as TMappedIntrinsicProperties + return MappedIntrinsicPropertyKeys(T['keys'], M, options) as never } // ------------------------------------------------------------------ // IntrinsicFromMappedKey @@ -108,6 +108,6 @@ export function IntrinsicFromMappedKey< M extends IntrinsicMode, P extends TProperties = TMappedIntrinsicProperties >(T: K, M: M, options: SchemaOptions): TMappedResult

      { - const P = MappedIntrinsicProperties(T, M, options) as unknown as P - return MappedResult(P) + const P = MappedIntrinsicProperties(T, M, options) + return MappedResult(P) as never } diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index 5d4442494..2f7e23c6c 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -36,7 +36,7 @@ import { type TMappedKey } from '../mapped/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsTemplateLiteral, IsUnion, IsLiteral } from '../guard/type' +import { IsMappedKey, IsTemplateLiteral, IsUnion, IsLiteral } from '../guard/kind' // ------------------------------------------------------------------ // Apply // ------------------------------------------------------------------ @@ -78,7 +78,7 @@ function FromTemplateLiteral Literal(value)) const mapped = FromRest(literals as any, mode) const union = Union(mapped) - return TemplateLiteral([union], options) as unknown as TFromTemplateLiteral + return TemplateLiteral([union], options) as never } // ------------------------------------------------------------------ // FromLiteralValue @@ -115,7 +115,7 @@ type TFromRest(T: [...T], M: M): TFromRest { - return T.map(L => Intrinsic(L, M)) as TFromRest + return T.map(L => Intrinsic(L, M)) as never } // ------------------------------------------------------------------ // TIntrinsic diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts index e89816fa7..335bae40c 100644 --- a/src/type/iterator/iterator.ts +++ b/src/type/iterator/iterator.ts @@ -44,5 +44,5 @@ export function Iterator(items: T, options: SchemaOptions = { [Kind]: 'Iterator', type: 'Iterator', items: CloneType(items), - } as unknown as TIterator + } as never } diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index d7edcd9ef..1f4f7df33 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -45,9 +45,9 @@ type TFromProperties< function FromProperties< K extends TProperties >(K: K, options: SchemaOptions): TFromProperties { - return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { - return {...Acc, [K2]: KeyOf(K[K2], options) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = KeyOf(K[K2], options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -62,7 +62,7 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult >(R: R, options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, options) as TFromMappedResult + return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ // KeyOfFromMappedResult @@ -79,6 +79,6 @@ export function KeyOfFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult >(R: R, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, options) + return MappedResult(P) as never } diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 8f7c17e3b..28f945bb1 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -39,7 +39,7 @@ import { SetUnionMany, SetIntersectMany, type TSetUnionMany, type TSetIntersectM // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsRecord } from '../guard/type' +import { IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsRecord } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ @@ -51,9 +51,9 @@ type TFromRest = ( ) // prettier-ignore function FromRest(T: [...T]): TFromRest { - return T.reduce((Acc, L) => { - return [...Acc, KeyOfPropertyKeys(L)] - }, [] as PropertyKey[][]) as TFromRest + const Acc = [] as PropertyKey[][] + for(const L of T) Acc.push(KeyOfPropertyKeys(L)) + return Acc as never } // ------------------------------------------------------------------ // FromIntersect @@ -68,7 +68,7 @@ type TFromIntersect< function FromIntersect(T: [...T]): TFromIntersect { const C = FromRest(T) as PropertyKey[][] const R = SetUnionMany(C) - return R as TFromIntersect + return R as never } // ------------------------------------------------------------------ // FromUnion @@ -83,7 +83,7 @@ type TFromUnion< function FromUnion(T: [...T]): TFromUnion { const C = FromRest(T) as PropertyKey[][] const R = SetIntersectMany(C) - return R as TFromUnion + return R as never } // ------------------------------------------------------------------ // FromTuple @@ -95,7 +95,7 @@ type TFromTuple(T: [...T]): TFromTuple { - return T.map((_, I) => I.toString()) as TFromTuple + return T.map((_, I) => I.toString()) as never } // ------------------------------------------------------------------ // FromArray @@ -121,7 +121,7 @@ type TFromProperties = ( function FromProperties(T: T): TFromProperties { return ( globalThis.Object.getOwnPropertyNames(T) - ) as TFromProperties + ) as never } // ------------------------------------------------------------------ // FromPatternProperties @@ -160,7 +160,7 @@ export function KeyOfPropertyKeys(T: T): TKeyOfPropertyKeys + ) as never } // ---------------------------------------------------------------- // KeyOfPattern diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 8e3b8fcde..895eaa6e0 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -40,7 +40,7 @@ import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult } from '../guard/type' +import { IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ @@ -54,7 +54,7 @@ export type TKeyOfPropertyKeysToRest(T: [...T]): TKeyOfPropertyKeysToRest { - return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as TKeyOfPropertyKeysToRest + return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never } // ------------------------------------------------------------------ // KeyOfTypeResolve diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts index fe3c7b64b..3091b4dbd 100644 --- a/src/type/literal/literal.ts +++ b/src/type/literal/literal.ts @@ -49,5 +49,5 @@ export function Literal(value: T, options: SchemaOption [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean', - } as unknown as TLiteral + } as never } diff --git a/src/type/mapped/mapped-key.ts b/src/type/mapped/mapped-key.ts index d7eba3634..c3a6f288c 100644 --- a/src/type/mapped/mapped-key.ts +++ b/src/type/mapped/mapped-key.ts @@ -39,5 +39,5 @@ export function MappedKey(T: [...T]): TMappedKey { return { [Kind]: 'MappedKey', keys: T - } as unknown as TMappedKey + } as never } diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 6ab806bd0..77c7d37a7 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -55,24 +55,7 @@ import type { TMappedKey } from './mapped-key' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -// prettier-ignore -import { - IsArray, - IsAsyncIterator, - IsConstructor, - IsFunction, - IsIntersect, - IsIterator, - IsReadonly, - IsMappedResult, - IsMappedKey, - IsObject, - IsOptional, - IsPromise, - IsSchema, - IsTuple, - IsUnion, -} from '../guard/type' +import { IsArray, IsAsyncIterator, IsConstructor, IsFunction, IsIntersect, IsIterator, IsReadonly, IsMappedResult, IsMappedKey, IsObject, IsOptional, IsPromise, IsSchema, IsTuple, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // FromMappedResult // @@ -101,7 +84,7 @@ function FromMappedResult(K: K, P: K in P ? FromSchemaType(K, P[K as string]) : MappedResult(P) - ) as TFromMappedResult + ) as never } // ------------------------------------------------------------------ // MappedKeyToKnownMappedResultProperties @@ -115,7 +98,7 @@ type TMappedKeyToKnownMappedResultProperties = { } // prettier-ignore function MappedKeyToKnownMappedResultProperties(K: K): TMappedKeyToKnownMappedResultProperties { - return { [K]: Literal(K as TLiteralValue) } as TMappedKeyToKnownMappedResultProperties + return { [K]: Literal(K as TLiteralValue) } as never } // ------------------------------------------------------------------ // MappedKeyToUnknownMappedResultProperties @@ -133,9 +116,9 @@ type TMappedKeyToUnknownMappedResultProperties

      (P: [...P]): TMappedKeyToUnknownMappedResultProperties

      { - return P.reduce((Acc, L) => { - return { ...Acc, [L]: Literal(L as TLiteralValue) } - }, {} as TProperties) as TMappedKeyToUnknownMappedResultProperties

      + const Acc = {} as Record + for(const L of P) Acc[L] = Literal(L as TLiteralValue) + return Acc as never } // ------------------------------------------------------------------ // MappedKeyToMappedResultProperties @@ -152,7 +135,7 @@ function MappedKeyToMappedResultProperties + ) as never } // prettier-ignore type TFromMappedKey< @@ -163,9 +146,9 @@ type TFromMappedKey< TFromMappedResult ) // prettier-ignore -function FromMappedKey(K: K, P: [...P]) { +function FromMappedKey(K: K, P: [...P]): TFromMappedKey { const R = MappedKeyToMappedResultProperties(K, P) - return FromMappedResult(K, R) + return FromMappedResult(K, R) as never } // ------------------------------------------------------------------ // FromRest @@ -178,7 +161,7 @@ type TFromRest(K: K, T: [...T]): TFromRest { - return T.map(L => FromSchemaType(K, L)) as TFromRest + return T.map(L => FromSchemaType(K, L)) as never } // ------------------------------------------------------------------ // FromProperties @@ -189,9 +172,9 @@ type FromProperties> = R // prettier-ignore function FromProperties(K: K, T: T): FromProperties { - return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K2) => { - return { ...Acc, [K2]: FromSchemaType(K, T[K2]) } - }, {}) as FromProperties + const Acc = {} as Record + for(const K2 of globalThis.Object.getOwnPropertyNames(T)) Acc[K2] = FromSchemaType(K, T[K2]) + return Acc as never } // ------------------------------------------------------------------ // FromMappedPropertyKey @@ -238,7 +221,7 @@ function FromSchemaType(K: K, T: T): F IsArray(T) ? Array(FromSchemaType(K, T.items)) : IsPromise(T) ? Promise(FromSchemaType(K, T.item)) : T - ) as FromSchemaType + ) as never } // ------------------------------------------------------------------ // MappedFunctionReturnType @@ -250,10 +233,10 @@ export type TMappedFunctionReturnType(K: [...K], T: T, Acc: TProperties = {}): TMappedFunctionReturnType { - return K.reduce((Acc, L) => { - return { ...Acc, [L]: FromSchemaType(L, T) } - }, {} as TProperties) as TMappedFunctionReturnType +export function MappedFunctionReturnType(K: [...K], T: T): TMappedFunctionReturnType { + const Acc = {} as Record + for(const L of K) Acc[L] = FromSchemaType(L, T) + return Acc as never } // ------------------------------------------------------------------ // TMappedFunction diff --git a/src/type/never/never.ts b/src/type/never/never.ts index d3113d216..d1ff1c234 100644 --- a/src/type/never/never.ts +++ b/src/type/never/never.ts @@ -40,5 +40,5 @@ export function Never(options: SchemaOptions = {}): TNever { ...options, [Kind]: 'Never', not: {}, - } as unknown as TNever + } as never } diff --git a/src/type/not/not.ts b/src/type/not/not.ts index d01ac2b3a..7315fec86 100644 --- a/src/type/not/not.ts +++ b/src/type/not/not.ts @@ -42,5 +42,5 @@ export function Not(schema: T, options?: SchemaOptions): TNot ...options, [Kind]: 'Not', not: CloneType(schema), - } as unknown as TNot + } as never } diff --git a/src/type/null/null.ts b/src/type/null/null.ts index 09142d299..40690048a 100644 --- a/src/type/null/null.ts +++ b/src/type/null/null.ts @@ -40,5 +40,5 @@ export function Null(options: SchemaOptions = {}): TNull { ...options, [Kind]: 'Null', type: 'null', - } as unknown as TNull + } as never } diff --git a/src/type/number/number.ts b/src/type/number/number.ts index 573eb403a..db7392cd0 100644 --- a/src/type/number/number.ts +++ b/src/type/number/number.ts @@ -47,5 +47,5 @@ export function Number(options: NumberOptions = {}): TNumber { ...options, [Kind]: 'Number', type: 'number', - } as unknown as TNumber + } as never } diff --git a/src/type/object/object.ts b/src/type/object/object.ts index 0cda73dfe..a9d4ce358 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -37,7 +37,7 @@ import { Kind } from '../symbols/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsOptional, IsSchema } from '../guard/type' +import { IsOptional, IsSchema } from '../guard/kind' // ------------------------------------------------------------------ // ObjectStatic @@ -89,10 +89,13 @@ function _Object(properties: T, options: ObjectOptions = const optionalKeys = propertyKeys.filter((key) => IsOptional(properties[key])) const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) const clonedAdditionalProperties = IsSchema(options.additionalProperties) ? { additionalProperties: CloneType(options.additionalProperties) } : {} - const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: CloneType(properties[key]) }), {} as TProperties) - return (requiredKeys.length > 0 - ? { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys } - : { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) as unknown as TObject + const clonedProperties = {} as Record + for (const key of propertyKeys) clonedProperties[key] = CloneType(properties[key]) + return ( + requiredKeys.length > 0 + ? { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys } + : { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties } + ) as never } /** `[Json]` Creates an Object type */ diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index 041101a9e..3ca1822dc 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -48,7 +48,7 @@ function FromPropertyKey< >(T: T, K: K, options: SchemaOptions): TFromPropertyKey { return { [K]: Omit(T, [K], options) - } as TFromPropertyKey + } as never } // ------------------------------------------------------------------ // FromPropertyKeys @@ -70,7 +70,7 @@ function FromPropertyKeys< >(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(T, LK, options) } - }, {} as TProperties) as TFromPropertyKeys + }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey @@ -87,7 +87,7 @@ function FromMappedKey< T extends TSchema, K extends TMappedKey, >(T: T, K: K, options: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as TFromMappedKey + return FromPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ // OmitFromMappedKey @@ -106,6 +106,6 @@ export function OmitFromMappedKey< K extends TMappedKey, P extends TProperties = TFromMappedKey >(T: T, K: K, options: SchemaOptions): TMappedResult

      { - const P = FromMappedKey(T, K, options) as unknown as P - return MappedResult(P) + const P = FromMappedKey(T, K, options) + return MappedResult(P) as never } diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index 01327fe21..5f54e4802 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -47,9 +47,9 @@ function FromProperties< P extends TProperties, K extends PropertyKey[], >(P: P, K: [...K], options: SchemaOptions): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Omit(P[K2], K, options) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Omit(P[K2], K, options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -66,7 +66,7 @@ function FromMappedResult< R extends TMappedResult, K extends PropertyKey[] >(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as TFromMappedResult + return FromProperties(R.properties, K, options) as never } // ------------------------------------------------------------------ // TOmitFromMappedResult @@ -85,6 +85,6 @@ export function OmitFromMappedResult< K extends PropertyKey[], P extends TProperties = TFromMappedResult >(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, K, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, K, options) + return MappedResult(P) as never } diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 5c5ae73b2..19751fdc4 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -43,7 +43,7 @@ import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-ma // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } from '../guard/type' +import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ // FromIntersect @@ -56,7 +56,7 @@ type TFromIntersect(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as TFromIntersect + return T.map((T) => OmitResolve(T, K)) as never } // ------------------------------------------------------------------ // FromUnion @@ -69,23 +69,21 @@ type TFromUnion(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as TFromUnion + return T.map((T) => OmitResolve(T, K)) as never } // ------------------------------------------------------------------ // FromProperty // ------------------------------------------------------------------ // prettier-ignore -function FromProperty, K extends PropertyKey>(T: T, K: K) { +function FromProperty, K extends PropertyKey>(T: T, K: K): TProperties { const { [K]: _, ...R } = T - return R as TProperties + return R } // prettier-ignore type TFromProperties> = Evaluate> // prettier-ignore function FromProperties(T: T, K: K) { - return K.reduce((T, K2) => { - return FromProperty(T, K2) - }, T as TProperties) + return K.reduce((T, K2) => FromProperty(T, K2), T as TProperties) } // ------------------------------------------------------------------ // OmitResolve @@ -97,7 +95,7 @@ function OmitResolve(T: T, K: [...K] IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : IsObject(T) ? Object(FromProperties(T.properties, K)) : Object({}) - ) as TOmit + ) as never } // prettier-ignore export type TOmit = diff --git a/src/type/optional/optional-from-mapped-result.ts b/src/type/optional/optional-from-mapped-result.ts index 813349da4..b5bb6966a 100644 --- a/src/type/optional/optional-from-mapped-result.ts +++ b/src/type/optional/optional-from-mapped-result.ts @@ -45,9 +45,9 @@ function FromProperties< P extends TProperties, F extends boolean, >(P: P, F: F): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Optional(P[K2], F) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Optional(P[K2], F) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -64,7 +64,7 @@ function FromMappedResult< R extends TMappedResult, F extends boolean, >(R: R, F: F): TFromMappedResult { - return FromProperties(R.properties, F) as TFromMappedResult + return FromProperties(R.properties, F) as never } // ------------------------------------------------------------------ // OptionalFromMappedResult diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts index 70703f101..0ecd8cad4 100644 --- a/src/type/optional/optional.ts +++ b/src/type/optional/optional.ts @@ -34,7 +34,7 @@ import { Discard } from '../discard/index' import type { TMappedResult } from '../mapped/index' import { OptionalFromMappedResult, type TOptionalFromMappedResult } from './optional-from-mapped-result' -import { IsMappedResult } from '../guard/type' +import { IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ // RemoveOptional // ------------------------------------------------------------------ diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts index 00f7338ac..466c608ff 100644 --- a/src/type/partial/partial-from-mapped-result.ts +++ b/src/type/partial/partial-from-mapped-result.ts @@ -45,9 +45,9 @@ type TFromProperties< function FromProperties< P extends TProperties >(K: P, options: SchemaOptions): TFromProperties

      { - return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { - return {...Acc, [K2]: Partial(K[K2], options) } - }, {}) as TFromProperties

      + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = Partial(K[K2], options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -62,7 +62,7 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult >(R: R, options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, options) as TFromMappedResult + return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ // TPartialFromMappedResult @@ -79,6 +79,6 @@ export function PartialFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult >(R: R, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, options) + return MappedResult(P) as never } diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 961d36f25..27a37b0b7 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -45,7 +45,7 @@ import { PartialFromMappedResult, type TPartialFromMappedResult } from './partia // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' +import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ @@ -57,7 +57,7 @@ type TFromRest = ( ) // prettier-ignore function FromRest(T: [...T]): TFromRest { - return T.map(L => PartialResolve(L)) as TFromRest + return T.map(L => PartialResolve(L)) as never } // ------------------------------------------------------------------ // FromProperties @@ -71,10 +71,10 @@ type TFromProperties = Evaluate<{ TOptional }> // prettier-ignore -function FromProperties(T: T) { - return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K) => { - return { ...Acc, [K]: Optional(T[K]) } - }, {} as TProperties) +function FromProperties(T: T): TFromProperties { + const Acc = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Optional(T[K]) + return Acc as never } // ------------------------------------------------------------------ // PartialResolve @@ -86,7 +86,7 @@ function PartialResolve(T: T): TPartial { IsUnion(T) ? Union(FromRest(T.anyOf)) : IsObject(T) ? Object(FromProperties(T.properties)) : Object({}) - ) as TPartial + ) as never } // ------------------------------------------------------------------ // TPartial diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index 963ba8557..7bd87f09e 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -48,7 +48,7 @@ function FromPropertyKey< >(T: T, K: K, options: SchemaOptions): TFromPropertyKey { return { [K]: Pick(T, [K], options) - } as TFromPropertyKey + } as never } // ------------------------------------------------------------------ // FromPropertyKeys @@ -70,7 +70,7 @@ function FromPropertyKeys< >(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(T, LK, options) } - }, {} as TProperties) as TFromPropertyKeys + }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey @@ -87,7 +87,7 @@ function FromMappedKey< T extends TSchema, K extends TMappedKey, >(T: T, K: K, options: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as TFromMappedKey + return FromPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ // PickFromMappedKey @@ -106,6 +106,6 @@ export function PickFromMappedKey< K extends TMappedKey, P extends TProperties = TFromMappedKey >(T: T, K: K, options: SchemaOptions): TMappedResult

      { - const P = FromMappedKey(T, K, options) as unknown as P - return MappedResult(P) + const P = FromMappedKey(T, K, options) + return MappedResult(P) as never } diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index bfa997a64..43b006457 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -47,9 +47,9 @@ function FromProperties< P extends TProperties, K extends PropertyKey[], >(P: P, K: [...K], options: SchemaOptions): TFromProperties { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Pick(P[K2], K, options) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Pick(P[K2], K, options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -66,7 +66,7 @@ function FromMappedResult< R extends TMappedResult, K extends PropertyKey[] >(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as TFromMappedResult + return FromProperties(R.properties, K, options) as never } // ------------------------------------------------------------------ // PickFromMappedResult @@ -85,6 +85,6 @@ export function PickFromMappedResult< K extends PropertyKey[], P extends TProperties = TFromMappedResult >(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, K, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, K, options) + return MappedResult(P) as never } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index c35b401ce..60a7b2227 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -31,7 +31,7 @@ import type { TupleToUnion, Evaluate } from '../helpers/index' import { type TRecursive } from '../recursive/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' -import { type TObject, type TProperties, Object } from '../object/index' +import { type TObject, type TProperties, type TPropertyKey, Object } from '../object/index' import type { TMappedKey, TMappedResult } from '../mapped/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Discard } from '../discard/index' @@ -43,7 +43,7 @@ import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-ma // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } from '../guard/type' +import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } from '../guard/kind' // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ @@ -74,9 +74,9 @@ function FromUnion(T: T, K: K) { type FromProperties> = Evaluate> // prettier-ignore function FromProperties(T: T, K: K) { - return K.reduce((Acc, K) => { - return K in T ? { ...Acc, [K]: T[K as keyof T] } : Acc - }, {}) + const Acc = {} as TProperties + for(const K2 of K) if(K2 in T) Acc[K2 as TPropertyKey] = T[K2 as keyof T] + return Acc as never } // ------------------------------------------------------------------ // PickResolve @@ -88,7 +88,7 @@ function PickResolve(T: T, K: [...K] IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : IsObject(T) ? Object(FromProperties(T.properties, K)) : Object({}) - ) as TPick + ) as never } // prettier-ignore export type TPick = diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts index bebb00171..bd9504194 100644 --- a/src/type/promise/promise.ts +++ b/src/type/promise/promise.ts @@ -44,5 +44,5 @@ export function Promise(item: T, options: SchemaOptions = {}) [Kind]: 'Promise', type: 'Promise', item: CloneType(item), - } as unknown as TPromise + } as never } diff --git a/src/type/readonly-optional/readonly-optional.ts b/src/type/readonly-optional/readonly-optional.ts index d32212777..5520539e8 100644 --- a/src/type/readonly-optional/readonly-optional.ts +++ b/src/type/readonly-optional/readonly-optional.ts @@ -34,5 +34,5 @@ export type TReadonlyOptional = TOptional & TReadonly /** `[Json]` Creates a Readonly and Optional property */ export function ReadonlyOptional(schema: T): TReadonly> { - return Readonly(Optional(schema)) as TReadonly> + return Readonly(Optional(schema)) as never } diff --git a/src/type/readonly/readonly-from-mapped-result.ts b/src/type/readonly/readonly-from-mapped-result.ts index 29857793b..cd0865aaa 100644 --- a/src/type/readonly/readonly-from-mapped-result.ts +++ b/src/type/readonly/readonly-from-mapped-result.ts @@ -45,9 +45,9 @@ function FromProperties< P extends TProperties, F extends boolean, >(K: P, F: F): TFromProperties { - return globalThis.Object.getOwnPropertyNames(K).reduce((Acc, K2) => { - return {...Acc, [K2]: Readonly(K[K2], F) } - }, {}) as TFromProperties + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = Readonly(K[K2], F) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -64,7 +64,7 @@ function FromMappedResult< R extends TMappedResult, F extends boolean, >(R: R, F: F): TFromMappedResult { - return FromProperties(R.properties, F) as TFromMappedResult + return FromProperties(R.properties, F) as never } // ------------------------------------------------------------------ // ReadonlyFromMappedResult @@ -83,6 +83,6 @@ export function ReadonlyFromMappedResult< F extends boolean, P extends TProperties = TFromMappedResult >(R: R, F: F): TMappedResult

      { - const P = FromMappedResult(R, F) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, F) + return MappedResult(P) as never } diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts index 594170fff..546caddb7 100644 --- a/src/type/readonly/readonly.ts +++ b/src/type/readonly/readonly.ts @@ -34,7 +34,7 @@ import { Discard } from '../discard/index' import type { TMappedResult } from '../mapped/index' import { ReadonlyFromMappedResult, type TReadonlyFromMappedResult } from './readonly-from-mapped-result' -import { IsMappedResult } from '../guard/type' +import { IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ // RemoveReadonly // ------------------------------------------------------------------ diff --git a/src/type/record/record.ts b/src/type/record/record.ts index b4ff98460..e94902712 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -51,7 +51,7 @@ import { IsUndefined } from '../guard/value' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsInteger, IsLiteral, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/type' +import { IsInteger, IsLiteral, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // RecordCreateFromPattern // ------------------------------------------------------------------ @@ -62,15 +62,16 @@ function RecordCreateFromPattern(pattern: string, T: TSchema, options: ObjectOpt [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: CloneType(T) } - } as unknown as TRecord + } as never } // ------------------------------------------------------------------ // RecordCreateFromKeys // ------------------------------------------------------------------ // prettier-ignore function RecordCreateFromKeys(K: string[], T: TSchema, options: ObjectOptions): TObject { - const P = K.reduce((Acc, K) => ({ ...Acc, [K]: CloneType(T) }), {} as TProperties) - return Object(P, { ...options, [Hint]: 'Record' }) + const Acc = {} as TProperties + for(const K2 of K) Acc[K2] = CloneType(T) + return Object(Acc, { ...options, [Hint]: 'Record' }) } // ------------------------------------------------------------------ // FromTemplateLiteralKey (Fast Inference) @@ -91,7 +92,7 @@ function FromTemplateLiteralKey(K IsTemplateLiteralFinite(K) ? RecordCreateFromKeys(IndexPropertyKeys(K), T, options) : RecordCreateFromPattern(K.pattern, T, options) - ) as TFromTemplateLiteralKey + ) as never } // ------------------------------------------------------------------ // FromEnumKey (Special Case) @@ -118,7 +119,7 @@ type TFromUnionKey(K: K, T: T, options: ObjectOptions): TFromUnionKey { - return RecordCreateFromKeys(IndexPropertyKeys(Union(K)), T, options) as TFromUnionKey + return RecordCreateFromKeys(IndexPropertyKeys(Union(K)), T, options) as never } // ------------------------------------------------------------------ // FromLiteralKey @@ -129,7 +130,7 @@ type TFromLiteralKey = ( ) // prettier-ignore function FromLiteralKey(K: K, T: T, options: ObjectOptions): TFromLiteralKey { - return RecordCreateFromKeys([(K as string).toString()], T, options) as TFromLiteralKey + return RecordCreateFromKeys([(K as string).toString()], T, options) as never } // ------------------------------------------------------------------ // TFromRegExpKey @@ -140,7 +141,7 @@ type TFromRegExpKey<_ extends TRegExp, T extends TSchema> = ( ) // prettier-ignore function FromRegExpKey(K: K, T: T, options: ObjectOptions): TFromRegExpKey { - return RecordCreateFromPattern(K.source, T, options) as TFromRegExpKey + return RecordCreateFromPattern(K.source, T, options) as never } // ------------------------------------------------------------------ // FromStringKey @@ -152,7 +153,7 @@ type TFromStringKey<_ extends TString, T extends TSchema> = ( // prettier-ignore function FromStringKey(K: K, T: T, options: ObjectOptions): TFromStringKey { const pattern = IsUndefined(K.pattern) ? PatternStringExact : K.pattern - return RecordCreateFromPattern(pattern, T, options) as TFromStringKey + return RecordCreateFromPattern(pattern, T, options) as never } // ------------------------------------------------------------------ // FromIntegerKey @@ -163,7 +164,7 @@ type TFromIntegerKey<_ extends TSchema, T extends TSchema> = ( ) // prettier-ignore function FromIntegerKey(_: K, T: T, options: ObjectOptions): TFromIntegerKey { - return RecordCreateFromPattern(PatternNumberExact, T, options) as TFromIntegerKey + return RecordCreateFromPattern(PatternNumberExact, T, options) as never } // ------------------------------------------------------------------ // FromNumberKey @@ -174,7 +175,7 @@ type TFromNumberKey<_ extends TSchema, T extends TSchema> = ( ) // prettier-ignore function FromNumberKey(_: K, T: T, options: ObjectOptions): TFromNumberKey { - return RecordCreateFromPattern(PatternNumberExact, T, options) as TFromNumberKey + return RecordCreateFromPattern(PatternNumberExact, T, options) as never } // ------------------------------------------------------------------ // TRecord @@ -220,5 +221,5 @@ export function Record(K: K, T: T, options IsRegExp(K) ? FromRegExpKey(K, T, options) : IsString(K) ? FromStringKey(K, T, options) : Never(options) - ) as TRecordOrObject + ) as never } diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts index d1e14e264..46d28e98a 100644 --- a/src/type/recursive/recursive.ts +++ b/src/type/recursive/recursive.ts @@ -59,5 +59,5 @@ export function Recursive(callback: (thisType: TThis) => T, o const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) thisType.$id = options.$id // prettier-ignore - return CloneType({ ...options, [Hint]: 'Recursive', ...thisType }) as unknown as TRecursive + return CloneType({ ...options, [Hint]: 'Recursive', ...thisType }) as never } diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts index bcf5780bf..1e382ac3d 100644 --- a/src/type/required/required-from-mapped-result.ts +++ b/src/type/required/required-from-mapped-result.ts @@ -45,9 +45,9 @@ type TFromProperties< function FromProperties< P extends TProperties >(P: P, options: SchemaOptions): TFromProperties

      { - return globalThis.Object.getOwnPropertyNames(P).reduce((Acc, K2) => { - return {...Acc, [K2]: Required(P[K2], options) } - }, {}) as TFromProperties

      + const Acc = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Required(P[K2], options) + return Acc as never } // ------------------------------------------------------------------ // FromMappedResult @@ -62,7 +62,7 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult >(R: R, options: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, options) as TFromMappedResult + return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ // TRequiredFromMappedResult @@ -79,6 +79,6 @@ export function RequiredFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult >(R: R, options: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, options) as unknown as P - return MappedResult(P) + const P = FromMappedResult(R, options) + return MappedResult(P) as never } diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 56220759b..9bbad39c7 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -46,7 +46,7 @@ import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './requ // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/type' +import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ @@ -58,7 +58,7 @@ type TFromRest = ( ) // prettier-ignore function FromRest(T: [...T]) : TFromRest { - return T.map(L => RequiredResolve(L)) as TFromRest + return T.map(L => RequiredResolve(L)) as never } // ------------------------------------------------------------------ // FromProperties @@ -73,9 +73,9 @@ type TFromProperties = Evaluate<{ }> // prettier-ignore function FromProperties(T: T) { - return globalThis.Object.getOwnPropertyNames(T).reduce((Acc, K) => { - return { ...Acc, [K]: Discard(T[K], [OptionalKind]) as TSchema } - }, {} as TProperties) + const Acc = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Discard(T[K], [OptionalKind]) as TSchema + return Acc as never } // ------------------------------------------------------------------ // RequiredResolve @@ -88,7 +88,7 @@ function RequiredResolve(T: T): TRequired { IsUnion(T) ? Union(FromRest(T.anyOf)) : IsObject(T) ? Object(FromProperties(T.properties)) : Object({}) - ) as TRequired + ) as never } // ------------------------------------------------------------------ // TRequired diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts index e12d787b8..86593daac 100644 --- a/src/type/rest/rest.ts +++ b/src/type/rest/rest.ts @@ -35,7 +35,7 @@ import { CloneRest } from '../clone/type' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsIntersect, IsUnion, IsTuple } from '../guard/type' +import { IsIntersect, IsUnion, IsTuple } from '../guard/kind' // ------------------------------------------------------------------ // RestResolve // ------------------------------------------------------------------ @@ -48,11 +48,11 @@ type TRestResolve = // prettier-ignore function RestResolve(T: T) { return ( - IsIntersect(T) ? [...T.allOf] : - IsUnion(T) ? [...T.anyOf] : - IsTuple(T) ? [...(T.items ?? [])] : + IsIntersect(T) ? CloneRest(T.allOf) : + IsUnion(T) ? CloneRest(T.anyOf) : + IsTuple(T) ? CloneRest(T.items ?? []) : [] - ) as TRestResolve + ) as never } // ------------------------------------------------------------------ // TRest diff --git a/src/type/sets/set.ts b/src/type/sets/set.ts index 70b1e02ee..35b697975 100644 --- a/src/type/sets/set.ts +++ b/src/type/sets/set.ts @@ -40,7 +40,7 @@ export type TSetIncludes = ( /** Returns true if element right is in the set of left */ // prettier-ignore export function SetIncludes(T: [...T], S: S): TSetIncludes { - return T.includes(S) as TSetIncludes + return T.includes(S) as never } // ------------------------------------------------------------------ // SetIsSubset @@ -55,7 +55,7 @@ export type TSetIsSubset = ( ) /** Returns true if left is a subset of right */ export function SetIsSubset(T: [...T], S: [...S]): TSetIsSubset { - return T.every((L) => SetIncludes(S, L)) as TSetIsSubset + return T.every((L) => SetIncludes(S, L)) as never } // ------------------------------------------------------------------ // SetDistinct @@ -69,7 +69,7 @@ export type TSetDistinct(T: [...T]): TSetDistinct { - return [...new Set(T)] as TSetDistinct + return [...new Set(T)] as never } // ------------------------------------------------------------------ // SetIntersect @@ -84,7 +84,7 @@ export type TSetIntersect(T: [...T], S: [...S]): TSetIntersect { - return T.filter((L) => S.includes(L)) as TSetIntersect + return T.filter((L) => S.includes(L)) as never } // ------------------------------------------------------------------ // SetUnion @@ -111,7 +111,7 @@ export type TSetComplement(T: [...T], S: [...S]): TSetComplement { - return T.filter(L => !S.includes(L)) as TSetComplement + return T.filter(L => !S.includes(L)) as never } // ------------------------------------------------------------------ // SetIntersectMany @@ -146,7 +146,7 @@ export function SetIntersectMany(T: [...T]): TSetInte : T.length > 1 ? SetIntersectManyResolve(T.slice(1), T[0]) : [] - ) as TSetIntersectMany + ) as never } // ------------------------------------------------------------------ // SetUnionMany @@ -159,5 +159,7 @@ export type TSetUnionMany(T: [...T]): TSetUnionMany { - return T.reduce((Acc, L) => [...Acc, ...L], [] as PropertyKey[]) as TSetUnionMany + const Acc = [] as PropertyKey[] + for (const L of T) Acc.push(...L) + return Acc as never } diff --git a/src/type/string/string.ts b/src/type/string/string.ts index 77d1c10ee..97ee88fc2 100644 --- a/src/type/string/string.ts +++ b/src/type/string/string.ts @@ -82,5 +82,5 @@ export interface TString extends TSchema, StringOptions { /** `[Json]` Creates a String type */ export function String(options: StringOptions = {}): TString { - return { ...options, [Kind]: 'String', type: 'string' } as unknown as TString + return { ...options, [Kind]: 'String', type: 'string' } as never } diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts index 02712500c..f7e86697d 100644 --- a/src/type/symbol/symbol.ts +++ b/src/type/symbol/symbol.ts @@ -37,5 +37,5 @@ export interface TSymbol extends TSchema, SchemaOptions { } /** `[JavaScript]` Creates a Symbol type */ export function Symbol(options?: SchemaOptions): TSymbol { - return { ...options, [Kind]: 'Symbol', type: 'symbol' } as unknown as TSymbol + return { ...options, [Kind]: 'Symbol', type: 'symbol' } as never } diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts index 23a56790b..9bc857fd7 100644 --- a/src/type/template-literal/generate.ts +++ b/src/type/template-literal/generate.ts @@ -144,5 +144,5 @@ export function TemplateLiteralGenerate(schema: T): IsTemplateLiteralExpressionFinite(expression) ? [...TemplateLiteralExpressionGenerate(expression)] : [] - ) as TTemplateLiteralGenerate + ) as never } diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts index 099ee1b5f..03ab3c76f 100644 --- a/src/type/template-literal/pattern.ts +++ b/src/type/template-literal/pattern.ts @@ -35,17 +35,7 @@ import { TypeBoxError } from '../error/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -// prettier-ignore -import { - IsTemplateLiteral, - IsUnion, - IsNumber, - IsInteger, - IsBigInt, - IsString, - IsLiteral, - IsBoolean -} from '../guard/type' +import { IsTemplateLiteral, IsUnion, IsNumber, IsInteger, IsBigInt, IsString, IsLiteral, IsBoolean } from '../guard/kind' // ------------------------------------------------------------------ // TemplateLiteralPatternError diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts index 2003e68a2..8798b3083 100644 --- a/src/type/template-literal/template-literal.ts +++ b/src/type/template-literal/template-literal.ts @@ -94,7 +94,7 @@ export function TemplateLiteral(syntax: T, options?: SchemaOpt export function TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral /** `[Json]` Creates a TemplateLiteral type */ // prettier-ignore -export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { +export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}): any { const pattern = IsString(unresolved) ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) : TemplateLiteralPattern(unresolved as TTemplateLiteralKind[]) diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts index bdd575e5b..345726c61 100644 --- a/src/type/transform/transform.ts +++ b/src/type/transform/transform.ts @@ -34,7 +34,7 @@ import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsTransform } from '../guard/type' +import { IsTransform } from '../guard/kind' // ------------------------------------------------------------------ // TransformBuilders @@ -56,13 +56,13 @@ export class TransformEncodeBuilder, StaticDecode>>(encode: E, schema: TSchema) { const Codec = { Decode: this.decode, Encode: encode } - return { ...schema as TSchema, [TransformKind]: Codec } + return { ...schema, [TransformKind]: Codec } } public Encode, StaticDecode>>(encode: E): TTransform> { const schema = CloneType(this.schema) return ( IsTransform(schema) ? this.EncodeTransform(encode, schema): this.EncodeSchema(encode, schema) - ) as unknown as TTransform> + ) as never } } // ------------------------------------------------------------------ diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts index bd4f8400f..193b3c840 100644 --- a/src/type/tuple/tuple.ts +++ b/src/type/tuple/tuple.ts @@ -60,5 +60,5 @@ export function Tuple(items: [...T], options: SchemaOptions items.length > 0 ? { ...options, [Kind]: 'Tuple', type: 'array', items: CloneRest(items), additionalItems, minItems, maxItems } : { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems } - ) as unknown as TTuple + ) as never } diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts index fa0fb5501..55458346c 100644 --- a/src/type/uint8array/uint8array.ts +++ b/src/type/uint8array/uint8array.ts @@ -40,5 +40,5 @@ export interface TUint8Array extends TSchema, Uint8ArrayOptions { } /** `[JavaScript]` Creates a Uint8Array type */ export function Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return { ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' } as unknown as TUint8Array + return { ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' } as never } diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts index 69614443a..bbba2b4bf 100644 --- a/src/type/undefined/undefined.ts +++ b/src/type/undefined/undefined.ts @@ -36,5 +36,5 @@ export interface TUndefined extends TSchema { } /** `[JavaScript]` Creates a Undefined type */ export function Undefined(options: SchemaOptions = {}): TUndefined { - return { ...options, [Kind]: 'Undefined', type: 'undefined' } as unknown as TUndefined + return { ...options, [Kind]: 'Undefined', type: 'undefined' } as never } diff --git a/src/type/union/union-create.ts b/src/type/union/union-create.ts index 48df85256..86093fe58 100644 --- a/src/type/union/union-create.ts +++ b/src/type/union/union-create.ts @@ -32,5 +32,5 @@ import { TUnion } from './union-type' import { Kind } from '../symbols/index' export function UnionCreate(T: [...T], options: SchemaOptions): TUnion { - return { ...options, [Kind]: 'Union', anyOf: CloneRest(T) } as unknown as TUnion + return { ...options, [Kind]: 'Union', anyOf: CloneRest(T) } as never } diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts index 5bac5539f..afbf4714f 100644 --- a/src/type/union/union-evaluated.ts +++ b/src/type/union/union-evaluated.ts @@ -39,7 +39,7 @@ import { UnionCreate } from './union-create' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsOptional } from '../guard/type' +import { IsOptional } from '../guard/kind' // ------------------------------------------------------------------ // IsUnionOptional // ------------------------------------------------------------------ @@ -53,7 +53,7 @@ type TIsUnionOptional = ( ) // prettier-ignore function IsUnionOptional(T: T): TIsUnionOptional { - return T.some(L => IsOptional(L)) as TIsUnionOptional + return T.some(L => IsOptional(L)) as never } // ------------------------------------------------------------------ // RemoveOptionalFromRest @@ -68,7 +68,7 @@ type TRemoveOptionalFromRest = ) // prettier-ignore function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { - return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as TRemoveOptionalFromRest + return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as never } // ------------------------------------------------------------------ // RemoveOptionalFromType @@ -83,7 +83,7 @@ type TRemoveOptionalFromType = ( function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { return ( Discard(T, [OptionalKind]) - ) as TRemoveOptionalFromType + ) as never } // ------------------------------------------------------------------ // ResolveUnion @@ -100,7 +100,7 @@ function ResolveUnion(T: T, options: SchemaOptions): TResol IsUnionOptional(T) ? Optional(UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options)) : UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options) - ) as TResolveUnion + ) as never } // ------------------------------------------------------------------ // Union @@ -118,5 +118,5 @@ export function UnionEvaluated>(T: [ T.length === 0 ? Never(options) : T.length === 1 ? CloneType(T[0], options) : ResolveUnion(T, options) - ) as R + ) as never } diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts index 974e4ade1..28dce27be 100644 --- a/src/type/unknown/unknown.ts +++ b/src/type/unknown/unknown.ts @@ -38,5 +38,5 @@ export function Unknown(options: SchemaOptions = {}): TUnknown { return { ...options, [Kind]: 'Unknown', - } as unknown as TUnknown + } as never } diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts index 7faf6154a..f70ec80fc 100644 --- a/src/type/unsafe/unsafe.ts +++ b/src/type/unsafe/unsafe.ts @@ -41,5 +41,5 @@ export function Unsafe(options: UnsafeOptions = {}): TUnsafe { return { ...options, [Kind]: options[Kind] ?? 'Unsafe', - } as unknown as TUnsafe + } as never } diff --git a/src/type/void/void.ts b/src/type/void/void.ts index 4cf4ae501..c7f704d13 100644 --- a/src/type/void/void.ts +++ b/src/type/void/void.ts @@ -40,5 +40,5 @@ export function Void(options: SchemaOptions = {}): TVoid { ...options, [Kind]: 'Void', type: 'void', - } as unknown as TVoid + } as never } diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index e913b4a67..589d3170b 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -105,7 +105,7 @@ function FromObject(schema: TObject, references: TSchema[], value: unknown): any function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any { if (!IsObject(value)) return value const additionalProperties = schema.additionalProperties as TSchema - const propertyKeys = Object.keys(value) + const propertyKeys = Object.getOwnPropertyNames(value) const [propertyKey, propertySchema] = Object.entries(schema.patternProperties)[0] const propertyKeyTest = new RegExp(propertyKey) for (const key of propertyKeys) { diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index f9f64f4cd..3bec35c88 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -36,8 +36,14 @@ import { IsArray, IsDate, IsStandardObject, IsTypedArray, IsValueType } from '.. // Clonable // ------------------------------------------------------------------ function ObjectType(value: ObjectType): any { - const keys = [...Object.getOwnPropertyNames(value), ...Object.getOwnPropertySymbols(value)] - return keys.reduce((acc, key) => ({ ...acc, [key]: Clone(value[key]) }), {}) + const Acc = {} as Record + for (const key of Object.getOwnPropertyNames(value)) { + Acc[key] = Clone(value[key]) + } + for (const key of Object.getOwnPropertySymbols(value)) { + Acc[key] = Clone(value[key]) + } + return Acc } function ArrayType(value: ArrayType): any { return value.map((element: any) => Clone(element)) diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 2b2ef63ea..008105bff 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -242,12 +242,12 @@ function FromObject(schema: TObject, references: TSchema[]): any { return FromDefault(schema.default) } else { const required = new Set(schema.required) - return ( - FromDefault(schema.default) || - Object.entries(schema.properties).reduce((acc, [key, schema]) => { - return required.has(key) ? { ...acc, [key]: Visit(schema, references) } : { ...acc } - }, {}) - ) + const Acc = {} as Record + for (const [key, subschema] of Object.entries(schema.properties)) { + if (!required.has(key)) continue + Acc[key] = Visit(subschema, references) + } + return Acc } } function FromPromise(schema: TPromise, references: TSchema[]): any { @@ -263,9 +263,9 @@ function FromRecord(schema: TRecord, references: TSchema[]): any { return FromDefault(schema.default) } else if (!(keyPattern === PatternStringExact || keyPattern === PatternNumberExact)) { const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') - return propertyKeys.reduce((acc, key) => { - return { ...acc, [key]: Visit(valueSchema, references) } - }, {}) + const Acc = {} as Record + for (const key of propertyKeys) Acc[key] = Visit(valueSchema, references) + return Acc } else { return {} } diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index beb159d09..faa1f66ea 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -107,7 +107,7 @@ function NumberType(value: number) { } function ObjectType(value: Record) { FNV1A64(ByteMarker.Object) - for (const key of globalThis.Object.keys(value).sort()) { + for (const key of globalThis.Object.getOwnPropertyNames(value).sort()) { Visit(key) Visit(value[key]) } diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index 45791bc24..0ae01a6fe 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -47,8 +47,8 @@ function ObjectType(root: Mutable, path: string, current: unknown, next: Record< if (!IsStandardObject(current)) { ValuePointer.Set(root, path, Clone(next)) } else { - const currentKeys = Object.keys(current) - const nextKeys = Object.keys(next) + const currentKeys = Object.getOwnPropertyNames(current) + const nextKeys = Object.getOwnPropertyNames(next) for (const currentKey of currentKeys) { if (!nextKeys.includes(currentKey)) { delete current[currentKey] diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 02982d0fd..00edd6d98 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -99,21 +99,19 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) as string[] - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(Index(schema, [key]), references, `${path}/${key}`, value[key]) } - : value - }, value) + const knownProperties = { ...value } as Record + for(const key of knownKeys) if(key in knownProperties) { + knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const unevaluatedProperties = schema.unevaluatedProperties as TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const unknownProperties = { ...knownProperties } as Record + for(const key of unknownKeys) if(!knownKeys.includes(key)) { + unknownProperties[key] = Default(unevaluatedProperties, `${path}/${key}`, unknownProperties[key]) + } return Default(schema, path, unknownProperties) } function FromNot(schema: TNot, references: TSchema[], path: string, value: any) { @@ -123,21 +121,19 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any) function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { if (!IsStandardObject(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(schema.properties[key], references, `${path}/${key}`, value[key]) } - : value - }, value) + const knownProperties = { ...value } as Record + for(const key of knownKeys) if(key in knownProperties) { + knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key]) + } if (!IsSchema(schema.additionalProperties)) { return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const unknownProperties = { ...knownProperties } as Record + for(const key of unknownKeys) if(!knownKeys.includes(key)) { + unknownProperties[key] = Default(additionalProperties, `${path}/${key}`, unknownProperties[key]) + } return Default(schema, path, unknownProperties) } // prettier-ignore @@ -145,21 +141,19 @@ function FromRecord(schema: TRecord, references: TSchema[], path: string, value: if (!IsStandardObject(value)) return Default(schema, path, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, `${path}/${key}`, value[key]) } - : value - }, value) + const knownProperties = { ...value } as Record + for(const key of Object.getOwnPropertyNames(value)) if(knownKeys.test(key)) { + knownProperties[key] = Visit(schema.patternProperties[pattern], references, `${path}/${key}`, knownProperties[key]) + } if (!IsSchema(schema.additionalProperties)) { return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const unknownProperties = {...knownProperties} as Record + for(const key of unknownKeys) if(!knownKeys.test(key)) { + unknownProperties[key] = Default(additionalProperties, `${path}/${key}`, unknownProperties[key]) + } return Default(schema, path, unknownProperties) } // prettier-ignore diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 2a2b8f465..be1a507b9 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -100,21 +100,20 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string, const defaulted = Default(schema, path, value) if (!IsStandardObject(value) || IsValueType(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] - const knownProperties = knownKeys.reduce((value, key) => { - return key in defaulted - ? { ...value, [key]: Visit(Index(schema, [key]), references, `${path}/${key}`, value[key]) } - : value - }, defaulted) + const knownProperties = { ...defaulted } as Record + for(const key of knownKeys) if(key in knownProperties) { + knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const unevaluatedProperties = schema.unevaluatedProperties as TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const properties = { ...knownProperties } as Record + for(const key of unknownKeys) if(!knownKeys.includes(key)) { + properties[key] = Default(unevaluatedProperties, `${path}/${key}`, properties[key]) + } + return properties } // prettier-ignore function FromNot(schema: TNot, references: TSchema[], path: string, value: any) { @@ -125,21 +124,20 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value: const defaulted = Default(schema, path, value) if (!IsStandardObject(value)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] - const knownProperties = knownKeys.reduce((value, key) => { - return key in value - ? { ...value, [key]: Visit(schema.properties[key], references, `${path}/${key}`, value[key]) } - : value - }, defaulted) + const knownProperties = { ...defaulted } as Record + for(const key of knownKeys) if(key in value) { + knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key]) + } if (!IsSchema(schema.additionalProperties)) { return knownProperties } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const properties = { ...knownProperties } as Record + for(const key of unknownKeys) if(!knownKeys.includes(key)) { + properties[key] = Default(additionalProperties, `${path}/${key}`, properties[key]) + } + return properties } // prettier-ignore function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { @@ -147,21 +145,20 @@ function FromRecord(schema: TRecord, references: TSchema[], path: string, value: if (!IsStandardObject(value)) return defaulted const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, `${path}/${key}`, value[key]) } - : value - }, defaulted) + const knownProperties = {...defaulted } as Record + for(const key of Object.getOwnPropertyNames(value)) if(knownKeys.test(key)) { + knownProperties[key] = Visit(schema.patternProperties[pattern], references, `${path}/${key}`, knownProperties[key]) + } if (!IsSchema(schema.additionalProperties)) { return Default(schema, path, knownProperties) } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, `${path}/${key}`, value[key]) } - : value - }, knownProperties) + const properties = { ...knownProperties } as Record + for(const key of unknownKeys) if(!knownKeys.test(key)) { + properties[key] = Default(additionalProperties, `${path}/${key}`, properties[key]) + } + return properties } // prettier-ignore function FromRef(schema: TRef, references: TSchema[], path: string, value: any) { diff --git a/test/runtime/type/guard/index.ts b/test/runtime/type/guard/index.ts index 271b53f88..2a6bdd114 100644 --- a/test/runtime/type/guard/index.ts +++ b/test/runtime/type/guard/index.ts @@ -1,52 +1,3 @@ -import './any' -import './array' -import './async-iterator' -import './awaited' -import './bigint' -import './boolean' -import './capitalize' -import './composite' -import './const' -import './constructor' -import './date' -import './deref' -import './enum' -import './exclude' -import './extract' -import './function' -import './indexed' -import './integer' -import './intersect' -import './iterator' -import './keyof' -import './kind' -import './literal' -import './lowercase' -import './mapped' -import './not' -import './null' -import './number' -import './object' -import './omit' -import './partial' -import './pick' -import './promise' -import './record' -import './recursive' -import './ref' -import './regexp' -import './required' -import './rest' -import './string' -import './symbol' -import './template-literal' -import './this' -import './tuple' -import './uint8array' -import './uncapitalize' -import './undefined' -import './union' -import './unknown' -import './unsafe' -import './uppercase' -import './void' +import './type/index' +import './kind/index' +import './value/index' diff --git a/test/runtime/type/guard/kind/any.ts b/test/runtime/type/guard/kind/any.ts new file mode 100644 index 000000000..87a860a0a --- /dev/null +++ b/test/runtime/type/guard/kind/any.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TAny', () => { + it('Should guard for TAny', () => { + const R = KindGuard.IsAny(Type.Any()) + Assert.IsTrue(R) + }) + it('Should not guard for TAny', () => { + const R = KindGuard.IsAny(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/array.ts b/test/runtime/type/guard/kind/array.ts new file mode 100644 index 000000000..6f7ce4454 --- /dev/null +++ b/test/runtime/type/guard/kind/array.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TArray', () => { + it('Should guard for TArray', () => { + const R = KindGuard.IsArray(Type.Array(Type.Number())) + Assert.IsTrue(R) + }) + it('Should not guard for TArray', () => { + const R = KindGuard.IsArray(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/async-iterator.ts b/test/runtime/type/guard/kind/async-iterator.ts new file mode 100644 index 000000000..3e6d16e8d --- /dev/null +++ b/test/runtime/type/guard/kind/async-iterator.ts @@ -0,0 +1,16 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TAsyncIterator', () => { + it('Should guard for TAsyncIterator', () => { + const T = Type.AsyncIterator(Type.Any()) + const R = KindGuard.IsAsyncIterator(T) + Assert.IsTrue(R) + }) + it('Should not guard for TAsyncIterator', () => { + const T = null + const R = KindGuard.IsAsyncIterator(T) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/awaited.ts b/test/runtime/type/guard/kind/awaited.ts new file mode 100644 index 000000000..021c7f62d --- /dev/null +++ b/test/runtime/type/guard/kind/awaited.ts @@ -0,0 +1,41 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/Awaited', () => { + it('Should guard for Awaited 1', () => { + const T = Type.Awaited(Type.String()) + const R = KindGuard.IsString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 2', () => { + const T = Type.Awaited(Type.Promise(Type.String())) + const R = KindGuard.IsString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 3', () => { + const T = Type.Awaited(Type.Awaited(Type.Promise(Type.String()))) + const R = KindGuard.IsString(T) + Assert.IsTrue(R) + }) + it('Should guard for Awaited 4', () => { + const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String()))])) + Assert.IsTrue(KindGuard.IsString(T)) + }) + it('Should guard for Awaited 5', () => { + const T = Type.Awaited(Type.Union([Type.Promise(Type.Promise(Type.String())), Type.Number()])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsString(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(T.anyOf[1])) + }) + it('Should guard for Awaited 6', () => { + const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String()))])) + Assert.IsTrue(KindGuard.IsString(T)) + }) + it('Should guard for Awaited 7', () => { + const T = Type.Awaited(Type.Intersect([Type.Promise(Type.Promise(Type.String())), Type.Number()])) + Assert.IsTrue(KindGuard.IsIntersect(T)) + Assert.IsTrue(KindGuard.IsString(T.allOf[0])) + Assert.IsTrue(KindGuard.IsNumber(T.allOf[1])) + }) +}) diff --git a/test/runtime/type/guard/kind/bigint.ts b/test/runtime/type/guard/kind/bigint.ts new file mode 100644 index 000000000..33e89b2be --- /dev/null +++ b/test/runtime/type/guard/kind/bigint.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TBigInt', () => { + it('Should guard for TBigInt', () => { + const R = KindGuard.IsBigInt(Type.BigInt()) + Assert.IsTrue(R) + }) + it('Should not guard for TBigInt', () => { + const R = KindGuard.IsBigInt(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/boolean.ts b/test/runtime/type/guard/kind/boolean.ts new file mode 100644 index 000000000..abdfd262d --- /dev/null +++ b/test/runtime/type/guard/kind/boolean.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TBoolean', () => { + it('Should guard for TBoolean', () => { + const R = KindGuard.IsBoolean(Type.Boolean()) + Assert.IsTrue(R) + }) + it('Should not guard for TBoolean', () => { + const R = KindGuard.IsBoolean(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/capitalize.ts b/test/runtime/type/guard/kind/capitalize.ts new file mode 100644 index 000000000..373937702 --- /dev/null +++ b/test/runtime/type/guard/kind/capitalize.ts @@ -0,0 +1,33 @@ +import { KindGuard, Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/Capitalize', () => { + it('Should guard for Capitalize 1', () => { + const T = Type.Capitalize(Type.Literal('hello'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'Hello') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Capitalize 2', () => { + const T = Type.Capitalize(Type.Literal('hello')) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'Hello') + }) + it('Should guard for Capitalize 3', () => { + const T = Type.Capitalize(Type.Union([Type.Literal('hello'), Type.Literal('world')])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'Hello') + Assert.IsEqual(T.anyOf[1].const, 'World') + }) + it('Should guard for Capitalize 4', () => { + const T = Type.Capitalize(Type.TemplateLiteral('hello${0|1}')) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') + }) + it('Should guard for Capitalize 5', () => { + const T = Type.Capitalize(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(Hello0|Hello1)$') + }) +}) diff --git a/test/runtime/type/guard/kind/composite.ts b/test/runtime/type/guard/kind/composite.ts new file mode 100644 index 000000000..bd709667e --- /dev/null +++ b/test/runtime/type/guard/kind/composite.ts @@ -0,0 +1,165 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TComposite', () => { + it('Should guard for distinct properties', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + }) + it('Should guard for overlapping properties', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.Number() })]) + Assert.IsTrue(KindGuard.IsIntersect(T.properties.x)) + // @ts-ignore + Assert.IsTrue(KindGuard.IsNumber(T.properties.x.allOf[0])) + // @ts-ignore + Assert.IsTrue(KindGuard.IsNumber(T.properties.x.allOf[1])) + }) + it('Should not produce optional property if all properties are not optional', () => { + const T = Type.Composite([Type.Object({ x: Type.Optional(Type.Number()) }), Type.Object({ x: Type.Number() })]) + Assert.IsFalse(KindGuard.IsOptional(T.properties.x)) + }) + // Note for: https://github.com/sinclairzx81/typebox/issues/419 + // Determining if a composite property is optional requires a deep check for all properties gathered during a indexed access + // call. Currently, there isn't a trivial way to perform this check without running into possibly infinite instantiation issues. + // The optional check is only specific to overlapping properties. Singular properties will continue to work as expected. The + // rule is "if all composite properties for a key are optional, then the composite property is optional". Defer this test and + // document as minor breaking change. + // + it('Should produce optional property if all composited properties are optional', () => { + // prettier-ignore + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Optional(Type.Number()) }) + ]) + Assert.IsTrue(KindGuard.IsOptional(T.properties.x)) + Assert.IsEqual(T.required, undefined) + }) + // prettier-ignore + it('Should produce required property if some composited properties are not optional', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + Type.Object({ x: Type.Number() }) + ]) + Assert.IsFalse(KindGuard.IsOptional(T.properties.x)) + Assert.IsTrue(T.required!.includes('x')) + }) + // prettier-ignore + it('Should preserve single optional property', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.Number()) }), + ]) + Assert.IsTrue(KindGuard.IsOptional(T.properties.x)) + Assert.IsEqual(T.required, undefined) + }) + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + // prettier-ignore + it('Should composite Intersect 1', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + })) + }) + // prettier-ignore + it('Should composite Intersect 2', () => { + const T = Type.Composite([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }), + ]), + Type.Intersect([ + Type.Object({ x: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.Intersect([Type.Number(), Type.Number()]), Type.Number()]) + })) + }) + // prettier-ignore + it('Should composite Intersect 3', () => { + const T = Type.Composite([ + Type.Number(), + Type.Boolean() + ]) + Assert.IsEqual(T, Type.Object({})) + }) + // prettier-ignore + it('Should composite Intersect 4', () => { + const T = Type.Composite([ + Type.Number(), + Type.Boolean(), + Type.Object({ x: Type.String() }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.String() + })) + }) + // prettier-ignore + it('Should composite Intersect 5', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.String()) }), + Type.Object({ x: Type.String() }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.String(), Type.String()]) + })) + }) + // prettier-ignore + it('Should composite Intersect 6', () => { + const T = Type.Composite([ + Type.Object({ x: Type.Optional(Type.String()) }), + Type.Object({ x: Type.Optional(Type.String()) }) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Optional(Type.Intersect([Type.String(), Type.String()])) + })) + }) + // ---------------------------------------------------------------- + // Union + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/789 + // prettier-ignore + it('Should composite Union 1 (non-overlapping)', () => { + const T = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }), + ]), + Type.Union([ + Type.Object({ z: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + z: Type.Number() + })) + }) + // https://github.com/sinclairzx81/typebox/issues/789 + // prettier-ignore + it('Should composite Union 2 (overlapping)', () => { + const T = Type.Composite([ + Type.Union([ + Type.Object({ x: Type.Number() }), + Type.Object({ x: Type.Number() }), + ]), + Type.Union([ + Type.Object({ x: Type.Number() }), + ]) + ]) + Assert.IsEqual(T, Type.Object({ + x: Type.Intersect([Type.Union([Type.Number(), Type.Number()]), Type.Number()]) + })) + }) +}) diff --git a/test/runtime/type/guard/kind/const.ts b/test/runtime/type/guard/kind/const.ts new file mode 100644 index 000000000..cb2115c92 --- /dev/null +++ b/test/runtime/type/guard/kind/const.ts @@ -0,0 +1,124 @@ +import { KindGuard, ValueGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TConstT', () => { + // ---------------------------------------------------------------- + // Identity Types + // ---------------------------------------------------------------- + it('Should guard for TConst 1', () => { + const T = Type.Const(undefined) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsUndefined(T)) + }) + it('Should guard for TConst 2', () => { + const T = Type.Const(null) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsNull(T)) + }) + it('Should guard for TConst 3', () => { + const T = Type.Const(Symbol()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsSymbol(T)) + }) + it('Should guard for TConst 4', () => { + const T = Type.Const(1 as const) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 1) + }) + it('Should guard for TConst 5', () => { + const T = Type.Const('hello' as const) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should guard for TConst 6', () => { + const T = Type.Const(true as const) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, true) + }) + // ---------------------------------------------------------------- + // Complex Types + // ---------------------------------------------------------------- + it('Should guard for TConst 7', () => { + const T = Type.Const(100n as const) + Assert.IsFalse(KindGuard.IsReadonly(T)) + // TS disparity because TLiteral does not support Bigint + Assert.IsTrue(KindGuard.IsBigInt(T)) + }) + it('Should guard for TConst 8', () => { + const T = Type.Const(new Date()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsDate(T)) + }) + it('Should guard for TConst 9', () => { + const T = Type.Const(new Uint8Array()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsUint8Array(T)) + }) + it('Should guard for TConst 10', () => { + const T = Type.Const(function () {}) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsFunction(T)) + Assert.IsTrue(T.parameters.length === 0) + Assert.IsTrue(KindGuard.IsUnknown(T.returns)) + }) + it('Should guard for TConst 11', () => { + const T = Type.Const(new (class {})()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsObject(T)) + // Object types that are neither Date or Uint8Array evaluate as empty objects + Assert.IsEqual(T.properties, {}) + }) + it('Should guard for TConst 12', () => { + const T = Type.Const((function* (): any {})()) + const R = ValueGuard.IsIterator((function* (): any {})()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsAny(T)) + }) + it('Should guard for TConst 13', () => { + const T = Type.Const((async function* (): any {})()) + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsAny(T)) + }) + it('Should guard for TConst 14', () => { + const T = Type.Const({ + x: 1, + y: { + z: 2, + }, + } as const) + // root + Assert.IsFalse(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsObject(T)) + // x + Assert.IsTrue(KindGuard.IsLiteral(T.properties.x)) + Assert.IsEqual(T.properties.x.const, 1) + // y + Assert.IsTrue(KindGuard.IsReadonly(T.properties.y)) + Assert.IsTrue(KindGuard.IsObject(T.properties.y)) + // y.z + Assert.IsTrue(KindGuard.IsReadonly(T.properties.y.properties.z)) + Assert.IsTrue(KindGuard.IsLiteral(T.properties.y.properties.z)) + Assert.IsEqual(T.properties.y.properties.z.const, 2) + }) + it('Should guard for TConst 15', () => { + const T = Type.Const([1, 2, 3] as const) + // root (arrays are always readonly as root) + Assert.IsTrue(KindGuard.IsReadonly(T)) + Assert.IsTrue(KindGuard.IsTuple(T)) + Assert.IsTrue(T.items?.length === 3) + // 0 + Assert.IsFalse(KindGuard.IsReadonly(T.items![0])) + Assert.IsTrue(KindGuard.IsLiteral(T.items![0])) + // 1 + Assert.IsFalse(KindGuard.IsReadonly(T.items![1])) + Assert.IsTrue(KindGuard.IsLiteral(T.items![1])) + // 2 + Assert.IsFalse(KindGuard.IsReadonly(T.items![2])) + Assert.IsTrue(KindGuard.IsLiteral(T.items![2])) + }) +}) diff --git a/test/runtime/type/guard/kind/constructor.ts b/test/runtime/type/guard/kind/constructor.ts new file mode 100644 index 000000000..d2146aa7c --- /dev/null +++ b/test/runtime/type/guard/kind/constructor.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TConstructor', () => { + it('Should guard for TConstructor', () => { + const R = KindGuard.IsConstructor(Type.Constructor([], Type.Number())) + Assert.IsTrue(R) + }) + it('Should not guard for TConstructor', () => { + const R = KindGuard.IsConstructor(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/date.ts b/test/runtime/type/guard/kind/date.ts new file mode 100644 index 000000000..5e2d252a7 --- /dev/null +++ b/test/runtime/type/guard/kind/date.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TDate', () => { + it('Should guard for TDate', () => { + const R = KindGuard.IsDate(Type.Date()) + Assert.IsTrue(R) + }) + it('Should not guard for TDate', () => { + const R = KindGuard.IsDate(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/deref.ts b/test/runtime/type/guard/kind/deref.ts new file mode 100644 index 000000000..98e6811d9 --- /dev/null +++ b/test/runtime/type/guard/kind/deref.ts @@ -0,0 +1,110 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TDeref', () => { + it('Should should deref 1', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const D = Type.Deref(R, [T]) + Assert.IsTrue(KindGuard.IsString(D)) + Assert.IsFalse('$id' in D) + }) + it('Should should deref 2', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Object({ + x: R, + y: R, + }) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsObject(D)) + Assert.IsTrue(KindGuard.IsString(D.properties.x)) + Assert.IsTrue(KindGuard.IsString(D.properties.y)) + Assert.IsFalse('$id' in D.properties.x) + Assert.IsFalse('$id' in D.properties.y) + }) + it('Should should deref 3', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Array(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsArray(D)) + Assert.IsTrue(KindGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 4', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.AsyncIterator(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsAsyncIterator(D)) + Assert.IsTrue(KindGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 5', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Iterator(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsIterator(D)) + Assert.IsTrue(KindGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 6', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Function([R], R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsFunction(D)) + Assert.IsTrue(KindGuard.IsString(D.parameters[0])) + Assert.IsTrue(KindGuard.IsString(D.returns)) + Assert.IsFalse('$id' in D.parameters[0]) + Assert.IsFalse('$id' in D.returns) + }) + it('Should should deref 7', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Constructor([R], R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsConstructor(D)) + Assert.IsTrue(KindGuard.IsString(D.parameters[0])) + Assert.IsTrue(KindGuard.IsString(D.returns)) + Assert.IsFalse('$id' in D.parameters[0]) + Assert.IsFalse('$id' in D.returns) + }) + it('Should should deref 8', () => { + const T = Type.String({ $id: 'T' }) + const R = Type.Ref(T) + const O = Type.Promise(R) + const D = Type.Deref(O, [T]) + Assert.IsTrue(KindGuard.IsPromise(D)) + Assert.IsTrue(KindGuard.IsString(D.item)) + Assert.IsFalse('$id' in D.item) + }) + it('Should should deref 9', () => { + const T = Type.String({ $id: 'T' }) + const R1 = Type.Ref(T, { $id: 'R1' }) + const R2 = Type.Ref(R1, { $id: 'R2' }) + const R3 = Type.Ref(R2, { $id: 'R3' }) + const R4 = Type.Ref(R3, { $id: 'R4' }) + const R5 = Type.Ref(R4, { $id: 'R5' }) + const R6 = Type.Ref(R5, { $id: 'R6' }) + const O = Type.Array(R6) + const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T]) + Assert.IsTrue(KindGuard.IsArray(D)) + Assert.IsTrue(KindGuard.IsString(D.items)) + Assert.IsFalse('$id' in D.items) + }) + it('Should should deref 10', () => { + const T = Type.String({ $id: 'T' }) + const R1 = Type.Ref(T, { $id: 'R1' }) + const R2 = Type.Ref(R1, { $id: 'R2' }) + const R3 = Type.Ref(R2, { $id: 'R3' }) + const R4 = Type.Ref(R3, { $id: 'R4' }) + const R5 = Type.Ref(R4, { $id: 'R5' }) + const R6 = Type.Ref(R5, { $id: 'R6' }) + const O = Type.Array(R6) + Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T + }) +}) diff --git a/test/runtime/type/guard/kind/enum.ts b/test/runtime/type/guard/kind/enum.ts new file mode 100644 index 000000000..b47631cfa --- /dev/null +++ b/test/runtime/type/guard/kind/enum.ts @@ -0,0 +1,143 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TEnum', () => { + // ---------------------------------------------------------------- + // Options + // ---------------------------------------------------------------- + it('Should guard for Options 1', () => { + const T = Type.Enum({ x: 1 }, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 2', () => { + enum E { + x, + } + const T = Type.Enum(E, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 3', () => { + enum E {} + const T = Type.Enum(E, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + it('Should guard for Options 4', () => { + const T = Type.Enum({}, { extra: 'hello', $id: 'T' }) + Assert.IsEqual(T.extra, 'hello') + Assert.IsEqual(T.$id, 'T') + }) + // ---------------------------------------------------------------- + // Empty + // ---------------------------------------------------------------- + it('Should guard for Empty 1', () => { + const T = Type.Enum({}) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should guard for Empty 2', () => { + enum E {} + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + + // ---------------------------------------------------------------- + // Enum + // ---------------------------------------------------------------- + it('Should guard for TEnum Enum 0', () => { + enum E {} + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should guard for TEnum Enum 1', () => { + enum E { + A, + } + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, E.A) + }) + it('Should guard for TEnum Enum 2', () => { + enum E { + A = 1, + B = 2, + C = 3, + } + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) + Assert.IsEqual(T.anyOf[2].const, E.C) + }) + it('Should guard for TEnum Enum 3', () => { + enum E { + A = 'X', + B = 'Y', + C = 'Z', + } + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) + Assert.IsEqual(T.anyOf[2].const, E.C) + }) + it('Should guard for TEnum Enum 4', () => { + enum E { + A = 'X', + B = 'Y', + C = 'X', + } + const T = Type.Enum(E) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, E.A) + Assert.IsEqual(T.anyOf[1].const, E.B) + Assert.IsEqual(T.anyOf.length, 2) + }) + // ---------------------------------------------------------------- + // Object Literal + // ---------------------------------------------------------------- + it('Should guard for TEnum Object Literal 0', () => { + const T = Type.Enum({}) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should guard for TEnum Object Literal 1', () => { + const T = Type.Enum({ A: 1 }) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 1) + }) + it('Should guard for TEnum Object Literal 2', () => { + const T = Type.Enum({ + A: 1, + B: 2, + C: 3, + }) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 1) + Assert.IsEqual(T.anyOf[1].const, 2) + Assert.IsEqual(T.anyOf[2].const, 3) + }) + it('Should guard for TEnum Object Literal 3', () => { + const T = Type.Enum({ + A: 'X', + B: 'Y', + C: 'Z', + }) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf[2].const, 'Z') + }) + it('Should guard for TEnum Object Literal 4', () => { + const T = Type.Enum({ + A: 'X', + B: 'Y', + C: 'X', + }) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'X') + Assert.IsEqual(T.anyOf[1].const, 'Y') + Assert.IsEqual(T.anyOf.length, 2) + }) +}) diff --git a/test/runtime/type/guard/kind/exclude.ts b/test/runtime/type/guard/kind/exclude.ts new file mode 100644 index 000000000..d3a19af16 --- /dev/null +++ b/test/runtime/type/guard/kind/exclude.ts @@ -0,0 +1,96 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TExclude', () => { + it('Should exclude string from number', () => { + const T = Type.Exclude(Type.String(), Type.Number()) + Assert.IsTrue(KindGuard.IsString(T)) + }) + it('Should exclude string from string', () => { + const T = Type.Exclude(Type.String(), Type.String()) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should exclude string | number | boolean from string', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsBoolean(T.anyOf[1])) + }) + it('Should exclude string | number | boolean from string | boolean', () => { + const T = Type.Exclude(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) + Assert.IsTrue(KindGuard.IsNumber(T)) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | TemplateLiteral + // ------------------------------------------------------------------------ + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C'].includes(T.const)) + }) + it('Should exclude TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | Union 1 + // ------------------------------------------------------------------------ + it('Should exclude TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Exclude(A, B) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should exclude TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C'].includes(T.const)) + }) + it('Should exclude TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) + }) + // ------------------------------------------------------------------------ + // Union | TemplateLiteral 1 + // ------------------------------------------------------------------------ + it('Should exclude Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should exclude Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C'].includes(T.const)) + }) + it('Should exclude Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Exclude(A, B) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['C', 'B'].includes(T.anyOf[1].const)) + }) + it('Should exclude with options', () => { + const A = Type.String() + const B = Type.String() + const T = Type.Exclude(A, B, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) +}) diff --git a/test/runtime/type/guard/kind/extract.ts b/test/runtime/type/guard/kind/extract.ts new file mode 100644 index 000000000..54f406380 --- /dev/null +++ b/test/runtime/type/guard/kind/extract.ts @@ -0,0 +1,102 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TExtract', () => { + it('Should extract string from number', () => { + const T = Type.Extract(Type.String(), Type.Number()) + Assert.IsTrue(KindGuard.IsNever(T)) + }) + it('Should extract string from string', () => { + const T = Type.Extract(Type.String(), Type.String()) + Assert.IsTrue(KindGuard.IsString(T)) + }) + it('Should extract string | number | boolean from string', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.String()) + Assert.IsTrue(KindGuard.IsString(T)) + }) + it('Should extract string | number | boolean from string | boolean', () => { + const T = Type.Extract(Type.Union([Type.String(), Type.Number(), Type.Boolean()]), Type.Union([Type.String(), Type.Boolean()])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsString(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsBoolean(T.anyOf[1])) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | TemplateLiteral + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) + }) + it('Should extract TemplateLiteral | TemplateLiteral 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A'].includes(T.const)) + }) + // ------------------------------------------------------------------------ + // TemplateLiteral | Union 1 + // ------------------------------------------------------------------------ + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) + }) + it('Should extract TemplateLiteral | Union 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const B = Type.Union([Type.Literal('A')]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A'].includes(T.const)) + }) + // ------------------------------------------------------------------------ + // Union | TemplateLiteral 1 + // ------------------------------------------------------------------------ + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[1].const)) + Assert.IsTrue(['A', 'B', 'C'].includes(T.anyOf[2].const)) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[0].const)) + Assert.IsTrue(['A', 'B'].includes(T.anyOf[1].const)) + }) + it('Should extract Union | TemplateLiteral 1', () => { + const A = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C')]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('A')])]) + const T = Type.Extract(A, B) + Assert.IsTrue(['A'].includes(T.const)) + }) + it('Should extract with options', () => { + const A = Type.String() + const B = Type.String() + const T = Type.Extract(A, B, { foo: 'bar' }) + Assert.IsEqual(T.foo, 'bar') + }) +}) diff --git a/test/runtime/type/guard/kind/function.ts b/test/runtime/type/guard/kind/function.ts new file mode 100644 index 000000000..cf868c71d --- /dev/null +++ b/test/runtime/type/guard/kind/function.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TFunction', () => { + it('Should guard for TFunction', () => { + const R = KindGuard.IsFunction(Type.Function([], Type.Number())) + Assert.IsTrue(R) + }) + it('Should not guard for TFunction', () => { + const R = KindGuard.IsFunction(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/index.ts b/test/runtime/type/guard/kind/index.ts new file mode 100644 index 000000000..271b53f88 --- /dev/null +++ b/test/runtime/type/guard/kind/index.ts @@ -0,0 +1,52 @@ +import './any' +import './array' +import './async-iterator' +import './awaited' +import './bigint' +import './boolean' +import './capitalize' +import './composite' +import './const' +import './constructor' +import './date' +import './deref' +import './enum' +import './exclude' +import './extract' +import './function' +import './indexed' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './lowercase' +import './mapped' +import './not' +import './null' +import './number' +import './object' +import './omit' +import './partial' +import './pick' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './required' +import './rest' +import './string' +import './symbol' +import './template-literal' +import './this' +import './tuple' +import './uint8array' +import './uncapitalize' +import './undefined' +import './union' +import './unknown' +import './unsafe' +import './uppercase' +import './void' diff --git a/test/runtime/type/guard/kind/indexed.ts b/test/runtime/type/guard/kind/indexed.ts new file mode 100644 index 000000000..4fc18ad3f --- /dev/null +++ b/test/runtime/type/guard/kind/indexed.ts @@ -0,0 +1,327 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TIndex', () => { + it('Should Index 1', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 2', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, ['x', 'y']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1])) + }) + it('Should Index 3', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const I = Type.Index(T, Type.KeyOf(T)) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1])) + }) + it('Should Index 4', () => { + const T = Type.Object({ + ab: Type.Number(), + ac: Type.String(), + }) + const I = Type.Index(T, Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])])) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1])) + }) + it('Should Index 5', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.String() })]) + const I = Type.Index(T, ['x', 'y']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1])) + }) + it('Should Index 6', () => { + const T = Type.Union([Type.Object({ x: Type.Number() }), Type.Object({ x: Type.String() })]) + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1])) + }) + it('Should Index 7', () => { + const T = Type.Array(Type.Null()) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(KindGuard.IsNull(I)) + }) + it('Should Index 6', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0]) + Assert.IsTrue(KindGuard.IsLiteralString(I)) + Assert.IsEqual(I.const, 'hello') + }) + it('Should Index 8', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [1]) + Assert.IsTrue(KindGuard.IsLiteralString(I)) + Assert.IsEqual(I.const, 'world') + }) + it('Should Index 9', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0, 1]) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'hello') + Assert.IsEqual(I.anyOf[1].const, 'world') + }) + it('Should Index 10', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [1, 0]) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'world') + Assert.IsEqual(I.anyOf[1].const, 'hello') + }) + it('Should Index 11', () => { + const T = Type.Tuple([Type.Literal('hello'), Type.Literal('world')]) + const I = Type.Index(T, [0, 0, 0, 1]) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsEqual(I.anyOf[0].const, 'hello') + Assert.IsEqual(I.anyOf[1].const, 'hello') + Assert.IsEqual(I.anyOf[2].const, 'hello') + Assert.IsEqual(I.anyOf[3].const, 'world') + }) + it('Should Index 12', () => { + const T = Type.Tuple([Type.String(), Type.Boolean()]) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsBoolean(I.anyOf[1])) + }) + it('Should Index 13', () => { + const T = Type.Tuple([Type.String()]) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(KindGuard.IsString(I)) + }) + it('Should Index 14', () => { + const T = Type.Tuple([]) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(KindGuard.IsNever(I)) + }) + it('Should Index 15', () => { + const T = Type.Object({ + 0: Type.Number(), + }) + const I = Type.Index(T, Type.Literal(0)) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 16', () => { + const T = Type.Object({ + 0: Type.Number(), + }) + const I = Type.Index(T, Type.Literal('0')) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 17', () => { + const T = Type.Object({ + '0': Type.Number(), + }) + const I = Type.Index(T, Type.Literal(0)) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 18', () => { + const T = Type.Object({ + '0': Type.Number(), + }) + const I = Type.Index(T, Type.Literal('0')) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 19', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(2)])) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsBoolean(I.anyOf[1])) + }) + it('Should Index 20', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.BigInt()) + Assert.IsTrue(KindGuard.IsNever(I)) + }) + it('Should Index 21', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Boolean(), + }) + const I = Type.Index(T, Type.Object({})) + Assert.IsTrue(KindGuard.IsNever(I)) + }) + it('Should Index 22', () => { + const A = Type.Object({ x: Type.Literal('A') }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsIntersect(I)) + Assert.IsTrue(KindGuard.IsLiteral(I.allOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(I.allOf[1])) + Assert.IsTrue(KindGuard.IsLiteral(I.allOf[2])) + Assert.IsTrue(KindGuard.IsLiteral(I.allOf[3])) + }) + it('Should Index 23', () => { + const A = Type.Object({ x: Type.Literal('A') }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Union([A, B, C, D]) + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsLiteral(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(I.anyOf[1])) + Assert.IsTrue(KindGuard.IsLiteral(I.anyOf[2])) + Assert.IsTrue(KindGuard.IsLiteral(I.anyOf[3])) + }) + it('Should Index 24', () => { + const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) + const B = Type.Object({ x: Type.Literal('B') }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x', 'y']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsIntersect(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + }) + it('Should Index 25', () => { + const A = Type.Object({ x: Type.Literal('A'), y: Type.Number() }) + const B = Type.Object({ x: Type.Literal('B'), y: Type.String() }) + const C = Type.Object({ x: Type.Literal('C') }) + const D = Type.Object({ x: Type.Literal('D') }) + const T = Type.Intersect([A, B, C, D]) + const I = Type.Index(T, ['x', 'y']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsIntersect(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsIntersect(I.anyOf[1])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1].allOf[0])) + Assert.IsTrue(KindGuard.IsString(I.anyOf[1].allOf[1])) + }) + it('Should Index 26', () => { + const T = Type.Recursive((This) => + Type.Object({ + x: Type.String(), + y: Type.Number(), + z: This, + }), + ) + const I = Type.Index(T, ['x', 'y', 'z']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + Assert.IsTrue(KindGuard.IsThis(I.anyOf[2])) + }) + it('Should Index 27', () => { + const T = Type.Object({ + 0: Type.String(), + 1: Type.Number(), + }) + const I = Type.Index(T, [0, 1]) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + }) + it('Should Index 28', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, [0, '1']) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + }) + it('Should Index 29', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal('1')])) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + }) + it('Should Index 30', () => { + const T = Type.Object({ + 0: Type.String(), + '1': Type.Number(), + }) + const I = Type.Index(T, Type.Union([Type.Literal(0), Type.Literal(1)])) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + // Note: Expect TNever for anyOf[1] but permit for TNumber due to IndexedAccess + // Resolve() which currently cannot differentiate between string and numeric keys + // on the object. This may be resolvable in later revisions, but test for this + // fall-through to ensure case is documented. For review. + }) + // -------------------------------------------------------- + // Modifier Optional Indexing + // -------------------------------------------------------- + it('Should Index 31', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsOptional(I)) + Assert.IsTrue(KindGuard.IsString(I)) + }) + it('Should Index 32', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['y']) + Assert.IsFalse(KindGuard.IsOptional(I)) + Assert.IsTrue(KindGuard.IsNumber(I)) + }) + it('Should Index 33', () => { + const T = Type.Object({ + x: Type.Optional(Type.String()), + y: Type.Number(), + }) + const I = Type.Index(T, ['x', 'y']) + Assert.IsTrue(KindGuard.IsOptional(I)) + Assert.IsTrue(KindGuard.IsUnion(I)) + Assert.IsTrue(KindGuard.IsString(I.anyOf[0])) + Assert.IsTrue(KindGuard.IsNumber(I.anyOf[1])) + }) + it('Should Index 34', () => { + const T = Type.String() + const I = Type.Index(T, ['x']) + Assert.IsTrue(KindGuard.IsNever(I)) + }) + it('Should Index 35', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(KindGuard.IsString(I)) + }) + it('Should Index 36', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, ['[number]']) + Assert.IsTrue(KindGuard.IsString(I)) + }) +}) diff --git a/test/runtime/type/guard/kind/integer.ts b/test/runtime/type/guard/kind/integer.ts new file mode 100644 index 000000000..a8fce9a9d --- /dev/null +++ b/test/runtime/type/guard/kind/integer.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TInteger', () => { + it('Should guard for TInteger', () => { + const R = KindGuard.IsInteger(Type.Integer()) + Assert.IsTrue(R) + }) + it('Should not guard for TInteger', () => { + const R = KindGuard.IsInteger(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/intersect.ts b/test/runtime/type/guard/kind/intersect.ts new file mode 100644 index 000000000..f1678103f --- /dev/null +++ b/test/runtime/type/guard/kind/intersect.ts @@ -0,0 +1,39 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TIntersect', () => { + it('Should guard for TIntersect', () => { + const R = KindGuard.IsIntersect( + Type.Intersect([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.IsTrue(R) + }) + it('Should not guard for TIntersect', () => { + const R = KindGuard.IsIntersect( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.IsFalse(R) + }) + it('Should throw for intersected transform types', () => { + const N = Type.Transform(Type.Number()) + .Decode((value) => value) + .Encode((value) => value) + + Assert.Throws(() => Type.Intersect([N, N])) + }) +}) diff --git a/test/runtime/type/guard/kind/iterator.ts b/test/runtime/type/guard/kind/iterator.ts new file mode 100644 index 000000000..5e650b41c --- /dev/null +++ b/test/runtime/type/guard/kind/iterator.ts @@ -0,0 +1,16 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TIterator', () => { + it('Should guard for TIterator', () => { + const T = Type.Iterator(Type.Any()) + const R = KindGuard.IsIterator(T) + Assert.IsTrue(R) + }) + it('Should not guard for TIterator', () => { + const T = null + const R = KindGuard.IsIterator(T) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/keyof.ts b/test/runtime/type/guard/kind/keyof.ts new file mode 100644 index 000000000..5d2867b3a --- /dev/null +++ b/test/runtime/type/guard/kind/keyof.ts @@ -0,0 +1,76 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TKeyOf', () => { + it('Should KeyOf 1', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsUnion(K)) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[1])) + }) + it('Should KeyOf 2', () => { + const T = Type.Recursive((Self) => + Type.Object({ + x: Type.Number(), + y: Type.Array(Self), + }), + ) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsUnion(K)) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[1])) + }) + it('Should KeyOf 3', () => { + const T = Type.Intersect([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsUnion(K)) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(K.anyOf[1])) + }) + it('Should KeyOf 4', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsNever(K)) + }) + it('Should KeyOf 5', () => { + const T = Type.Null() + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsNever(K)) + }) + it('Should KeyOf 6', () => { + const T = Type.Array(Type.Number()) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsNumber(K)) + }) + it('Should KeyOf 7', () => { + const T = Type.Tuple([]) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsNever(K)) + }) + it('Should KeyOf 8', () => { + const T = Type.Tuple([Type.Number(), Type.Null()]) + const K = Type.KeyOf(T) + Assert.IsTrue(KindGuard.IsUnion(K)) + Assert.IsEqual(K.anyOf[0].const, '0') + Assert.IsEqual(K.anyOf[1].const, '1') + }) +}) diff --git a/test/runtime/type/guard/kind/kind.ts b/test/runtime/type/guard/kind/kind.ts new file mode 100644 index 000000000..6ae707a6d --- /dev/null +++ b/test/runtime/type/guard/kind/kind.ts @@ -0,0 +1,13 @@ +import { KindGuard, Kind } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TKind', () => { + it('Should guard 1', () => { + const T = { [Kind]: 'Kind' } + Assert.IsTrue(KindGuard.IsKind(T)) + }) + it('Should guard 2', () => { + const T = {} + Assert.IsFalse(KindGuard.IsKind(T)) + }) +}) diff --git a/test/runtime/type/guard/kind/literal.ts b/test/runtime/type/guard/kind/literal.ts new file mode 100644 index 000000000..413bc94b0 --- /dev/null +++ b/test/runtime/type/guard/kind/literal.ts @@ -0,0 +1,18 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TLiteral', () => { + it('Should guard for TLiteral of String', () => { + const R = KindGuard.IsLiteral(Type.Literal('hello')) + Assert.IsTrue(R) + }) + it('Should guard for TLiteral of Number', () => { + const R = KindGuard.IsLiteral(Type.Literal(42)) + Assert.IsTrue(R) + }) + it('Should guard for TLiteral of Boolean', () => { + const R = KindGuard.IsLiteral(Type.Literal(true)) + Assert.IsTrue(R) + }) +}) diff --git a/test/runtime/type/guard/kind/lowercase.ts b/test/runtime/type/guard/kind/lowercase.ts new file mode 100644 index 000000000..d2ef0ccee --- /dev/null +++ b/test/runtime/type/guard/kind/lowercase.ts @@ -0,0 +1,33 @@ +import { KindGuard, Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/Lowercase', () => { + it('Should guard for Lowercase 1', () => { + const T = Type.Lowercase(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Lowercase 2', () => { + const T = Type.Lowercase(Type.Literal('HELLO')) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should guard for Lowercase 3', () => { + const T = Type.Lowercase(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hello') + Assert.IsEqual(T.anyOf[1].const, 'world') + }) + it('Should guard for Lowercase 4', () => { + const T = Type.Lowercase(Type.TemplateLiteral('HELLO${0|1}')) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello0|hello1)$') + }) + it('Should guard for Lowercase 5', () => { + const T = Type.Lowercase(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hello0|hello1)$') + }) +}) diff --git a/test/runtime/type/guard/mapped.ts b/test/runtime/type/guard/kind/mapped.ts similarity index 99% rename from test/runtime/type/guard/mapped.ts rename to test/runtime/type/guard/kind/mapped.ts index 1ab3daf9f..c4673e126 100644 --- a/test/runtime/type/guard/mapped.ts +++ b/test/runtime/type/guard/kind/mapped.ts @@ -1,8 +1,8 @@ import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' // prettier-ignore -describe('type/guard/Mapped', () => { +describe('guard/kind/Mapped', () => { it('Should guard mapped 1', () => { const T = Type.Mapped(Type.Union([ Type.Literal('x'), diff --git a/test/runtime/type/guard/kind/not.ts b/test/runtime/type/guard/kind/not.ts new file mode 100644 index 000000000..a30c2e6dd --- /dev/null +++ b/test/runtime/type/guard/kind/not.ts @@ -0,0 +1,10 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TNot', () => { + it('Should guard for TNot', () => { + const R = KindGuard.IsNot(Type.Not(Type.String())) + Assert.IsTrue(R) + }) +}) diff --git a/test/runtime/type/guard/kind/null.ts b/test/runtime/type/guard/kind/null.ts new file mode 100644 index 000000000..81e29b0f3 --- /dev/null +++ b/test/runtime/type/guard/kind/null.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TNull', () => { + it('Should guard for TNull', () => { + const R = KindGuard.IsNull(Type.Null()) + Assert.IsTrue(R) + }) + it('Should not guard for TNull', () => { + const R = KindGuard.IsNull(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/number.ts b/test/runtime/type/guard/kind/number.ts new file mode 100644 index 000000000..00a992ba1 --- /dev/null +++ b/test/runtime/type/guard/kind/number.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TNumber', () => { + it('Should guard for TNumber', () => { + const R = KindGuard.IsNumber(Type.Number()) + Assert.IsTrue(R) + }) + it('Should not guard for TNumber', () => { + const R = KindGuard.IsNumber(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/object.ts b/test/runtime/type/guard/kind/object.ts new file mode 100644 index 000000000..f0c45189d --- /dev/null +++ b/test/runtime/type/guard/kind/object.ts @@ -0,0 +1,19 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TObject', () => { + it('Should guard for TObject', () => { + const R = KindGuard.IsObject( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + Assert.IsTrue(R) + }) + it('Should not guard for TObject', () => { + const R = KindGuard.IsObject(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/omit.ts b/test/runtime/type/guard/kind/omit.ts new file mode 100644 index 000000000..1aba2a82d --- /dev/null +++ b/test/runtime/type/guard/kind/omit.ts @@ -0,0 +1,129 @@ +import { KindGuard, Type, Kind, TransformKind } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TOmit', () => { + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/384 + // ------------------------------------------------------------------------- + it('Should support TUnsafe omit properties with no Kind', () => { + const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1 }), y: Type.Number() }), ['x']) + Assert.IsEqual(T.required, ['y']) + }) + it('Should support TUnsafe omit properties with unregistered Kind', () => { + const T = Type.Omit(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownOmitType' }), y: Type.Number() }), ['x']) + Assert.IsEqual(T.required, ['y']) + }) + // ------------------------------------------------------------------------- + // Standard Tests + // ------------------------------------------------------------------------- + it('Should Omit 1', () => { + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ['x'], + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + Assert.IsEqual(T.required, ['y']) + }) + it('Should Omit 2', () => { + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Optional(Type.Number()), + }), + ['x'], + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + Assert.IsEqual(T.required, undefined) + }) + it('Should Omit 3', () => { + const L = Type.Literal('x') + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + Assert.IsEqual(T.required, ['y']) + }) + it('Should Omit 4', () => { + const L = Type.Literal('x') + const T = Type.Omit(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) + Assert.IsEqual(KindGuard.IsNumber(T.allOf[1].properties.y), true) + // @ts-ignore + Assert.IsEqual(T.allOf[1].properties.x, undefined) + }) + it('Should Omit 5', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + // @ts-ignore + Assert.IsEqual(T.properties.x, undefined) + // @ts-ignore + Assert.IsEqual(T.properties.y, undefined) + // @ts-ignore + Assert.IsEqual(T.required, undefined) + }) + it('Should Omit 6', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) + const T = Type.Omit( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + // @ts-ignore + Assert.IsEqual(T.properties.x, undefined) + // @ts-ignore + Assert.IsEqual(T.properties.y, undefined) + // @ts-ignore + Assert.IsEqual(T.required, undefined) + }) + it('Should Omit 7', () => { + const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) + const T = Type.Omit( + Type.Object({ + ab: Type.Number(), + ac: Type.Number(), + ad: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.ad)) + Assert.IsEqual(T.required, ['ad']) + }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x']) + Assert.IsFalse('$id' in T) + }) + it('Should discard transform', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Omit(S, ['x']) + Assert.IsFalse(TransformKind in R) + }) +}) diff --git a/test/runtime/type/guard/kind/partial.ts b/test/runtime/type/guard/kind/partial.ts new file mode 100644 index 000000000..6520da9d8 --- /dev/null +++ b/test/runtime/type/guard/kind/partial.ts @@ -0,0 +1,67 @@ +import { KindGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TPartial', () => { + it('Should produce a valid TSchema', () => { + const T = Type.Partial(Type.Object({ x: Type.Number() })) + Assert.IsTrue(KindGuard.IsSchema(T)) + }) + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/364 + // ------------------------------------------------------------------------- + it('Should support TUnsafe partial properties with no Kind', () => { + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ x: 1 }) })) + Assert.IsEqual(T.required, undefined) + }) + it('Should support TUnsafe partial properties with unknown Kind', () => { + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'UnknownPartialType', x: 1 }) })) + Assert.IsEqual(T.required, undefined) + }) + it('Should support TUnsafe partial properties with known Kind', () => { + TypeRegistry.Set('KnownPartialType', () => true) + const T = Type.Partial(Type.Object({ x: Type.Unsafe({ [Kind]: 'KnownPartialType', x: 1 }) })) + Assert.IsEqual(T.required, undefined) + }) + it('Should support applying partial to intersect', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const I = Type.Intersect([A, B]) + const T = Type.Partial(I) + Assert.IsEqual(T.allOf.length, 2) + Assert.IsEqual(T.allOf[0].required, undefined) + Assert.IsEqual(T.allOf[1].required, undefined) + }) + it('Should support applying partial to union', () => { + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.Number() }) + const I = Type.Union([A, B]) + const T = Type.Partial(I) + Assert.IsEqual(T.anyOf.length, 2) + Assert.IsEqual(T.anyOf[0].required, undefined) + Assert.IsEqual(T.anyOf[1].required, undefined) + }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A) + Assert.IsFalse('$id' in T) + }) + it('Should discard transform', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Partial(S) + Assert.IsFalse(TransformKind in R) + }) +}) diff --git a/test/runtime/type/guard/kind/pick.ts b/test/runtime/type/guard/kind/pick.ts new file mode 100644 index 000000000..25bc99e06 --- /dev/null +++ b/test/runtime/type/guard/kind/pick.ts @@ -0,0 +1,131 @@ +import { KindGuard, Type, Kind, TransformKind } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TPick', () => { + // ------------------------------------------------------------------------- + // case: https://github.com/sinclairzx81/typebox/issues/384 + // ------------------------------------------------------------------------- + it('Should support TUnsafe omit properties with no Kind', () => { + const T = Type.Pick( + Type.Object({ + x: Type.Unsafe({ x: 1 }), + y: Type.Number(), + }), + ['x'], + ) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support TUnsafe omit properties with unregistered Kind', () => { + const T = Type.Pick(Type.Object({ x: Type.Unsafe({ x: 1, [Kind]: 'UnknownPickType' }), y: Type.Number() }), ['x']) + Assert.IsEqual(T.required, ['x']) + }) + // ------------------------------------------------------------------------- + // Standard Tests + // ------------------------------------------------------------------------- + it('Should Pick 1', () => { + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ['x'], + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsEqual(T.required, ['x']) + }) + it('Should Pick 2', () => { + const T = Type.Pick( + Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number(), + }), + ['x'], + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsEqual(T.required, undefined) + }) + it('Should Pick 3', () => { + const L = Type.Literal('x') + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsEqual(T.required, ['x']) + }) + it('Should Pick 4', () => { + const L = Type.Literal('x') + const T = Type.Pick(Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]), L) + + Assert.IsTrue(KindGuard.IsNumber(T.allOf[0].properties.x)) + // @ts-ignore + Assert.IsEqual(T.allOf[1].properties.y, undefined) + }) + it('Should Pick 5', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y')]) + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + Assert.IsEqual(T.required, ['x', 'y']) + }) + it('Should Pick 6', () => { + const L = Type.Union([Type.Literal('x'), Type.Literal('y'), Type.Literal('z')]) + const T = Type.Pick( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.x)) + Assert.IsTrue(KindGuard.IsNumber(T.properties.y)) + Assert.IsEqual(T.required, ['x', 'y']) + }) + it('Should Pick 7', () => { + const L = Type.TemplateLiteral([Type.Literal('a'), Type.Union([Type.Literal('b'), Type.Literal('c')])]) + const T = Type.Pick( + Type.Object({ + ab: Type.Number(), + ac: Type.Number(), + ad: Type.Number(), + }), + L, + ) + Assert.IsTrue(KindGuard.IsNumber(T.properties.ab)) + Assert.IsTrue(KindGuard.IsNumber(T.properties.ac)) + Assert.IsEqual(T.required, ['ab', 'ac']) + }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x']) + Assert.IsFalse('$id' in T) + }) + it('Should discard transform', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Pick(S, ['x']) + Assert.IsFalse(TransformKind in R) + }) +}) diff --git a/test/runtime/type/guard/kind/promise.ts b/test/runtime/type/guard/kind/promise.ts new file mode 100644 index 000000000..f59c94625 --- /dev/null +++ b/test/runtime/type/guard/kind/promise.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TPromise', () => { + it('Should guard for TPromise', () => { + const R = KindGuard.IsPromise(Type.Promise(Type.Number())) + Assert.IsTrue(R) + }) + it('Should not guard for TPromise', () => { + const R = KindGuard.IsPromise(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/record.ts b/test/runtime/type/guard/kind/record.ts similarity index 98% rename from test/runtime/type/guard/record.ts rename to test/runtime/type/guard/kind/record.ts index f0c11b6f9..12365ee3b 100644 --- a/test/runtime/type/guard/record.ts +++ b/test/runtime/type/guard/kind/record.ts @@ -1,8 +1,8 @@ import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRecord', () => { +describe('guard/kind/TRecord', () => { // ------------------------------------------------------------- // Overloads // ------------------------------------------------------------- diff --git a/test/runtime/type/guard/kind/recursive.ts b/test/runtime/type/guard/kind/recursive.ts new file mode 100644 index 000000000..81f8e41b9 --- /dev/null +++ b/test/runtime/type/guard/kind/recursive.ts @@ -0,0 +1,16 @@ +import { KindGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TRecursive', () => { + it('Should guard 1', () => { + const T = Type.Recursive((This) => Type.Object({ nodes: This })) + Assert.IsTrue(KindGuard.IsRecursive(T)) + Assert.IsTrue(KindGuard.IsObject(T)) + }) + it('Should guard 2', () => { + const T = Type.Recursive((This) => Type.Tuple([This])) + Assert.IsTrue(KindGuard.IsRecursive(T)) + Assert.IsTrue(KindGuard.IsTuple(T)) + }) +}) diff --git a/test/runtime/type/guard/kind/ref.ts b/test/runtime/type/guard/kind/ref.ts new file mode 100644 index 000000000..441d412cd --- /dev/null +++ b/test/runtime/type/guard/kind/ref.ts @@ -0,0 +1,15 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TRef', () => { + it('Should guard for TRef', () => { + const T = Type.Number({ $id: 'T' }) + const R = KindGuard.IsRef(Type.Ref(T)) + Assert.IsTrue(R) + }) + it('Should not guard for TRef', () => { + const R = KindGuard.IsRef(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/regexp.ts b/test/runtime/type/guard/kind/regexp.ts new file mode 100644 index 000000000..a4f38143a --- /dev/null +++ b/test/runtime/type/guard/kind/regexp.ts @@ -0,0 +1,18 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TRegExp', () => { + it('Should guard for TRegExp 1', () => { + const T = Type.RegExp(/foo/, { $id: 'T' }) + Assert.IsTrue(KindGuard.IsSchema(T)) + }) + it('Should guard for TRegExp 1', () => { + const T = Type.RegExp(/foo/, { $id: 'T' }) + Assert.IsTrue(KindGuard.IsRegExp(T)) + }) + it('Should guard for TRegExp 2', () => { + const T = Type.RegExp('foo', { $id: 'T' }) + Assert.IsTrue(KindGuard.IsRegExp(T)) + }) +}) diff --git a/test/runtime/type/guard/kind/required.ts b/test/runtime/type/guard/kind/required.ts new file mode 100644 index 000000000..e795a05e1 --- /dev/null +++ b/test/runtime/type/guard/kind/required.ts @@ -0,0 +1,64 @@ +import { KindGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TRequired', () => { + it('Should produce a valid TSchema', () => { + const T = Type.Required(Type.Object({ x: Type.Number() })) + Assert.IsTrue(KindGuard.IsSchema(T)) + }) + it('Should support TUnsafe required properties with no Kind', () => { + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support TUnsafe required properties with unknown Kind', () => { + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ [Kind]: 'UnknownRequiredType', x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support TUnsafe required properties with known Kind', () => { + TypeRegistry.Set('KnownRequiredType', () => true) + const T = Type.Required(Type.Object({ x: Type.Optional(Type.Unsafe({ [Kind]: 'KnownRequiredType', x: 1 })) })) + Assert.IsEqual(T.required, ['x']) + }) + it('Should support applying required to intersect', () => { + const A = Type.Object({ x: Type.Optional(Type.Number()) }) + const B = Type.Object({ y: Type.Optional(Type.Number()) }) + const I = Type.Intersect([A, B]) + const T = Type.Required(I) + Assert.IsEqual(T.allOf.length, 2) + Assert.IsEqual(T.allOf[0].required, ['x']) + Assert.IsEqual(T.allOf[1].required, ['y']) + }) + it('Should support applying required to union', () => { + const A = Type.Object({ x: Type.Optional(Type.Number()) }) + const B = Type.Object({ y: Type.Optional(Type.Number()) }) + const I = Type.Union([A, B]) + const T = Type.Required(I) + Assert.IsEqual(T.anyOf.length, 2) + Assert.IsEqual(T.anyOf[0].required, ['x']) + Assert.IsEqual(T.anyOf[1].required, ['y']) + }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A) + Assert.IsFalse('$id' in T) + }) + it('Should discard transform', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Required(S) + Assert.IsFalse(TransformKind in R) + }) +}) diff --git a/test/runtime/type/guard/kind/rest.ts b/test/runtime/type/guard/kind/rest.ts new file mode 100644 index 000000000..bb63a4702 --- /dev/null +++ b/test/runtime/type/guard/kind/rest.ts @@ -0,0 +1,59 @@ +import { Type, KindGuard } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TRest', () => { + it('Should guard 1', () => { + // union never + const A = Type.String() + const B = Type.Union(Type.Rest(A)) + Assert.IsTrue(KindGuard.IsNever(B)) + }) + it('Should guard 2', () => { + // intersect never + const A = Type.String() + const B = Type.Intersect(Type.Rest(A)) + Assert.IsTrue(KindGuard.IsNever(B)) + }) + it('Should guard 3', () => { + // tuple + const A = Type.Tuple([Type.Number(), Type.String()]) + const B = Type.Union(Type.Rest(A)) + Assert.IsTrue(KindGuard.IsUnion(B)) + Assert.IsEqual(B.anyOf.length, 2) + Assert.IsTrue(KindGuard.IsNumber(B.anyOf[0])) + Assert.IsTrue(KindGuard.IsString(B.anyOf[1])) + }) + it('Should guard 4', () => { + // tuple spread + const A = Type.Tuple([Type.Literal(1), Type.Literal(2)]) + const B = Type.Tuple([Type.Literal(3), Type.Literal(4)]) + const C = Type.Tuple([...Type.Rest(A), ...Type.Rest(B)]) + Assert.IsTrue(KindGuard.IsTuple(C)) + Assert.IsEqual(C.items!.length, 4) + Assert.IsEqual(C.items![0].const, 1) + Assert.IsEqual(C.items![1].const, 2) + Assert.IsEqual(C.items![2].const, 3) + Assert.IsEqual(C.items![3].const, 4) + }) + it('Should guard 5', () => { + // union to intersect + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Union([A, B]) + const D = Type.Intersect(Type.Rest(C)) + Assert.IsTrue(KindGuard.IsIntersect(D)) + Assert.IsEqual(D.allOf.length, 2) + Assert.IsTrue(KindGuard.IsObject(D.allOf[0])) + Assert.IsTrue(KindGuard.IsObject(D.allOf[1])) + }) + it('Should guard 6', () => { + // intersect to composite + const A = Type.Object({ x: Type.Number() }) + const B = Type.Object({ y: Type.String() }) + const C = Type.Intersect([A, B]) + const D = Type.Composite(Type.Rest(C)) + Assert.IsTrue(KindGuard.IsObject(D)) + Assert.IsTrue(KindGuard.IsNumber(D.properties.x)) + Assert.IsTrue(KindGuard.IsString(D.properties.y)) + }) +}) diff --git a/test/runtime/type/guard/kind/string.ts b/test/runtime/type/guard/kind/string.ts new file mode 100644 index 000000000..ca9b5917a --- /dev/null +++ b/test/runtime/type/guard/kind/string.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TString', () => { + it('Should guard for TString', () => { + const R = KindGuard.IsString(Type.String()) + Assert.IsTrue(R) + }) + it('Should not guard for TString', () => { + const R = KindGuard.IsString(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/symbol.ts b/test/runtime/type/guard/kind/symbol.ts new file mode 100644 index 000000000..f58924b07 --- /dev/null +++ b/test/runtime/type/guard/kind/symbol.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TSymbol', () => { + it('Should guard for TSymbol', () => { + const R = KindGuard.IsSymbol(Type.Symbol()) + Assert.IsTrue(R) + }) + it('Should not guard for TSymbol', () => { + const R = KindGuard.IsSymbol(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/template-literal.ts b/test/runtime/type/guard/kind/template-literal.ts new file mode 100644 index 000000000..9c01c310d --- /dev/null +++ b/test/runtime/type/guard/kind/template-literal.ts @@ -0,0 +1,35 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TTemplateLiteral', () => { + it('Should guard for empty TemplateLiteral', () => { + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([])) + Assert.IsTrue(R) + }) + it('Should guard for TSchema', () => { + const R = KindGuard.IsSchema(Type.TemplateLiteral([])) + Assert.IsTrue(R) + }) + it('Should guard for TemplateLiteral (TTemplateLiteral)', () => { + const T = Type.TemplateLiteral([Type.Literal('hello')]) + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([T, Type.Literal('world')])) + Assert.IsTrue(R) + }) + it('Should guard for TemplateLiteral (TLiteral)', () => { + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Literal('hello')])) + Assert.IsTrue(R) + }) + it('Should guard for TemplateLiteral (TString)', () => { + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.String()])) + Assert.IsTrue(R) + }) + it('Should guard for TemplateLiteral (TNumber)', () => { + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Number()])) + Assert.IsTrue(R) + }) + it('Should guard for TemplateLiteral (TBoolean)', () => { + const R = KindGuard.IsTemplateLiteral(Type.TemplateLiteral([Type.Boolean()])) + Assert.IsTrue(R) + }) +}) diff --git a/test/runtime/type/guard/kind/this.ts b/test/runtime/type/guard/kind/this.ts new file mode 100644 index 000000000..ee1cd4dcb --- /dev/null +++ b/test/runtime/type/guard/kind/this.ts @@ -0,0 +1,13 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TThis', () => { + it('Should guard for TThis', () => { + Type.Recursive((This) => { + const R = KindGuard.IsThis(This) + Assert.IsTrue(R) + return Type.Object({ nodes: Type.Array(This) }) + }) + }) +}) diff --git a/test/runtime/type/guard/kind/tuple.ts b/test/runtime/type/guard/kind/tuple.ts new file mode 100644 index 000000000..289ba57ae --- /dev/null +++ b/test/runtime/type/guard/kind/tuple.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TTuple', () => { + it('Should guard for TTuple', () => { + const R = KindGuard.IsTuple(Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsTrue(R) + }) + it('Should not guard for TTuple', () => { + const R = KindGuard.IsTuple(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/uint8array.ts b/test/runtime/type/guard/kind/uint8array.ts new file mode 100644 index 000000000..f8af30701 --- /dev/null +++ b/test/runtime/type/guard/kind/uint8array.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TUint8Array', () => { + it('Should guard for TUint8Array', () => { + const R = KindGuard.IsUint8Array(Type.Uint8Array()) + Assert.IsTrue(R) + }) + it('Should not guard for TUint8Array', () => { + const R = KindGuard.IsUint8Array(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/uncapitalize.ts b/test/runtime/type/guard/kind/uncapitalize.ts new file mode 100644 index 000000000..65c75b2c4 --- /dev/null +++ b/test/runtime/type/guard/kind/uncapitalize.ts @@ -0,0 +1,33 @@ +import { KindGuard, Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/Uncapitalize', () => { + it('Should guard for Uncapitalize 1', () => { + const T = Type.Uncapitalize(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hELLO') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Uncapitalize 2', () => { + const T = Type.Uncapitalize(Type.Literal('HELLO')) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'hELLO') + }) + it('Should guard for Uncapitalize 3', () => { + const T = Type.Uncapitalize(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'hELLO') + Assert.IsEqual(T.anyOf[1].const, 'wORLD') + }) + it('Should guard for Uncapitalize 4', () => { + const T = Type.Uncapitalize(Type.TemplateLiteral('HELLO${0|1}')) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') + }) + it('Should guard for Uncapitalize 5', () => { + const T = Type.Uncapitalize(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(hELLO0|hELLO1)$') + }) +}) diff --git a/test/runtime/type/guard/kind/undefined.ts b/test/runtime/type/guard/kind/undefined.ts new file mode 100644 index 000000000..1c7aebbcf --- /dev/null +++ b/test/runtime/type/guard/kind/undefined.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TUndefined', () => { + it('Should guard for TUndefined', () => { + const R = KindGuard.IsUndefined(Type.Undefined()) + Assert.IsTrue(R) + }) + it('Should not guard for TUndefined', () => { + const R = KindGuard.IsUndefined(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/union.ts b/test/runtime/type/guard/kind/union.ts new file mode 100644 index 000000000..03e934fd5 --- /dev/null +++ b/test/runtime/type/guard/kind/union.ts @@ -0,0 +1,42 @@ +import { TSchema, KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TUnion', () => { + it('Should guard for TUnion', () => { + const R = KindGuard.IsUnion( + Type.Union([ + Type.Object({ + x: Type.Number(), + }), + Type.Object({ + y: Type.Number(), + }), + ]), + ) + Assert.IsTrue(R) + }) + it('Should not guard for TUnion', () => { + const R = KindGuard.IsUnion(null) + Assert.IsFalse(R) + }) + it('Transform: Should transform to never for zero length union', () => { + const T = Type.Union([]) + const R = KindGuard.IsNever(T) + Assert.IsTrue(R) + }) + it('Transform: Should unwrap union type for array of length === 1', () => { + const T = Type.Union([Type.String()]) + const R = KindGuard.IsString(T) + Assert.IsTrue(R) + }) + it('Transform: Should retain union if array length > 1', () => { + const T = Type.Union([Type.String(), Type.Number()]) + const R1 = KindGuard.IsUnion(T) + const R2 = KindGuard.IsString(T.anyOf[0]) + const R3 = KindGuard.IsNumber(T.anyOf[1]) + Assert.IsTrue(R1) + Assert.IsTrue(R2) + Assert.IsTrue(R3) + }) +}) diff --git a/test/runtime/type/guard/kind/unknown.ts b/test/runtime/type/guard/kind/unknown.ts new file mode 100644 index 000000000..066414e69 --- /dev/null +++ b/test/runtime/type/guard/kind/unknown.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TUnknown', () => { + it('Should guard for TUnknown', () => { + const R = KindGuard.IsUnknown(Type.Unknown()) + Assert.IsTrue(R) + }) + it('Should not guard for TUnknown', () => { + const R = KindGuard.IsUnknown(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/unsafe.ts b/test/runtime/type/guard/kind/unsafe.ts new file mode 100644 index 000000000..c0f206d7e --- /dev/null +++ b/test/runtime/type/guard/kind/unsafe.ts @@ -0,0 +1,33 @@ +import { Kind, KindGuard, TypeRegistry } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TUnsafe', () => { + it('Should guard raw TUnsafe', () => { + const T = Type.Unsafe({ x: 1 }) + const R = KindGuard.IsUnsafe(T) + Assert.IsTrue(R) + }) + it('Should guard raw TUnsafe as TSchema', () => { + const T = Type.Unsafe({ x: 1 }) + const R = KindGuard.IsSchema(T) + Assert.IsTrue(R) + }) + it('Should guard override TUnsafe as TSchema when registered', () => { + TypeRegistry.Set('UnsafeType', () => true) + const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) + const R = KindGuard.IsSchema(T) + Assert.IsTrue(R) + TypeRegistry.Delete('UnsafeType') + }) + it('Should not guard TUnsafe with unregistered kind', () => { + const T = Type.Unsafe({ [Kind]: 'UnsafeType' }) + const R = KindGuard.IsUnsafe(T) + Assert.IsFalse(R) + }) + it('Should not guard for TString', () => { + const T = Type.String() + const R = KindGuard.IsUnsafe(T) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/uppercase.ts b/test/runtime/type/guard/kind/uppercase.ts new file mode 100644 index 000000000..fc8a79603 --- /dev/null +++ b/test/runtime/type/guard/kind/uppercase.ts @@ -0,0 +1,33 @@ +import { KindGuard, Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/Uppercase', () => { + it('Should guard for Uppercase 1', () => { + const T = Type.Uppercase(Type.Literal('hello'), { $id: 'hello', foo: 1 }) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'HELLO') + Assert.IsEqual(T.$id, 'hello') + Assert.IsEqual(T.foo, 1) + }) + it('Should guard for Uppercase 2', () => { + const T = Type.Uppercase(Type.Literal('hello')) + Assert.IsTrue(KindGuard.IsLiteral(T)) + Assert.IsEqual(T.const, 'HELLO') + }) + it('Should guard for Uppercase 3', () => { + const T = Type.Uppercase(Type.Union([Type.Literal('hello'), Type.Literal('world')])) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsEqual(T.anyOf[0].const, 'HELLO') + Assert.IsEqual(T.anyOf[1].const, 'WORLD') + }) + it('Should guard for Uppercase 4', () => { + const T = Type.Uppercase(Type.TemplateLiteral('hello${0|1}')) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') + }) + it('Should guard for Uppercase 5', () => { + const T = Type.Uppercase(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(0), Type.Literal(1)])])) + Assert.IsTrue(KindGuard.IsTemplateLiteral(T)) + Assert.IsEqual(T.pattern, '^(HELLO0|HELLO1)$') + }) +}) diff --git a/test/runtime/type/guard/kind/void.ts b/test/runtime/type/guard/kind/void.ts new file mode 100644 index 000000000..40370e079 --- /dev/null +++ b/test/runtime/type/guard/kind/void.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TVoid', () => { + it('Should guard for TVoid', () => { + const R = KindGuard.IsVoid(Type.Void()) + Assert.IsTrue(R) + }) + it('Should not guard for TVoid', () => { + const R = KindGuard.IsVoid(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/any.ts b/test/runtime/type/guard/type/any.ts similarity index 84% rename from test/runtime/type/guard/any.ts rename to test/runtime/type/guard/type/any.ts index ecc4cc92b..6eebd946d 100644 --- a/test/runtime/type/guard/any.ts +++ b/test/runtime/type/guard/type/any.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TAny', () => { +describe('guard/type/TAny', () => { it('Should guard for TAny', () => { const R = TypeGuard.IsAny(Type.Any()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/array.ts b/test/runtime/type/guard/type/array.ts similarity index 94% rename from test/runtime/type/guard/array.ts rename to test/runtime/type/guard/type/array.ts index 12f0a9e20..fe1033754 100644 --- a/test/runtime/type/guard/array.ts +++ b/test/runtime/type/guard/type/array.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TArray', () => { +describe('guard/type/TArray', () => { it('Should guard for TArray', () => { const R = TypeGuard.IsArray(Type.Array(Type.Number())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/async-iterator.ts b/test/runtime/type/guard/type/async-iterator.ts similarity index 86% rename from test/runtime/type/guard/async-iterator.ts rename to test/runtime/type/guard/type/async-iterator.ts index 55a52da32..cde73726f 100644 --- a/test/runtime/type/guard/async-iterator.ts +++ b/test/runtime/type/guard/type/async-iterator.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TAsyncIterator', () => { +describe('guard/type/TAsyncIterator', () => { it('Should guard for TAsyncIterator', () => { const T = Type.AsyncIterator(Type.Any()) const R = TypeGuard.IsAsyncIterator(T) diff --git a/test/runtime/type/guard/awaited.ts b/test/runtime/type/guard/type/awaited.ts similarity index 94% rename from test/runtime/type/guard/awaited.ts rename to test/runtime/type/guard/type/awaited.ts index 7515107a7..fbfe4dc09 100644 --- a/test/runtime/type/guard/awaited.ts +++ b/test/runtime/type/guard/type/awaited.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/Awaited', () => { +describe('guard/type/Awaited', () => { it('Should guard for Awaited 1', () => { const T = Type.Awaited(Type.String()) const R = TypeGuard.IsString(T) diff --git a/test/runtime/type/guard/bigint.ts b/test/runtime/type/guard/type/bigint.ts similarity index 84% rename from test/runtime/type/guard/bigint.ts rename to test/runtime/type/guard/type/bigint.ts index ae7edab9f..fa9eb3b7c 100644 --- a/test/runtime/type/guard/bigint.ts +++ b/test/runtime/type/guard/type/bigint.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TBigInt', () => { +describe('guard/type/TBigInt', () => { it('Should guard for TBigInt', () => { const R = TypeGuard.IsBigInt(Type.BigInt()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/boolean.ts b/test/runtime/type/guard/type/boolean.ts similarity index 84% rename from test/runtime/type/guard/boolean.ts rename to test/runtime/type/guard/type/boolean.ts index e74af2e16..27c9df1aa 100644 --- a/test/runtime/type/guard/boolean.ts +++ b/test/runtime/type/guard/type/boolean.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TBoolean', () => { +describe('guard/type/TBoolean', () => { it('Should guard for TBoolean', () => { const R = TypeGuard.IsBoolean(Type.Boolean()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/capitalize.ts b/test/runtime/type/guard/type/capitalize.ts similarity index 93% rename from test/runtime/type/guard/capitalize.ts rename to test/runtime/type/guard/type/capitalize.ts index def31e727..c34b0849d 100644 --- a/test/runtime/type/guard/capitalize.ts +++ b/test/runtime/type/guard/type/capitalize.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/Capitalize', () => { +describe('guard/type/Capitalize', () => { it('Should guard for Capitalize 1', () => { const T = Type.Capitalize(Type.Literal('hello'), { $id: 'hello', foo: 1 }) Assert.IsTrue(TypeGuard.IsLiteral(T)) diff --git a/test/runtime/type/guard/composite.ts b/test/runtime/type/guard/type/composite.ts similarity index 98% rename from test/runtime/type/guard/composite.ts rename to test/runtime/type/guard/type/composite.ts index 351041bb2..1cada8065 100644 --- a/test/runtime/type/guard/composite.ts +++ b/test/runtime/type/guard/type/composite.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TComposite', () => { +describe('guard/type/TComposite', () => { it('Should guard for distinct properties', () => { const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) diff --git a/test/runtime/type/guard/const.ts b/test/runtime/type/guard/type/const.ts similarity index 98% rename from test/runtime/type/guard/const.ts rename to test/runtime/type/guard/type/const.ts index 0e5ad4288..408075c37 100644 --- a/test/runtime/type/guard/const.ts +++ b/test/runtime/type/guard/type/const.ts @@ -1,8 +1,8 @@ import { TypeGuard, ValueGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TConstT', () => { +describe('guard/type/TConstT', () => { // ---------------------------------------------------------------- // Identity Types // ---------------------------------------------------------------- diff --git a/test/runtime/type/guard/constructor.ts b/test/runtime/type/guard/type/constructor.ts similarity index 93% rename from test/runtime/type/guard/constructor.ts rename to test/runtime/type/guard/type/constructor.ts index bcbb78aa6..9c55370ca 100644 --- a/test/runtime/type/guard/constructor.ts +++ b/test/runtime/type/guard/type/constructor.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TConstructor', () => { +describe('guard/type/TConstructor', () => { it('Should guard for TConstructor', () => { const R = TypeGuard.IsConstructor(Type.Constructor([], Type.Number())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/date.ts b/test/runtime/type/guard/type/date.ts similarity index 94% rename from test/runtime/type/guard/date.ts rename to test/runtime/type/guard/type/date.ts index fdfc720e2..930cc19af 100644 --- a/test/runtime/type/guard/date.ts +++ b/test/runtime/type/guard/type/date.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TDate', () => { +describe('guard/type/TDate', () => { it('Should guard for TDate', () => { const R = TypeGuard.IsDate(Type.Date()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/deref.ts b/test/runtime/type/guard/type/deref.ts similarity index 97% rename from test/runtime/type/guard/deref.ts rename to test/runtime/type/guard/type/deref.ts index eedd3a652..64f6d7d61 100644 --- a/test/runtime/type/guard/deref.ts +++ b/test/runtime/type/guard/type/deref.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TDeref', () => { +describe('guard/type/TDeref', () => { it('Should should deref 1', () => { const T = Type.String({ $id: 'T' }) const R = Type.Ref(T) diff --git a/test/runtime/type/guard/enum.ts b/test/runtime/type/guard/type/enum.ts similarity index 97% rename from test/runtime/type/guard/enum.ts rename to test/runtime/type/guard/type/enum.ts index dc092e838..4097c1bee 100644 --- a/test/runtime/type/guard/enum.ts +++ b/test/runtime/type/guard/type/enum.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TEnum', () => { +describe('guard/type/TEnum', () => { // ---------------------------------------------------------------- // Options // ---------------------------------------------------------------- diff --git a/test/runtime/type/guard/exclude.ts b/test/runtime/type/guard/type/exclude.ts similarity index 98% rename from test/runtime/type/guard/exclude.ts rename to test/runtime/type/guard/type/exclude.ts index 88366678c..973ab850c 100644 --- a/test/runtime/type/guard/exclude.ts +++ b/test/runtime/type/guard/type/exclude.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TExclude', () => { +describe('guard/type/TExclude', () => { it('Should exclude string from number', () => { const T = Type.Exclude(Type.String(), Type.Number()) Assert.IsTrue(TypeGuard.IsString(T)) diff --git a/test/runtime/type/guard/extract.ts b/test/runtime/type/guard/type/extract.ts similarity index 98% rename from test/runtime/type/guard/extract.ts rename to test/runtime/type/guard/type/extract.ts index ffa7d8871..93f7b1fda 100644 --- a/test/runtime/type/guard/extract.ts +++ b/test/runtime/type/guard/type/extract.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TExtract', () => { +describe('guard/type/TExtract', () => { it('Should extract string from number', () => { const T = Type.Extract(Type.String(), Type.Number()) Assert.IsTrue(TypeGuard.IsNever(T)) diff --git a/test/runtime/type/guard/function.ts b/test/runtime/type/guard/type/function.ts similarity index 93% rename from test/runtime/type/guard/function.ts rename to test/runtime/type/guard/type/function.ts index 400c83004..81bbfe84c 100644 --- a/test/runtime/type/guard/function.ts +++ b/test/runtime/type/guard/type/function.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TFunction', () => { +describe('guard/type/TFunction', () => { it('Should guard for TFunction', () => { const R = TypeGuard.IsFunction(Type.Function([], Type.Number())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/type/index.ts b/test/runtime/type/guard/type/index.ts new file mode 100644 index 000000000..271b53f88 --- /dev/null +++ b/test/runtime/type/guard/type/index.ts @@ -0,0 +1,52 @@ +import './any' +import './array' +import './async-iterator' +import './awaited' +import './bigint' +import './boolean' +import './capitalize' +import './composite' +import './const' +import './constructor' +import './date' +import './deref' +import './enum' +import './exclude' +import './extract' +import './function' +import './indexed' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './lowercase' +import './mapped' +import './not' +import './null' +import './number' +import './object' +import './omit' +import './partial' +import './pick' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './required' +import './rest' +import './string' +import './symbol' +import './template-literal' +import './this' +import './tuple' +import './uint8array' +import './uncapitalize' +import './undefined' +import './union' +import './unknown' +import './unsafe' +import './uppercase' +import './void' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/type/indexed.ts similarity index 99% rename from test/runtime/type/guard/indexed.ts rename to test/runtime/type/guard/type/indexed.ts index 0e29896a9..55225c894 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/type/indexed.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TIndex', () => { +describe('guard/type/TIndex', () => { it('Should Index 1', () => { const T = Type.Object({ x: Type.Number(), diff --git a/test/runtime/type/guard/integer.ts b/test/runtime/type/guard/type/integer.ts similarity index 94% rename from test/runtime/type/guard/integer.ts rename to test/runtime/type/guard/type/integer.ts index 42a8c6120..add1f318c 100644 --- a/test/runtime/type/guard/integer.ts +++ b/test/runtime/type/guard/type/integer.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TInteger', () => { +describe('guard/type/TInteger', () => { it('Should guard for TInteger', () => { const R = TypeGuard.IsInteger(Type.Integer()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/intersect.ts b/test/runtime/type/guard/type/intersect.ts similarity index 90% rename from test/runtime/type/guard/intersect.ts rename to test/runtime/type/guard/type/intersect.ts index ab2b58466..7b345ef66 100644 --- a/test/runtime/type/guard/intersect.ts +++ b/test/runtime/type/guard/type/intersect.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TIntersect', () => { +describe('guard/type/TIntersect', () => { it('Should guard for TIntersect', () => { const R = TypeGuard.IsIntersect( Type.Intersect([ diff --git a/test/runtime/type/guard/iterator.ts b/test/runtime/type/guard/type/iterator.ts similarity index 86% rename from test/runtime/type/guard/iterator.ts rename to test/runtime/type/guard/type/iterator.ts index df549edd1..74e984c49 100644 --- a/test/runtime/type/guard/iterator.ts +++ b/test/runtime/type/guard/type/iterator.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TIterator', () => { +describe('guard/type/TIterator', () => { it('Should guard for TIterator', () => { const T = Type.Iterator(Type.Any()) const R = TypeGuard.IsIterator(T) diff --git a/test/runtime/type/guard/keyof.ts b/test/runtime/type/guard/type/keyof.ts similarity index 95% rename from test/runtime/type/guard/keyof.ts rename to test/runtime/type/guard/type/keyof.ts index 1b066c4ed..50d95ec5a 100644 --- a/test/runtime/type/guard/keyof.ts +++ b/test/runtime/type/guard/type/keyof.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TKeyOf', () => { +describe('guard/type/TKeyOf', () => { it('Should KeyOf 1', () => { const T = Type.Object({ x: Type.Number(), diff --git a/test/runtime/type/guard/kind.ts b/test/runtime/type/guard/type/kind.ts similarity index 75% rename from test/runtime/type/guard/kind.ts rename to test/runtime/type/guard/type/kind.ts index 26006958f..f0223e8ad 100644 --- a/test/runtime/type/guard/kind.ts +++ b/test/runtime/type/guard/type/kind.ts @@ -1,7 +1,7 @@ import { TypeGuard, Kind } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TKind', () => { +describe('guard/type/TKind', () => { it('Should guard 1', () => { const T = { [Kind]: 'Kind' } Assert.IsTrue(TypeGuard.IsKind(T)) diff --git a/test/runtime/type/guard/literal.ts b/test/runtime/type/guard/type/literal.ts similarity index 91% rename from test/runtime/type/guard/literal.ts rename to test/runtime/type/guard/type/literal.ts index c1e6b21f5..fcfe4b74e 100644 --- a/test/runtime/type/guard/literal.ts +++ b/test/runtime/type/guard/type/literal.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TLiteral', () => { +describe('guard/type/TLiteral', () => { it('Should guard for TLiteral of String', () => { const R = TypeGuard.IsLiteral(Type.Literal('hello')) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/lowercase.ts b/test/runtime/type/guard/type/lowercase.ts similarity index 93% rename from test/runtime/type/guard/lowercase.ts rename to test/runtime/type/guard/type/lowercase.ts index be9e797cd..f274be4b4 100644 --- a/test/runtime/type/guard/lowercase.ts +++ b/test/runtime/type/guard/type/lowercase.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/Lowercase', () => { +describe('guard/type/Lowercase', () => { it('Should guard for Lowercase 1', () => { const T = Type.Lowercase(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) Assert.IsTrue(TypeGuard.IsLiteral(T)) diff --git a/test/runtime/type/guard/type/mapped.ts b/test/runtime/type/guard/type/mapped.ts new file mode 100644 index 000000000..99eeff52b --- /dev/null +++ b/test/runtime/type/guard/type/mapped.ts @@ -0,0 +1,609 @@ +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +// prettier-ignore +describe('guard/type/Mapped', () => { + it('Should guard mapped 1', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), _ => Type.Number(), { custom: 1 }) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + }, { custom: 1 })) + }) + it('Should guard mapped 2', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), _ => Type.Number()) + Assert.IsEqual(T, Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + })) + }) + it('Should guard mapped 3', () => { + const T = Type.Mapped(Type.Union([ + Type.Literal('x'), + Type.Literal('y'), + Type.Literal('z'), + ]), K => K) + Assert.IsEqual(T, Type.Object({ + x: Type.Literal('x'), + y: Type.Literal('y'), + z: Type.Literal('z'), + })) + }) + it('Should guard mapped 4', () => { + const T = Type.Mapped(Type.TemplateLiteral('${0|1}${0|1}'), K => Type.Number()) + Assert.IsEqual(T, Type.Object({ + '00': Type.Number(), + '01': Type.Number(), + '10': Type.Number(), + '11': Type.Number(), + })) + }) + it('Should guard mapped 5', () => { + const T = Type.Mapped(Type.TemplateLiteral('${a|b}'), X => + Type.Mapped(Type.TemplateLiteral('${c|d}'), Y => + Type.Mapped(Type.TemplateLiteral('${e|f}'), Z => + Type.Tuple([X, Y, Z]) + ) + ) + ) + Assert.IsEqual(T, Type.Object({ + a: Type.Object({ + c: Type.Object({ + e: Type.Tuple([Type.Literal("a"), Type.Literal("c"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("a"), Type.Literal("c"), Type.Literal("f")]) + }), + d: Type.Object({ + e: Type.Tuple([Type.Literal("a"), Type.Literal("d"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("a"), Type.Literal("d"), Type.Literal("f")]) + }), + }), + b: Type.Object({ + c: Type.Object({ + e: Type.Tuple([Type.Literal("b"), Type.Literal("c"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("b"), Type.Literal("c"), Type.Literal("f")]) + }), + d: Type.Object({ + e: Type.Tuple([Type.Literal("b"), Type.Literal("d"), Type.Literal("e")]), + f: Type.Tuple([Type.Literal("b"), Type.Literal("d"), Type.Literal("f")]) + }), + }), + })) + }) + it('Should guard mapped 6', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => K) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal('x'), + y: Type.Literal('y'), + z: Type.Literal('z') + })) + }) + it('Should guard mapped 7', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + })) + }) + it('Should guard mapped 8', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K, { custom: 1 })) + Assert.IsEqual(M, Type.Object({ + x: Type.Number({ custom: 1 }), + y: Type.String({ custom: 1 }), + z: Type.Boolean({ custom: 1 }) + })) + }) + // ---------------------------------------------------------------- + // Extract + // ---------------------------------------------------------------- + it('Should guard mapped 9', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.String()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.String() + })) + }) + it('Should guard mapped 10', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.String(), Type.Number()]) + })) + }) + it('Should guard mapped 11', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Extract(Type.Index(T, K), Type.Null()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Never() + })) + }) + // ---------------------------------------------------------------- + // Extends + // ---------------------------------------------------------------- + it('Should guard mapped 12', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(K, Type.Literal('x'), Type.Literal(1), + Type.Extends(K, Type.Literal('y'), Type.Literal(2), + Type.Extends(K, Type.Literal('z'), Type.Literal(3), Type.Never()))) + ) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal(1), + y: Type.Literal(2), + z: Type.Literal(3), + })) + }) + it('Should guard mapped 13', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + z: Type.Boolean() + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return ( + Type.Extends(Type.Index(T, K), Type.Number(), Type.Literal(3), + Type.Extends(Type.Index(T, K), Type.String(), Type.Literal(2), + Type.Extends(Type.Index(T, K), Type.Boolean(), Type.Literal(1), Type.Never()))) + ) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Literal(3), + y: Type.Literal(2), + z: Type.Literal(1), + })) + }) + // ---------------------------------------------------------------- + // Exclude + // ---------------------------------------------------------------- + it('Should guard mapped 14', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.String()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.Number(), Type.Boolean()]) + })) + }) + it('Should guard mapped 15', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Union([ + Type.String(), + Type.Number() + ])) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Boolean() + })) + }) + it('Should guard mapped 16', () => { + const T = Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Exclude(Type.Index(T, K), Type.Null()) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Union([Type.String(), Type.Number(), Type.Boolean()]) + })) + }) + // ---------------------------------------------------------------- + // Non-Evaluated + // ---------------------------------------------------------------- + it('Should guard mapped 17', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Array(Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Array(Type.Number()) })) + }) + it('Should guard mapped 18', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Promise(Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Promise(Type.Number()) })) + }) + it('Should guard mapped 19', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Function([Type.Index(T, K)], Type.Index(T, K))) + Assert.IsEqual(M, Type.Object({ x: Type.Function([Type.Number()], Type.Number())})) + }) + it('Should guard mapped 20', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Tuple([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Tuple([Type.Number(), Type.Number()]) })) + }) + it('Should guard mapped 21', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Union([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Union([Type.Number(), Type.Number()]) })) + }) + it('Should guard mapped 22', () => { + const T = Type.Object({ x: Type.Number() }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Intersect([Type.Index(T, K), Type.Index(T, K)])) + Assert.IsEqual(M, Type.Object({ x: Type.Intersect([Type.Number(), Type.Number()]) })) + }) + // ---------------------------------------------------------------- + // Numeric Keys + // ---------------------------------------------------------------- + it('Should guard mapped 23', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => K) + Assert.IsEqual(M, Type.Object({ + 0: Type.Literal('0'), + 1: Type.Literal('1'), + 2: Type.Literal('2'), + })) + }) + it('Should guard mapped 24', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Index(T, K)) + Assert.IsEqual(M, Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number(), + })) + }) + it('Should guard mapped 25', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.String()) + Assert.IsEqual(M, Type.Object({ + 0: Type.String(), + 1: Type.String(), + 2: Type.String(), + })) + }) + it('Should guard mapped 26', () => { + const T = Type.Object({ + 0: Type.Number(), + 1: Type.Number(), + 2: Type.Number() + }) + const M = Type.Mapped(Type.KeyOf(T), K => Type.Extends(K, Type.Literal('1'), Type.String(), Type.Number())) + Assert.IsEqual(M, Type.Object({ + 0: Type.Number(), + 1: Type.String(), + 2: Type.Number(), + })) + }) + // ---------------------------------------------------------------- + // Modifiers: Optional + // ---------------------------------------------------------------- + it('Should guard mapped 27', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number() + }) + // subtractive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), false)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.Number() + })) + }) + it('Should guard mapped 28', () => { + const T = Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Number() + }) + // additive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Optional(Type.Index(T, K), true)) + Assert.IsEqual(M, Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()) + })) + }) + // ---------------------------------------------------------------- + // Modifiers: Readonly + // ---------------------------------------------------------------- + it('Should guard mapped 27', () => { + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Number() + }) + // subtractive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), false)) + Assert.IsEqual(M, Type.Object({ + x: Type.Number(), + y: Type.Number() + })) + }) + it('Should guard mapped 28', () => { + const T = Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Number() + }) + // additive + const M = Type.Mapped(Type.KeyOf(T), K => Type.Readonly(Type.Index(T, K), true)) + Assert.IsEqual(M, Type.Object({ + x: Type.Readonly(Type.Number()), + y: Type.Readonly(Type.Number()) + })) + }) + // ---------------------------------------------------------------- + // Finite Boolean + // ---------------------------------------------------------------- + it('Should guard mapped 29', () => { + const T = Type.TemplateLiteral('${boolean}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + true: Type.Literal('true'), + false: Type.Literal('false'), + })) + }) + it('Should guard mapped 30', () => { + const T = Type.TemplateLiteral('${0|1}${boolean}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + '0true': Type.Literal('0true'), + '0false': Type.Literal('0false'), + '1true': Type.Literal('1true'), + '1false': Type.Literal('1false'), + })) + }) + it('Should guard mapped 31', () => { + const T = Type.TemplateLiteral('${boolean}${0|1}') + const M = Type.Mapped(T, K => K) + Assert.IsEqual(M, Type.Object({ + 'true0': Type.Literal('true0'), + 'true1': Type.Literal('true1'), + 'false0': Type.Literal('false0'), + 'false1': Type.Literal('false1'), + })) + }) + // ---------------------------------------------------------------- + // Numeric Mapping + // ---------------------------------------------------------------- + it('Should guard mapped 32', () => { + const T = Type.TemplateLiteral([ + Type.Union([Type.Literal(0), Type.Literal(1)]), + Type.Union([Type.Literal(0), Type.Literal(1)]), + ]) + const M = Type.Mapped(T, (K) => K) + Assert.IsEqual(M, Type.Object({ + '00': Type.Literal('00'), + '01': Type.Literal('01'), + '10': Type.Literal('10'), + '11': Type.Literal('11'), + })) + }) + // ---------------------------------------------------------------- + // Indexed Key Remap + // ---------------------------------------------------------------- + it('Should guard mapped 33', () => { + const T = Type.Object({ + hello: Type.Number(), + world: Type.String(), + }) + const M = Type.Mapped(Type.Uppercase(Type.KeyOf(T)), (K) => { + return Type.Index(T, Type.Lowercase(K)) + }) + Assert.IsEqual(M, Type.Object({ + HELLO: Type.Number(), + WORLD: Type.String() + })) + }) + // ---------------------------------------------------------------- + // Partial + // ---------------------------------------------------------------- + it('Should guard mapped 34', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Partial(Type.Index(T, K)) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Partial(Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + })), + y: Type.Partial(Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + })), + })) + }) + // ---------------------------------------------------------------- + // Required + // ---------------------------------------------------------------- + it('Should guard mapped 35', () => { + const T = Type.Object({ + x: Type.Partial(Type.Object({ + x: Type.Number(), + y: Type.Number(), + })), + y: Type.Partial(Type.Object({ + x: Type.Number(), + y: Type.Number(), + })), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Required(Type.Index(T, K)) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + })) + }) + // ------------------------------------------------------------------ + // Pick With Key + // ------------------------------------------------------------------ + it('Should guard mapped 36', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Pick(T, K) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + y: Type.Object({ + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + })) + }) + // ------------------------------------------------------------------ + // Pick With Result + // ------------------------------------------------------------------ + it('Should guard mapped 37', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Pick(Type.Index(T, K), ['x']) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ x: Type.Number() }), + y: Type.Object({ x: Type.Number() }) + })) + }) + // ------------------------------------------------------------------ + // Omit With Key + // ------------------------------------------------------------------ + it('Should guard mapped 36', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number() + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number() + }) + }) + const M = Type.Mapped(Type.KeyOf(T), K => { + return Type.Omit(T, K) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + y: Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + }), + })) + }) + // ------------------------------------------------------------------ + // Omit With Result + // ------------------------------------------------------------------ + it('Should guard mapped 37', () => { + const T = Type.Object({ + x: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + y: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }) + const M = Type.Mapped(Type.KeyOf(T), (K) => { + return Type.Omit(Type.Index(T, K), ['x']) + }) + Assert.IsEqual(M, Type.Object({ + x: Type.Object({ y: Type.Number() }), + y: Type.Object({ y: Type.Number() }) + })) + }) +}) diff --git a/test/runtime/type/guard/not.ts b/test/runtime/type/guard/type/not.ts similarity index 85% rename from test/runtime/type/guard/not.ts rename to test/runtime/type/guard/type/not.ts index b21cb5d7c..fcfd8b787 100644 --- a/test/runtime/type/guard/not.ts +++ b/test/runtime/type/guard/type/not.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TNot', () => { +describe('guard/type/TNot', () => { it('Should guard for TNot', () => { const R = TypeGuard.IsNot(Type.Not(Type.String())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/null.ts b/test/runtime/type/guard/type/null.ts similarity index 84% rename from test/runtime/type/guard/null.ts rename to test/runtime/type/guard/type/null.ts index 0ab379276..fc2e85fd5 100644 --- a/test/runtime/type/guard/null.ts +++ b/test/runtime/type/guard/type/null.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TNull', () => { +describe('guard/type/TNull', () => { it('Should guard for TNull', () => { const R = TypeGuard.IsNull(Type.Null()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/number.ts b/test/runtime/type/guard/type/number.ts similarity index 94% rename from test/runtime/type/guard/number.ts rename to test/runtime/type/guard/type/number.ts index 02ba9d6a7..c4550aeba 100644 --- a/test/runtime/type/guard/number.ts +++ b/test/runtime/type/guard/type/number.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TNumber', () => { +describe('guard/type/TNumber', () => { it('Should guard for TNumber', () => { const R = TypeGuard.IsNumber(Type.Number()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/object.ts b/test/runtime/type/guard/type/object.ts similarity index 96% rename from test/runtime/type/guard/object.ts rename to test/runtime/type/guard/type/object.ts index ff5b2f0e2..49368dd36 100644 --- a/test/runtime/type/guard/object.ts +++ b/test/runtime/type/guard/type/object.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TObject', () => { +describe('guard/type/TObject', () => { it('Should guard for TObject', () => { const R = TypeGuard.IsObject( Type.Object({ diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/type/omit.ts similarity index 97% rename from test/runtime/type/guard/omit.ts rename to test/runtime/type/guard/type/omit.ts index c449cc7e6..d9c77c686 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/type/omit.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type, Kind, TransformKind } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TOmit', () => { +describe('guard/type/TOmit', () => { // ------------------------------------------------------------------------- // case: https://github.com/sinclairzx81/typebox/issues/384 // ------------------------------------------------------------------------- diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/type/partial.ts similarity index 96% rename from test/runtime/type/guard/partial.ts rename to test/runtime/type/guard/type/partial.ts index 6c419f451..1e5c61512 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/type/partial.ts @@ -1,7 +1,7 @@ import { TypeGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TPartial', () => { +describe('guard/type/TPartial', () => { it('Should produce a valid TSchema', () => { const T = Type.Partial(Type.Object({ x: Type.Number() })) Assert.IsTrue(TypeGuard.IsSchema(T)) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/type/pick.ts similarity index 97% rename from test/runtime/type/guard/pick.ts rename to test/runtime/type/guard/type/pick.ts index 7b53d4da9..e1cf25952 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/type/pick.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type, Kind, TransformKind } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TPick', () => { +describe('guard/type/TPick', () => { // ------------------------------------------------------------------------- // case: https://github.com/sinclairzx81/typebox/issues/384 // ------------------------------------------------------------------------- diff --git a/test/runtime/type/guard/promise.ts b/test/runtime/type/guard/type/promise.ts similarity index 92% rename from test/runtime/type/guard/promise.ts rename to test/runtime/type/guard/type/promise.ts index 7fab077b8..073dbcf70 100644 --- a/test/runtime/type/guard/promise.ts +++ b/test/runtime/type/guard/type/promise.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TPromise', () => { +describe('guard/type/TPromise', () => { it('Should guard for TPromise', () => { const R = TypeGuard.IsPromise(Type.Promise(Type.Number())) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/type/record.ts b/test/runtime/type/guard/type/record.ts new file mode 100644 index 000000000..14729961c --- /dev/null +++ b/test/runtime/type/guard/type/record.ts @@ -0,0 +1,156 @@ +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/type/TRecord', () => { + // ------------------------------------------------------------- + // Overloads + // ------------------------------------------------------------- + it('Should guard overload 1', () => { + const T = Type.Record(Type.Union([Type.Literal('A'), Type.Literal('B')]), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsString(T.properties.B)) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 2', () => { + const T = Type.Record(Type.Union([Type.Literal('A')]), Type.String(), { extra: 1 }) // unwrap as literal + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 3', () => { + // @ts-ignore + const T = Type.Record(Type.Union([]), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + it('Should guard overload 4', () => { + // @ts-ignore + const T = Type.Record(Type.BigInt(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + it('Should guard overload 5', () => { + const T = Type.Record(Type.Literal('A'), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 6', () => { + const L = Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const T = Type.Record(L, Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.helloA)) + Assert.IsTrue(TypeGuard.IsString(T.properties.helloB)) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 7', () => { + const T = Type.Record(Type.Number(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNumberExact])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 8', () => { + const T = Type.Record(Type.Integer(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNumberExact])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 9', () => { + const T = Type.Record(Type.String(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternStringExact])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 10', () => { + const L = Type.TemplateLiteral([Type.String(), Type.Literal('_foo')]) + const T = Type.Record(L, Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[`^${PatternString}_foo$`])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 11', () => { + const L = Type.Union([Type.Literal('A'), Type.Union([Type.Literal('B'), Type.Literal('C')])]) + const T = Type.Record(L, Type.String()) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.A)) + Assert.IsTrue(TypeGuard.IsString(T.properties.B)) + Assert.IsTrue(TypeGuard.IsString(T.properties.C)) + }) + it('Should guard overload 12', () => { + enum E { + A = 'X', + B = 'Y', + C = 'Z', + } + const T = Type.Enum(E) + const R = Type.Record(T, Type.Null()) + Assert.IsTrue(TypeGuard.IsObject(R)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.X)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.Y)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.Z)) + }) + // ------------------------------------------------------------- + // Variants + // ------------------------------------------------------------- + it('Should guard for TRecord', () => { + const R = TypeGuard.IsRecord(Type.Record(Type.String(), Type.Number())) + Assert.IsTrue(R) + }) + it('Should guard for TRecord with TObject value', () => { + const R = TypeGuard.IsRecord( + Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ), + ) + Assert.IsTrue(R) + }) + it('Should not guard for TRecord', () => { + const R = TypeGuard.IsRecord(null) + Assert.IsFalse(R) + }) + it('Should not guard for TRecord with invalid $id', () => { + // @ts-ignore + const R = TypeGuard.IsRecord(Type.Record(Type.String(), Type.Number(), { $id: 1 })) + Assert.IsFalse(R) + }) + it('Should not guard for TRecord with TObject value with invalid Property', () => { + const R = TypeGuard.IsRecord( + Type.Record( + Type.String(), + Type.Object({ + x: Type.Number(), + y: {} as any, + }), + ), + ) + Assert.IsFalse(R) + }) + it('Normalize: Should should normalize to TObject for single literal union value', () => { + const K = Type.Union([Type.Literal('ok')]) + const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) + Assert.IsTrue(R) + }) + it('Normalize: Should should normalize to TObject for multi literal union value', () => { + const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) + const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) + Assert.IsTrue(R) + }) + // ------------------------------------------------------------------ + // Evaluated: Dollar Sign Escape + // https://github.com/sinclairzx81/typebox/issues/794 + // ------------------------------------------------------------------ + // prettier-ignore + { + const K = Type.TemplateLiteral('$prop${A|B|C}') // issue + const T = Type.Record(K, Type.String()) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propA)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propB)) + Assert.IsTrue(TypeGuard.IsString(T.properties.$propC)) + Assert.IsEqual(T.required, ['$propA', '$propB', '$propC']) + } +}) diff --git a/test/runtime/type/guard/recursive.ts b/test/runtime/type/guard/type/recursive.ts similarity index 85% rename from test/runtime/type/guard/recursive.ts rename to test/runtime/type/guard/type/recursive.ts index 57c6fe775..861b06668 100644 --- a/test/runtime/type/guard/recursive.ts +++ b/test/runtime/type/guard/type/recursive.ts @@ -1,8 +1,8 @@ import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRecursive', () => { +describe('guard/type/TRecursive', () => { it('Should guard 1', () => { const T = Type.Recursive((This) => Type.Object({ nodes: This })) Assert.IsTrue(TypeGuard.IsRecursive(T)) diff --git a/test/runtime/type/guard/ref.ts b/test/runtime/type/guard/type/ref.ts similarity index 87% rename from test/runtime/type/guard/ref.ts rename to test/runtime/type/guard/type/ref.ts index e2b5a74b8..ab8f109bf 100644 --- a/test/runtime/type/guard/ref.ts +++ b/test/runtime/type/guard/type/ref.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRef', () => { +describe('guard/type/TRef', () => { it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = TypeGuard.IsRef(Type.Ref(T)) diff --git a/test/runtime/type/guard/regexp.ts b/test/runtime/type/guard/type/regexp.ts similarity index 92% rename from test/runtime/type/guard/regexp.ts rename to test/runtime/type/guard/type/regexp.ts index 558329cba..759d39860 100644 --- a/test/runtime/type/guard/regexp.ts +++ b/test/runtime/type/guard/type/regexp.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRegExp', () => { +describe('guard/type/TRegExp', () => { it('Should guard for TRegExp 1', () => { const T = Type.RegExp(/foo/, { $id: 'T' }) Assert.IsTrue(TypeGuard.IsSchema(T)) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/type/required.ts similarity index 96% rename from test/runtime/type/guard/required.ts rename to test/runtime/type/guard/type/required.ts index e4315e912..733302b4f 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/type/required.ts @@ -1,7 +1,7 @@ import { TypeGuard, TypeRegistry, Type, Kind, TransformKind } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRequired', () => { +describe('guard/type/TRequired', () => { it('Should produce a valid TSchema', () => { const T = Type.Required(Type.Object({ x: Type.Number() })) Assert.IsTrue(TypeGuard.IsSchema(T)) diff --git a/test/runtime/type/guard/rest.ts b/test/runtime/type/guard/type/rest.ts similarity index 95% rename from test/runtime/type/guard/rest.ts rename to test/runtime/type/guard/type/rest.ts index dc018d6e7..8383c2b2d 100644 --- a/test/runtime/type/guard/rest.ts +++ b/test/runtime/type/guard/type/rest.ts @@ -1,7 +1,7 @@ import { Type, TypeGuard } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TRest', () => { +describe('guard/type/TRest', () => { it('Should guard 1', () => { // union never const A = Type.String() diff --git a/test/runtime/type/guard/string.ts b/test/runtime/type/guard/type/string.ts similarity index 92% rename from test/runtime/type/guard/string.ts rename to test/runtime/type/guard/type/string.ts index 5eb862683..42aae379b 100644 --- a/test/runtime/type/guard/string.ts +++ b/test/runtime/type/guard/type/string.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TString', () => { +describe('guard/type/TString', () => { it('Should guard for TString', () => { const R = TypeGuard.IsString(Type.String()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/symbol.ts b/test/runtime/type/guard/type/symbol.ts similarity index 84% rename from test/runtime/type/guard/symbol.ts rename to test/runtime/type/guard/type/symbol.ts index 8723b2151..488e08334 100644 --- a/test/runtime/type/guard/symbol.ts +++ b/test/runtime/type/guard/type/symbol.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TSymbol', () => { +describe('guard/type/TSymbol', () => { it('Should guard for TSymbol', () => { const R = TypeGuard.IsSymbol(Type.Symbol()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/template-literal.ts b/test/runtime/type/guard/type/template-literal.ts similarity index 94% rename from test/runtime/type/guard/template-literal.ts rename to test/runtime/type/guard/type/template-literal.ts index b24518e08..7b0f98ce4 100644 --- a/test/runtime/type/guard/template-literal.ts +++ b/test/runtime/type/guard/type/template-literal.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TTemplateLiteral', () => { +describe('guard/type/TTemplateLiteral', () => { it('Should guard for empty TemplateLiteral', () => { const R = TypeGuard.IsTemplateLiteral(Type.TemplateLiteral([])) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/this.ts b/test/runtime/type/guard/type/this.ts similarity index 86% rename from test/runtime/type/guard/this.ts rename to test/runtime/type/guard/type/this.ts index 8ca3a7e0d..800018219 100644 --- a/test/runtime/type/guard/this.ts +++ b/test/runtime/type/guard/type/this.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TThis', () => { +describe('guard/type/TThis', () => { it('Should guard for TThis', () => { Type.Recursive((This) => { const R = TypeGuard.IsThis(This) diff --git a/test/runtime/type/guard/tuple.ts b/test/runtime/type/guard/type/tuple.ts similarity index 89% rename from test/runtime/type/guard/tuple.ts rename to test/runtime/type/guard/type/tuple.ts index 31a15c280..b47ecde3f 100644 --- a/test/runtime/type/guard/tuple.ts +++ b/test/runtime/type/guard/type/tuple.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TTuple', () => { +describe('guard/type/TTuple', () => { it('Should guard for TTuple', () => { const R = TypeGuard.IsTuple(Type.Tuple([Type.Number(), Type.Number()])) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/uint8array.ts b/test/runtime/type/guard/type/uint8array.ts similarity index 91% rename from test/runtime/type/guard/uint8array.ts rename to test/runtime/type/guard/type/uint8array.ts index de668fd9f..ae0b88cfd 100644 --- a/test/runtime/type/guard/uint8array.ts +++ b/test/runtime/type/guard/type/uint8array.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TUint8Array', () => { +describe('guard/type/TUint8Array', () => { it('Should guard for TUint8Array', () => { const R = TypeGuard.IsUint8Array(Type.Uint8Array()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/uncapitalize.ts b/test/runtime/type/guard/type/uncapitalize.ts similarity index 93% rename from test/runtime/type/guard/uncapitalize.ts rename to test/runtime/type/guard/type/uncapitalize.ts index ff412676a..61a60e908 100644 --- a/test/runtime/type/guard/uncapitalize.ts +++ b/test/runtime/type/guard/type/uncapitalize.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/Uncapitalize', () => { +describe('guard/type/Uncapitalize', () => { it('Should guard for Uncapitalize 1', () => { const T = Type.Uncapitalize(Type.Literal('HELLO'), { $id: 'hello', foo: 1 }) Assert.IsTrue(TypeGuard.IsLiteral(T)) diff --git a/test/runtime/type/guard/undefined.ts b/test/runtime/type/guard/type/undefined.ts similarity index 85% rename from test/runtime/type/guard/undefined.ts rename to test/runtime/type/guard/type/undefined.ts index a6389f4e6..ccebb9551 100644 --- a/test/runtime/type/guard/undefined.ts +++ b/test/runtime/type/guard/type/undefined.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TUndefined', () => { +describe('guard/type/TUndefined', () => { it('Should guard for TUndefined', () => { const R = TypeGuard.IsUndefined(Type.Undefined()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/union.ts b/test/runtime/type/guard/type/union.ts similarity index 96% rename from test/runtime/type/guard/union.ts rename to test/runtime/type/guard/type/union.ts index edcf34043..fb5ca4462 100644 --- a/test/runtime/type/guard/union.ts +++ b/test/runtime/type/guard/type/union.ts @@ -1,8 +1,8 @@ import { TSchema, TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TUnion', () => { +describe('guard/type/TUnion', () => { it('Should guard for TUnion', () => { const R = TypeGuard.IsUnion( Type.Union([ diff --git a/test/runtime/type/guard/unknown.ts b/test/runtime/type/guard/type/unknown.ts similarity index 84% rename from test/runtime/type/guard/unknown.ts rename to test/runtime/type/guard/type/unknown.ts index cc488fd74..7cc9a29ae 100644 --- a/test/runtime/type/guard/unknown.ts +++ b/test/runtime/type/guard/type/unknown.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TUnknown', () => { +describe('guard/type/TUnknown', () => { it('Should guard for TUnknown', () => { const R = TypeGuard.IsUnknown(Type.Unknown()) Assert.IsTrue(R) diff --git a/test/runtime/type/guard/unsafe.ts b/test/runtime/type/guard/type/unsafe.ts similarity index 91% rename from test/runtime/type/guard/unsafe.ts rename to test/runtime/type/guard/type/unsafe.ts index 5691abea8..1b88d9ddc 100644 --- a/test/runtime/type/guard/unsafe.ts +++ b/test/runtime/type/guard/type/unsafe.ts @@ -1,8 +1,8 @@ import { Kind, TypeGuard, TypeRegistry } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TUnsafe', () => { +describe('guard/type/TUnsafe', () => { it('Should guard raw TUnsafe', () => { const T = Type.Unsafe({ x: 1 }) const R = TypeGuard.IsUnsafe(T) diff --git a/test/runtime/type/guard/uppercase.ts b/test/runtime/type/guard/type/uppercase.ts similarity index 93% rename from test/runtime/type/guard/uppercase.ts rename to test/runtime/type/guard/type/uppercase.ts index 539a93755..ba10aa46f 100644 --- a/test/runtime/type/guard/uppercase.ts +++ b/test/runtime/type/guard/type/uppercase.ts @@ -1,7 +1,7 @@ import { TypeGuard, Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/Uppercase', () => { +describe('guard/type/Uppercase', () => { it('Should guard for Uppercase 1', () => { const T = Type.Uppercase(Type.Literal('hello'), { $id: 'hello', foo: 1 }) Assert.IsTrue(TypeGuard.IsLiteral(T)) diff --git a/test/runtime/type/guard/void.ts b/test/runtime/type/guard/type/void.ts similarity index 84% rename from test/runtime/type/guard/void.ts rename to test/runtime/type/guard/type/void.ts index 9c1f52beb..f9320c6c4 100644 --- a/test/runtime/type/guard/void.ts +++ b/test/runtime/type/guard/type/void.ts @@ -1,8 +1,8 @@ import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' -describe('type/guard/TVoid', () => { +describe('guard/type/TVoid', () => { it('Should guard for TVoid', () => { const R = TypeGuard.IsVoid(Type.Void()) Assert.IsTrue(R) diff --git a/test/runtime/type/value/guard.ts b/test/runtime/type/guard/value/guard.ts similarity index 99% rename from test/runtime/type/value/guard.ts rename to test/runtime/type/guard/value/guard.ts index d68d18952..b58df9bea 100644 --- a/test/runtime/type/value/guard.ts +++ b/test/runtime/type/guard/value/guard.ts @@ -1,4 +1,4 @@ -import { Assert } from '../../assert/index' +import { Assert } from '../../../assert/index' import { ValueGuard } from '@sinclair/typebox' describe('type/ValueGuard', () => { diff --git a/test/runtime/type/value/index.ts b/test/runtime/type/guard/value/index.ts similarity index 100% rename from test/runtime/type/value/index.ts rename to test/runtime/type/guard/value/index.ts diff --git a/test/runtime/type/index.ts b/test/runtime/type/index.ts index 120c570f6..2551b5ae8 100644 --- a/test/runtime/type/index.ts +++ b/test/runtime/type/index.ts @@ -7,4 +7,3 @@ import './options/index' import './registry/index' import './sets/index' import './template-literal/index' -import './value/index' From a670487154724c6e08517a9a1d8bb0d3ede10a3d Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 27 Apr 2024 16:15:28 +0900 Subject: [PATCH 255/369] Revision 0.32.26 (#851) * Use Optimized Number Check * Fix Readme Error | Update Overview * Version --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 8 ++++---- src/compiler/compiler.ts | 4 ++-- src/system/policy.ts | 3 +-- src/type/guard/type.ts | 2 +- src/value/guard/guard.ts | 2 +- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8c489aee..a0d06c80b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.25", + "version": "0.32.26", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.25", + "version": "0.32.26", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 300281f8f..f2162e4e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.25", + "version": "0.32.26", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 86aac5555..239e894e8 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates in-memory Json Schema objects that infer as TypeScript types. The schematics produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime asserted using standard Json Schema validation. -This library is designed to allow Json Schema to compose with the same flexibility as TypeScript's programmable type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to allow Json Schema to compose with a similar flexibility to TypeScript's type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. License MIT @@ -895,9 +895,9 @@ const K = Type.TemplateLiteral('prop${A|B|C}') // const K: TTemplateLitera // ]> const R = Type.Record(K, Type.String()) // const R: TObject<{ - // hello1: TString, - // hello2: TString, - // hello3: TString, + // propA: TString, + // propB: TString, + // propC: TString, // }> ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 39cb2f4c2..8e939f1f7 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -210,7 +210,7 @@ export namespace Policy { : `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))` } export function IsNumberLike(value: string): string { - return !TypeSystemPolicy.AllowNaN ? `(typeof ${value} === 'number' && Number.isFinite(${value}))` : `typeof ${value} === 'number'` + return TypeSystemPolicy.AllowNaN ? `typeof ${value} === 'number'` : `Number.isFinite(${value})` } export function IsVoidLike(value: string): string { return TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` @@ -288,7 +288,7 @@ export namespace TypeCompiler { yield `(typeof ${value} === 'function')` } function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { - yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` + yield `Number.isInteger(${value})` if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` if (IsNumber(schema.maximum)) yield `${value} <= ${schema.maximum}` diff --git a/src/system/policy.ts b/src/system/policy.ts index e2d1a08e7..400c74b16 100644 --- a/src/system/policy.ts +++ b/src/system/policy.ts @@ -56,8 +56,7 @@ export namespace TypeSystemPolicy { } /** Asserts this value using the AllowNaN policy */ export function IsNumberLike(value: unknown): value is number { - const isNumber = IsNumber(value) - return AllowNaN ? isNumber : isNumber && Number.isFinite(value) + return AllowNaN ? IsNumber(value) : Number.isFinite(value) } /** Asserts this value using the AllowVoidNull policy */ export function IsVoidLike(value: unknown): value is void { diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index f80b24fe7..b2e9daa9a 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -30,7 +30,7 @@ import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TypeBoxError } from '../error/index' import { TransformOptions } from '../transform/index' -import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' +import { TTemplateLiteral } from '../template-literal/index' import { TArray } from '../array/index' import { TBoolean } from '../boolean/index' import type { TRecord } from '../record/index' diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 8e45b1bd8..14c575dca 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -171,7 +171,7 @@ export function IsNumber(value: unknown): value is number { } /** Returns true if this value is an integer */ export function IsInteger(value: unknown): value is number { - return IsNumber(value) && Number.isInteger(value) + return Number.isInteger(value) } /** Returns true if this value is bigint */ export function IsBigInt(value: unknown): value is bigint { From 135ac79a3db647179f9679ea2bc85b2b5e0a4f09 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 27 Apr 2024 17:54:58 +0900 Subject: [PATCH 256/369] Revision 0.32.26 (#852) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 239e894e8..6d1aa239b 100644 --- a/readme.md +++ b/readme.md @@ -53,7 +53,7 @@ type T = Static // type T = { TypeBox is a runtime type builder that creates in-memory Json Schema objects that infer as TypeScript types. The schematics produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox offers a unified type that can be statically checked by TypeScript and runtime asserted using standard Json Schema validation. -This library is designed to allow Json Schema to compose with a similar flexibility to TypeScript's type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. +This library is designed to allow Json Schema to compose similar to how types compose within TypeScript's type system. It can be used as a simple tool to build up complex schematics or integrated into REST and RPC services to help validate data received over the wire. License MIT From 93a32b0bf0ca27d09939a64f84f28ea9a38d09cd Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 28 Apr 2024 19:59:48 +0900 Subject: [PATCH 257/369] Revision 0.32.27 (#854) * Refactor Build Tooling * Override esm.sh Bundling Strategy * Version --- hammer.mjs | 6 ++-- package-lock.json | 4 +-- package.json | 2 +- task/build/{require => cjs}/build.ts | 6 ++-- task/build/{require => cjs}/compile.ts | 0 task/build/{import => esm}/build.ts | 6 ++-- task/build/{import => esm}/compile.ts | 0 task/build/{import => esm}/convert-to-esm.ts | 0 task/build/index.ts | 6 ++-- .../{common => notices}/remove-notices.ts | 0 task/build/{redirect => package}/build.ts | 6 ++-- .../create-package-json-redirect.ts} | 19 ++++++------- .../create-package-json.ts | 28 ++++++++++--------- 13 files changed, 42 insertions(+), 41 deletions(-) rename task/build/{require => cjs}/build.ts (91%) rename task/build/{require => cjs}/compile.ts (100%) rename task/build/{import => esm}/build.ts (91%) rename task/build/{import => esm}/compile.ts (100%) rename task/build/{import => esm}/convert-to-esm.ts (100%) rename task/build/{common => notices}/remove-notices.ts (100%) rename task/build/{redirect => package}/build.ts (90%) rename task/build/{redirect/create-redirect-paths.ts => package/create-package-json-redirect.ts} (92%) rename task/build/{redirect => package}/create-package-json.ts (80%) diff --git a/hammer.mjs b/hammer.mjs index 065e0326b..d936b2034 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -60,9 +60,9 @@ export async function build(target = 'target/build') { await test() await clean() await Promise.all([ - Build.Import.build(target), - Build.Require.build(target), - Build.Redirect.build(target) + Build.Package.build(target), + Build.Esm.build(target), + Build.Cjs.build(target), ]) await folder(target).add('readme.md') await folder(target).add('license') diff --git a/package-lock.json b/package-lock.json index a0d06c80b..9e69929a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.26", + "version": "0.32.27", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.26", + "version": "0.32.27", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f2162e4e9..16ed2818e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.26", + "version": "0.32.27", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/task/build/require/build.ts b/task/build/cjs/build.ts similarity index 91% rename from task/build/require/build.ts rename to task/build/cjs/build.ts index 52bba8179..65f1c8410 100644 --- a/task/build/require/build.ts +++ b/task/build/cjs/build.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { removeNotices } from '../common/remove-notices' +import { removeNotices } from '../notices/remove-notices' import { compile } from './compile' /** Builds the CommonJS version of this package */ export async function build(target: string) { - console.log('building...require') - const buildTarget = `${target}/build/require` + console.log('building...cjs') + const buildTarget = `${target}/build/cjs` await compile(buildTarget) await removeNotices(buildTarget) } diff --git a/task/build/require/compile.ts b/task/build/cjs/compile.ts similarity index 100% rename from task/build/require/compile.ts rename to task/build/cjs/compile.ts diff --git a/task/build/import/build.ts b/task/build/esm/build.ts similarity index 91% rename from task/build/import/build.ts rename to task/build/esm/build.ts index bb2637876..04a0eb450 100644 --- a/task/build/import/build.ts +++ b/task/build/esm/build.ts @@ -26,14 +26,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { removeNotices } from '../common/remove-notices' +import { removeNotices } from '../notices/remove-notices' import { convertToEsm } from './convert-to-esm' import { compile } from './compile' /** Builds the ESM version of this package */ export async function build(target: string) { - console.log('building...import') - const buildTarget = `${target}/build/import` + console.log('building...esm') + const buildTarget = `${target}/build/esm` await compile(buildTarget) await convertToEsm(buildTarget) await removeNotices(buildTarget) diff --git a/task/build/import/compile.ts b/task/build/esm/compile.ts similarity index 100% rename from task/build/import/compile.ts rename to task/build/esm/compile.ts diff --git a/task/build/import/convert-to-esm.ts b/task/build/esm/convert-to-esm.ts similarity index 100% rename from task/build/import/convert-to-esm.ts rename to task/build/esm/convert-to-esm.ts diff --git a/task/build/index.ts b/task/build/index.ts index 948330f61..e55ce6705 100644 --- a/task/build/index.ts +++ b/task/build/index.ts @@ -26,6 +26,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * as Import from './import/build' -export * as Require from './require/build' -export * as Redirect from './redirect/build' +export * as Package from './package/build' +export * as Esm from './esm/build' +export * as Cjs from './cjs/build' diff --git a/task/build/common/remove-notices.ts b/task/build/notices/remove-notices.ts similarity index 100% rename from task/build/common/remove-notices.ts rename to task/build/notices/remove-notices.ts diff --git a/task/build/redirect/build.ts b/task/build/package/build.ts similarity index 90% rename from task/build/redirect/build.ts rename to task/build/package/build.ts index f3eba27d7..e7975ed73 100644 --- a/task/build/redirect/build.ts +++ b/task/build/package/build.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { createPackageJsonRedirect } from './create-package-json-redirect' import { createPackageJson } from './create-package-json' -import { createRedirectPaths } from './create-redirect-paths' /** Builds package.json and redirect directories */ export async function build(target: string) { - console.log('building...redirect') + console.log('building...package.json') const submodules = ['compiler', 'errors', 'system', 'type', 'value'] + await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) - await createRedirectPaths(target, submodules) } diff --git a/task/build/redirect/create-redirect-paths.ts b/task/build/package/create-package-json-redirect.ts similarity index 92% rename from task/build/redirect/create-redirect-paths.ts rename to task/build/package/create-package-json-redirect.ts index 3915b08e5..f586763f3 100644 --- a/task/build/redirect/create-redirect-paths.ts +++ b/task/build/package/create-package-json-redirect.ts @@ -28,6 +28,14 @@ THE SOFTWARE. import * as Fs from 'node:fs' +// prettier-ignore +function writeRedirect(target: string, submodule: string) { + Fs.mkdirSync(`${target}/${submodule}`, { recursive: true }) + Fs.writeFileSync(`${target}/${submodule}/package.json`,JSON.stringify({ + main: `../build/cjs/${submodule}/index.js`, + types: `../build/cjs/${submodule}/index.d.ts`, + }, null, 2)) +} // -------------------------------------------------------------------------------------------------------------------------- // Builds redirect directories for earlier versions of Node. Note that TypeScript will use these directories to // resolve types when tsconfig.json is configured for `moduleResolution: 'node'`. This approach is referred to as @@ -37,15 +45,6 @@ import * as Fs from 'node:fs' // -------------------------------------------------------------------------------------------------------------------------- // prettier-ignore -export function createRedirectPaths(target: string, submodules: string[]) { +export function createPackageJsonRedirect(target: string, submodules: string[]) { submodules.forEach((submodule) => writeRedirect(target, submodule)) } - -// prettier-ignore -function writeRedirect(target: string, submodule: string) { - Fs.mkdirSync(`${target}/${submodule}`, { recursive: true }) - Fs.writeFileSync(`${target}/${submodule}/package.json`,JSON.stringify({ - main: `../build/require/${submodule}/index.js`, - types: `../build/require/${submodule}/index.d.ts`, - }, null, 2)) -} diff --git a/task/build/redirect/create-package-json.ts b/task/build/package/create-package-json.ts similarity index 80% rename from task/build/redirect/create-package-json.ts rename to task/build/package/create-package-json.ts index d25cc61c4..4113a763c 100644 --- a/task/build/redirect/create-package-json.ts +++ b/task/build/package/create-package-json.ts @@ -48,13 +48,12 @@ function resolvePackageJson(submodules: string[]) { function resolveSubmoduleExports(submodule: string) { return { require: { - types: `./build/require/${submodule}/index.d.ts`, - default: `./build/require/${submodule}/index.js`, + types: `./build/cjs/${submodule}/index.d.ts`, + default: `./build/cjs/${submodule}/index.js`, }, import: { - types: `./build/import/${submodule}/index.d.mts`, - default: `./build/import/${submodule}/index.mjs`, - + types: `./build/esm/${submodule}/index.d.mts`, + default: `./build/esm/${submodule}/index.mjs`, } } } @@ -66,13 +65,13 @@ function resolveExports(submodules: string[]) { // ... and root module ".": { "require": { - "types": "./build/require/index.d.ts", - "default": "./build/require/index.js", + "types": "./build/cjs/index.d.ts", + "default": "./build/cjs/index.js", }, "import": { - "types": "./build/import/index.d.mts", - "default": "./build/import/index.mjs", + "types": "./build/esm/index.d.mts", + "default": "./build/esm/index.mjs", } } }) @@ -90,9 +89,12 @@ function resolveMetadata() { author: packageJson.author, license: packageJson.license, repository: packageJson.repository, - scripts: { test: 'echo test' }, // flagged by socket.dev - types: "./build/require/index.d.ts", - main: "./build/require/index.js", - module: "./build/import/index.mjs", + // flagged by socket.dev if not present + scripts: { test: 'echo test' }, + // disable auto bundle strategy: see https://github.com/esm-dev/esm.sh#bundling-strategy + 'esm.sh': { 'bundle': false }, + types: "./build/cjs/index.d.ts", + main: "./build/cjs/index.js", + module: "./build/esm/index.mjs" } } From e21b8746303fc0e3ef738dd1d1382af9927554c9 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 3 May 2024 21:40:44 +0900 Subject: [PATCH 258/369] Revision 0.32.28 (#861) * Fix Encode Property Check * Test Case for Nested Transform Encode * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/transform/encode.ts | 4 ++-- test/runtime/value/transform/object.ts | 22 ++++++++++++++++++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e69929a7..120d4b5de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.27", + "version": "0.32.28", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.27", + "version": "0.32.28", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 16ed2818e..f84806a1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.27", + "version": "0.32.28", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index be1a507b9..80ee98562 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -122,10 +122,10 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any) // prettier-ignore function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) - if (!IsStandardObject(value)) return defaulted + if (!IsStandardObject(defaulted)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = { ...defaulted } as Record - for(const key of knownKeys) if(key in value) { + for(const key of knownKeys) if(key in knownProperties) { knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key]) } if (!IsSchema(schema.additionalProperties)) { diff --git a/test/runtime/value/transform/object.ts b/test/runtime/value/transform/object.ts index 0a73a998e..db5119127 100644 --- a/test/runtime/value/transform/object.ts +++ b/test/runtime/value/transform/object.ts @@ -151,4 +151,26 @@ describe('value/transform/Object', () => { it('Should throw on map decode', () => { Assert.Throws(() => Encoder.Decode(T5, {})) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/859 + // ---------------------------------------------------------------- + // prettier-ignore + it('Should decode for nested transform with renamed property', () => { + class User { constructor(public name: string, public createdAt: Date) {} } + const TDate = Type.Transform(Type.Number()) + .Decode(v => new Date(v)) + .Encode(v => v.getTime()) + const TUser = Type.Transform(Type.Object({ + name: Type.String(), + created_at: TDate + })) + .Decode(v => new User(v.name, v.created_at)) + .Encode(v => ({ name: v.name, created_at: v.createdAt })) + + const D = Value.Decode(TUser, { name: 'name', created_at: 0 }) + const E = Value.Encode(TUser, D) + + Assert.IsEqual(E.name, 'name') + Assert.IsEqual(E.created_at, 0) + }) }) From 7a42aeef5bb989c07bbfc9acdbd9d74b3febed05 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 5 May 2024 05:45:40 +0900 Subject: [PATCH 259/369] Revision 0.32.29 (#862) * Optimize Intersect Encode and Decode * Version --- package-lock.json | 4 +-- package.json | 2 +- src/type/keyof/index.ts | 1 + src/type/keyof/keyof-property-entries.ts | 42 ++++++++++++++++++++++++ src/value/transform/decode.ts | 9 ++--- src/value/transform/encode.ts | 9 ++--- src/value/value/value.ts | 6 ++-- 7 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 src/type/keyof/keyof-property-entries.ts diff --git a/package-lock.json b/package-lock.json index 120d4b5de..28bd5810f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f84806a1b..e8e4708ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index ecaa5e169..67512251b 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -27,5 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './keyof-from-mapped-result' +export * from './keyof-property-entries' export * from './keyof-property-keys' export * from './keyof' diff --git a/src/type/keyof/keyof-property-entries.ts b/src/type/keyof/keyof-property-entries.ts new file mode 100644 index 000000000..f8eb236f9 --- /dev/null +++ b/src/type/keyof/keyof-property-entries.ts @@ -0,0 +1,42 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IndexFromPropertyKeys } from '../indexed/indexed' +import { KeyOfPropertyKeys } from './keyof-property-keys' +import { TSchema } from '../schema/index' + +/** + * `[Utility]` Resolves an array of keys and schemas from the given schema. This method is faster + * than obtaining the keys and resolving each individually via indexing. This method was written + * accellerate Intersect and Union encoding. + */ +export function KeyOfPropertyEntries(schema: TSchema): [key: string, schema: TSchema][] { + const keys = KeyOfPropertyKeys(schema) as string[] + const schemas = IndexFromPropertyKeys(schema, keys) + return keys.map((_, index) => [keys[index], schemas[index]]) +} diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 00edd6d98..e6ffb9a5a 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' -import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -98,10 +98,11 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, path, value) - const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownEntries = KeyOfPropertyEntries(schema) + const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...value } as Record - for(const key of knownKeys) if(key in knownProperties) { - knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) { + knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey]) } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 80ee98562..39a43cf9e 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' -import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -99,10 +99,11 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) if (!IsStandardObject(value) || IsValueType(value)) return defaulted - const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownEntries = KeyOfPropertyEntries(schema) + const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...defaulted } as Record - for(const key of knownKeys) if(key in knownProperties) { - knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) { + knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey]) } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 1228fa827..944d27d60 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' +import { HasTransform, TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' import { Mutate as MutateValue, type Mutable } from '../mutate/index' import { Hash as HashValue } from '../hash/index' import { Equal as EqualValue } from '../equal/index' @@ -95,7 +95,7 @@ export function Decode>(schema: T, value: export function Decode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return TransformDecode(schema, references, value) + return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value } /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown @@ -112,7 +112,7 @@ export function Encode>(schema: T, value: /** Encodes a value or throws if error */ export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = TransformEncode(schema, references, value) + const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) return encoded } From 24c8639fbf3449dc540a3994d09e15e7757dd536 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 11 May 2024 18:14:22 +0900 Subject: [PATCH 260/369] Revision 0.32.30 (#868) * Support Encode Decode for Null Prototype * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/guard/guard.ts | 2 +- test/runtime/value/guard/guard.ts | 4 ++++ test/runtime/value/transform/object.ts | 17 ++++++++++++++++- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 28bd5810f..ee68e061f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.29", + "version": "0.32.30", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.29", + "version": "0.32.30", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e8e4708ef..af3523404 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.29", + "version": "0.32.30", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 14c575dca..942560b78 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -61,7 +61,7 @@ export function IsIterator(value: unknown): value is IterableIterator { // -------------------------------------------------------------------------- /** Returns true if this value is not an instance of a class */ export function IsStandardObject(value: unknown): value is ObjectType { - return IsObject(value) && !IsArray(value) && IsFunction(value.constructor) && value.constructor.name === 'Object' + return IsObject(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) } /** Returns true if this value is an instance of a class */ export function IsInstanceObject(value: unknown): value is ObjectType { diff --git a/test/runtime/value/guard/guard.ts b/test/runtime/value/guard/guard.ts index cd775097c..88aaec6f2 100644 --- a/test/runtime/value/guard/guard.ts +++ b/test/runtime/value/guard/guard.ts @@ -197,6 +197,10 @@ describe('value/guard/ValueGuard', () => { const R = ValueGuard.IsStandardObject(new (class {})()) Assert.IsFalse(R) }) + it('Should guard StandardObject 5', () => { + const R = ValueGuard.IsStandardObject(Object.create(null)) + Assert.IsTrue(R) + }) // ----------------------------------------------------- // IsInstanceObject // ----------------------------------------------------- diff --git a/test/runtime/value/transform/object.ts b/test/runtime/value/transform/object.ts index db5119127..9425e4de6 100644 --- a/test/runtime/value/transform/object.ts +++ b/test/runtime/value/transform/object.ts @@ -3,6 +3,7 @@ import { Assert } from '../../assert' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' +// prettier-ignore describe('value/transform/Object', () => { // -------------------------------------------------------- // Identity @@ -154,7 +155,6 @@ describe('value/transform/Object', () => { // ---------------------------------------------------------------- // https://github.com/sinclairzx81/typebox/issues/859 // ---------------------------------------------------------------- - // prettier-ignore it('Should decode for nested transform with renamed property', () => { class User { constructor(public name: string, public createdAt: Date) {} } const TDate = Type.Transform(Type.Number()) @@ -173,4 +173,19 @@ describe('value/transform/Object', () => { Assert.IsEqual(E.name, 'name') Assert.IsEqual(E.created_at, 0) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/865 + // ---------------------------------------------------------------- + it('Should decode for null prototype', () => { + const N = Type.Transform(Type.Number()) + .Decode(value => value.toString()) + .Encode(value => parseInt(value)) + const T = Type.Object({ x: N }) + const A = Object.create(null); A.x = 1 + const B = Object.create(null); B.x = '1' + const D = Value.Decode(T, A) + const E = Value.Encode(T, B) + Assert.IsEqual(D, { x: '1' }) + Assert.IsEqual(E, { x: 1 }) + }) }) From 95d5a3e5a86110c4690b470d77e6330e8bcb9e91 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 24 May 2024 13:56:58 +0900 Subject: [PATCH 261/369] Revision 0.32.31 (#881) * Deref Union Variants on Cast * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/cast/cast.ts | 5 +++-- src/value/deref/deref.ts | 15 +++++++++++---- test/runtime/value/cast/union.ts | 16 ++++++++++++++++ 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee68e061f..33b9131a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index af3523404..8ae41d115 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.30", + "version": "0.32.31", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 999e9379a..c3313f7b1 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -80,8 +80,9 @@ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number } } function SelectUnion(union: TUnion, references: TSchema[], value: any): TSchema { - let [select, best] = [union.anyOf[0], 0] - for (const schema of union.anyOf) { + const schemas = union.anyOf.map((schema) => Deref(schema, references)) + let [select, best] = [schemas[0], 0] + for (const schema of schemas) { const score = ScoreUnion(schema, references, value) if (score > best) { select = schema diff --git a/src/value/deref/deref.ts b/src/value/deref/deref.ts index c51fc23f7..91a77915c 100644 --- a/src/value/deref/deref.ts +++ b/src/value/deref/deref.ts @@ -30,15 +30,22 @@ import type { TSchema } from '../../type/schema/index' import type { TRef } from '../../type/ref/index' import type { TThis } from '../../type/recursive/index' import { TypeBoxError } from '../../type/error/index' +import { Kind } from '../../type/symbols/index' export class TypeDereferenceError extends TypeBoxError { constructor(public readonly schema: TRef | TThis) { super(`Unable to dereference schema with $id '${schema.$id}'`) } } +function Resolve(schema: TThis | TRef, references: TSchema[]): TSchema { + const target = references.find((target) => target.$id === schema.$ref) + if (target === undefined) throw new TypeDereferenceError(schema) + return Deref(target, references) +} /** Dereferences a schema from the references array or throws if not found */ -export function Deref(schema: TRef | TThis, references: TSchema[]): TSchema { - const index = references.findIndex((target) => target.$id === schema.$ref) - if (index === -1) throw new TypeDereferenceError(schema) - return references[index] +export function Deref(schema: TSchema, references: TSchema[]): TSchema { + // prettier-ignore + return (schema[Kind] === 'This' || schema[Kind] === 'Ref') + ? Resolve(schema as never, references) + : schema } diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 33d77b41d..d9ae73020 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -145,4 +145,20 @@ describe('value/cast/Union', () => { value: 'B', }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/880 + // ---------------------------------------------------------------- + // prettier-ignore + it('Should dereference union variants', () => { + const A = Type.Object({ type: Type.Literal('A') }, { $id: 'A' }) + const B = Type.Object({ type: Type.Literal('B'), value: Type.Number() }, { $id: 'B' }) + const RA = Type.Union([A, B]) + const RB = Type.Union([Type.Ref(A), Type.Ref(B)]) + // variant 0 + Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'B' }), { type: 'B', value: 0 }) + Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'B' }), { type: 'B', value: 0 }) + // variant 1 + Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'A' }), { type: 'A' }) + Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'A' }), { type: 'A' }) + }) }) From 7ef8e833d53d12639b0bf606308f6e3e886bff74 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 13 Jun 2024 05:22:25 +0900 Subject: [PATCH 262/369] Revision 0.32.32 (#898) * Fix Mapped Enum * Test Case * Version --- package-lock.json | 293 ++++++++++---------------------------- package.json | 4 +- src/type/mapped/mapped.ts | 27 ++-- test/static/mapped.ts | 13 ++ 4 files changed, 104 insertions(+), 233 deletions(-) diff --git a/package-lock.json b/package-lock.json index 33b9131a7..aa5289c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.31", + "version": "0.32.32", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.31", + "version": "0.32.32", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -15,7 +15,7 @@ "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "mocha": "^9.2.2", + "mocha": "^10.4.0", "prettier": "^2.7.1", "typescript": "^5.4.5" } @@ -141,12 +141,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -269,22 +263,21 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -447,16 +440,10 @@ "node": ">=14" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "dependencies": { "ms": "2.1.2" @@ -912,9 +899,9 @@ "dev": true }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -978,20 +965,20 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1009,27 +996,6 @@ "node": ">= 6" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1052,6 +1018,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1136,12 +1103,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1248,58 +1209,50 @@ } }, "node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" + "node": ">= 14.0.0" } }, "node_modules/ms": { @@ -1308,18 +1261,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/node-emoji": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", @@ -1392,15 +1333,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1692,25 +1624,10 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "node_modules/wrap-ansi": { @@ -1893,12 +1810,6 @@ "undici-types": "~5.26.4" } }, - "@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -1985,22 +1896,21 @@ "dev": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-stdout": { @@ -2119,16 +2029,10 @@ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, "requires": { "ms": "2.1.2" @@ -2366,9 +2270,9 @@ "dev": true }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -2410,28 +2314,16 @@ "dev": true }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } + "minimatch": "^5.0.1", + "once": "^1.3.0" } }, "glob-parent": { @@ -2443,12 +2335,6 @@ "is-glob": "^4.0.1" } }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2525,12 +2411,6 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2603,41 +2483,37 @@ } }, "minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" } }, "mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "requires": { - "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", - "debug": "4.3.3", + "debug": "4.3.4", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "4.2.1", + "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", + "workerpool": "6.2.1", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -2649,12 +2525,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true - }, "node-emoji": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", @@ -2706,12 +2576,6 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2909,19 +2773,10 @@ "builtins": "^5.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, "workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 8ae41d115..5067d04fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.31", + "version": "0.32.32", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -35,7 +35,7 @@ "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "mocha": "^9.2.2", + "mocha": "^10.4.0", "prettier": "^2.7.1", "typescript": "^5.4.5" } diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 77c7d37a7..797679b9c 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -35,6 +35,7 @@ import { Discard } from '../discard/index' import { Array, type TArray } from '../array/index' import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' import { Constructor, type TConstructor } from '../constructor/index' +import { type TEnum, type TEnumRecord } from '../enum/index' import { Function as FunctionType, type TFunction } from '../function/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Intersect, type TIntersect } from '../intersect/index' @@ -182,22 +183,24 @@ function FromProperties(K: K, T: T // prettier-ignore type FromSchemaType = ( // unevaluated modifier types - T extends TReadonly ? TReadonly> : - T extends TOptional ? TOptional> : + T extends TReadonly ? TReadonly> : + T extends TOptional ? TOptional> : // unevaluated mapped types - T extends TMappedResult ? TFromMappedResult : - T extends TMappedKey ? TFromMappedKey : + T extends TMappedResult ? TFromMappedResult : + T extends TMappedKey ? TFromMappedKey : // unevaluated types T extends TConstructor ? TConstructor, FromSchemaType> : T extends TFunction ? TFunction, FromSchemaType> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TIterator ? TIterator> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TTuple ? TTuple> : - T extends TObject ? TObject> : - T extends TArray ? TArray> : - T extends TPromise ? TPromise> : + T extends TAsyncIterator ? TAsyncIterator> : + T extends TIterator ? TIterator> : + T extends TIntersect ? TIntersect> : + // note: special case for enums as these are mapped as union types. + T extends TEnum ? TEnum : + T extends TUnion ? TUnion> : + T extends TTuple ? TTuple> : + T extends TObject ? TObject> : + T extends TArray ? TArray> : + T extends TPromise ? TPromise> : T ) // prettier-ignore diff --git a/test/static/mapped.ts b/test/static/mapped.ts index 9f29b5716..1b57668b9 100644 --- a/test/static/mapped.ts +++ b/test/static/mapped.ts @@ -402,3 +402,16 @@ Expect(T).ToStatic // ok y: { y: number } }> } +// ------------------------------------------------------------------ +// With Enum +// issue: https://github.com/sinclairzx81/typebox/issues/897 +// ------------------------------------------------------------------ +{ + enum E { + A, + B, + } + const T = Type.Object({ a: Type.Enum(E) }) + const M = Type.Mapped(Type.KeyOf(T), (K) => Type.Index(T, K)) + Expect(M).ToStatic<{ a: E }> +} From f065cfc6d1e3e506396e9314e96701d9606c50f8 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 17 Jun 2024 19:42:53 +0900 Subject: [PATCH 263/369] Revision 0.32.33 (#905) * Use ES2020 Target * Version --- package-lock.json | 4 ++-- package.json | 2 +- task/build/esm/compile.ts | 2 +- tsconfig.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa5289c4c..5fa9b2127 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.32", + "version": "0.32.33", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.32", + "version": "0.32.33", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 5067d04fc..eeb755cc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.32", + "version": "0.32.33", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/task/build/esm/compile.ts b/task/build/esm/compile.ts index e8beb64a5..08e0129e5 100644 --- a/task/build/esm/compile.ts +++ b/task/build/esm/compile.ts @@ -32,7 +32,7 @@ declare function shell(command: string): Promise export async function compile(target: string) { const options = [ `--outDir ${target}`, - '--target ESNext', + '--target ES2020', '--module ESNext', '--declaration', ].join(' ') diff --git a/tsconfig.json b/tsconfig.json index 6a47a31ec..7ba7f03ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "strict": true, - "target": "ESNext", + "target": "ES2020", "module": "ESNext", "moduleResolution": "Node", "baseUrl": ".", From 83e05bb43ca79cb22c9efc27e2e16caf7cb6a0e5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 22 Jun 2024 16:20:08 +0900 Subject: [PATCH 264/369] Revision 0.32.34 (#914) * Fix Embedded TemplateLiteral Generation * TypeScript 5.5.2 * Version --- hammer.mjs | 2 +- package-lock.json | 18 ++++----- package.json | 4 +- src/type/template-literal/generate.ts | 1 + .../type/guard/type/template-literal.ts | 37 ++++++++++++++++++- test/static/template-literal.ts | 15 ++++++++ 6 files changed, 64 insertions(+), 13 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index d936b2034..ef5d2b894 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 5fa9b2127..443474f51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.33", + "version": "0.32.34", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.33", + "version": "0.32.34", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.4.5" + "typescript": "^5.5.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index eeb755cc5..0afff5aa8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.33", + "version": "0.32.34", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.4.5" + "typescript": "^5.5.2" } } diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts index 9bc857fd7..22d87a99a 100644 --- a/src/type/template-literal/generate.ts +++ b/src/type/template-literal/generate.ts @@ -83,6 +83,7 @@ type TFromTemplateLiteralUnionKinds = type TFromTemplateLiteralKinds = T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] ? ( + L extends TTemplateLiteral ? TFromTemplateLiteralKinds<[...S, ...R], Acc> : L extends TLiteral ? TFromTemplateLiteralKinds : L extends TUnion ? TFromTemplateLiteralKinds]> : L extends TBoolean ? TFromTemplateLiteralKinds : diff --git a/test/runtime/type/guard/type/template-literal.ts b/test/runtime/type/guard/type/template-literal.ts index 7b0f98ce4..63fb736aa 100644 --- a/test/runtime/type/guard/type/template-literal.ts +++ b/test/runtime/type/guard/type/template-literal.ts @@ -1,5 +1,5 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, TemplateLiteralGenerate } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TTemplateLiteral', () => { @@ -44,4 +44,39 @@ describe('guard/type/TTemplateLiteral', () => { T.pattern = T.pattern.slice(0, T.pattern.length - 1) Assert.IsFalse(TypeGuard.IsTemplateLiteral(T)) }) + // ---------------------------------------------------------------- + // issue: https://github.com/sinclairzx81/typebox/issues/913 + // ---------------------------------------------------------------- + it('Should generate embedded template literal 1', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])]) + const L = Type.TemplateLiteral([Type.Literal('KEY'), A, B]) + const T = TemplateLiteralGenerate(L) + Assert.IsEqual(T, ['KEYAX', 'KEYAY', 'KEYBX', 'KEYBY']) + }) + it('Should generate embedded template literal 2', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])]) + const L = Type.TemplateLiteral([A, Type.Literal('KEY'), B]) + const T = TemplateLiteralGenerate(L) + Assert.IsEqual(T, ['AKEYX', 'AKEYY', 'BKEYX', 'BKEYY']) + }) + it('Should generate embedded template literal 3', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])]) + const L = Type.TemplateLiteral([A, B, Type.Literal('KEY')]) + const T = TemplateLiteralGenerate(L) + Assert.IsEqual(T, ['AXKEY', 'AYKEY', 'BXKEY', 'BYKEY']) + }) + it('Should map embedded template literal', () => { + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])]) + const L = Type.TemplateLiteral([Type.Literal('KEY'), A, B]) + const T = Type.Mapped(L, (K) => Type.Null()) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNull(T.properties.KEYAX)) + Assert.IsTrue(TypeGuard.IsNull(T.properties.KEYAY)) + Assert.IsTrue(TypeGuard.IsNull(T.properties.KEYBX)) + Assert.IsTrue(TypeGuard.IsNull(T.properties.KEYBY)) + }) }) diff --git a/test/static/template-literal.ts b/test/static/template-literal.ts index 773ee1fa1..574d0e949 100644 --- a/test/static/template-literal.ts +++ b/test/static/template-literal.ts @@ -112,3 +112,18 @@ import { Type } from '@sinclair/typebox' '$propAx}Yx' | '$propBx}Yx' | '$propCx}Yx' >() } +// --------------------------------------------------------------------- +// issue: https://github.com/sinclairzx81/typebox/issues/913 +// --------------------------------------------------------------------- +{ + const A = Type.TemplateLiteral([Type.Union([Type.Literal('A'), Type.Literal('B')])]) + const B = Type.TemplateLiteral([Type.Union([Type.Literal('X'), Type.Literal('Y')])]) + const L = Type.TemplateLiteral([Type.Literal('KEY'), A, B]) + const T = Type.Mapped(L, (K) => Type.Null()) + Expect(T).ToStatic<{ + KEYAX: null + KEYAY: null + KEYBX: null + KEYBY: null + }>() +} From e686997fcdfaec286520af7e3adecf685805e2e3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 23 Jul 2024 03:08:57 +0900 Subject: [PATCH 265/369] Revision 0.32.35 (#932) * Support Any and Never Record Keys * Update Require Property Message * Ensure Record Enumerable on Convert * TypeScript 5.5.3 * Version --- hammer.mjs | 2 +- package-lock.json | 18 ++++---- package.json | 4 +- src/errors/function.ts | 2 +- src/type/patterns/patterns.ts | 2 + src/type/record/record.ts | 31 +++++++++++++- src/value/convert/convert.ts | 2 + test/runtime/compiler-ajv/record.ts | 52 +++++++++++++++++++++++ test/runtime/compiler/record.ts | 52 +++++++++++++++++++++++ test/runtime/type/guard/kind/record.ts | 26 ++++++++++-- test/runtime/type/guard/type/record.ts | 28 +++++++++++-- test/runtime/value/check/record.ts | 58 ++++++++++++++++++++++++++ test/runtime/value/convert/record.ts | 33 +++++++++++++++ test/static/record.ts | 13 ++++++ 14 files changed, 302 insertions(+), 21 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index ef5d2b894..20af660ca 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 443474f51..f75983e2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.34", + "version": "0.32.35", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.34", + "version": "0.32.35", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.2" + "typescript": "^5.5.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 0afff5aa8..967cc46e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.34", + "version": "0.32.35", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.2" + "typescript": "^5.5.3" } } diff --git a/src/errors/function.ts b/src/errors/function.ts index 8bb1605dd..94504c5ad 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -124,7 +124,7 @@ export function DefaultErrorFunction(error: ErrorFunctionParameter) { case ValueErrorType.ObjectMinProperties: return `Expected object to have at least ${error.schema.minProperties} properties` case ValueErrorType.ObjectRequiredProperty: - return 'Required property' + return 'Expected required property' case ValueErrorType.Promise: return 'Expected Promise' case ValueErrorType.RegExp: diff --git a/src/type/patterns/patterns.ts b/src/type/patterns/patterns.ts index 016d60424..9c9e910a2 100644 --- a/src/type/patterns/patterns.ts +++ b/src/type/patterns/patterns.ts @@ -29,6 +29,8 @@ THE SOFTWARE. export const PatternBoolean = '(true|false)' export const PatternNumber = '(0|[1-9][0-9]*)' export const PatternString = '(.*)' +export const PatternNever = '(?!.*)' export const PatternBooleanExact = `^${PatternBoolean}$` export const PatternNumberExact = `^${PatternNumber}$` export const PatternStringExact = `^${PatternString}$` +export const PatternNeverExact = `^${PatternNever}$` diff --git a/src/type/record/record.ts b/src/type/record/record.ts index e94902712..cc6e9e7b8 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -29,6 +29,7 @@ THE SOFTWARE. import type { TSchema } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' +import { type TAny } from '../any/index' import { Object, type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions } from '../object/index' import { type TLiteral, type TLiteralValue } from '../literal/index' import { Never, type TNever } from '../never/index' @@ -40,7 +41,7 @@ import { type TNumber } from '../number/index' import { type TEnum } from '../enum/index' import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLiteral } from '../template-literal/index' -import { PatternStringExact, PatternNumberExact } from '../patterns/index' +import { PatternStringExact, PatternNumberExact, PatternNeverExact } from '../patterns/index' import { IndexPropertyKeys } from '../indexed/index' import { Kind, Hint } from '../symbols/index' import { CloneType } from '../clone/type' @@ -51,7 +52,7 @@ import { IsUndefined } from '../guard/value' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsInteger, IsLiteral, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/kind' +import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/kind' // ------------------------------------------------------------------ // RecordCreateFromPattern // ------------------------------------------------------------------ @@ -156,6 +157,28 @@ function FromStringKey(K: K, T: T, options return RecordCreateFromPattern(pattern, T, options) as never } // ------------------------------------------------------------------ +// FromAnyKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAnyKey<_ extends TAny, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromAnyKey(K: K, T: T, options: ObjectOptions): TFromAnyKey { + return RecordCreateFromPattern(PatternStringExact, T, options) as never +} +// ------------------------------------------------------------------ +// FromNeverKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromNeverKey<_ extends TNever, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromNeverKey(K: K, T: T, options: ObjectOptions): TFromNeverKey { + return RecordCreateFromPattern(PatternNeverExact, T, options) as never +} +// ------------------------------------------------------------------ // FromIntegerKey // ------------------------------------------------------------------ // prettier-ignore @@ -205,6 +228,8 @@ export type TRecordOrObject = K extends TNumber ? TFromNumberKey : K extends TRegExp ? TFromRegExpKey : K extends TString ? TFromStringKey : + K extends TAny ? TFromAnyKey : + K extends TNever ? TFromNeverKey : TNever // ------------------------------------------------------------------ // TRecordOrObject @@ -220,6 +245,8 @@ export function Record(K: K, T: T, options IsNumber(K) ? FromNumberKey(K, T, options) : IsRegExp(K) ? FromRegExpKey(K, T, options) : IsString(K) ? FromStringKey(K, T, options) : + IsAny(K) ? FromAnyKey(K, T, options) : + IsNever(K) ? FromNeverKey(K, T, options) : Never(options) ) as never } diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 9d8191342..21d1dbbcc 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -203,6 +203,8 @@ function FromObject(schema: TObject, references: TSchema[], value: any): unknown return result } function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown { + const isConvertable = IsObject(value) + if (!isConvertable) return value const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] const result = {} as Record diff --git a/test/runtime/compiler-ajv/record.ts b/test/runtime/compiler-ajv/record.ts index 75b739b9c..0dcba8f8f 100644 --- a/test/runtime/compiler-ajv/record.ts +++ b/test/runtime/compiler-ajv/record.ts @@ -238,4 +238,56 @@ describe('compiler-ajv/Record', () => { key2: 1, }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/916 + // ---------------------------------------------------------------- + it('Should validate for string keys', () => { + const T = Type.Record(Type.String(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) + it('Should validate for number keys', () => { + const T = Type.Record(Type.Number(), Type.Null(), { + additionalProperties: false, + }) + Fail(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + Ok(T, { + 0: null, + 1: null, + }) + }) + it('Should validate for any keys', () => { + const T = Type.Record(Type.Any(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) + it('Should validate for never keys', () => { + const T = Type.Record(Type.Never(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, {}) + Fail(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) }) diff --git a/test/runtime/compiler/record.ts b/test/runtime/compiler/record.ts index 8b7cfbb65..af4417ba8 100644 --- a/test/runtime/compiler/record.ts +++ b/test/runtime/compiler/record.ts @@ -269,4 +269,56 @@ describe('compiler/Record', () => { key2: 1, }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/916 + // ---------------------------------------------------------------- + it('Should validate for string keys', () => { + const T = Type.Record(Type.String(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) + it('Should validate for number keys', () => { + const T = Type.Record(Type.Number(), Type.Null(), { + additionalProperties: false, + }) + Fail(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + Ok(T, { + 0: null, + 1: null, + }) + }) + it('Should validate for any keys', () => { + const T = Type.Record(Type.Any(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) + it('Should validate for never keys', () => { + const T = Type.Record(Type.Never(), Type.Null(), { + additionalProperties: false, + }) + Ok(T, {}) + Fail(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + }) }) diff --git a/test/runtime/type/guard/kind/record.ts b/test/runtime/type/guard/kind/record.ts index 12365ee3b..85ba4e96e 100644 --- a/test/runtime/type/guard/kind/record.ts +++ b/test/runtime/type/guard/kind/record.ts @@ -1,4 +1,4 @@ -import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNeverExact } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' @@ -21,8 +21,11 @@ describe('guard/kind/TRecord', () => { }) it('Should guard overload 3', () => { // @ts-ignore - const T = Type.Record(Type.Union([]), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.IsNever(T)) + const N = Type.Union([]) // Never + const T = Type.Record(N, Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNeverExact])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 4', () => { // @ts-ignore @@ -89,6 +92,23 @@ describe('guard/kind/TRecord', () => { Assert.IsTrue(TypeGuard.IsNull(R.properties.Y)) Assert.IsTrue(TypeGuard.IsNull(R.properties.Z)) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/916 + // ---------------------------------------------------------------- + it('Should guard overload 13', () => { + // @ts-ignore + const T = Type.Record(Type.Never(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNeverExact])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 14', () => { + // @ts-ignore + const T = Type.Record(Type.Any(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternStringExact])) + Assert.IsEqual(T.extra, 1) + }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- diff --git a/test/runtime/type/guard/type/record.ts b/test/runtime/type/guard/type/record.ts index 14729961c..38c7d1bc4 100644 --- a/test/runtime/type/guard/type/record.ts +++ b/test/runtime/type/guard/type/record.ts @@ -1,4 +1,4 @@ -import { TypeGuard, PatternNumberExact, PatternStringExact, PatternString, PatternNumber } from '@sinclair/typebox' +import { TypeGuard, PatternNumberExact, PatternStringExact, PatternNeverExact, PatternString, PatternNumber } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' @@ -21,8 +21,11 @@ describe('guard/type/TRecord', () => { }) it('Should guard overload 3', () => { // @ts-ignore - const T = Type.Record(Type.Union([]), Type.String(), { extra: 1 }) - Assert.IsTrue(TypeGuard.IsNever(T)) + const N = Type.Union([]) // Never + const T = Type.Record(N, Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNeverExact])) + Assert.IsEqual(T.extra, 1) }) it('Should guard overload 4', () => { // @ts-ignore @@ -89,6 +92,25 @@ describe('guard/type/TRecord', () => { Assert.IsTrue(TypeGuard.IsNull(R.properties.Y)) Assert.IsTrue(TypeGuard.IsNull(R.properties.Z)) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/916 + // + // Added overload for Any and Never Keys + // ---------------------------------------------------------------- + it('Should guard overload 13', () => { + // @ts-ignore + const T = Type.Record(Type.Never(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternNeverExact])) + Assert.IsEqual(T.extra, 1) + }) + it('Should guard overload 14', () => { + // @ts-ignore + const T = Type.Record(Type.Any(), Type.String(), { extra: 1 }) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsString(T.patternProperties[PatternStringExact])) + Assert.IsEqual(T.extra, 1) + }) // ------------------------------------------------------------- // Variants // ------------------------------------------------------------- diff --git a/test/runtime/value/check/record.ts b/test/runtime/value/check/record.ts index 2e4bd7362..c4b9c1fd8 100644 --- a/test/runtime/value/check/record.ts +++ b/test/runtime/value/check/record.ts @@ -236,4 +236,62 @@ describe('value/check/Record', () => { const R = Value.Check(T, { 1: '', 2: '', x: true }) Assert.IsEqual(R, true) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/916 + // ---------------------------------------------------------------- + it('Should validate for string keys', () => { + const T = Type.Record(Type.String(), Type.Null(), { + additionalProperties: false, + }) + const R = Value.Check(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + Assert.IsEqual(R, true) + }) + it('Should validate for number keys', () => { + const T = Type.Record(Type.Number(), Type.Null(), { + additionalProperties: false, + }) + const R1 = Value.Check(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + const R2 = Value.Check(T, { + 0: null, + 1: null, + }) + Assert.IsEqual(R1, false) + Assert.IsEqual(R2, true) + }) + it('Should validate for any keys', () => { + const T = Type.Record(Type.Any(), Type.Null(), { + additionalProperties: false, + }) + const R = Value.Check(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + Assert.IsEqual(R, true) + }) + it('Should validate for never keys', () => { + const T = Type.Record(Type.Never(), Type.Null(), { + additionalProperties: false, + }) + const R1 = Value.Check(T, {}) + const R2 = Value.Check(T, { + a: null, + b: null, + 0: null, + 1: null, + }) + Assert.IsEqual(R1, true) + Assert.IsEqual(R2, false) + }) }) diff --git a/test/runtime/value/convert/record.ts b/test/runtime/value/convert/record.ts index 90c24f440..edd3b5a8a 100644 --- a/test/runtime/value/convert/record.ts +++ b/test/runtime/value/convert/record.ts @@ -8,4 +8,37 @@ describe('value/convert/Record', () => { const V = Value.Convert(T, { x: '42', y: '24', z: 'hello' }) Assert.IsEqual(V, { x: 42, y: 24, z: 'hello' }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/930 + // ---------------------------------------------------------------- + it('Should convert record union 1', () => { + const T = Type.Union([Type.Null(), Type.Record(Type.Number(), Type.Any())]) + const V = Value.Convert(T, {}) + Assert.IsEqual(V, {}) + }) + it('Should convert record union 2', () => { + const T = Type.Union([Type.Record(Type.Number(), Type.Any()), Type.Null()]) + const V = Value.Convert(T, {}) + Assert.IsEqual(V, {}) + }) + it('Should convert record union 3', () => { + const T = Type.Union([Type.Null(), Type.Record(Type.Number(), Type.Any())]) + const V = Value.Convert(T, null) + Assert.IsEqual(V, null) + }) + it('Should convert record union 4', () => { + const T = Type.Union([Type.Record(Type.Number(), Type.Any()), Type.Null()]) + const V = Value.Convert(T, null) + Assert.IsEqual(V, null) + }) + it('Should convert record union 5', () => { + const T = Type.Union([Type.Null(), Type.Record(Type.Number(), Type.Any())]) + const V = Value.Convert(T, 'NULL') + Assert.IsEqual(V, null) + }) + it('Should convert record union 6', () => { + const T = Type.Union([Type.Record(Type.Number(), Type.Any()), Type.Null()]) + const V = Value.Convert(T, 'NULL') + Assert.IsEqual(V, null) + }) }) diff --git a/test/static/record.ts b/test/static/record.ts index da8a0288f..ab18b06b4 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -189,3 +189,16 @@ import { Type, Static } from '@sinclair/typebox' '$propC': string }>() } +// ------------------------------------------------------------------ +// https://github.com/sinclairzx81/typebox/issues/916 +// ------------------------------------------------------------------ +{ + const K = Type.Any() + const T = Type.Record(K, Type.String()) + Expect(T).ToStatic>() +} +{ + const K = Type.Never() + const T = Type.Record(K, Type.String()) + Expect(T).ToStatic<{}>() +} From 333c2a149e2f0fa54c5618297139fbce6e43f95a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 7 Aug 2024 22:57:57 +0900 Subject: [PATCH 266/369] Revision 0.33.0 (#941) * Implement Immutable Types * Update TypeScript * Support Clone and Freeze Instancing * Ensure Options Cloned on Mapped Key and Result * Revision 0.33.0 --- hammer.mjs | 2 +- package-lock.json | 18 ++--- package.json | 4 +- src/index.ts | 1 + src/system/policy.ts | 15 +++- src/type/any/any.ts | 5 +- src/type/array/array.ts | 11 +-- src/type/async-iterator/async-iterator.ts | 11 +-- src/type/awaited/awaited.ts | 6 +- src/type/bigint/bigint.ts | 9 +-- src/type/boolean/boolean.ts | 9 +-- src/type/clone/type.ts | 6 +- src/type/composite/composite.ts | 2 +- src/type/const/const.ts | 6 +- .../constructor-parameters.ts | 5 +- src/type/constructor/constructor.ts | 10 +-- src/type/create/immutable.ts | 66 +++++++++++++++++ src/type/create/index.ts | 29 ++++++++ src/type/create/type.ts | 45 ++++++++++++ src/type/date/date.ts | 9 +-- src/type/discard/discard.ts | 1 + src/type/enum/enum.ts | 2 +- src/type/exclude/exclude.ts | 8 +-- src/type/extends/extends-from-mapped-key.ts | 11 +-- .../extends/extends-from-mapped-result.ts | 9 +-- src/type/extends/extends.ts | 8 +-- src/type/extract/extract.ts | 16 ++--- src/type/function/function.ts | 10 +-- src/type/guard/value.ts | 3 + src/type/indexed/indexed-from-mapped-key.ts | 11 +-- .../indexed/indexed-from-mapped-result.ts | 6 +- src/type/indexed/indexed.ts | 18 ++--- src/type/instance-type/instance-type.ts | 6 +- src/type/integer/integer.ts | 9 +-- src/type/intersect/intersect-create.ts | 14 ++-- src/type/intersect/intersect-evaluated.ts | 9 ++- src/type/intersect/intersect.ts | 11 ++- .../intrinsic/intrinsic-from-mapped-key.ts | 7 +- src/type/intrinsic/intrinsic.ts | 8 ++- src/type/iterator/iterator.ts | 11 +-- src/type/keyof/keyof-from-mapped-result.ts | 10 +-- src/type/keyof/keyof.ts | 8 +-- src/type/literal/literal.ts | 17 +++-- src/type/mapped/mapped-key.ts | 5 +- src/type/mapped/mapped-result.ts | 5 +- src/type/mapped/mapped.ts | 6 +- src/type/never/never.ts | 9 +-- src/type/not/not.ts | 10 +-- src/type/null/null.ts | 9 +-- src/type/number/number.ts | 9 +-- src/type/object/object.ts | 25 ++++--- src/type/omit/omit-from-mapped-key.ts | 11 +-- src/type/omit/omit-from-mapped-result.ts | 9 +-- src/type/omit/omit.ts | 8 +-- src/type/optional/optional.ts | 6 +- src/type/parameters/parameters.ts | 5 +- .../partial/partial-from-mapped-result.ts | 9 +-- src/type/partial/partial.ts | 8 +-- src/type/pick/pick-from-mapped-key.ts | 11 +-- src/type/pick/pick-from-mapped-result.ts | 9 +-- src/type/pick/pick.ts | 8 +-- src/type/promise/promise.ts | 11 +-- src/type/readonly/readonly.ts | 6 +- src/type/record/record.ts | 11 ++- src/type/recursive/recursive.ts | 5 +- src/type/ref/ref.ts | 11 ++- src/type/regexp/regexp.ts | 5 +- .../required/required-from-mapped-result.ts | 6 +- src/type/required/required.ts | 12 ++-- src/type/rest/rest.ts | 15 ++-- src/type/return-type/return-type.ts | 6 +- src/type/string/string.ts | 5 +- src/type/symbol/symbol.ts | 3 +- src/type/template-literal/template-literal.ts | 5 +- src/type/transform/transform.ts | 6 +- src/type/tuple/tuple.ts | 14 ++-- src/type/type/javascript.ts | 26 +++---- src/type/type/json.ts | 70 +++++++++---------- src/type/uint8array/uint8array.ts | 5 +- src/type/undefined/undefined.ts | 5 +- src/type/union/union-create.ts | 6 +- src/type/union/union-evaluated.ts | 8 +-- src/type/union/union.ts | 6 +- src/type/unknown/unknown.ts | 8 +-- src/type/unsafe/unsafe.ts | 6 +- src/type/void/void.ts | 9 +-- test/runtime/index.ts | 7 ++ test/runtime/type/guard/type/ref.ts | 4 +- .../type/guard/type/template-literal.ts | 6 +- 89 files changed, 521 insertions(+), 410 deletions(-) create mode 100644 src/type/create/immutable.ts create mode 100644 src/type/create/index.ts create mode 100644 src/type/create/type.ts diff --git a/hammer.mjs b/hammer.mjs index 20af660ca..49faf269b 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index f75983e2a..4b22daec6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.35", + "version": "0.33.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.35", + "version": "0.33.0", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.3" + "typescript": "^5.5.4" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 967cc46e0..bfe38dad3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.35", + "version": "0.33.0", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.3" + "typescript": "^5.5.4" } } diff --git a/src/index.ts b/src/index.ts index dbbe67127..b88ad07ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,7 @@ THE SOFTWARE. // Infrastructure // ------------------------------------------------------------------ export * from './type/clone/index' +export * from './type/create/index' export * from './type/error/index' export * from './type/guard/index' export * from './type/helpers/index' diff --git a/src/system/policy.ts b/src/system/policy.ts index 400c74b16..93a68f809 100644 --- a/src/system/policy.ts +++ b/src/system/policy.ts @@ -30,9 +30,20 @@ import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard/index' export namespace TypeSystemPolicy { // ------------------------------------------------------------------ - // TypeSystemPolicy + // TypeSystemPolicy: Instancing + // ------------------------------------------------------------------ + /** + * Configures the instantiation behavior of TypeBox types. The `default` option assigns raw JavaScript + * references for embedded types, which may cause side effects if type properties are explicitly updated + * outside the TypeBox type builder. The `clone` option creates copies of any shared types upon creation, + * preventing unintended side effects. The `freeze` option applies `Object.freeze()` to the type, making + * it fully readonly and immutable. Implementations should use `default` whenever possible, as it is the + * fastest way to instantiate types. The default setting is `default`. + */ + export let InstanceMode: 'default' | 'clone' | 'freeze' = 'default' + // ------------------------------------------------------------------ + // TypeSystemPolicy: Checking // ------------------------------------------------------------------ - /** Shared assertion routines used by the value and errors modules */ /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ export let ExactOptionalPropertyTypes: boolean = false /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ diff --git a/src/type/any/any.ts b/src/type/any/any.ts index 90eaf93ec..f5d7d907c 100644 --- a/src/type/any/any.ts +++ b/src/type/any/any.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/index' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -35,6 +36,6 @@ export interface TAny extends TSchema { } /** `[Json]` Creates an Any type */ -export function Any(options: SchemaOptions = {}): TAny { - return { ...options, [Kind]: 'Any' } as never +export function Any(options?: SchemaOptions): TAny { + return CreateType({ [Kind]: 'Any' }, options) as never } diff --git a/src/type/array/array.ts b/src/type/array/array.ts index fecf073db..388ba25d7 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' import { Ensure } from '../helpers/index' import type { SchemaOptions, TSchema } from '../schema/index' import type { Static } from '../static/index' @@ -54,11 +54,6 @@ export interface TArray extends TSchema, ArrayOptio items: T } /** `[Json]` Creates an Array type */ -export function Array(schema: T, options: ArrayOptions = {}): TArray { - return { - ...options, - [Kind]: 'Array', - type: 'array', - items: CloneType(schema), - } as never +export function Array(items: T, options?: ArrayOptions): TArray { + return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never } diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts index ee9417421..2a97d73a0 100644 --- a/src/type/async-iterator/async-iterator.ts +++ b/src/type/async-iterator/async-iterator.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import { Kind } from '../symbols/index' -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' export interface TAsyncIterator extends TSchema { [Kind]: 'AsyncIterator' @@ -38,11 +38,6 @@ export interface TAsyncIterator extends TSchema { items: T } /** `[JavaScript]` Creates a AsyncIterator type */ -export function AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { - return { - ...options, - [Kind]: 'AsyncIterator', - type: 'AsyncIterator', - items: CloneType(items), - } as never +export function AsyncIterator(items: T, options?: SchemaOptions): TAsyncIterator { + return CreateType({ [Kind]: 'AsyncIterator', type: 'AsyncIterator', items }, options) as never } diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index 5318d98ea..e129b6a52 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -30,7 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { type TPromise } from '../promise/index' -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' // ------------------------------------------------------------------ // TypeGuard @@ -96,6 +96,6 @@ export type TAwaited = T extends TPromise ? TAwaited : T /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ -export function Awaited(T: T, options: SchemaOptions = {}): TAwaited { - return CloneType(AwaitedResolve(T), options) +export function Awaited(T: T, options?: SchemaOptions): TAwaited { + return CreateType(AwaitedResolve(T), options) as never } diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts index b653a09a2..e98491b2a 100644 --- a/src/type/bigint/bigint.ts +++ b/src/type/bigint/bigint.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' +import { CreateType } from '../create/index' export interface BigIntOptions extends SchemaOptions { exclusiveMaximum?: bigint @@ -42,10 +43,6 @@ export interface TBigInt extends TSchema, BigIntOptions { type: 'bigint' } /** `[JavaScript]` Creates a BigInt type */ -export function BigInt(options: BigIntOptions = {}): TBigInt { - return { - ...options, - [Kind]: 'BigInt', - type: 'bigint', - } as never +export function BigInt(options?: BigIntOptions): TBigInt { + return CreateType({ [Kind]: 'BigInt', type: 'bigint' }, options) as never } diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts index 7729a0052..acbf44207 100644 --- a/src/type/boolean/boolean.ts +++ b/src/type/boolean/boolean.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' +import { CreateType } from '../create/index' export interface TBoolean extends TSchema { [Kind]: 'Boolean' @@ -35,10 +36,6 @@ export interface TBoolean extends TSchema { type: 'boolean' } /** `[Json]` Creates a Boolean type */ -export function Boolean(options: SchemaOptions = {}): TBoolean { - return { - ...options, - [Kind]: 'Boolean', - type: 'boolean', - } as never +export function Boolean(options?: SchemaOptions): TBoolean { + return CreateType({ [Kind]: 'Boolean', type: 'boolean' }, options) as never } diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts index 89b17b2ee..18a10f3ec 100644 --- a/src/type/clone/type.ts +++ b/src/type/clone/type.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TSchema, SchemaOptions } from '../schema/index' +import { TSchema, SchemaOptions } from '../schema/index' import { Clone } from './value' /** Clones a Rest */ @@ -34,6 +34,6 @@ export function CloneRest(schemas: T): T { return schemas.map((schema) => CloneType(schema)) as never } /** Clones a Type */ -export function CloneType(schema: T, options: SchemaOptions = {}): T { - return { ...Clone(schema), ...options } +export function CloneType(schema: T, options?: SchemaOptions): T { + return options === undefined ? Clone(schema) : Clone({ ...options, ...schema }) } diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index c3a6f8084..26b4406b5 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -114,7 +114,7 @@ type TCompositeEvaluate< // prettier-ignore export type TComposite = TCompositeEvaluate // prettier-ignore -export function Composite(T: [...T], options: ObjectOptions = {}): TComposite { +export function Composite(T: [...T], options?: ObjectOptions): TComposite { const K = CompositeKeys(T) const P = CompositeProperties(T, K) const R = Object(P, options) diff --git a/src/type/const/const.ts b/src/type/const/const.ts index 1606b9930..fcaa788a3 100644 --- a/src/type/const/const.ts +++ b/src/type/const/const.ts @@ -44,7 +44,7 @@ import { Readonly, type TReadonly } from '../readonly/index' import { Undefined, type TUndefined } from '../undefined/index' import { Uint8Array, type TUint8Array } from '../uint8array/index' import { Unknown, type TUnknown } from '../unknown/index' -import { CloneType } from '../clone/index' +import { CreateType } from '../create/index' // ------------------------------------------------------------------ // ValueGuard @@ -130,6 +130,6 @@ function FromValue(value: T, root: Root): FromValue = FromValue /** `[JavaScript]` Creates a readonly const type from the given value. */ -export function Const(T: T, options: SchemaOptions = {}): TConst { - return CloneType(FromValue(T, true), options) as never +export function Const(T: T, options?: SchemaOptions): TConst { + return CreateType(FromValue(T, true), options) as never } diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts index 9b00791ab..3ce3e0034 100644 --- a/src/type/constructor-parameters/constructor-parameters.ts +++ b/src/type/constructor-parameters/constructor-parameters.ts @@ -30,7 +30,6 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { Ensure } from '../helpers/index' import type { TConstructor } from '../constructor/index' import { Tuple, type TTuple } from '../tuple/index' -import { CloneRest } from '../clone/type' // ------------------------------------------------------------------ // ConstructorParameters @@ -41,6 +40,6 @@ export type TConstructorParameters> = ) /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ -export function ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { - return Tuple(CloneRest(schema.parameters), { ...options }) +export function ConstructorParameters>(schema: T, options?: SchemaOptions): TConstructorParameters { + return Tuple(schema.parameters, options) } diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index 65f440c32..6904b8541 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -32,7 +32,7 @@ import type { Ensure } from '../helpers/index' import type { TReadonlyOptional } from '../readonly-optional/index' import type { TReadonly } from '../readonly/index' import type { TOptional } from '../optional/index' -import { CloneType, CloneRest } from '../clone/type' +import { CreateType } from '../create/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ @@ -66,11 +66,5 @@ export interface TConstructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - return { - ...options, - [Kind]: 'Constructor', - type: 'Constructor', - parameters: CloneRest(parameters), - returns: CloneType(returns), - } as never + return CreateType({ [Kind]: 'Constructor', type: 'Constructor', parameters, returns }, options) as never } diff --git a/src/type/create/immutable.ts b/src/type/create/immutable.ts new file mode 100644 index 000000000..92ff32e5d --- /dev/null +++ b/src/type/create/immutable.ts @@ -0,0 +1,66 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as ValueGuard from '../guard/value' + +function ImmutableArray(value: unknown[]) { + return globalThis.Object.freeze(value as any).map((value: unknown) => Immutable(value as any)) +} +function ImmutableDate(value: Date) { + return value +} +function ImmutableUint8Array(value: Uint8Array) { + return value +} +function ImmutableRegExp(value: RegExp) { + return value +} +function ImmutableObject(value: Record) { + const result = {} as Record + for (const key of Object.getOwnPropertyNames(value)) { + result[key] = Immutable(value[key]) + } + for (const key of Object.getOwnPropertySymbols(value)) { + result[key] = Immutable(value[key]) + } + return globalThis.Object.freeze(result) +} +/** Specialized deep immutable value. Applies freeze recursively to the given value */ +export function Immutable(value: T): T { + return ValueGuard.IsArray(value) + ? ImmutableArray(value) + : ValueGuard.IsDate(value) + ? ImmutableDate(value) + : ValueGuard.IsUint8Array(value) + ? ImmutableUint8Array(value) + : ValueGuard.IsRegExp(value) + ? ImmutableRegExp(value) + : ValueGuard.IsObject(value) + ? ImmutableObject(value) + : value +} diff --git a/src/type/create/index.ts b/src/type/create/index.ts new file mode 100644 index 000000000..53780a6ef --- /dev/null +++ b/src/type/create/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './type' diff --git a/src/type/create/type.ts b/src/type/create/type.ts new file mode 100644 index 000000000..4d378a109 --- /dev/null +++ b/src/type/create/type.ts @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TypeSystemPolicy } from '../../system/policy' +import { SchemaOptions } from '../schema/schema' +import { Immutable } from './immutable' +import { Clone } from '../clone/value' + +/** Creates TypeBox schematics using the configured InstanceMode */ +export function CreateType(schema: Record, options?: SchemaOptions): unknown { + const result = options !== undefined ? Object.assign(options, schema) : schema + switch (TypeSystemPolicy.InstanceMode) { + case 'freeze': + return Immutable(result) + case 'clone': + return Clone(result) + default: + return result + } +} diff --git a/src/type/date/date.ts b/src/type/date/date.ts index 192cea310..44b9d0d3d 100644 --- a/src/type/date/date.ts +++ b/src/type/date/date.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' +import { CreateType } from '../create/type' export interface DateOptions extends SchemaOptions { /** The exclusive maximum timestamp value */ @@ -47,10 +48,6 @@ export interface TDate extends TSchema, DateOptions { type: 'date' } /** `[JavaScript]` Creates a Date type */ -export function Date(options: DateOptions = {}): TDate { - return { - ...options, - [Kind]: 'Date', - type: 'Date', - } as never +export function Date(options?: DateOptions): TDate { + return CreateType({ [Kind]: 'Date', type: 'Date' }, options) as never } diff --git a/src/type/discard/discard.ts b/src/type/discard/discard.ts index 9e4b98dc3..cf1688ddd 100644 --- a/src/type/discard/discard.ts +++ b/src/type/discard/discard.ts @@ -30,6 +30,7 @@ function DiscardKey(value: Record, key: PropertyKey) { const { [key]: _, ...rest } = value return rest } +/** Discards property keys from the given value. This function returns a shallow Clone. */ export function Discard(value: Record, keys: PropertyKey[]) { return keys.reduce((acc, key) => DiscardKey(acc, key), value) } diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts index 08a87b495..1cd8a5867 100644 --- a/src/type/enum/enum.ts +++ b/src/type/enum/enum.ts @@ -47,7 +47,7 @@ export interface TEnum = Record[] } /** `[Json]` Creates a Enum type */ -export function Enum>(item: T, options: SchemaOptions = {}): TEnum { +export function Enum>(item: T, options?: SchemaOptions): TEnum { if (IsUndefined(item)) throw new Error('Enum undefined or empty') const values1 = globalThis.Object.getOwnPropertyNames(item) .filter((key) => isNaN(key as any)) diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index 3140a5e10..daee3ea1f 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { UnionToTuple, AssertRest, AssertType } from '../helpers/index' import type { TMappedResult } from '../mapped/index' @@ -35,7 +36,6 @@ import { Never, type TNever } from '../never/index' import { type Static } from '../static/index' import { type TUnionEvaluated } from '../union/index' import { ExtendsCheck, ExtendsResult } from '../extends/index' -import { CloneType } from '../clone/type' import { ExcludeFromMappedResult, type TExcludeFromMappedResult } from './exclude-from-mapped-result' import { ExcludeFromTemplateLiteral, type TExcludeFromTemplateLiteral } from './exclude-from-template-literal' @@ -72,10 +72,10 @@ export function Exclude(unionType: L, excl /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ export function Exclude(L: TSchema, R: TSchema, options: SchemaOptions = {}): any { // overloads - if (IsTemplateLiteral(L)) return CloneType(ExcludeFromTemplateLiteral(L, R), options) - if (IsMappedResult(L)) return CloneType(ExcludeFromMappedResult(L, R), options) + if (IsTemplateLiteral(L)) return CreateType(ExcludeFromTemplateLiteral(L, R), options) + if (IsMappedResult(L)) return CreateType(ExcludeFromMappedResult(L, R), options) // prettier-ignore - return CloneType( + return CreateType( IsUnion(L) ? ExcludeRest(L.anyOf, R) : ExtendsCheck(L, R) !== ExtendsResult.False ? Never() : L , options) diff --git a/src/type/extends/extends-from-mapped-key.ts b/src/type/extends/extends-from-mapped-key.ts index ce43b6b60..506ebc854 100644 --- a/src/type/extends/extends-from-mapped-key.ts +++ b/src/type/extends/extends-from-mapped-key.ts @@ -32,6 +32,7 @@ import type { Assert } from '../helpers/index' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Extends, type TExtends } from './extends' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromPropertyKey @@ -51,9 +52,9 @@ function FromPropertyKey< U extends TSchema, L extends TSchema, R extends TSchema ->(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKey { +>(K: K, U: U, L: L, R: R, options?: SchemaOptions): TFromPropertyKey { return { - [K]: Extends(Literal(K as TLiteralValue), U, L, R, options) as any + [K]: Extends(Literal(K as TLiteralValue), U, L, R, Clone(options)) as any } as never } // ------------------------------------------------------------------ @@ -77,7 +78,7 @@ function FromPropertyKeys< U extends TSchema, L extends TSchema, R extends TSchema ->(K: [...K], U: U, L: L, R: R, options: SchemaOptions): TFromPropertyKeys { +>(K: [...K], U: U, L: L, R: R, options?: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(LK, U, L, R, options) } }, {} as TProperties) as never @@ -100,7 +101,7 @@ function FromMappedKey< U extends TSchema, L extends TSchema, R extends TSchema ->(K: K, U: U, L: L, R: R, options: SchemaOptions): TFromMappedKey { +>(K: K, U: U, L: L, R: R, options?: SchemaOptions): TFromMappedKey { return FromPropertyKeys(K.keys, U, L, R, options) as never } // ------------------------------------------------------------------ @@ -123,7 +124,7 @@ export function ExtendsFromMappedKey< L extends TSchema, R extends TSchema, P extends TProperties = TFromMappedKey ->(T: T, U: U, L: L, R: R, options: SchemaOptions): TMappedResult

      { +>(T: T, U: U, L: L, R: R, options?: SchemaOptions): TMappedResult

      { const P = FromMappedKey(T, U, L, R, options) return MappedResult(P) as never } diff --git a/src/type/extends/extends-from-mapped-result.ts b/src/type/extends/extends-from-mapped-result.ts index 2c857cf9d..02a1dc84b 100644 --- a/src/type/extends/extends-from-mapped-result.ts +++ b/src/type/extends/extends-from-mapped-result.ts @@ -30,6 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Extends, type TExtends } from './extends' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties @@ -49,9 +50,9 @@ function FromProperties< Right extends TSchema, True extends TSchema, False extends TSchema ->(P: P, Right: Right, True: True, False: False, options: SchemaOptions): TFromProperties { +>(P: P, Right: Right, True: True, False: False, options?: SchemaOptions): TFromProperties { const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extends(P[K2], Right, True, False, options) + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Extends(P[K2], Right, True, False, Clone(options)) return Acc as never } // ------------------------------------------------------------------ @@ -72,7 +73,7 @@ function FromMappedResult< Right extends TSchema, True extends TSchema, False extends TSchema ->(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TFromMappedResult { +>(Left: Left, Right: Right, True: True, False: False, options?: SchemaOptions): TFromMappedResult { return FromProperties(Left.properties, Right, True, False, options) as never } // ------------------------------------------------------------------ @@ -95,7 +96,7 @@ export function ExtendsFromMappedResult< True extends TSchema, False extends TSchema, P extends TProperties = TFromMappedResult ->(Left: Left, Right: Right, True: True, False: False, options: SchemaOptions): TMappedResult

      { +>(Left: Left, Right: Right, True: True, False: False, options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(Left, Right, True, False, options) return MappedResult(P) as never } diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts index 614722a4f..9048d1eab 100644 --- a/src/type/extends/extends.ts +++ b/src/type/extends/extends.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import { type TUnion, Union } from '../union/index' import { TMappedKey, TMappedResult } from '../mapped/index' import { ExtendsCheck, ExtendsResult } from './extends-check' import { UnionToTuple } from '../helpers/index' -import { CloneType } from '../clone/type' import { ExtendsFromMappedKey, type TExtendsFromMappedKey } from './extends-from-mapped-key' import { ExtendsFromMappedResult, type TExtendsFromMappedResult } from './extends-from-mapped-result' @@ -70,11 +70,11 @@ export function Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtends /** `[Json]` Creates a Conditional type */ -export function Extends(L: L, R: R, T: T, F: F, options: SchemaOptions = {}) { +export function Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions) { // prettier-ignore return ( IsMappedResult(L) ? ExtendsFromMappedResult(L, R, T, F, options) : - IsMappedKey(L) ? CloneType(ExtendsFromMappedKey(L, R, T, F, options)) : - CloneType(ExtendsResolve(L, R, T, F), options) + IsMappedKey(L) ? CreateType(ExtendsFromMappedKey(L, R, T, F, options)) : + CreateType(ExtendsResolve(L, R, T, F), options) ) as never } diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index 91c8ca5e2..8c94045b2 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { AssertRest, AssertType, UnionToTuple } from '../helpers/index' import type { TMappedResult } from '../mapped/index' @@ -35,7 +36,6 @@ import { Never, type TNever } from '../never/index' import { type TUnionEvaluated } from '../union/index' import { type TTemplateLiteral } from '../template-literal/index' import { ExtendsCheck, ExtendsResult } from '../extends/index' -import { CloneType } from '../clone/type' import { ExtractFromMappedResult, type TExtractFromMappedResult } from './extract-from-mapped-result' import { ExtractFromTemplateLiteral, type TExtractFromTemplateLiteral } from './extract-from-template-literal' @@ -50,7 +50,7 @@ import { IsMappedResult, IsTemplateLiteral, IsUnion } from '../guard/kind' // prettier-ignore type TExtractRest = AssertRest> extends Static ? L[K] : never -}[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never + }[number]>> extends infer R extends TSchema[] ? TUnionEvaluated : never function ExtractRest(L: [...L], R: R) { const extracted = L.filter((inner) => ExtendsCheck(inner, R) !== ExtendsResult.False) @@ -71,13 +71,13 @@ export function Extract(type: L, /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ export function Extract(type: L, union: R, options?: SchemaOptions): TExtract /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ -export function Extract(L: TSchema, R: TSchema, options: SchemaOptions = {}): any { +export function Extract(L: TSchema, R: TSchema, options?: SchemaOptions): never { // overloads - if (IsTemplateLiteral(L)) return CloneType(ExtractFromTemplateLiteral(L, R), options) - if (IsMappedResult(L)) return CloneType(ExtractFromMappedResult(L, R), options) + if (IsTemplateLiteral(L)) return CreateType(ExtractFromTemplateLiteral(L, R), options) as never + if (IsMappedResult(L)) return CreateType(ExtractFromMappedResult(L, R), options) as never // prettier-ignore - return CloneType( + return CreateType( IsUnion(L) ? ExtractRest(L.anyOf, R) : - ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never() - , options) + ExtendsCheck(L, R) !== ExtendsResult.False ? L : Never() + , options) as never } diff --git a/src/type/function/function.ts b/src/type/function/function.ts index f4333585c..150fc7338 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import type { Ensure } from '../helpers/index' import type { TReadonlyOptional } from '../readonly-optional/index' import type { TReadonly } from '../readonly/index' import type { TOptional } from '../optional/index' -import { CloneType, CloneRest } from '../clone/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ @@ -67,11 +67,5 @@ export interface TFunction(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { - return { - ...options, - [Kind]: 'Function', - type: 'Function', - parameters: CloneRest(parameters), - returns: CloneType(returns), - } as never + return CreateType({ [Kind]: 'Function', type: 'Function', parameters, returns }, options) as never } diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts index c3ecc85fe..e0f63974a 100644 --- a/src/type/guard/value.ts +++ b/src/type/guard/value.ts @@ -26,6 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +// -------------------------------------------------------------------------- +// Object Instances +// -------------------------------------------------------------------------- /** Returns true if this value is an async iterator */ export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator { return IsObject(value) && !IsArray(value) && !IsUint8Array(value) && Symbol.asyncIterator in value diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index e82b1b5f5..4251cea89 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -31,6 +31,7 @@ import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { Index, type TIndex } from './indexed' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // MappedIndexPropertyKey @@ -46,8 +47,8 @@ type TMappedIndexPropertyKey< function MappedIndexPropertyKey< T extends TSchema, K extends PropertyKey ->(T: T, K: K, options: SchemaOptions): TMappedIndexPropertyKey { - return { [K]: Index(T, [K], options) } as never +>(T: T, K: K, options?: SchemaOptions): TMappedIndexPropertyKey { + return { [K]: Index(T, [K], Clone(options)) } as never } // ------------------------------------------------------------------ // MappedIndexPropertyKeys @@ -62,7 +63,7 @@ type TMappedIndexPropertyKeys(T: T, K: [...K], options: SchemaOptions): TMappedIndexPropertyKeys { +>(T: T, K: [...K], options?: SchemaOptions): TMappedIndexPropertyKeys { return K.reduce((Acc, L) => { return { ...Acc, ...MappedIndexPropertyKey(T, L, options) } }, {} as TProperties) as never @@ -78,7 +79,7 @@ type TMappedIndexProperties = Evaluate< function MappedIndexProperties< T extends TSchema, K extends TMappedKey ->(T: T, K: K, options: SchemaOptions): TMappedIndexProperties { +>(T: T, K: K, options?: SchemaOptions): TMappedIndexProperties { return MappedIndexPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ @@ -97,7 +98,7 @@ export function IndexFromMappedKey< T extends TSchema, K extends TMappedKey, P extends TProperties = TMappedIndexProperties ->(T: T, K: K, options: SchemaOptions): TMappedResult

      { +>(T: T, K: K, options?: SchemaOptions): TMappedResult

      { const P = MappedIndexProperties(T, K, options) return MappedResult(P) as never } diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index f51837c1f..d23fa750d 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -46,7 +46,7 @@ type TFromProperties< function FromProperties< T extends TSchema, P extends TProperties ->(T: T, P: P, options: SchemaOptions): TFromProperties { +>(T: T, P: P, options?: SchemaOptions): TFromProperties { const Acc = {} as Record for(const K2 of Object.getOwnPropertyNames(P)) { Acc[K2] = Index(T, IndexPropertyKeys(P[K2]), options) @@ -67,7 +67,7 @@ type TFromMappedResult< function FromMappedResult< T extends TSchema, R extends TMappedResult ->(T: T, R: R, options: SchemaOptions): TFromMappedResult { +>(T: T, R: R, options?: SchemaOptions): TFromMappedResult { return FromProperties(T, R.properties, options) as never } // ------------------------------------------------------------------ @@ -86,7 +86,7 @@ export function IndexFromMappedResult< T extends TSchema, R extends TMappedResult, P extends TProperties = TFromMappedResult ->(T: T, R: R, options: SchemaOptions): TMappedResult

      { +>(T: T, R: R, options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(T, R, options) return MappedResult(P) as never } diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index f5519e46b..6de947666 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import { type TSchema, SchemaOptions } from '../schema/index' import { type TObject, type TProperties } from '../object/index' import { type Assert } from '../helpers/index' @@ -38,7 +39,6 @@ import { type TTuple } from '../tuple/index' import { type TArray } from '../array/index' import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' -import { CloneType } from '../clone/type' import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys' import { IndexFromMappedKey, type TIndexFromMappedKey } from './indexed-from-mapped-key' @@ -259,12 +259,14 @@ export function Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(T: TSchema, K: any, options: SchemaOptions = {}): any { +export function Index(T: TSchema, K: any, options?: SchemaOptions): any { + // mapped-types + if (IsMappedResult(K)) return IndexFromMappedResult(T, K, options) + if (IsMappedKey(K)) return IndexFromMappedKey(T, K, options) // prettier-ignore - return ( - IsMappedResult(K) ? CloneType(IndexFromMappedResult(T, K, options)) : - IsMappedKey(K) ? CloneType(IndexFromMappedKey(T, K, options)) : - IsSchema(K) ? CloneType(FromSchema(T, IndexPropertyKeys(K)), options) : - CloneType(FromSchema(T, K as string[]), options) - ) + return CreateType( + IsSchema(K) + ? FromSchema(T, IndexPropertyKeys(K)) + : FromSchema(T, K as string[]) + , options) as never } diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts index 358ff7040..853c8da61 100644 --- a/src/type/instance-type/instance-type.ts +++ b/src/type/instance-type/instance-type.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { TConstructor } from '../constructor/index' -import { CloneType } from '../clone/type' export type TInstanceType> = T['returns'] /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ -export function InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { - return CloneType(schema.returns, options) +export function InstanceType>(schema: T, options?: SchemaOptions): TInstanceType { + return CreateType(schema.returns, options) } diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts index 3146c25cf..8b15618d3 100644 --- a/src/type/integer/integer.ts +++ b/src/type/integer/integer.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -42,10 +43,6 @@ export interface TInteger extends TSchema, IntegerOptions { type: 'integer' } /** `[Json]` Creates an Integer type */ -export function Integer(options: IntegerOptions = {}): TInteger { - return { - ...options, - [Kind]: 'Integer', - type: 'integer', - } as never +export function Integer(options?: IntegerOptions): TInteger { + return CreateType({ [Kind]: 'Integer', type: 'integer' }, options) as never } diff --git a/src/type/intersect/intersect-create.ts b/src/type/intersect/intersect-create.ts index 300291aa1..6b38ce830 100644 --- a/src/type/intersect/intersect-create.ts +++ b/src/type/intersect/intersect-create.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import { Kind } from '../symbols/index' -import { CloneType, CloneRest } from '../clone/type' import type { TIntersect, IntersectOptions } from './intersect-type' // ------------------------------------------------------------------ @@ -39,14 +39,14 @@ import { IsObject, IsSchema } from '../guard/kind' // IntersectCreate // ------------------------------------------------------------------ // prettier-ignore -export function IntersectCreate(T: [...T], options: IntersectOptions): TIntersect { +export function IntersectCreate(T: [...T], options: IntersectOptions = {}): TIntersect { const allObjects = T.every((schema) => IsObject(schema)) const clonedUnevaluatedProperties = IsSchema(options.unevaluatedProperties) - ? { unevaluatedProperties: CloneType(options.unevaluatedProperties) } + ? { unevaluatedProperties: options.unevaluatedProperties } : {} - return ( + return CreateType( (options.unevaluatedProperties === false || IsSchema(options.unevaluatedProperties) || allObjects - ? { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: CloneRest(T) } - : { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: CloneRest(T) }) - ) as never + ? { ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: T } + : { ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: T }) + , options) as never } diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts index f8a5c884e..a0c73484b 100644 --- a/src/type/intersect/intersect-evaluated.ts +++ b/src/type/intersect/intersect-evaluated.ts @@ -28,9 +28,8 @@ THE SOFTWARE. import type { SchemaOptions, TSchema } from '../schema/index' import { OptionalKind } from '../symbols/index' -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' import { Discard } from '../discard/index' - import { Never, type TNever } from '../never/index' import { Optional, type TOptional } from '../optional/index' import type { TReadonly } from '../readonly/index' @@ -116,8 +115,8 @@ export type TIntersectEvaluated = ( ) /** `[Json]` Creates an evaluated Intersect type */ export function IntersectEvaluated>(T: [...T], options: IntersectOptions = {}): R { - if (T.length === 0) return Never(options) as R - if (T.length === 1) return CloneType(T[0], options) as R + if (T.length === 0) return Never(options) as never + if (T.length === 1) return CreateType(T[0], options) as never if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') - return ResolveIntersect(T, options) as R + return ResolveIntersect(T, options) as never } diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts index 5672f4c63..66275057f 100644 --- a/src/type/intersect/intersect.ts +++ b/src/type/intersect/intersect.ts @@ -26,10 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' -import { CloneType } from '../clone/type' import { Never, type TNever } from '../never/index' - import { TIntersect, IntersectOptions } from './intersect-type' import { IntersectCreate } from './intersect-create' @@ -47,9 +46,9 @@ export type Intersect = ( TIntersect ) /** `[Json]` Creates an evaluated Intersect type */ -export function Intersect(T: [...T], options: IntersectOptions = {}): Intersect { - if (T.length === 0) return Never(options) as Intersect - if (T.length === 1) return CloneType(T[0], options) as Intersect +export function Intersect(T: [...T], options?: IntersectOptions): Intersect { + if (T.length === 0) return Never(options) as never + if (T.length === 1) return CreateType(T[0], options) as never if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') - return IntersectCreate(T, options) as Intersect + return IntersectCreate(T, options) as never } diff --git a/src/type/intrinsic/intrinsic-from-mapped-key.ts b/src/type/intrinsic/intrinsic-from-mapped-key.ts index 51ce4c719..16afb241e 100644 --- a/src/type/intrinsic/intrinsic-from-mapped-key.ts +++ b/src/type/intrinsic/intrinsic-from-mapped-key.ts @@ -32,6 +32,7 @@ import { Assert } from '../helpers/index' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' import { Intrinsic, type TIntrinsic, type IntrinsicMode } from './intrinsic' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // MappedIntrinsicPropertyKey @@ -49,7 +50,7 @@ function MappedIntrinsicPropertyKey< M extends IntrinsicMode, >(K: K, M: M, options: SchemaOptions): TMappedIntrinsicPropertyKey { return { - [K]: Intrinsic(Literal(K as TLiteralValue), M, options) + [K]: Intrinsic(Literal(K as TLiteralValue), M, Clone(options)) } as never } // ------------------------------------------------------------------ @@ -70,9 +71,11 @@ function MappedIntrinsicPropertyKeys< K extends PropertyKey[], M extends IntrinsicMode >(K: [...K], M: M, options: SchemaOptions): TMappedIntrinsicPropertyKeys { - return K.reduce((Acc, L) => { + const result = K.reduce((Acc, L) => { return { ...Acc, ...MappedIntrinsicPropertyKey(L, M, options) } }, {} as TProperties) as never + + return result } // ------------------------------------------------------------------ // MappedIntrinsicProperties diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index 2f7e23c6c..6745597ec 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { TemplateLiteral, TemplateLiteralParseExact, IsTemplateLiteralExpressionFinite, TemplateLiteralExpressionGenerate, type TTemplateLiteral, type TTemplateLiteralKind } from '../template-literal/index' import { IntrinsicFromMappedKey, type TIntrinsicFromMappedKey } from './intrinsic-from-mapped-key' @@ -134,7 +135,7 @@ export function Intrinsic(schema: /** Applies an intrinsic string manipulation to the given type. */ export function Intrinsic(schema: T, mode: M, options?: SchemaOptions): TIntrinsic /** Applies an intrinsic string manipulation to the given type. */ -export function Intrinsic(schema: TSchema, mode: IntrinsicMode, options: SchemaOptions = {}): any { +export function Intrinsic(schema: TSchema, mode: IntrinsicMode, options: SchemaOptions = {}): never { // prettier-ignore return ( // Intrinsic-Mapped-Inference @@ -143,6 +144,7 @@ export function Intrinsic(schema: TSchema, mode: IntrinsicMode, options: SchemaO IsTemplateLiteral(schema) ? FromTemplateLiteral(schema, mode, schema) : IsUnion(schema) ? Union(FromRest(schema.anyOf, mode), options) : IsLiteral(schema) ? Literal(FromLiteralValue(schema.const, mode), options) : - schema - ) + // Default Type + CreateType(schema, options) + ) as never } diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts index 335bae40c..da528a8ae 100644 --- a/src/type/iterator/iterator.ts +++ b/src/type/iterator/iterator.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' -import { CloneType } from '../clone/type' import { Kind } from '../symbols/index' export interface TIterator extends TSchema { @@ -38,11 +38,6 @@ export interface TIterator extends TSchema { items: T } /** `[JavaScript]` Creates an Iterator type */ -export function Iterator(items: T, options: SchemaOptions = {}): TIterator { - return { - ...options, - [Kind]: 'Iterator', - type: 'Iterator', - items: CloneType(items), - } as never +export function Iterator(items: T, options?: SchemaOptions): TIterator { + return CreateType({ [Kind]: 'Iterator', type: 'Iterator', items }, options) as never } diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 1f4f7df33..9991b7c15 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -31,7 +31,7 @@ import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { KeyOf, type TKeyOf } from './keyof' - +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ @@ -44,9 +44,9 @@ type TFromProperties< // prettier-ignore function FromProperties< K extends TProperties ->(K: K, options: SchemaOptions): TFromProperties { +>(K: K, options?: SchemaOptions): TFromProperties { const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = KeyOf(K[K2], options) + for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = KeyOf(K[K2], Clone(options)) return Acc as never } // ------------------------------------------------------------------ @@ -61,7 +61,7 @@ type TFromMappedResult< // prettier-ignore function FromMappedResult< R extends TMappedResult ->(R: R, options: SchemaOptions): TFromMappedResult { +>(R: R, options?: SchemaOptions): TFromMappedResult { return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ @@ -78,7 +78,7 @@ export type TKeyOfFromMappedResult< export function KeyOfFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult ->(R: R, options: SchemaOptions): TMappedResult

      { +>(R: R, options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(R, options) return MappedResult(P) as never } diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 895eaa6e0..2e4c42df6 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import type { Assert, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' @@ -34,7 +35,6 @@ import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Number, type TNumber } from '../number/index' import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from './keyof-property-keys' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' -import { CloneType } from '../clone/type' import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from-mapped-result' // ------------------------------------------------------------------ @@ -73,13 +73,13 @@ export function KeyOf(T: T, options?: SchemaOptions): T /** `[Json]` Creates a KeyOf type */ export function KeyOf(T: T, options?: SchemaOptions): TKeyOf /** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: TSchema, options: SchemaOptions = {}): any { +export function KeyOf(T: TSchema, options?: SchemaOptions): never { if (IsMappedResult(T)) { - return KeyOfFromMappedResult(T, options) + return KeyOfFromMappedResult(T, options) as never } else { const K = KeyOfPropertyKeys(T) const S = KeyOfPropertyKeysToRest(K) const U = UnionEvaluated(S) - return CloneType(U, options) + return CreateType(U, options) as never } } diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts index 3091b4dbd..5a17d8959 100644 --- a/src/type/literal/literal.ts +++ b/src/type/literal/literal.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -43,11 +44,13 @@ export interface TLiteral extends TSche const: T } /** `[Json]` Creates a Literal type */ -export function Literal(value: T, options: SchemaOptions = {}): TLiteral { - return { - ...options, - [Kind]: 'Literal', - const: value, - type: typeof value as 'string' | 'number' | 'boolean', - } as never +export function Literal(value: T, options?: SchemaOptions): TLiteral { + return CreateType( + { + [Kind]: 'Literal', + const: value, + type: typeof value as 'string' | 'number' | 'boolean', + }, + options, + ) as never } diff --git a/src/type/mapped/mapped-key.ts b/src/type/mapped/mapped-key.ts index c3a6f288c..42a9a6926 100644 --- a/src/type/mapped/mapped-key.ts +++ b/src/type/mapped/mapped-key.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import { Kind } from '../symbols/index' @@ -36,8 +37,8 @@ export interface TMappedKey extends TSc } // prettier-ignore export function MappedKey(T: [...T]): TMappedKey { - return { + return CreateType({ [Kind]: 'MappedKey', keys: T - } as never + }) as never } diff --git a/src/type/mapped/mapped-result.ts b/src/type/mapped/mapped-result.ts index d2aade75c..7faf21d75 100644 --- a/src/type/mapped/mapped-result.ts +++ b/src/type/mapped/mapped-result.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import type { TProperties } from '../object/index' import { Kind } from '../symbols/index' @@ -37,8 +38,8 @@ export interface TMappedResult extends TSch } // prettier-ignore export function MappedResult(properties: T): TMappedResult { - return { + return CreateType({ [Kind]: 'MappedResult', properties - } as never + }) as never } diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 797679b9c..30301a808 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import type { TSchema } from '../schema/index' import type { Ensure, Evaluate, Assert } from '../helpers/index' import { Kind, OptionalKind, ReadonlyKind } from '../symbols/index' -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' import { Discard } from '../discard/index' // evaluation types import { Array, type TArray } from '../array/index' @@ -261,9 +261,9 @@ export function Mapped = TMappedFunction, R extends TMapped = TMapped>(key: [...K], map: F, options?: ObjectOptions): R /** `[Json]` Creates a Mapped object type */ -export function Mapped(key: any, map: Function, options: ObjectOptions = {}) { +export function Mapped(key: any, map: Function, options?: ObjectOptions) { const K = IsSchema(key) ? IndexPropertyKeys(key) : (key as PropertyKey[]) const RT = map({ [Kind]: 'MappedKey', keys: K } as TMappedKey) const R = MappedFunctionReturnType(K, RT) - return CloneType(Object(R), options) + return Object(R, options) } diff --git a/src/type/never/never.ts b/src/type/never/never.ts index d1ff1c234..f2df6451e 100644 --- a/src/type/never/never.ts +++ b/src/type/never/never.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -35,10 +36,6 @@ export interface TNever extends TSchema { not: {} } /** `[Json]` Creates a Never type */ -export function Never(options: SchemaOptions = {}): TNever { - return { - ...options, - [Kind]: 'Never', - not: {}, - } as never +export function Never(options?: SchemaOptions): TNever { + return CreateType({ [Kind]: 'Never', not: {} }, options) as never } diff --git a/src/type/not/not.ts b/src/type/not/not.ts index 7315fec86..256a3c8eb 100644 --- a/src/type/not/not.ts +++ b/src/type/not/not.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' -import { CloneType } from '../clone/type' import { Kind } from '../symbols/index' export interface TNot extends TSchema { @@ -37,10 +37,6 @@ export interface TNot extends TSchema { not: T } /** `[Json]` Creates a Not type */ -export function Not(schema: T, options?: SchemaOptions): TNot { - return { - ...options, - [Kind]: 'Not', - not: CloneType(schema), - } as never +export function Not(not: T, options?: SchemaOptions): TNot { + return CreateType({ [Kind]: 'Not', not }, options) as never } diff --git a/src/type/null/null.ts b/src/type/null/null.ts index 40690048a..f953d04ec 100644 --- a/src/type/null/null.ts +++ b/src/type/null/null.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -35,10 +36,6 @@ export interface TNull extends TSchema { type: 'null' } /** `[Json]` Creates a Null type */ -export function Null(options: SchemaOptions = {}): TNull { - return { - ...options, - [Kind]: 'Null', - type: 'null', - } as never +export function Null(options?: SchemaOptions): TNull { + return CreateType({ [Kind]: 'Null', type: 'null' }, options) as never } diff --git a/src/type/number/number.ts b/src/type/number/number.ts index db7392cd0..0ef585560 100644 --- a/src/type/number/number.ts +++ b/src/type/number/number.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -42,10 +43,6 @@ export interface TNumber extends TSchema, NumberOptions { type: 'number' } /** `[Json]` Creates a Number type */ -export function Number(options: NumberOptions = {}): TNumber { - return { - ...options, - [Kind]: 'Number', - type: 'number', - } as never +export function Number(options?: NumberOptions): TNumber { + return CreateType({ [Kind]: 'Number', type: 'number' }, options) as never } diff --git a/src/type/object/object.ts b/src/type/object/object.ts index a9d4ce358..18d1559b2 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -26,12 +26,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate } from '../helpers/index' import type { TReadonly } from '../readonly/index' import type { TOptional } from '../optional/index' -import { CloneType } from '../clone/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ @@ -83,19 +83,18 @@ export interface TObject extends TSchema, O properties: T required?: string[] } +function RequiredKeys(properties: T): string[] { + const keys: string[] = [] + for (let key in properties) { + if (!IsOptional(properties[key])) keys.push(key) + } + return keys +} /** `[Json]` Creates an Object type */ -function _Object(properties: T, options: ObjectOptions = {}): TObject { - const propertyKeys = globalThis.Object.getOwnPropertyNames(properties) - const optionalKeys = propertyKeys.filter((key) => IsOptional(properties[key])) - const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) - const clonedAdditionalProperties = IsSchema(options.additionalProperties) ? { additionalProperties: CloneType(options.additionalProperties) } : {} - const clonedProperties = {} as Record - for (const key of propertyKeys) clonedProperties[key] = CloneType(properties[key]) - return ( - requiredKeys.length > 0 - ? { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys } - : { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties } - ) as never +function _Object(properties: T, options?: ObjectOptions): TObject { + const required = RequiredKeys(properties) + const schematic = required.length > 0 ? { [Kind]: 'Object', type: 'object', properties, required } : { [Kind]: 'Object', type: 'object', properties } + return CreateType(schematic, options) as never } /** `[Json]` Creates an Object type */ diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index 3ca1822dc..5eeb68660 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -30,6 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' import { Omit, type TOmit } from './omit' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromPropertyKey @@ -45,9 +46,9 @@ type TFromPropertyKey< function FromPropertyKey< T extends TSchema, K extends PropertyKey, ->(T: T, K: K, options: SchemaOptions): TFromPropertyKey { +>(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { return { - [K]: Omit(T, [K], options) + [K]: Omit(T, [K], Clone(options)) } as never } // ------------------------------------------------------------------ @@ -67,7 +68,7 @@ type TFromPropertyKeys< function FromPropertyKeys< T extends TSchema, K extends PropertyKey[] ->(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { +>(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(T, LK, options) } }, {} as TProperties) as never @@ -86,7 +87,7 @@ type TFromMappedKey< function FromMappedKey< T extends TSchema, K extends TMappedKey, ->(T: T, K: K, options: SchemaOptions): TFromMappedKey { +>(T: T, K: K, options?: SchemaOptions): TFromMappedKey { return FromPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ @@ -105,7 +106,7 @@ export function OmitFromMappedKey< T extends TSchema, K extends TMappedKey, P extends TProperties = TFromMappedKey ->(T: T, K: K, options: SchemaOptions): TMappedResult

      { +>(T: T, K: K, options?: SchemaOptions): TMappedResult

      { const P = FromMappedKey(T, K, options) return MappedResult(P) as never } diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index 5f54e4802..205047ea8 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -31,6 +31,7 @@ import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Omit, type TOmit } from './omit' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties @@ -46,9 +47,9 @@ type TFromProperties< function FromProperties< P extends TProperties, K extends PropertyKey[], ->(P: P, K: [...K], options: SchemaOptions): TFromProperties { +>(P: P, K: [...K], options?: SchemaOptions): TFromProperties { const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Omit(P[K2], K, options) + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Omit(P[K2], K, Clone(options)) return Acc as never } // ------------------------------------------------------------------ @@ -65,7 +66,7 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult, K extends PropertyKey[] ->(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { +>(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { return FromProperties(R.properties, K, options) as never } // ------------------------------------------------------------------ @@ -84,7 +85,7 @@ export function OmitFromMappedResult< R extends TMappedResult, K extends PropertyKey[], P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { +>(R: R, K: [...K], options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(R, K, options) return MappedResult(P) as never } diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 19751fdc4..0d959fe25 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { TupleToUnion, Evaluate } from '../helpers/index' import { type TRecursive } from '../recursive/index' @@ -36,7 +37,6 @@ import { Object, type TObject, type TProperties } from '../object/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key' import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result' @@ -115,13 +115,13 @@ export function Omit(T: T, K: K, option export function Omit>(T: T, K: K, options?: SchemaOptions): TOmit /** `[Json]` Constructs a type whose keys are omitted from the given type */ export function Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit -export function Omit(T: TSchema, K: any, options: SchemaOptions = {}): any { +export function Omit(T: TSchema, K: any, options?: SchemaOptions): any { // mapped if (IsMappedKey(K)) return OmitFromMappedKey(T, K, options) if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = CloneType(OmitResolve(T, I), options) - return { ...D, ...R } + const R = OmitResolve(T, I) + return CreateType({ ...D, ...R }, options) } diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts index 0ecd8cad4..a481135b7 100644 --- a/src/type/optional/optional.ts +++ b/src/type/optional/optional.ts @@ -26,10 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import type { Ensure } from '../helpers/index' import { OptionalKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { Discard } from '../discard/index' import type { TMappedResult } from '../mapped/index' @@ -40,14 +40,14 @@ import { IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ type TRemoveOptional = T extends TOptional ? S : T function RemoveOptional(schema: T) { - return Discard(CloneType(schema), [OptionalKind]) + return CreateType(Discard(schema, [OptionalKind])) } // ------------------------------------------------------------------ // AddOptional // ------------------------------------------------------------------ type TAddOptional = T extends TOptional ? TOptional : Ensure> function AddOptional(schema: T) { - return { ...CloneType(schema), [OptionalKind]: 'Optional' } + return CreateType({ ...schema, [OptionalKind]: 'Optional' }) } // prettier-ignore export type TOptionalWithFlag = diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts index 6e5bcbe5d..a7a0daa45 100644 --- a/src/type/parameters/parameters.ts +++ b/src/type/parameters/parameters.ts @@ -30,7 +30,6 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { TFunction } from '../function/index' import type { Ensure } from '../helpers/index' import { Tuple, type TTuple } from '../tuple/index' -import { CloneRest } from '../clone/type' // ------------------------------------------------------------------ // Parameters @@ -38,6 +37,6 @@ import { CloneRest } from '../clone/type' export type TParameters = Ensure> /** `[JavaScript]` Extracts the Parameters from the given Function type */ -export function Parameters>(schema: T, options: SchemaOptions = {}): TParameters { - return Tuple(CloneRest(schema.parameters), { ...options }) +export function Parameters>(schema: T, options?: SchemaOptions): TParameters { + return Tuple(schema.parameters, options) } diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts index 466c608ff..af60533b8 100644 --- a/src/type/partial/partial-from-mapped-result.ts +++ b/src/type/partial/partial-from-mapped-result.ts @@ -31,6 +31,7 @@ import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Partial, type TPartial } from './partial' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties @@ -44,9 +45,9 @@ type TFromProperties< // prettier-ignore function FromProperties< P extends TProperties ->(K: P, options: SchemaOptions): TFromProperties

      { +>(K: P, options?: SchemaOptions): TFromProperties

      { const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = Partial(K[K2], options) + for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = Partial(K[K2], Clone(options)) return Acc as never } // ------------------------------------------------------------------ @@ -61,7 +62,7 @@ type TFromMappedResult< // prettier-ignore function FromMappedResult< R extends TMappedResult ->(R: R, options: SchemaOptions): TFromMappedResult { +>(R: R, options?: SchemaOptions): TFromMappedResult { return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ @@ -78,7 +79,7 @@ export type TPartialFromMappedResult< export function PartialFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult ->(R: R, options: SchemaOptions): TMappedResult

      { +>(R: R, options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(R, options) return MappedResult(P) as never } diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 27a37b0b7..a2382b281 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Evaluate } from '../helpers/index' import type { TMappedResult } from '../mapped/index' @@ -38,7 +39,6 @@ import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result' @@ -104,9 +104,9 @@ export function Partial(T: T, options?: SchemaOptions): /** `[Json]` Constructs a type where all properties are optional */ export function Partial(T: T, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: TSchema, options: SchemaOptions = {}): any { +export function Partial(T: TSchema, options?: SchemaOptions): any { if (IsMappedResult(T)) return PartialFromMappedResult(T, options) const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = CloneType(PartialResolve(T), options) - return { ...D, ...R } as never + const R = PartialResolve(T) + return CreateType({ ...options, ...D, ...R }) as never } diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index 7bd87f09e..062e1e527 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -30,6 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult, type TMappedKey } from '../mapped/index' import { Pick, type TPick } from './pick' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromPropertyKey @@ -45,9 +46,9 @@ type TFromPropertyKey< function FromPropertyKey< T extends TSchema, K extends PropertyKey, ->(T: T, K: K, options: SchemaOptions): TFromPropertyKey { +>(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { return { - [K]: Pick(T, [K], options) + [K]: Pick(T, [K], Clone(options)) } as never } // ------------------------------------------------------------------ @@ -67,7 +68,7 @@ type TFromPropertyKeys< function FromPropertyKeys< T extends TSchema, K extends PropertyKey[] ->(T: T, K: [...K], options: SchemaOptions): TFromPropertyKeys { +>(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { return K.reduce((Acc, LK) => { return { ...Acc, ...FromPropertyKey(T, LK, options) } }, {} as TProperties) as never @@ -86,7 +87,7 @@ type TFromMappedKey< function FromMappedKey< T extends TSchema, K extends TMappedKey, ->(T: T, K: K, options: SchemaOptions): TFromMappedKey { +>(T: T, K: K, options?: SchemaOptions): TFromMappedKey { return FromPropertyKeys(T, K.keys, options) as never } // ------------------------------------------------------------------ @@ -105,7 +106,7 @@ export function PickFromMappedKey< T extends TSchema, K extends TMappedKey, P extends TProperties = TFromMappedKey ->(T: T, K: K, options: SchemaOptions): TMappedResult

      { +>(T: T, K: K, options?: SchemaOptions): TMappedResult

      { const P = FromMappedKey(T, K, options) return MappedResult(P) as never } diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 43b006457..3abf5ad17 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -31,6 +31,7 @@ import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' import { Pick, type TPick } from './pick' +import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties @@ -46,9 +47,9 @@ type TFromProperties< function FromProperties< P extends TProperties, K extends PropertyKey[], ->(P: P, K: [...K], options: SchemaOptions): TFromProperties { +>(P: P, K: [...K], options?: SchemaOptions): TFromProperties { const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Pick(P[K2], K, options) + for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Pick(P[K2], K, Clone(options)) return Acc as never } // ------------------------------------------------------------------ @@ -65,7 +66,7 @@ type TFromMappedResult< function FromMappedResult< R extends TMappedResult, K extends PropertyKey[] ->(R: R, K: [...K], options: SchemaOptions): TFromMappedResult { +>(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { return FromProperties(R.properties, K, options) as never } // ------------------------------------------------------------------ @@ -84,7 +85,7 @@ export function PickFromMappedResult< R extends TMappedResult, K extends PropertyKey[], P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options: SchemaOptions): TMappedResult

      { +>(R: R, K: [...K], options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(R, K, options) return MappedResult(P) as never } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index 60a7b2227..017914046 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { TupleToUnion, Evaluate } from '../helpers/index' import { type TRecursive } from '../recursive/index' @@ -36,7 +37,6 @@ import type { TMappedKey, TMappedResult } from '../mapped/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' @@ -106,13 +106,13 @@ export function Pick(T: T, K: K, option export function Pick>(T: T, K: K, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ export function Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick -export function Pick(T: TSchema, K: any, options: SchemaOptions = {}): any { +export function Pick(T: TSchema, K: any, options?: SchemaOptions): any { // mapped if (IsMappedKey(K)) return PickFromMappedKey(T, K, options) if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = CloneType(PickResolve(T, I), options) - return { ...D, ...R } + const R = PickResolve(T, I) + return CreateType({ ...D, ...R }, options) } diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts index bd9504194..4a2488408 100644 --- a/src/type/promise/promise.ts +++ b/src/type/promise/promise.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' -import { CloneType } from '../clone/type' import { Kind } from '../symbols/index' export interface TPromise extends TSchema { @@ -38,11 +38,6 @@ export interface TPromise extends TSchema { item: TSchema } /** `[JavaScript]` Creates a Promise type */ -export function Promise(item: T, options: SchemaOptions = {}): TPromise { - return { - ...options, - [Kind]: 'Promise', - type: 'Promise', - item: CloneType(item), - } as never +export function Promise(item: T, options?: SchemaOptions): TPromise { + return CreateType({ [Kind]: 'Promise', type: 'Promise', item }, options) as never } diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts index 546caddb7..47d9ee0da 100644 --- a/src/type/readonly/readonly.ts +++ b/src/type/readonly/readonly.ts @@ -26,10 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import type { Ensure } from '../helpers/index' import { ReadonlyKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { Discard } from '../discard/index' import type { TMappedResult } from '../mapped/index' @@ -40,14 +40,14 @@ import { IsMappedResult } from '../guard/kind' // ------------------------------------------------------------------ type TRemoveReadonly = T extends TReadonly ? S : T function RemoveReadonly(schema: T) { - return Discard(CloneType(schema), [ReadonlyKind]) + return CreateType(Discard(schema, [ReadonlyKind])) } // ------------------------------------------------------------------ // AddReadonly // ------------------------------------------------------------------ type TAddReadonly = T extends TReadonly ? TReadonly : Ensure> function AddReadonly(schema: T) { - return { ...CloneType(schema), [ReadonlyKind]: 'Readonly' } + return CreateType({ ...schema, [ReadonlyKind]: 'Readonly' }) } // prettier-ignore export type TReadonlyWithFlag = diff --git a/src/type/record/record.ts b/src/type/record/record.ts index cc6e9e7b8..e32ae7a26 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' @@ -44,7 +45,6 @@ import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLitera import { PatternStringExact, PatternNumberExact, PatternNeverExact } from '../patterns/index' import { IndexPropertyKeys } from '../indexed/index' import { Kind, Hint } from '../symbols/index' -import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ @@ -58,12 +58,11 @@ import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsT // ------------------------------------------------------------------ // prettier-ignore function RecordCreateFromPattern(pattern: string, T: TSchema, options: ObjectOptions): TRecord { - return { - ...options, + return CreateType({ [Kind]: 'Record', type: 'object', - patternProperties: { [pattern]: CloneType(T) } - } as never + patternProperties: { [pattern]: T } + }, options) as never } // ------------------------------------------------------------------ // RecordCreateFromKeys @@ -71,7 +70,7 @@ function RecordCreateFromPattern(pattern: string, T: TSchema, options: ObjectOpt // prettier-ignore function RecordCreateFromKeys(K: string[], T: TSchema, options: ObjectOptions): TObject { const Acc = {} as TProperties - for(const K2 of K) Acc[K2] = CloneType(T) + for(const K2 of K) Acc[K2] = T return Object(Acc, { ...options, [Hint]: 'Record' }) } // ------------------------------------------------------------------ diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts index 46d28e98a..50df4f320 100644 --- a/src/type/recursive/recursive.ts +++ b/src/type/recursive/recursive.ts @@ -28,6 +28,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' import { IsUndefined } from '../guard/value' import { Kind, Hint } from '../symbols/index' import { Static } from '../static/index' @@ -56,8 +57,8 @@ let Ordinal = 0 /** `[Json]` Creates a Recursive type */ export function Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { if (IsUndefined(options.$id)) (options as any).$id = `T${Ordinal++}` - const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) + const thisType = CloneType(callback({ [Kind]: 'This', $ref: `${options.$id}` } as any)) thisType.$id = options.$id // prettier-ignore - return CloneType({ ...options, [Hint]: 'Recursive', ...thisType }) as never + return CreateType({ [Hint]: 'Recursive', ...thisType }, options) as never } diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index 97fffe26f..3edd35b3d 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' import { Kind } from '../symbols/index' @@ -46,12 +47,8 @@ export function Ref(schema: T, options?: SchemaOptions): TRef /** `[Json]` Creates a Ref type. */ export function Ref($ref: string, options?: SchemaOptions): TRef /** `[Json]` Creates a Ref type. */ -export function Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { - if (IsString(unresolved)) return { ...options, [Kind]: 'Ref', $ref: unresolved } +export function Ref(unresolved: TSchema | string, options?: SchemaOptions) { + if (IsString(unresolved)) return CreateType({ [Kind]: 'Ref', $ref: unresolved }, options) if (IsUndefined(unresolved.$id)) throw new Error('Reference target type must specify an $id') - return { - ...options, - [Kind]: 'Ref', - $ref: unresolved.$id!, - } + return CreateType({ [Kind]: 'Ref', $ref: unresolved.$id! }, options) } diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts index c4d0030d0..6f005e696 100644 --- a/src/type/regexp/regexp.ts +++ b/src/type/regexp/regexp.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { SchemaOptions } from '../schema/index' import type { TSchema } from '../schema/index' import { IsString } from '../guard/value' @@ -50,7 +51,7 @@ export function RegExp(pattern: string, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ export function RegExp(regex: RegExp, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ -export function RegExp(unresolved: RegExp | string, options: RegExpOptions = {}) { +export function RegExp(unresolved: RegExp | string, options?: RegExpOptions) { const expr = IsString(unresolved) ? new globalThis.RegExp(unresolved) : unresolved - return { ...options, [Kind]: 'RegExp', type: 'RegExp', source: expr.source, flags: expr.flags } as never + return CreateType({ [Kind]: 'RegExp', type: 'RegExp', source: expr.source, flags: expr.flags }, options) as never } diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts index 1e382ac3d..147508846 100644 --- a/src/type/required/required-from-mapped-result.ts +++ b/src/type/required/required-from-mapped-result.ts @@ -44,7 +44,7 @@ type TFromProperties< // prettier-ignore function FromProperties< P extends TProperties ->(P: P, options: SchemaOptions): TFromProperties

      { +>(P: P, options?: SchemaOptions): TFromProperties

      { const Acc = {} as TProperties for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Required(P[K2], options) return Acc as never @@ -61,7 +61,7 @@ type TFromMappedResult< // prettier-ignore function FromMappedResult< R extends TMappedResult ->(R: R, options: SchemaOptions): TFromMappedResult { +>(R: R, options?: SchemaOptions): TFromMappedResult { return FromProperties(R.properties, options) as never } // ------------------------------------------------------------------ @@ -78,7 +78,7 @@ export type TRequiredFromMappedResult< export function RequiredFromMappedResult< R extends TMappedResult, P extends TProperties = TFromMappedResult ->(R: R, options: SchemaOptions): TMappedResult

      { +>(R: R, options?: SchemaOptions): TMappedResult

      { const P = FromMappedResult(R, options) return MappedResult(P) as never } diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 9bbad39c7..807fff050 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Evaluate } from '../helpers/index' import type { TMappedResult } from '../mapped/index' @@ -36,11 +37,8 @@ import { type TRecursive } from '../recursive/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' import { type TObject, type TProperties, Object } from '../object/index' - import { OptionalKind, TransformKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { Discard } from '../discard/index' - import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result' // ------------------------------------------------------------------ @@ -106,12 +104,12 @@ export function Required(T: T, options?: SchemaOptions) /** `[Json]` Constructs a type where all properties are required */ export function Required(T: T, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options: SchemaOptions = {}) { +export function Required(T: T, options?: SchemaOptions): never { if (IsMappedResult(T)) { - return RequiredFromMappedResult(T, options) + return RequiredFromMappedResult(T, options) as never } else { const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = CloneType(RequiredResolve(T) as any, options) - return { ...D, ...R } + const R = RequiredResolve(T) as any + return CreateType({ ...D, ...R }, options) as never } } diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts index 86593daac..d437fa2da 100644 --- a/src/type/rest/rest.ts +++ b/src/type/rest/rest.ts @@ -30,7 +30,6 @@ import type { TSchema } from '../schema/index' import type { TIntersect } from '../intersect/index' import type { TUnion } from '../union/index' import type { TTuple } from '../tuple/index' -import { CloneRest } from '../clone/type' // ------------------------------------------------------------------ // TypeGuard @@ -41,16 +40,16 @@ import { IsIntersect, IsUnion, IsTuple } from '../guard/kind' // ------------------------------------------------------------------ // prettier-ignore type TRestResolve = - T extends TIntersect ? [...S] : - T extends TUnion ? [...S] : - T extends TTuple ? [...S] : + T extends TIntersect ? S : + T extends TUnion ? S : + T extends TTuple ? S : [] // prettier-ignore function RestResolve(T: T) { return ( - IsIntersect(T) ? CloneRest(T.allOf) : - IsUnion(T) ? CloneRest(T.anyOf) : - IsTuple(T) ? CloneRest(T.items ?? []) : + IsIntersect(T) ? T.allOf : + IsUnion(T) ? T.anyOf : + IsTuple(T) ? T.items ?? [] : [] ) as never } @@ -61,5 +60,5 @@ export type TRest = TRestResolve /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ export function Rest(T: T): TRest { - return CloneRest(RestResolve(T)) + return RestResolve(T) } diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts index f3f843355..f868d2fcf 100644 --- a/src/type/return-type/return-type.ts +++ b/src/type/return-type/return-type.ts @@ -26,13 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { SchemaOptions } from '../schema/index' import type { TFunction } from '../function/index' -import { CloneType } from '../clone/type' export type TReturnType = T['returns'] /** `[JavaScript]` Extracts the ReturnType from the given Function type */ -export function ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { - return CloneType(schema.returns, options) +export function ReturnType>(schema: T, options?: SchemaOptions): TReturnType { + return CreateType(schema.returns, options) as never } diff --git a/src/type/string/string.ts b/src/type/string/string.ts index 97ee88fc2..c1d6061b9 100644 --- a/src/type/string/string.ts +++ b/src/type/string/string.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -81,6 +82,6 @@ export interface TString extends TSchema, StringOptions { } /** `[Json]` Creates a String type */ -export function String(options: StringOptions = {}): TString { - return { ...options, [Kind]: 'String', type: 'string' } as never +export function String(options?: StringOptions): TString { + return CreateType({ [Kind]: 'String', type: 'string' }, options) as never } diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts index f7e86697d..5a65b23b9 100644 --- a/src/type/symbol/symbol.ts +++ b/src/type/symbol/symbol.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -37,5 +38,5 @@ export interface TSymbol extends TSchema, SchemaOptions { } /** `[JavaScript]` Creates a Symbol type */ export function Symbol(options?: SchemaOptions): TSymbol { - return { ...options, [Kind]: 'Symbol', type: 'symbol' } as never + return CreateType({ [Kind]: 'Symbol', type: 'symbol' }, options) as never } diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts index 8798b3083..7eadc534d 100644 --- a/src/type/template-literal/template-literal.ts +++ b/src/type/template-literal/template-literal.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Assert } from '../helpers/index' import type { TUnion } from '../union/index' @@ -94,9 +95,9 @@ export function TemplateLiteral(syntax: T, options?: SchemaOpt export function TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral /** `[Json]` Creates a TemplateLiteral type */ // prettier-ignore -export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}): any { +export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options?: SchemaOptions): any { const pattern = IsString(unresolved) ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) : TemplateLiteralPattern(unresolved as TTemplateLiteralKind[]) - return { ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern } + return CreateType({ [Kind]: 'TemplateLiteral', type: 'string', pattern }, options) } diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts index 345726c61..eb7c561cd 100644 --- a/src/type/transform/transform.ts +++ b/src/type/transform/transform.ts @@ -29,7 +29,6 @@ THE SOFTWARE. import type { TSchema } from '../schema/index' import type { Static, StaticDecode } from '../static/index' import { TransformKind } from '../symbols/index' -import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // TypeGuard @@ -58,10 +57,9 @@ export class TransformEncodeBuilder, StaticDecode>>(encode: E): TTransform> { - const schema = CloneType(this.schema) + public Encode, StaticDecode>>(encode: E): TTransform> { return ( - IsTransform(schema) ? this.EncodeTransform(encode, schema): this.EncodeSchema(encode, schema) + IsTransform(this.schema) ? this.EncodeTransform(encode, this.schema): this.EncodeSchema(encode, this.schema) ) as never } } diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts index 193b3c840..67d564f9d 100644 --- a/src/type/tuple/tuple.ts +++ b/src/type/tuple/tuple.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import type { Static } from '../static/index' -import { CloneRest } from '../clone/type' import { Kind } from '../symbols/index' // ------------------------------------------------------------------ @@ -52,13 +52,11 @@ export interface TTuple extends TSchema { maxItems: number } /** `[Json]` Creates a Tuple type */ -export function Tuple(items: [...T], options: SchemaOptions = {}): TTuple { - // return TupleResolver.Resolve(T) - const [additionalItems, minItems, maxItems] = [false, items.length, items.length] +export function Tuple(items: [...T], options?: SchemaOptions): TTuple { // prettier-ignore - return ( + return CreateType( items.length > 0 ? - { ...options, [Kind]: 'Tuple', type: 'array', items: CloneRest(items), additionalItems, minItems, maxItems } : - { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems } - ) as never + { [Kind]: 'Tuple', type: 'array', items, additionalItems: false, minItems: items.length, maxItems: items.length } : + { [Kind]: 'Tuple', type: 'array', minItems: items.length, maxItems: items.length }, + options) as never } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index ec87ac728..fad81ea22 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -49,19 +49,19 @@ import { Void, type TVoid } from '../void/index' /** JavaScript Type Builder with Static Resolution for TypeScript */ export class JavaScriptTypeBuilder extends JsonTypeBuilder { /** `[JavaScript]` Creates a AsyncIterator type */ - public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { + public AsyncIterator(items: T, options?: SchemaOptions): TAsyncIterator { return AsyncIterator(items, options) } /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ - public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { + public Awaited(schema: T, options?: SchemaOptions): TAwaited { return Awaited(schema, options) } /** `[JavaScript]` Creates a BigInt type */ - public BigInt(options: BigIntOptions = {}): TBigInt { + public BigInt(options?: BigIntOptions): TBigInt { return BigInt(options) } /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + public ConstructorParameters>(schema: T, options?: SchemaOptions): TConstructorParameters { return ConstructorParameters(schema, options) } /** `[JavaScript]` Creates a Constructor type */ @@ -77,19 +77,19 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return FunctionType(parameters, returns, options) } /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ - public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + public InstanceType>(schema: T, options?: SchemaOptions): TInstanceType { return InstanceType(schema, options) } /** `[JavaScript]` Creates an Iterator type */ - public Iterator(items: T, options: SchemaOptions = {}): TIterator { + public Iterator(items: T, options?: SchemaOptions): TIterator { return Iterator(items, options) } /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + public Parameters>(schema: T, options?: SchemaOptions): TParameters { return Parameters(schema, options) } /** `[JavaScript]` Creates a Promise type */ - public Promise(item: T, options: SchemaOptions = {}): TPromise { + public Promise(item: T, options?: SchemaOptions): TPromise { return Promise(item, options) } /** `[JavaScript]` Creates a RegExp type */ @@ -97,11 +97,11 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { /** `[JavaScript]` Creates a RegExp type */ public RegExp(regex: RegExp, options?: RegExpOptions): TRegExp /** `[JavaScript]` Creates a RegExp type */ - public RegExp(unresolved: string | RegExp, options: RegExpOptions = {}) { + public RegExp(unresolved: string | RegExp, options?: RegExpOptions) { return RegExp(unresolved as any, options) } /** `[JavaScript]` Extracts the ReturnType from the given Function type */ - public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + public ReturnType>(schema: T, options?: SchemaOptions): TReturnType { return ReturnType(schema, options) } /** `[JavaScript]` Creates a Symbol type */ @@ -109,15 +109,15 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return Symbol(options) } /** `[JavaScript]` Creates a Undefined type */ - public Undefined(options: SchemaOptions = {}): TUndefined { + public Undefined(options?: SchemaOptions): TUndefined { return Undefined(options) } /** `[JavaScript]` Creates a Uint8Array type */ - public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + public Uint8Array(options?: Uint8ArrayOptions): TUint8Array { return Uint8Array(options) } /** `[JavaScript]` Creates a Void type */ - public Void(options: SchemaOptions = {}): TVoid { + public Void(options?: SchemaOptions): TVoid { return Void(options) } } diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 3d1854005..f9a1ae741 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -114,19 +114,19 @@ export class JsonTypeBuilder { // Types // ------------------------------------------------------------------------ /** `[Json]` Creates an Any type */ - public Any(options: SchemaOptions = {}): TAny { + public Any(options?: SchemaOptions): TAny { return Any(options) } /** `[Json]` Creates an Array type */ - public Array(schema: T, options: ArrayOptions = {}): TArray { + public Array(schema: T, options?: ArrayOptions): TArray { return Array(schema, options) } /** `[Json]` Creates a Boolean type */ - public Boolean(options: SchemaOptions = {}): TBoolean { + public Boolean(options?: SchemaOptions): TBoolean { return Boolean(options) } /** `[Json]` Intrinsic function to Capitalize LiteralString types */ - public Capitalize(schema: T, options: SchemaOptions = {}): TCapitalize { + public Capitalize(schema: T, options?: SchemaOptions): TCapitalize { return Capitalize(schema, options) } /** `[Json]` Creates a Composite object type */ @@ -134,7 +134,7 @@ export class JsonTypeBuilder { return Composite(schemas, options) // (error) TS 5.4.0-dev - review TComposite implementation } /** `[JavaScript]` Creates a readonly const type from the given value. */ - public Const(value: T, options: SchemaOptions = {}): TConst { + public Const(value: T, options?: SchemaOptions): TConst { return Const(value, options) } /** `[Json]` Creates a dereferenced type */ @@ -142,7 +142,7 @@ export class JsonTypeBuilder { return Deref(schema, references) } /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { + public Enum>(item: T, options?: SchemaOptions): TEnum { return Enum(item, options) } /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ @@ -152,7 +152,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ public Exclude(unionType: L, excludedMembers: R, options?: SchemaOptions): TExclude /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ - public Exclude(unionType: TSchema, excludedMembers: TSchema, options: SchemaOptions = {}): any { + public Exclude(unionType: TSchema, excludedMembers: TSchema, options?: SchemaOptions): any { return Exclude(unionType, excludedMembers, options) } /** `[Json]` Creates a Conditional type */ @@ -162,7 +162,7 @@ export class JsonTypeBuilder { /** `[Json]` Creates a Conditional type */ public Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions): TExtends /** `[Json]` Creates a Conditional type */ - public Extends(L: L, R: R, T: T, F: F, options: SchemaOptions = {}) { + public Extends(L: L, R: R, T: T, F: F, options?: SchemaOptions) { return Extends(L, R, T, F, options) } /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ @@ -172,7 +172,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ public Extract(type: L, union: R, options?: SchemaOptions): TExtract /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ - public Extract(type: TSchema, union: TSchema, options: SchemaOptions = {}): any { + public Extract(type: TSchema, union: TSchema, options?: SchemaOptions): any { return Extract(type, union, options) } /** `[Json]` Returns an Indexed property type for the given keys */ @@ -184,15 +184,15 @@ export class JsonTypeBuilder { /** `[Json]` Returns an Indexed property type for the given keys */ public Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + public Index(schema: TSchema, unresolved: any, options?: SchemaOptions): any { return Index(schema, unresolved, options) } /** `[Json]` Creates an Integer type */ - public Integer(options: IntegerOptions = {}): TInteger { + public Integer(options?: IntegerOptions): TInteger { return Integer(options) } /** `[Json]` Creates an Intersect type */ - public Intersect(T: [...T], options: IntersectOptions = {}): Intersect { + public Intersect(T: [...T], options?: IntersectOptions): Intersect { return Intersect(T, options) } /** `[Json]` Creates a KeyOf type */ @@ -200,15 +200,15 @@ export class JsonTypeBuilder { /** `[Json]` Creates a KeyOf type */ public KeyOf(schema: T, options?: SchemaOptions): TKeyOf /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: TSchema, options: SchemaOptions = {}): any { + public KeyOf(schema: TSchema, options?: SchemaOptions): any { return KeyOf(schema, options) } /** `[Json]` Creates a Literal type */ - public Literal(value: T, options: SchemaOptions = {}): TLiteral { + public Literal(value: T, options?: SchemaOptions): TLiteral { return Literal(value, options) } /** `[Json]` Intrinsic function to Lowercase LiteralString types */ - public Lowercase(schema: T, options: SchemaOptions = {}): TLowercase { + public Lowercase(schema: T, options?: SchemaOptions): TLowercase { return Lowercase(schema, options) } /** `[Json]` Creates a Mapped object type */ @@ -216,11 +216,11 @@ export class JsonTypeBuilder { /** `[Json]` Creates a Mapped object type */ public Mapped = TMappedFunction, R extends TMapped = TMapped>(key: [...K], map: F, options?: ObjectOptions): R /** `[Json]` Creates a Mapped object type */ - public Mapped(key: any, map: TMappedFunction, options: ObjectOptions = {}): any { + public Mapped(key: any, map: TMappedFunction, options?: ObjectOptions): any { return Mapped(key, map, options) } /** `[Json]` Creates a Never type */ - public Never(options: SchemaOptions = {}): TNever { + public Never(options?: SchemaOptions): TNever { return Never(options) } /** `[Json]` Creates a Not type */ @@ -228,15 +228,15 @@ export class JsonTypeBuilder { return Not(schema, options) } /** `[Json]` Creates a Null type */ - public Null(options: SchemaOptions = {}): TNull { + public Null(options?: SchemaOptions): TNull { return Null(options) } /** `[Json]` Creates a Number type */ - public Number(options: NumberOptions = {}): TNumber { + public Number(options?: NumberOptions): TNumber { return Number(options) } /** `[Json]` Creates an Object type */ - public Object(properties: T, options: ObjectOptions = {}): TObject { + public Object(properties: T, options?: ObjectOptions): TObject { return Object(properties, options) } /** `[Json]` Constructs a type whose keys are omitted from the given type */ @@ -248,7 +248,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + public Omit(schema: TSchema, unresolved: any, options?: SchemaOptions): any { return Omit(schema, unresolved, options) } /** `[Json]` Constructs a type where all properties are optional */ @@ -256,7 +256,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type where all properties are optional */ public Partial(schema: T, options?: ObjectOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: TSchema, options: ObjectOptions = {}): any { + public Partial(schema: TSchema, options?: ObjectOptions): any { return Partial(schema, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ @@ -268,15 +268,15 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { + public Pick(schema: TSchema, unresolved: any, options?: SchemaOptions): any { return Pick(schema, unresolved, options) } /** `[Json]` Creates a Record type */ - public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordOrObject { + public Record(key: K, schema: T, options?: ObjectOptions): TRecordOrObject { return Record(key, schema, options) } /** `[Json]` Creates a Recursive type */ - public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { + public Recursive(callback: (thisType: TThis) => T, options?: SchemaOptions): TRecursive { return Recursive(callback, options) } /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ @@ -284,7 +284,7 @@ export class JsonTypeBuilder { /** `[Json]` Creates a Ref type. */ public Ref($ref: string, options?: SchemaOptions): TRef /** `[Json]` Creates a Ref type. */ - public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { + public Ref(unresolved: TSchema | string, options?: SchemaOptions) { return Ref(unresolved as any, options) } /** `[Json]` Constructs a type where all properties are required */ @@ -292,7 +292,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type where all properties are required */ public Required(schema: T, options?: ObjectOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: TSchema, options: ObjectOptions = {}): any { + public Required(schema: TSchema, options?: ObjectOptions): any { return Required(schema, options) } /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ @@ -300,7 +300,7 @@ export class JsonTypeBuilder { return Rest(schema) } /** `[Json]` Creates a String type */ - public String(options: StringOptions = {}): TString { + public String(options?: StringOptions): TString { return String(options) } /** `[Json]` Creates a TemplateLiteral type from template dsl string */ @@ -308,7 +308,7 @@ export class JsonTypeBuilder { /** `[Json]` Creates a TemplateLiteral type */ public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { + public TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options?: SchemaOptions) { return TemplateLiteral(unresolved as any, options) } /** `[Json]` Creates a Transform type */ @@ -316,27 +316,27 @@ export class JsonTypeBuilder { return Transform(schema) } /** `[Json]` Creates a Tuple type */ - public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { + public Tuple(items: [...T], options?: SchemaOptions): TTuple { return Tuple(items, options) } /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ - public Uncapitalize(schema: T, options: SchemaOptions = {}): TUncapitalize { + public Uncapitalize(schema: T, options?: SchemaOptions): TUncapitalize { return Uncapitalize(schema, options) } /** `[Json]` Creates a Union type */ - public Union(schemas: [...T], options: SchemaOptions = {}): Union { + public Union(schemas: [...T], options?: SchemaOptions): Union { return Union(schemas, options) } /** `[Json]` Creates an Unknown type */ - public Unknown(options: SchemaOptions = {}): TUnknown { + public Unknown(options?: SchemaOptions): TUnknown { return Unknown(options) } /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ - public Unsafe(options: UnsafeOptions = {}): TUnsafe { + public Unsafe(options?: UnsafeOptions): TUnsafe { return Unsafe(options) } /** `[Json]` Intrinsic function to Uppercase LiteralString types */ - public Uppercase(schema: T, options: SchemaOptions = {}): TUppercase { + public Uppercase(schema: T, options?: SchemaOptions): TUppercase { return Uppercase(schema, options) } } diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts index 55458346c..09c57ab01 100644 --- a/src/type/uint8array/uint8array.ts +++ b/src/type/uint8array/uint8array.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -39,6 +40,6 @@ export interface TUint8Array extends TSchema, Uint8ArrayOptions { type: 'uint8array' } /** `[JavaScript]` Creates a Uint8Array type */ -export function Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return { ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' } as never +export function Uint8Array(options?: Uint8ArrayOptions): TUint8Array { + return CreateType({ [Kind]: 'Uint8Array', type: 'Uint8Array' }, options) as never } diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts index bbba2b4bf..efd2a7649 100644 --- a/src/type/undefined/undefined.ts +++ b/src/type/undefined/undefined.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -35,6 +36,6 @@ export interface TUndefined extends TSchema { type: 'undefined' } /** `[JavaScript]` Creates a Undefined type */ -export function Undefined(options: SchemaOptions = {}): TUndefined { - return { ...options, [Kind]: 'Undefined', type: 'undefined' } as never +export function Undefined(options?: SchemaOptions): TUndefined { + return CreateType({ [Kind]: 'Undefined', type: 'undefined' }, options) as never } diff --git a/src/type/union/union-create.ts b/src/type/union/union-create.ts index 86093fe58..856f1ffc4 100644 --- a/src/type/union/union-create.ts +++ b/src/type/union/union-create.ts @@ -27,10 +27,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' -import { CloneRest } from '../clone/type' +import { CreateType } from '../create/type' import { TUnion } from './union-type' import { Kind } from '../symbols/index' -export function UnionCreate(T: [...T], options: SchemaOptions): TUnion { - return { ...options, [Kind]: 'Union', anyOf: CloneRest(T) } as never +export function UnionCreate(T: [...T], options?: SchemaOptions): TUnion { + return CreateType({ [Kind]: 'Union', anyOf: T }, options) as never } diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts index afbf4714f..9e4004a20 100644 --- a/src/type/union/union-evaluated.ts +++ b/src/type/union/union-evaluated.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { SchemaOptions, TSchema } from '../schema/index' import { OptionalKind } from '../symbols/index' -import { CloneType } from '../clone/type' import { Discard } from '../discard/index' import { Never, type TNever } from '../never/index' import { Optional, type TOptional } from '../optional/index' @@ -95,7 +95,7 @@ type TResolveUnion ) // prettier-ignore -function ResolveUnion(T: T, options: SchemaOptions): TResolveUnion { +function ResolveUnion(T: T, options?: SchemaOptions): TResolveUnion { return ( IsUnionOptional(T) ? Optional(UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options)) @@ -112,11 +112,11 @@ export type TUnionEvaluated = ( TResolveUnion ) /** `[Json]` Creates an evaluated Union type */ -export function UnionEvaluated>(T: [...T], options: SchemaOptions = {}): R { +export function UnionEvaluated>(T: [...T], options?: SchemaOptions): R { // prettier-ignore return ( T.length === 0 ? Never(options) : - T.length === 1 ? CloneType(T[0], options) : + T.length === 1 ? CreateType(T[0], options) : ResolveUnion(T, options) ) as never } diff --git a/src/type/union/union.ts b/src/type/union/union.ts index 2024ad8bb..a057141c8 100644 --- a/src/type/union/union.ts +++ b/src/type/union/union.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import { type TNever, Never } from '../never/index' import type { TUnion } from './union-type' -import { CloneType } from '../clone/type' +import { CreateType } from '../create/type' import { UnionCreate } from './union-create' // prettier-ignore @@ -39,11 +39,11 @@ export type Union = ( TUnion ) /** `[Json]` Creates a Union type */ -export function Union(T: [...T], options: SchemaOptions = {}): Union { +export function Union(T: [...T], options?: SchemaOptions): Union { // prettier-ignore return ( T.length === 0 ? Never(options) : - T.length === 1 ? CloneType(T[0], options) : + T.length === 1 ? CreateType(T[0], options) : UnionCreate(T, options) ) as Union } diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts index 28dce27be..f91f9613d 100644 --- a/src/type/unknown/unknown.ts +++ b/src/type/unknown/unknown.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -34,9 +35,6 @@ export interface TUnknown extends TSchema { static: unknown } /** `[Json]` Creates an Unknown type */ -export function Unknown(options: SchemaOptions = {}): TUnknown { - return { - ...options, - [Kind]: 'Unknown', - } as never +export function Unknown(options?: SchemaOptions): TUnknown { + return CreateType({ [Kind]: 'Unknown' }, options) as never } diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts index f70ec80fc..4a52650d8 100644 --- a/src/type/unsafe/unsafe.ts +++ b/src/type/unsafe/unsafe.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -38,8 +39,5 @@ export interface TUnsafe extends TSchema { } /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ export function Unsafe(options: UnsafeOptions = {}): TUnsafe { - return { - ...options, - [Kind]: options[Kind] ?? 'Unsafe', - } as never + return CreateType({ [Kind]: options[Kind] ?? 'Unsafe' }, options) as never } diff --git a/src/type/void/void.ts b/src/type/void/void.ts index c7f704d13..0672480b9 100644 --- a/src/type/void/void.ts +++ b/src/type/void/void.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' import { Kind } from '../symbols/index' @@ -35,10 +36,6 @@ export interface TVoid extends TSchema { type: 'void' } /** `[JavaScript]` Creates a Void type */ -export function Void(options: SchemaOptions = {}): TVoid { - return { - ...options, - [Kind]: 'Void', - type: 'void', - } as never +export function Void(options?: SchemaOptions): TVoid { + return CreateType({ [Kind]: 'Void', type: 'void' }, options) as never } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index fb4f897fd..16011ae88 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -1,3 +1,10 @@ +import { TypeSystemPolicy } from '@sinclair/typebox/system' + +// ------------------------------------------------------------------ +// InstanceMode: Freeze (Detect Unintended Side Effects) +// ------------------------------------------------------------------ +TypeSystemPolicy.InstanceMode = 'default' + import './compiler/index' import './compiler-ajv/index' import './errors/index' diff --git a/test/runtime/type/guard/type/ref.ts b/test/runtime/type/guard/type/ref.ts index ab8f109bf..c864cbd4f 100644 --- a/test/runtime/type/guard/type/ref.ts +++ b/test/runtime/type/guard/type/ref.ts @@ -1,5 +1,5 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, CloneType } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TRef', () => { @@ -14,7 +14,7 @@ describe('guard/type/TRef', () => { }) it('Should not guard for TRef with invalid $ref', () => { const T = Type.Number({ $id: 'T' }) - const S = Type.Ref(T) + const S = CloneType(Type.Ref(T)) // @ts-ignore S.$ref = 1 const R = TypeGuard.IsRef(S) diff --git a/test/runtime/type/guard/type/template-literal.ts b/test/runtime/type/guard/type/template-literal.ts index 63fb736aa..f00db33c2 100644 --- a/test/runtime/type/guard/type/template-literal.ts +++ b/test/runtime/type/guard/type/template-literal.ts @@ -1,5 +1,5 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type, TemplateLiteralGenerate } from '@sinclair/typebox' +import { Type, CloneType, TemplateLiteralGenerate } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TTemplateLiteral', () => { @@ -33,13 +33,13 @@ describe('guard/type/TTemplateLiteral', () => { Assert.IsTrue(R) }) it('Should not guard for missing ^ expression prefix', () => { - const T = Type.TemplateLiteral([Type.Literal('hello')]) + const T = CloneType(Type.TemplateLiteral([Type.Literal('hello')])) // @ts-ignore T.pattern = T.pattern.slice(1) Assert.IsFalse(TypeGuard.IsTemplateLiteral(T)) }) it('Should not guard for missing $ expression postfix', () => { - const T = Type.TemplateLiteral([Type.Literal('hello')]) + const T = CloneType(Type.TemplateLiteral([Type.Literal('hello')])) // @ts-ignore T.pattern = T.pattern.slice(0, T.pattern.length - 1) Assert.IsFalse(TypeGuard.IsTemplateLiteral(T)) From b00293ee1c173c78da03824668d68b9a2c769361 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 9 Aug 2024 11:13:27 +0900 Subject: [PATCH 267/369] Revision 0.33.1 (#945) * Interior Mutation Fix + Tests * Revision --- package-lock.json | 4 +-- package.json | 2 +- src/type/create/type.ts | 2 +- src/type/intrinsic/intrinsic.ts | 3 +- test/runtime/index.ts | 2 +- test/runtime/system/policy/index.ts | 1 + test/runtime/system/policy/instance-mode.ts | 34 +++++++++++++++++++++ test/runtime/value/check/not.ts | 9 ++---- 8 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 test/runtime/system/policy/instance-mode.ts diff --git a/package-lock.json b/package-lock.json index 4b22daec6..06a44d3fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.0", + "version": "0.33.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.0", + "version": "0.33.1", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index bfe38dad3..eb4c3e5bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.0", + "version": "0.33.1", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/create/type.ts b/src/type/create/type.ts index 4d378a109..cb8371abd 100644 --- a/src/type/create/type.ts +++ b/src/type/create/type.ts @@ -33,7 +33,7 @@ import { Clone } from '../clone/value' /** Creates TypeBox schematics using the configured InstanceMode */ export function CreateType(schema: Record, options?: SchemaOptions): unknown { - const result = options !== undefined ? Object.assign(options, schema) : schema + const result = options !== undefined ? { ...options, ...schema } : schema switch (TypeSystemPolicy.InstanceMode) { case 'freeze': return Immutable(result) diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index 6745597ec..bbb139be6 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -130,6 +130,7 @@ export type TIntrinsic = T extends TUnion ? TUnion> : T extends TLiteral ? TLiteral> : T + /** Applies an intrinsic string manipulation to the given type. */ export function Intrinsic(schema: T, mode: M, options?: SchemaOptions): TIntrinsicFromMappedKey /** Applies an intrinsic string manipulation to the given type. */ @@ -141,7 +142,7 @@ export function Intrinsic(schema: TSchema, mode: IntrinsicMode, options: SchemaO // Intrinsic-Mapped-Inference IsMappedKey(schema) ? IntrinsicFromMappedKey(schema, mode, options) : // Standard-Inference - IsTemplateLiteral(schema) ? FromTemplateLiteral(schema, mode, schema) : + IsTemplateLiteral(schema) ? FromTemplateLiteral(schema, mode, options) : IsUnion(schema) ? Union(FromRest(schema.anyOf, mode), options) : IsLiteral(schema) ? Literal(FromLiteralValue(schema.const, mode), options) : // Default Type diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 16011ae88..15ca57aca 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -3,7 +3,7 @@ import { TypeSystemPolicy } from '@sinclair/typebox/system' // ------------------------------------------------------------------ // InstanceMode: Freeze (Detect Unintended Side Effects) // ------------------------------------------------------------------ -TypeSystemPolicy.InstanceMode = 'default' +TypeSystemPolicy.InstanceMode = 'freeze' import './compiler/index' import './compiler-ajv/index' diff --git a/test/runtime/system/policy/index.ts b/test/runtime/system/policy/index.ts index 29bf29666..144b64afe 100644 --- a/test/runtime/system/policy/index.ts +++ b/test/runtime/system/policy/index.ts @@ -2,3 +2,4 @@ import './allow-array-object' import './allow-nan' import './allow-null-void' import './exact-optional-property-types' +import './instance-mode' diff --git a/test/runtime/system/policy/instance-mode.ts b/test/runtime/system/policy/instance-mode.ts new file mode 100644 index 000000000..1e400c247 --- /dev/null +++ b/test/runtime/system/policy/instance-mode.ts @@ -0,0 +1,34 @@ +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('system/TypeSystemPolicy/InstanceMode', () => { + after(() => { + TypeSystemPolicy.InstanceMode = 'freeze' + }) + // --------------------------------------------------------------- + // Number + // --------------------------------------------------------------- + it('Should use instance mode default', () => { + TypeSystemPolicy.InstanceMode = 'default' + const S = Type.String() + const T = Type.Array(S) + S.$id = 'updated' + Assert.IsEqual(T.items.$id, 'updated') + }) + it('Should use instance mode clone', () => { + TypeSystemPolicy.InstanceMode = 'clone' + const S = Type.String() + const T = Type.Array(S) + S.$id = 'updated' + Assert.IsEqual(T.items.$id, undefined) + }) + it('Should use instance mode freeze', () => { + TypeSystemPolicy.InstanceMode = 'freeze' + Assert.Throws(() => { + const S = Type.String() + const T = Type.Array(S) + S.$id = 'updated' + }) + }) +}) diff --git a/test/runtime/value/check/not.ts b/test/runtime/value/check/not.ts index ba5471bf8..866ec401a 100644 --- a/test/runtime/value/check/not.ts +++ b/test/runtime/value/check/not.ts @@ -4,7 +4,7 @@ import { Assert } from '../../assert/index' describe('value/check/Not', () => { it('Should validate with not number', () => { - const T = Type.Not(Type.Number(), Type.String()) + const T = Type.Not(Type.Number()) Assert.IsEqual(Value.Check(T, 1), false) Assert.IsEqual(Value.Check(T, 'A'), true) }) @@ -14,7 +14,7 @@ describe('value/check/Not', () => { Type.Literal('A'), Type.Literal('B'), Type.Literal('C') - ]), Type.String()) + ])) Assert.IsEqual(Value.Check(T, 'A'), false) Assert.IsEqual(Value.Check(T, 'B'), false) Assert.IsEqual(Value.Check(T, 'C'), false) @@ -22,10 +22,7 @@ describe('value/check/Not', () => { }) it('Should validate with union right', () => { // prettier-ignore - const T = Type.Not(Type.Number(), Type.Union([ - Type.String(), - Type.Boolean() - ])) + const T = Type.Not(Type.Number()) Assert.IsEqual(Value.Check(T, 1), false) Assert.IsEqual(Value.Check(T, 'A'), true) Assert.IsEqual(Value.Check(T, true), true) From c4894642d2049e16adeb70e8e82b9c2ca4bab7fe Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 9 Aug 2024 17:59:50 +0900 Subject: [PATCH 268/369] Revision 0.33.2 (#947) * Retain Interior Properties on Pick, Omit and Mapped Types * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/type/mapped/mapped.ts | 22 +++++++++++--------- src/type/omit/omit.ts | 6 ++---- src/type/pick/pick.ts | 6 ++---- test/runtime/type/guard/type/omit.ts | 31 ++++++++++++++++++++++++++++ test/runtime/type/guard/type/pick.ts | 31 ++++++++++++++++++++++++++++ 7 files changed, 81 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06a44d3fd..79ede7384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.1", + "version": "0.33.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.1", + "version": "0.33.2", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index eb4c3e5bd..9f4f29ebd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.1", + "version": "0.33.2", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 30301a808..7efdd2139 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -205,6 +205,8 @@ type FromSchemaType = ( ) // prettier-ignore function FromSchemaType(K: K, T: T): FromSchemaType { + // required to retain user defined options for mapped type + const options = { ...T } return ( // unevaluated modifier types IsOptional(T) ? Optional(FromSchemaType(K, Discard(T, [OptionalKind]) as TSchema)) : @@ -213,16 +215,16 @@ function FromSchemaType(K: K, T: T): F IsMappedResult(T) ? FromMappedResult(K, T.properties) : IsMappedKey(T) ? FromMappedKey(K, T.keys) : // unevaluated types - IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) : - IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns)) : - IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items)) : - IsIterator(T) ? Iterator(FromSchemaType(K, T.items)) : - IsIntersect(T) ? Intersect(FromRest(K, T.allOf)) : - IsUnion(T) ? Union(FromRest(K, T.anyOf)) : - IsTuple(T) ? Tuple(FromRest(K, T.items ?? [])) : - IsObject(T) ? Object(FromProperties(K, T.properties)) : - IsArray(T) ? Array(FromSchemaType(K, T.items)) : - IsPromise(T) ? Promise(FromSchemaType(K, T.item)) : + IsConstructor(T) ? Constructor(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) : + IsFunction(T) ? FunctionType(FromRest(K, T.parameters), FromSchemaType(K, T.returns), options) : + IsAsyncIterator(T) ? AsyncIterator(FromSchemaType(K, T.items), options) : + IsIterator(T) ? Iterator(FromSchemaType(K, T.items), options) : + IsIntersect(T) ? Intersect(FromRest(K, T.allOf), options) : + IsUnion(T) ? Union(FromRest(K, T.anyOf), options) : + IsTuple(T) ? Tuple(FromRest(K, T.items ?? []), options) : + IsObject(T) ? Object(FromProperties(K, T.properties), options) : + IsArray(T) ? Array(FromSchemaType(K, T.items), options) : + IsPromise(T) ? Promise(FromSchemaType(K, T.item), options) : T ) as never } diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 0d959fe25..60de12989 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -93,7 +93,7 @@ function OmitResolve(T: T, K: [...K] return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? Object(FromProperties(T.properties, K)) : + IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) : Object({}) ) as never } @@ -121,7 +121,5 @@ export function Omit(T: TSchema, K: any, options?: SchemaOptions): any { if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = OmitResolve(T, I) - return CreateType({ ...D, ...R }, options) + return CreateType(OmitResolve(T, I), options) } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index 017914046..5340a4674 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -86,7 +86,7 @@ function PickResolve(T: T, K: [...K] return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? Object(FromProperties(T.properties, K)) : + IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) : Object({}) ) as never } @@ -112,7 +112,5 @@ export function Pick(T: TSchema, K: any, options?: SchemaOptions): any { if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = PickResolve(T, I) - return CreateType({ ...D, ...R }, options) + return CreateType(PickResolve(T, I), options) } diff --git a/test/runtime/type/guard/type/omit.ts b/test/runtime/type/guard/type/omit.ts index d9c77c686..dbd7e023c 100644 --- a/test/runtime/type/guard/type/omit.ts +++ b/test/runtime/type/guard/type/omit.ts @@ -126,4 +126,35 @@ describe('guard/type/TOmit', () => { const R = Type.Omit(S, ['x']) Assert.IsFalse(TransformKind in R) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/944 + // ---------------------------------------------------------------- + it('Should retain interior properties 1', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const T = Type.Omit(A, ['x']) + Assert.IsFalse(T.additionalProperties as boolean) + }) + it('Should retain interior properties 2', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const U = Type.Union([A, B]) + const T = Type.Omit(U, ['x']) + Assert.IsFalse(T.anyOf[0].additionalProperties as boolean) + Assert.IsFalse(T.anyOf[1].additionalProperties as boolean) + }) + it('Should retain interior properties 3', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const U = Type.Intersect([A, B]) + const T = Type.Omit(U, ['x']) + Assert.IsFalse(T.allOf[0].additionalProperties as boolean) + Assert.IsFalse(T.allOf[1].additionalProperties as boolean) + }) + it('Should retain interior properties 4', () => { + const A = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) + const T = Type.Mapped(Type.TemplateLiteral('${x|y|z}'), (_) => Type.Omit(A, ['x'])) + Assert.IsFalse(T.properties.x.additionalProperties as boolean) + Assert.IsFalse(T.properties.y.additionalProperties as boolean) + Assert.IsFalse(T.properties.z.additionalProperties as boolean) + }) }) diff --git a/test/runtime/type/guard/type/pick.ts b/test/runtime/type/guard/type/pick.ts index e1cf25952..cb3c5edff 100644 --- a/test/runtime/type/guard/type/pick.ts +++ b/test/runtime/type/guard/type/pick.ts @@ -128,4 +128,35 @@ describe('guard/type/TPick', () => { const R = Type.Pick(S, ['x']) Assert.IsFalse(TransformKind in R) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/944 + // ---------------------------------------------------------------- + it('Should retain interior properties 1', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const T = Type.Pick(A, ['x']) + Assert.IsFalse(T.additionalProperties as boolean) + }) + it('Should retain interior properties 2', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const U = Type.Union([A, B]) + const T = Type.Pick(U, ['x']) + Assert.IsFalse(T.anyOf[0].additionalProperties as boolean) + Assert.IsFalse(T.anyOf[1].additionalProperties as boolean) + }) + it('Should retain interior properties 3', () => { + const A = Type.Object({ x: Type.Number() }, { additionalProperties: false }) + const B = Type.Object({ y: Type.Number() }, { additionalProperties: false }) + const U = Type.Intersect([A, B]) + const T = Type.Pick(U, ['x']) + Assert.IsFalse(T.allOf[0].additionalProperties as boolean) + Assert.IsFalse(T.allOf[1].additionalProperties as boolean) + }) + it('Should retain interior properties 4', () => { + const A = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) + const T = Type.Mapped(Type.TemplateLiteral('${x|y|z}'), (_) => Type.Pick(A, ['x'])) + Assert.IsFalse(T.properties.x.additionalProperties as boolean) + Assert.IsFalse(T.properties.y.additionalProperties as boolean) + Assert.IsFalse(T.properties.z.additionalProperties as boolean) + }) }) From a5b03c0b83bb746fb9d4685bc1c2809d49a9e0a1 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 9 Aug 2024 22:29:53 +0900 Subject: [PATCH 269/369] Revision 0.33.3 (#950) * Reimplement Object Diff * Version --- package-lock.json | 4 +-- package.json | 2 +- src/value/delta/delta.ts | 55 ++++++++++++++++++------------- test/runtime/value/delta/diff.ts | 22 +++++++++---- test/runtime/value/delta/patch.ts | 10 ++++++ 5 files changed, 61 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 79ede7384..e2eb7ab66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.2", + "version": "0.33.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.2", + "version": "0.33.3", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 9f4f29ebd..07be5d30f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.2", + "version": "0.33.3", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/delta/delta.ts b/src/value/delta/delta.ts index bd437acc1..fd65c57cc 100644 --- a/src/value/delta/delta.ts +++ b/src/value/delta/delta.ts @@ -26,11 +26,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsStandardObject, IsArray, IsTypedArray, IsValueType, IsSymbol, IsUndefined } from '../guard/index' +import { HasPropertyKey, IsStandardObject, IsArray, IsTypedArray, IsValueType } from '../guard/index' import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' import type { Static } from '../../type/static/index' import { ValuePointer } from '../pointer/index' import { Clone } from '../clone/index' +import { Equal } from '../equal/equal' import { TypeBoxError } from '../../type/error/index' import { Literal, type TLiteral } from '../../type/literal/index' @@ -85,16 +86,11 @@ export const Edit: TUnion<[typeof Insert, typeof Update, typeof Delete]> = Union // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ -export class ValueDeltaError extends TypeBoxError { +export class ValueDiffError extends TypeBoxError { constructor(public readonly value: unknown, message: string) { super(message) } } -export class ValueDeltaSymbolError extends ValueDeltaError { - constructor(public readonly value: unknown) { - super(value, 'Cannot diff objects with symbol keys') - } -} // ------------------------------------------------------------------ // Command Factory // ------------------------------------------------------------------ @@ -108,28 +104,41 @@ function CreateDelete(path: string): Edit { return { type: 'delete', path } } // ------------------------------------------------------------------ +// AssertDiffable +// ------------------------------------------------------------------ +function AssertDiffable(value: unknown): asserts value is Record { + if (globalThis.Object.getOwnPropertySymbols(value).length > 0) throw new ValueDiffError(value, 'Cannot diff objects with symbols') +} +// ------------------------------------------------------------------ // Diffing Generators // ------------------------------------------------------------------ function* ObjectType(path: string, current: ObjectType, next: unknown): IterableIterator { + AssertDiffable(current) + AssertDiffable(next) if (!IsStandardObject(next)) return yield CreateUpdate(path, next) - const currentKeys = [...globalThis.Object.keys(current), ...globalThis.Object.getOwnPropertySymbols(current)] - const nextKeys = [...globalThis.Object.keys(next), ...globalThis.Object.getOwnPropertySymbols(next)] - for (const key of currentKeys) { - if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) - if (IsUndefined(next[key]) && nextKeys.includes(key)) yield CreateUpdate(`${path}/${globalThis.String(key)}`, undefined) - } + const currentKeys = globalThis.Object.getOwnPropertyNames(current) + const nextKeys = globalThis.Object.getOwnPropertyNames(next) + // ---------------------------------------------------------------- + // inserts + // ---------------------------------------------------------------- for (const key of nextKeys) { - if (IsUndefined(current[key]) || IsUndefined(next[key])) continue - if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) - yield* Visit(`${path}/${globalThis.String(key)}`, current[key], next[key]) + if (HasPropertyKey(current, key)) continue + yield CreateInsert(`${path}/${key}`, next[key]) } - for (const key of nextKeys) { - if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) - if (IsUndefined(current[key])) yield CreateInsert(`${path}/${globalThis.String(key)}`, next[key]) + // ---------------------------------------------------------------- + // updates + // ---------------------------------------------------------------- + for (const key of currentKeys) { + if (!HasPropertyKey(next, key)) continue + if (Equal(current, next)) continue + yield* Visit(`${path}/${key}`, current[key], next[key]) } - for (const key of currentKeys.reverse()) { - if (IsSymbol(key)) throw new ValueDeltaSymbolError(key) - if (IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${globalThis.String(key)}`) + // ---------------------------------------------------------------- + // deletes + // ---------------------------------------------------------------- + for (const key of currentKeys) { + if (HasPropertyKey(next, key)) continue + yield CreateDelete(`${path}/${key}`) } } function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIterator { @@ -161,7 +170,7 @@ function* Visit(path: string, current: unknown, next: unknown): IterableIterator if (IsArray(current)) return yield* ArrayType(path, current, next) if (IsTypedArray(current)) return yield* TypedArrayType(path, current, next) if (IsValueType(current)) return yield* ValueType(path, current, next) - throw new ValueDeltaError(current, 'Unable to create diff edits for unknown value') + throw new ValueDiffError(current, 'Unable to diff value') } // ------------------------------------------------------------------ // Diff diff --git a/test/runtime/value/delta/diff.ts b/test/runtime/value/delta/diff.ts index 2943e157f..6be662fcb 100644 --- a/test/runtime/value/delta/diff.ts +++ b/test/runtime/value/delta/diff.ts @@ -251,14 +251,14 @@ describe('value/delta/Diff', () => { const A = { x: 1, y: 1, z: 1 } const B = { a: 2, b: 2, c: 2 } const D = Value.Diff(A, B) - const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] + const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/x'), Delete('/y'), Delete('/z')] Assert.IsEqual(D, E) }) - it('Should diff PROPERTY update, insert and delete order preserved', () => { + it('Should diff PROPERTY insert, update, and delete order preserved', () => { const A = { x: 1, y: 1, z: 1, w: 1 } const B = { a: 2, b: 2, c: 2, w: 2 } const D = Value.Diff(A, B) - const E = [Update('/w', 2), Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Delete('/z'), Delete('/y'), Delete('/x')] + const E = [Insert('/a', 2), Insert('/b', 2), Insert('/c', 2), Update('/w', 2), Delete('/x'), Delete('/y'), Delete('/z')] Assert.IsEqual(D, E) }) // ---------------------------------------------------- @@ -303,7 +303,7 @@ describe('value/delta/Diff', () => { const A = { v: { x: 1, y: 1 } } const B = { v: { x: 2, w: 2 } } const D = Value.Diff(A, B) - const E = [Update('/v/x', B.v.x), Insert('/v/w', B.v.w), Delete('/v/y')] + const E = [Insert('/v/w', B.v.w), Update('/v/x', B.v.x), Delete('/v/y')] Assert.IsEqual(D, E) }) // ---------------------------------------------------- @@ -344,11 +344,11 @@ describe('value/delta/Diff', () => { const E = [Delete('/0/v/z')] Assert.IsEqual(D, E) }) - it('Should diff NESTED ARRAY update, insert and delete order preserved', () => { + it('Should diff NESTED ARRAY insert update and delete order preserved', () => { const A = [{ v: { x: 1, y: 1 } }] const B = [{ v: { x: 2, w: 2 } }] const D = Value.Diff(A, B) - const E = [Update('/0/v/x', B[0].v.x), Insert('/0/v/w', B[0].v.w), Delete('/0/v/y')] + const E = [Insert('/0/v/w', B[0].v.w), Update('/0/v/x', B[0].v.x), Delete('/0/v/y')] Assert.IsEqual(D, E) }) it('Should throw if attempting to diff a current value with symbol key', () => { @@ -382,4 +382,14 @@ describe('value/delta/Diff', () => { const E = [Update('', new Uint8Array([0, 9, 2, 3, 4]))] Assert.IsEqual(D, E) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/937 + // ---------------------------------------------------------------- + it('Should generate no diff for undefined properties of current and next', () => { + const A = { a: undefined } + const B = { a: undefined } + const D = Value.Diff(A, B) + const E = [] as any + Assert.IsEqual(D, E) + }) }) diff --git a/test/runtime/value/delta/patch.ts b/test/runtime/value/delta/patch.ts index 7e308c42a..2addfe53c 100644 --- a/test/runtime/value/delta/patch.ts +++ b/test/runtime/value/delta/patch.ts @@ -411,4 +411,14 @@ describe('value/delta/Patch', () => { const P = Value.Patch(A, D) Assert.IsEqual(B, P) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/937 + // ---------------------------------------------------------------- + it('Should generate no diff for undefined properties of current and next', () => { + const A = { a: undefined } + const B = { a: undefined } + const D = Value.Diff(A, B) + const P = Value.Patch(A, D) + Assert.IsEqual(B, P) + }) }) From 92851ea30a361288665ee65fa8e11f43ea3d1f77 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 10 Aug 2024 18:16:09 +0900 Subject: [PATCH 270/369] Revision 0.33.4 (#953) * Add Assert and Parse Functions * Documentation * Version * Benchmarks --- package-lock.json | 4 +- package.json | 2 +- readme.md | 244 ++++++++++++------------- src/compiler/compiler.ts | 9 +- src/value/assert/assert.ts | 73 ++++++++ src/value/assert/index.ts | 29 +++ src/value/cast/cast.ts | 4 +- src/value/clean/clean.ts | 15 +- src/value/clone/clone.ts | 32 ++-- src/value/convert/convert.ts | 41 ++--- src/value/create/create.ts | 6 +- src/value/default/default.ts | 30 ++- src/value/equal/equal.ts | 6 +- src/value/hash/hash.ts | 4 +- src/value/index.ts | 2 + src/value/mutate/mutate.ts | 10 +- src/value/parse/index.ts | 29 +++ src/value/parse/parse.ts | 68 +++++++ src/value/transform/decode.ts | 14 +- src/value/transform/encode.ts | 16 +- src/value/transform/has.ts | 6 +- src/value/value/value.ts | 42 +++-- test/runtime/value/assert/assert.ts | 35 ++++ test/runtime/value/assert/index.ts | 1 + test/runtime/value/default/union.ts | 12 +- test/runtime/value/index.ts | 2 + test/runtime/value/parse/index.ts | 1 + test/runtime/value/parse/parse.ts | 90 +++++++++ test/runtime/value/transform/record.ts | 1 - 29 files changed, 592 insertions(+), 236 deletions(-) create mode 100644 src/value/assert/assert.ts create mode 100644 src/value/assert/index.ts create mode 100644 src/value/parse/index.ts create mode 100644 src/value/parse/parse.ts create mode 100644 test/runtime/value/assert/assert.ts create mode 100644 test/runtime/value/assert/index.ts create mode 100644 test/runtime/value/parse/index.ts create mode 100644 test/runtime/value/parse/parse.ts diff --git a/package-lock.json b/package-lock.json index e2eb7ab66..01590d97b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.3", + "version": "0.33.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.3", + "version": "0.33.4", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 07be5d30f..04c5b2a29 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.3", + "version": "0.33.4", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 6d1aa239b..1fca89253 100644 --- a/readme.md +++ b/readme.md @@ -74,13 +74,12 @@ License MIT - [Indexed](#types-indexed) - [Mapped](#types-mapped) - [Conditional](#types-conditional) - - [Intrinsic](#types-intrinsic) - [Transform](#types-transform) - - [Rest](#types-rest) - [Guard](#types-guard) - [Unsafe](#types-unsafe) - [Strict](#types-strict) - [Values](#values) + - [Assert](#values-assert) - [Create](#values-create) - [Clone](#values-clone) - [Check](#values-check) @@ -90,6 +89,7 @@ License MIT - [Cast](#values-cast) - [Decode](#values-decode) - [Encode](#values-decode) + - [Parse](#values-parse) - [Equal](#values-equal) - [Hash](#values-hash) - [Diff](#values-diff) @@ -176,19 +176,17 @@ type T = Static // type T = { //-------------------------------------------------------------------------------------------- // -// ... then use the type both as Json Schema and as a TypeScript type. +// ... or use the type to parse JavaScript values. // //-------------------------------------------------------------------------------------------- import { Value } from '@sinclair/typebox/value' -function receive(value: T) { // ... as a Static Type - - if(Value.Check(T, value)) { // ... as a Json Schema - - // ok... - } -} +const R = Value.Parse(T, value) // const R: { + // id: string, + // name: string, + // timestamp: number + // } ``` @@ -1000,38 +998,6 @@ const C = Type.Exclude( // type C = Exclude<1 | 2 | ) // ]> ``` - - -### Intrinsic Types - -TypeBox supports the TypeScript intrinsic string manipulation types Uppercase, Lowercase, Capitalize and Uncapitalize. These types can be used to remap Literal, Template Literal and Union of Literal types. - -```typescript -// TypeScript -type A = Capitalize<'hello'> // type A = 'Hello' - -type B = Capitalize<'hello' | 'world'> // type C = 'Hello' | 'World' - -type C = Capitalize<`hello${1|2|3}`> // type B = 'Hello1' | 'Hello2' | 'Hello3' - -// TypeBox -const A = Type.Capitalize(Type.Literal('hello')) // const A: TLiteral<'Hello'> - -const B = Type.Capitalize(Type.Union([ // const B: TUnion<[ - Type.Literal('hello'), // TLiteral<'Hello'>, - Type.Literal('world') // TLiteral<'World'> -])) // ]> - -const C = Type.Capitalize( // const C: TTemplateLiteral<[ - Type.TemplateLiteral('hello${1|2|3}') // TLiteral<'Hello'>, -) // TUnion<[ - // TLiteral<'1'>, - // TLiteral<'2'>, - // TLiteral<'3'> - // ]> - // ]> -``` - ### Transform Types @@ -1061,26 +1027,6 @@ type E = StaticEncode // type E = Array type T = Static // type T = Array ``` - - -### Rest Types - -TypeBox provides the Rest type to uniformly extract variadic tuples from Intersect, Union and Tuple types. This type can be useful to remap variadic types into different forms. The following uses Rest to remap a Tuple into a Union. - -```typescript -const T = Type.Tuple([ // const T: TTuple<[ - Type.String(), // TString, - Type.Number() // TNumber -]) // ]> - -const R = Type.Rest(T) // const R: [TString, TNumber] - -const U = Type.Union(R) // const T: TUnion<[ - // TString, - // TNumber - // ]> -``` - ### Unsafe Types @@ -1171,6 +1117,18 @@ TypeBox provides an optional Value submodule that can be used to perform structu import { Value } from '@sinclair/typebox/value' ``` + + +### Assert + +Use the Assert function to assert a value is valid. + +```typescript +let value: unknown = 1 + +Value.Assert(Type.Number(), value) // throws AssertError if invalid +``` + ### Create @@ -1296,6 +1254,32 @@ const A = Value.Encode(Type.String(), 'hello') // const A = 'hello' const B = Value.Encode(Type.String(), 42) // throw ``` + + +### Parse + +Use the Parse function to parse a value or throw if invalid. This function internally uses Default, Clean, Convert and Decode to make a best effort attempt to parse the value into the expected type. This function should not be used in performance critical code paths. + +```typescript +const T = Type.Object({ x: Type.Number({ default: 0 }), y: Type.Number({ default: 0 }) }) + +// Default + +const A = Value.Parse(T, { }) // const A = { x: 0, y: 0 } + +// Convert + +const B = Value.Parse(T, { x: '1', y: '2' }) // const B = { x: 1, y: 2 } + +// Clean + +const C = Value.Parse(T, { x: 1, y: 2, z: 3 }) // const C = { x: 1, y: 2 } + +// Assert + +const D = Value.Parse(T, undefined) // throws AssertError +``` + ### Equal @@ -1716,37 +1700,37 @@ This benchmark measures compilation performance for varying types. ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ -│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ +│ (index) │ Iterations │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000 │ ' 242 ms' │ ' 10 ms' │ ' 24.20 x' │ -│ Literal_Number │ 1000 │ ' 200 ms' │ ' 8 ms' │ ' 25.00 x' │ -│ Literal_Boolean │ 1000 │ ' 168 ms' │ ' 6 ms' │ ' 28.00 x' │ -│ Primitive_Number │ 1000 │ ' 165 ms' │ ' 8 ms' │ ' 20.63 x' │ -│ Primitive_String │ 1000 │ ' 154 ms' │ ' 6 ms' │ ' 25.67 x' │ -│ Primitive_String_Pattern │ 1000 │ ' 208 ms' │ ' 14 ms' │ ' 14.86 x' │ -│ Primitive_Boolean │ 1000 │ ' 142 ms' │ ' 6 ms' │ ' 23.67 x' │ -│ Primitive_Null │ 1000 │ ' 143 ms' │ ' 6 ms' │ ' 23.83 x' │ -│ Object_Unconstrained │ 1000 │ ' 1217 ms' │ ' 31 ms' │ ' 39.26 x' │ -│ Object_Constrained │ 1000 │ ' 1275 ms' │ ' 26 ms' │ ' 49.04 x' │ -│ Object_Vector3 │ 1000 │ ' 405 ms' │ ' 12 ms' │ ' 33.75 x' │ -│ Object_Box3D │ 1000 │ ' 1833 ms' │ ' 27 ms' │ ' 67.89 x' │ -│ Tuple_Primitive │ 1000 │ ' 475 ms' │ ' 13 ms' │ ' 36.54 x' │ -│ Tuple_Object │ 1000 │ ' 1267 ms' │ ' 30 ms' │ ' 42.23 x' │ -│ Composite_Intersect │ 1000 │ ' 604 ms' │ ' 18 ms' │ ' 33.56 x' │ -│ Composite_Union │ 1000 │ ' 545 ms' │ ' 20 ms' │ ' 27.25 x' │ -│ Math_Vector4 │ 1000 │ ' 829 ms' │ ' 12 ms' │ ' 69.08 x' │ -│ Math_Matrix4 │ 1000 │ ' 405 ms' │ ' 10 ms' │ ' 40.50 x' │ -│ Array_Primitive_Number │ 1000 │ ' 372 ms' │ ' 12 ms' │ ' 31.00 x' │ -│ Array_Primitive_String │ 1000 │ ' 327 ms' │ ' 5 ms' │ ' 65.40 x' │ -│ Array_Primitive_Boolean │ 1000 │ ' 300 ms' │ ' 4 ms' │ ' 75.00 x' │ -│ Array_Object_Unconstrained │ 1000 │ ' 1755 ms' │ ' 21 ms' │ ' 83.57 x' │ -│ Array_Object_Constrained │ 1000 │ ' 1516 ms' │ ' 20 ms' │ ' 75.80 x' │ -│ Array_Tuple_Primitive │ 1000 │ ' 825 ms' │ ' 14 ms' │ ' 58.93 x' │ -│ Array_Tuple_Object │ 1000 │ ' 1616 ms' │ ' 16 ms' │ ' 101.00 x' │ -│ Array_Composite_Intersect │ 1000 │ ' 776 ms' │ ' 16 ms' │ ' 48.50 x' │ -│ Array_Composite_Union │ 1000 │ ' 820 ms' │ ' 14 ms' │ ' 58.57 x' │ -│ Array_Math_Vector4 │ 1000 │ ' 1166 ms' │ ' 15 ms' │ ' 77.73 x' │ -│ Array_Math_Matrix4 │ 1000 │ ' 695 ms' │ ' 8 ms' │ ' 86.88 x' │ +│ Literal_String │ 1000 │ ' 211 ms' │ ' 8 ms' │ ' 26.38 x' │ +│ Literal_Number │ 1000 │ ' 185 ms' │ ' 5 ms' │ ' 37.00 x' │ +│ Literal_Boolean │ 1000 │ ' 195 ms' │ ' 4 ms' │ ' 48.75 x' │ +│ Primitive_Number │ 1000 │ ' 149 ms' │ ' 7 ms' │ ' 21.29 x' │ +│ Primitive_String │ 1000 │ ' 135 ms' │ ' 5 ms' │ ' 27.00 x' │ +│ Primitive_String_Pattern │ 1000 │ ' 193 ms' │ ' 10 ms' │ ' 19.30 x' │ +│ Primitive_Boolean │ 1000 │ ' 152 ms' │ ' 4 ms' │ ' 38.00 x' │ +│ Primitive_Null │ 1000 │ ' 147 ms' │ ' 4 ms' │ ' 36.75 x' │ +│ Object_Unconstrained │ 1000 │ ' 1065 ms' │ ' 26 ms' │ ' 40.96 x' │ +│ Object_Constrained │ 1000 │ ' 1183 ms' │ ' 26 ms' │ ' 45.50 x' │ +│ Object_Vector3 │ 1000 │ ' 407 ms' │ ' 9 ms' │ ' 45.22 x' │ +│ Object_Box3D │ 1000 │ ' 1777 ms' │ ' 24 ms' │ ' 74.04 x' │ +│ Tuple_Primitive │ 1000 │ ' 485 ms' │ ' 11 ms' │ ' 44.09 x' │ +│ Tuple_Object │ 1000 │ ' 1344 ms' │ ' 17 ms' │ ' 79.06 x' │ +│ Composite_Intersect │ 1000 │ ' 606 ms' │ ' 14 ms' │ ' 43.29 x' │ +│ Composite_Union │ 1000 │ ' 522 ms' │ ' 17 ms' │ ' 30.71 x' │ +│ Math_Vector4 │ 1000 │ ' 851 ms' │ ' 9 ms' │ ' 94.56 x' │ +│ Math_Matrix4 │ 1000 │ ' 406 ms' │ ' 10 ms' │ ' 40.60 x' │ +│ Array_Primitive_Number │ 1000 │ ' 367 ms' │ ' 6 ms' │ ' 61.17 x' │ +│ Array_Primitive_String │ 1000 │ ' 339 ms' │ ' 7 ms' │ ' 48.43 x' │ +│ Array_Primitive_Boolean │ 1000 │ ' 325 ms' │ ' 5 ms' │ ' 65.00 x' │ +│ Array_Object_Unconstrained │ 1000 │ ' 1863 ms' │ ' 21 ms' │ ' 88.71 x' │ +│ Array_Object_Constrained │ 1000 │ ' 1535 ms' │ ' 18 ms' │ ' 85.28 x' │ +│ Array_Tuple_Primitive │ 1000 │ ' 829 ms' │ ' 14 ms' │ ' 59.21 x' │ +│ Array_Tuple_Object │ 1000 │ ' 1674 ms' │ ' 14 ms' │ ' 119.57 x' │ +│ Array_Composite_Intersect │ 1000 │ ' 789 ms' │ ' 13 ms' │ ' 60.69 x' │ +│ Array_Composite_Union │ 1000 │ ' 822 ms' │ ' 15 ms' │ ' 54.80 x' │ +│ Array_Math_Vector4 │ 1000 │ ' 1129 ms' │ ' 14 ms' │ ' 80.64 x' │ +│ Array_Math_Matrix4 │ 1000 │ ' 673 ms' │ ' 9 ms' │ ' 74.78 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1758,39 +1742,39 @@ This benchmark measures validation performance for varying types. ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ -│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ +│ (index) │ Iterations │ ValueCheck │ Ajv │ TypeCompiler │ Performance │ ├────────────────────────────┼────────────┼──────────────┼──────────────┼──────────────┼──────────────┤ -│ Literal_String │ 1000000 │ ' 18 ms' │ ' 5 ms' │ ' 4 ms' │ ' 1.25 x' │ -│ Literal_Number │ 1000000 │ ' 16 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ -│ Literal_Boolean │ 1000000 │ ' 15 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_Number │ 1000000 │ ' 21 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ -│ Primitive_String │ 1000000 │ ' 22 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ -│ Primitive_String_Pattern │ 1000000 │ ' 155 ms' │ ' 41 ms' │ ' 34 ms' │ ' 1.21 x' │ -│ Primitive_Boolean │ 1000000 │ ' 18 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Primitive_Null │ 1000000 │ ' 19 ms' │ ' 17 ms' │ ' 9 ms' │ ' 1.89 x' │ -│ Object_Unconstrained │ 1000000 │ ' 1003 ms' │ ' 32 ms' │ ' 24 ms' │ ' 1.33 x' │ -│ Object_Constrained │ 1000000 │ ' 1265 ms' │ ' 49 ms' │ ' 38 ms' │ ' 1.29 x' │ -│ Object_Vector3 │ 1000000 │ ' 418 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ -│ Object_Box3D │ 1000000 │ ' 2035 ms' │ ' 56 ms' │ ' 49 ms' │ ' 1.14 x' │ -│ Object_Recursive │ 1000000 │ ' 5243 ms' │ ' 326 ms' │ ' 157 ms' │ ' 2.08 x' │ -│ Tuple_Primitive │ 1000000 │ ' 153 ms' │ ' 20 ms' │ ' 12 ms' │ ' 1.67 x' │ -│ Tuple_Object │ 1000000 │ ' 781 ms' │ ' 28 ms' │ ' 18 ms' │ ' 1.56 x' │ -│ Composite_Intersect │ 1000000 │ ' 742 ms' │ ' 25 ms' │ ' 14 ms' │ ' 1.79 x' │ -│ Composite_Union │ 1000000 │ ' 558 ms' │ ' 24 ms' │ ' 13 ms' │ ' 1.85 x' │ -│ Math_Vector4 │ 1000000 │ ' 246 ms' │ ' 22 ms' │ ' 11 ms' │ ' 2.00 x' │ -│ Math_Matrix4 │ 1000000 │ ' 1052 ms' │ ' 43 ms' │ ' 28 ms' │ ' 1.54 x' │ -│ Array_Primitive_Number │ 1000000 │ ' 272 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ -│ Array_Primitive_String │ 1000000 │ ' 235 ms' │ ' 24 ms' │ ' 14 ms' │ ' 1.71 x' │ -│ Array_Primitive_Boolean │ 1000000 │ ' 134 ms' │ ' 23 ms' │ ' 14 ms' │ ' 1.64 x' │ -│ Array_Object_Unconstrained │ 1000000 │ ' 6280 ms' │ ' 65 ms' │ ' 59 ms' │ ' 1.10 x' │ -│ Array_Object_Constrained │ 1000000 │ ' 6076 ms' │ ' 130 ms' │ ' 119 ms' │ ' 1.09 x' │ -│ Array_Object_Recursive │ 1000000 │ ' 22738 ms' │ ' 1730 ms' │ ' 635 ms' │ ' 2.72 x' │ -│ Array_Tuple_Primitive │ 1000000 │ ' 689 ms' │ ' 35 ms' │ ' 30 ms' │ ' 1.17 x' │ -│ Array_Tuple_Object │ 1000000 │ ' 3266 ms' │ ' 63 ms' │ ' 52 ms' │ ' 1.21 x' │ -│ Array_Composite_Intersect │ 1000000 │ ' 3310 ms' │ ' 44 ms' │ ' 36 ms' │ ' 1.22 x' │ -│ Array_Composite_Union │ 1000000 │ ' 2432 ms' │ ' 69 ms' │ ' 33 ms' │ ' 2.09 x' │ -│ Array_Math_Vector4 │ 1000000 │ ' 1158 ms' │ ' 37 ms' │ ' 24 ms' │ ' 1.54 x' │ -│ Array_Math_Matrix4 │ 1000000 │ ' 5435 ms' │ ' 132 ms' │ ' 92 ms' │ ' 1.43 x' │ +│ Literal_String │ 1000000 │ ' 17 ms' │ ' 5 ms' │ ' 5 ms' │ ' 1.00 x' │ +│ Literal_Number │ 1000000 │ ' 14 ms' │ ' 18 ms' │ ' 9 ms' │ ' 2.00 x' │ +│ Literal_Boolean │ 1000000 │ ' 14 ms' │ ' 20 ms' │ ' 9 ms' │ ' 2.22 x' │ +│ Primitive_Number │ 1000000 │ ' 17 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ +│ Primitive_String │ 1000000 │ ' 17 ms' │ ' 18 ms' │ ' 10 ms' │ ' 1.80 x' │ +│ Primitive_String_Pattern │ 1000000 │ ' 172 ms' │ ' 46 ms' │ ' 41 ms' │ ' 1.12 x' │ +│ Primitive_Boolean │ 1000000 │ ' 14 ms' │ ' 19 ms' │ ' 10 ms' │ ' 1.90 x' │ +│ Primitive_Null │ 1000000 │ ' 16 ms' │ ' 19 ms' │ ' 9 ms' │ ' 2.11 x' │ +│ Object_Unconstrained │ 1000000 │ ' 437 ms' │ ' 28 ms' │ ' 14 ms' │ ' 2.00 x' │ +│ Object_Constrained │ 1000000 │ ' 653 ms' │ ' 46 ms' │ ' 37 ms' │ ' 1.24 x' │ +│ Object_Vector3 │ 1000000 │ ' 201 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ +│ Object_Box3D │ 1000000 │ ' 961 ms' │ ' 37 ms' │ ' 19 ms' │ ' 1.95 x' │ +│ Object_Recursive │ 1000000 │ ' 3715 ms' │ ' 363 ms' │ ' 174 ms' │ ' 2.09 x' │ +│ Tuple_Primitive │ 1000000 │ ' 107 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ +│ Tuple_Object │ 1000000 │ ' 375 ms' │ ' 28 ms' │ ' 15 ms' │ ' 1.87 x' │ +│ Composite_Intersect │ 1000000 │ ' 377 ms' │ ' 22 ms' │ ' 12 ms' │ ' 1.83 x' │ +│ Composite_Union │ 1000000 │ ' 337 ms' │ ' 30 ms' │ ' 17 ms' │ ' 1.76 x' │ +│ Math_Vector4 │ 1000000 │ ' 137 ms' │ ' 23 ms' │ ' 11 ms' │ ' 2.09 x' │ +│ Math_Matrix4 │ 1000000 │ ' 576 ms' │ ' 37 ms' │ ' 28 ms' │ ' 1.32 x' │ +│ Array_Primitive_Number │ 1000000 │ ' 145 ms' │ ' 23 ms' │ ' 12 ms' │ ' 1.92 x' │ +│ Array_Primitive_String │ 1000000 │ ' 152 ms' │ ' 22 ms' │ ' 13 ms' │ ' 1.69 x' │ +│ Array_Primitive_Boolean │ 1000000 │ ' 131 ms' │ ' 20 ms' │ ' 13 ms' │ ' 1.54 x' │ +│ Array_Object_Unconstrained │ 1000000 │ ' 2821 ms' │ ' 62 ms' │ ' 45 ms' │ ' 1.38 x' │ +│ Array_Object_Constrained │ 1000000 │ ' 2958 ms' │ ' 119 ms' │ ' 134 ms' │ ' 0.89 x' │ +│ Array_Object_Recursive │ 1000000 │ ' 14695 ms' │ ' 1621 ms' │ ' 635 ms' │ ' 2.55 x' │ +│ Array_Tuple_Primitive │ 1000000 │ ' 478 ms' │ ' 35 ms' │ ' 28 ms' │ ' 1.25 x' │ +│ Array_Tuple_Object │ 1000000 │ ' 1623 ms' │ ' 63 ms' │ ' 48 ms' │ ' 1.31 x' │ +│ Array_Composite_Intersect │ 1000000 │ ' 1582 ms' │ ' 43 ms' │ ' 30 ms' │ ' 1.43 x' │ +│ Array_Composite_Union │ 1000000 │ ' 1331 ms' │ ' 76 ms' │ ' 40 ms' │ ' 1.90 x' │ +│ Array_Math_Vector4 │ 1000000 │ ' 564 ms' │ ' 38 ms' │ ' 24 ms' │ ' 1.58 x' │ +│ Array_Math_Matrix4 │ 1000000 │ ' 2382 ms' │ ' 111 ms' │ ' 83 ms' │ ' 1.34 x' │ └────────────────────────────┴────────────┴──────────────┴──────────────┴──────────────┴──────────────┘ ``` @@ -1802,13 +1786,13 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ```typescript ┌──────────────────────┬────────────┬────────────┬─────────────┐ -│ (index) │ Compiled │ Minified │ Compression │ +│ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '126.9 kb' │ ' 55.7 kb' │ '2.28 x' │ -│ typebox/errors │ ' 46.1 kb' │ ' 20.8 kb' │ '2.22 x' │ -│ typebox/system │ ' 4.7 kb' │ ' 2.0 kb' │ '2.33 x' │ -│ typebox/value │ '152.2 kb' │ ' 64.5 kb' │ '2.36 x' │ -│ typebox │ ' 95.7 kb' │ ' 39.8 kb' │ '2.40 x' │ +│ typebox/compiler │ '119.6 kb' │ ' 52.6 kb' │ '2.27 x' │ +│ typebox/errors │ ' 48.6 kb' │ ' 21.9 kb' │ '2.22 x' │ +│ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ +│ typebox/value │ '157.8 kb' │ ' 66.6 kb' │ '2.37 x' │ +│ typebox │ ' 98.3 kb' │ ' 40.9 kb' │ '2.40 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 8e939f1f7..f41203479 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -71,6 +71,7 @@ import type { TSymbol } from '../type/symbol/index' import type { TUndefined } from '../type/undefined/index' import type { TUint8Array } from '../type/uint8array/index' import type { TVoid } from '../type/void/index' + // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ @@ -104,15 +105,15 @@ export class TypeCheck { return this.checkFunc(value) } /** Decodes a value or throws if error */ - public Decode(value: unknown): StaticDecode { + public Decode>(value: unknown): R { if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) - return this.hasTransform ? TransformDecode(this.schema, this.references, value) : value + return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never } /** Encodes a value or throws if error */ - public Encode(value: unknown): StaticEncode { + public Encode>(value: unknown): R { const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) - return encoded + return encoded as never } } // ------------------------------------------------------------------ diff --git a/src/value/assert/assert.ts b/src/value/assert/assert.ts new file mode 100644 index 000000000..07a50db10 --- /dev/null +++ b/src/value/assert/assert.ts @@ -0,0 +1,73 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Errors, ValueErrorIterator, ValueError } from '../../errors/index' +import { TypeBoxError } from '../../type/error/error' +import { TSchema } from '../../type/schema/index' +import { Static } from '../../type/static/index' +import { Check } from '../check/check' + +// ------------------------------------------------------------------ +// AssertError +// ------------------------------------------------------------------ +export class AssertError extends TypeBoxError { + readonly #iterator: ValueErrorIterator + error: ValueError | undefined + constructor(iterator: ValueErrorIterator) { + const error = iterator.First() + super(error === undefined ? 'Invalid Value' : error.message) + this.#iterator = iterator + this.error = error + } + /** Returns an iterator for each error in this value. */ + public Errors(): ValueErrorIterator { + return new ValueErrorIterator(this.#Iterator()) + } + *#Iterator(): IterableIterator { + if (this.error) yield this.error + yield* this.#iterator + } +} +// ------------------------------------------------------------------ +// AssertValue +// ------------------------------------------------------------------ +function AssertValue(schema: TSchema, references: TSchema[], value: unknown): unknown { + if (Check(schema, references, value)) return + throw new AssertError(Errors(schema, references, value)) +} +// ------------------------------------------------------------------ +// Assert +// ------------------------------------------------------------------ +/** Asserts a value matches the given type or throws an `AssertError` if invalid */ +export function Assert(schema: T, references: TSchema[], value: unknown): asserts value is Static +/** Asserts a value matches the given type or throws an `AssertError` if invalid */ +export function Assert(schema: T, value: unknown): asserts value is Static +/** Asserts a value matches the given type or throws an `AssertError` if invalid */ +export function Assert(...args: any[]): unknown { + return args.length === 3 ? AssertValue(args[0], args[1], args[2]) : AssertValue(args[0], [], args[1]) +} diff --git a/src/value/assert/index.ts b/src/value/assert/index.ts new file mode 100644 index 000000000..c2b2c88b3 --- /dev/null +++ b/src/value/assert/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './assert' diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index c3313f7b1..78ac75433 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsStandardObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index' +import { IsObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index' import { TypeBoxError } from '../../type/error/index' import { Kind } from '../../type/symbols/index' import { Create } from '../create/index' @@ -135,7 +135,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any } function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { const created = Create(schema, references) - const mapped = IsStandardObject(created) && IsStandardObject(value) ? { ...(created as any), ...value } : value + const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value return Check(schema, references, mapped) ? mapped : Create(schema, references) } function FromNever(schema: TNever, references: TSchema[], value: any): any { diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 589d3170b..d3be4a79d 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -47,6 +47,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // prettier-ignore import { + HasPropertyKey, IsString, IsObject, IsArray, @@ -57,13 +58,13 @@ import { // ------------------------------------------------------------------ // prettier-ignore import { - IsSchema -} from '../../type/guard/type' + IsKind +} from '../../type/guard/kind' // ------------------------------------------------------------------ // IsCheckable // ------------------------------------------------------------------ function IsCheckable(schema: unknown): boolean { - return IsSchema(schema) && schema[Kind] !== 'Unsafe' + return IsKind(schema) && schema[Kind] !== 'Unsafe' } // ------------------------------------------------------------------ // Types @@ -76,7 +77,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown const unevaluatedProperties = schema.unevaluatedProperties as TSchema const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value))) const composite = intersections.reduce((acc: any, value: any) => (IsObject(value) ? { ...acc, ...value } : value), {}) - if (!IsObject(value) || !IsObject(composite) || !IsSchema(unevaluatedProperties)) return composite + if (!IsObject(value) || !IsObject(composite) || !IsKind(unevaluatedProperties)) return composite const knownkeys = KeyOfPropertyKeys(schema) as string[] for (const key of Object.getOwnPropertyNames(value)) { if (knownkeys.includes(key)) continue @@ -90,11 +91,11 @@ function FromObject(schema: TObject, references: TSchema[], value: unknown): any if (!IsObject(value) || IsArray(value)) return value // Check IsArray for AllowArrayObject configuration const additionalProperties = schema.additionalProperties as TSchema for (const key of Object.getOwnPropertyNames(value)) { - if (key in schema.properties) { + if (HasPropertyKey(schema.properties, key)) { value[key] = Visit(schema.properties[key], references, value[key]) continue } - if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { + if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) { value[key] = Visit(additionalProperties, references, value[key]) continue } @@ -113,7 +114,7 @@ function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any value[key] = Visit(propertySchema, references, value[key]) continue } - if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { + if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) { value[key] = Visit(additionalProperties, references, value[key]) continue } diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index 3bec35c88..370fdac49 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -26,16 +26,16 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' +import type { ObjectType as FromObject, ArrayType as FromArray, TypedArrayType, ValueType } from '../guard/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsArray, IsDate, IsStandardObject, IsTypedArray, IsValueType } from '../guard/index' +import { IsArray, IsDate, IsMap, IsSet, IsObject, IsTypedArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // Clonable // ------------------------------------------------------------------ -function ObjectType(value: ObjectType): any { +function FromObject(value: FromObject): any { const Acc = {} as Record for (const key of Object.getOwnPropertyNames(value)) { Acc[key] = Clone(value[key]) @@ -45,16 +45,22 @@ function ObjectType(value: ObjectType): any { } return Acc } -function ArrayType(value: ArrayType): any { +function FromArray(value: FromArray): any { return value.map((element: any) => Clone(element)) } -function TypedArrayType(value: TypedArrayType): any { +function FromTypedArray(value: TypedArrayType): any { return value.slice() } -function DateType(value: Date): any { +function FromMap(value: Map): any { + return new Map(Clone([...value.entries()])) +} +function FromSet(value: Set): any { + return new Set(Clone([...value.entries()])) +} +function FromDate(value: Date): any { return new Date(value.toISOString()) } -function ValueType(value: ValueType): any { +function FromValue(value: ValueType): any { return value } // ------------------------------------------------------------------ @@ -62,10 +68,12 @@ function ValueType(value: ValueType): any { // ------------------------------------------------------------------ /** Returns a clone of the given value */ export function Clone(value: T): T { - if (IsArray(value)) return ArrayType(value) - if (IsDate(value)) return DateType(value) - if (IsStandardObject(value)) return ObjectType(value) - if (IsTypedArray(value)) return TypedArrayType(value) - if (IsValueType(value)) return ValueType(value) + if (IsArray(value)) return FromArray(value) + if (IsDate(value)) return FromDate(value) + if (IsTypedArray(value)) return FromTypedArray(value) + if (IsMap(value)) return FromMap(value) + if (IsSet(value)) return FromSet(value) + if (IsObject(value)) return FromObject(value) + if (IsValueType(value)) return FromValue(value) throw new Error('ValueClone: Unable to clone value') } diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 21d1dbbcc..e493e2444 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -106,7 +106,7 @@ function TryConvertLiteral(schema: TLiteral, value: unknown) { IsString(schema.const) ? TryConvertLiteralString(value, schema.const) : IsNumber(schema.const) ? TryConvertLiteralNumber(value, schema.const) : IsBoolean(schema.const) ? TryConvertLiteralBoolean(value, schema.const) : - Clone(value) + value ) } function TryConvertBoolean(value: unknown) { @@ -156,7 +156,7 @@ function TryConvertDate(value: unknown) { // ------------------------------------------------------------------ // Default // ------------------------------------------------------------------ -function Default(value: any) { +function Default(value: unknown): unknown { return value } // ------------------------------------------------------------------ @@ -192,26 +192,21 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown } // prettier-ignore function FromObject(schema: TObject, references: TSchema[], value: any): unknown { - const isConvertable = IsObject(value) - if(!isConvertable) return value - const result: Record = {} - for(const key of Object.keys(value)) { - result[key] = HasPropertyKey(schema.properties, key) - ? Visit(schema.properties[key], references, value[key]) - : value[key] + if(!IsObject(value)) return value + for(const key of Object.getOwnPropertyNames(schema.properties)) { + value[key] = Visit(schema.properties[key], references, value[key]) } - return result + return value } function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown { const isConvertable = IsObject(value) if (!isConvertable) return value const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] - const result = {} as Record for (const [propKey, propValue] of Object.entries(value)) { - result[propKey] = Visit(property, references, propValue) + value[propKey] = Visit(property, references, propValue) } - return result + return value } function FromRef(schema: TRef, references: TSchema[], value: any): unknown { return Visit(Deref(schema, references), references, value) @@ -233,21 +228,25 @@ function FromTuple(schema: TTuple, references: TSchema[], value: any): unknown { return (index < schema.items!.length) ? Visit(schema.items![index], references, value) : value - }) + }) } function FromUndefined(schema: TUndefined, references: TSchema[], value: any): unknown { return TryConvertUndefined(value) } function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { for (const subschema of schema.anyOf) { - const converted = Visit(subschema, references, value) + const converted = Visit(subschema, references, Clone(value)) if (!Check(subschema, references, converted)) continue return converted } return value } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} function Visit(schema: TSchema, references: TSchema[], value: any): unknown { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? AddReference(references, schema) : references const schema_ = schema as any switch (schema[Kind]) { case 'Array': @@ -293,14 +292,12 @@ function Visit(schema: TSchema, references: TSchema[], value: any): unknown { // ------------------------------------------------------------------ // Convert // ------------------------------------------------------------------ -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ // prettier-ignore export function Convert(...args: any[]) { - return args.length === 3 - ? Visit(args[0], args[1], args[2]) - : Visit(args[0], [], args[1]) + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) } diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 008105bff..b46e83761 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -389,8 +389,12 @@ function FromKind(schema: TSchema, references: TSchema[]): any { throw new Error('User defined types must specify a default value') } } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} function Visit(schema: TSchema, references: TSchema[]): unknown { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? AddReference(references, schema) : references const schema_ = schema as any switch (schema_[Kind]) { case 'Any': diff --git a/src/value/default/default.ts b/src/value/default/default.ts index b84ce005f..6c2d64709 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -48,7 +48,7 @@ import { IsString, IsObject, IsArray, IsUndefined } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsSchema } from '../../type/guard/type' +import { IsKind } from '../../type/guard/kind' // ------------------------------------------------------------------ // ValueOrDefault // ------------------------------------------------------------------ @@ -56,16 +56,10 @@ function ValueOrDefault(schema: TSchema, value: unknown) { return value === undefined && 'default' in schema ? Clone(schema.default) : value } // ------------------------------------------------------------------ -// IsCheckable +// HasDefaultProperty // ------------------------------------------------------------------ -function IsCheckable(schema: unknown): boolean { - return IsSchema(schema) && schema[Kind] !== 'Unsafe' -} -// ------------------------------------------------------------------ -// IsDefaultSchema -// ------------------------------------------------------------------ -function IsDefaultSchema(value: unknown): value is TSchema { - return IsSchema(value) && 'default' in value +function HasDefaultProperty(schema: unknown): schema is TSchema { + return IsKind(schema) && 'default' in schema } // ------------------------------------------------------------------ // Types @@ -92,11 +86,11 @@ function FromObject(schema: TObject, references: TSchema[], value: unknown): any const knownPropertyKeys = Object.getOwnPropertyNames(schema.properties) // properties for (const key of knownPropertyKeys) { - if (!IsDefaultSchema(schema.properties[key])) continue + if (!HasDefaultProperty(schema.properties[key])) continue defaulted[key] = Visit(schema.properties[key], references, defaulted[key]) } // return if not additional properties - if (!IsDefaultSchema(additionalPropertiesSchema)) return defaulted + if (!HasDefaultProperty(additionalPropertiesSchema)) return defaulted // additional properties for (const key of Object.getOwnPropertyNames(defaulted)) { if (knownPropertyKeys.includes(key)) continue @@ -112,11 +106,11 @@ function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any const knownPropertyKey = new RegExp(propertyKeyPattern) // properties for (const key of Object.getOwnPropertyNames(defaulted)) { - if (!(knownPropertyKey.test(key) && IsDefaultSchema(propertySchema))) continue + if (!(knownPropertyKey.test(key) && HasDefaultProperty(propertySchema))) continue defaulted[key] = Visit(propertySchema, references, defaulted[key]) } // return if not additional properties - if (!IsDefaultSchema(additionalPropertiesSchema)) return defaulted + if (!HasDefaultProperty(additionalPropertiesSchema)) return defaulted // additional properties for (const key of Object.getOwnPropertyNames(defaulted)) { if (knownPropertyKey.test(key)) continue @@ -143,14 +137,18 @@ function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) for (const inner of schema.anyOf) { const result = Visit(inner, references, defaulted) - if (IsCheckable(inner) && Check(inner, result)) { + if (Check(inner, result)) { return result } } return defaulted } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} function Visit(schema: TSchema, references: TSchema[], value: unknown): any { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? AddReference(references, schema) : references const schema_ = schema as any switch (schema_[Kind]) { case 'Array': diff --git a/src/value/equal/equal.ts b/src/value/equal/equal.ts index ce64ea993..8646fd739 100644 --- a/src/value/equal/equal.ts +++ b/src/value/equal/equal.ts @@ -26,14 +26,14 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsStandardObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' +import { IsObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' // ------------------------------------------------------------------ // Equality Checks // ------------------------------------------------------------------ function ObjectType(left: ObjectType, right: unknown): boolean { - if (!IsStandardObject(right)) return false + if (!IsObject(right)) return false const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] const rightKeys = [...Object.keys(right), ...Object.getOwnPropertySymbols(right)] if (leftKeys.length !== rightKeys.length) return false @@ -58,10 +58,10 @@ function ValueType(left: ValueType, right: unknown): any { // ------------------------------------------------------------------ /** Returns true if the left value deep-equals the right */ export function Equal(left: T, right: unknown): right is T { - if (IsStandardObject(left)) return ObjectType(left, right) if (IsDate(left)) return DateType(left, right) if (IsTypedArray(left)) return TypedArrayType(left, right) if (IsArray(left)) return ArrayType(left, right) + if (IsObject(left)) return ObjectType(left, right) if (IsValueType(left)) return ValueType(left, right) throw new Error('ValueEquals: Unable to compare value') } diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index faa1f66ea..2a173423c 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsStandardObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index' +import { IsArray, IsBoolean, IsBigInt, IsDate, IsNull, IsNumber, IsObject, IsString, IsSymbol, IsUint8Array, IsUndefined } from '../guard/index' import { TypeBoxError } from '../../type/error/index' // ------------------------------------------------------------------ @@ -140,7 +140,7 @@ function Visit(value: any) { if (IsDate(value)) return DateType(value) if (IsNull(value)) return NullType(value) if (IsNumber(value)) return NumberType(value) - if (IsStandardObject(value)) return ObjectType(value) + if (IsObject(value)) return ObjectType(value) if (IsString(value)) return StringType(value) if (IsSymbol(value)) return SymbolType(value) if (IsUint8Array(value)) return Uint8ArrayType(value) diff --git a/src/value/index.ts b/src/value/index.ts index 445581ad3..d422f87ca 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -37,6 +37,7 @@ export * from './guard/index' // ------------------------------------------------------------------ // Operators // ------------------------------------------------------------------ +export * from './assert/index' export * from './cast/index' export * from './check/index' export * from './clean/index' @@ -48,6 +49,7 @@ export * from './delta/index' export * from './equal/index' export * from './hash/index' export * from './mutate/index' +export * from './parse/index' export * from './pointer/index' export * from './transform/index' // ------------------------------------------------------------------ diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index 0ae01a6fe..ed35d9cfd 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsStandardObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index' +import { IsObject, IsArray, IsTypedArray, IsValueType, type TypedArrayType } from '../guard/index' import { ValuePointer } from '../pointer/index' import { Clone } from '../clone/index' import { TypeBoxError } from '../../type/error/index' @@ -44,7 +44,7 @@ export class ValueMutateError extends TypeBoxError { // ------------------------------------------------------------------ export type Mutable = { [key: string]: unknown } | unknown[] function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { - if (!IsStandardObject(current)) { + if (!IsObject(current)) { ValuePointer.Set(root, path, Clone(next)) } else { const currentKeys = Object.getOwnPropertyNames(current) @@ -90,7 +90,7 @@ function ValueType(root: Mutable, path: string, current: unknown, next: unknown) function Visit(root: Mutable, path: string, current: unknown, next: unknown) { if (IsArray(next)) return ArrayType(root, path, current, next) if (IsTypedArray(next)) return TypedArrayType(root, path, current, next) - if (IsStandardObject(next)) return ObjectType(root, path, current, next) + if (IsObject(next)) return ObjectType(root, path, current, next) if (IsValueType(next)) return ValueType(root, path, current, next) } // ------------------------------------------------------------------ @@ -102,8 +102,8 @@ function IsNonMutableValue(value: unknown): value is Mutable { function IsMismatchedValue(current: unknown, next: unknown) { // prettier-ignore return ( - (IsStandardObject(current) && IsArray(next)) || - (IsArray(current) && IsStandardObject(next)) + (IsObject(current) && IsArray(next)) || + (IsArray(current) && IsObject(next)) ) } // ------------------------------------------------------------------ diff --git a/src/value/parse/index.ts b/src/value/parse/index.ts new file mode 100644 index 000000000..3d329f1ce --- /dev/null +++ b/src/value/parse/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './parse' diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts new file mode 100644 index 000000000..13477a4c9 --- /dev/null +++ b/src/value/parse/parse.ts @@ -0,0 +1,68 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TransformDecode, HasTransform } from '../transform/index' +import { TSchema } from '../../type/schema/index' +import { StaticDecode } from '../../type/static/index' +import { Assert } from '../assert/assert' +import { Default } from '../default/default' +import { Convert } from '../convert/convert' +import { Clean } from '../clean/clean' +import { Clone } from '../clone/index' + +// ------------------------------------------------------------------ +// ParseReducer +// ------------------------------------------------------------------ +type ReducerFunction = (schema: TSchema, references: TSchema[], value: unknown) => unknown + +// prettier-ignore +const ParseReducer: ReducerFunction[] = [ + (_schema, _references, value) => Clone(value), + (schema, references, value) => Default(schema, references, value), + (schema, references, value) => Clean(schema, references, value), + (schema, references, value) => Convert(schema, references, value), + (schema, references, value) => { Assert(schema, references, value); return value }, + (schema, references, value) => (HasTransform(schema, references) ? TransformDecode(schema, references, value) : value), +] +// ------------------------------------------------------------------ +// ParseValue +// ------------------------------------------------------------------ +function ParseValue>(schema: T, references: TSchema[], value: unknown): R { + return ParseReducer.reduce((value, reducer) => reducer(schema, references, value), value) as R +} +// ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse>(schema: T, references: TSchema[], value: unknown): R +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse>(schema: T, value: unknown): R +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse(...args: any[]): unknown { + return args.length === 3 ? ParseValue(args[0], args[1], args[2]) : ParseValue(args[0], [], args[1]) +} diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index e6ffb9a5a..a713c62a5 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -48,7 +48,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsStandardObject, IsArray, IsValueType } from '../guard/index' +import { IsObject, IsArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -97,7 +97,7 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a } // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { - if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, path, value) + if (!IsObject(value) || IsValueType(value)) return Default(schema, path, value) const knownEntries = KeyOfPropertyEntries(schema) const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...value } as Record @@ -120,7 +120,7 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any) } // prettier-ignore function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { - if (!IsStandardObject(value)) return Default(schema, path, value) + if (!IsObject(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) const knownProperties = { ...value } as Record for(const key of knownKeys) if(key in knownProperties) { @@ -139,7 +139,7 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value: } // prettier-ignore function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { - if (!IsStandardObject(value)) return Default(schema, path, value) + if (!IsObject(value)) return Default(schema, path, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = { ...value } as Record @@ -183,9 +183,13 @@ function FromUnion(schema: TUnion, references: TSchema[], path: string, value: a } return Default(schema, path, value) } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} // prettier-ignore function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const references_ = typeof schema.$id === 'string' ? AddReference(references, schema) : references const schema_ = schema as any switch (schema[Kind]) { case 'Array': diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 39a43cf9e..2d857b7bd 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -48,7 +48,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsStandardObject, IsArray, IsValueType } from '../guard/index' +import { IsObject, IsArray, IsValueType } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -98,7 +98,7 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) - if (!IsStandardObject(value) || IsValueType(value)) return defaulted + if (!IsObject(value) || IsValueType(value)) return defaulted const knownEntries = KeyOfPropertyEntries(schema) const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...defaulted } as Record @@ -123,7 +123,7 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any) // prettier-ignore function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) - if (!IsStandardObject(defaulted)) return defaulted + if (!IsObject(defaulted)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = { ...defaulted } as Record for(const key of knownKeys) if(key in knownProperties) { @@ -143,7 +143,7 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value: // prettier-ignore function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) as Record - if (!IsStandardObject(value)) return defaulted + if (!IsObject(value)) return defaulted const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) const knownProperties = {...defaulted } as Record @@ -151,7 +151,7 @@ function FromRecord(schema: TRecord, references: TSchema[], path: string, value: knownProperties[key] = Visit(schema.patternProperties[pattern], references, `${path}/${key}`, knownProperties[key]) } if (!IsSchema(schema.additionalProperties)) { - return Default(schema, path, knownProperties) + return knownProperties } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const additionalProperties = schema.additionalProperties as TSchema @@ -194,9 +194,13 @@ function FromUnion(schema: TUnion, references: TSchema[], path: string, value: a } return Default(schema, path, value) } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} // prettier-ignore function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references + const references_ = typeof schema.$id === 'string' ? AddReference(references, schema) : references const schema_ = schema as any switch (schema[Kind]) { case 'Array': diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 9aff3d25e..27d731554 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -120,9 +120,13 @@ function FromTuple(schema: TTuple, references: TSchema[]) { function FromUnion(schema: TUnion, references: TSchema[]) { return IsTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) } +function AddReference(references: TSchema[], schema: TSchema): TSchema[] { + references.push(schema) + return references +} // prettier-ignore function Visit(schema: TSchema, references: TSchema[]): boolean { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? AddReference(references, schema) : references const schema_ = schema as any if (schema.$id && visited.has(schema.$id)) return false if (schema.$id) visited.add(schema.$id) diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 944d27d60..347b14a29 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { HasTransform, TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' +import { Assert as AssertValue } from '../assert/index' import { Mutate as MutateValue, type Mutable } from '../mutate/index' import { Hash as HashValue } from '../hash/index' import { Equal as EqualValue } from '../equal/index' @@ -36,6 +37,7 @@ import { Convert as ConvertValue } from '../convert/index' import { Create as CreateValue } from '../create/index' import { Clean as CleanValue } from '../clean/index' import { Check as CheckValue } from '../check/index' +import { Parse as ParseValue } from '../parse/index' import { Default as DefaultValue } from '../default/index' import { Diff as DiffValue, Patch as PatchValue, Edit } from '../delta/index' import { Errors as ValueErrors, ValueErrorIterator } from '../../errors/index' @@ -43,12 +45,20 @@ import { Errors as ValueErrors, ValueErrorIterator } from '../../errors/index' import type { TSchema } from '../../type/schema/index' import type { Static, StaticDecode, StaticEncode } from '../../type/static/index' +/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ +export function Assert>(schema: T, references: TSchema[], value: unknown): asserts value is R +/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ +export function Assert>(schema: T, value: unknown): asserts value is R +/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ +export function Assert(...args: any[]): any { + return AssertValue.apply(AssertValue, args as any) +} /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(schema: T, references: TSchema[], value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(schema: T, value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(...args: any[]) { +export function Cast(...args: any[]): any { return CastValue.apply(CastValue, args as any) } /** Creates a value from the given type and references */ @@ -56,7 +66,7 @@ export function Create(schema: T, references: TSchema[]): Sta /** Creates a value from the given type */ export function Create(schema: T): Static /** Creates a value from the given type */ -export function Create(...args: any[]) { +export function Create(...args: any[]): any { return CreateValue.apply(CreateValue, args as any) } /** Returns true if the value matches the given type and references */ @@ -64,7 +74,7 @@ export function Check(schema: T, references: TSchema[], value /** Returns true if the value matches the given type */ export function Check(schema: T, value: unknown): value is Static /** Returns true if the value matches the given type */ -export function Check(...args: any[]) { +export function Check(...args: any[]): any { return CheckValue.apply(CheckValue, args as any) } /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ @@ -72,15 +82,15 @@ export function Clean(schema: TSchema, references: TSchema[], value: unknown): u /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ export function Clean(schema: TSchema, value: unknown): unknown /** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(...args: any[]) { +export function Clean(...args: any[]): any { return CleanValue.apply(CleanValue, args as any) } -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ export function Convert(schema: TSchema, value: unknown): unknown -/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(...args: any[]) { +/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +export function Convert(...args: any[]): any { return ConvertValue.apply(ConvertValue, args as any) } /** Returns a structural clone of the given value */ @@ -92,7 +102,7 @@ export function Decode>(schema: T, refere /** Decodes a value or throws if error */ export function Decode>(schema: T, value: unknown): R /** Decodes a value or throws if error */ -export function Decode(...args: any[]) { +export function Decode(...args: any[]): any { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value @@ -102,7 +112,7 @@ export function Default(schema: TSchema, references: TSchema[], value: unknown): /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ export function Default(schema: TSchema, value: unknown): unknown /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(...args: any[]) { +export function Default(...args: any[]): any { return DefaultValue.apply(DefaultValue, args as any) } /** Encodes a value or throws if error */ @@ -110,18 +120,26 @@ export function Encode>(schema: T, refere /** Encodes a value or throws if error */ export function Encode>(schema: T, value: unknown): R /** Encodes a value or throws if error */ -export function Encode(...args: any[]) { +export function Encode(...args: any[]): any { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) return encoded } +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse>(schema: T, references: TSchema[], value: unknown): R +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse>(schema: T, value: unknown): R +/** Parses a value or throws an `AssertError` if invalid. */ +export function Parse(...args: any[]): unknown { + return ParseValue.apply(ParseValue, args as any) +} /** Returns an iterator for each error in this value. */ export function Errors(schema: T, references: TSchema[], value: unknown): ValueErrorIterator /** Returns an iterator for each error in this value. */ export function Errors(schema: T, value: unknown): ValueErrorIterator /** Returns an iterator for each error in this value. */ -export function Errors(...args: any[]) { +export function Errors(...args: any[]): any { return ValueErrors.apply(ValueErrors, args as any) } /** Returns true if left and right values are structurally equal */ diff --git a/test/runtime/value/assert/assert.ts b/test/runtime/value/assert/assert.ts new file mode 100644 index 000000000..fe3afdce2 --- /dev/null +++ b/test/runtime/value/assert/assert.ts @@ -0,0 +1,35 @@ +import { Value, AssertError } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/Assert', () => { + it('Should Assert', () => { + Assert.Throws(() => Value.Assert(Type.String(), 1)) + }) + it('Should throw AssertError', () => { + try { + Value.Assert(Type.String(), 1) + } catch (error) { + if (error instanceof AssertError) { + return + } + throw error + } + }) + it('Should throw AssertError and produce Iterator', () => { + try { + Value.Assert(Type.String(), 1) + } catch (error) { + if (error instanceof AssertError) { + const first = error.Errors().First() + Assert.HasProperty(first, 'type') + Assert.HasProperty(first, 'schema') + Assert.HasProperty(first, 'path') + Assert.HasProperty(first, 'value') + Assert.HasProperty(first, 'message') + return + } + throw error + } + }) +}) diff --git a/test/runtime/value/assert/index.ts b/test/runtime/value/assert/index.ts new file mode 100644 index 000000000..f043fc641 --- /dev/null +++ b/test/runtime/value/assert/index.ts @@ -0,0 +1 @@ +import './assert' diff --git a/test/runtime/value/default/union.ts b/test/runtime/value/default/union.ts index 56b572caf..e9f4e682e 100644 --- a/test/runtime/value/default/union.ts +++ b/test/runtime/value/default/union.ts @@ -1,5 +1,5 @@ import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/default/Union', () => { @@ -86,25 +86,29 @@ describe('value/default/Union', () => { // Interior Unsafe // ---------------------------------------------------------------- it('Should default interior unsafe 1', () => { + TypeRegistry.Set('DefaultUnsafe', (schema, value) => typeof value === 'string') const T = Type.Union([ Type.Object({ x: Type.Number({ default: 1 }), y: Type.Number({ default: 2 }), }), - Type.Unsafe({ default: 'hello' }), + Type.Unsafe({ [Kind]: 'DefaultUnsafe', default: 'hello' }), ]) const R = Value.Default(T, undefined) - Assert.IsEqual(R, undefined) + Assert.IsEqual(R, 'hello') + TypeRegistry.Delete('DefaultUnsafe') }) it('Should default interior unsafe 2', () => { + TypeRegistry.Set('DefaultUnsafe', (schema, value) => typeof value === 'string') const T = Type.Union([ Type.Object({ x: Type.Number({ default: 1 }), y: Type.Number({ default: 2 }), }), - Type.Unsafe({ default: 'hello' }), + Type.Unsafe({ [Kind]: 'DefaultUnsafe', default: 'hello' }), ]) const R = Value.Default(T, 'world') Assert.IsEqual(R, 'world') + TypeRegistry.Delete('DefaultUnsafe') }) }) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index 4e6ce996e..515d5fff8 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -1,3 +1,4 @@ +import './assert' import './cast' import './check' import './clean' @@ -10,5 +11,6 @@ import './equal' import './guard' import './hash' import './mutate' +import './parse' import './pointer' import './transform' diff --git a/test/runtime/value/parse/index.ts b/test/runtime/value/parse/index.ts new file mode 100644 index 000000000..09cc87100 --- /dev/null +++ b/test/runtime/value/parse/index.ts @@ -0,0 +1 @@ +import './parse' diff --git a/test/runtime/value/parse/parse.ts b/test/runtime/value/parse/parse.ts new file mode 100644 index 000000000..5c8be9dd7 --- /dev/null +++ b/test/runtime/value/parse/parse.ts @@ -0,0 +1,90 @@ +import { Value, AssertError } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/Parse', () => { + it('Should Parse', () => { + const A = Value.Parse(Type.Literal('hello'), 'hello') + Assert.IsEqual(A, 'hello') + }) + it('Should not Parse', () => { + Assert.Throws(() => Value.Parse(Type.Literal('hello'), 'world')) + }) + it('Should throw AssertError', () => { + try { + Value.Parse(Type.Literal('hello'), 'world') + } catch(error) { + if(error instanceof AssertError) { + return + } + throw error + } + }) + it('Should throw AssertError and produce Iterator', () => { + try { + Value.Parse(Type.Literal('hello'), 'world') + } catch(error) { + if(error instanceof AssertError) { + const first = error.Errors().First() + Assert.HasProperty(first, 'type') + Assert.HasProperty(first, 'schema') + Assert.HasProperty(first, 'path') + Assert.HasProperty(first, 'value') + Assert.HasProperty(first, 'message') + return + } + throw error + } + }) + // ---------------------------------------------------------------- + // Default + // ---------------------------------------------------------------- + it('Should use Default values', () => { + const X = Value.Parse(Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }) + }), { }) + Assert.IsEqual(X, { x: 1, y: 2 }) + }) + it('Should throw on invalid Default values', () => { + Assert.Throws(() => Value.Parse(Type.Object({ + x: Type.Number({ default: null }), + y: Type.Number({ default: null }) + }), { })) + }) + // ---------------------------------------------------------------- + // Convert + // ---------------------------------------------------------------- + it('Should Convert value 1', () => { + const X = Value.Parse(Type.Object({ + x: Type.Number(), + y: Type.Number() + }), { x: '1', y: '2' }) + Assert.IsEqual(X, { x: 1, y: 2 }) + }) + it('Should Convert value 2', () => { + const X = Value.Parse(Type.Array(Type.String()), [1, 2, 3, 4]) + Assert.IsEqual(X, ['1', '2', '3', '4']) + }) + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should Clean value', () => { + const X = Value.Parse(Type.Object({ + x: Type.Number(), + y: Type.Number() + }), { x: 1, y: 2, z: 3 }) + Assert.IsEqual(X, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Decode + // ---------------------------------------------------------------- + it('Should Decode value', () => { + const T = Type.Transform(Type.String()) + .Decode(value => 'hello') + .Encode(value => value) + const X = Value.Parse(T, 'world') + Assert.IsEqual(X, 'hello') + }) +}) diff --git a/test/runtime/value/transform/record.ts b/test/runtime/value/transform/record.ts index a91172dbf..6f53ef552 100644 --- a/test/runtime/value/transform/record.ts +++ b/test/runtime/value/transform/record.ts @@ -1,6 +1,5 @@ import * as Encoder from './_encoder' import { Assert } from '../../assert' -import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' describe('value/transform/Record', () => { From e36f5658e3a56d8c32a711aa616ec8bb34ca14b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Correa=20Casablanca?= Date: Sun, 11 Aug 2024 05:56:24 +0200 Subject: [PATCH 271/369] Ecosystem: Add `nominal-typebox` (#955) --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 1fca89253..b79458c44 100644 --- a/readme.md +++ b/readme.md @@ -1676,6 +1676,7 @@ The following is a list of community packages that offer general tooling, extend | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | | [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | +| [nominal-typebox](https://github.com/Coder-Spirit/nominal/tree/main/%40coderspirit/nominal-typebox) | Allows devs to integrate nominal types into TypeBox schemas | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schema matching the database models | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | From cc5e6c7aca2237a67bf1b4a907904cca5a154ca2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 14 Aug 2024 14:56:09 +0900 Subject: [PATCH 272/369] Revision 0.33.5 (#959) * Use ExactOptionalProperty Policy on Decode | Encode * Version --- package-lock.json | 4 +- package.json | 2 +- src/system/policy.ts | 10 +-- src/value/convert/convert.ts | 11 ++- src/value/transform/decode.ts | 19 ++-- src/value/transform/encode.ts | 17 +++- test/runtime/value/convert/object.ts | 5 ++ test/runtime/value/transform/object.ts | 115 ++++++++++++++++++++++++- 8 files changed, 162 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01590d97b..b5b0fad33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.4", + "version": "0.33.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.4", + "version": "0.33.5", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 04c5b2a29..8e8477db3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.4", + "version": "0.33.5", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/system/policy.ts b/src/system/policy.ts index 93a68f809..8501462c5 100644 --- a/src/system/policy.ts +++ b/src/system/policy.ts @@ -52,24 +52,24 @@ export namespace TypeSystemPolicy { export let AllowNaN: boolean = false /** Sets whether `null` should validate for void types. The default is `false` */ export let AllowNullVoid: boolean = false - /** Asserts this value using the ExactOptionalPropertyTypes policy */ + /** Checks this value using the ExactOptionalPropertyTypes policy */ export function IsExactOptionalProperty(value: Record, key: string) { return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined } - /** Asserts this value using the AllowArrayObjects policy */ + /** Checks this value using the AllowArrayObjects policy */ export function IsObjectLike(value: unknown): value is Record { const isObject = IsObject(value) return AllowArrayObject ? isObject : isObject && !IsArray(value) } - /** Asserts this value as a record using the AllowArrayObjects policy */ + /** Checks this value as a record using the AllowArrayObjects policy */ export function IsRecordLike(value: unknown): value is Record { return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array) } - /** Asserts this value using the AllowNaN policy */ + /** Checks this value using the AllowNaN policy */ export function IsNumberLike(value: unknown): value is number { return AllowNaN ? IsNumber(value) : Number.isFinite(value) } - /** Asserts this value using the AllowVoidNull policy */ + /** Checks this value using the AllowVoidNull policy */ export function IsVoidLike(value: unknown): value is void { const isUndefined = IsUndefined(value) return AllowNullVoid ? isUndefined || value === null : isUndefined diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index e493e2444..5342320ed 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -55,6 +55,12 @@ import type { TUndefined } from '../../type/undefined/index' // ValueGuard // ------------------------------------------------------------------ import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol, HasPropertyKey } from '../guard/index' + +// ------------------------------------------------------------------ +// TypeGuard +// ------------------------------------------------------------------ +import { IsOptional } from '../../type/guard/kind' + // ------------------------------------------------------------------ // Conversions // ------------------------------------------------------------------ @@ -193,8 +199,9 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown // prettier-ignore function FromObject(schema: TObject, references: TSchema[], value: any): unknown { if(!IsObject(value)) return value - for(const key of Object.getOwnPropertyNames(schema.properties)) { - value[key] = Visit(schema.properties[key], references, value[key]) + for(const propertyKey of Object.getOwnPropertyNames(schema.properties)) { + if(!HasPropertyKey(value, propertyKey)) continue + value[propertyKey] = Visit(schema.properties[propertyKey], references, value[propertyKey]) } return value } diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index a713c62a5..5bac20ecf 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -26,11 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { TypeSystemPolicy } from '../../system/policy' import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' -import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -48,11 +48,11 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsObject, IsArray, IsValueType } from '../guard/index' +import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -121,9 +121,18 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any) // prettier-ignore function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { if (!IsObject(value)) return Default(schema, path, value) - const knownKeys = KeyOfPropertyKeys(schema) + const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = { ...value } as Record - for(const key of knownKeys) if(key in knownProperties) { + for(const key of knownKeys) { + if(!HasPropertyKey(knownProperties, key)) continue + // if the property value is undefined, but the target is not, nor does it satisfy exact optional + // property policy, then we need to continue. This is a special case for optional property handling + // where a transforms wrapped in a optional modifiers should not run. + if(IsUndefinedValue(knownProperties[key]) && ( + !IsUndefined(schema.properties[key]) || + TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key) + )) continue + // decode property knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key]) } if (!IsSchema(schema.additionalProperties)) { diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 2d857b7bd..365f91edd 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -26,11 +26,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { TypeSystemPolicy } from '../../system/policy' import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' -import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -48,11 +48,11 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsObject, IsArray, IsValueType } from '../guard/index' +import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -126,7 +126,16 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value: if (!IsObject(defaulted)) return defaulted const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = { ...defaulted } as Record - for(const key of knownKeys) if(key in knownProperties) { + for(const key of knownKeys) { + if(!HasPropertyKey(knownProperties, key)) continue + // if the property value is undefined, but the target is not, nor does it satisfy exact optional + // property policy, then we need to continue. This is a special case for optional property handling + // where a transforms wrapped in a optional modifiers should not run. + if(IsUndefinedValue(knownProperties[key]) && ( + !IsUndefined(schema.properties[key]) || + TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key) + )) continue + // encode property knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key]) } if (!IsSchema(schema.additionalProperties)) { diff --git a/test/runtime/value/convert/object.ts b/test/runtime/value/convert/object.ts index 042c05647..69a8564be 100644 --- a/test/runtime/value/convert/object.ts +++ b/test/runtime/value/convert/object.ts @@ -21,4 +21,9 @@ describe('value/convert/Object', () => { const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) }) + it('Should not convert missing properties', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Convert(T, { }) + Assert.IsEqual(R, { }) + }) }) diff --git a/test/runtime/value/transform/object.ts b/test/runtime/value/transform/object.ts index 9425e4de6..8580201ef 100644 --- a/test/runtime/value/transform/object.ts +++ b/test/runtime/value/transform/object.ts @@ -1,5 +1,7 @@ import * as Encoder from './_encoder' import { Assert } from '../../assert' + +import { TypeSystemPolicy } from '@sinclair/typebox/system' import { Value } from '@sinclair/typebox/value' import { Type } from '@sinclair/typebox' @@ -156,7 +158,7 @@ describe('value/transform/Object', () => { // https://github.com/sinclairzx81/typebox/issues/859 // ---------------------------------------------------------------- it('Should decode for nested transform with renamed property', () => { - class User { constructor(public name: string, public createdAt: Date) {} } + class User { constructor(public name: string, public createdAt: Date) { } } const TDate = Type.Transform(Type.Number()) .Decode(v => new Date(v)) .Encode(v => v.getTime()) @@ -185,7 +187,116 @@ describe('value/transform/Object', () => { const B = Object.create(null); B.x = '1' const D = Value.Decode(T, A) const E = Value.Encode(T, B) - Assert.IsEqual(D, { x: '1' }) + Assert.IsEqual(D, { x: '1' }) Assert.IsEqual(E, { x: 1 }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/958 + // ---------------------------------------------------------------- + it('Should not decode missing optional properties 0', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => { Invoked = true; return value }) + .Encode((value) => value) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Decode(T, { value: 'foo' }) + Assert.IsEqual(D, { value: 'foo' }) + Assert.IsTrue(Invoked) + }) + it('Should not decode missing optional properties 1', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => { Invoked = true; return value }) + .Encode((value) => value) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Decode(T, {}) + Assert.IsEqual(D, {}) + Assert.IsFalse(Invoked) + }) + it('Should not decode missing optional properties 2', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => { Invoked = true; return value }) + .Encode((value) => value) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Decode(T, { value: undefined }) + Assert.IsEqual(D, { value: undefined }) + Assert.IsFalse(Invoked) + }) + it('Should not decode missing optional properties 3 (ExactOptionalPropertyTypes)', () => { + let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes] + TypeSystemPolicy.ExactOptionalPropertyTypes = true + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => { Invoked = true; return value }) + .Encode((value) => value) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Decode(T, {}) + Assert.IsEqual(D, {}) + Assert.IsFalse(Invoked) + TypeSystemPolicy.ExactOptionalPropertyTypes = Revert + }) + it('Should not decode missing optional properties 4 (ExactOptionalPropertyTypes)', () => { + let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes] + TypeSystemPolicy.ExactOptionalPropertyTypes = true + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => { Invoked = true; return value }) + .Encode((value) => value) + const T = Type.Object({ value: Type.Optional(S) }) + Assert.Throws(() => Value.Decode(T, { value: undefined })) + Assert.IsFalse(Invoked) + TypeSystemPolicy.ExactOptionalPropertyTypes = Revert + }) + it('Should not encode missing optional properties 0', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => value) + .Encode((value) => { Invoked = true; return value }) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Encode(T, { value: 'foo' }) + Assert.IsEqual(D, { value: 'foo' }) + Assert.IsTrue(Invoked) + }) + it('Should not encode missing optional properties 1', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => value) + .Encode((value) => { Invoked = true; return value }) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Encode(T, {}) + Assert.IsEqual(D, {}) + Assert.IsFalse(Invoked) + }) + it('Should not encode missing optional properties 2', () => { + let Invoked = false + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => value) + .Encode((value) => { Invoked = true; return value }) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Encode(T, { value: undefined }) + Assert.IsEqual(D, { value: undefined }) + Assert.IsFalse(Invoked) + }) + it('Should not encode missing optional properties 3 (ExactOptionalPropertyTypes)', () => { + let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes] + TypeSystemPolicy.ExactOptionalPropertyTypes = true + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => value) + .Encode((value) => { Invoked = true; return value }) + const T = Type.Object({ value: Type.Optional(S) }) + const D = Value.Encode(T, {}) + Assert.IsEqual(D, {}) + Assert.IsFalse(Invoked) + TypeSystemPolicy.ExactOptionalPropertyTypes = Revert + }) + it('Should not encode missing optional properties 4 (ExactOptionalPropertyTypes)', () => { + let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes] + TypeSystemPolicy.ExactOptionalPropertyTypes = true + const S = Type.Transform(Type.RegExp(/foo/)) + .Decode((value) => value) + .Encode((value) => { Invoked = true; return value }) + const T = Type.Object({ value: Type.Optional(S) }) + Assert.Throws(() => Value.Encode(T, { value: undefined })) + Assert.IsFalse(Invoked) + TypeSystemPolicy.ExactOptionalPropertyTypes = Revert + }) }) From 6da8b3fbc44844ab0a4d144bc6750e230f838fc2 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 15 Aug 2024 05:15:01 +0900 Subject: [PATCH 273/369] Revision 0.33.6 (#963) * Traverse Instanced Object Values Inside Default * Version --- package-lock.json | 4 ++-- package.json | 2 +- src/value/default/default.ts | 13 ++++++++----- test/runtime/value/default/object.ts | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5b0fad33..aa7cef85b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.5", + "version": "0.33.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.5", + "version": "0.33.6", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 8e8477db3..299a5e2dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.5", + "version": "0.33.6", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 6c2d64709..abda4cdd4 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -82,19 +82,22 @@ function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown function FromObject(schema: TObject, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) if (!IsObject(defaulted)) return defaulted - const additionalPropertiesSchema = schema.additionalProperties as TSchema const knownPropertyKeys = Object.getOwnPropertyNames(schema.properties) // properties for (const key of knownPropertyKeys) { - if (!HasDefaultProperty(schema.properties[key])) continue - defaulted[key] = Visit(schema.properties[key], references, defaulted[key]) + // note: we need to traverse into the object and test if the return value + // yielded a non undefined result. Here we interpret an undefined result as + // a non assignable property and continue. + const propertyValue = Visit(schema.properties[key], references, defaulted[key]) + if (IsUndefined(propertyValue)) continue + defaulted[key] = propertyValue } // return if not additional properties - if (!HasDefaultProperty(additionalPropertiesSchema)) return defaulted + if (!HasDefaultProperty(schema.additionalProperties)) return defaulted // additional properties for (const key of Object.getOwnPropertyNames(defaulted)) { if (knownPropertyKeys.includes(key)) continue - defaulted[key] = Visit(additionalPropertiesSchema, references, defaulted[key]) + defaulted[key] = Visit(schema.additionalProperties, references, defaulted[key]) } return defaulted } diff --git a/test/runtime/value/default/object.ts b/test/runtime/value/default/object.ts index 1accf40ee..ddfad94a6 100644 --- a/test/runtime/value/default/object.ts +++ b/test/runtime/value/default/object.ts @@ -209,4 +209,23 @@ describe('value/default/Object', () => { Value.Default(A, {}) Assert.IsEqual(A, B) }) + // ---------------------------------------------------------------- + // Traveral: https://github.com/sinclairzx81/typebox/issues/962 + // ---------------------------------------------------------------- + it('Should traverse into an object 1 (initialize)', () => { + const Child = Type.Object({ a: Type.String({ default: 'x' }) }) + const Parent = Type.Object({ child: Child }) + Assert.IsEqual(Value.Default(Child, {}), { a: 'x' }) + Assert.IsEqual(Value.Default(Parent, { child: {} }), { child: { a: 'x' } }) + }) + it('Should traverse into an object 2 (retain)', () => { + const Child = Type.Object({ a: Type.String({ default: 'x' }) }) + const Parent = Type.Object({ child: Child }) + Assert.IsEqual(Value.Default(Parent, { child: { a: 1 } }), { child: { a: 1 } }) + }) + it('Should traverse into an object 3 (ignore on undefined)', () => { + const Child = Type.Object({ a: Type.String({ default: 'x' }) }) + const Parent = Type.Object({ child: Child }) + Assert.IsEqual(Value.Default(Parent, { child: undefined }), { child: undefined }) + }) }) From 8b503f613ac2153d8aae41c4ae2f08650e2e5fc7 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 15 Aug 2024 14:28:12 +0900 Subject: [PATCH 274/369] Revision 0.33.7 (#964) * Additional Improvements to Object Default * Version --- package-lock.json | 4 +-- package.json | 2 +- src/value/default/default.ts | 10 +++--- test/runtime/value/default/object.ts | 48 ++++++++++++++++++++++------ 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa7cef85b..86b31daf8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.6", + "version": "0.33.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.6", + "version": "0.33.7", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 299a5e2dd..aae8d9de3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.6", + "version": "0.33.7", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/default/default.ts b/src/value/default/default.ts index abda4cdd4..27b05f555 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -44,7 +44,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsString, IsObject, IsArray, IsUndefined } from '../guard/index' +import { IsString, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -52,8 +52,9 @@ import { IsKind } from '../../type/guard/kind' // ------------------------------------------------------------------ // ValueOrDefault // ------------------------------------------------------------------ -function ValueOrDefault(schema: TSchema, value: unknown) { - return value === undefined && 'default' in schema ? Clone(schema.default) : value +function ValueOrDefault(schema: TSchema, value: unknown): unknown { + const clone = HasPropertyKey(schema, 'default') ? Clone(schema.default) : undefined + return IsUndefined(value) ? clone : IsObject(value) && IsObject(clone) ? Object.assign(clone, value) : value } // ------------------------------------------------------------------ // HasDefaultProperty @@ -81,6 +82,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown } function FromObject(schema: TObject, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) + // return defaulted if (!IsObject(defaulted)) return defaulted const knownPropertyKeys = Object.getOwnPropertyNames(schema.properties) // properties @@ -90,7 +92,7 @@ function FromObject(schema: TObject, references: TSchema[], value: unknown): any // a non assignable property and continue. const propertyValue = Visit(schema.properties[key], references, defaulted[key]) if (IsUndefined(propertyValue)) continue - defaulted[key] = propertyValue + defaulted[key] = Visit(schema.properties[key], references, defaulted[key]) } // return if not additional properties if (!HasDefaultProperty(schema.additionalProperties)) return defaulted diff --git a/test/runtime/value/default/object.ts b/test/runtime/value/default/object.ts index ddfad94a6..86a7df19f 100644 --- a/test/runtime/value/default/object.ts +++ b/test/runtime/value/default/object.ts @@ -213,19 +213,47 @@ describe('value/default/Object', () => { // Traveral: https://github.com/sinclairzx81/typebox/issues/962 // ---------------------------------------------------------------- it('Should traverse into an object 1 (initialize)', () => { - const Child = Type.Object({ a: Type.String({ default: 'x' }) }) - const Parent = Type.Object({ child: Child }) - Assert.IsEqual(Value.Default(Child, {}), { a: 'x' }) - Assert.IsEqual(Value.Default(Parent, { child: {} }), { child: { a: 'x' } }) + const Y = Type.Object({ y: Type.String({ default: 'y' }) }) + const X = Type.Object({ x: Y }) + Assert.IsEqual(Value.Default(Y, {}), { y: 'y' }) + Assert.IsEqual(Value.Default(X, { x: {} }), { x: { y: 'y' } }) }) it('Should traverse into an object 2 (retain)', () => { - const Child = Type.Object({ a: Type.String({ default: 'x' }) }) - const Parent = Type.Object({ child: Child }) - Assert.IsEqual(Value.Default(Parent, { child: { a: 1 } }), { child: { a: 1 } }) + const Y = Type.Object({ y: Type.String({ default: 'y' }) }) + const X = Type.Object({ x: Y }) + Assert.IsEqual(Value.Default(X, { x: { y: 1 } }), { x: { y: 1 } }) }) it('Should traverse into an object 3 (ignore on undefined)', () => { - const Child = Type.Object({ a: Type.String({ default: 'x' }) }) - const Parent = Type.Object({ child: Child }) - Assert.IsEqual(Value.Default(Parent, { child: undefined }), { child: undefined }) + const Y = Type.Object({ y: Type.String({ default: 'y' }) }) + const X = Type.Object({ x: Y }) + Assert.IsEqual(Value.Default(X, { x: undefined }), { x: undefined }) + }) + // ---------------------------------------------------------------- + // Exterior Object Defaults + // ---------------------------------------------------------------- + it('Should default exterior into an object 1', () => { + const X = Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) + const R = Value.Default(X, undefined) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should default exterior into an object 2', () => { + const X = Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) + const R = Value.Default(X, {}) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should default exterior into an object 3', () => { + const X = Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) + const R = Value.Default(X, { y: 3 }) + Assert.IsEqual(R, { y: 3, x: 1 }) + }) + it('Should default exterior into an object 4', () => { + const X = Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) + const R = Value.Default(X, { y: 3, x: 7 }) + Assert.IsEqual(R, { y: 3, x: 7 }) + }) + it('Should default exterior into an object 5', () => { + const X = Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) + const R = Value.Default(X, { x: 2 }) + Assert.IsEqual(R, { x: 2 }) }) }) From 1aee5c0863943369c0fc07e7b9c8744c15f077ba Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 28 Aug 2024 20:28:41 +0900 Subject: [PATCH 275/369] ChangeLog (#973) * Add Latest ChangeLog for 0.32.0 and 0.33.0 --- changelog/latest.md | 100 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 changelog/latest.md diff --git a/changelog/latest.md b/changelog/latest.md new file mode 100644 index 000000000..29fc5f990 --- /dev/null +++ b/changelog/latest.md @@ -0,0 +1,100 @@ +### 0.33.0 + +- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964) Additional updates to improve Default for enumerable objects. +- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963) Add object traversal path for Default. Ensure enumerable objects are traversed. +- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959) Provide better support for transforming properties with optional modifiers. +- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953) Add Assert and Parse value functions. Add defacto AssertError type. +- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950) Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE. +- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947) Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped. +- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945) Apply mutability fix for Intrinsic and Not schematics (inline with Default) +- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941) Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default. + +### 0.32.0 + +- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert. +- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals. +- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020. +- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types. +- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring. +- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode. +- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance. +- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations. +- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates. +- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`. +- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types. +- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning. +- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments. +- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite. +- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures. +- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16. +- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type. +- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any. +- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error. +- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures. +- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules. +- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp +- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types. +- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal +- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required +- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[] +- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create. +- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default. +- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types +- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024 +- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported +- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type +- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ed4c89a9ab0372e4b8f82c88b788380bb290fdbe Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 3 Sep 2024 04:28:33 +0900 Subject: [PATCH 276/369] Revision 0.33.8 (#983) * Prevent Intersect Transform Encode callback from being called twice * Make strict the Encode and Decode return type * Support default annotation being assigned Functions for lazy value initialization on Create * Enable Mapping types to override user defined options from source type * Support Constraint Copy for Pick, Omit (inline with Partial / Required) (Trialing Implementation) * Flag Strict For Deprecation --- changelog/0.32.0.md | 39 +++++++++ changelog/0.33.0.md | 26 ++++++ changelog/latest.md | 100 ----------------------- package-lock.json | 4 +- package.json | 2 +- readme.md | 30 ------- src/compiler/compiler.ts | 4 +- src/type/object/object.ts | 2 +- src/type/omit/omit.ts | 38 ++++++--- src/type/partial/partial.ts | 35 +++++--- src/type/pick/pick.ts | 36 +++++--- src/type/required/required.ts | 30 +++++-- src/type/strict/strict.ts | 14 +++- src/type/type/json.ts | 32 +++++--- src/type/type/type.ts | 2 +- src/value/create/create.ts | 4 +- src/value/default/default.ts | 5 +- src/value/transform/encode.ts | 2 +- src/value/value/value.ts | 8 +- test/runtime/compiler-ajv/pick.ts | 1 + test/runtime/compiler-ajv/required.ts | 1 + test/runtime/compiler/omit.ts | 19 ++--- test/runtime/compiler/pick.ts | 35 ++++---- test/runtime/compiler/required.ts | 1 + test/runtime/type/guard/type/omit.ts | 9 ++ test/runtime/type/guard/type/partial.ts | 9 ++ test/runtime/type/guard/type/pick.ts | 9 ++ test/runtime/type/guard/type/required.ts | 9 ++ test/runtime/value/create/_deferred.ts | 11 +++ test/runtime/value/create/constructor.ts | 11 +-- test/runtime/value/create/function.ts | 2 +- test/runtime/value/create/index.ts | 1 + test/runtime/value/default/_deferred.ts | 11 +++ test/runtime/value/default/index.ts | 1 + test/static/index.ts | 1 - test/static/strict.ts | 20 ----- 36 files changed, 307 insertions(+), 257 deletions(-) create mode 100644 changelog/0.33.0.md delete mode 100644 changelog/latest.md create mode 100644 test/runtime/value/create/_deferred.ts create mode 100644 test/runtime/value/default/_deferred.ts delete mode 100644 test/static/strict.ts diff --git a/changelog/0.32.0.md b/changelog/0.32.0.md index 83a1bddfc..e2609e3bd 100644 --- a/changelog/0.32.0.md +++ b/changelog/0.32.0.md @@ -1,3 +1,42 @@ + + +### 0.32.0 + +- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert. +- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals. +- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020. +- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types. +- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring. +- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode. +- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance. +- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations. +- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates. +- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`. +- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types. +- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning. +- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments. +- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite. +- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures. +- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16. +- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type. +- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any. +- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error. +- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures. +- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules. +- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp +- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types. +- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal +- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required +- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[] +- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create. +- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default. +- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types +- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024 +- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported +- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type +- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack + + ## [0.32.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.32.0) ## Overview diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md new file mode 100644 index 000000000..991172793 --- /dev/null +++ b/changelog/0.33.0.md @@ -0,0 +1,26 @@ +### 0.33.0 + +- [Revision 0.33.8](https://github.com/sinclairzx81/typebox/pull/983) + - [982](https://github.com/sinclairzx81/typebox/issues/982) Prevent Intersect Transform Encode callback from being called twice + - [974](https://github.com/sinclairzx81/typebox/issues/974) Make strict the Encode and Decode return type + - [975](https://github.com/sinclairzx81/typebox/issues/975) Support default annotation being assigned Functions for lazy value initialization on Create + - [980](https://github.com/sinclairzx81/typebox/issues/980) Enable Mapping types to override user defined options from source type + - [976](https://github.com/sinclairzx81/typebox/issues/976) Support Constraint Copy for Pick, Omit (inline with Partial / Required) (Trialing Implementation) + - Flag Strict For Deprecation +- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964) + - Additional updates to improve Default for enumerable objects. +- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963) + - Add object traversal path for Default. Ensure enumerable objects are traversed. +- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959) + - Provide better support for transforming properties with optional modifiers. +- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953) + - Add Assert and Parse value functions. Add defacto AssertError type. +- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950) + - Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE. +- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947) + - Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped. +- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945) + - Apply mutability fix for Intrinsic and Not schematics (inline with Default) +- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941) + - Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default. + diff --git a/changelog/latest.md b/changelog/latest.md deleted file mode 100644 index 29fc5f990..000000000 --- a/changelog/latest.md +++ /dev/null @@ -1,100 +0,0 @@ -### 0.33.0 - -- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964) Additional updates to improve Default for enumerable objects. -- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963) Add object traversal path for Default. Ensure enumerable objects are traversed. -- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959) Provide better support for transforming properties with optional modifiers. -- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953) Add Assert and Parse value functions. Add defacto AssertError type. -- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950) Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE. -- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947) Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped. -- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945) Apply mutability fix for Intrinsic and Not schematics (inline with Default) -- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941) Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default. - -### 0.32.0 - -- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert. -- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals. -- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020. -- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types. -- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring. -- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode. -- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance. -- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations. -- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates. -- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`. -- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types. -- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning. -- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments. -- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite. -- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures. -- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16. -- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type. -- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any. -- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error. -- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures. -- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules. -- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp -- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types. -- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal -- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required -- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[] -- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create. -- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default. -- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types -- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024 -- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported -- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type -- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/package-lock.json b/package-lock.json index 86b31daf8..2bdb89d64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.7", + "version": "0.33.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.7", + "version": "0.33.8", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index aae8d9de3..2d0f7a39b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.7", + "version": "0.33.8", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index b79458c44..ea7d87557 100644 --- a/readme.md +++ b/readme.md @@ -77,7 +77,6 @@ License MIT - [Transform](#types-transform) - [Guard](#types-guard) - [Unsafe](#types-unsafe) - - [Strict](#types-strict) - [Values](#values) - [Assert](#values-assert) - [Create](#values-create) @@ -1078,35 +1077,6 @@ if(TypeGuard.IsString(T)) { } ``` - - -### Strict - -TypeBox types contain various symbol properties that are used for reflection, composition and compilation. These properties are not strictly valid Json Schema; so in some cases it may be desirable to omit them. TypeBox provides a `Strict` function that will omit these properties if necessary. - -```typescript -const T = Type.Object({ // const T = { - name: Type.Optional(Type.String()) // [Symbol(TypeBox.Kind)]: 'Object', -}) // type: 'object', - // properties: { - // name: { - // type: 'string', - // [Symbol(TypeBox.Kind)]: 'String', - // [Symbol(TypeBox.Optional)]: 'Optional' - // } - // } - // } - -const U = Type.Strict(T) // const U = { - // type: 'object', - // properties: { - // name: { - // type: 'string' - // } - // } - // } -``` - ## Values diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index f41203479..bc1fd8b4d 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -105,12 +105,12 @@ export class TypeCheck { return this.checkFunc(value) } /** Decodes a value or throws if error */ - public Decode>(value: unknown): R { + public Decode, Result extends Static = Static>(value: unknown): Result { if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never } /** Encodes a value or throws if error */ - public Encode>(value: unknown): R { + public Encode, Result extends Static = Static>(value: unknown): Result { const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) return encoded as never diff --git a/src/type/object/object.ts b/src/type/object/object.ts index 18d1559b2..c8f2bb06f 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -37,7 +37,7 @@ import { Kind } from '../symbols/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsOptional, IsSchema } from '../guard/kind' +import { IsOptional } from '../guard/kind' // ------------------------------------------------------------------ // ObjectStatic diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 60de12989..99a947706 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -27,19 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' -import type { TSchema, SchemaOptions } from '../schema/index' -import type { TupleToUnion, Evaluate } from '../helpers/index' +import { Discard } from '../discard/discard' +import type { SchemaOptions, TSchema } from '../schema/index' +import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' import type { TMappedKey, TMappedResult } from '../mapped/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { Object, type TObject, type TProperties } from '../object/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' -import { Discard } from '../discard/index' -import { TransformKind } from '../symbols/index' import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key' import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result' - +import { TransformKind } from '../symbols/symbols' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -86,6 +85,19 @@ function FromProperties(T: T, K: return K.reduce((T, K2) => FromProperty(T, K2), T as TProperties) } // ------------------------------------------------------------------ +// FromObject +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +)>> +// prettier-ignore +function FromObject(T: T, K: K): TFromObject { + const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(T['properties'], K) + return Object(properties, options) as never +} +// ------------------------------------------------------------------ // OmitResolve // ------------------------------------------------------------------ // prettier-ignore @@ -93,17 +105,18 @@ function OmitResolve(T: T, K: [...K] return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) : + IsObject(T) ? FromObject(T, K) : Object({}) ) as never } // prettier-ignore -export type TOmit = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : +export type TOmit = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject, K> : TObject<{}> +) // ------------------------------------------------------------------ // TOmit // ------------------------------------------------------------------ @@ -121,5 +134,6 @@ export function Omit(T: TSchema, K: any, options?: SchemaOptions): any { if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - return CreateType(OmitResolve(T, I), options) + // special: mapping types require overridable options + return CreateType({ ...OmitResolve(T, I), ...options }) } diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index a2382b281..21bd5cf3e 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' -import type { Evaluate } from '../helpers/index' +import type { Evaluate, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' import { type TReadonlyOptional } from '../readonly-optional/index' import { type TOptional, Optional } from '../optional/index' @@ -77,6 +77,19 @@ function FromProperties(T: T): TFromProperties { return Acc as never } // ------------------------------------------------------------------ +// FromObject +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +)>> +// prettier-ignore +function FromObject(T: T): TFromObject { + const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(T['properties']) + return Object(properties, options) as never +} +// ------------------------------------------------------------------ // PartialResolve // ------------------------------------------------------------------ // prettier-ignore @@ -84,7 +97,7 @@ function PartialResolve(T: T): TPartial { return ( IsIntersect(T) ? Intersect(FromRest(T.allOf)) : IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? Object(FromProperties(T.properties)) : + IsObject(T) ? FromObject(T) : Object({}) ) as never } @@ -93,10 +106,10 @@ function PartialResolve(T: T): TPartial { // ------------------------------------------------------------------ // prettier-ignore export type TPartial = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are optional */ @@ -105,8 +118,10 @@ export function Partial(T: T, options?: SchemaOptions): export function Partial(T: T, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ export function Partial(T: TSchema, options?: SchemaOptions): any { - if (IsMappedResult(T)) return PartialFromMappedResult(T, options) - const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = PartialResolve(T) - return CreateType({ ...options, ...D, ...R }) as never + if (IsMappedResult(T)) { + return PartialFromMappedResult(T, options) + } else { + // special: mapping types require overridable options + return CreateType({ ...PartialResolve(T), ...options }) + } } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index 5340a4674..bde1e17f0 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -27,18 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' +import { Discard } from '../discard/discard' import type { TSchema, SchemaOptions } from '../schema/index' -import type { TupleToUnion, Evaluate } from '../helpers/index' +import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' import { type TObject, type TProperties, type TPropertyKey, Object } from '../object/index' import type { TMappedKey, TMappedResult } from '../mapped/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' -import { Discard } from '../discard/index' -import { TransformKind } from '../symbols/index' import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' +import { TransformKind } from '../symbols/symbols' // ------------------------------------------------------------------ // TypeGuard @@ -71,14 +71,27 @@ function FromUnion(T: T, K: K) { // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type FromProperties> = Evaluate> +type TFromProperties> = Evaluate> // prettier-ignore -function FromProperties(T: T, K: K) { +function FromProperties(T: T, K: K): TFromProperties { const Acc = {} as TProperties for(const K2 of K) if(K2 in T) Acc[K2 as TPropertyKey] = T[K2 as keyof T] return Acc as never } // ------------------------------------------------------------------ +// FromObject +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +)>> +// prettier-ignore +function FromObject(T: T, K: K): TFromObject { + const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(T['properties'], K) + return Object(properties, options) as never +} +// ------------------------------------------------------------------ // PickResolve // ------------------------------------------------------------------ // prettier-ignore @@ -86,16 +99,16 @@ function PickResolve(T: T, K: [...K] return ( IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) : + IsObject(T) ? FromObject(T, K) : Object({}) ) as never } // prettier-ignore export type TPick = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject, K> : TObject<{}> /** `[Json]` Constructs a type whose keys are picked from the given type */ @@ -112,5 +125,6 @@ export function Pick(T: TSchema, K: any, options?: SchemaOptions): any { if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) // non-mapped const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - return CreateType(PickResolve(T, I), options) + // special: mapping types require overridable options + return CreateType({ ...PickResolve(T, I), ...options }) } diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 807fff050..fab0632f2 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' -import type { Evaluate } from '../helpers/index' +import type { Evaluate, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' import { type TReadonlyOptional } from '../readonly-optional/index' import { type TOptional } from '../optional/index' @@ -76,6 +76,19 @@ function FromProperties(T: T) { return Acc as never } // ------------------------------------------------------------------ +// FromObject +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +)>> +// prettier-ignore +function FromObject(T: T): TFromObject { + const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(T['properties']) + return Object(properties, options) as never +} +// ------------------------------------------------------------------ // RequiredResolve // ------------------------------------------------------------------ @@ -84,7 +97,7 @@ function RequiredResolve(T: T): TRequired { return ( IsIntersect(T) ? Intersect(FromRest(T.allOf)) : IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? Object(FromProperties(T.properties)) : + IsObject(T) ? FromObject(T) : Object({}) ) as never } @@ -93,10 +106,10 @@ function RequiredResolve(T: T): TRequired { // ------------------------------------------------------------------ // prettier-ignore export type TRequired = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */ @@ -108,8 +121,7 @@ export function Required(T: T, options?: SchemaOptions): neve if (IsMappedResult(T)) { return RequiredFromMappedResult(T, options) as never } else { - const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema - const R = RequiredResolve(T) as any - return CreateType({ ...D, ...R }, options) as never + // special: mapping types require overridable options + return CreateType({ ...RequiredResolve(T), ...options }) as never } } diff --git a/src/type/strict/strict.ts b/src/type/strict/strict.ts index ddcf88836..96da5a1e1 100644 --- a/src/type/strict/strict.ts +++ b/src/type/strict/strict.ts @@ -28,7 +28,17 @@ THE SOFTWARE. import type { TSchema } from '../schema/index' -/** `[Json]` Omits compositing symbols from this schema. */ -export function Strict(schema: T): T { +export type TStrict = T + +/** + * @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended + * to use the JSON parse/stringify to remove compositing symbols if needed. This + * is how Strict works internally. + * + * ```typescript + * JSON.parse(JSON.stringify(Type.String())) + * ``` + */ +export function Strict(schema: T): TStrict { return JSON.parse(JSON.stringify(schema)) } diff --git a/src/type/type/json.ts b/src/type/type/json.ts index f9a1ae741..0650ebc96 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -61,7 +61,7 @@ import { Ref, type TRef } from '../ref/index' import { Required, type TRequired, type TRequiredFromMappedResult } from '../required/index' import { Rest, type TRest } from '../rest/index' import { type TSchema, type SchemaOptions } from '../schema/index' -import { Strict } from '../strict/index' +import { Strict, type TStrict } from '../strict/index' import { String, type TString, type StringOptions } from '../string/index' import { TemplateLiteral, type TTemplateLiteral, type TTemplateLiteralKind, type TTemplateLiteralSyntax } from '../template-literal/index' import { Transform, TransformDecodeBuilder } from '../transform/index' @@ -75,8 +75,16 @@ export class JsonTypeBuilder { // ------------------------------------------------------------------------ // Strict // ------------------------------------------------------------------------ - /** `[Json]` Omits compositing symbols from this schema */ - public Strict(schema: T): T { + /** + * @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended + * to use the JSON parse/stringify to remove compositing symbols if needed. This + * is how Strict works internally. + * + * ```typescript + * JSON.parse(JSON.stringify(Type.String())) + * ``` + */ + public Strict(schema: T): TStrict { return Strict(schema) } // ------------------------------------------------------------------------ @@ -242,7 +250,7 @@ export class JsonTypeBuilder { /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: K): TOmitFromMappedKey + public Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey /** `[Json]` Constructs a type whose keys are omitted from the given type */ public Omit>(T: T, K: K, options?: SchemaOptions): TOmit /** `[Json]` Constructs a type whose keys are omitted from the given type */ @@ -252,17 +260,17 @@ export class JsonTypeBuilder { return Omit(schema, unresolved, options) } /** `[Json]` Constructs a type where all properties are optional */ - public Partial(T: T, options?: ObjectOptions): TPartialFromMappedResult + public Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: T, options?: ObjectOptions): TPartial + public Partial(schema: T, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: TSchema, options?: ObjectOptions): any { + public Partial(schema: TSchema, options?: SchemaOptions): any { return Partial(schema, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: [...K]): TPickFromMappedResult + public Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: K): TPickFromMappedKey + public Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey /** `[Json]` Constructs a type whose keys are picked from the given type */ public Pick>(T: T, K: K, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ @@ -288,11 +296,11 @@ export class JsonTypeBuilder { return Ref(unresolved as any, options) } /** `[Json]` Constructs a type where all properties are required */ - public Required(T: T, options?: ObjectOptions): TRequiredFromMappedResult + public Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: T, options?: ObjectOptions): TRequired + public Required(schema: T, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: TSchema, options?: ObjectOptions): any { + public Required(schema: TSchema, options?: SchemaOptions): any { return Required(schema, options) } /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ diff --git a/src/type/type/type.ts b/src/type/type/type.ts index 4f0fc5550..8dd7cc893 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -75,8 +75,8 @@ export { RegExp } from '../regexp/index' export { Required } from '../required/index' export { Rest } from '../rest/index' export { ReturnType } from '../return-type/index' -export { Strict } from '../strict/index' export { String } from '../string/index' +export { Strict } from '../strict/index' export { Symbol } from '../symbol/index' export { TemplateLiteral } from '../template-literal/index' export { Transform } from '../transform/index' diff --git a/src/value/create/create.ts b/src/value/create/create.ts index b46e83761..952c35751 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -70,6 +70,8 @@ import type { TUndefined } from '../../type/undefined/index' import type { TUint8Array } from '../../type/uint8array/index' import type { TVoid } from '../../type/void/index' +import { IsFunction } from '../guard/guard' + // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -82,7 +84,7 @@ export class ValueCreateError extends TypeBoxError { // Default // ------------------------------------------------------------------ function FromDefault(value: unknown) { - return typeof value === 'function' ? value : Clone(value) + return IsFunction(value) ? value() : Clone(value) } // ------------------------------------------------------------------ // Create diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 27b05f555..a29a5a5e7 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -44,7 +44,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsString, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' +import { IsString, IsFunction, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -53,7 +53,8 @@ import { IsKind } from '../../type/guard/kind' // ValueOrDefault // ------------------------------------------------------------------ function ValueOrDefault(schema: TSchema, value: unknown): unknown { - const clone = HasPropertyKey(schema, 'default') ? Clone(schema.default) : undefined + const defaultValue = HasPropertyKey(schema, 'default') ? schema.default : undefined + const clone = IsFunction(defaultValue) ? defaultValue() : Clone(defaultValue) return IsUndefined(value) ? clone : IsObject(value) && IsObject(clone) ? Object.assign(clone, value) : value } // ------------------------------------------------------------------ diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 365f91edd..a39aaa0ea 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -106,7 +106,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string, knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey]) } if (!IsTransform(schema.unevaluatedProperties)) { - return Default(schema, path, knownProperties) + return knownProperties } const unknownKeys = Object.getOwnPropertyNames(knownProperties) const unevaluatedProperties = schema.unevaluatedProperties as TSchema diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 347b14a29..9838f89ab 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -98,9 +98,9 @@ export function Clone(value: T): T { return CloneValue(value) } /** Decodes a value or throws if error */ -export function Decode>(schema: T, references: TSchema[], value: unknown): R +export function Decode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result /** Decodes a value or throws if error */ -export function Decode>(schema: T, value: unknown): R +export function Decode, Result extends Static = Static>(schema: T, value: unknown): Result /** Decodes a value or throws if error */ export function Decode(...args: any[]): any { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] @@ -116,9 +116,9 @@ export function Default(...args: any[]): any { return DefaultValue.apply(DefaultValue, args as any) } /** Encodes a value or throws if error */ -export function Encode>(schema: T, references: TSchema[], value: unknown): R +export function Encode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result /** Encodes a value or throws if error */ -export function Encode>(schema: T, value: unknown): R +export function Encode, Result extends Static = Static>(schema: T, value: unknown): Result /** Encodes a value or throws if error */ export function Encode(...args: any[]): any { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] diff --git a/test/runtime/compiler-ajv/pick.ts b/test/runtime/compiler-ajv/pick.ts index e9c09c700..c628a1f22 100644 --- a/test/runtime/compiler-ajv/pick.ts +++ b/test/runtime/compiler-ajv/pick.ts @@ -52,6 +52,7 @@ describe('compiler-ajv/Pick', () => { Assert.IsEqual(A.additionalProperties, false) Assert.IsEqual(T.additionalProperties, false) }) + it('Should pick with keyof object', () => { const A = Type.Object({ x: Type.Number(), diff --git a/test/runtime/compiler-ajv/required.ts b/test/runtime/compiler-ajv/required.ts index 73820cfb1..8d1028a27 100644 --- a/test/runtime/compiler-ajv/required.ts +++ b/test/runtime/compiler-ajv/required.ts @@ -44,6 +44,7 @@ describe('compiler-ajv/Required', () => { Assert.IsEqual(A.additionalPropeties, false) Assert.IsEqual(T.additionalPropeties, false) }) + // it('Should construct new object when targetting reference', () => { // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) // const R = Type.Ref(T) diff --git a/test/runtime/compiler/omit.ts b/test/runtime/compiler/omit.ts index 8ff7b1509..2adf42ed0 100644 --- a/test/runtime/compiler/omit.ts +++ b/test/runtime/compiler/omit.ts @@ -28,7 +28,6 @@ describe('compiler/Omit', () => { const T = Type.Omit(A, ['z']) strictEqual(T.required!.includes('z'), false) }) - it('Should inherit options from the source object', () => { const A = Type.Object( { @@ -42,7 +41,6 @@ describe('compiler/Omit', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) - it('Should omit with keyof object', () => { const A = Type.Object({ x: Type.Number(), @@ -58,15 +56,14 @@ describe('compiler/Omit', () => { Fail(T, { x: 0, y: 0, z: 0 }) }) it('Should support Omit of Literal', () => { - const A = Type.Object( - { - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }, - { additionalProperties: false }, - ) - const T = Type.Omit(A, Type.Literal('x')) + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Omit(A, Type.Literal('x'), { + additionalProperties: false, + }) Ok(T, { y: 1, z: 1 }) Fail(T, { x: 1, y: 1, z: 1 }) }) diff --git a/test/runtime/compiler/pick.ts b/test/runtime/compiler/pick.ts index 227a55b27..59915e257 100644 --- a/test/runtime/compiler/pick.ts +++ b/test/runtime/compiler/pick.ts @@ -40,6 +40,7 @@ describe('compiler/Pick', () => { strictEqual(A.additionalProperties, false) strictEqual(T.additionalProperties, false) }) + it('Should pick with keyof object', () => { const A = Type.Object({ x: Type.Number(), @@ -55,28 +56,26 @@ describe('compiler/Pick', () => { Fail(T, { x: 0, y: 0, z: 0 }) }) it('Should support Pick of Literal', () => { - const A = Type.Object( - { - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }, - { additionalProperties: false }, - ) - const T = Type.Pick(A, Type.Literal('x')) + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Pick(A, Type.Literal('x'), { + additionalProperties: false, + }) Ok(T, { x: 1 }) Fail(T, { x: 1, y: 1, z: 1 }) }) it('Should support Pick of Never', () => { - const A = Type.Object( - { - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), - }, - { additionalProperties: false }, - ) - const T = Type.Pick(A, Type.Never()) + const A = Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }) + const T = Type.Pick(A, Type.Never(), { + additionalProperties: false, + }) Fail(T, { x: 1, y: 1, z: 1 }) Ok(T, {}) }) diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index fb071fecc..c94daf975 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -44,6 +44,7 @@ describe('compiler/Required', () => { strictEqual(A.additionalPropeties, false) strictEqual(T.additionalPropeties, false) }) + // it('Should construct new object when targetting reference', () => { // const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' }) // const R = Type.Ref(T) diff --git a/test/runtime/type/guard/type/omit.ts b/test/runtime/type/guard/type/omit.ts index dbd7e023c..cbfa16109 100644 --- a/test/runtime/type/guard/type/omit.ts +++ b/test/runtime/type/guard/type/omit.ts @@ -157,4 +157,13 @@ describe('guard/type/TOmit', () => { Assert.IsFalse(T.properties.y.additionalProperties as boolean) Assert.IsFalse(T.properties.z.additionalProperties as boolean) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/980 + // ---------------------------------------------------------------- + it('Should override properties in source type', () => { + const A = Type.Object({ x: Type.Number() }, { title: 'A' }) + const B = Type.Omit(A, ['x'], { title: 'B' }) + Assert.IsEqual(A.title, 'A') + Assert.IsEqual(B.title, 'B') + }) }) diff --git a/test/runtime/type/guard/type/partial.ts b/test/runtime/type/guard/type/partial.ts index 1e5c61512..1fb138d82 100644 --- a/test/runtime/type/guard/type/partial.ts +++ b/test/runtime/type/guard/type/partial.ts @@ -64,4 +64,13 @@ describe('guard/type/TPartial', () => { const R = Type.Partial(S) Assert.IsFalse(TransformKind in R) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/980 + // ---------------------------------------------------------------- + it('Should override properties in source type', () => { + const A = Type.Object({ x: Type.Number() }, { title: 'A' }) + const B = Type.Partial(A, { title: 'B' }) + Assert.IsEqual(A.title, 'A') + Assert.IsEqual(B.title, 'B') + }) }) diff --git a/test/runtime/type/guard/type/pick.ts b/test/runtime/type/guard/type/pick.ts index cb3c5edff..65153aab7 100644 --- a/test/runtime/type/guard/type/pick.ts +++ b/test/runtime/type/guard/type/pick.ts @@ -159,4 +159,13 @@ describe('guard/type/TPick', () => { Assert.IsFalse(T.properties.y.additionalProperties as boolean) Assert.IsFalse(T.properties.z.additionalProperties as boolean) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/980 + // ---------------------------------------------------------------- + it('Should override properties in source type', () => { + const A = Type.Object({ x: Type.Number() }, { title: 'A' }) + const B = Type.Pick(A, ['x'], { title: 'B' }) + Assert.IsEqual(A.title, 'A') + Assert.IsEqual(B.title, 'B') + }) }) diff --git a/test/runtime/type/guard/type/required.ts b/test/runtime/type/guard/type/required.ts index 733302b4f..b4baf6dd1 100644 --- a/test/runtime/type/guard/type/required.ts +++ b/test/runtime/type/guard/type/required.ts @@ -61,4 +61,13 @@ describe('guard/type/TRequired', () => { const R = Type.Required(S) Assert.IsFalse(TransformKind in R) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/980 + // ---------------------------------------------------------------- + it('Should override properties in source type', () => { + const A = Type.Object({ x: Type.Number() }, { title: 'A' }) + const B = Type.Required(A, { title: 'B' }) + Assert.IsEqual(A.title, 'A') + Assert.IsEqual(B.title, 'B') + }) }) diff --git a/test/runtime/value/create/_deferred.ts b/test/runtime/value/create/_deferred.ts new file mode 100644 index 000000000..6b2a86cac --- /dev/null +++ b/test/runtime/value/create/_deferred.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Deferred', () => { + it('Should use deferred value', () => { + const T = Type.Any({ default: () => 1 }) + const R = Value.Create(T) + Assert.IsEqual(R, 1) + }) +}) diff --git a/test/runtime/value/create/constructor.ts b/test/runtime/value/create/constructor.ts index 39c54bdd1..d42dc85c0 100644 --- a/test/runtime/value/create/constructor.ts +++ b/test/runtime/value/create/constructor.ts @@ -22,11 +22,12 @@ describe('value/create/Constructor', () => { test: Type.Function([], Type.Number({ default: 123 })), }), { - default: class { - test() { - return 321 - } - }, + default: () => + class { + test() { + return 321 + } + }, }, ) const C = Value.Create(T) diff --git a/test/runtime/value/create/function.ts b/test/runtime/value/create/function.ts index 0d1c2f1fb..7495bd076 100644 --- a/test/runtime/value/create/function.ts +++ b/test/runtime/value/create/function.ts @@ -10,7 +10,7 @@ describe('value/create/Function', () => { Assert.IsEqual(R, 123) }) it('Should create default', () => { - const T = Type.Function([], Type.Number({ default: 123 }), { default: () => 321 }) + const T = Type.Function([], Type.Number({ default: 123 }), { default: () => () => 321 }) const F = Value.Create(T) const R = F() Assert.IsEqual(R, 321) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 7b51cf3bf..222f79342 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -1,3 +1,4 @@ +import './_deferred' import './any' import './array' import './async-iterator' diff --git a/test/runtime/value/default/_deferred.ts b/test/runtime/value/default/_deferred.ts new file mode 100644 index 000000000..1fc77c567 --- /dev/null +++ b/test/runtime/value/default/_deferred.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Deferred', () => { + it('Should use deferred value', () => { + const T = Type.Any({ default: () => 1 }) + const R = Value.Default(T, 1) + Assert.IsEqual(R, 1) + }) +}) diff --git a/test/runtime/value/default/index.ts b/test/runtime/value/default/index.ts index 7395f9fce..e553311b7 100644 --- a/test/runtime/value/default/index.ts +++ b/test/runtime/value/default/index.ts @@ -1,3 +1,4 @@ +import './_deferred' import './any' import './array' import './async-iterator' diff --git a/test/static/index.ts b/test/static/index.ts index df90d857c..9012f8682 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -44,7 +44,6 @@ import './regexp' import './required' import './rest' import './return-type' -import './strict' import './string' import './symbol' import './template-literal' diff --git a/test/static/strict.ts b/test/static/strict.ts deleted file mode 100644 index a6a0ebcac..000000000 --- a/test/static/strict.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Expect } from './assert' -import { Type, Static } from '@sinclair/typebox' - -{ - const T = Type.Strict( - Type.Object({ - A: Type.String(), - B: Type.String(), - C: Type.String(), - }), - ) - - type T = Static - - Expect(T).ToStatic<{ - A: string - B: string - C: string - }>() -} From d387b2adb7532d7059d5be9cffe9bb8552d72566 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 3 Sep 2024 07:17:46 +0900 Subject: [PATCH 277/369] Revision 0.33.9 (#984) * Generate Interior Intersect Errors * Version * ChangeLog --- changelog/0.33.0.md | 3 +- package-lock.json | 4 +-- package.json | 2 +- src/errors/errors.ts | 11 +++++--- test/runtime/errors/types/intersect.ts | 39 +++++++++++++++++++++----- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 991172793..b098d7e99 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,5 +1,6 @@ ### 0.33.0 - +- [Revision 0.33.9](https://github.com/sinclairzx81/typebox/pull/984) + - [887](https://github.com/sinclairzx81/typebox/issues/887) Generate Nested Intersect Errors - [Revision 0.33.8](https://github.com/sinclairzx81/typebox/pull/983) - [982](https://github.com/sinclairzx81/typebox/issues/982) Prevent Intersect Transform Encode callback from being called twice - [974](https://github.com/sinclairzx81/typebox/issues/974) Make strict the Encode and Decode return type diff --git a/package-lock.json b/package-lock.json index 2bdb89d64..9acbb82ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.8", + "version": "0.33.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.8", + "version": "0.33.9", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 2d0f7a39b..cdc4759f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.8", + "version": "0.33.9", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/errors/errors.ts b/src/errors/errors.ts index f81de1a69..edb58a06d 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -312,13 +312,16 @@ function* FromInteger(schema: TInteger, references: TSchema[], path: string, val } } function* FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): IterableIterator { + let hasError = false for (const inner of schema.allOf) { - const next = Visit(inner, references, path, value).next() - if (!next.done) { - yield Create(ValueErrorType.Intersect, schema, path, value) - yield next.value + for (const error of Visit(inner, references, path, value)) { + hasError = true + yield error } } + if (hasError) { + return yield Create(ValueErrorType.Intersect, schema, path, value) + } if (schema.unevaluatedProperties === false) { const keyCheck = new RegExp(KeyOfPattern(schema)) for (const valueKey of Object.getOwnPropertyNames(value)) { diff --git a/test/runtime/errors/types/intersect.ts b/test/runtime/errors/types/intersect.ts index e45b0dd19..5e1f8f163 100644 --- a/test/runtime/errors/types/intersect.ts +++ b/test/runtime/errors/types/intersect.ts @@ -4,21 +4,46 @@ import { Resolve } from './resolve' import { Assert } from '../../assert' describe('errors/type/Intersect', () => { - const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) it('Should pass 0', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) const R = Resolve(T, { x: 1, y: 1 }) Assert.IsEqual(R.length, 0) }) it('Should pass 1', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) const R = Resolve(T, { x: 1 }) - Assert.IsEqual(R.length, 2) - Assert.IsEqual(R[0].type, ValueErrorType.Intersect) - Assert.IsEqual(R[1].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R.length, 3) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[2].type, ValueErrorType.Intersect) }) it('Should pass 2', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) const R = Resolve(T, { y: 1 }) - Assert.IsEqual(R.length, 2) - Assert.IsEqual(R[0].type, ValueErrorType.Intersect) - Assert.IsEqual(R[1].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R.length, 3) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[2].type, ValueErrorType.Intersect) + }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/887 + // ---------------------------------------------------------------- + it('Should pass 3', () => { + const A = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const B = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ z: Type.Number() })]) + const T = Type.Intersect([A, B]) + const R = Resolve(T, {}) + Assert.IsEqual(R.length, 11) + Assert.IsEqual(R[0].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[1].type, ValueErrorType.Number) + Assert.IsEqual(R[2].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[3].type, ValueErrorType.Number) + Assert.IsEqual(R[4].type, ValueErrorType.Intersect) + Assert.IsEqual(R[5].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[6].type, ValueErrorType.Number) + Assert.IsEqual(R[7].type, ValueErrorType.ObjectRequiredProperty) + Assert.IsEqual(R[8].type, ValueErrorType.Number) + Assert.IsEqual(R[9].type, ValueErrorType.Intersect) + Assert.IsEqual(R[10].type, ValueErrorType.Intersect) }) }) From d049293b2302374e747297110f2f897d5b558cfb Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 12 Sep 2024 15:15:18 +0900 Subject: [PATCH 278/369] Revision 0.33.10 (#991) * Specify Modules with Potential for Side Effects * Version --- package-lock.json | 4 ++-- package.json | 2 +- task/build/package/create-package-json.ts | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9acbb82ae..05b92eb87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.9", + "version": "0.33.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.9", + "version": "0.33.10", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index cdc4759f9..0069f0d80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.9", + "version": "0.33.10", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/task/build/package/create-package-json.ts b/task/build/package/create-package-json.ts index 4113a763c..3fc0a186e 100644 --- a/task/build/package/create-package-json.ts +++ b/task/build/package/create-package-json.ts @@ -91,10 +91,19 @@ function resolveMetadata() { repository: packageJson.repository, // flagged by socket.dev if not present scripts: { test: 'echo test' }, - // disable auto bundle strategy: see https://github.com/esm-dev/esm.sh#bundling-strategy - 'esm.sh': { 'bundle': false }, types: "./build/cjs/index.d.ts", main: "./build/cjs/index.js", - module: "./build/esm/index.mjs" + module: "./build/esm/index.mjs", + // disable auto bundle strategy: see https://github.com/esm-dev/esm.sh#bundling-strategy + 'esm.sh': { 'bundle': false }, + // specify modules with potential for side effects + 'sideEffects': [ + './build/esm/type/registry/format.mjs', + './build/esm/type/registry/type.mjs', + './build/esm/type/system/policy.mjs', + './build/cjs/type/registry/format.js', + './build/cjs/type/registry/type.js', + './build/cjs/type/system/policy.js' + ] } } From a82c224ddcc2efaf0e7a2f681d3327ae243dd432 Mon Sep 17 00:00:00 2001 From: Eric Haynes Date: Sat, 14 Sep 2024 04:05:52 -0400 Subject: [PATCH 279/369] Revision 0.33.11 (#994) - Avoid mutating if no union value matches (#994) --- src/value/default/default.ts | 3 ++- test/runtime/value/default/union.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/value/default/default.ts b/src/value/default/default.ts index a29a5a5e7..0821d7939 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -142,7 +142,8 @@ function FromTuple(schema: TTuple, references: TSchema[], value: unknown): any { function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) for (const inner of schema.anyOf) { - const result = Visit(inner, references, defaulted) + const result = Visit(inner, references, Clone(defaulted)) + if (Check(inner, result)) { return result } diff --git a/test/runtime/value/default/union.ts b/test/runtime/value/default/union.ts index e9f4e682e..b7c07ac1f 100644 --- a/test/runtime/value/default/union.ts +++ b/test/runtime/value/default/union.ts @@ -82,6 +82,15 @@ describe('value/default/Union', () => { const R = Value.Default(T, { x: 3, y: 4 }) Assert.IsEqual(R, { x: 3, y: 4 }) }) + it('Should return the original value if no schemas match', async () => { + const T = Type.Union([ + Type.Tuple([Type.Number(), Type.Number()]), + Type.Array(Type.Number()), + ]) + const value = ['hello'] + const R = Value.Default(T, value) + Assert.IsTrue(R === value) + }) // ---------------------------------------------------------------- // Interior Unsafe // ---------------------------------------------------------------- From ddd85e1f78439f9beaa46767f3e85ad0d458410a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 14 Sep 2024 17:21:12 +0900 Subject: [PATCH 280/369] Revision 0.33.11 (#995) * Reference Issue on Unit | Formatting * TypeScript 5.6.2 * Version --- hammer.mjs | 2 +- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/value/default/default.ts | 1 - test/runtime/value/default/union.ts | 10 +++++++--- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 49faf269b..28f7b3d6a 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 05b92eb87..f084fe304 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.10", + "version": "0.33.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.10", + "version": "0.33.11", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.4" + "typescript": "^5.6.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 0069f0d80..420747da9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.10", + "version": "0.33.11", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.5.4" + "typescript": "^5.6.2" } } diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 0821d7939..d16d9235c 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -143,7 +143,6 @@ function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) for (const inner of schema.anyOf) { const result = Visit(inner, references, Clone(defaulted)) - if (Check(inner, result)) { return result } diff --git a/test/runtime/value/default/union.ts b/test/runtime/value/default/union.ts index b7c07ac1f..15b3512fc 100644 --- a/test/runtime/value/default/union.ts +++ b/test/runtime/value/default/union.ts @@ -2,6 +2,7 @@ import { Value } from '@sinclair/typebox/value' import { Type, Kind, TypeRegistry } from '@sinclair/typebox' import { Assert } from '../../assert/index' +// prettier-ignore describe('value/default/Union', () => { it('Should use default', () => { const T = Type.Union([Type.Number(), Type.String()], { default: 1 }) @@ -82,10 +83,13 @@ describe('value/default/Union', () => { const R = Value.Default(T, { x: 3, y: 4 }) Assert.IsEqual(R, { x: 3, y: 4 }) }) - it('Should return the original value if no schemas match', async () => { + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/993 + // ---------------------------------------------------------------- + it('Should return the original value if no schemas match (cloned interior variant)', async () => { const T = Type.Union([ - Type.Tuple([Type.Number(), Type.Number()]), - Type.Array(Type.Number()), + Type.Tuple([Type.Number(), Type.Number()]), + Type.Array(Type.Number()) ]) const value = ['hello'] const R = Value.Default(T, value) From 928ebd91fc018fbca50b0ce4de48b0ce399503c3 Mon Sep 17 00:00:00 2001 From: Hamish Peebles Date: Wed, 18 Sep 2024 18:23:59 +0100 Subject: [PATCH 281/369] Revision 0.33.12 (#999) * Avoid losing precision when converting to bigints * Use `truncateInteger` rather than `parseInt` * Simplify --- src/value/convert/convert.ts | 3 ++- test/runtime/value/convert/bigint.ts | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 5342320ed..cddb0cf22 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -119,7 +119,8 @@ function TryConvertBoolean(value: unknown) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value } function TryConvertBigInt(value: unknown) { - return IsStringNumeric(value) ? BigInt(parseInt(value)) : IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value + const truncateInteger = (value: string) => value.split('.')[0]; + return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value } function TryConvertString(value: unknown) { return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts index c5057ab00..350e7a1f4 100644 --- a/test/runtime/value/convert/bigint.ts +++ b/test/runtime/value/convert/bigint.ts @@ -23,6 +23,26 @@ describe('value/convert/BigInt', () => { const R = Value.Convert(T, 'false') Assert.IsEqual(R, BigInt(0)) }) + it('Should convert bigint from string 5', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '12345678901234567890') + Assert.IsEqual(R, BigInt("12345678901234567890")) + }) + it('Should convert bigint from string 6', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '-12345678901234567890') + Assert.IsEqual(R, BigInt("-12345678901234567890")) + }) + it('Should convert bigint from string 7', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '12345678901234567890.123') + Assert.IsEqual(R, BigInt("12345678901234567890")) + }) + it('Should convert bigint from string 8', () => { + const T = Type.BigInt() + const R = Value.Convert(T, '-12345678901234567890.123') + Assert.IsEqual(R, BigInt("-12345678901234567890")) + }) it('Should convert bitint from number 1', () => { const T = Type.BigInt() const R = Value.Convert(T, 1) From 0ed2071eed41194fb1d333299c85736ef670700c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 19 Sep 2024 02:39:40 +0900 Subject: [PATCH 282/369] Revision 0.33.12 (#1000) * Version + Formatting * ChangeLog --- changelog/0.33.0.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- src/value/convert/convert.ts | 2 +- test/runtime/value/convert/bigint.ts | 8 ++++---- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index b098d7e99..74913d5ec 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,10 @@ ### 0.33.0 +- [Revision 0.33.12](https://github.com/sinclairzx81/typebox/pull/999) + - [998](https://github.com/sinclairzx81/typebox/issues/998) Avoid losing precision when converting to bigints +- [Revision 0.33.11](https://github.com/sinclairzx81/typebox/pull/994) + - [993](https://github.com/sinclairzx81/typebox/issues/993) Prevent mutation on union values during Convert +- [Revision 0.33.10](https://github.com/sinclairzx81/typebox/pull/991) + - [907](https://github.com/sinclairzx81/typebox/issues/907) Add package.json metadata to specify possible side effect modules - [Revision 0.33.9](https://github.com/sinclairzx81/typebox/pull/984) - [887](https://github.com/sinclairzx81/typebox/issues/887) Generate Nested Intersect Errors - [Revision 0.33.8](https://github.com/sinclairzx81/typebox/pull/983) diff --git a/package-lock.json b/package-lock.json index f084fe304..7ee28d5b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.11", + "version": "0.33.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.11", + "version": "0.33.12", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 420747da9..062077bbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.11", + "version": "0.33.12", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index cddb0cf22..46dae1b3f 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -119,7 +119,7 @@ function TryConvertBoolean(value: unknown) { return IsValueTrue(value) ? true : IsValueFalse(value) ? false : value } function TryConvertBigInt(value: unknown) { - const truncateInteger = (value: string) => value.split('.')[0]; + const truncateInteger = (value: string) => value.split('.')[0] return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value } function TryConvertString(value: unknown) { diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts index 350e7a1f4..383e692a6 100644 --- a/test/runtime/value/convert/bigint.ts +++ b/test/runtime/value/convert/bigint.ts @@ -26,22 +26,22 @@ describe('value/convert/BigInt', () => { it('Should convert bigint from string 5', () => { const T = Type.BigInt() const R = Value.Convert(T, '12345678901234567890') - Assert.IsEqual(R, BigInt("12345678901234567890")) + Assert.IsEqual(R, BigInt('12345678901234567890')) }) it('Should convert bigint from string 6', () => { const T = Type.BigInt() const R = Value.Convert(T, '-12345678901234567890') - Assert.IsEqual(R, BigInt("-12345678901234567890")) + Assert.IsEqual(R, BigInt('-12345678901234567890')) }) it('Should convert bigint from string 7', () => { const T = Type.BigInt() const R = Value.Convert(T, '12345678901234567890.123') - Assert.IsEqual(R, BigInt("12345678901234567890")) + Assert.IsEqual(R, BigInt('12345678901234567890')) }) it('Should convert bigint from string 8', () => { const T = Type.BigInt() const R = Value.Convert(T, '-12345678901234567890.123') - Assert.IsEqual(R, BigInt("-12345678901234567890")) + Assert.IsEqual(R, BigInt('-12345678901234567890')) }) it('Should convert bitint from number 1', () => { const T = Type.BigInt() From e5d5ec051587510b24fab4e1f8b4146b08ba93a0 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Wed, 2 Oct 2024 22:00:16 +0900 Subject: [PATCH 283/369] Revision 0.33.13 (#1011) * Correctly Deref Union Variant in Default * Version * ChangeLog --- changelog/0.33.0.md | 2 ++ package-lock.json | 4 +-- package.json | 2 +- src/value/convert/convert.ts | 8 ++---- src/value/create/create.ts | 10 ++----- src/value/default/default.ts | 12 +++----- src/value/deref/deref.ts | 14 ++++++++-- src/value/transform/decode.ts | 8 ++---- src/value/transform/encode.ts | 8 ++---- src/value/transform/has.ts | 10 ++----- test/runtime/value/default/recursive.ts | 37 ++++++++++++++++++++++++- 11 files changed, 68 insertions(+), 47 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 74913d5ec..71155ab8a 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.13](https://github.com/sinclairzx81/typebox/pull/1011) + - [1010](https://github.com/sinclairzx81/typebox/pull/1011) Fixes Value.Parse fails with recursive types - [Revision 0.33.12](https://github.com/sinclairzx81/typebox/pull/999) - [998](https://github.com/sinclairzx81/typebox/issues/998) Avoid losing precision when converting to bigints - [Revision 0.33.11](https://github.com/sinclairzx81/typebox/pull/994) diff --git a/package-lock.json b/package-lock.json index 7ee28d5b4..103ce872d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.12", + "version": "0.33.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.12", + "version": "0.33.13", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 062077bbc..375d8ab30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.12", + "version": "0.33.13", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 46dae1b3f..92e6c5811 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { Clone } from '../clone/index' import { Check } from '../check/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' @@ -249,12 +249,8 @@ function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { } return value } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} function Visit(schema: TSchema, references: TSchema[], value: any): unknown { - const references_ = IsString(schema.$id) ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any switch (schema[Kind]) { case 'Array': diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 952c35751..a1c24c578 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -26,10 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { HasPropertyKey, IsString } from '../guard/index' +import { HasPropertyKey } from '../guard/index' import { Check } from '../check/index' import { Clone } from '../clone/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { TemplateLiteralGenerate, IsTemplateLiteralFinite } from '../../type/template-literal/index' import { PatternStringExact, PatternNumberExact } from '../../type/patterns/index' import { TypeRegistry } from '../../type/registry/index' @@ -391,12 +391,8 @@ function FromKind(schema: TSchema, references: TSchema[]): any { throw new Error('User defined types must specify a default value') } } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} function Visit(schema: TSchema, references: TSchema[]): unknown { - const references_ = IsString(schema.$id) ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any switch (schema_[Kind]) { case 'Any': diff --git a/src/value/default/default.ts b/src/value/default/default.ts index d16d9235c..d12b40338 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { Check } from '../check/index' import { Clone } from '../clone/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' @@ -44,7 +44,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsString, IsFunction, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' +import { IsFunction, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -143,18 +143,14 @@ function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) for (const inner of schema.anyOf) { const result = Visit(inner, references, Clone(defaulted)) - if (Check(inner, result)) { + if (Check(inner, references, result)) { return result } } return defaulted } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} function Visit(schema: TSchema, references: TSchema[], value: unknown): any { - const references_ = IsString(schema.$id) ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any switch (schema_[Kind]) { case 'Array': diff --git a/src/value/deref/deref.ts b/src/value/deref/deref.ts index 91a77915c..2f7d594ad 100644 --- a/src/value/deref/deref.ts +++ b/src/value/deref/deref.ts @@ -31,10 +31,10 @@ import type { TRef } from '../../type/ref/index' import type { TThis } from '../../type/recursive/index' import { TypeBoxError } from '../../type/error/index' import { Kind } from '../../type/symbols/index' - +import { IsString } from '../guard/guard' export class TypeDereferenceError extends TypeBoxError { constructor(public readonly schema: TRef | TThis) { - super(`Unable to dereference schema with $id '${schema.$id}'`) + super(`Unable to dereference schema with $id '${schema.$ref}'`) } } function Resolve(schema: TThis | TRef, references: TSchema[]): TSchema { @@ -42,7 +42,15 @@ function Resolve(schema: TThis | TRef, references: TSchema[]): TSchema { if (target === undefined) throw new TypeDereferenceError(schema) return Deref(target, references) } -/** Dereferences a schema from the references array or throws if not found */ + +/** `[Internal]` Pushes a schema onto references if the schema has an $id and does not exist on references */ +export function Pushref(schema: TSchema, references: TSchema[]): TSchema[] { + if (!IsString(schema.$id) || references.some((target) => target.$id === schema.$id)) return references + references.push(schema) + return references +} + +/** `[Internal]` Dereferences a schema from the references array or throws if not found */ export function Deref(schema: TSchema, references: TSchema[]): TSchema { // prettier-ignore return (schema[Kind] === 'This' || schema[Kind] === 'Ref') diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 5bac20ecf..be56d12bc 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -31,7 +31,7 @@ import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Check } from '../check/index' import type { TSchema } from '../../type/schema/index' @@ -192,13 +192,9 @@ function FromUnion(schema: TUnion, references: TSchema[], path: string, value: a } return Default(schema, path, value) } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} // prettier-ignore function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { - const references_ = typeof schema.$id === 'string' ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any switch (schema[Kind]) { case 'Array': diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index a39aaa0ea..cb626f916 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -31,7 +31,7 @@ import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Check } from '../check/index' import type { TSchema } from '../../type/schema/index' @@ -203,13 +203,9 @@ function FromUnion(schema: TUnion, references: TSchema[], path: string, value: a } return Default(schema, path, value) } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} // prettier-ignore function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { - const references_ = typeof schema.$id === 'string' ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any switch (schema[Kind]) { case 'Array': diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 27d731554..7481d86a1 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' @@ -52,7 +52,7 @@ import { IsTransform, IsSchema } from '../../type/guard/type' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsString, IsUndefined } from '../guard/index' +import { IsUndefined } from '../guard/index' // prettier-ignore function FromArray(schema: TArray, references: TSchema[]): boolean { @@ -120,13 +120,9 @@ function FromTuple(schema: TTuple, references: TSchema[]) { function FromUnion(schema: TUnion, references: TSchema[]) { return IsTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) } -function AddReference(references: TSchema[], schema: TSchema): TSchema[] { - references.push(schema) - return references -} // prettier-ignore function Visit(schema: TSchema, references: TSchema[]): boolean { - const references_ = IsString(schema.$id) ? AddReference(references, schema) : references + const references_ = Pushref(schema, references) const schema_ = schema as any if (schema.$id && visited.has(schema.$id)) return false if (schema.$id) visited.add(schema.$id) diff --git a/test/runtime/value/default/recursive.ts b/test/runtime/value/default/recursive.ts index 417727bda..492f92d24 100644 --- a/test/runtime/value/default/recursive.ts +++ b/test/runtime/value/default/recursive.ts @@ -1,5 +1,5 @@ import { Value } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { TSchema, Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' // prettier-ignore @@ -55,4 +55,39 @@ describe('value/default/Recursive', () => { id: 1 }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1010 + // ---------------------------------------------------------------- + it('Should default Recursive Union', () => { + const Binary = (node: Node) => Type.Object({ + type: Type.Literal('Binary'), + left: node, + right: node + }) + const Node = Type.Object({ + type: Type.Literal('Node'), + value: Type.String({ default: 'X' }) + }) + const Expr = Type.Recursive(This => Type.Union([Binary(This), Node])) + const R = Value.Default(Expr, { + type: 'Binary', + left: { + type: 'Node' + }, + right: { + type: 'Node' + } + }) + Assert.IsEqual(R, { + type: 'Binary', + left: { + type: 'Node', + value: 'X' + }, + right: { + type: 'Node', + value: 'X' + } + }) + }) }) From 0306823c823930143245e1c62b32a83c0b2b6529 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 3 Oct 2024 11:47:38 +0900 Subject: [PATCH 284/369] Module Prototype (#1016) * Add Module Prototype * Discard Evaluate and Mutual Prototypes * Update Prototype Readme --- example/prototypes/evaluate.ts | 134 -------------------------- example/prototypes/index.ts | 2 +- example/prototypes/module.ts | 166 +++++++++++++++++++++++++++++++++ example/prototypes/mutual.ts | 48 ---------- example/prototypes/readme.md | 70 +++++++------- 5 files changed, 201 insertions(+), 219 deletions(-) delete mode 100644 example/prototypes/evaluate.ts create mode 100644 example/prototypes/module.ts delete mode 100644 example/prototypes/mutual.ts diff --git a/example/prototypes/evaluate.ts b/example/prototypes/evaluate.ts deleted file mode 100644 index 460d86773..000000000 --- a/example/prototypes/evaluate.ts +++ /dev/null @@ -1,134 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/prototypes - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { - AssertRest, - AssertType, - Static, - Type, - TypeGuard, - TSchema, - TObject, - Evaluate, - TArray, - TFunction, - TConstructor, - TPromise, - TIterator, - TAsyncIterator, - TTuple, - TProperties, - TIntersect, - TUnion, - TNever -} from '@sinclair/typebox' - -// -------------------------------------------------------------------------- -// TEvaluate -// -------------------------------------------------------------------------- -// prettier-ignore -export type TEvaluateIntersectType = ( - Static extends Static ? - Static extends Static ? R : - Static extends Static ? L : - TNever : - Static extends Static ? R : - TNever -) -// prettier-ignore -export type TEvaluateIntersectRest = - T extends [infer L, infer R, ...infer Rest] ? TEvaluateIntersectRest<[TEvaluateIntersectType, AssertType>, ...AssertRest]> : - T -// prettier-ignore -export type TEvaluateProperties = Evaluate<{ - [K in keyof T]: TEvaluate -}> -// prettier-ignore -export type TEvaluateArray = T extends [infer L, ...infer R] ? - [TEvaluate>, ...TEvaluateArray>] : - [] -// prettier-ignore -export type TEvaluate = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TConstructor ? TConstructor, TEvaluate> : - T extends TFunction ? TFunction, TEvaluate> : - T extends TObject ? TObject> : - T extends TTuple ? TTuple> : - T extends TArray ? TArray> : - T extends TPromise ? TPromise> : - T extends TIterator ? TIterator> : - T extends TAsyncIterator ? TAsyncIterator> : - T -// -------------------------------------------------------------------------- -// Evaluate -// -------------------------------------------------------------------------- -// prettier-ignore -export function EvaluateIntersectType(L: X, R: Y) { - return Type.Extends(L, R, - Type.Extends(R, L, R, - Type.Extends(L, R, L, - Type.Never())), - Type.Extends(R, L, R, - Type.Never())) -} -// prettier-ignore -export function EvaluateIntersectRest(T: [...T]): TEvaluateIntersectRest { - if(T.length >= 2) { - const [L, R, ...Rest] = T - return EvaluateIntersectRest([EvaluateIntersectType(L, R), ...Rest]) as any - } else { - return T as any - } -} -export function EvaluateProperties(properties: T) { - return Object.getOwnPropertyNames(properties).reduce((acc, key) => { - return { ...acc, [key]: Evaluate(properties[key]) } - }, {} as TProperties) -} -export function EvaluateArray(rest: T) { - if (rest === undefined) return [] // for tuple items - return rest.map((schema) => Evaluate(schema)) -} -// prettier-ignore -export function Evaluate(schema: T): TEvaluate { - return ( - TypeGuard.IsIntersect(schema) ? Type.Intersect(EvaluateIntersectRest(schema.allOf)) : - TypeGuard.IsUnion(schema) ? Type.Union(EvaluateArray(schema.anyOf)) : - TypeGuard.IsAsyncIterator(schema) ? Type.AsyncIterator(Evaluate(schema.items)) : - TypeGuard.IsIterator(schema) ? Type.Iterator(Evaluate(schema.items)) : - TypeGuard.IsObject(schema) ? Type.Object(EvaluateProperties(schema.properties)) : - TypeGuard.IsConstructor(schema) ? Type.Constructor(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : - TypeGuard.IsFunction(schema) ? Type.Function(EvaluateArray(schema.parameters), Evaluate(schema.returns)) : - TypeGuard.IsTuple(schema) ? Type.Tuple(EvaluateArray(schema.items)) : - TypeGuard.IsArray(schema) ? Type.Promise(Evaluate(schema.items)) : - TypeGuard.IsPromise(schema) ? Type.Promise(Evaluate(schema.item)) : - schema - ) as TEvaluate -} - diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index c0039fc87..cb81ad0bb 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './evaluate' export * from './from-schema' +export * from './module' export * from './partial-deep' export * from './union-enum' export * from './union-oneof' diff --git a/example/prototypes/module.ts b/example/prototypes/module.ts new file mode 100644 index 000000000..e56dcb2dd --- /dev/null +++ b/example/prototypes/module.ts @@ -0,0 +1,166 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' +import { Check } from '@sinclair/typebox/value' +// ------------------------------------------------------------------ +// Infer +// ------------------------------------------------------------------ +// prettier-ignore +export type InferImport = ( + Infer +) +// prettier-ignore +export type InferModuleRef = ( + Ref extends keyof Module ? Infer : never +) +// prettier-ignore +export type InferObject = { + [K in keyof Properties]: Infer +} & {} +// prettier-ignore +export type InferConstructor = Types.Ensure< + new (...args: InferTuple) => Infer +> +// prettier-ignore +export type InferFunction = Types.Ensure< + (...args: InferTuple) => Infer +> +// prettier-ignore +export type InferTuple = ( + Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] + ? InferTuple]> + : Result +) +// prettier-ignore +export type InferIntersect = ( + Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] + ? InferIntersect> + : Result +) +// prettier-ignore +export type InferUnion = ( + Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] + ? InferUnion> + : Result +) +// prettier-ignore +export type InferArray = ( + Types.Ensure>> +) +// prettier-ignore +export type InferAsyncIterator = ( + Types.Ensure>> +) +// prettier-ignore +export type InferIterator = ( + Types.Ensure>> +) +// prettier-ignore +type Infer = ( + Type extends TImport ? InferImport : + Type extends TModuleRef ? InferModuleRef : + Type extends Types.TObject ? InferObject : + Type extends Types.TConstructor ? InferConstructor : + Type extends Types.TFunction ? InferFunction : + Type extends Types.TTuple ? InferTuple : + Type extends Types.TIntersect ? InferIntersect : + Type extends Types.TUnion ? InferUnion : + Type extends Types.TArray ? InferArray : + Type extends Types.TAsyncIterator ? InferAsyncIterator : + Type extends Types.TIterator ? InferIterator : + Type extends Types.TTemplateLiteral ? Types.Static> : + Type extends Types.TLiteral ? S : + Type extends Types.TAny ? any : + Type extends Types.TBigInt ? bigint : + Type extends Types.TBoolean ? boolean : + Type extends Types.TDate ? Date : + Type extends Types.TInteger ? number : + Type extends Types.TNever ? never : + Type extends Types.TNumber ? number : + Type extends Types.TRegExp ? string : + Type extends Types.TString ? string : + Type extends Types.TSymbol ? symbol : + Type extends Types.TNull ? null : + Type extends Types.TUint8Array ? Uint8Array : + Type extends Types.TUndefined ? undefined : + Type extends Types.TUnknown ? unknown : + Type extends Types.TVoid ? void : + never +) +// ------------------------------------------------------------------ +// ModuleRef +// ------------------------------------------------------------------ +// prettier-ignore +export interface TModuleRef extends Types.TSchema { + [Types.Kind]: 'ModuleRef' + $ref: Ref +} +// prettier-ignore +export function ModuleRef($ref: Ref): TModuleRef { + return Types.Type.Ref($ref) as never +} +// ------------------------------------------------------------------ +// Import +// ------------------------------------------------------------------ +// prettier-ignore +Types.TypeRegistry.Set('Import', (module: TImport, value: unknown) => { + const keys = Object.getOwnPropertyNames(module.$defs) + const references = keys.map(key => module.$defs[key as never]) as Types.TSchema[] + const schema = module.$defs[module.$ref] + return Check(schema, references, value) +}) +// prettier-ignore +export type TModuleProperties = Record + +// prettier-ignore +export interface TImport extends Types.TSchema { + [Types.Kind]: 'Import' + static: InferImport + $defs: Definitions + $ref: Key +} +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export class ModuleInstance { + constructor(private readonly properties: Properties) {} + public Import(key: Key): TImport { + const $defs = globalThis.Object.getOwnPropertyNames(this.properties).reduce((Result, Key) => ( + { ...Result, [Key]: { ...this.properties[Key], $id: Key }} + ), {}) + return { [Types.Kind]: 'Import', $defs, $ref: key } as never + } +} +export function Module(properties: Properties): ModuleInstance { + return new ModuleInstance(properties) +} + + + diff --git a/example/prototypes/mutual.ts b/example/prototypes/mutual.ts deleted file mode 100644 index c47b7c798..000000000 --- a/example/prototypes/mutual.ts +++ /dev/null @@ -1,48 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/prototypes - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Type, TSchema, Static } from '@sinclair/typebox' - -// Mutual Recursive Template -const __A = (reference: T) => Type.Object({ - b: Type.Union([reference, Type.Null()]) -}, { additionalProperties: false }) - -const __B = (reference: T) => Type.Object({ - a: Type.Union([reference, Type.Null()]) -}, { additionalProperties: false }) - -// .... - -// Mutual Recursive Types -const A = Type.Recursive((This) => __A(__B(This))) -const B = Type.Recursive((This) => __B(__A(This))) - -type A = Static; -type B = Static; - diff --git a/example/prototypes/readme.md b/example/prototypes/readme.md index 77a962c70..7c7c429f2 100644 --- a/example/prototypes/readme.md +++ b/example/prototypes/readme.md @@ -1,48 +1,46 @@ # TypeBox Prototypes -TypeBox prototypes are a set of types that are either under consideration for inclusion into the library, or have been requested by users but cannot be added to the library either due to complexity, using schematics that fall outside the supported TypeBox or should be expressed by users via advanced type composition. +TypeBox prototypes are a set of types that are either under consideration for inclusion into the library, or have been requested by users but cannot be added to the library either due to complexity, using schematics that fall outside the supported TypeBox or should be expressed by users via advanced type composition. -Each prototype is written as a standalone module that can be copied into projects and used directly, or integrated into extended type builders. +## Module, ModuleRef and Import -## Const - -This type will wrap all interior properties as `readonly` leaving the outer type unwrapped. This type is analogous to the `Readonly` TypeScript utility type, but as TypeBox uses this name as a property modifier, the name `Const` is used. +The Module type as a candidate referencing system for TypeBox. Modules enable deferred cross type referencing and support mutual recursive inference. Module types must be instanced via `M.Import(...)` which constructs a `$def` schematic containing each definition required to validate, and a self referential `$ref` to one of the type being imported. ```typescript -import { Const } from './prototypes' - -const T = Const(Type.Object({ // const T: TObject<{ - x: Type.Number() // x: Type.Readonly(Type.Number()) -})) // }> - -type T = Static // type T = { - // readonly x: number - // } -``` -## Evaluate - -This type is an advanced mapping type will evaluate for schema redundancy by reducing evaluating intersection rest arguments. This type detects if intersection would produce illogical `never`, removes duplicates and handles intersection type narrowing. This type is a strong candidate for inclusion into the TypeBox library but is pending an equivalent redundancy check for `union` rest arguments. - -```typescript -import { Evaluate } from './prototypes' - -// Evaluate for Duplicates -// -const T = Type.Intersect([ Type.Number(), Type.Number(), Type.Number() ]) +import { Module, ModuleRef } from './prototypes' -const E = Evaluate(T) // const E: TNumber - -// Evaluate for TNever -// -const T = Type.Intersect([ Type.Number(), Type.String() ]) - -const E = Evaluate(T) // const E: TIntersect<[TNumber, TString]> +// ------------------------------------------------------------------ +// Module, ModuleRef +// ------------------------------------------------------------------ +const Math = Module({ + Vector2: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + Vector3: Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number() + }), + Vertex: Type.Object({ + position: ModuleRef('Vector3'), + normal: ModuleRef('Vector3'), + texcoord: ModuleRef('Vector2') + }), + Geometry: Type.Object({ + vertices: Type.Array(ModuleRef('Vertex')), + indices: Type.Array(Type.Integer()) + }) +}) -// Evaluate for most narrowed type -// -const T = Type.Intersect([ Type.Number(), Type.Literal(1) ]) +// ------------------------------------------------------------------ +// Import +// ----------------------------------------------------------------- -const E = Evaluate(T) // const E: TLiteral<1> +const Vector2 = Math.Import('Vector2') +const Vector3 = Math.Import('Vector2') +const Vertex = Math.Import('Vertex') +const Geometry = Math.Import('Geometry') ``` ## PartialDeep From 9a0309869dc7fbe8b71a23cd04869d318d4d11ce Mon Sep 17 00:00:00 2001 From: Hamish Peebles Date: Fri, 4 Oct 2024 22:08:04 +0100 Subject: [PATCH 285/369] Fix: Converting Large Numbers to BigInt (#1019) * Fix converting large numbers to BigInts * Clarify numeric value * Add test --- src/value/convert/convert.ts | 2 +- test/runtime/value/convert/bigint.ts | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 92e6c5811..a5ddf7b0a 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -120,7 +120,7 @@ function TryConvertBoolean(value: unknown) { } function TryConvertBigInt(value: unknown) { const truncateInteger = (value: string) => value.split('.')[0] - return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(value | 0) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value + return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(Math.trunc(value)) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value } function TryConvertString(value: unknown) { return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value diff --git a/test/runtime/value/convert/bigint.ts b/test/runtime/value/convert/bigint.ts index 383e692a6..02014a86e 100644 --- a/test/runtime/value/convert/bigint.ts +++ b/test/runtime/value/convert/bigint.ts @@ -3,7 +3,7 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/convert/BigInt', () => { - it('Should convert bitint from string 1', () => { + it('Should convert bigint from string 1', () => { const T = Type.BigInt() const R = Value.Convert(T, '1') Assert.IsEqual(R, BigInt(1)) @@ -13,7 +13,7 @@ describe('value/convert/BigInt', () => { const R = Value.Convert(T, '3.14') Assert.IsEqual(R, BigInt(3)) }) - it('Should convert bitint from string 3', () => { + it('Should convert bigint from string 3', () => { const T = Type.BigInt() const R = Value.Convert(T, 'true') Assert.IsEqual(R, BigInt(1)) @@ -43,7 +43,7 @@ describe('value/convert/BigInt', () => { const R = Value.Convert(T, '-12345678901234567890.123') Assert.IsEqual(R, BigInt('-12345678901234567890')) }) - it('Should convert bitint from number 1', () => { + it('Should convert bigint from number 1', () => { const T = Type.BigInt() const R = Value.Convert(T, 1) Assert.IsEqual(R, BigInt(1)) @@ -53,7 +53,27 @@ describe('value/convert/BigInt', () => { const R = Value.Convert(T, 3.14) Assert.IsEqual(R, BigInt(3)) }) - it('Should convert bitint from boolean 1', () => { + it('Should convert bigint from number 3', () => { + const T = Type.BigInt() + const R = Value.Convert(T, Math.pow(2, 31)) + Assert.IsEqual(R, BigInt(2147483648)) + }) + it('Should convert bigint from number 4', () => { + const T = Type.BigInt() + const R = Value.Convert(T, Number.MAX_SAFE_INTEGER) + Assert.IsEqual(R, BigInt(9007199254740991)) + }) + it('Should convert bigint from number 5', () => { + const T = Type.BigInt() + const R = Value.Convert(T, 123456789012345.6789) + Assert.IsEqual(R, BigInt(123456789012345)) + }) + it('Should convert bigint from number 6', () => { + const T = Type.BigInt() + const R = Value.Convert(T, -123456789012345.6789) + Assert.IsEqual(R, BigInt(-123456789012345)) + }) + it('Should convert bigint from boolean 1', () => { const T = Type.BigInt() const R = Value.Convert(T, true) Assert.IsEqual(R, BigInt(1)) From 99f4d9c1a182f5ab3f11c9a1493b343ea4c5b188 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Sat, 5 Oct 2024 06:36:40 +0900 Subject: [PATCH 286/369] Revision 0.33.14 (#1022) * ChangeLog * Version --- changelog/0.33.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 71155ab8a..62359cffb 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.14](https://github.com/sinclairzx81/typebox/pull/1019) + - [1019](https://github.com/sinclairzx81/typebox/pull/1019) Converting Large Numbers to BigInt - [Revision 0.33.13](https://github.com/sinclairzx81/typebox/pull/1011) - [1010](https://github.com/sinclairzx81/typebox/pull/1011) Fixes Value.Parse fails with recursive types - [Revision 0.33.12](https://github.com/sinclairzx81/typebox/pull/999) diff --git a/package-lock.json b/package-lock.json index 103ce872d..a5a68959f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.13", + "version": "0.33.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.13", + "version": "0.33.14", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 375d8ab30..0e27a7a75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.13", + "version": "0.33.14", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From f9134bbadf81f4b9c175ebb8897ea98957e3d4a0 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Mon, 7 Oct 2024 12:08:27 +0900 Subject: [PATCH 287/369] Revision 0.33.15 (#1025) * Add Resolver for Default Date * Version * ChangeLog --- changelog/0.33.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/type/guard/value.ts | 7 +++++++ src/value/default/default.ts | 8 +++++++- src/value/guard/guard.ts | 7 +++++-- test/runtime/value/default/date.ts | 18 ++++++++++++++++++ 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 62359cffb..dbdff7da1 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.15](https://github.com/sinclairzx81/typebox/pull/1025) + - [1024](https://github.com/sinclairzx81/typebox/issues/1024) Fix to correctly resolve default Dates - [Revision 0.33.14](https://github.com/sinclairzx81/typebox/pull/1019) - [1019](https://github.com/sinclairzx81/typebox/pull/1019) Converting Large Numbers to BigInt - [Revision 0.33.13](https://github.com/sinclairzx81/typebox/pull/1011) diff --git a/package-lock.json b/package-lock.json index a5a68959f..0971b4634 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.14", + "version": "0.33.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.14", + "version": "0.33.15", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 0e27a7a75..a217221c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.14", + "version": "0.33.15", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts index e0f63974a..4e4f9d1e2 100644 --- a/src/type/guard/value.ts +++ b/src/type/guard/value.ts @@ -26,6 +26,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +// -------------------------------------------------------------------------- +// PropertyKey +// -------------------------------------------------------------------------- +/** Returns true if this value has this property key */ +export function HasPropertyKey(value: Record, key: K): value is Record & { [_ in K]: unknown } { + return key in value +} // -------------------------------------------------------------------------- // Object Instances // -------------------------------------------------------------------------- diff --git a/src/value/default/default.ts b/src/value/default/default.ts index d12b40338..19b4b6496 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -44,7 +44,7 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ -import { IsFunction, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index' +import { IsArray, IsDate, IsFunction, IsObject, IsUndefined, HasPropertyKey } from '../guard/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ @@ -74,6 +74,10 @@ function FromArray(schema: TArray, references: TSchema[], value: unknown): any { } return defaulted } +function FromDate(schema: TArray, references: TSchema[], value: unknown): any { + // special case intercept for dates + return IsDate(value) ? value : ValueOrDefault(schema, value) +} function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) return schema.allOf.reduce((acc, schema) => { @@ -155,6 +159,8 @@ function Visit(schema: TSchema, references: TSchema[], value: unknown): any { switch (schema_[Kind]) { case 'Array': return FromArray(schema_, references_, value) + case 'Date': + return FromDate(schema_, references_, value) case 'Intersect': return FromIntersect(schema_, references_, value) case 'Object': diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 942560b78..8d5fa25d0 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -139,12 +139,15 @@ export function IsBigUint64Array(value: unknown): value is BigUint64Array { return value instanceof globalThis.BigUint64Array } // -------------------------------------------------------------------------- -// Standard +// PropertyKey // -------------------------------------------------------------------------- /** Returns true if this value has this property key */ -export function HasPropertyKey(value: Record, key: K): value is ObjectType & Record { +export function HasPropertyKey(value: Record, key: K): value is Record & { [_ in K]: unknown } { return key in value } +// -------------------------------------------------------------------------- +// Standard +// -------------------------------------------------------------------------- /** Returns true of this value is an object type */ export function IsObject(value: unknown): value is ObjectType { return value !== null && typeof value === 'object' diff --git a/test/runtime/value/default/date.ts b/test/runtime/value/default/date.ts index 2522cf20d..72b95c0b4 100644 --- a/test/runtime/value/default/date.ts +++ b/test/runtime/value/default/date.ts @@ -13,4 +13,22 @@ describe('value/default/Date', () => { const R = Value.Default(T, null) Assert.IsEqual(R, null) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1024 + // ---------------------------------------------------------------- + it('Should use value if Date is valid', () => { + const T = Type.Date({ default: new Date(1) }) + const R = Value.Default(T, new Date(2)) as Date + Assert.IsEqual(R.getTime(), 2) + }) + it('Should use default if Date is undefined', () => { + const T = Type.Date({ default: new Date(1) }) + const R = Value.Default(T, undefined) as Date + Assert.IsEqual(R.getTime(), 1) + }) + it('Should use value if Date is invalid', () => { + const T = Type.Date({ default: new Date(1) }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) }) From 0f65387f556cbf633c0f28dd8a2f1723b998b24d Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Thu, 10 Oct 2024 21:11:38 -0400 Subject: [PATCH 288/369] Union ValueError Iterator (#1015) * feat: attach to ValueError the first error of each Union.anyOf visit * chore: small tweak * test: add test * chore: tweak test * fix: flatten nested union errors * feat: expose variant errors as ValueErrorIterator[] * feat: use a single ValueErrorIterator instance * update tests * implement requested changes * update test * update ErrorFunctionParameter * small fix --- src/errors/errors.ts | 25 ++++++++++++++----------- src/errors/function.ts | 4 +++- test/runtime/errors/types/union.ts | 4 +++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/errors/errors.ts b/src/errors/errors.ts index edb58a06d..44a8b0227 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -34,6 +34,7 @@ import { GetErrorFunction } from './function' import { TypeBoxError } from '../type/error/index' import { Deref } from '../value/deref/index' import { Hash } from '../value/hash/index' +import { Check } from '../value/check/index' import { Kind } from '../type/symbols/index' import type { TSchema } from '../type/schema/index' @@ -167,6 +168,7 @@ export interface ValueError { path: string value: unknown message: string + errors: ValueErrorIterator[] } // ------------------------------------------------------------------ // ValueErrors @@ -205,8 +207,15 @@ export class ValueErrorIterator { // -------------------------------------------------------------------------- // Create // -------------------------------------------------------------------------- -function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown): ValueError { - return { type: errorType, schema, path, value, message: GetErrorFunction()({ errorType, path, schema, value }) } +function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: unknown, errors: ValueErrorIterator[] = []): ValueError { + return { + type: errorType, + schema, + path, + value, + message: GetErrorFunction()({ errorType, path, schema, value, errors }), + errors, + } } // -------------------------------------------------------------------------- // Types @@ -516,15 +525,9 @@ function* FromUndefined(schema: TUndefined, references: TSchema[], path: string, if (!IsUndefined(value)) yield Create(ValueErrorType.Undefined, schema, path, value) } function* FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): IterableIterator { - let count = 0 - for (const subschema of schema.anyOf) { - const errors = [...Visit(subschema, references, path, value)] - if (errors.length === 0) return // matched - count += errors.length - } - if (count > 0) { - yield Create(ValueErrorType.Union, schema, path, value) - } + if (Check(schema, references, value)) return + const errors = schema.anyOf.map((variant) => new ValueErrorIterator(Visit(variant, references, path, value))) + yield Create(ValueErrorType.Union, schema, path, value, errors) } function* FromUint8Array(schema: TUint8Array, references: TSchema[], path: string, value: any): IterableIterator { if (!IsUint8Array(value)) return yield Create(ValueErrorType.Uint8Array, schema, path, value) diff --git a/src/errors/function.ts b/src/errors/function.ts index 94504c5ad..536f3e682 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -28,7 +28,7 @@ THE SOFTWARE. import { TSchema } from '../type/schema/index' import { Kind } from '../type/symbols/index' -import { ValueErrorType } from './errors' +import { ValueErrorIterator, ValueErrorType } from './errors' /** Creates an error message using en-US as the default locale */ export function DefaultErrorFunction(error: ErrorFunctionParameter) { @@ -178,6 +178,8 @@ export type ErrorFunctionParameter = { schema: TSchema /** The value associated with the error */ value: unknown + /** Interior errors for this error */ + errors: ValueErrorIterator[] } export type ErrorFunction = (parameter: ErrorFunctionParameter) => string /** Manages error message providers */ diff --git a/test/runtime/errors/types/union.ts b/test/runtime/errors/types/union.ts index df110ed30..70d1389a3 100644 --- a/test/runtime/errors/types/union.ts +++ b/test/runtime/errors/types/union.ts @@ -1,5 +1,5 @@ import { Type } from '@sinclair/typebox' -import { ValueErrorType } from '@sinclair/typebox/errors' +import { ValueErrorIterator, ValueErrorType } from '@sinclair/typebox/errors' import { Resolve } from './resolve' import { Assert } from '../../assert' @@ -17,5 +17,7 @@ describe('errors/type/Union', () => { const R = Resolve(T, true) Assert.IsEqual(R.length, 1) Assert.IsEqual(R[0].type, ValueErrorType.Union) + Assert.IsEqual(R[0].errors[0].First()?.type, ValueErrorType.String) + Assert.IsEqual(R[0].errors[1].First()?.type, ValueErrorType.Number) }) }) From 295f8d9e3994f04cd7d76c613da123d89e70c61d Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Fri, 11 Oct 2024 10:40:36 +0900 Subject: [PATCH 289/369] Revision 0.33.16 (#1028) * Version * TypeScript 5.6.3 --- hammer.mjs | 2 +- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 28f7b3d6a..82755bffb 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 0971b4634..d61c9a896 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.15", + "version": "0.33.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.15", + "version": "0.33.16", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.6.2" + "typescript": "^5.6.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index a217221c8..0d2987917 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.15", + "version": "0.33.16", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.6.2" + "typescript": "^5.6.3" } } From ac90038ec7002c9045434ad4f9d82ed4ddf8e244 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 17 Oct 2024 14:19:42 +0900 Subject: [PATCH 290/369] Revision 0.33.17 (#1042) * Use Constant Initializer on Hash Size * Version * ChangeLog --- changelog/0.33.0.md | 4 ++++ package-lock.json | 4 ++-- package.json | 2 +- src/value/hash/hash.ts | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index dbdff7da1..e07a964ef 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,8 @@ ### 0.33.0 +- [Revision 0.33.17](https://github.com/sinclairzx81/typebox/pull/1042) + - [1041](https://github.com/sinclairzx81/typebox/issues/1041) Avoid Exponentiation operator on Value.Hash +- [Revision 0.33.16](https://github.com/sinclairzx81/typebox/pull/1015) + - [1015](https://github.com/sinclairzx81/typebox/issues/1015) Add sub error iterators to ValueError - [Revision 0.33.15](https://github.com/sinclairzx81/typebox/pull/1025) - [1024](https://github.com/sinclairzx81/typebox/issues/1024) Fix to correctly resolve default Dates - [Revision 0.33.14](https://github.com/sinclairzx81/typebox/pull/1019) diff --git a/package-lock.json b/package-lock.json index d61c9a896..f05528cca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.16", + "version": "0.33.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.16", + "version": "0.33.17", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 0d2987917..505014947 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.16", + "version": "0.33.17", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index 2a173423c..eb86c7c72 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -57,7 +57,7 @@ enum ByteMarker { // State // ------------------------------------------------------------------ let Accumulator = BigInt('14695981039346656037') -const [Prime, Size] = [BigInt('1099511628211'), BigInt('2') ** BigInt('64')] +const [Prime, Size] = [BigInt('1099511628211'), BigInt('18446744073709551616' /* 2 ^ 64 */)] const Bytes = Array.from({ length: 256 }).map((_, i) => BigInt(i)) const F64 = new Float64Array(1) const F64In = new DataView(F64.buffer) From a0293a7f29a9338c9616a38116cdf00f8ffc8446 Mon Sep 17 00:00:00 2001 From: Hacxy <162423108+hacxy@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:25:06 +0800 Subject: [PATCH 291/369] Ecosystem (#1050) * Add json2typebox to ecosystem --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index ea7d87557..9590eeaf6 100644 --- a/readme.md +++ b/readme.md @@ -1646,6 +1646,7 @@ The following is a list of community packages that offer general tooling, extend | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | | [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | +| [json2typebox](https://github.com/hacxy/json2typebox) | Creating TypeBox code from Json Data | | [nominal-typebox](https://github.com/Coder-Spirit/nominal/tree/main/%40coderspirit/nominal-typebox) | Allows devs to integrate nominal types into TypeBox schemas | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | | [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schema matching the database models | From c086389ec9836513d43fb57ffa53bf2b4b2e24f4 Mon Sep 17 00:00:00 2001 From: Alec Larson <1925840+aleclarson@users.noreply.github.com> Date: Wed, 6 Nov 2024 05:33:17 -0500 Subject: [PATCH 292/369] Export Decode | Encode Functions (#1052) --- src/value/decode/decode.ts | 45 +++++++++++ src/value/decode/index.ts | 29 +++++++ src/value/encode/encode.ts | 46 +++++++++++ src/value/encode/index.ts | 29 +++++++ src/value/index.ts | 2 + src/value/value/value.ts | 153 +++++-------------------------------- 6 files changed, 168 insertions(+), 136 deletions(-) create mode 100644 src/value/decode/decode.ts create mode 100644 src/value/decode/index.ts create mode 100644 src/value/encode/encode.ts create mode 100644 src/value/encode/index.ts diff --git a/src/value/decode/decode.ts b/src/value/decode/decode.ts new file mode 100644 index 000000000..c1cc8a248 --- /dev/null +++ b/src/value/decode/decode.ts @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { HasTransform, TransformDecode, TransformDecodeCheckError } from '../transform/index' +import { Check } from '../check/index' +import { Errors } from '../../errors/index' + +import type { TSchema } from '../../type/schema/index' +import type { StaticDecode } from '../../type/static/index' + +/** Decodes a value or throws if error */ +export function Decode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result +/** Decodes a value or throws if error */ +export function Decode, Result extends Static = Static>(schema: T, value: unknown): Result +/** Decodes a value or throws if error */ +export function Decode(...args: any[]): any { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) + return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value +} diff --git a/src/value/decode/index.ts b/src/value/decode/index.ts new file mode 100644 index 000000000..4372c79ae --- /dev/null +++ b/src/value/decode/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './decode' diff --git a/src/value/encode/encode.ts b/src/value/encode/encode.ts new file mode 100644 index 000000000..357effe66 --- /dev/null +++ b/src/value/encode/encode.ts @@ -0,0 +1,46 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { HasTransform, TransformEncode, TransformEncodeCheckError } from '../transform/index' +import { Check } from '../check/index' +import { Errors } from '../../errors/index' + +import type { TSchema } from '../../type/schema/index' +import type { StaticEncode } from '../../type/static/index' + +/** Encodes a value or throws if error */ +export function Encode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result +/** Encodes a value or throws if error */ +export function Encode, Result extends Static = Static>(schema: T, value: unknown): Result +/** Encodes a value or throws if error */ +export function Encode(...args: any[]): any { + const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] + const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value + if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) + return encoded +} diff --git a/src/value/encode/index.ts b/src/value/encode/index.ts new file mode 100644 index 000000000..4b57d9b32 --- /dev/null +++ b/src/value/encode/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './encode' diff --git a/src/value/index.ts b/src/value/index.ts index d422f87ca..a1b4e46f5 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -44,8 +44,10 @@ export * from './clean/index' export * from './clone/index' export * from './convert/index' export * from './create/index' +export * from './decode/index' export * from './default/index' export * from './delta/index' +export * from './encode/index' export * from './equal/index' export * from './hash/index' export * from './mutate/index' diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 9838f89ab..ab0e06b3d 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -26,139 +26,20 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { HasTransform, TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' -import { Assert as AssertValue } from '../assert/index' -import { Mutate as MutateValue, type Mutable } from '../mutate/index' -import { Hash as HashValue } from '../hash/index' -import { Equal as EqualValue } from '../equal/index' -import { Cast as CastValue } from '../cast/index' -import { Clone as CloneValue } from '../clone/index' -import { Convert as ConvertValue } from '../convert/index' -import { Create as CreateValue } from '../create/index' -import { Clean as CleanValue } from '../clean/index' -import { Check as CheckValue } from '../check/index' -import { Parse as ParseValue } from '../parse/index' -import { Default as DefaultValue } from '../default/index' -import { Diff as DiffValue, Patch as PatchValue, Edit } from '../delta/index' -import { Errors as ValueErrors, ValueErrorIterator } from '../../errors/index' - -import type { TSchema } from '../../type/schema/index' -import type { Static, StaticDecode, StaticEncode } from '../../type/static/index' - -/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ -export function Assert>(schema: T, references: TSchema[], value: unknown): asserts value is R -/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ -export function Assert>(schema: T, value: unknown): asserts value is R -/** Asserts a value matches the given type or throws an `AssertError` if invalid. */ -export function Assert(...args: any[]): any { - return AssertValue.apply(AssertValue, args as any) -} -/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, references: TSchema[], value: unknown): Static -/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, value: unknown): Static -/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(...args: any[]): any { - return CastValue.apply(CastValue, args as any) -} -/** Creates a value from the given type and references */ -export function Create(schema: T, references: TSchema[]): Static -/** Creates a value from the given type */ -export function Create(schema: T): Static -/** Creates a value from the given type */ -export function Create(...args: any[]): any { - return CreateValue.apply(CreateValue, args as any) -} -/** Returns true if the value matches the given type and references */ -export function Check(schema: T, references: TSchema[], value: unknown): value is Static -/** Returns true if the value matches the given type */ -export function Check(schema: T, value: unknown): value is Static -/** Returns true if the value matches the given type */ -export function Check(...args: any[]): any { - return CheckValue.apply(CheckValue, args as any) -} -/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: TSchema, references: TSchema[], value: unknown): unknown -/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(schema: TSchema, value: unknown): unknown -/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ -export function Clean(...args: any[]): any { - return CleanValue.apply(CleanValue, args as any) -} -/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown -/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: TSchema, value: unknown): unknown -/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(...args: any[]): any { - return ConvertValue.apply(ConvertValue, args as any) -} -/** Returns a structural clone of the given value */ -export function Clone(value: T): T { - return CloneValue(value) -} -/** Decodes a value or throws if error */ -export function Decode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result -/** Decodes a value or throws if error */ -export function Decode, Result extends Static = Static>(schema: T, value: unknown): Result -/** Decodes a value or throws if error */ -export function Decode(...args: any[]): any { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value -} -/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown -/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(schema: TSchema, value: unknown): unknown -/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ -export function Default(...args: any[]): any { - return DefaultValue.apply(DefaultValue, args as any) -} -/** Encodes a value or throws if error */ -export function Encode, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result -/** Encodes a value or throws if error */ -export function Encode, Result extends Static = Static>(schema: T, value: unknown): Result -/** Encodes a value or throws if error */ -export function Encode(...args: any[]): any { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value - if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) - return encoded -} -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, references: TSchema[], value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse(...args: any[]): unknown { - return ParseValue.apply(ParseValue, args as any) -} -/** Returns an iterator for each error in this value. */ -export function Errors(schema: T, references: TSchema[], value: unknown): ValueErrorIterator -/** Returns an iterator for each error in this value. */ -export function Errors(schema: T, value: unknown): ValueErrorIterator -/** Returns an iterator for each error in this value. */ -export function Errors(...args: any[]): any { - return ValueErrors.apply(ValueErrors, args as any) -} -/** Returns true if left and right values are structurally equal */ -export function Equal(left: T, right: unknown): right is T { - return EqualValue(left, right) -} -/** Returns edits to transform the current value into the next value */ -export function Diff(current: unknown, next: unknown): Edit[] { - return DiffValue(current, next) -} -/** Returns a FNV1A-64 non cryptographic hash of the given value */ -export function Hash(value: unknown): bigint { - return HashValue(value) -} -/** Returns a new value with edits applied to the given value */ -export function Patch(current: unknown, edits: Edit[]): T { - return PatchValue(current, edits) as T -} -/** `[Mutable]` Performs a deep mutable value assignment while retaining internal references. */ -export function Mutate(current: Mutable, next: Mutable): void { - MutateValue(current, next) -} +export { Errors, ValueErrorIterator } from '../../errors/index' + +export { Assert } from '../assert/index' +export { Cast } from '../cast/index' +export { Check } from '../check/index' +export { Clean } from '../clean/index' +export { Clone } from '../clone/index' +export { Convert } from '../convert/index' +export { Create } from '../create/index' +export { Decode } from '../decode/index' +export { Default } from '../default/index' +export { Diff, Patch, Edit } from '../delta/index' +export { Encode } from '../encode/index' +export { Equal } from '../equal/index' +export { Hash } from '../hash/index' +export { Mutate, type Mutable } from '../mutate/index' +export { Parse } from '../parse/index' From 6ed1d9b5329b8c930aa852cb679e24bc1ed665ab Mon Sep 17 00:00:00 2001 From: Norbert Elter <72046715+itsyoboieltr@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:37:38 +0400 Subject: [PATCH 293/369] Export Object with Var (#1057) --- src/type/object/object.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type/object/object.ts b/src/type/object/object.ts index c8f2bb06f..965ab8295 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -98,4 +98,4 @@ function _Object(properties: T, options?: ObjectOptions): } /** `[Json]` Creates an Object type */ -export const Object = _Object +export var Object = _Object From db54fac2ec6d576d964504d34915e0d629ce6517 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Wed, 6 Nov 2024 20:01:01 +0900 Subject: [PATCH 294/369] Revision 0.33.18 (#1060) * Version * ChangeLog --- changelog/0.33.0.md | 3 +++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index e07a964ef..319866840 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,7 @@ ### 0.33.0 +- [Revision 0.33.18](https://github.com/sinclairzx81/typebox/pull/1060) + - [1052](https://github.com/sinclairzx81/typebox/pull/1052) Export the Encode | Decode functions directly. Refactoring on Value submodule. + - [1057](https://github.com/sinclairzx81/typebox/pull/1057) Export Object with var declaration to prevent global shadowing. Related Babel [Issue](https://github.com/babel/babel/issues/16943). - [Revision 0.33.17](https://github.com/sinclairzx81/typebox/pull/1042) - [1041](https://github.com/sinclairzx81/typebox/issues/1041) Avoid Exponentiation operator on Value.Hash - [Revision 0.33.16](https://github.com/sinclairzx81/typebox/pull/1015) diff --git a/package-lock.json b/package-lock.json index f05528cca..55faa5b00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.17", + "version": "0.33.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.17", + "version": "0.33.18", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 505014947..4594b1dbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.17", + "version": "0.33.18", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From e75af6daf0760af33412c8a55c5f0ba16c514b1d Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 7 Nov 2024 05:39:29 +0900 Subject: [PATCH 295/369] Revision 0.33.19 (#1061) * Type Fix for Immutable Function * Version --- changelog/0.33.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/type/create/immutable.ts | 23 +++++++++++------------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 319866840..ce6af4c58 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.19](https://github.com/sinclairzx81/typebox/pull/1061) + - Preemptive fix for TypeScript 5.8.0-dev (Type Fix for Immutable Function) - [Revision 0.33.18](https://github.com/sinclairzx81/typebox/pull/1060) - [1052](https://github.com/sinclairzx81/typebox/pull/1052) Export the Encode | Decode functions directly. Refactoring on Value submodule. - [1057](https://github.com/sinclairzx81/typebox/pull/1057) Export Object with var declaration to prevent global shadowing. Related Babel [Issue](https://github.com/babel/babel/issues/16943). diff --git a/package-lock.json b/package-lock.json index 55faa5b00..8f97ebc7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.18", + "version": "0.33.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.18", + "version": "0.33.19", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 4594b1dbb..e614d8232 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.18", + "version": "0.33.19", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/create/immutable.ts b/src/type/create/immutable.ts index 92ff32e5d..d2069efe7 100644 --- a/src/type/create/immutable.ts +++ b/src/type/create/immutable.ts @@ -50,17 +50,16 @@ function ImmutableObject(value: Record) { } return globalThis.Object.freeze(result) } + /** Specialized deep immutable value. Applies freeze recursively to the given value */ -export function Immutable(value: T): T { - return ValueGuard.IsArray(value) - ? ImmutableArray(value) - : ValueGuard.IsDate(value) - ? ImmutableDate(value) - : ValueGuard.IsUint8Array(value) - ? ImmutableUint8Array(value) - : ValueGuard.IsRegExp(value) - ? ImmutableRegExp(value) - : ValueGuard.IsObject(value) - ? ImmutableObject(value) - : value +// prettier-ignore +export function Immutable(value: unknown): unknown { + return ( + ValueGuard.IsArray(value) ? ImmutableArray(value) : + ValueGuard.IsDate(value) ? ImmutableDate(value) : + ValueGuard.IsUint8Array(value) ? ImmutableUint8Array(value) : + ValueGuard.IsRegExp(value) ? ImmutableRegExp(value) : + ValueGuard.IsObject(value) ? ImmutableObject(value) : + value + ) } From 0144c8622c514525754d49ac2f531ae6f39e434c Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 7 Nov 2024 14:14:06 +0900 Subject: [PATCH 296/369] Revision 0.33.20 (#1062) * Add Parse Infrastructure * Version --- changelog/0.33.0.md | 2 + example/index.ts | 10 +- package-lock.json | 4 +- package.json | 2 +- readme.md | 9 +- src/index.ts | 6 +- src/parse/index.ts | 29 + src/parse/parse.ts | 54 ++ src/parse/parsebox/index.ts | 30 + src/parse/parsebox/runtime/guard.ts | 96 +++ src/parse/parsebox/runtime/index.ts | 33 + src/parse/parsebox/runtime/module.ts | 51 ++ src/parse/parsebox/runtime/parse.ts | 141 ++++ src/parse/parsebox/runtime/token.ts | 247 ++++++ src/parse/parsebox/runtime/types.ts | 169 ++++ src/parse/parsebox/static/index.ts | 31 + src/parse/parsebox/static/parse.ts | 119 +++ src/parse/parsebox/static/token.ts | 213 +++++ src/parse/parsebox/static/types.ts | 100 +++ src/parse/runtime.ts | 678 ++++++++++++++++ src/parse/static.ts | 764 ++++++++++++++++++ .../compression/module/typebox-parse.ts | 3 + task/build/package/build.ts | 2 +- test/runtime/index.ts | 1 + test/runtime/parse/index.ts | 1 + test/runtime/parse/parse.ts | 389 +++++++++ tsconfig.json | 1 + 27 files changed, 3175 insertions(+), 10 deletions(-) create mode 100644 src/parse/index.ts create mode 100644 src/parse/parse.ts create mode 100644 src/parse/parsebox/index.ts create mode 100644 src/parse/parsebox/runtime/guard.ts create mode 100644 src/parse/parsebox/runtime/index.ts create mode 100644 src/parse/parsebox/runtime/module.ts create mode 100644 src/parse/parsebox/runtime/parse.ts create mode 100644 src/parse/parsebox/runtime/token.ts create mode 100644 src/parse/parsebox/runtime/types.ts create mode 100644 src/parse/parsebox/static/index.ts create mode 100644 src/parse/parsebox/static/parse.ts create mode 100644 src/parse/parsebox/static/token.ts create mode 100644 src/parse/parsebox/static/types.ts create mode 100644 src/parse/runtime.ts create mode 100644 src/parse/static.ts create mode 100644 task/benchmark/compression/module/typebox-parse.ts create mode 100644 test/runtime/parse/index.ts create mode 100644 test/runtime/parse/parse.ts diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index ce6af4c58..f349e7495 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.20](https://github.com/sinclairzx81/typebox/pull/1062) + - Add TypeScript Parsing Infrastructure. Add Parse API to top level import. - [Revision 0.33.19](https://github.com/sinclairzx81/typebox/pull/1061) - Preemptive fix for TypeScript 5.8.0-dev (Type Fix for Immutable Function) - [Revision 0.33.18](https://github.com/sinclairzx81/typebox/pull/1060) diff --git a/example/index.ts b/example/index.ts index ed52156bd..711d7d03e 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,7 +1,7 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' +import { Type, Parse, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // ----------------------------------------------------------- // Create: Type @@ -17,6 +17,14 @@ type T = Static console.log(T) +// ----------------------------------------------------------- +// Parse: Type +// ----------------------------------------------------------- + +const S = Parse({ T }, `Partial`) + +type S = Static + // ----------------------------------------------------------- // Create: Value // ----------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index 8f97ebc7b..fae9cd780 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e614d8232..e31bce433 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.19", + "version": "0.33.20", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 9590eeaf6..e62b15785 100644 --- a/readme.md +++ b/readme.md @@ -1760,11 +1760,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '119.6 kb' │ ' 52.6 kb' │ '2.27 x' │ -│ typebox/errors │ ' 48.6 kb' │ ' 21.9 kb' │ '2.22 x' │ +│ typebox/compiler │ '119.8 kb' │ ' 52.6 kb' │ '2.28 x' │ +│ typebox/errors │ ' 74.4 kb' │ ' 33.1 kb' │ '2.25 x' │ +│ typebox/parse │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │ │ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ -│ typebox/value │ '157.8 kb' │ ' 66.6 kb' │ '2.37 x' │ -│ typebox │ ' 98.3 kb' │ ' 40.9 kb' │ '2.40 x' │ +│ typebox/value │ '157.2 kb' │ ' 66.1 kb' │ '2.38 x' │ +│ typebox │ '127.3 kb' │ ' 53.3 kb' │ '2.39 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/index.ts b/src/index.ts index b88ad07ea..9f0429d85 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,10 @@ export * from './type/registry/index' export * from './type/sets/index' export * from './type/symbols/index' // ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +export * from './parse/index' +// ------------------------------------------------------------------ // Types // ------------------------------------------------------------------ export * from './type/any/index' @@ -102,6 +106,6 @@ export * from './type/unknown/index' export * from './type/unsafe/index' export * from './type/void/index' // ------------------------------------------------------------------ -// Namespace +// Type.* // ------------------------------------------------------------------ export * from './type/type/index' diff --git a/src/parse/index.ts b/src/parse/index.ts new file mode 100644 index 000000000..d0a1a960c --- /dev/null +++ b/src/parse/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './parse' diff --git a/src/parse/parse.ts b/src/parse/parse.ts new file mode 100644 index 000000000..ef9928f2e --- /dev/null +++ b/src/parse/parse.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Static } from './parsebox/index' +import { CreateType } from '../type/create/type' +import { TSchema, SchemaOptions } from '../type/schema/index' +import { Module } from './runtime' +import { Type } from './static' + +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse = {}>(context: Context, code: Code, options?: SchemaOptions): Static.Parse[0] +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse(code: Code, options?: SchemaOptions): Static.Parse[0] +/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +export function Parse(...args: any[]): never { + return ParseOnly.apply(null, args as never) as never +} + +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly = {}>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly(code: Code, options?: SchemaOptions): TSchema | undefined +/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +export function ParseOnly(...args: any[]): TSchema | undefined { + const withContext = typeof args[0] === 'string' ? false : true + const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] + const type = Module.Parse('Type', code, context)[0] as TSchema | undefined + return (type !== undefined ? CreateType(type, options) : undefined) as never +} diff --git a/src/parse/parsebox/index.ts b/src/parse/parsebox/index.ts new file mode 100644 index 000000000..ec3432ded --- /dev/null +++ b/src/parse/parsebox/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Runtime from './runtime/index' +export * as Static from './static/index' diff --git a/src/parse/parsebox/runtime/guard.ts b/src/parse/parsebox/runtime/guard.ts new file mode 100644 index 000000000..fa002e473 --- /dev/null +++ b/src/parse/parsebox/runtime/guard.ts @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IIdent, INumber, IRef, IString, IConst, ITuple, IUnion } from './types' + +// ------------------------------------------------------------------ +// Value Guard +// ------------------------------------------------------------------ +// prettier-ignore +function HasPropertyKey(value: Record, key: Key): value is Record & { [_ in Key]: unknown } { + return key in value +} +// prettier-ignore +function IsObjectValue(value: unknown): value is Record { + return typeof value === 'object' && value !== null +} +// prettier-ignore +function IsArrayValue(value: unknown): value is unknown[] { + return globalThis.Array.isArray(value) +} +// ------------------------------------------------------------------ +// Parser Guard +// ------------------------------------------------------------------ +/** Returns true if the value is a Tuple Parser */ +// prettier-ignore +export function IsTuple(value: unknown): value is ITuple { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Tuple' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} +/** Returns true if the value is a Union Parser */ +// prettier-ignore +export function IsUnion(value: unknown): value is IUnion { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Union' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} +/** Returns true if the value is a Const Parser */ +// prettier-ignore +export function IsConst(value: unknown): value is IConst { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Const' && HasPropertyKey(value, 'value') && typeof value.value === 'string' +} +/** Returns true if the value is a Ident Parser */ +// prettier-ignore +export function IsIdent(value: unknown): value is IIdent { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ident' +} +/** Returns true if the value is a Number Parser */ +// prettier-ignore +export function IsNumber(value: unknown): value is INumber { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Number' +} +/** Returns true if the value is a Ref Parser */ +// prettier-ignore +export function IsRef(value: unknown): value is IRef { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ref' && HasPropertyKey(value, 'ref') && typeof value.ref === 'string' +} +/** Returns true if the value is a String Parser */ +// prettier-ignore +export function IsString(value: unknown): value is IString { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'String' && HasPropertyKey(value, 'options') && IsArrayValue(value.options) +} +/** Returns true if the value is a Parser */ +// prettier-ignore +export function IsParser(value: unknown) { + return ( + IsTuple(value) || + IsUnion(value) || + IsConst(value) || + IsIdent(value) || + IsNumber(value) || + IsRef(value) || + IsString(value) + ) +} diff --git a/src/parse/parsebox/runtime/index.ts b/src/parse/parsebox/runtime/index.ts new file mode 100644 index 000000000..3c15d4624 --- /dev/null +++ b/src/parse/parsebox/runtime/index.ts @@ -0,0 +1,33 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Guard from './guard' +export * as Token from './token' +export * from './types' +export * from './module' +export * from './parse' diff --git a/src/parse/parsebox/runtime/module.ts b/src/parse/parsebox/runtime/module.ts new file mode 100644 index 000000000..4384ce4f4 --- /dev/null +++ b/src/parse/parsebox/runtime/module.ts @@ -0,0 +1,51 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from './types' +import { Parse } from './parse' + +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export class Module { + constructor(private readonly properties: Properties) { } + + /** Parses using one of the parsers defined on this instance */ + public Parse(key: Key, code: string, context: unknown): [] | [Types.StaticParser, string] + /** Parses using one of the parsers defined on this instance */ + public Parse(key: Key, code: string): [] | [Types.StaticParser, string] + /** Parses using one of the parsers defined on this instance */ + public Parse(...args: any[]): never { + const [key, code, context] = + args.length === 3 ? [args[0], args[1], args[2]] : + args.length === 2 ? [args[0], args[1], undefined] : + (() => { throw Error('Invalid parse arguments') })() + return Parse(this.properties[key], this.properties, code, context) as never + } +} diff --git a/src/parse/parsebox/runtime/parse.ts b/src/parse/parsebox/runtime/parse.ts new file mode 100644 index 000000000..73ecf2f75 --- /dev/null +++ b/src/parse/parsebox/runtime/parse.ts @@ -0,0 +1,141 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Guard from './guard' +import * as Token from './token' +import * as Types from './types' + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +function ParseTuple(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown[], string] { + const buffer = [] as unknown[] + let rest = code + for(const parser of parsers) { + const result = ParseParser(parser, properties, rest, context) + if(result.length === 0) return [] + buffer.push(result[0]) + rest = result[1] + } + return [buffer, rest] +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +function ParseUnion(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown, string] { + for(const parser of parsers) { + const result = ParseParser(parser, properties, code, context) + if(result.length === 0) continue + return result + } + return [] +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +function ParseConst(value: Value, code: string, context: unknown): [] | [Value, string] { + return Token.Const(value, code) as never +} +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +// prettier-ignore +function ParseRef(ref: Ref, properties: Properties, code: string, context: unknown): [] | [string, string] { + const parser = properties[ref] + if(!Guard.IsParser(parser)) throw Error(`Cannot dereference parser '${ref}'`) + return ParseParser(parser, properties, code, context) as never +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function ParseString(options: string[], code: string, _context: unknown): [] | [string, string] { + return Token.String(options, code) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +function ParseNumber(code: string, _context: unknown): [] | [string, string] { + return Token.Number(code) +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +function ParseIdent(code: string, _context: unknown): [] | [string, string] { + return Token.Ident(code) +} +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +// prettier-ignore +function ParseParser(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] { + const result = ( + Guard.IsTuple(parser) ? ParseTuple(parser.parsers, properties, code, context) : + Guard.IsUnion(parser) ? ParseUnion(parser.parsers, properties, code, context) : + Guard.IsConst(parser) ? ParseConst(parser.value, code, context) : + Guard.IsRef(parser) ? ParseRef(parser.ref, properties, code, context) : + Guard.IsString(parser) ? ParseString(parser.options, code, context) : + Guard.IsIdent(parser) ? ParseIdent(code, context) : + Guard.IsNumber(parser) ? ParseNumber(code, context) : + [] + ) + return ( + result.length === 2 + ? [parser.mapping(result[0], context), result[1]] + : result + ) as never +} +// ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, code: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(parser: Parser, code: string): [] | [Types.StaticParser, string] +/** Parses content using the given parser */ +// prettier-ignore +export function Parse(...args: any[]): never { + const withProperties = typeof args[1] === 'string' ? false : true + const [parser, properties, code, context] = withProperties + ? [args[0], args[1], args[2], args[3]] + : [args[0], {}, args[1], args[2]] + return ParseParser(parser, properties, code, context) as never +} diff --git a/src/parse/parsebox/runtime/token.ts b/src/parse/parsebox/runtime/token.ts new file mode 100644 index 000000000..6ad9f4927 --- /dev/null +++ b/src/parse/parsebox/runtime/token.ts @@ -0,0 +1,247 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Chars +// ------------------------------------------------------------------ +// prettier-ignore +namespace Chars { + /** Returns true if the char code is a whitespace */ + export function IsWhitespace(value: number): boolean { + return value === 32 + } + /** Returns true if the char code is a newline */ + export function IsNewline(value: number): boolean { + return value === 10 + } + /** Returns true if the char code is a alpha */ + export function IsAlpha(value: number): boolean { + return ( + (value >= 65 && value <= 90) || // A-Z + (value >= 97 && value <= 122) // a-z + ) + } + /** Returns true if the char code is zero */ + export function IsZero(value: number): boolean { + return value === 48 + } + /** Returns true if the char code is non-zero */ + export function IsNonZero(value: number): boolean { + return value >= 49 && value <= 57 + } + /** Returns true if the char code is a digit */ + export function IsDigit(value: number): boolean { + return ( + IsNonZero(value) || + IsZero(value) + ) + } + /** Returns true if the char code is a dot */ + export function IsDot(value: number): boolean { + return value === 46 + } + /** Returns true if this char code is a underscore */ + export function IsUnderscore(value: unknown): boolean { + return value === 95 + } + /** Returns true if this char code is a dollar sign */ + export function IsDollarSign(value: unknown): boolean { + return value === 36 + } +} + +// ------------------------------------------------------------------ +// Trim +// ------------------------------------------------------------------ +// prettier-ignore +namespace Trim { + /** Trims Whitespace and retains Newline, Tabspaces, etc. */ + export function TrimWhitespaceOnly(code: string): string { + for (let i = 0; i < code.length; i++) { + if (Chars.IsWhitespace(code.charCodeAt(i))) continue + return code.slice(i) + } + return code + } + /** Trims Whitespace including Newline, Tabspaces, etc. */ + export function TrimAll(code: string): string { + return code.trimStart() + } +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +/** Checks the value matches the next string */ +// prettier-ignore +function NextTokenCheck(value: string, code: string): boolean { + if (value.length > code.length) return false + for (let i = 0; i < value.length; i++) { + if (value.charCodeAt(i) !== code.charCodeAt(i)) return false + } + return true +} +/** Gets the next constant string value or empty if no match */ +// prettier-ignore +function NextConst(value: string, code: string, ): [] | [string, string] { + return NextTokenCheck(value, code) + ? [code.slice(0, value.length), code.slice(value.length)] + : [] +} +/** Takes the next constant string value skipping any whitespace */ +// prettier-ignore +export function Const(value: string, code: string): [] | [string, string] { + if(value.length === 0) return ['', code] + const char_0 = value.charCodeAt(0) + return ( + Chars.IsNewline(char_0) ? NextConst(value, Trim.TrimWhitespaceOnly(code)) : + Chars.IsWhitespace(char_0) ? NextConst(value, code) : + NextConst(value, Trim.TrimAll(code)) + ) +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +function IdentIsFirst(char: number) { + return ( + Chars.IsAlpha(char) || + Chars.IsDollarSign(char) || + Chars.IsUnderscore(char) + ) +} +// prettier-ignore +function IdentIsRest(char: number) { + return ( + Chars.IsAlpha(char) || + Chars.IsDigit(char) || + Chars.IsDollarSign(char) || + Chars.IsUnderscore(char) + ) +} +// prettier-ignore +function NextIdent(code: string): [] | [string, string] { + if (!IdentIsFirst(code.charCodeAt(0))) return [] + for (let i = 1; i < code.length; i++) { + const char = code.charCodeAt(i) + if (IdentIsRest(char)) continue + const slice = code.slice(0, i) + const rest = code.slice(i) + return [slice, rest] + } + return [code, ''] +} +/** Scans for the next Ident token */ +// prettier-ignore +export function Ident(code: string): [] | [string, string] { + return NextIdent(Trim.TrimAll(code)) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +/** Checks that the next number is not a leading zero */ +// prettier-ignore +function NumberLeadingZeroCheck(code: string, index: number) { + const char_0 = code.charCodeAt(index + 0) + const char_1 = code.charCodeAt(index + 1) + return ( + ( + // 1-9 + Chars.IsNonZero(char_0) + ) || ( + // 0 + Chars.IsZero(char_0) && + !Chars.IsDigit(char_1) + ) || ( + // 0. + Chars.IsZero(char_0) && + Chars.IsDot(char_1) + ) || ( + // .0 + Chars.IsDot(char_0) && + Chars.IsDigit(char_1) + ) + ) +} +/** Gets the next number token */ +// prettier-ignore +function NextNumber(code: string): [] | [string, string] { + const negated = code.charAt(0) === '-' + const index = negated ? 1 : 0 + if (!NumberLeadingZeroCheck(code, index)) { + return [] + } + const dash = negated ? '-' : '' + let hasDot = false + for (let i = index; i < code.length; i++) { + const char_i = code.charCodeAt(i) + if (Chars.IsDigit(char_i)) { + continue + } + if (Chars.IsDot(char_i)) { + if (hasDot) { + const slice = code.slice(index, i) + const rest = code.slice(i) + return [`${dash}${slice}`, rest] + } + hasDot = true + continue + } + const slice = code.slice(index, i) + const rest = code.slice(i) + return [`${dash}${slice}`, rest] + } + return [code, ''] +} +/** Scans for the next number token */ +// prettier-ignore +export function Number(code: string) { + return NextNumber(Trim.TrimAll(code)) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function NextString(options: string[], code: string): [] | [string, string] { + const first = code.charAt(0) + if(!options.includes(first)) return [] + const quote = first + for(let i = 1; i < code.length; i++) { + const char = code.charAt(i) + if(char === quote) { + const slice = code.slice(1, i) + const rest = code.slice(i + 1) + return [slice, rest] + } + } + return [] +} +/** Scans the next Literal String value */ +// prettier-ignore +export function String(options: string[], code: string) { + return NextString(options, Trim.TrimAll(code)) +} diff --git a/src/parse/parsebox/runtime/types.ts b/src/parse/parsebox/runtime/types.ts new file mode 100644 index 000000000..53dd047d0 --- /dev/null +++ b/src/parse/parsebox/runtime/types.ts @@ -0,0 +1,169 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type IModuleProperties = Record + +// ------------------------------------------------------------------ +// Static +// ------------------------------------------------------------------ + +/** Infers the Output Parameter for a Parser */ +export type StaticParser = Parser extends IParser ? Output : unknown + +// ------------------------------------------------------------------ +// Mapping +// ------------------------------------------------------------------ +export type IMapping = (input: Input, context: any) => Output + +/** Maps input to output. This is the default Mapping */ +export const Identity = (value: unknown) => value + +/** Maps the output as the given parameter T */ +export const As = + (mapping: T): ((value: unknown) => T) => + (_: unknown) => + mapping + +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +export interface IParser { + type: string + mapping: IMapping +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +export type TupleParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? TupleParameter]> : Result +export interface ITuple extends IParser { + type: 'Tuple' + parsers: IParser[] +} +/** Creates a Tuple parser */ +export function Tuple>>(parsers: [...Parsers], mapping: Mapping): ITuple> +/** Creates a Tuple parser */ +export function Tuple(parsers: [...Parsers]): ITuple> +export function Tuple(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Tuple', parsers, mapping } as never +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +export type UnionParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? UnionParameter> : Result +export interface IUnion extends IParser { + type: 'Union' + parsers: IParser[] +} +/** Creates a Union parser */ +export function Union>>(parsers: [...Parsers], mapping: Mapping): IUnion> +/** Creates a Union parser */ +export function Union(parsers: [...Parsers]): IUnion> +export function Union(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Union', parsers, mapping } as never +} + +// ------------------------------------------------------------------ +// Token +// ------------------------------------------------------------------ +export interface IConst extends IParser { + type: 'Const' + value: string +} +/** Creates a Const parser */ +export function Const>(value: Value, mapping: Mapping): IConst> +/** Creates a Const parser */ +export function Const(value: Value): IConst +export function Const(...args: unknown[]): never { + const [value, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Const', value, mapping } as never +} + +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +export interface IRef extends IParser { + type: 'Ref' + ref: string +} +/** Creates a Ref parser */ +export function Ref>(ref: string, mapping: Mapping): IRef> +/** Creates a Ref parser */ +export function Ref(ref: string): IRef +export function Ref(...args: unknown[]): never { + const [ref, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Ref', ref, mapping } as never +} + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +export interface IString extends IParser { + type: 'String' + options: string[] +} +/** Creates a String Parser. Options are an array of permissable quote characters */ +export function String>(options: string[], mapping: Mapping): IString> +/** Creates a String Parser. Options are an array of permissable quote characters */ +export function String(options: string[]): IString +export function String(...params: unknown[]): never { + const [options, mapping] = params.length === 2 ? [params[0], params[1]] : [params[0], Identity] + return { type: 'String', options, mapping } as never +} + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +export interface IIdent extends IParser { + type: 'Ident' +} +/** Creates an Ident parser */ +export function Ident>(mapping: Mapping): IIdent> +/** Creates an Ident parser */ +export function Ident(): IIdent +export function Ident(...params: unknown[]): never { + const mapping = params.length === 1 ? params[0] : Identity + return { type: 'Ident', mapping } as never +} + +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +export interface INumber extends IParser { + type: 'Number' +} +/** Creates a Number parser */ +export function Number>(mapping: Mapping): INumber> +/** Creates a Number parser */ +export function Number(): INumber +export function Number(...params: unknown[]): never { + const mapping = params.length === 1 ? params[0] : Identity + return { type: 'Number', mapping } as never +} diff --git a/src/parse/parsebox/static/index.ts b/src/parse/parsebox/static/index.ts new file mode 100644 index 000000000..3eb58ee18 --- /dev/null +++ b/src/parse/parsebox/static/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Token from './token' +export * from './parse' +export * from './types' diff --git a/src/parse/parsebox/static/parse.ts b/src/parse/parsebox/static/parse.ts new file mode 100644 index 000000000..f4fc12e1e --- /dev/null +++ b/src/parse/parsebox/static/parse.ts @@ -0,0 +1,119 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Tokens from './token' +import * as Types from './types' + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TupleParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? TupleParser + : [] + : [Result, Code] +) + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type UnionParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? [Value, Rest] + : UnionParser + : [] +) + +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type ConstParser = ( + Tokens.Const extends [infer Match extends Value, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +// prettier-ignore +type IdentParser = ( + Tokens.Ident extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +type NumberParser = ( + Tokens.Number extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +type StringParser = ( + Tokens.String extends [infer Match extends string, infer Rest extends string] + ? [Match, Rest] + : [] +) + +// ------------------------------------------------------------------ +// Parse +// ------------------------------------------------------------------ +// prettier-ignore +type ParseCode = ( + Type extends Types.Union ? UnionParser : + Type extends Types.Tuple ? TupleParser : + Type extends Types.Const ? ConstParser : + Type extends Types.String ? StringParser : + Type extends Types.Ident ? IdentParser : + Type extends Types.Number ? NumberParser : + [] +) +// prettier-ignore +type ParseMapping = ( + (Parser['mapping'] & { input: Result, context: Context })['output'] +) +/** Parses code with the given parser */ +// prettier-ignore +export type Parse = ( + ParseCode extends [infer L extends unknown, infer R extends string] + ? [ParseMapping, R] + : [] +) diff --git a/src/parse/parsebox/static/token.ts b/src/parse/parsebox/static/token.ts new file mode 100644 index 000000000..8bfdf19dd --- /dev/null +++ b/src/parse/parsebox/static/token.ts @@ -0,0 +1,213 @@ +/*-------------------------------------------------------------------------- + +@sinclair/parsebox + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Chars +// ------------------------------------------------------------------ +// prettier-ignore +namespace Chars { + export type Empty = '' + export type Space = ' ' + export type Newline = '\n' + export type Dot = '.' + export type Hyphen = '-' + export type Digit = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + ] + export type Alpha = [ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + ] +} + +// ------------------------------------------------------------------ +// Trim +// ------------------------------------------------------------------ +// prettier-ignore +namespace Trim { + // ------------------------------------------------------------------ + // Whitespace Filters + // ------------------------------------------------------------------ + type W9 = `${W8}${W8}` // 512 + type W8 = `${W7}${W7}` // 256 + type W7 = `${W6}${W6}` // 128 + type W6 = `${W5}${W5}` // 64 + type W5 = `${W4}${W4}` // 32 + type W4 = `${W3}${W3}` // 16 + type W3 = `${W2}${W2}` // 8 + type W2 = `${W1}${W1}` // 4 + type W1 = `${W0}${W0}` // 2 + type W0 = ` ` // 1 + // ------------------------------------------------------------------ + // TrimWhitespace + // ------------------------------------------------------------------ + /** Trims whitespace only */ + export type TrimWhitespace = ( + Code extends `${W4}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W3}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W1}${infer Rest extends string}` ? TrimWhitespace : + Code extends `${W0}${infer Rest extends string}` ? TrimWhitespace : + Code + ) + // ------------------------------------------------------------------ + // Trim + // ------------------------------------------------------------------ + /** Trims Whitespace and Newline */ + export type TrimAll = ( + Code extends `${W4}${infer Rest extends string}` ? TrimAll : + Code extends `${W3}${infer Rest extends string}` ? TrimAll : + Code extends `${W1}${infer Rest extends string}` ? TrimAll : + Code extends `${W0}${infer Rest extends string}` ? TrimAll : + Code extends `${Chars.Newline}${infer Rest extends string}` ? TrimAll : + Code + ) +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +/** Scans for the next match union */ +// prettier-ignore +type NextUnion = ( + Variants extends [infer Variant extends string, ...infer Rest1 extends string[]] + ? NextConst extends [infer Match extends string, infer Rest2 extends string] + ? [Match, Rest2] + : NextUnion + : [] +) + +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +// prettier-ignore +type NextConst = ( + Code extends `${Value}${infer Rest extends string}` + ? [Value, Rest] + : [] +) +/** Scans for the next constant value */ +// prettier-ignore +export type Const = ( + Value extends '' ? ['', Code] : + Value extends `${infer First extends string}${string}` + ? ( + First extends Chars.Newline ? NextConst> : + First extends Chars.Space ? NextConst : + NextConst> + ) : never +) +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +type NextNumberNegate = ( + Code extends `${Chars.Hyphen}${infer Rest extends string}` + ? [Chars.Hyphen, Rest] + : [Chars.Empty, Code] +) +// prettier-ignore +type NextNumberZeroCheck = ( + Code extends `0${infer Rest}` + ? NextUnion extends [string, string] ? false : true + : true +) +// prettier-ignore +type NextNumberScan = ( + NextUnion<[...Chars.Digit, Chars.Dot], Code> extends [infer Char extends string, infer Rest extends string] + ? Char extends Chars.Dot + ? HasDecimal extends false + ? NextNumberScan + : [Result, `.${Rest}`] + : NextNumberScan + : [Result, Code] +) +// prettier-ignore +export type NextNumber = ( + NextNumberNegate extends [infer Negate extends string, infer Rest extends string] + ? NextNumberZeroCheck extends true + ? NextNumberScan extends [infer Number extends string, infer Rest2 extends string] + ? Number extends Chars.Empty + ? [] + : [`${Negate}${Number}`, Rest2] + : [] + : [] + : [] +) +/** Scans for the next literal number */ +export type Number = NextNumber> + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +type NextStringQuote = NextUnion +// prettier-ignore +type NextStringBody = ( + Code extends `${infer Char extends string}${infer Rest extends string}` + ? Char extends Quote + ? [Result, Rest] + : NextStringBody + : [] +) +// prettier-ignore +type NextString = ( + NextStringQuote extends [infer Quote extends string, infer Rest extends string] + ? NextStringBody extends [infer String extends string, infer Rest extends string] + ? [String, Rest] + : [] + : [] +) +/** Scans for the next literal string */ +export type String = NextString> + +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +type IdentLeft = [...Chars.Alpha, '_', '$'] // permissable first characters +type IdentRight = [...Chars.Digit, ...IdentLeft] // permissible subsequent characters + +// prettier-ignore +type NextIdentScan = ( + NextUnion extends [infer Char extends string, infer Rest extends string] + ? NextIdentScan + : [Result, Code] +) +// prettier-ignore +type NextIdent = ( + NextUnion extends [infer Left extends string, infer Rest1 extends string] + ? NextIdentScan extends [infer Right extends string, infer Rest2 extends string] + ? [`${Left}${Right}`, Rest2] + : [] + : [] +) + +/** Scans for the next Ident */ +export type Ident = NextIdent> diff --git a/src/parse/parsebox/static/types.ts b/src/parse/parsebox/static/types.ts new file mode 100644 index 000000000..534125505 --- /dev/null +++ b/src/parse/parsebox/static/types.ts @@ -0,0 +1,100 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/parse + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Mapping +// ------------------------------------------------------------------ +export interface IMapping { + context: unknown + input: unknown + output: unknown +} +/** Maps input to output. This is the default Mapping */ +export interface Identity extends IMapping { + output: this['input'] +} +/** Maps the output as the given parameter T */ +export interface As extends IMapping { + output: T +} +// ------------------------------------------------------------------ +// Parser +// ------------------------------------------------------------------ +/** Base type Parser implemented by all other parsers */ +export interface IParser { + type: string + mapping: Mapping +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +/** Creates a Tuple Parser */ +export interface Tuple extends IParser { + type: 'Tuple' + parsers: [...Parsers] +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +/** Creates a Union Parser */ +export interface Union extends IParser { + type: 'Union' + parsers: [...Parsers] +} +// ------------------------------------------------------------------ +// Const +// ------------------------------------------------------------------ +/** Creates a Const Parser */ +export interface Const extends IParser { + type: 'Const' + value: Value +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +/** Creates a String Parser. Options are an array of permissable quote characters */ +export interface String extends IParser { + type: 'String' + quote: Options +} +// ------------------------------------------------------------------ +// Ident +// ------------------------------------------------------------------ +/** Creates an Ident Parser. */ +// prettier-ignore +export interface Ident extends IParser { + type: 'Ident' +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +/** Creates a Number Parser. */ +// prettier-ignore +export interface Number extends IParser { + type: 'Number' +} diff --git a/src/parse/runtime.ts b/src/parse/runtime.ts new file mode 100644 index 000000000..de018ed65 --- /dev/null +++ b/src/parse/runtime.ts @@ -0,0 +1,678 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Runtime } from './parsebox/index' +import * as Types from '../type/index' + +// ------------------------------------------------------------------ +// Tokens +// ------------------------------------------------------------------ +const Newline = '\n' +const LBracket = '[' +const RBracket = ']' +const LParen = '(' +const RParen = ')' +const LBrace = '{' +const RBrace = '}' +const LAngle = '<' +const RAngle = '>' +const Question = '?' +const Colon = ':' +const Comma = ',' +const SemiColon = ';' +const SingleQuote = "'" +const DoubleQuote = '"' +const Tilde = '`' + +// ------------------------------------------------------------------ +// DestructureRight +// ------------------------------------------------------------------ +// prettier-ignore +function DestructureRight(values: T[]): [T[], T | undefined] { + return (values.length > 0) + ? [values.slice(0, values.length - 1), values[values.length - 1]] + : [values, undefined] +} +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +const Reference = Runtime.Ident((value, context: Record) => { + return value in context ? context[value] : Types.Ref(value) +}) + +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +const Literal = Runtime.Union([ + Runtime.Union([Runtime.Const('true'), Runtime.Const('false')], value => Types.Literal(value === 'true')), + Runtime.Number(value => Types.Literal(parseFloat(value))), + Runtime.String([SingleQuote, DoubleQuote, Tilde], value => Types.Literal(value)) +]) + +// ------------------------------------------------------------------ +// Keyword +// ------------------------------------------------------------------ +// prettier-ignore +const Keyword = Runtime.Union([ + Runtime.Const('any', Runtime.As(Types.Any())), + Runtime.Const('bigint', Runtime.As(Types.BigInt())), + Runtime.Const('boolean', Runtime.As(Types.Boolean())), + Runtime.Const('integer', Runtime.As(Types.Integer())), + Runtime.Const('never', Runtime.As(Types.Never())), + Runtime.Const('null', Runtime.As(Types.Null())), + Runtime.Const('number', Runtime.As(Types.Number())), + Runtime.Const('string', Runtime.As(Types.String())), + Runtime.Const('symbol', Runtime.As(Types.Symbol())), + Runtime.Const('undefined', Runtime.As(Types.Undefined())), + Runtime.Const('unknown', Runtime.As(Types.Unknown())), + Runtime.Const('void', Runtime.As(Types.Void())), +]) + +// ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +const KeyOfMapping = (values: unknown[]) => ( + values.length > 0 +) +// prettier-ignore +const KeyOf = Runtime.Union([ + Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([]) +], KeyOfMapping) + +// ------------------------------------------------------------------ +// IndexArray +// ------------------------------------------------------------------ +// prettier-ignore +const IndexArrayMapping = (values: unknown[]) => ( + values.length === 4 ? [[values[1]], ...values[3] as unknown[]] : + values.length === 3 ? [[], ...values[2] as unknown[]] : + [] +) +// prettier-ignore +const IndexArray = Runtime.Union([ + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), + Runtime.Tuple([]) +], value => IndexArrayMapping(value)) + +// ------------------------------------------------------------------ +// Extends +// ------------------------------------------------------------------ +// prettier-ignore +const ExtendsMapping = (values: unknown[]) => { + return values.length === 6 + ? [values[1], values[3], values[5]] + : [] +} +// prettier-ignore +const Extends = Runtime.Union([ + Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]), + Runtime.Tuple([]) +], ExtendsMapping) + +// ------------------------------------------------------------------ +// Base +// ------------------------------------------------------------------ +// prettier-ignore +const BaseMapping = (values: unknown[]) => { + return values.length === 3 ? values[1] : values[0] +} +// prettier-ignore +const Base = Runtime.Union([ + Runtime.Tuple([ + Runtime.Const(LParen), + Runtime.Ref('Type'), + Runtime.Const(RParen) + ]), + Runtime.Tuple([Runtime.Union([ + Runtime.Ref('Literal'), + Runtime.Ref('Keyword'), + Runtime.Ref('Object'), + Runtime.Ref('Tuple'), + Runtime.Ref('Constructor'), + Runtime.Ref('Function'), + Runtime.Ref('Mapped'), + Runtime.Ref('AsyncIterator'), + Runtime.Ref('Iterator'), + Runtime.Ref('ConstructorParameters'), + Runtime.Ref('FunctionParameters'), + Runtime.Ref('InstanceType'), + Runtime.Ref('ReturnType'), + Runtime.Ref('Awaited'), + Runtime.Ref('Array'), + Runtime.Ref('Record'), + Runtime.Ref('Promise'), + Runtime.Ref('Partial'), + Runtime.Ref('Required'), + Runtime.Ref('Pick'), + Runtime.Ref('Omit'), + Runtime.Ref('Exclude'), + Runtime.Ref('Extract'), + Runtime.Ref('Uppercase'), + Runtime.Ref('Lowercase'), + Runtime.Ref('Capitalize'), + Runtime.Ref('Uncapitalize'), + Runtime.Ref('Date'), + Runtime.Ref('Uint8Array'), + Runtime.Ref('Reference') + ])]) +], BaseMapping) + +// ------------------------------------------------------------------ +// Factor +// ------------------------------------------------------------------ +// prettier-ignore +const FactorExtends = (Type: Types.TSchema, Extends: Types.TSchema[]) => { + return Extends.length === 3 + ? Types.Extends(Type, Extends[0], Extends[1], Extends[2]) + : Type +} +// prettier-ignore +const FactorIndexArray = (Type: Types.TSchema, IndexArray: unknown[]): Types.TSchema => { + const [Left, Right] = DestructureRight(IndexArray) as [unknown[], Types.TSchema[]] + return ( + !Types.ValueGuard.IsUndefined(Right) ? ( + Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) : + Right.length === 0 ? Types.Array(FactorIndexArray(Type, Left)) : + Types.Never() + ) : Type + ) +} +// prettier-ignore +const FactorMapping = (KeyOf: boolean, Type: Types.TSchema, IndexArray: unknown[], Extends: Types.TSchema[]) => { + return KeyOf + ? FactorExtends(Types.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) + : FactorExtends(FactorIndexArray(Type, IndexArray), Extends) +} +// prettier-ignore +const Factor = Runtime.Tuple([ + Runtime.Ref('KeyOf'), + Runtime.Ref('Base'), + Runtime.Ref('IndexArray'), + Runtime.Ref('Extends') +], values => FactorMapping(...values)) + +// ------------------------------------------------------------------ +// Expr +// ------------------------------------------------------------------ +// prettier-ignore +function ExprBinaryMapping(Left: Types.TSchema, Rest: unknown[]): Types.TSchema { + return ( + Rest.length === 3 ? (() => { + const [Operator, Right, Next] = Rest as [string, Types.TSchema, unknown[]] + const Schema = ExprBinaryMapping(Right, Next) + if (Operator === '&') { + return Types.TypeGuard.IsIntersect(Schema) + ? Types.Intersect([Left, ...Schema.allOf]) + : Types.Intersect([Left, Schema]) + } + if (Operator === '|') { + return Types.TypeGuard.IsUnion(Schema) + ? Types.Union([Left, ...Schema.anyOf]) + : Types.Union([Left, Schema]) + } + throw 1 + })() : Left + ) +} +// prettier-ignore +const ExprTermTail = Runtime.Union([ + Runtime.Tuple([Runtime.Const('&'), Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail')]), + Runtime.Tuple([]) +]) +// prettier-ignore +const ExprTerm = Runtime.Tuple([ + Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') +], value => ExprBinaryMapping(...value)) +// prettier-ignore +const ExprTail = Runtime.Union([ + Runtime.Tuple([Runtime.Const('|'), Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail')]), + Runtime.Tuple([]) +]) +// prettier-ignore +const Expr = Runtime.Tuple([ + Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') +], value => ExprBinaryMapping(...value)) + +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +const Type = Runtime.Ref('Expr') + +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])]) +// prettier-ignore +const PropertyReadonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], value => value.length > 0) +// prettier-ignore +const PropertyOptional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], value => value.length > 0) +// prettier-ignore +const PropertyMapping = (Readonly: boolean, Key: string, Optional: boolean, _: typeof Colon, Type: Types.TSchema) => ({ + [Key]: ( + Readonly && Optional ? Types.ReadonlyOptional(Type) : + Readonly && !Optional ? Types.Readonly(Type) : + !Readonly && Optional ? Types.Optional(Type) : + Type + ) +}) +// prettier-ignore +const Property = Runtime.Tuple([ + Runtime.Ref('PropertyReadonly'), + Runtime.Ref('PropertyKey'), + Runtime.Ref('PropertyOptional'), + Runtime.Const(Colon), + Runtime.Ref('Type'), +], value => PropertyMapping(...value)) +// prettier-ignore +const PropertyDelimiter = Runtime.Union([ + Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), + Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]), + Runtime.Tuple([Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Const(SemiColon)]), + Runtime.Tuple([Runtime.Const(Newline)]), +]) +// prettier-ignore +const Properties = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), + Runtime.Tuple([Runtime.Ref('Property')]), + Runtime.Tuple([]) +], values => ( + values.length === 3 ? [values[0], ...values[2] as unknown[]] : + values.length === 2 ? [values[0]] : + values.length === 1 ? [values[0]] : + [] +)) + +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +const ObjectMapping = (values: Record[]) => Types.Object(values.reduce((properties, record) => { + return { ...properties, ...record } +}, {} as Types.TProperties)) +// prettier-ignore +const Object = Runtime.Tuple([ + Runtime.Const(LBrace), + Runtime.Ref[]>('Properties'), + Runtime.Const(RBrace) +], values => ObjectMapping(values[1])) + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +const Elements = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma), Runtime.Ref('Elements')]), + Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Ref('Type')]), + Runtime.Tuple([]), +], value => ( + value.length === 3 ? [value[0], ...value[2]] : + value.length === 2 ? [value[0]] : + value.length === 1 ? [value[0]] : + [] +)) +// prettier-ignore +const Tuple = Runtime.Tuple([ + Runtime.Const(LBracket), + Runtime.Ref('Elements'), + Runtime.Const(RBracket) +], value => Types.Tuple(value[1])) + +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +// prettier-ignore +const Parameter = Runtime.Tuple([ + Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') +], value => value[2]) +// prettier-ignore +const Parameters = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma), Runtime.Ref('Parameters')]), + Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Ref('Parameter')]), + Runtime.Tuple([]), +], value => ( + value.length === 3 ? [value[0], ...value[2]] : + value.length === 2 ? [value[0]] : + value.length === 1 ? [value[0]] : + [] +)) +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +const Constructor = Runtime.Tuple([ + Runtime.Const('new'), + Runtime.Const(LParen), + Runtime.Ref('Parameters'), + Runtime.Const(RParen), + Runtime.Const('=>'), + Runtime.Ref('Type') +], value => Types.Constructor(value[2], value[5])) +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +const Function = Runtime.Tuple([ + Runtime.Const(LParen), + Runtime.Ref('Parameters'), + Runtime.Const(RParen), + Runtime.Const('=>'), + Runtime.Ref('Type') +], value => Types.Function(value[1], value[4])) +// ------------------------------------------------------------------ +// Mapped (requires deferred types) +// ------------------------------------------------------------------ +// prettier-ignore +const MappedMapping = (values: unknown[]) => { + return Types.Literal('Mapped types not supported') +} +// prettier-ignore +const Mapped = Runtime.Tuple([ + Runtime.Const(LBrace), Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), Runtime.Ref('Type'), Runtime.Const(RBrace) +], MappedMapping) +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +const AsyncIterator = Runtime.Tuple([ + Runtime.Const('AsyncIterator'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.AsyncIterator(value[2])) +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +const Iterator = Runtime.Tuple([ + Runtime.Const('Iterator'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Iterator(value[2])) +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +const ConstructorParameters = Runtime.Tuple([ + Runtime.Const('ConstructorParameters'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.ConstructorParameters(value[2])) +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +// prettier-ignore +const FunctionParameters = Runtime.Tuple([ + Runtime.Const('Parameters'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Parameters(value[2])) +// ------------------------------------------------------------------ +// InstanceType +// ------------------------------------------------------------------ +// prettier-ignore +const InstanceType = Runtime.Tuple([ + Runtime.Const('InstanceType'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.InstanceType(value[2])) +// ------------------------------------------------------------------ +// ReturnType +// ------------------------------------------------------------------ +// prettier-ignore +const ReturnType = Runtime.Tuple([ + Runtime.Const('ReturnType'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.ReturnType(value[2])) +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +const Awaited = Runtime.Tuple([ + Runtime.Const('Awaited'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Awaited(value[2])) +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +const Array = Runtime.Tuple([ + Runtime.Const('Array'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Array(value[2])) +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +const Record = Runtime.Tuple([ + Runtime.Const('Record'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Record(value[2], value[4])) +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +const Promise = Runtime.Tuple([ + Runtime.Const('Promise'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Promise(value[2])) +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +const Partial = Runtime.Tuple([ + Runtime.Const('Partial'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Partial(value[2])) +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +// prettier-ignore +const Required = Runtime.Tuple([ + Runtime.Const('Required'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Required(value[2])) +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +const Pick = Runtime.Tuple([ + Runtime.Const('Pick'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Pick(value[2], value[4])) +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +const Omit = Runtime.Tuple([ + Runtime.Const('Omit'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Omit(value[2], value[4])) +// ------------------------------------------------------------------ +// Exclude +// ------------------------------------------------------------------ +// prettier-ignore +const Exclude = Runtime.Tuple([ + Runtime.Const('Exclude'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Exclude(value[2], value[4])) +// ------------------------------------------------------------------ +// Extract +// ------------------------------------------------------------------ +// prettier-ignore +const Extract = Runtime.Tuple([ + Runtime.Const('Extract'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(Comma), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Extract(value[2], value[4])) +// ------------------------------------------------------------------ +// Uppercase +// ------------------------------------------------------------------ +// prettier-ignore +const Uppercase = Runtime.Tuple([ + Runtime.Const('Uppercase'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Uppercase(value[2])) +// ------------------------------------------------------------------ +// Lowercase +// ------------------------------------------------------------------ +// prettier-ignore +const Lowercase = Runtime.Tuple([ + Runtime.Const('Lowercase'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Lowercase(value[2])) +// ------------------------------------------------------------------ +// Capitalize +// ------------------------------------------------------------------ +// prettier-ignore +const Capitalize = Runtime.Tuple([ + Runtime.Const('Capitalize'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Capitalize(value[2])) +// ------------------------------------------------------------------ +// Uncapitalize +// ------------------------------------------------------------------ +// prettier-ignore +const Uncapitalize = Runtime.Tuple([ + Runtime.Const('Uncapitalize'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], value => Types.Uncapitalize(value[2])) +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +const Date = Runtime.Const('Date', Runtime.As(Types.Date())) +// ------------------------------------------------------------------ +// Uint8Array +// ------------------------------------------------------------------ +const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(Types.Uint8Array())) +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export const Module = new Runtime.Module({ + Literal, + Keyword, + KeyOf, + IndexArray, + Extends, + Base, + Factor, + ExprTermTail, + ExprTerm, + ExprTail, + Expr, + Type, + PropertyKey, + PropertyReadonly, + PropertyOptional, + Property, + PropertyDelimiter, + Properties, + Object, + Elements, + Tuple, + Parameter, + Function, + Parameters, + Constructor, + Mapped, + AsyncIterator, + Iterator, + Awaited, + Array, + Record, + Promise, + ConstructorParameters, + FunctionParameters, + InstanceType, + ReturnType, + Partial, + Required, + Pick, + Omit, + Exclude, + Extract, + Uppercase, + Lowercase, + Capitalize, + Uncapitalize, + Date, + Uint8Array, + Reference +}) diff --git a/src/parse/static.ts b/src/parse/static.ts new file mode 100644 index 000000000..90bec8e33 --- /dev/null +++ b/src/parse/static.ts @@ -0,0 +1,764 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Static } from './parsebox/index' +import * as Types from '../type/index' + +// ------------------------------------------------------------------ +// Tokens +// ------------------------------------------------------------------ +type Newline = '\n' +type LBracket = '[' +type RBracket = ']' +type LParen = '(' +type RParen = ')' +type LBrace = '{' +type RBrace = '}' +type LAngle = '<' +type RAngle = '>' +type Question = '?' +type Colon = ':' +type Comma = ',' +type SemiColon = ';' +type SingleQuote = "'" +type DoubleQuote = '"' +type Tilde = '`' + +// ------------------------------------------------------------------ +// Delimit +// +// This type is used to perform a partial breadth match for repeated +// elements in a sequence. It used to mitigate depth+1 traversal for +// each element which helps prevent reaching instantiation limits. The +// technique works by infering as wide as possible on the sequence +// enabling TS to hoist interior matches, but does come at slight +// inference performance cost. The current (infer=9) is configured +// to match 64 terminal tuple elements. +// +// for the given sequence +// +// [a, b, c, d, e, f, g] +// +// ------------------------------------------------------------------ +// +// without breadth mapping (infer=1, depth=6) +// +// [infer a, +// [infer b, +// [infer c, +// [infer d, +// [infer e, +// [infer f, +// [infer g, +// []]]]]]]] +// +// ------------------------------------------------------------------ +// +// with breadth mapping (infer=4, depth=2) +// +// [infer a, infer b, infer c, infer d, +// [infer e, infer f, infer g, +// []]] +// +// +// ------------------------------------------------------------------ +// prettier-ignore +interface DelimitTailMapping<_ = unknown> extends Static.IMapping { + output: ( + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer I, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, I, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer Rest extends unknown[]] ? [A, B, C, D, E, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer Rest extends unknown[]] ? [A, B, C, D, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer Rest extends unknown[]] ? [A, B, C, ...Rest] : + this['input'] extends [_, infer A, _, infer B, _, infer Rest extends unknown[]] ? [A, B, ...Rest] : + this['input'] extends [_, infer A, _, infer Rest extends unknown[]] ? [A, ...Rest] : + this['input'] extends [_, infer Rest extends unknown[]] ? [...Rest] : + this['input'] extends [_] ? [] : + [] + ) +} +// prettier-ignore +type DelimitTail = Static.Union<[ + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, T, _, T, _,Delimit]>, + Static.Tuple<[_, T, _, T, _, Delimit]>, + Static.Tuple<[_, T, _, Delimit]>, + Static.Tuple<[_, Delimit]>, + Static.Tuple<[_]>, + Static.Tuple<[]> +], DelimitTailMapping> +// prettier-ignore +interface DelimitMapping extends Static.IMapping { + output: ( + this['input'] extends [infer Element extends unknown, infer Rest extends unknown[]] + ? [Element, ...Rest] + : [] + ) +} +// prettier-ignore +type Delimit = ( + Static.Union<[ + Static.Tuple<[Parser, DelimitTail]>, + Static.Tuple<[]> + ], DelimitMapping> +) +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +interface ReferenceMapping extends Static.IMapping { + output: this['input'] extends [infer Key extends string] + ? Key extends keyof this['context'] + ? this['context'][Key] + : Types.TRef + : never +} +type Reference = Static.Tuple<[Static.Ident], ReferenceMapping> + +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +interface LiteralBooleanMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends boolean}` ? Types.TLiteral : never +} +// prettier-ignore +interface LiteralNumberMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends number}` ? Types.TLiteral : never +} +// prettier-ignore +interface LiteralStringMapping extends Static.IMapping { + output: this['input'] extends `${infer S extends string}` ? Types.TLiteral : never +} +// prettier-ignore +type Literal = Static.Union<[ + Static.Union<[Static.Const<'true'>, Static.Const<'false'>], LiteralBooleanMapping>, + Static.Number, + Static.String<[DoubleQuote, SingleQuote, Tilde], LiteralStringMapping>, +]> +// ------------------------------------------------------------------ +// Keyword +// ------------------------------------------------------------------ +// prettier-ignore +type Keyword = Static.Union<[ + Static.Const<'any', Static.As>, + Static.Const<'bigint', Static.As>, + Static.Const<'boolean', Static.As>, + Static.Const<'integer', Static.As>, + Static.Const<'never', Static.As>, + Static.Const<'null', Static.As>, + Static.Const<'number', Static.As>, + Static.Const<'string', Static.As>, + Static.Const<'symbol', Static.As>, + Static.Const<'undefined', Static.As>, + Static.Const<'unknown', Static.As>, + Static.Const<'void', Static.As>, +]> +// ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +interface KeyOfMapping extends Static.IMapping { + output: this['input'] extends [] ? false : true +} +// prettier-ignore +type KeyOf = Static.Union<[ + Static.Tuple<[Static.Const<'keyof'>]>, + Static.Tuple<[]> +], KeyOfMapping> + +// ------------------------------------------------------------------ +// IndexArray +// ------------------------------------------------------------------ +// prettier-ignore +interface IndexArrayMapping extends Static.IMapping { + output: ( + this['input'] extends [LBracket, infer Type extends Types.TSchema, RBracket, infer Rest extends unknown[]] ? [[Type], ...Rest] : + this['input'] extends [LBracket, RBracket, infer Rest extends unknown[]] ? [[], ...Rest] : + [] + ) +} +// prettier-ignore +type IndexArray = Static.Union<[ + Static.Tuple<[Static.Const, Type, Static.Const, IndexArray]>, + Static.Tuple<[Static.Const, Static.Const, IndexArray]>, + Static.Tuple<[]> +], IndexArrayMapping> + +// ------------------------------------------------------------------ +// Extends +// ------------------------------------------------------------------ +// prettier-ignore +interface ExtendsMapping extends Static.IMapping { + output: this['input'] extends ['extends', infer Type extends Types.TSchema, Question, infer True extends Types.TSchema, Colon, infer False extends Types.TSchema] + ? [Type, True, False] + : [] +} +// prettier-ignore +type Extends = Static.Union<[ + Static.Tuple<[Static.Const<'extends'>, Type, Static.Const, Type, Static.Const, Type]>, + Static.Tuple<[]> +], ExtendsMapping> + +// ------------------------------------------------------------------ +// Base +// ------------------------------------------------------------------ +// prettier-ignore +interface BaseMapping extends Static.IMapping { + output: ( + this['input'] extends [LParen, infer Type extends Types.TSchema, RParen] ? Type : + this['input'] extends [infer Type extends Types.TSchema] ? Type : + never + ) +} +// prettier-ignore +type Base = Static.Union<[ + Static.Tuple<[ + Static.Const, + Type, + Static.Const + ]>, + Static.Tuple<[Static.Union<[ + Literal, + Keyword, + Object, + Tuple, + Function, + Constructor, + Mapped, + AsyncIterator, + Iterator, + ConstructorParameters, + FunctionParameters, + InstanceType, + ReturnType, + Awaited, + Array, + Record, + Promise, + Partial, + Required, + Pick, + Omit, + Exclude, + Extract, + Lowercase, + Uppercase, + Capitalize, + Uncapitalize, + Date, + Uint8Array, + Reference + ]>]> +], BaseMapping> +// ------------------------------------------------------------------ +// Factor +// ------------------------------------------------------------------ +// prettier-ignore +type FactorExtends = ( + Extends extends [infer Right extends Types.TSchema, infer True extends Types.TSchema, infer False extends Types.TSchema] + ? Types.TExtends + : Type +) +// prettier-ignore +type FactorIndexArray = ( + IndexArray extends [...infer Left extends unknown[], infer Right extends Types.TSchema[]] ? ( + Right extends [infer Indexer extends Types.TSchema] ? Types.TIndex, Types.TIndexPropertyKeys> : + Right extends [] ? Types.TArray> : + Types.TNever + ) : Type +) +// prettier-ignore +interface FactorMapping extends Static.IMapping { + output: this['input'] extends [infer KeyOf extends boolean, infer Type extends Types.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] + ? KeyOf extends true + ? FactorExtends>, Extends> + : FactorExtends, Extends> + : never +} +// prettier-ignore +type Factor = Static.Tuple<[ + KeyOf, Base, IndexArray, Extends +], FactorMapping> +// ------------------------------------------------------------------ +// Expr +// ------------------------------------------------------------------ +// prettier-ignore +type ExprBinaryReduce = ( + Rest extends [infer Operator extends unknown, infer Right extends Types.TSchema, infer Next extends unknown[]] ? ( + ExprBinaryReduce extends infer Schema extends Types.TSchema ? ( + Operator extends '&' ? ( + Schema extends Types.TIntersect + ? Types.TIntersect<[Left, ...Types]> + : Types.TIntersect<[Left, Schema]> + ) : + Operator extends '|' ? ( + Schema extends Types.TUnion + ? Types.TUnion<[Left, ...Types]> + : Types.TUnion<[Left, Schema]> + ) : never + ) : never + ) : Left +) +// prettier-ignore +interface ExprBinaryMapping extends Static.IMapping { + output: ( + this['input'] extends [infer Left extends Types.TSchema, infer Rest extends unknown[]] + ? ExprBinaryReduce + : [] + ) +} +// prettier-ignore +type ExprTermTail = Static.Union<[ + Static.Tuple<[Static.Const<'&'>, Factor, ExprTermTail]>, + Static.Tuple<[]> +]> +// prettier-ignore +type ExprTerm = Static.Tuple<[ + Factor, ExprTermTail +], ExprBinaryMapping> +// prettier-ignore +type ExprTail = Static.Union<[ + Static.Tuple<[Static.Const<'|'>, ExprTerm, ExprTail]>, + Static.Tuple<[]> +]> +// prettier-ignore +type Expr = Static.Tuple<[ + ExprTerm, ExprTail +], ExprBinaryMapping> +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +export type Type = Expr +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +interface PropertyKeyStringMapping extends Static.IMapping { + output: this['input'] +} +type PropertyKeyString = Static.String<[SingleQuote, DoubleQuote], PropertyKeyStringMapping> + +type PropertyKey = Static.Union<[Static.Ident, PropertyKeyString]> +// prettier-ignore +interface PropertyReadonlyMapping extends Static.IMapping { + output: this['input'] extends ['readonly'] ? true : false +} +type PropertyReadonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], PropertyReadonlyMapping> +// prettier-ignore +interface PropertyOptionalMapping extends Static.IMapping { + output: this['input'] extends [Question] ? true : false +} +type PropertyOptional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], PropertyOptionalMapping> +// prettier-ignore +interface PropertyMapping extends Static.IMapping { + output: this['input'] extends [infer Readonly extends boolean, infer Key extends string, infer Optional extends boolean, string, infer Type extends Types.TSchema] + ? { + [_ in Key]: ( + [Readonly, Optional] extends [true, true] ? Types.TReadonlyOptional : + [Readonly, Optional] extends [true, false] ? Types.TReadonly : + [Readonly, Optional] extends [false, true] ? Types.TOptional : + Type + ) + } : never +} + +type Property = Static.Tuple<[PropertyReadonly, PropertyKey, PropertyOptional, Static.Const, Type], PropertyMapping> + +type PropertiesEvaluate = { [K in keyof T]: T[K] } & {} +// prettier-ignore +type PropertyDelimiter = Static.Union<[ + Static.Tuple<[Static.Const, Static.Const]>, + Static.Tuple<[Static.Const, Static.Const]>, + Static.Tuple<[Static.Const]>, + Static.Tuple<[Static.Const]>, + Static.Tuple<[Static.Const]>, +]> +// prettier-ignore +type PropertiesReduce = ( + PropertiesArray extends [infer Left extends Types.TProperties, ...infer Right extends Types.TProperties[]] + ? PropertiesReduce> + : Result +) +// prettier-ignore +interface PropertiesMapping extends Static.IMapping { + output: this['input'] extends Types.TProperties[] ? PropertiesReduce : never +} +type Properties = Static.Union<[Delimit], PropertiesMapping> +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +interface ObjectMapping extends Static.IMapping { + output: this['input'] extends [unknown, infer Properties extends Types.TProperties, unknown] + ? Types.TObject + : never +} +// prettier-ignore +type Object = Static.Tuple<[ + Static.Const, Properties, Static.Const +], ObjectMapping> +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +type Elements = Delimit> +// prettier-ignore +interface TupleMapping extends Static.IMapping { + output: this['input'] extends [unknown, infer Elements extends Types.TSchema[], unknown] ? Types.TTuple : never +} +// prettier-ignore +type Tuple = Static.Tuple<[ + Static.Const, Elements, Static.Const +], TupleMapping> +// ------------------------------------------------------------------ +// Parameters +// ------------------------------------------------------------------ +interface ParameterMapping extends Static.IMapping { + output: this['input'] extends [string, Colon, infer Type extends Types.TSchema] ? Type : never +} +// prettier-ignore +type Parameter = Static.Tuple<[ + Static.Ident, Static.Const, Type +], ParameterMapping> + +type Parameters = Delimit> +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +interface FunctionMapping extends Static.IMapping { + output: this['input'] extends [LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer ReturnType extends Types.TSchema] + ? Types.TFunction + : never +} +// prettier-ignore +type Function = Static.Tuple<[ + Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type +], FunctionMapping> +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +interface ConstructorMapping extends Static.IMapping { + output: this['input'] extends ['new', LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer InstanceType extends Types.TSchema] + ? Types.TConstructor + : never +} +// prettier-ignore +type Constructor = Static.Tuple<[ + Static.Const<'new'>, Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type +], ConstructorMapping> +// ------------------------------------------------------------------ +// Mapped (requires deferred types) +// ------------------------------------------------------------------ +// prettier-ignore +interface MappedMapping extends Static.IMapping { + output: this['input'] extends [LBrace, LBracket, infer Key extends string, 'in', infer Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace] + ? Types.TLiteral<'Mapped types not supported'> + : this['input'] +} +// prettier-ignore +type Mapped = Static.Tuple<[ + Static.Const, Static.Const, Static.Ident, Static.Const<'in'>, Type, Static.Const, Static.Const, Type, Static.Const +], MappedMapping> +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +interface ArrayMapping extends Static.IMapping { + output: this['input'] extends ['Array', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TArray + : never +} +// prettier-ignore +type Array = Static.Tuple<[ + Static.Const<'Array'>, Static.Const, Type, Static.Const, +], ArrayMapping> +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +interface AsyncIteratorMapping extends Static.IMapping { + output: this['input'] extends ['AsyncIterator', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TAsyncIterator + : never +} +// prettier-ignore +type AsyncIterator = Static.Tuple<[ + Static.Const<'AsyncIterator'>, Static.Const, Type, Static.Const, +], AsyncIteratorMapping> +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +interface IteratorMapping extends Static.IMapping { + output: this['input'] extends ['Iterator', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TIterator + : never +} +// prettier-ignore +type Iterator = Static.Tuple<[ + Static.Const<'Iterator'>, Static.Const, Type, Static.Const, +], IteratorMapping> + +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +interface ConstructorParametersMapping extends Static.IMapping { + output: this['input'] extends ['ConstructorParameters', LAngle, infer Type extends Types.TConstructor, RAngle] + ? Types.TConstructorParameters + : never +} +// prettier-ignore +type ConstructorParameters = Static.Tuple<[ + Static.Const<'ConstructorParameters'>, Static.Const, Type, Static.Const, +], ConstructorParametersMapping> +// ------------------------------------------------------------------ +// FunctionParameters +// ------------------------------------------------------------------ +// prettier-ignore +interface FunctionParametersMapping extends Static.IMapping { + output: this['input'] extends ['Parameters', LAngle, infer Type extends Types.TFunction, RAngle] + ? Types.TParameters + : never +} +// prettier-ignore +type FunctionParameters = Static.Tuple<[ + Static.Const<'Parameters'>, Static.Const, Type, Static.Const, +], FunctionParametersMapping> +// ------------------------------------------------------------------ +// InstanceType +// ------------------------------------------------------------------ +// prettier-ignore +interface InstanceTypeMapping extends Static.IMapping { + output: this['input'] extends ['InstanceType', LAngle, infer Type extends Types.TConstructor, RAngle] + ? Types.TInstanceType + : never +} +// prettier-ignore +type InstanceType = Static.Tuple<[ + Static.Const<'InstanceType'>, Static.Const, Type, Static.Const, +], InstanceTypeMapping> +// ------------------------------------------------------------------ +// ReturnType +// ------------------------------------------------------------------ +// prettier-ignore +interface ReturnTypeMapping extends Static.IMapping { + output: this['input'] extends ['ReturnType', LAngle, infer Type extends Types.TFunction, RAngle] + ? Types.TReturnType + : never +} +// prettier-ignore +type ReturnType = Static.Tuple<[ + Static.Const<'ReturnType'>, Static.Const, Type, Static.Const, +], ReturnTypeMapping> +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +interface AwaitedMapping extends Static.IMapping { + output: this['input'] extends ['Awaited', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TAwaited + : never +} +// prettier-ignore +type Awaited = Static.Tuple<[ + Static.Const<'Awaited'>, Static.Const, Type, Static.Const, +], AwaitedMapping> +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +interface PromiseMapping extends Static.IMapping { + output: this['input'] extends ['Promise', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPromise + : never +} +// prettier-ignore +type Promise = Static.Tuple<[ + Static.Const<'Promise'>, Static.Const, Type, Static.Const, +], PromiseMapping> +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +interface RecordMapping extends Static.IMapping { + output: this['input'] extends ['Record', LAngle, infer Key extends Types.TSchema, Comma, infer Type extends Types.TSchema, RAngle] + ? Types.TRecord + : never +} +// prettier-ignore +type Record = Static.Tuple<[ + Static.Const<'Record'>, Static.Const, Type, Static.Const, Type, Static.Const, +], RecordMapping> +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +interface PartialMapping extends Static.IMapping { + output: this['input'] extends ['Partial', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPartial + : never +} +// prettier-ignore +type Partial = Static.Tuple<[ + Static.Const<'Partial'>, Static.Const, Type, Static.Const, +], PartialMapping> +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +// prettier-ignore +interface RequiredMapping extends Static.IMapping { + output: this['input'] extends ['Required', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TPartial + : never +} +// prettier-ignore +type Required = Static.Tuple<[ + Static.Const<'Required'>, Static.Const, Type, Static.Const, +], RequiredMapping> +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +interface PickMapping extends Static.IMapping { + output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TPick> + : never +} +// prettier-ignore +type Pick = Static.Tuple<[ + Static.Const<'Pick'>, Static.Const, Type, Static.Const, Type, Static.Const, +], PickMapping> +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +interface OmitMapping extends Static.IMapping { + output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TOmit> + : never +} +// prettier-ignore +type Omit = Static.Tuple<[ + Static.Const<'Omit'>, Static.Const, Type, Static.Const, Type, Static.Const +], OmitMapping> +// ------------------------------------------------------------------ +// Exclude +// ------------------------------------------------------------------ +// prettier-ignore +interface ExcludeMapping extends Static.IMapping { + output: this['input'] extends ['Exclude', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TExclude + : never +} +// prettier-ignore +type Exclude = Static.Tuple<[ + Static.Const<'Exclude'>, Static.Const, Type, Static.Const, Type, Static.Const +], ExcludeMapping> +// ------------------------------------------------------------------ +// Extract +// ------------------------------------------------------------------ +// prettier-ignore +interface ExtractMapping extends Static.IMapping { + output: this['input'] extends ['Extract', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] + ? Types.TExtract + : never +} +// prettier-ignore +type Extract = Static.Tuple<[ + Static.Const<'Extract'>, Static.Const, Type, Static.Const, Type, Static.Const +], ExtractMapping> +// ------------------------------------------------------------------ +// Uppercase +// ------------------------------------------------------------------ +// prettier-ignore +interface UppercaseMapping extends Static.IMapping { + output: this['input'] extends ['Uppercase', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TUppercase + : never +} +// prettier-ignore +type Uppercase = Static.Tuple<[ + Static.Const<'Uppercase'>, Static.Const, Type, Static.Const, +], UppercaseMapping> +// ------------------------------------------------------------------ +// Lowercase +// ------------------------------------------------------------------ +// prettier-ignore +interface LowercaseMapping extends Static.IMapping { + output: this['input'] extends ['Lowercase', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TLowercase + : never +} +// prettier-ignore +type Lowercase = Static.Tuple<[ + Static.Const<'Lowercase'>, Static.Const, Type, Static.Const, +], LowercaseMapping> +// ------------------------------------------------------------------ +// Capitalize +// ------------------------------------------------------------------ +// prettier-ignore +interface CapitalizeMapping extends Static.IMapping { + output: this['input'] extends ['Capitalize', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TCapitalize + : never +} +// prettier-ignore +type Capitalize = Static.Tuple<[ + Static.Const<'Capitalize'>, Static.Const, Type, Static.Const, +], CapitalizeMapping> +// ------------------------------------------------------------------ +// Uncapitalize +// ------------------------------------------------------------------ +// prettier-ignore +interface UncapitalizeMapping extends Static.IMapping { + output: this['input'] extends ['Uncapitalize', LAngle, infer Type extends Types.TSchema, RAngle] + ? Types.TUncapitalize + : never +} +// prettier-ignore +type Uncapitalize = Static.Tuple<[ + Static.Const<'Uncapitalize'>, Static.Const, Type, Static.Const, +], UncapitalizeMapping> +// ------------------------------------------------------------------ +// Date +// ------------------------------------------------------------------ +type Date = Static.Const<'Date', Static.As> +// ------------------------------------------------------------------ +// Uint8Array +// ------------------------------------------------------------------ +type Uint8Array = Static.Const<'Uint8Array', Static.As> diff --git a/task/benchmark/compression/module/typebox-parse.ts b/task/benchmark/compression/module/typebox-parse.ts new file mode 100644 index 000000000..7ba2bf33a --- /dev/null +++ b/task/benchmark/compression/module/typebox-parse.ts @@ -0,0 +1,3 @@ +import * as Parse from '@sinclair/typebox/parse' + +console.log(Parse) diff --git a/task/build/package/build.ts b/task/build/package/build.ts index e7975ed73..a7fc92f33 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -32,7 +32,7 @@ import { createPackageJson } from './create-package-json' /** Builds package.json and redirect directories */ export async function build(target: string) { console.log('building...package.json') - const submodules = ['compiler', 'errors', 'system', 'type', 'value'] + const submodules = ['compiler', 'errors', 'parse', 'system', 'type', 'value'] await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 15ca57aca..56b6a3434 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -8,6 +8,7 @@ TypeSystemPolicy.InstanceMode = 'freeze' import './compiler/index' import './compiler-ajv/index' import './errors/index' +import './parse/index' import './system/index' import './type/index' import './value/index' diff --git a/test/runtime/parse/index.ts b/test/runtime/parse/index.ts new file mode 100644 index 000000000..09cc87100 --- /dev/null +++ b/test/runtime/parse/index.ts @@ -0,0 +1 @@ +import './parse' diff --git a/test/runtime/parse/parse.ts b/test/runtime/parse/parse.ts new file mode 100644 index 000000000..1ac85df70 --- /dev/null +++ b/test/runtime/parse/parse.ts @@ -0,0 +1,389 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type, Parse } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +// prettier-ignore +describe('parse/Parse', () => { + it('Should parse Any', () => { + const T = Parse(`any`) + Assert.IsTrue(TypeGuard.IsAny(T)) + }) + it('Should parse Array 1', () => { + const T = Parse(`number[]`) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse Array 2', () => { + const T = Parse(`Array`) + Assert.IsTrue(TypeGuard.IsArray(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse AsyncIterator', () => { + const T = Parse(`AsyncIterator`) + Assert.IsTrue(TypeGuard.IsAsyncIterator(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse Awaited', () => { + const T = Parse(`Awaited>`) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should parse BigInt', () => { + const T = Parse(`bigint`) + Assert.IsTrue(TypeGuard.IsBigInt(T)) + }) + it('Should parse Boolean', () => { + const T = Parse(`boolean`) + Assert.IsTrue(TypeGuard.IsBoolean(T)) + }) + it('Should parse ConstructorParameters', () => { + const T = Parse(`ConstructorParameters boolean>`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + it('Should parse Constructor', () => { + const T = Parse(`new (a: number, b: string) => boolean`) + Assert.IsTrue(TypeGuard.IsConstructor(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) + Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) + }) + it('Should parse Date', () => { + const T = Parse(`Date`) + Assert.IsTrue(TypeGuard.IsDate(T)) + }) + it('Should parse Exclude', () => { + const T = Parse(`Exclude<1 | 2 | 3, 1>`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 2) + Assert.IsTrue(T.anyOf[1].const === 3) + }) + it('Should parse Extract', () => { + const T = Parse(`Extract<1 | 2 | 3, 1 | 2>`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + // @ts-ignore fix: incorrect union order (result of UnionToTuple, replace with Tuple destructuring) + Assert.IsTrue(T.anyOf[0].const === 1) + // @ts-ignore fix: incorrect union order (result of UnionToTuple, replace with Tuple destructuring) + Assert.IsTrue(T.anyOf[1].const === 2) + }) + it('Should parse Function', () => { + const T = Parse(`(a: number, b: string) => boolean`) + Assert.IsTrue(TypeGuard.IsFunction(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) + Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) + Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) + }) + it('Should parse Indexed 1', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }['x']`) + Assert.IsTrue(T.const === 1) + }) + it('Should parse Indexed 2', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }['x' | 'y' | 'z']`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + Assert.IsTrue(T.anyOf[2].const === 3) + }) + it('Should parse Indexed 3', () => { + const T = Parse(`{ x: 1, y: 2, z: 3 }`) + const S = Parse({ T }, `T[keyof T]`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 1) + Assert.IsTrue(S.anyOf[1].const === 2) + Assert.IsTrue(S.anyOf[2].const === 3) + }) + it('Should parse Indexed 4', () => { + const T = Parse(`['A', 'B', 'C']`) + const S = Parse({ T }, `T[number]`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 'A') + Assert.IsTrue(S.anyOf[1].const === 'B') + Assert.IsTrue(S.anyOf[2].const === 'C') + }) + it('Should parse Integer', () => { + const T = Parse(`integer`) + Assert.IsTrue(TypeGuard.IsInteger(T)) + }) + it('Should parse Intersect 1', () => { + const T = Parse(`1 & 2`) + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(T.allOf[0].const === 1) + Assert.IsTrue(T.allOf[1].const === 2) + }) + it('Should parse Intersect 2', () => { + const T = Parse(`1 & (2 & 3)`) // expect flatten + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(T.allOf[0].const === 1) + Assert.IsTrue(T.allOf[1].const === 2) + Assert.IsTrue(T.allOf[2].const === 3) + }) + it('Should parse Intersect 3', () => { + const T = Parse(`(1 | 2) & 3`) // operator precedence + Assert.IsTrue(TypeGuard.IsIntersect(T)) + Assert.IsTrue(TypeGuard.IsUnion(T.allOf[0])) + Assert.IsTrue(T.allOf[1].const === 3) + }) + it('Should parse InstanceType 1', () => { + const T = Parse(`InstanceType { x: 1, y: 2 }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(T.properties.x.const === 1) + Assert.IsTrue(T.properties.y.const === 2) + }) + it('Should parse InstanceType 2', () => { + const T = Parse(`InstanceType`) // generalization issue + Assert.IsTrue(T === undefined) + }) + it('Should parse Iterator', () => { + const T = Parse(`Iterator`) + Assert.IsTrue(TypeGuard.IsIterator(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items)) + }) + it('Should parse KeyOf 1', () => { + const T = Parse(`keyof { x: 1, y: 2 }`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 'x') + Assert.IsTrue(T.anyOf[1].const === 'y') + }) + it('Should parse KeyOf 2', () => { + const T = Parse(`keyof [0, 1]`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === '0') + Assert.IsTrue(T.anyOf[1].const === '1') + }) + it('Should parse KeyOf 3', () => { + const T = Parse(`{ x: 1, y: 2 }`) + const S = Parse({ T }, `keyof T`) + Assert.IsTrue(TypeGuard.IsUnion(S)) + Assert.IsTrue(S.anyOf[0].const === 'x') + Assert.IsTrue(S.anyOf[1].const === 'y') + }) + it('Should parse Literal Boolean 1', () => { + const T = Parse(`true`) + Assert.IsTrue(T.const === true) + }) + it('Should parse Literal Boolean 2', () => { + const T = Parse(`false`) + Assert.IsTrue(T.const === false) + }) + it('Should parse Literal Number', () => { + const T = Parse(`1`) + Assert.IsTrue(T.const === 1) + }) + it('Should parse Literal String', () => { + const T = Parse(`'1'`) + Assert.IsTrue(T.const === '1') + }) + it('Should parse Mapped (Pending)', () => { + const T = Parse(`{ [K in 1 | 2 | 3]: K }`) + Assert.IsTrue(T.const === 'Mapped types not supported') + }) + it('Should parse Never', () => { + const T = Parse(`never`) + Assert.IsTrue(TypeGuard.IsNever(T)) + }) + it('Should parse Null', () => { + const T = Parse(`null`) + Assert.IsTrue(TypeGuard.IsNull(T)) + }) + it('Should parse Number', () => { + const T = Parse(`number`) + Assert.IsTrue(TypeGuard.IsNumber(T)) + }) + it('Should parse Object 1', () => { + const T = Parse(`{x: boolean, y: number, z: string, }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 2', () => { + const T = Parse(`{x: boolean; y: number; z: string; }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 3', () => { + const T = Parse(`{ + x: boolean + y: number + z: string + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 4', () => { + const T = Parse(`{ + x: boolean; + y: number; + z: string; + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Object 5', () => { + const T = Parse(`{ + x: boolean, + y: number, + z: string, + }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsString(T.properties.z)) + }) + it('Should parse Omit 1', () => { + const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Omit 2', () => { + const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z' | 'y'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(('y' in T.properties) === false) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Parameters', () => { + const T = Parse(`Parameters<(a: number, b: string) => boolean>`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + it('Should parse Partial', () => { + const T = Parse(`Partial<{ x: boolean, y: number, z: string }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(('required' in T) === false) + }) + it('Should parse Pick 1', () => { + const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x' | 'y'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Pick 2', () => { + const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x'>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(('y' in T.properties) === false) + Assert.IsTrue(('z' in T.properties) === false) + }) + it('Should parse Promise', () => { + const T = Parse(`Promise`) + Assert.IsTrue(TypeGuard.IsPromise(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.item)) + }) + it('Should parse ReadonlyOptional', () => { + const T = Parse(`{ readonly x?: boolean, readonly y?: number }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) + Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) + }) + it('Should parse Readonly', () => { + const T = Parse(`{ readonly x: boolean, readonly y: number }`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) + }) + it('Should parse Record 1', () => { + const T = Parse(`Record`) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(.*)$'])) + }) + it('Should parse Record 2', () => { + const T = Parse(`Record`) + Assert.IsTrue(TypeGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(0|[1-9][0-9]*)$'])) + }) + it('Should parse Record 3', () => { + const T = Parse(`Record<'x' | 'y', number>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) + }) + it('Should parse Recursive', () => { + const T = Type.Recursive(This => Parse({ This }, `{ id: string, nodes: This[] }`)) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(TypeGuard.IsString(T.properties.id)) + Assert.IsTrue(TypeGuard.IsArray(T.properties.nodes)) + Assert.IsTrue(TypeGuard.IsThis(T.properties.nodes.items)) + }) + it('Should parse Ref', () => { + const T = Parse('foo') + Assert.IsTrue(TypeGuard.IsRef(T)) + Assert.IsTrue(T.$ref === 'foo') + }) + it('Should parse Required', () => { + const T = Parse(`Required<{ x?: boolean, y?: number, z?: string }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsEqual(T.required, ['x', 'y', 'z']) + }) + it('Should parse ReturnType 1', () => { + const T = Parse(`ReturnType<() => { x: 1, y: 2 }>`) + Assert.IsTrue(TypeGuard.IsObject(T)) + Assert.IsTrue(T.properties.x.const === 1) + Assert.IsTrue(T.properties.y.const === 2) + }) + it('Should parse ReturnType 2', () => { + const T = Parse(`ReturnType`) // generalization issue + Assert.IsTrue(T === undefined) + }) + it('Should parse String', () => { + const T = Parse(`string`) + Assert.IsTrue(TypeGuard.IsString(T)) + }) + it('Should parse Symbol', () => { + const T = Parse(`symbol`) + Assert.IsTrue(TypeGuard.IsSymbol(T)) + }) + it('Should parse Tuple', () => { + const T = Parse(`[0, 1, 2, 3]`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(T.items![0].const === 0) + Assert.IsTrue(T.items![1].const === 1) + Assert.IsTrue(T.items![2].const === 2) + Assert.IsTrue(T.items![3].const === 3) + }) + it('Should parse Uint8Array', () => { + const T = Parse(`Uint8Array`) + Assert.IsTrue(TypeGuard.IsUint8Array(T)) + }) + it('Should parse Undefined', () => { + const T = Parse(`undefined`) + Assert.IsTrue(TypeGuard.IsUndefined(T)) + }) + it('Should parse Union 1', () => { + const T = Parse(`1 | 2`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + }) + it('Should parse Union 2', () => { + const T = Parse(`1 | (2 | 3)`) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(T.anyOf[0].const === 1) + Assert.IsTrue(T.anyOf[1].const === 2) + Assert.IsTrue(T.anyOf[2].const === 3) + }) + it('Should parse Unknown', () => { + const T = Parse(`unknown`) + Assert.IsTrue(TypeGuard.IsUnknown(T)) + }) + it('Should parse Void', () => { + const T = Parse(`void`) + Assert.IsTrue(TypeGuard.IsVoid(T)) + }) +}) diff --git a/tsconfig.json b/tsconfig.json index 7ba7f03ed..811eedcdd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "paths": { "@sinclair/typebox/compiler": ["src/compiler/index.ts"], "@sinclair/typebox/errors": ["src/errors/index.ts"], + "@sinclair/typebox/parse": ["src/parse/index.ts"], "@sinclair/typebox/system": ["src/system/index.ts"], "@sinclair/typebox/type": ["src/type/index.ts"], "@sinclair/typebox/value": ["src/value/index.ts"], From 52ceab98c59543704b07e40607f10a076ec86bea Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 7 Nov 2024 22:09:57 +0900 Subject: [PATCH 297/369] Revision 0.33.21 (#1064) * HotFix: 0.33.20 - Object Shadowing on Runtime * ChangeLog --- changelog/0.33.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/parse/runtime.ts | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index f349e7495..f42f59f27 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.21](https://github.com/sinclairzx81/typebox/pull/1064) + - [1063](https://github.com/sinclairzx81/typebox/issues/1063) Hotfix to resolve variable shadowing on Object (Parser Runtime) - [Revision 0.33.20](https://github.com/sinclairzx81/typebox/pull/1062) - Add TypeScript Parsing Infrastructure. Add Parse API to top level import. - [Revision 0.33.19](https://github.com/sinclairzx81/typebox/pull/1061) diff --git a/package-lock.json b/package-lock.json index fae9cd780..f9c7d9407 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.20", + "version": "0.33.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.20", + "version": "0.33.21", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e31bce433..4dbbc3ef6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.20", + "version": "0.33.21", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/parse/runtime.ts b/src/parse/runtime.ts index de018ed65..933f93894 100644 --- a/src/parse/runtime.ts +++ b/src/parse/runtime.ts @@ -322,7 +322,7 @@ const ObjectMapping = (values: Record[]) => Types.Object( return { ...properties, ...record } }, {} as Types.TProperties)) // prettier-ignore -const Object = Runtime.Tuple([ +const _Object = Runtime.Tuple([ Runtime.Const(LBrace), Runtime.Ref[]>('Properties'), Runtime.Const(RBrace) @@ -644,7 +644,7 @@ export const Module = new Runtime.Module({ Property, PropertyDelimiter, Properties, - Object, + Object: _Object, Elements, Tuple, Parameter, From 6cf8596c3a0d456c3718798bb601f21f799de63c Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Sat, 9 Nov 2024 23:09:10 +0900 Subject: [PATCH 298/369] Revision 0.33.22 (#1065) * Remove Parse from Top Level Import * Rename Parse submodule to Syntax * Export Parse Inference Types * ChangeLog * Version --- changelog/0.33.0.md | 2 ++ example/index.ts | 5 ++-- package-lock.json | 4 +-- package.json | 2 +- src/index.ts | 5 +--- src/{parse => syntax}/index.ts | 2 +- src/{parse => syntax}/parse.ts | 27 ++++++++++++------- src/{parse => syntax}/parsebox/index.ts | 0 .../parsebox/runtime/guard.ts | 0 .../parsebox/runtime/index.ts | 0 .../parsebox/runtime/module.ts | 0 .../parsebox/runtime/parse.ts | 0 .../parsebox/runtime/token.ts | 0 .../parsebox/runtime/types.ts | 0 .../parsebox/static/index.ts | 0 .../parsebox/static/parse.ts | 0 .../parsebox/static/token.ts | 0 .../parsebox/static/types.ts | 0 src/{parse => syntax}/runtime.ts | 2 +- src/{parse => syntax}/static.ts | 2 +- src/tsconfig.json | 2 +- task/build/package/build.ts | 2 +- test/runtime/index.ts | 2 +- test/runtime/parse/index.ts | 1 - test/runtime/syntax/index.ts | 1 + .../{parse/parse.ts => syntax/syntax.ts} | 3 ++- tsconfig.json | 2 +- 27 files changed, 36 insertions(+), 28 deletions(-) rename src/{parse => syntax}/index.ts (98%) rename src/{parse => syntax}/parse.ts (59%) rename src/{parse => syntax}/parsebox/index.ts (100%) rename src/{parse => syntax}/parsebox/runtime/guard.ts (100%) rename src/{parse => syntax}/parsebox/runtime/index.ts (100%) rename src/{parse => syntax}/parsebox/runtime/module.ts (100%) rename src/{parse => syntax}/parsebox/runtime/parse.ts (100%) rename src/{parse => syntax}/parsebox/runtime/token.ts (100%) rename src/{parse => syntax}/parsebox/runtime/types.ts (100%) rename src/{parse => syntax}/parsebox/static/index.ts (100%) rename src/{parse => syntax}/parsebox/static/parse.ts (100%) rename src/{parse => syntax}/parsebox/static/token.ts (100%) rename src/{parse => syntax}/parsebox/static/types.ts (100%) rename src/{parse => syntax}/runtime.ts (99%) rename src/{parse => syntax}/static.ts (99%) delete mode 100644 test/runtime/parse/index.ts create mode 100644 test/runtime/syntax/index.ts rename test/runtime/{parse/parse.ts => syntax/syntax.ts} (99%) diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index f42f59f27..31140defd 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,6 @@ ### 0.33.0 +- [Revision 0.33.22](https://github.com/sinclairzx81/typebox/pull/1065) + - Rename TypeScript parsing infrastructure from `/parse` to `/syntax`. Remove Parse API from top level import. - [Revision 0.33.21](https://github.com/sinclairzx81/typebox/pull/1064) - [1063](https://github.com/sinclairzx81/typebox/issues/1063) Hotfix to resolve variable shadowing on Object (Parser Runtime) - [Revision 0.33.20](https://github.com/sinclairzx81/typebox/pull/1062) diff --git a/example/index.ts b/example/index.ts index 711d7d03e..9d228684a 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,7 +1,8 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Type, Parse, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' +import { Parse, StaticParseAsType } from '@sinclair/typebox/syntax' +import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' // ----------------------------------------------------------- // Create: Type @@ -21,7 +22,7 @@ console.log(T) // Parse: Type // ----------------------------------------------------------- -const S = Parse({ T }, `Partial`) +const S = Parse({ T }, `{ x: T, y: T, z: T }`) type S = Static diff --git a/package-lock.json b/package-lock.json index f9c7d9407..262945a64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.21", + "version": "0.33.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.21", + "version": "0.33.22", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 4dbbc3ef6..1a8dff98f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.21", + "version": "0.33.22", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/index.ts b/src/index.ts index 9f0429d85..6a99799a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,10 +38,7 @@ export * from './type/patterns/index' export * from './type/registry/index' export * from './type/sets/index' export * from './type/symbols/index' -// ------------------------------------------------------------------ -// Parse -// ------------------------------------------------------------------ -export * from './parse/index' + // ------------------------------------------------------------------ // Types // ------------------------------------------------------------------ diff --git a/src/parse/index.ts b/src/syntax/index.ts similarity index 98% rename from src/parse/index.ts rename to src/syntax/index.ts index d0a1a960c..ae2cee724 100644 --- a/src/parse/index.ts +++ b/src/syntax/index.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox +@sinclair/typebox/syntax The MIT License (MIT) diff --git a/src/parse/parse.ts b/src/syntax/parse.ts similarity index 59% rename from src/parse/parse.ts rename to src/syntax/parse.ts index ef9928f2e..4605b9cbe 100644 --- a/src/parse/parse.ts +++ b/src/syntax/parse.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox +@sinclair/typebox/syntax The MIT License (MIT) @@ -29,23 +29,30 @@ THE SOFTWARE. import { Static } from './parsebox/index' import { CreateType } from '../type/create/type' import { TSchema, SchemaOptions } from '../type/schema/index' +import { StaticDecode } from '../type/static/index' import { Module } from './runtime' import { Type } from './static' -/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ -export function Parse = {}>(context: Context, code: Code, options?: SchemaOptions): Static.Parse[0] -/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ -export function Parse(code: Code, options?: SchemaOptions): Static.Parse[0] -/** `[Experimental]` Parses a TypeScript type annotation as an inferred TypeBox type */ +/** `[Experimental]` Infers a TypeBox type from TypeScript syntax. */ +export type StaticParseAsSchema, Code extends string> = Static.Parse[0] + +/** `[Experimental]` Infers a TypeScript type from TypeScript syntax. */ +export type StaticParseAsType, Code extends string> = StaticParseAsSchema extends infer Type extends TSchema ? StaticDecode : undefined + +/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ +export function Parse, Code extends string>(context: Context, code: Code, options?: SchemaOptions): StaticParseAsSchema +/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ +export function Parse(code: Code, options?: SchemaOptions): StaticParseAsSchema<{}, Code> +/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ export function Parse(...args: any[]): never { return ParseOnly.apply(null, args as never) as never } -/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ -export function ParseOnly = {}>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined -/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ +export function ParseOnly, Code extends string>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined +/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax */ export function ParseOnly(code: Code, options?: SchemaOptions): TSchema | undefined -/** `[Experimental]` Parses a TypeScript type annotation as TSchema */ +/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ export function ParseOnly(...args: any[]): TSchema | undefined { const withContext = typeof args[0] === 'string' ? false : true const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] diff --git a/src/parse/parsebox/index.ts b/src/syntax/parsebox/index.ts similarity index 100% rename from src/parse/parsebox/index.ts rename to src/syntax/parsebox/index.ts diff --git a/src/parse/parsebox/runtime/guard.ts b/src/syntax/parsebox/runtime/guard.ts similarity index 100% rename from src/parse/parsebox/runtime/guard.ts rename to src/syntax/parsebox/runtime/guard.ts diff --git a/src/parse/parsebox/runtime/index.ts b/src/syntax/parsebox/runtime/index.ts similarity index 100% rename from src/parse/parsebox/runtime/index.ts rename to src/syntax/parsebox/runtime/index.ts diff --git a/src/parse/parsebox/runtime/module.ts b/src/syntax/parsebox/runtime/module.ts similarity index 100% rename from src/parse/parsebox/runtime/module.ts rename to src/syntax/parsebox/runtime/module.ts diff --git a/src/parse/parsebox/runtime/parse.ts b/src/syntax/parsebox/runtime/parse.ts similarity index 100% rename from src/parse/parsebox/runtime/parse.ts rename to src/syntax/parsebox/runtime/parse.ts diff --git a/src/parse/parsebox/runtime/token.ts b/src/syntax/parsebox/runtime/token.ts similarity index 100% rename from src/parse/parsebox/runtime/token.ts rename to src/syntax/parsebox/runtime/token.ts diff --git a/src/parse/parsebox/runtime/types.ts b/src/syntax/parsebox/runtime/types.ts similarity index 100% rename from src/parse/parsebox/runtime/types.ts rename to src/syntax/parsebox/runtime/types.ts diff --git a/src/parse/parsebox/static/index.ts b/src/syntax/parsebox/static/index.ts similarity index 100% rename from src/parse/parsebox/static/index.ts rename to src/syntax/parsebox/static/index.ts diff --git a/src/parse/parsebox/static/parse.ts b/src/syntax/parsebox/static/parse.ts similarity index 100% rename from src/parse/parsebox/static/parse.ts rename to src/syntax/parsebox/static/parse.ts diff --git a/src/parse/parsebox/static/token.ts b/src/syntax/parsebox/static/token.ts similarity index 100% rename from src/parse/parsebox/static/token.ts rename to src/syntax/parsebox/static/token.ts diff --git a/src/parse/parsebox/static/types.ts b/src/syntax/parsebox/static/types.ts similarity index 100% rename from src/parse/parsebox/static/types.ts rename to src/syntax/parsebox/static/types.ts diff --git a/src/parse/runtime.ts b/src/syntax/runtime.ts similarity index 99% rename from src/parse/runtime.ts rename to src/syntax/runtime.ts index 933f93894..0d28dbbf2 100644 --- a/src/parse/runtime.ts +++ b/src/syntax/runtime.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox +@sinclair/typebox/syntax The MIT License (MIT) diff --git a/src/parse/static.ts b/src/syntax/static.ts similarity index 99% rename from src/parse/static.ts rename to src/syntax/static.ts index 90bec8e33..ff75dca2e 100644 --- a/src/parse/static.ts +++ b/src/syntax/static.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox +@sinclair/typebox/syntax The MIT License (MIT) diff --git a/src/tsconfig.json b/src/tsconfig.json index e53e3ebf1..89a62a897 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] + "files": ["compiler/index.ts", "errors/index.ts", "syntax/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] } diff --git a/task/build/package/build.ts b/task/build/package/build.ts index a7fc92f33..0e05ede91 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -32,7 +32,7 @@ import { createPackageJson } from './create-package-json' /** Builds package.json and redirect directories */ export async function build(target: string) { console.log('building...package.json') - const submodules = ['compiler', 'errors', 'parse', 'system', 'type', 'value'] + const submodules = ['compiler', 'errors', 'syntax', 'system', 'type', 'value'] await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) } diff --git a/test/runtime/index.ts b/test/runtime/index.ts index 56b6a3434..ec19724e0 100644 --- a/test/runtime/index.ts +++ b/test/runtime/index.ts @@ -8,7 +8,7 @@ TypeSystemPolicy.InstanceMode = 'freeze' import './compiler/index' import './compiler-ajv/index' import './errors/index' -import './parse/index' +import './syntax/index' import './system/index' import './type/index' import './value/index' diff --git a/test/runtime/parse/index.ts b/test/runtime/parse/index.ts deleted file mode 100644 index 09cc87100..000000000 --- a/test/runtime/parse/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './parse' diff --git a/test/runtime/syntax/index.ts b/test/runtime/syntax/index.ts new file mode 100644 index 000000000..8d81ae4fb --- /dev/null +++ b/test/runtime/syntax/index.ts @@ -0,0 +1 @@ +import './syntax' diff --git a/test/runtime/parse/parse.ts b/test/runtime/syntax/syntax.ts similarity index 99% rename from test/runtime/parse/parse.ts rename to test/runtime/syntax/syntax.ts index 1ac85df70..11e28298d 100644 --- a/test/runtime/parse/parse.ts +++ b/test/runtime/syntax/syntax.ts @@ -1,5 +1,6 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type, Parse } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Parse } from '@sinclair/typebox/syntax' import { Assert } from '../assert/index' // prettier-ignore diff --git a/tsconfig.json b/tsconfig.json index 811eedcdd..34a4b8f21 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "paths": { "@sinclair/typebox/compiler": ["src/compiler/index.ts"], "@sinclair/typebox/errors": ["src/errors/index.ts"], - "@sinclair/typebox/parse": ["src/parse/index.ts"], + "@sinclair/typebox/syntax": ["src/syntax/index.ts"], "@sinclair/typebox/system": ["src/system/index.ts"], "@sinclair/typebox/type": ["src/type/index.ts"], "@sinclair/typebox/value": ["src/value/index.ts"], From 46b09f7e83a5e8ced61d910e8635df3838a73657 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 10 Nov 2024 19:05:17 +0900 Subject: [PATCH 299/369] Documentation --- readme.md | 4 ++-- task/benchmark/compression/module/typebox-parse.ts | 3 --- task/benchmark/compression/module/typebox-syntax.ts | 3 +++ 3 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 task/benchmark/compression/module/typebox-parse.ts create mode 100644 task/benchmark/compression/module/typebox-syntax.ts diff --git a/readme.md b/readme.md index e62b15785..7cfcad304 100644 --- a/readme.md +++ b/readme.md @@ -1762,10 +1762,10 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ├──────────────────────┼────────────┼────────────┼─────────────┤ │ typebox/compiler │ '119.8 kb' │ ' 52.6 kb' │ '2.28 x' │ │ typebox/errors │ ' 74.4 kb' │ ' 33.1 kb' │ '2.25 x' │ -│ typebox/parse │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │ +│ typebox/syntax │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │ │ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ │ typebox/value │ '157.2 kb' │ ' 66.1 kb' │ '2.38 x' │ -│ typebox │ '127.3 kb' │ ' 53.3 kb' │ '2.39 x' │ +│ typebox │ ' 98.9 kb' │ ' 41.2 kb' │ '2.40 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/task/benchmark/compression/module/typebox-parse.ts b/task/benchmark/compression/module/typebox-parse.ts deleted file mode 100644 index 7ba2bf33a..000000000 --- a/task/benchmark/compression/module/typebox-parse.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as Parse from '@sinclair/typebox/parse' - -console.log(Parse) diff --git a/task/benchmark/compression/module/typebox-syntax.ts b/task/benchmark/compression/module/typebox-syntax.ts new file mode 100644 index 000000000..311871c68 --- /dev/null +++ b/task/benchmark/compression/module/typebox-syntax.ts @@ -0,0 +1,3 @@ +import * as Syntax from '@sinclair/typebox/syntax' + +console.log(Syntax) From ec510b8a3e6238bfd461fd62533a85565815ab81 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Mon, 11 Nov 2024 14:00:45 +0900 Subject: [PATCH 300/369] Documentation (#1066) --- readme.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/readme.md b/readme.md index 7cfcad304..ea74942f9 100644 --- a/readme.md +++ b/readme.md @@ -77,6 +77,11 @@ License MIT - [Transform](#types-transform) - [Guard](#types-guard) - [Unsafe](#types-unsafe) +- [Syntax](#syntax) + - [Parse](#syntax-parse) + - [Param](#syntax-param) + - [Static](#syntax-static) + - [Limits](#syntax-limits) - [Values](#values) - [Assert](#values-assert) - [Create](#values-create) @@ -1077,6 +1082,148 @@ if(TypeGuard.IsString(T)) { } ``` + + +## Syntax + +TypeBox supports parsing TypeScript type annotation syntax directly into TypeBox types, providing an alternative to the Type.* API for type construction. This feature functions at runtime and within the TypeScript type system, enabling types encoded as strings to be inferred as equivalent TypeBox types. + +TypeScript syntax parsing is provided as an optional import. + +```typescript +import { Parse } from '@sinclair/typebox/syntax' +``` + + + +### Parse + +The Parse function can be used to parse TypeScript code into TypeBox types. The left hand result will be a inferred TypeBox type of TSchema. Invalid syntax will result in an undefined return value. + +```typescript +const A = Parse('string') // const A: TString + +const B = Parse('[1, 2, 3]') // const B: TTuple<[ + // TLiteral<1>, + // TLiteral<2>, + // TLiteral<3> + // ]> + +const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ + // x: TNumber + // y: TNumber + // }> +``` + + + +### Param + +The Parse function accepts an optional context parameter that enables external types to be referenced within the syntax. This can be helpful for reusing types in different contexts. + +```typescript +const T = Type.Object({ // could be written as: Parse(`{ + x: Type.Number(), // x: number, + y: Type.Number(), // y: number, + z: Type.Number() // z: number +}) // }`) + +const A = Parse({ T }, `Partial`) // const A: TObject<{ + // x: TOptional, + // y: TOptional, + // z: TOptional + // }> + +const B = Parse({ T }, `keyof T`) // const B: TUnion<[ + // TLiteral<'x'>, + // TLiteral<'y'>, + // TLiteral<'z'> + // ]> + +const C = Parse({ T }, `T & { w: number }`) // const C: TObject<{ + // x: TNumber, + // y: TNumber, + // z: TNumber, + // w: TNumber + // }> + + +``` + + + +### Static + +TypeBox provides two Static types for inferring TypeScript syntax directly from strings. + +```typescript +import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' + +// Will infer as a TSchema + +type S = StaticParseAsSchema<`{ x: number }`> // type S: TObject<{ + // x: TNumber + // }> + +// Will infer as a type + +type T = StaticParseAsType<`{ x: number }`> // type T = { + // x: number + // +``` + + + + +### Limits + +The Parse function works by TypeBox parsing TypeScript syntax within the type system itself. This can place some additional demand on the TypeScript compiler, which may affect language service (LSP) responsiveness when working with especially large or complex types. In particular, very wide structures or deeply nested types may approach TypeScript’s instantiation limits. + +```typescript +// Excessively wide structures will result in instantiation limits exceeding +const A = Parse(`[ + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, +]`) + +// Excessively nested structures will result in instantiation limits exceeding +const B = Parse(`{ + x: { + y: { + z: { + w: 1 <-- Type instantiation is excessively deep and possibly infinite. + } + } + } +}`) +``` + +For parsing especially complex types, TypeBox provides the ParseOnly function, which parses strings at runtime and returns a non-inferred TSchema type. ParseOnly allows for arbitrarily complex types without reaching instantiation limits, making it useful for cases where TypeScript types need to be directly converted to Json Schema. + +```typescript +import { ParseOnly } from '@sinclair/typebox/syntax' + +// ok: but where A is TSchema | undefined + +const A = ParseOnly(`{ + x: { + y: { + z: { + w: 1 + } + } + } +}`) +``` + +Optimizing TypeScript string parsing performance is an ongoing area of research. For more information, see the [ParseBox](https://github.com/sinclairzx81/parsebox) project, which documents TypeBox’s parsing infrastructure and focuses efforts to improve parsing performance. + ## Values From 4833af59bc77bc13830ee59fd97eddd8c097af04 Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 11 Nov 2024 16:16:01 +0900 Subject: [PATCH 301/369] Documentation --- readme.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/readme.md b/readme.md index ea74942f9..5836c10a8 100644 --- a/readme.md +++ b/readme.md @@ -79,7 +79,7 @@ License MIT - [Unsafe](#types-unsafe) - [Syntax](#syntax) - [Parse](#syntax-parse) - - [Param](#syntax-param) + - [Context](#syntax-context) - [Static](#syntax-static) - [Limits](#syntax-limits) - [Values](#values) @@ -1100,6 +1100,8 @@ import { Parse } from '@sinclair/typebox/syntax' The Parse function can be used to parse TypeScript code into TypeBox types. The left hand result will be a inferred TypeBox type of TSchema. Invalid syntax will result in an undefined return value. +[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChJdCDQzwAgnAC8KdNgAUZBVFoBzMgEo4ps+YuXLrVnFnylALjgAVAMow9NfdPsK4AEKq6phY2gDaAIwANHAATLEAzAC6xlbpGTZ2cv4Bzi4uAK5gDFgAPOGSGdU1tdVZpi4AMsAwWFCoDGWRAHzRVXWDQ5m2jS1tHV1xfQPDc3MNruPtnWWJPbPzW7VZyRsyOfAAwsFooZoABkj8zjSFIDztsRy3949QeBcm6Vl+x-kAeR4ACssHQYGUEJttjCrIsbq4AHJvdrQ2Ho0yLF5IlFQNEY2FZXD7IA) + ```typescript const A = Parse('string') // const A: TString @@ -1115,11 +1117,13 @@ const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ // }> ``` - + + +### Context -### Param +The Parse function accepts an optional context parameter that enables external types to be referenced within the syntax. -The Parse function accepts an optional context parameter that enables external types to be referenced within the syntax. This can be helpful for reusing types in different contexts. +[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChQkWIjgAVLjnxES5KrUbM2nbnwmTJdCDQzwFcALyLlAOgDyPAFZY6MABRI4P33-8BgayscCYArgwAJnA8OADuUMAwMFg0cKgYAFwo6NgeAAYIkj782UrcdgByYSCxUB4AlAA0ga1tbcG+pXA0NXVNxXAcZfbVtVj1ze3TM50+wz19EwM+AF4jFWN1jTO7rXNr2b3jUJK4DXuXV-6duPkNRiZm8ACC1jmYWF6KeC35aLBgKgGAAeBQAPnuV06T3McBeZScrncIKK13R1wO3QUDjAMGApmBYK2E3BKwxFNmIXmiLxBJoRIUJKgZMGlPZQWpcHWilx+MJoKZSxZbI5Yp8t3Bj1McIAQu8AXkkJZcH8ANZYDgQAiKKHomEy+CysoAVRo9JBAG1ReKOQcFAAZJITIlkCSs222+1OlJQV0cMgez1i73Ov2gsirQM24MYzoAXSlxkNcAAwgrcl9lb84PlLAAyeRxI7CvB6zmhFOpsoASVEE2wKMtOJcbhgqJjscxXLg2OZAG5O13LgchmUB0Ph7tRzyhSdB1OKZKWi3ke20Yv9T3i4oJ5ut3hwYmjEA) ```typescript const T = Type.Object({ // could be written as: Parse(`{ @@ -1140,12 +1144,13 @@ const B = Parse({ T }, `keyof T`) // const B: TUnion<[ // TLiteral<'z'> // ]> -const C = Parse({ T }, `T & { w: number }`) // const C: TObject<{ - // x: TNumber, - // y: TNumber, - // z: TNumber, - // w: TNumber - // }> +const C = Parse({ T }, `T & { w: number }`) // const C: TIntersect<[TObject<{ + // x: TNumber; + // y: TNumber; + // z: TNumber; + // }>, TObject<{ + // w: TNumber; + // }>]> ``` @@ -1156,18 +1161,20 @@ const C = Parse({ T }, `T & { w: number }`) // const C: TObject<{ TypeBox provides two Static types for inferring TypeScript syntax directly from strings. +[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgZRgQxsAxgBTVAZwFMBBA5LACyJDQBoV1Nd9iyAVATzCLgF84AMygQQcAOQABAsAB2WADZpgUAPQxuRAEYQAHqoKdZ6XeLgAoc6tVwA6sAUK4cwUShw0BD3HYVqtSw0eFDgAXkYMbDxCUnIqGjQAHgQ+BgADJF0ALjhZAFcQLTd+NIA+OArrOCDeZBz2AHktACsiLBhk8wrunt6+-oHBgaqK7J8AOQKiqC6hufmF3qq+Ussq+0dnWVd3T28awM0fMIjmaLYCLh5k1LgMuDH8wuK+MqWbGuPwhFnFv--3qMck9pr8AeDFiM4EA) + ```typescript import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' // Will infer as a TSchema -type S = StaticParseAsSchema<`{ x: number }`> // type S: TObject<{ +type S = StaticParseAsSchema<{}, `{ x: number }`> // type S: TObject<{ // x: TNumber // }> // Will infer as a type -type T = StaticParseAsType<`{ x: number }`> // type T = { +type T = StaticParseAsType<{}, `{ x: number }`> // type T = { // x: number // ``` From 4e2158deb238b1fe958b0f78ad9e05f21e45041c Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 11 Nov 2024 20:06:45 +0900 Subject: [PATCH 302/369] Standard Schema Reference --- example/standard/index.ts | 29 +++++++++++++++ example/standard/readme.md | 67 +++++++++++++++++++++++++++++++++++ example/standard/standard.ts | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 example/standard/index.ts create mode 100644 example/standard/readme.md create mode 100644 example/standard/standard.ts diff --git a/example/standard/index.ts b/example/standard/index.ts new file mode 100644 index 000000000..331f30aa4 --- /dev/null +++ b/example/standard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/standard-schema + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './standard' \ No newline at end of file diff --git a/example/standard/readme.md b/example/standard/readme.md new file mode 100644 index 000000000..343d60616 --- /dev/null +++ b/example/standard/readme.md @@ -0,0 +1,67 @@ +### Standard Schema + +This example is a reference implementation of [Standard Schema](https://github.com/standard-schema/standard-schema) for TypeBox. + +### Overview + +This example provides a reference implementation for the Standard Schema specification. Despite the name, this specification is NOT focused on schematics. Rather, it defines a set of common TypeScript interfaces that libraries are expected to implement to be considered "Standard" for framework integration, as defined by the specification's authors. + +The TypeBox project has some concerns about the Standard Schema specification, particularly regarding its avoidance to separate concerns between schematics and the logic used to validate those schematics. TypeBox does respect such separation for direct interoperability with industry standard validators as well as to allow intermediate processing of types (Compile). Additionally, augmenting schematics in the way proposed by Standard Schema would render Json Schema invalidated (which is a general concern for interoperability with Json Schema validation infrastructure, such as Ajv). + +The Standard Schema specification is currently in its RFC stage. TypeBox advocates for renaming the specification to better reflect its purpose, such as "Common Interface for Type Integration." Additionally, the requirement for type libraries to adopt a common interface for integration warrants review. The reference project link provided below demonstrates an alternative approach that would enable integration of all type libraries (including those not immediately compatible with Standard Schema) to be integrated into frameworks (such as tRPC) without modification to a library's core structure. + +[Type Adapters](https://github.com/sinclairzx81/type-adapters) + +### Example + +The Standard Schema function will augment TypeBox's Json Schema with runtime validation methods. These methods are assigned to the sub property `~standard`. Once a type is augmented, it should no longer be considered valid Json Schema. + +```typescript +import { Type } from '@sinclair/typebox' +import { StandardSchema } from './standard' + +// The Standard Schema function will augment a TypeBox type with runtime validation +// logic to validate / parse a value (as mandated by the Standard Schema interfaces). +// Calling this function will render the json-schema schematics invalidated, specifically +// the non-standard keyword `~standard` which ideally should be expressed as a non +// serializable symbol (Ajv strict) + +const A = StandardSchema(Type.Object({ // const A = { + x: Type.Number(), // '~standard': { version: 1, vendor: 'TypeBox', validate: [Function: validate] }, + y: Type.Number(), // type: 'object', + z: Type.Number(), // properties: { +})) // x: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, + // y: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, + // z: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' } + // }, + // required: [ 'x', 'y', 'z' ], + // [Symbol(TypeBox.Kind)]: 'Object' + // } + +const R = A['~standard'].validate({ x: 1, y: 2, z: 3 }) // const R = { value: { x: 1, y: 2, z: 3 }, issues: [] } +``` + +### Ajv Strict + +Applying Standard Schema to a TypeBox types renders them unusable in Ajv. The issue is due to the `~standard` property being un-assignable as a keyword (due to the leading `~`) + +```typescript +import Ajv from 'ajv' + +const ajv = new Ajv().addKeyword('~standard') // cannot be defined as keyword. + +const A = StandardSchema(Type.Object({ // const A = { + x: Type.Number(), // '~standard': { version: 1, vendor: 'TypeBox', validate: [Function: validate] }, + y: Type.Number(), // type: 'object', + z: Type.Number(), // properties: { +})) // x: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, + // y: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, + // z: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' } + // }, + // required: [ 'x', 'y', 'z' ], + // [Symbol(TypeBox.Kind)]: 'Object' + // } + + +ajv.validate(A, { x: 1, y: 2, z: 3 }) // Error: Keyword ~standard has invalid name +``` diff --git a/example/standard/standard.ts b/example/standard/standard.ts new file mode 100644 index 000000000..b61dcbba0 --- /dev/null +++ b/example/standard/standard.ts @@ -0,0 +1,68 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/standard-schema + +The MIT License (MIT) + +Copyright (c) 2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { AssertError, Value, ValueError } from '@sinclair/typebox/value' +import { TSchema, StaticDecode, CloneType } from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// StandardSchema: Common +// ------------------------------------------------------------------ +interface StandardResult { + value: Output + issues: [ValueError[]] +} +interface StandardSchema { + readonly "~standard": StandardSchemaProps; +} + +interface StandardSchemaProps { + readonly version: 1; + readonly vendor: 'TypeBox'; + readonly validate: (value: unknown) => StandardResult; + readonly types?: undefined; +} +// ------------------------------------------------------------------ +// StandardSchema: TypeBox +// ------------------------------------------------------------------ +export type TStandardSchema> = Result +/** + * Augments a Json Schema with runtime validation logic required by StandardSchema. Wrapping a type in this way renders the type + * incompatible with the Json Schema specification. + */ +export function StandardSchema(schema: Type, references: TSchema[] = []): TStandardSchema> { + const validate = (value: unknown) => { + try { + return { value: Value.Parse(schema, references, value) } + } catch (error) { + const asserted = error instanceof AssertError ? error : undefined + return asserted ? { issues: [...asserted.Errors()] } : { issues: ['Unknown error']} + } + } + const standard = { version: 1, vendor: 'TypeBox', validate } + return CloneType(schema, { ['~standard']: standard }) as never +} \ No newline at end of file From 55ae006127a4772d58fa9069f666cd322cdd6ea8 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Wed, 13 Nov 2024 23:56:23 +0900 Subject: [PATCH 303/369] Revision 0.34.0 (#1069) * Add Module Types * Add Syntax Types --- changelog/0.34.0.md | 196 ++++++++++++++++ example/prototypes/index.ts | 1 - example/prototypes/module.ts | 166 -------------- example/prototypes/readme.md | 40 ---- package-lock.json | 4 +- package.json | 2 +- readme.md | 212 ++++++++---------- src/compiler/compiler.ts | 18 +- src/errors/errors.ts | 8 + src/index.ts | 3 +- src/syntax/parse.ts | 41 ++-- src/syntax/runtime.ts | 233 +++++++++++++++---- src/syntax/static.ts | 194 +++++++++++++--- src/type/deref/deref.ts | 174 -------------- src/type/guard/kind.ts | 5 + src/type/guard/type.ts | 14 ++ src/type/index.ts | 3 +- src/type/{deref => module}/index.ts | 2 +- src/type/module/module.ts | 189 ++++++++++++++++ src/type/ref/ref.ts | 24 +- src/type/static/static.ts | 2 +- src/type/strict/index.ts | 29 --- src/type/strict/strict.ts | 44 ---- src/type/type/json.ts | 34 +-- src/type/type/type.ts | 3 +- src/value/cast/cast.ts | 12 +- src/value/check/check.ts | 12 +- src/value/clean/clean.ts | 13 +- src/value/convert/convert.ts | 8 + src/value/create/create.ts | 8 + src/value/default/default.ts | 8 + src/value/transform/decode.ts | 32 ++- src/value/transform/encode.ts | 12 + test/runtime/compiler-ajv/index.ts | 1 + test/runtime/compiler-ajv/module.ts | 78 +++++++ test/runtime/compiler-ajv/ref.ts | 6 +- test/runtime/compiler/index.ts | 1 + test/runtime/compiler/module.ts | 78 +++++++ test/runtime/compiler/ref.ts | 8 +- test/runtime/compiler/unicode.ts | 2 +- test/runtime/syntax/syntax.ts | 94 +++++++- test/runtime/type/guard/kind/deref.ts | 110 --------- test/runtime/type/guard/kind/import.ts | 15 ++ test/runtime/type/guard/kind/index.ts | 2 +- test/runtime/type/guard/type/deref.ts | 110 --------- test/runtime/type/guard/type/import.ts | 15 ++ test/runtime/type/guard/type/index.ts | 2 +- test/runtime/type/guard/type/ref.ts | 4 +- test/runtime/value/cast/import.ts | 51 +++++ test/runtime/value/cast/index.ts | 1 + test/runtime/value/cast/union.ts | 2 +- test/runtime/value/check/index.ts | 1 + test/runtime/value/check/module.ts | 80 +++++++ test/runtime/value/check/ref.ts | 8 +- test/runtime/value/clean/import.ts | 203 +++++++++++++++++ test/runtime/value/clean/index.ts | 1 + test/runtime/value/convert/import.ts | 29 +++ test/runtime/value/convert/index.ts | 1 + test/runtime/value/create/import.ts | 98 ++++++++ test/runtime/value/create/index.ts | 1 + test/runtime/value/create/ref.ts | 8 +- test/runtime/value/default/import.ts | 299 +++++++++++++++++++++++++ test/runtime/value/default/index.ts | 1 + test/runtime/value/transform/import.ts | 157 +++++++++++++ test/runtime/value/transform/index.ts | 1 + test/runtime/value/transform/ref.ts | 6 +- test/static/deref.ts | 56 ----- test/static/ref.ts | 4 +- 68 files changed, 2241 insertions(+), 1039 deletions(-) create mode 100644 changelog/0.34.0.md delete mode 100644 example/prototypes/module.ts delete mode 100644 src/type/deref/deref.ts rename src/type/{deref => module}/index.ts (98%) create mode 100644 src/type/module/module.ts delete mode 100644 src/type/strict/index.ts delete mode 100644 src/type/strict/strict.ts create mode 100644 test/runtime/compiler-ajv/module.ts create mode 100644 test/runtime/compiler/module.ts delete mode 100644 test/runtime/type/guard/kind/deref.ts create mode 100644 test/runtime/type/guard/kind/import.ts delete mode 100644 test/runtime/type/guard/type/deref.ts create mode 100644 test/runtime/type/guard/type/import.ts create mode 100644 test/runtime/value/cast/import.ts create mode 100644 test/runtime/value/check/module.ts create mode 100644 test/runtime/value/clean/import.ts create mode 100644 test/runtime/value/convert/import.ts create mode 100644 test/runtime/value/create/import.ts create mode 100644 test/runtime/value/default/import.ts create mode 100644 test/runtime/value/transform/import.ts delete mode 100644 test/static/deref.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md new file mode 100644 index 000000000..fdc9a75ff --- /dev/null +++ b/changelog/0.34.0.md @@ -0,0 +1,196 @@ +## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0) + +## Overview + +Revision 0.34.0 represents a significant milestone for the TypeBox project. This update changes how TypeBox manages type references (Ref) and introduces a new Module type to support mutual recursion and self-referencing types. Additionally, it includes a new submodule for parsing TypeScript syntax directly into TypeBox types. + +Please note that this release includes breaking changes to Ref and some deprecations. These updates require a minor semver revision. + +## Contents + +- [Enhancements](#Enhancements) + - [Module Types](#Module-Types) + - [Syntax Types](#Syntax-Types) +- [Breaking Changes](#Breaking-Changes) + - [Ref](#Ref) + - [Deref](#Deref) + - [Strict](#Strict) + + + + +## Enhancements + +Below are the enhancements introduced in Version 0.34.0. + + + +### Module Types + +Revision 0.34.0 introduces a new type, called Module. Modules are represented as JSON Schema $def schematics, specifically designed to support both mutual and self-recursive types. This addition resolves a longstanding issue in TypeBox, where complex recursive structures often encountered "definition order" problems, making certain recursive structures difficult to represent cleanly. With Modules, you can now define schematics within a Module context, allowing them to be referenced within the type system through a separate inference operation. + +```typescript +// The following creates a circular recursive type. + +const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B') // Ref B: + }), + B: Type.Object({ + c: Type.Ref('C') // Ref C: + }), + C: Type.Object({ + a: Type.Ref('A') // Ref A: + }), +}) + +// Module types must be imported before use. + +const A = Module.Import('A') // const A: TImport<{...}, 'A'> + +type A = Static // type A = { + // b: { + // c: { + // a: { + // b: ... + // } + // } + // } + // } +``` + + + +### Syntax Types + + +Revision 0.34.0 introduces a new submodule for parsing TypeScript syntax directly into TypeBox types, implemented both at runtime and within the type system. This feature was made possible through the development of a separate project, [ParseBox](https://github.com/sinclairzx81/parsebox) (MIT-licensed), which provides a symmetric runtime and type-level parsing infrastructure. + +As of 0.34.0, Syntax Types are available as an opt-in feature, with the parsing infrastructure adding approximately 10kb (minified) to the existing type builder. With further optimizations, this feature may be elevated to a top-level import in future updates to minimize bundling size. + +To use Syntax Types, import them from the `@sinclair/typebox/syntax` path. + +```typescript +import { Parse } from '@sinclair/typebox/syntax' + +// All primitive types are supported + +const A = Parse('string') // const A: TString +const B = Parse('number') // const B: TNumber +const C = Parse('boolean') // const C: TBoolean + +// ... Multiline parsing is supported (but comments are not) + +const T = Parse(`{ + x: number + y: number + z: number +}`) + + +// ... Parametertized parsing is supported +const O = Parse({ T }, `T & { w: number }`) // const O: TIntersect<[ + // TObject<{ + // x: TNumber, + // y: TNumber, + // z: TNumber, + // }>, + // TObject<{ + // w: TNumber + // }> + // ]> + +// ... Module parsing is also supported. + +const Math = Parse(`module Math { + export interface X { + x: number + } + export interface Y { + y: number + } + export interface Z { + z: number + } + export interface Vector extends X, Y, Z { + type: 'Vector' + } +}`) + +const Vector = Math.Import('Vector') +``` + +Runtime parsing performance should be quite good; however, static parsing performance could be improved. TypeScript will invoke the parser for each property accessed at design time. Ongoing efforts within the ParseBox project aim to optimize string parsing in TypeScript, with additional research underway into type-level caching strategies within the TypeScript compiler. Additional optimizations will be explored over the course of 0.34.x. + + + +## Breaking Changes + +The following are the breaking changes in Revision 0.34.0. + + + +### Ref + +Revision 0.34.0 introduces a breaking change to Ref, modifying its signature to accept only constant string values. Previously, Ref could accept an existing TypeBox type, provided it had an $id assigned. + +```typescript + +// Revision 0.33.0 + +const T = Type.String({ $id: 'T' }) + +const R = Type.Ref(T) + +type R = Static // type R = string + +// Revision 0.34.0 + +const T = Type.String({ $id: 'T' }) + +const R = Type.Ref('T') + +type R = Static // type R = unknown + +``` + +In Revision 0.34.0, the inferred type for Ref is now unknown. Implementations using the previous version of Ref can switch to Unsafe to type the reference to the target value. + +```typescript +// Revision 0.34.0 + +const T = Type.String({ $id: 'T' }) + +const R = Type.Unsafe>(Type.Ref('T')) + +type R = Static // type R = string +``` + + + +### Deref + +Revision 0.34.0 removes the Deref type, which was previously used to dereference schematics. Since the Ref signature has changed from TSchema to string, there is no longer a way to resolve reference types accurately. TypeBox may provide a prototype example of this type upon request. + + + +### Strict + +Revision 0.34.0 removes the Strict type from the Type Builder, which was deprecated in version 0.33.8. This type was introduced several years ago in response to a change in Ajv that prohibited unknown keywords. At that time, TypeBox used string property keys for `kind` and `modifier`, which required either Ajv configuration or the use of Strict. These properties have since been updated to Symbol properties, resolving the issues with Ajv. However, the Strict type remained due to some use in ecosystem projects, which has since reduced. + +For those who still need Strict, the recommended approach is to use the JSON stringify/parse method outlined in the deprecation notice. + +```typescript +/** + * @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended + * to use the JSON parse/stringify to remove compositing symbols if needed. This + * is how Strict works internally. + * + * ```typescript + * JSON.parse(JSON.stringify(Type.String())) + * ``` + */ +export function Strict(schema: T): TStrict { + return JSON.parse(JSON.stringify(schema)) +} +``` \ No newline at end of file diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index cb81ad0bb..179da98fd 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -27,7 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './from-schema' -export * from './module' export * from './partial-deep' export * from './union-enum' export * from './union-oneof' diff --git a/example/prototypes/module.ts b/example/prototypes/module.ts deleted file mode 100644 index e56dcb2dd..000000000 --- a/example/prototypes/module.ts +++ /dev/null @@ -1,166 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/prototypes - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '@sinclair/typebox' -import { Check } from '@sinclair/typebox/value' -// ------------------------------------------------------------------ -// Infer -// ------------------------------------------------------------------ -// prettier-ignore -export type InferImport = ( - Infer -) -// prettier-ignore -export type InferModuleRef = ( - Ref extends keyof Module ? Infer : never -) -// prettier-ignore -export type InferObject = { - [K in keyof Properties]: Infer -} & {} -// prettier-ignore -export type InferConstructor = Types.Ensure< - new (...args: InferTuple) => Infer -> -// prettier-ignore -export type InferFunction = Types.Ensure< - (...args: InferTuple) => Infer -> -// prettier-ignore -export type InferTuple = ( - Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] - ? InferTuple]> - : Result -) -// prettier-ignore -export type InferIntersect = ( - Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] - ? InferIntersect> - : Result -) -// prettier-ignore -export type InferUnion = ( - Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]] - ? InferUnion> - : Result -) -// prettier-ignore -export type InferArray = ( - Types.Ensure>> -) -// prettier-ignore -export type InferAsyncIterator = ( - Types.Ensure>> -) -// prettier-ignore -export type InferIterator = ( - Types.Ensure>> -) -// prettier-ignore -type Infer = ( - Type extends TImport ? InferImport : - Type extends TModuleRef ? InferModuleRef : - Type extends Types.TObject ? InferObject : - Type extends Types.TConstructor ? InferConstructor : - Type extends Types.TFunction ? InferFunction : - Type extends Types.TTuple ? InferTuple : - Type extends Types.TIntersect ? InferIntersect : - Type extends Types.TUnion ? InferUnion : - Type extends Types.TArray ? InferArray : - Type extends Types.TAsyncIterator ? InferAsyncIterator : - Type extends Types.TIterator ? InferIterator : - Type extends Types.TTemplateLiteral ? Types.Static> : - Type extends Types.TLiteral ? S : - Type extends Types.TAny ? any : - Type extends Types.TBigInt ? bigint : - Type extends Types.TBoolean ? boolean : - Type extends Types.TDate ? Date : - Type extends Types.TInteger ? number : - Type extends Types.TNever ? never : - Type extends Types.TNumber ? number : - Type extends Types.TRegExp ? string : - Type extends Types.TString ? string : - Type extends Types.TSymbol ? symbol : - Type extends Types.TNull ? null : - Type extends Types.TUint8Array ? Uint8Array : - Type extends Types.TUndefined ? undefined : - Type extends Types.TUnknown ? unknown : - Type extends Types.TVoid ? void : - never -) -// ------------------------------------------------------------------ -// ModuleRef -// ------------------------------------------------------------------ -// prettier-ignore -export interface TModuleRef extends Types.TSchema { - [Types.Kind]: 'ModuleRef' - $ref: Ref -} -// prettier-ignore -export function ModuleRef($ref: Ref): TModuleRef { - return Types.Type.Ref($ref) as never -} -// ------------------------------------------------------------------ -// Import -// ------------------------------------------------------------------ -// prettier-ignore -Types.TypeRegistry.Set('Import', (module: TImport, value: unknown) => { - const keys = Object.getOwnPropertyNames(module.$defs) - const references = keys.map(key => module.$defs[key as never]) as Types.TSchema[] - const schema = module.$defs[module.$ref] - return Check(schema, references, value) -}) -// prettier-ignore -export type TModuleProperties = Record - -// prettier-ignore -export interface TImport extends Types.TSchema { - [Types.Kind]: 'Import' - static: InferImport - $defs: Definitions - $ref: Key -} -// ------------------------------------------------------------------ -// Module -// ------------------------------------------------------------------ -// prettier-ignore -export class ModuleInstance { - constructor(private readonly properties: Properties) {} - public Import(key: Key): TImport { - const $defs = globalThis.Object.getOwnPropertyNames(this.properties).reduce((Result, Key) => ( - { ...Result, [Key]: { ...this.properties[Key], $id: Key }} - ), {}) - return { [Types.Kind]: 'Import', $defs, $ref: key } as never - } -} -export function Module(properties: Properties): ModuleInstance { - return new ModuleInstance(properties) -} - - - diff --git a/example/prototypes/readme.md b/example/prototypes/readme.md index 7c7c429f2..cd95b34fb 100644 --- a/example/prototypes/readme.md +++ b/example/prototypes/readme.md @@ -2,46 +2,6 @@ TypeBox prototypes are a set of types that are either under consideration for inclusion into the library, or have been requested by users but cannot be added to the library either due to complexity, using schematics that fall outside the supported TypeBox or should be expressed by users via advanced type composition. -## Module, ModuleRef and Import - -The Module type as a candidate referencing system for TypeBox. Modules enable deferred cross type referencing and support mutual recursive inference. Module types must be instanced via `M.Import(...)` which constructs a `$def` schematic containing each definition required to validate, and a self referential `$ref` to one of the type being imported. - -```typescript -import { Module, ModuleRef } from './prototypes' - -// ------------------------------------------------------------------ -// Module, ModuleRef -// ------------------------------------------------------------------ -const Math = Module({ - Vector2: Type.Object({ - x: Type.Number(), - y: Type.Number(), - }), - Vector3: Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number() - }), - Vertex: Type.Object({ - position: ModuleRef('Vector3'), - normal: ModuleRef('Vector3'), - texcoord: ModuleRef('Vector2') - }), - Geometry: Type.Object({ - vertices: Type.Array(ModuleRef('Vertex')), - indices: Type.Array(Type.Integer()) - }) -}) - -// ------------------------------------------------------------------ -// Import -// ----------------------------------------------------------------- - -const Vector2 = Math.Import('Vector2') -const Vector3 = Math.Import('Vector2') -const Vertex = Math.Import('Vertex') -const Geometry = Math.Import('Geometry') -``` ## PartialDeep diff --git a/package-lock.json b/package-lock.json index 262945a64..fbacace2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.33.22", + "version": "0.34.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.33.22", + "version": "0.34.0", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 1a8dff98f..44b16b8e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.33.22", + "version": "0.34.0", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 5836c10a8..39fd65056 100644 --- a/readme.md +++ b/readme.md @@ -68,8 +68,7 @@ License MIT - [Options](#types-options) - [Properties](#types-properties) - [Generics](#types-generics) - - [References](#types-references) - - [Recursive](#types-recursive) + - [Modules](#types-modules) - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) - [Mapped](#types-mapped) @@ -79,7 +78,9 @@ License MIT - [Unsafe](#types-unsafe) - [Syntax](#syntax) - [Parse](#syntax-parse) + - [Compose](#syntax-compose) - [Context](#syntax-context) + - [Module](#syntax-module) - [Static](#syntax-static) - [Limits](#syntax-limits) - [Values](#values) @@ -769,103 +770,40 @@ const T = Nullable(Type.String()) // const T = { type T = Static // type T = string | null ``` - + -### Reference Types +### Module Types -Reference types can be created with Ref. These types infer the same as the target type but only store a named `$ref` to the target type. +TypeBox Modules are containers for related types. They function as namespaces and enable internal types to reference each other via string references. Modules support both singular and mutually recursive types. They provide a mechanism to create circular types irrespective of the order in which types are defined. ```typescript -const Vector = Type.Object({ // const Vector = { - x: Type.Number(), // type: 'object', - y: Type.Number(), // required: ['x', 'y', 'z'], -}, { $id: 'Vector' }) // properties: { - // x: { type: 'number' }, - // y: { type: 'number' } - // }, - // $id: 'Vector' - // } - -const VectorRef = Type.Ref(Vector) // const VectorRef = { - // $ref: 'Vector' - // } - -type VectorRef = Static // type VectorRef = { - // x: number, - // y: number - // } -``` -Use Deref to dereference a type. This function will replace any interior reference with the target type. -```typescript -const Vertex = Type.Object({ // const Vertex = { - position: VectorRef, // type: 'object', - texcoord: VectorRef, // required: ['position', 'texcoord'], -}) // properties: { - // position: { $ref: 'Vector' }, - // texcoord: { $ref: 'Vector' } - // } - // } - -const VertexDeref = Type.Deref(Vertex, [Vector]) // const VertexDeref = { - // type: 'object', - // required: ['position', 'texcoord'], - // properties: { - // position: { - // type: 'object', - // required: ['x', 'y', 'z'], - // properties: { - // x: { type: 'number' }, - // y: { type: 'number' } - // } - // }, - // texcoord: { - // type: 'object', - // required: ['x', 'y', 'z'], - // properties: { - // x: { type: 'number' }, - // y: { type: 'number' } - // } - // } - // } - // } -``` -Note that Ref types do not store structural information about the type they're referencing. Because of this, these types cannot be used with some mapping types (such as Partial or Pick). For applications that require mapping on Ref, use Deref to normalize the type first. - - - -### Recursive Types - -TypeBox supports recursive data structures with Recursive. This type wraps an interior type and provides it a `this` context that allows the type to reference itself. The following creates a recursive type. Singular recursive inference is also supported. +// The following creates a circular recursive type. + +const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B') // Ref B: + }), + B: Type.Object({ + c: Type.Ref('C') // Ref C: + }), + C: Type.Object({ + a: Type.Ref('A') // Ref A: + }), +}) -```typescript -const Node = Type.Recursive(This => Type.Object({ // const Node = { - id: Type.String(), // $id: 'Node', - nodes: Type.Array(This) // type: 'object', -}), { $id: 'Node' }) // properties: { - // id: { - // type: 'string' - // }, - // nodes: { - // type: 'array', - // items: { - // $ref: 'Node' - // } - // } - // }, - // required: [ - // 'id', - // 'nodes' - // ] - // } +// Module types must be imported before use. -type Node = Static // type Node = { - // id: string - // nodes: Node[] - // } +const A = Module.Import('A') // const A: TImport<{...}, 'A'> -function test(node: Node) { - const id = node.nodes[0].nodes[0].id // id is string -} +type A = Static // type A = { + // b: { + // c: { + // a: { + // b: ... + // } + // } + // } + // } ``` @@ -1084,11 +1022,11 @@ if(TypeGuard.IsString(T)) { -## Syntax +## Syntax Types -TypeBox supports parsing TypeScript type annotation syntax directly into TypeBox types, providing an alternative to the Type.* API for type construction. This feature functions at runtime and within the TypeScript type system, enabling types encoded as strings to be inferred as equivalent TypeBox types. +TypeBox provides support for Syntax Types, enabling it to parse TypeScript syntax directly into TypeBox types. Syntax Types serve as a DSL frontend for TypeBox's type builder and are useful for converting existing TypeScript type definitions into Json Schema schematics. -TypeScript syntax parsing is provided as an optional import. +Syntax Types are provided via optional import. ```typescript import { Parse } from '@sinclair/typebox/syntax' @@ -1098,9 +1036,7 @@ import { Parse } from '@sinclair/typebox/syntax' ### Parse -The Parse function can be used to parse TypeScript code into TypeBox types. The left hand result will be a inferred TypeBox type of TSchema. Invalid syntax will result in an undefined return value. - -[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChJdCDQzwAgnAC8KdNgAUZBVFoBzMgEo4ps+YuXLrVnFnylALjgAVAMow9NfdPsK4AEKq6phY2gDaAIwANHAATLEAzAC6xlbpGTZ2cv4Bzi4uAK5gDFgAPOGSGdU1tdVZpi4AMsAwWFCoDGWRAHzRVXWDQ5m2jS1tHV1xfQPDc3MNruPtnWWJPbPzW7VZyRsyOfAAwsFooZoABkj8zjSFIDztsRy3949QeBcm6Vl+x-kAeR4ACssHQYGUEJttjCrIsbq4AHJvdrQ2Ho0yLF5IlFQNEY2FZXD7IA) +Use the Parse function to convert a TypeScript string into a TypeBox type. TypeBox will infer the appropriate TSchema type or return undefined if there is a syntax error. ```typescript const A = Parse('string') // const A: TString @@ -1117,64 +1053,96 @@ const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ // }> ``` + + + + +### Compose + +Syntax Types are designed to be interchangeable with standard Types. + +```typescript +const T = Type.Object({ // const T: TObject<{ + x: Parse('number'), // x: TNumber, + y: Parse('number'), // y: TNumber, + z: Parse('number') // z: TNumber +}) // }> +``` + + + +### Module + +Syntax Types support Module parsing, which is useful for processing multiple TypeScript types. Module parsing supports type alias and interface definitions. Generics are currently unsupported as of 0.34.0. + +```typescript +const Foo = Parse(`module Foo { + + export type A = string + + export type B = number + + export type C = A | B + +}`) + +const C = Foo.Import('C') // const C: TImport<{ + // ... + // }, 'C'> +``` + ### Context -The Parse function accepts an optional context parameter that enables external types to be referenced within the syntax. - -[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChQkWIjgAVLjnxES5KrUbM2nbnwmTJdCDQzwFcALyLlAOgDyPAFZY6MABRI4P33-8BgayscCYArgwAJnA8OADuUMAwMFg0cKgYAFwo6NgeAAYIkj782UrcdgByYSCxUB4AlAA0ga1tbcG+pXA0NXVNxXAcZfbVtVj1ze3TM50+wz19EwM+AF4jFWN1jTO7rXNr2b3jUJK4DXuXV-6duPkNRiZm8ACC1jmYWF6KeC35aLBgKgGAAeBQAPnuV06T3McBeZScrncIKK13R1wO3QUDjAMGApmBYK2E3BKwxFNmIXmiLxBJoRIUJKgZMGlPZQWpcHWilx+MJoKZSxZbI5Yp8t3Bj1McIAQu8AXkkJZcH8ANZYDgQAiKKHomEy+CysoAVRo9JBAG1ReKOQcFAAZJITIlkCSs222+1OlJQV0cMgez1i73Ov2gsirQM24MYzoAXSlxkNcAAwgrcl9lb84PlLAAyeRxI7CvB6zmhFOpsoASVEE2wKMtOJcbhgqJjscxXLg2OZAG5O13LgchmUB0Ph7tRzyhSdB1OKZKWi3ke20Yv9T3i4oJ5ut3hwYmjEA) +The Parse function accepts an initial Context argument, allowing external types to be passed into the parser. ```typescript const T = Type.Object({ // could be written as: Parse(`{ x: Type.Number(), // x: number, y: Type.Number(), // y: number, - z: Type.Number() // z: number + z: Type.Number() // z: number }) // }`) -const A = Parse({ T }, `Partial`) // const A: TObject<{ +const A = Parse({ T }, 'Partial') // const A: TObject<{ // x: TOptional, // y: TOptional, // z: TOptional // }> -const B = Parse({ T }, `keyof T`) // const B: TUnion<[ +const B = Parse({ T }, 'keyof T') // const B: TUnion<[ // TLiteral<'x'>, // TLiteral<'y'>, // TLiteral<'z'> // ]> -const C = Parse({ T }, `T & { w: number }`) // const C: TIntersect<[TObject<{ +const C = Parse({ T }, 'T & { w: number }') // const C: TIntersect<[TObject<{ // x: TNumber; // y: TNumber; // z: TNumber; // }>, TObject<{ // w: TNumber; // }>]> - - ``` ### Static -TypeBox provides two Static types for inferring TypeScript syntax directly from strings. - -[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgZRgQxsAxgBTVAZwFMBBA5LACyJDQBoV1Nd9iyAVATzCLgF84AMygQQcAOQABAsAB2WADZpgUAPQxuRAEYQAHqoKdZ6XeLgAoc6tVwA6sAUK4cwUShw0BD3HYVqtSw0eFDgAXkYMbDxCUnIqGjQAHgQ+BgADJF0ALjhZAFcQLTd+NIA+OArrOCDeZBz2AHktACsiLBhk8wrunt6+-oHBgaqK7J8AOQKiqC6hufmF3qq+Ussq+0dnWVd3T28awM0fMIjmaLYCLh5k1LgMuDH8wuK+MqWbGuPwhFnFv--3qMck9pr8AeDFiM4EA) +Syntax Types provide two Static types for inferring TypeScript syntax from strings. ```typescript import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' // Will infer as a TSchema -type S = StaticParseAsSchema<{}, `{ x: number }`> // type S: TObject<{ +type S = StaticParseAsSchema<{}, '{ x: number }'> // type S: TObject<{ // x: TNumber // }> // Will infer as a type -type T = StaticParseAsType<{}, `{ x: number }`> // type T = { +type T = StaticParseAsType<{}, '{ x: number }'> // type T = { // x: number // ``` @@ -1182,9 +1150,9 @@ type T = StaticParseAsType<{}, `{ x: number }`> // type T = { -### Limits +### Limitations -The Parse function works by TypeBox parsing TypeScript syntax within the type system itself. This can place some additional demand on the TypeScript compiler, which may affect language service (LSP) responsiveness when working with especially large or complex types. In particular, very wide structures or deeply nested types may approach TypeScript’s instantiation limits. +Syntax Types work by having TypeBox parse TypeScript syntax within the TypeScript type system. This approach can place some strain on the TypeScript compiler and language service, potentially affecting responsiveness. While TypeBox makes a best-effort attempt to optimize for Syntax Types, users should be mindful of the following structures: ```typescript // Excessively wide structures will result in instantiation limits exceeding @@ -1211,12 +1179,12 @@ const B = Parse(`{ }`) ``` -For parsing especially complex types, TypeBox provides the ParseOnly function, which parses strings at runtime and returns a non-inferred TSchema type. ParseOnly allows for arbitrarily complex types without reaching instantiation limits, making it useful for cases where TypeScript types need to be directly converted to Json Schema. +In cases where Syntax Types busts through TypeScript instantiation limits, TypeBox offers a fallback ParseOnly function which will Parse the types at runtime, but not infer the type. This function can also be used for parsing non-constant strings. ```typescript import { ParseOnly } from '@sinclair/typebox/syntax' -// ok: but where A is TSchema | undefined +// Where A is TSchema | undefined const A = ParseOnly(`{ x: { @@ -1229,7 +1197,7 @@ const A = ParseOnly(`{ }`) ``` -Optimizing TypeScript string parsing performance is an ongoing area of research. For more information, see the [ParseBox](https://github.com/sinclairzx81/parsebox) project, which documents TypeBox’s parsing infrastructure and focuses efforts to improve parsing performance. +For more information on TypeBox's parsing infrastructure, refer to the [ParseBox](https://github.com/sinclairzx81/parsebox) project. @@ -1914,12 +1882,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '119.8 kb' │ ' 52.6 kb' │ '2.28 x' │ -│ typebox/errors │ ' 74.4 kb' │ ' 33.1 kb' │ '2.25 x' │ -│ typebox/syntax │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │ +│ typebox/compiler │ '121.7 kb' │ ' 53.4 kb' │ '2.28 x' │ +│ typebox/errors │ ' 75.3 kb' │ ' 33.4 kb' │ '2.25 x' │ +│ typebox/syntax │ '120.1 kb' │ ' 50.5 kb' │ '2.38 x' │ │ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ -│ typebox/value │ '157.2 kb' │ ' 66.1 kb' │ '2.38 x' │ -│ typebox │ ' 98.9 kb' │ ' 41.2 kb' │ '2.40 x' │ +│ typebox/value │ '160.3 kb' │ ' 67.4 kb' │ '2.38 x' │ +│ typebox │ ' 96.2 kb' │ ' 40.2 kb' │ '2.39 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index bc1fd8b4d..6e438702b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -47,6 +47,7 @@ import type { TBoolean } from '../type/boolean/index' import type { TDate } from '../type/date/index' import type { TConstructor } from '../type/constructor/index' import type { TFunction } from '../type/function/index' +import type { TImport } from '../type/module/index' import type { TInteger } from '../type/integer/index' import type { TIntersect } from '../type/intersect/index' import type { TIterator } from '../type/iterator/index' @@ -110,7 +111,7 @@ export class TypeCheck { return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never } /** Encodes a value or throws if error */ - public Encode, Result extends Static = Static>(value: unknown): Result { + public Encode, Result extends Static = Static>(value: unknown): Result { const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) return encoded as never @@ -288,6 +289,11 @@ export namespace TypeCompiler { function* FromFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } + function* FromImport(schema: TImport, references: TSchema[], value: string): IterableIterator { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + yield* Visit(target, [...references, ...definitions], value) + } function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { yield `Number.isInteger(${value})` if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` @@ -385,8 +391,12 @@ export namespace TypeCompiler { function* FromRef(schema: TRef, references: TSchema[], value: string): IterableIterator { const target = Deref(schema, references) // Reference: If we have seen this reference before we can just yield and return the function call. - // If this isn't the case we defer to visit to generate and set the function for subsequent passes. - if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` + // If this isn't the case we defer to visit to generate and set the _recursion_end_for_ for subsequent + // passes. This operation is very awkward as we are using the functions state to store values to + // enable self referential types to terminate. This needs to be refactored. + const recursiveEnd = `_recursion_end_for_${schema.$ref}` + if (state.functions.has(recursiveEnd)) return yield `${CreateFunctionName(schema.$ref)}(${value})` + state.functions.set(recursiveEnd, '') // terminate recursion here by setting the name. yield* Visit(target, references, value) } function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator { @@ -485,6 +495,8 @@ export namespace TypeCompiler { return yield* FromDate(schema_, references_, value) case 'Function': return yield* FromFunction(schema_, references_, value) + case 'Import': + return yield* FromImport(schema_, references_, value) case 'Integer': return yield* FromInteger(schema_, references_, value) case 'Intersect': diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 44a8b0227..9bb1f6cd1 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -46,6 +46,7 @@ import type { TBoolean } from '../type/boolean/index' import type { TDate } from '../type/date/index' import type { TConstructor } from '../type/constructor/index' import type { TFunction } from '../type/function/index' +import type { TImport } from '../type/module/index' import type { TInteger } from '../type/integer/index' import type { TIntersect } from '../type/intersect/index' import type { TIterator } from '../type/iterator/index' @@ -302,6 +303,11 @@ function* FromDate(schema: TDate, references: TSchema[], path: string, value: an function* FromFunction(schema: TFunction, references: TSchema[], path: string, value: any): IterableIterator { if (!IsFunction(value)) yield Create(ValueErrorType.Function, schema, path, value) } +function* FromImport(schema: TImport, references: TSchema[], path: string, value: any): IterableIterator { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + yield* Visit(target, [...references, ...definitions], path, value) +} function* FromInteger(schema: TInteger, references: TSchema[], path: string, value: any): IterableIterator { if (!IsInteger(value)) return yield Create(ValueErrorType.Integer, schema, path, value) if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { @@ -566,6 +572,8 @@ function* Visit(schema: T, references: TSchema[], path: strin return yield* FromDate(schema_, references_, path, value) case 'Function': return yield* FromFunction(schema_, references_, path, value) + case 'Import': + return yield* FromImport(schema_, references_, path, value) case 'Integer': return yield* FromInteger(schema_, references_, path, value) case 'Intersect': diff --git a/src/index.ts b/src/index.ts index 6a99799a0..90525bd71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,7 +53,6 @@ export * from './type/const/index' export * from './type/constructor/index' export * from './type/constructor-parameters/index' export * from './type/date/index' -export * from './type/deref/index' export * from './type/enum/index' export * from './type/exclude/index' export * from './type/extends/index' @@ -67,6 +66,7 @@ export * from './type/iterator/index' export * from './type/intrinsic/index' export * from './type/keyof/index' export * from './type/literal/index' +export * from './type/module/index' export * from './type/mapped/index' export * from './type/never/index' export * from './type/not/index' @@ -90,7 +90,6 @@ export * from './type/rest/index' export * from './type/return-type/index' export * from './type/schema/index' export * from './type/static/index' -export * from './type/strict/index' export * from './type/string/index' export * from './type/symbol/index' export * from './type/template-literal/index' diff --git a/src/syntax/parse.ts b/src/syntax/parse.ts index 4605b9cbe..1ec0d41fd 100644 --- a/src/syntax/parse.ts +++ b/src/syntax/parse.ts @@ -26,36 +26,35 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import * as Types from '../type/index' import { Static } from './parsebox/index' -import { CreateType } from '../type/create/type' -import { TSchema, SchemaOptions } from '../type/schema/index' -import { StaticDecode } from '../type/static/index' import { Module } from './runtime' -import { Type } from './static' +import { Main } from './static' -/** `[Experimental]` Infers a TypeBox type from TypeScript syntax. */ -export type StaticParseAsSchema, Code extends string> = Static.Parse[0] +/** `[Syntax]` Infers a TypeBox type from TypeScript syntax. */ +export type StaticParseAsSchema, Code extends string> = Static.Parse[0] -/** `[Experimental]` Infers a TypeScript type from TypeScript syntax. */ -export type StaticParseAsType, Code extends string> = StaticParseAsSchema extends infer Type extends TSchema ? StaticDecode : undefined +/** `[Syntax]` Infers a TypeScript type from TypeScript syntax. */ +export type StaticParseAsType, Code extends string> = StaticParseAsSchema extends infer Type extends Types.TSchema ? Types.StaticDecode : undefined -/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ -export function Parse, Code extends string>(context: Context, code: Code, options?: SchemaOptions): StaticParseAsSchema -/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ -export function Parse(code: Code, options?: SchemaOptions): StaticParseAsSchema<{}, Code> -/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */ +/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ +export function Parse, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): StaticParseAsSchema +/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ +export function Parse(code: Code, options?: Types.SchemaOptions): StaticParseAsSchema<{}, Code> +/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ export function Parse(...args: any[]): never { return ParseOnly.apply(null, args as never) as never } -/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ -export function ParseOnly, Code extends string>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined -/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax */ -export function ParseOnly(code: Code, options?: SchemaOptions): TSchema | undefined -/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ -export function ParseOnly(...args: any[]): TSchema | undefined { +/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ +export function ParseOnly, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax */ +export function ParseOnly(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ +export function ParseOnly(...args: any[]): Types.TSchema | undefined { const withContext = typeof args[0] === 'string' ? false : true const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] - const type = Module.Parse('Type', code, context)[0] as TSchema | undefined - return (type !== undefined ? CreateType(type, options) : undefined) as never + const type = Module.Parse('Main', code, context)[0] as Types.TSchema | undefined + // Note: Parsing may return either a ModuleInstance or Type. We only apply options on the Type. + return Types.KindGuard.IsSchema(type) ? Types.CloneType(type, options) : type } diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 0d28dbbf2..15001ee48 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -48,6 +48,7 @@ const SemiColon = ';' const SingleQuote = "'" const DoubleQuote = '"' const Tilde = '`' +const Equals = '=' // ------------------------------------------------------------------ // DestructureRight @@ -59,13 +60,140 @@ function DestructureRight(values: T[]): [T[], T | undefined] { : [values, undefined] } // ------------------------------------------------------------------ -// Reference +// Deref +// ------------------------------------------------------------------ +const Deref = (context: Types.TProperties, key: string): Types.TSchema => { + return key in context ? context[key] : Types.Ref(key) +} +// ------------------------------------------------------------------ +// ExportModifier // ------------------------------------------------------------------ // prettier-ignore -const Reference = Runtime.Ident((value, context: Record) => { - return value in context ? context[value] : Types.Ref(value) -}) +const ExportModifierMapping = (values: unknown[]) => { + return values.length === 1 +} +// prettier-ignore +const ExportModifier = Runtime.Union([ + Runtime.Tuple([Runtime.Const('export')]), Runtime.Tuple([]) +], ExportModifierMapping) + +// ------------------------------------------------------------------ +// TypeAliasDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +const TypeAliasDeclarationMapping = (_Export: boolean, _Keyword: 'type', Ident: string, _Equals: typeof Equals, Type: Types.TSchema) => { + return { [Ident]: Type } +} +// prettier-ignore +const TypeAliasDeclaration = Runtime.Tuple([ + Runtime.Ref('ExportModifier'), + Runtime.Const('type'), + Runtime.Ident(), + Runtime.Const(Equals), + Runtime.Ref('Type') +], value => TypeAliasDeclarationMapping(...value)) + +// ------------------------------------------------------------------ +// HeritageList +// ------------------------------------------------------------------ +// prettier-ignore (note, heritage list should disallow trailing comma) +const HeritageListDelimiter = Runtime.Union([Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), Runtime.Tuple([Runtime.Const(Comma)])]) +// prettier-ignore +const HeritageListMapping = (values: string[], context: Types.TProperties): Types.TSchema[] => { + return ( + values.length === 3 ? [Deref(context, values[0]), ...values[2] as never] : + values.length === 1 ? [Deref(context, values[0])] : + [] + ) as never +} +// prettier-ignore +const HeritageList = Runtime.Union([ + Runtime.Tuple([Runtime.Ident(), HeritageListDelimiter, Runtime.Ref('HeritageList')]), + Runtime.Tuple([Runtime.Ident()]), + Runtime.Tuple([]) +], HeritageListMapping) +// ------------------------------------------------------------------ +// Heritage +// ------------------------------------------------------------------ +// prettier-ignore +const HeritageMapping = (values: unknown[]): unknown[] => { + return (values.length === 2 ? values[1] : []) as never +} +// prettier-ignore +const Heritage = Runtime.Union([ + Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('HeritageList')]), + Runtime.Tuple([]) +], HeritageMapping) +// ------------------------------------------------------------------ +// InterfaceDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +const InterfaceDeclarationMapping = (_0: boolean, _1: 'interface', Ident: string, Heritage: Types.TRef[], _4: typeof LBrace, Properties: Types.TProperties, _6: typeof RBrace) => { + return { [Ident]: Types.Intersect([...Heritage, Types.Object(Properties)]) } +} +// prettier-ignore +const InterfaceDeclaration = Runtime.Tuple([ + Runtime.Ref('ExportModifier'), + Runtime.Const('interface'), + Runtime.Ident(), + Runtime.Ref('Heritage'), + Runtime.Const(LBrace), + Runtime.Ref('Properties'), + Runtime.Const(RBrace), +], values => InterfaceDeclarationMapping(...values)) +// ------------------------------------------------------------------ +// ModuleType +// ------------------------------------------------------------------ +// prettier-ignore +const ModuleType = Runtime.Union([ + Runtime.Ref('InterfaceDeclaration'), + Runtime.Ref('TypeAliasDeclaration') +]) +// ------------------------------------------------------------------ +// ModuleProperties +// ------------------------------------------------------------------ +// prettier-ignore +const ModulePropertiesDelimiter = Runtime.Union([ + Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]), + Runtime.Tuple([Runtime.Const(SemiColon)]), + Runtime.Tuple([Runtime.Const(Newline)]), +]) +// prettier-ignore +const ModulePropertiesMapping = (values: unknown[]): Types.TProperties => { + return ( + values.length === 3 ? { ...values[0] as Types.TProperties, ...values[2] as Types.TProperties }: + values.length === 1 ? values[0] as Types.TProperties : + {} as Types.TProperties + ) +} +// prettier-ignore +const ModuleProperties = Runtime.Union([ + Runtime.Tuple([Runtime.Ref('ModuleType'), ModulePropertiesDelimiter, Runtime.Ref('ModuleProperties')]), + Runtime.Tuple([Runtime.Ref('ModuleType')]), + Runtime.Tuple([]), +], ModulePropertiesMapping) +// ------------------------------------------------------------------ +// ModuleDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +const ModuleIdentifier = Runtime.Union([ + Runtime.Tuple([Runtime.Ident()]), + Runtime.Tuple([]) +]) +// prettier-ignore +const ModuleDeclarationMapping = (_1: boolean, _2: 'module', _Ident: string[], _3: typeof LBrace, Properties: Types.TProperties, _5: typeof RBrace) => { + return Types.Module(Properties) +} +// prettier-ignore +const ModuleDeclaration = Runtime.Tuple([ + Runtime.Ref('ExportModifier'), Runtime.Const('module'), ModuleIdentifier, Runtime.Const(LBrace), Runtime.Ref('ModuleProperties'), Runtime.Const(RBrace) +], values => ModuleDeclarationMapping(...values)) +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +const Reference = Runtime.Ident((value, context: Types.TProperties) => Deref(context, value)) // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ @@ -75,7 +203,6 @@ const Literal = Runtime.Union([ Runtime.Number(value => Types.Literal(parseFloat(value))), Runtime.String([SingleQuote, DoubleQuote, Tilde], value => Types.Literal(value)) ]) - // ------------------------------------------------------------------ // Keyword // ------------------------------------------------------------------ @@ -94,7 +221,6 @@ const Keyword = Runtime.Union([ Runtime.Const('unknown', Runtime.As(Types.Unknown())), Runtime.Const('void', Runtime.As(Types.Void())), ]) - // ------------------------------------------------------------------ // KeyOf // ------------------------------------------------------------------ @@ -106,7 +232,6 @@ const KeyOfMapping = (values: unknown[]) => ( const KeyOf = Runtime.Union([ Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([]) ], KeyOfMapping) - // ------------------------------------------------------------------ // IndexArray // ------------------------------------------------------------------ @@ -137,7 +262,6 @@ const Extends = Runtime.Union([ Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]), Runtime.Tuple([]) ], ExtendsMapping) - // ------------------------------------------------------------------ // Base // ------------------------------------------------------------------ @@ -219,7 +343,6 @@ const Factor = Runtime.Tuple([ Runtime.Ref('IndexArray'), Runtime.Ref('Extends') ], values => FactorMapping(...values)) - // ------------------------------------------------------------------ // Expr // ------------------------------------------------------------------ @@ -266,30 +389,26 @@ const Expr = Runtime.Tuple([ // Type // ------------------------------------------------------------------ const Type = Runtime.Ref('Expr') - // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ -// prettier-ignore const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])]) +const Readonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], (value) => value.length > 0) +const Optional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], (value) => value.length > 0) // prettier-ignore -const PropertyReadonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], value => value.length > 0) -// prettier-ignore -const PropertyOptional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], value => value.length > 0) -// prettier-ignore -const PropertyMapping = (Readonly: boolean, Key: string, Optional: boolean, _: typeof Colon, Type: Types.TSchema) => ({ +const PropertyMapping = (IsReadonly: boolean, Key: string, IsOptional: boolean, _: typeof Colon, Type: Types.TSchema) => ({ [Key]: ( - Readonly && Optional ? Types.ReadonlyOptional(Type) : - Readonly && !Optional ? Types.Readonly(Type) : - !Readonly && Optional ? Types.Optional(Type) : + IsReadonly && IsOptional ? Types.ReadonlyOptional(Type) : + IsReadonly && !IsOptional ? Types.Readonly(Type) : + !IsReadonly && IsOptional ? Types.Optional(Type) : Type ) }) // prettier-ignore const Property = Runtime.Tuple([ - Runtime.Ref('PropertyReadonly'), + Runtime.Ref('Readonly'), Runtime.Ref('PropertyKey'), - Runtime.Ref('PropertyOptional'), + Runtime.Ref('Optional'), Runtime.Const(Colon), Runtime.Ref('Type'), ], value => PropertyMapping(...value)) @@ -303,30 +422,27 @@ const PropertyDelimiter = Runtime.Union([ ]) // prettier-ignore const Properties = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), - Runtime.Tuple([Runtime.Ref('Property')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), + Runtime.Tuple([Runtime.Ref('Property')]), Runtime.Tuple([]) ], values => ( - values.length === 3 ? [values[0], ...values[2] as unknown[]] : - values.length === 2 ? [values[0]] : - values.length === 1 ? [values[0]] : - [] + values.length === 3 ? { ...values[0], ...values[2] } : + values.length === 2 ? values[0] : + values.length === 1 ? values[0] : + {} )) - // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ // prettier-ignore -const ObjectMapping = (values: Record[]) => Types.Object(values.reduce((properties, record) => { - return { ...properties, ...record } -}, {} as Types.TProperties)) +const ObjectMapping = (_0: typeof LBrace, Properties: Types.TProperties, _2: typeof RBrace) => Types.Object(Properties) // prettier-ignore const _Object = Runtime.Tuple([ Runtime.Const(LBrace), - Runtime.Ref[]>('Properties'), + Runtime.Ref('Properties'), Runtime.Const(RBrace) -], values => ObjectMapping(values[1])) +], values => ObjectMapping(...values)) // ------------------------------------------------------------------ // Tuple @@ -401,7 +517,15 @@ const MappedMapping = (values: unknown[]) => { } // prettier-ignore const Mapped = Runtime.Tuple([ - Runtime.Const(LBrace), Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), Runtime.Ref('Type'), Runtime.Const(RBrace) + Runtime.Const(LBrace), + Runtime.Const(LBracket), + Runtime.Ident(), + Runtime.Const('in'), + Runtime.Ref('Type'), + Runtime.Const(RBracket), + Runtime.Const(Colon), + Runtime.Ref('Type'), + Runtime.Const(RBrace) ], MappedMapping) // ------------------------------------------------------------------ // AsyncIterator @@ -621,11 +745,37 @@ const Date = Runtime.Const('Date', Runtime.As(Types.Date())) // Uint8Array // ------------------------------------------------------------------ const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(Types.Uint8Array())) + +// ------------------------------------------------------------------ +// Main +// ------------------------------------------------------------------ +// prettier-ignore +const Main = Runtime.Union([ + ModuleDeclaration, + TypeAliasDeclaration, + InterfaceDeclaration, + Type +]) // ------------------------------------------------------------------ // Module // ------------------------------------------------------------------ // prettier-ignore export const Module = new Runtime.Module({ + // ---------------------------------------------------------------- + // Modules, Interfaces and Type Aliases + // ---------------------------------------------------------------- + ExportModifier, + HeritageList, + Heritage, + InterfaceDeclaration, + TypeAliasDeclaration, + ModuleType, + ModuleProperties, + ModuleDeclaration, + + // ---------------------------------------------------------------- + // Type Expressions + // ---------------------------------------------------------------- Literal, Keyword, KeyOf, @@ -637,10 +787,10 @@ export const Module = new Runtime.Module({ ExprTerm, ExprTail, Expr, - Type, + Type, // Alias for Expr PropertyKey, - PropertyReadonly, - PropertyOptional, + Readonly, + Optional, Property, PropertyDelimiter, Properties, @@ -674,5 +824,10 @@ export const Module = new Runtime.Module({ Uncapitalize, Date, Uint8Array, - Reference + Reference, + + // ---------------------------------------------------------------- + // Main + // ---------------------------------------------------------------- + Main }) diff --git a/src/syntax/static.ts b/src/syntax/static.ts index ff75dca2e..0b30b8ead 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -48,6 +48,7 @@ type SemiColon = ';' type SingleQuote = "'" type DoubleQuote = '"' type Tilde = '`' +type Equals = '=' // ------------------------------------------------------------------ // Delimit @@ -135,18 +136,156 @@ type Delimit = ], DelimitMapping> ) // ------------------------------------------------------------------ -// Reference +// Deref // ------------------------------------------------------------------ // prettier-ignore -interface ReferenceMapping extends Static.IMapping { - output: this['input'] extends [infer Key extends string] - ? Key extends keyof this['context'] - ? this['context'][Key] - : Types.TRef +type Deref = ( + Ref extends keyof Context ? Context[Ref] : Types.TRef +) +// ------------------------------------------------------------------ +// ExportModifier +// ------------------------------------------------------------------ +// prettier-ignore +interface ExportModifierMapping extends Static.IMapping { + output: this['input'] extends [string] ? true : false +} +// prettier-ignore +type ExportModifier = Static.Union<[ + Static.Tuple<[Static.Const<'export'>]>, + Static.Tuple<[]>, +], ExportModifierMapping> + +// ------------------------------------------------------------------ +// TypeAliasDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +interface TypeAliasDeclarationMapping extends Static.IMapping { + output: this['input'] extends [infer _Export extends boolean, 'type', infer Ident extends string, Equals, infer Type extends Types.TSchema] + ? { [_ in Ident]: Type } : never } -type Reference = Static.Tuple<[Static.Ident], ReferenceMapping> +// prettier-ignore +type TypeAliasDeclaration = Static.Tuple<[ + ExportModifier, Static.Const<'type'>, Static.Ident, Static.Const, Type +], TypeAliasDeclarationMapping> +// ------------------------------------------------------------------ +// HeritageList +// ------------------------------------------------------------------ +// prettier-ignore (note, heritage list should disallow trailing comma) +type HeritageListDelimiter = Static.Union<[Static.Tuple<[Static.Const, Static.Const]>, Static.Tuple<[Static.Const]>]> +// prettier-ignore +type HeritageListReduce = ( + Values extends [infer Ref extends string, ...infer Rest extends string[]] + ? HeritageListReduce]> + : Result +) +// prettier-ignore +interface HeritageListMapping extends Static.IMapping { + output: ( + this['context'] extends Types.TProperties ? + this['input'] extends string[] + ? HeritageListReduce + : [] + : [] + ) +} +// prettier-ignore +type HeritageList = Static.Union<[Delimit], HeritageListMapping> +// ------------------------------------------------------------------ +// Heritage +// ------------------------------------------------------------------ +// prettier-ignore +interface HeritageMapping extends Static.IMapping { + output: this['input'] extends ['extends', infer List extends Types.TSchema[]] ? List : [] +} +// prettier-ignore +type Heritage = Static.Union<[ + Static.Tuple<[Static.Const<'extends'>, HeritageList]>, + Static.Tuple<[]> +], HeritageMapping> +// ------------------------------------------------------------------ +// InterfaceDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +interface InterfaceDeclarationMapping extends Static.IMapping { + output: this['input'] extends [boolean, 'interface', infer Ident extends string, infer Heritage extends Types.TSchema[], LBrace, infer Properties extends Types.TProperties, RBrace] + ? { [_ in Ident]: Types.TIntersectEvaluated<[...Heritage, Types.TObject]> } + : never +} +// prettier-ignore +type InterfaceDeclaration = Static.Tuple<[ + ExportModifier, + Static.Const<'interface'>, + Static.Ident, + Heritage, + Static.Const, + Properties, + Static.Const, +], InterfaceDeclarationMapping> +// ------------------------------------------------------------------ +// ModuleType +// ------------------------------------------------------------------ +// prettier-ignore +type ModuleType = Static.Union<[ + InterfaceDeclaration, + TypeAliasDeclaration +]> +// ------------------------------------------------------------------ +// ModuleProperties +// ------------------------------------------------------------------ +// prettier-ignore +type ModulePropertiesDelimiter = Static.Union<[ + Static.Tuple<[Static.Const, Static.Const]>, + Static.Tuple<[Static.Const]>, + Static.Tuple<[Static.Const]>, +]> +// prettier-ignore +type ModulePropertiesReduce = ( + Value extends [infer ModuleType extends Types.TProperties, unknown[], ...infer Rest extends unknown[]] ? ModulePropertiesReduce : + Value extends [infer ModuleType extends Types.TProperties] ? ModulePropertiesReduce<[], Result & ModuleType> : + Types.Evaluate +) +// prettier-ignore +interface ModulePropertiesMapping extends Static.IMapping { + output: this['input'] extends unknown[] ? ModulePropertiesReduce : never +} +// prettier-ignore +type ModuleProperties = Static.Union<[ + Static.Tuple<[ModuleType, ModulePropertiesDelimiter, ModuleProperties]>, + Static.Tuple<[ModuleType]>, + Static.Tuple<[]>, +], ModulePropertiesMapping> +// ------------------------------------------------------------------ +// ModuleDeclaration +// ------------------------------------------------------------------ +// prettier-ignore +type ModuleIdentifier = Static.Union<[ + Static.Tuple<[Static.Ident]>, + Static.Tuple<[]> +]> +// prettier-ignore +interface ModuleDeclarationMapping extends Static.IMapping { + output: this['input'] extends [boolean, 'module', infer _Ident extends string[], LBrace, infer Properties extends Types.TProperties, RBrace] + ? Types.TModule + : never +} +// prettier-ignore +type ModuleDeclaration = Static.Tuple<[ + ExportModifier, Static.Const<'module'>, ModuleIdentifier, Static.Const, ModuleProperties, Static.Const +], ModuleDeclarationMapping> +// ------------------------------------------------------------------ +// Reference +// ------------------------------------------------------------------ +// prettier-ignore +interface ReferenceMapping extends Static.IMapping { + output: this['context'] extends Types.TProperties + ? this['input'] extends string + ? Deref + : never + : never +} +type Reference = Static.Ident // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ @@ -198,7 +337,6 @@ type KeyOf = Static.Union<[ Static.Tuple<[Static.Const<'keyof'>]>, Static.Tuple<[]> ], KeyOfMapping> - // ------------------------------------------------------------------ // IndexArray // ------------------------------------------------------------------ @@ -231,7 +369,6 @@ type Extends = Static.Union<[ Static.Tuple<[Static.Const<'extends'>, Type, Static.Const, Type, Static.Const, Type]>, Static.Tuple<[]> ], ExtendsMapping> - // ------------------------------------------------------------------ // Base // ------------------------------------------------------------------ @@ -361,7 +498,7 @@ type Expr = Static.Tuple<[ // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ -export type Type = Expr +type Type = Expr // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ @@ -370,34 +507,30 @@ interface PropertyKeyStringMapping extends Static.IMapping { output: this['input'] } type PropertyKeyString = Static.String<[SingleQuote, DoubleQuote], PropertyKeyStringMapping> - type PropertyKey = Static.Union<[Static.Ident, PropertyKeyString]> // prettier-ignore -interface PropertyReadonlyMapping extends Static.IMapping { +interface ReadonlyMapping extends Static.IMapping { output: this['input'] extends ['readonly'] ? true : false } -type PropertyReadonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], PropertyReadonlyMapping> +type Readonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], ReadonlyMapping> // prettier-ignore -interface PropertyOptionalMapping extends Static.IMapping { +interface OptionalMapping extends Static.IMapping { output: this['input'] extends [Question] ? true : false } -type PropertyOptional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], PropertyOptionalMapping> +type Optional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], OptionalMapping> // prettier-ignore interface PropertyMapping extends Static.IMapping { - output: this['input'] extends [infer Readonly extends boolean, infer Key extends string, infer Optional extends boolean, string, infer Type extends Types.TSchema] + output: this['input'] extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends Types.TSchema] ? { [_ in Key]: ( - [Readonly, Optional] extends [true, true] ? Types.TReadonlyOptional : - [Readonly, Optional] extends [true, false] ? Types.TReadonly : - [Readonly, Optional] extends [false, true] ? Types.TOptional : + [IsReadonly, IsOptional] extends [true, true] ? Types.TReadonlyOptional : + [IsReadonly, IsOptional] extends [true, false] ? Types.TReadonly : + [IsReadonly, IsOptional] extends [false, true] ? Types.TOptional : Type ) } : never } - -type Property = Static.Tuple<[PropertyReadonly, PropertyKey, PropertyOptional, Static.Const, Type], PropertyMapping> - -type PropertiesEvaluate = { [K in keyof T]: T[K] } & {} +type Property = Static.Tuple<[Readonly, PropertyKey, Optional, Static.Const, Type], PropertyMapping> // prettier-ignore type PropertyDelimiter = Static.Union<[ Static.Tuple<[Static.Const, Static.Const]>, @@ -409,7 +542,7 @@ type PropertyDelimiter = Static.Union<[ // prettier-ignore type PropertiesReduce = ( PropertiesArray extends [infer Left extends Types.TProperties, ...infer Right extends Types.TProperties[]] - ? PropertiesReduce> + ? PropertiesReduce> : Result ) // prettier-ignore @@ -485,7 +618,7 @@ type Constructor = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface MappedMapping extends Static.IMapping { - output: this['input'] extends [LBrace, LBracket, infer Key extends string, 'in', infer Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace] + output: this['input'] extends [LBrace, LBracket, infer _Key extends string, 'in', infer _Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace] ? Types.TLiteral<'Mapped types not supported'> : this['input'] } @@ -762,3 +895,14 @@ type Date = Static.Const<'Date', Static.As> // Uint8Array // ------------------------------------------------------------------ type Uint8Array = Static.Const<'Uint8Array', Static.As> + +// ------------------------------------------------------------------ +// Main +// ------------------------------------------------------------------ +// prettier-ignore +export type Main = Static.Union<[ + ModuleDeclaration, + TypeAliasDeclaration, + InterfaceDeclaration, + Type +]> diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts deleted file mode 100644 index 76d626a29..000000000 --- a/src/type/deref/deref.ts +++ /dev/null @@ -1,174 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/type - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import type { TSchema } from '../schema/index' -import type { Evaluate } from '../helpers/index' -import type { TTuple } from '../tuple/index' -import type { TIntersect } from '../intersect/index' -import type { TUnion } from '../union/index' -import type { TPromise } from '../promise/index' -import type { TAsyncIterator } from '../async-iterator/index' -import type { TIterator } from '../iterator/index' -import type { TArray } from '../array/index' -import type { TConstructor } from '../constructor/index' -import type { TFunction } from '../function/index' -import type { TRef } from '../ref/index' -import type { TObject, TProperties } from '../object/index' -import { CloneType, CloneRest } from '../clone/type' -import { Discard } from '../discard/index' -import { IsUndefined } from '../guard/value' - -// ------------------------------------------------------------------ -// TypeGuard -// ------------------------------------------------------------------ -import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsPromise, IsAsyncIterator, IsIterator, IsRef } from '../guard/kind' -// ------------------------------------------------------------------ -// FromRest -// ------------------------------------------------------------------ -// prettier-ignore -export type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc -) -function FromRest(schema: [...T], references: TSchema[]): TFromRest { - return schema.map((schema) => Deref(schema, references)) as never -} -// ------------------------------------------------------------------ -// FromProperties -// ------------------------------------------------------------------ -// prettier-ignore -type FromProperties = Evaluate<{ - [K in keyof T]: TDeref -}> -// prettier-ignore -function FromProperties(properties: TProperties, references: TSchema[]) { - const Acc = {} as TProperties - for(const K of globalThis.Object.getOwnPropertyNames(properties)) { - Acc[K] = Deref(properties[K], references) - } - return Acc as never -} -// prettier-ignore -function FromConstructor(schema: TConstructor, references: TSchema[]) { - schema.parameters = FromRest(schema.parameters, references) - schema.returns = Deref(schema.returns, references) - return schema -} -// prettier-ignore -function FromFunction(schema: TFunction, references: TSchema[]) { - schema.parameters = FromRest(schema.parameters, references) - schema.returns = Deref(schema.returns, references) - return schema -} -// prettier-ignore -function FromIntersect(schema: TIntersect, references: TSchema[]) { - schema.allOf = FromRest(schema.allOf, references) - return schema -} -// prettier-ignore -function FromUnion(schema: TUnion, references: TSchema[]) { - schema.anyOf = FromRest(schema.anyOf, references) - return schema -} -// prettier-ignore -function FromTuple(schema: TTuple, references: TSchema[]) { - if(IsUndefined(schema.items)) return schema - schema.items = FromRest(schema.items, references) - return schema -} -// prettier-ignore -function FromArray(schema: TArray, references: TSchema[]) { - schema.items = Deref(schema.items, references) - return schema -} -// prettier-ignore -function FromObject(schema: TObject, references: TSchema[]) { - schema.properties = FromProperties(schema.properties, references) - return schema -} -// prettier-ignore -function FromPromise(schema: TPromise, references: TSchema[]) { - schema.item = Deref(schema.item, references) - return schema -} -// prettier-ignore -function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { - schema.items = Deref(schema.items, references) - return schema -} -// prettier-ignore -function FromIterator(schema: TIterator, references: TSchema[]) { - schema.items = Deref(schema.items, references) - return schema -} -// prettier-ignore -function FromRef(schema: TRef, references: TSchema[]) { - const target = references.find(remote => remote.$id === schema.$ref) - if(target === undefined) throw Error(`Unable to dereference schema with $id ${schema.$ref}`) - const discard = Discard(target, ['$id']) as TSchema - return Deref(discard, references) -} -// prettier-ignore -function DerefResolve(schema: T, references: TSchema[]): TDeref { - return ( - IsConstructor(schema) ? FromConstructor(schema, references) : - IsFunction(schema) ? FromFunction(schema, references) : - IsIntersect(schema) ? FromIntersect(schema, references) : - IsUnion(schema) ? FromUnion(schema, references) : - IsTuple(schema) ? FromTuple(schema, references) : - IsArray(schema) ? FromArray(schema, references) : - IsObject(schema) ? FromObject(schema, references) : - IsPromise(schema) ? FromPromise(schema, references) : - IsAsyncIterator(schema) ? FromAsyncIterator(schema, references) : - IsIterator(schema) ? FromIterator(schema, references) : - IsRef(schema) ? FromRef(schema, references) : - schema - ) as never -} -// prettier-ignore -export type TDeref = - T extends TConstructor ? TConstructor, TDeref> : - T extends TFunction ? TFunction, TDeref> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TTuple ? TTuple> : - T extends TObject ? TObject> : - T extends TArray ? TArray> : - T extends TPromise ? TPromise> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TIterator ? TIterator> : - T extends TRef ? TDeref : - T -// ------------------------------------------------------------------ -// TDeref -// ------------------------------------------------------------------ -/** `[Json]` Creates a dereferenced type */ -export function Deref(schema: T, references: TSchema[]): TDeref { - return DerefResolve(CloneType(schema), CloneRest(references)) -} diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index b68f01bca..2e61a1df9 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -40,6 +40,7 @@ import type { TAsyncIterator } from '../async-iterator/index' import type { TBigInt } from '../bigint/index' import type { TConstructor } from '../constructor/index' import type { TFunction } from '../function/index' +import type { TImport } from '../module/index' import type { TInteger } from '../integer/index' import type { TIntersect } from '../intersect/index' import type { TIterator } from '../iterator/index' @@ -107,6 +108,10 @@ export function IsFunction(value: unknown): value is TFunction { return IsKindOf(value, 'Function') } /** `[Kind-Only]` Returns true if the given value is TInteger */ +export function IsImport(value: unknown): value is TImport { + return IsKindOf(value, 'Import') +} +/** `[Kind-Only]` Returns true if the given value is TInteger */ export function IsInteger(value: unknown): value is TInteger { return IsKindOf(value, 'Integer') } diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index b2e9daa9a..ed8eb54fe 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -41,6 +41,7 @@ import type { TAsyncIterator } from '../async-iterator/index' import type { TBigInt } from '../bigint/index' import type { TConstructor } from '../constructor/index' import type { TFunction } from '../function/index' +import type { TImport } from '../module/index' import type { TInteger } from '../integer/index' import type { TIntersect } from '../intersect/index' import type { TIterator } from '../iterator/index' @@ -253,6 +254,19 @@ export function IsFunction(value: unknown): value is TFunction { IsSchema(value.returns) ) } +/** Returns true if the given value is TImport */ +export function IsImport(value: unknown): value is TImport { + // prettier-ignore + return ( + IsKindOf(value, 'Import') && + ValueGuard.HasPropertyKey(value, '$defs') && + ValueGuard.IsObject(value.$defs) && + IsProperties(value.$defs) && + ValueGuard.HasPropertyKey(value, '$ref') && + ValueGuard.IsString(value.$ref) && + value.$ref in value.$defs // required + ) +} /** Returns true if the given value is TInteger */ export function IsInteger(value: unknown): value is TInteger { return ( diff --git a/src/type/index.ts b/src/type/index.ts index b69a1fa72..d9d5d5076 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -38,7 +38,6 @@ export * from './const/index' export * from './constructor/index' export * from './constructor-parameters/index' export * from './date/index' -export * from './deref/index' export * from './discard/index' export * from './enum/index' export * from './error/index' @@ -57,6 +56,7 @@ export * from './iterator/index' export * from './keyof/index' export * from './literal/index' export * from './mapped/index' +export * from './module/index' export * from './never/index' export * from './not/index' export * from './null/index' @@ -82,7 +82,6 @@ export * from './return-type/index' export * from './schema/index' export * from './sets/index' export * from './static/index' -export * from './strict/index' export * from './string/index' export * from './symbol/index' export * from './symbols/index' diff --git a/src/type/deref/index.ts b/src/type/module/index.ts similarity index 98% rename from src/type/deref/index.ts rename to src/type/module/index.ts index 28c03fc50..ce46d5a0c 100644 --- a/src/type/deref/index.ts +++ b/src/type/module/index.ts @@ -26,4 +26,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './deref' +export * from './module' diff --git a/src/type/module/module.ts b/src/type/module/module.ts new file mode 100644 index 000000000..3fef83c80 --- /dev/null +++ b/src/type/module/module.ts @@ -0,0 +1,189 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Ensure } from '../helpers/index' +import { CreateType } from '../create/index' +import { Kind } from '../symbols/index' +import { SchemaOptions, TSchema } from '../schema/index' +import { TObject, TProperties } from '../object/index' +import { TConstructor } from '../constructor/index' +import { TFunction } from '../function/index' +import { TTuple } from '../tuple/index' +import { TIntersect } from '../intersect/index' +import { TUnion } from '../union/index' +import { TArray } from '../array/index' +import { TAsyncIterator } from '../async-iterator/index' +import { TIterator } from '../iterator/index' +import { TLiteral, TLiteralValue } from '../literal/index' +import { TAny } from '../any/index' +import { TBigInt } from '../bigint/index' +import { TBoolean } from '../boolean/index' +import { TDate } from '../date/index' +import { TInteger } from '../integer/index' +import { TNever } from '../never/index' +import { TNumber } from '../number/index' +import { TNull } from '../null/index' +import { TRef } from '../ref/index' +import { TRegExp } from '../regexp/index' +import { TString } from '../string/index' +import { TSymbol } from '../symbol/index' +import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' +import { TUint8Array } from '../uint8array/index' +import { TUndefined } from '../undefined/index' +import { TUnknown } from '../unknown/index' +import { TVoid } from '../void/index' +import { Static } from '../static/index' + +// ------------------------------------------------------------------ +// Infer +// ------------------------------------------------------------------ +// prettier-ignore +type InferImport = ( + Infer +) +// prettier-ignore +type InferRef = ( + Ref extends keyof Properties ? Infer : never +) +// prettier-ignore +type InferObject = { + [K in keyof Properties]: Infer +} & {} +// prettier-ignore +type InferConstructor = Ensure< + new (...args: InferTuple) => Infer +> +// prettier-ignore +type InferFunction = Ensure< + (...args: InferTuple) => Infer +> +// prettier-ignore +type InferTuple = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? InferTuple]> + : Result +) +// prettier-ignore +type InferIntersect = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? InferIntersect> + : Result +) +// prettier-ignore +type InferUnion = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? InferUnion> + : Result +) +// prettier-ignore +type InferArray = ( + Ensure>> +) +// prettier-ignore +type InferAsyncIterator = ( + Ensure>> +) +// prettier-ignore +type InferIterator = ( + Ensure>> +) +// prettier-ignore +type Infer = ( + Type extends TImport ? InferImport : + Type extends TRef ? InferRef : + Type extends TObject ? InferObject : + Type extends TConstructor ? InferConstructor : + Type extends TFunction ? InferFunction : + Type extends TTuple ? InferTuple : + Type extends TIntersect ? InferIntersect : + Type extends TUnion ? InferUnion : + Type extends TArray ? InferArray : + Type extends TAsyncIterator ? InferAsyncIterator : + Type extends TIterator ? InferIterator : + Type extends TTemplateLiteral ? Static> : + Type extends TLiteral ? S : + Type extends TAny ? any : + Type extends TBigInt ? bigint : + Type extends TBoolean ? boolean : + Type extends TDate ? Date : + Type extends TInteger ? number : + Type extends TNever ? never : + Type extends TNumber ? number : + Type extends TRegExp ? string : + Type extends TString ? string : + Type extends TSymbol ? symbol : + Type extends TNull ? null : + Type extends TUint8Array ? Uint8Array : + Type extends TUndefined ? undefined : + Type extends TUnknown ? unknown : + Type extends TVoid ? void : + never +) +// ------------------------------------------------------------------ +// Definitions +// ------------------------------------------------------------------ +export interface TDefinitions extends TSchema { + static: { [K in keyof ModuleProperties]: Static } + $defs: ModuleProperties +} +// ------------------------------------------------------------------ +// Import +// ------------------------------------------------------------------ +// prettier-ignore +export interface TImport extends TSchema { + [Kind]: 'Import' + static: InferImport + $defs: ModuleProperties + $ref: Key +} +// ------------------------------------------------------------------ +// Module +// ------------------------------------------------------------------ +// prettier-ignore +export class TModule { + constructor(private readonly $defs: Properties, private readonly options: SchemaOptions = {}) {} + + /** `[Json]` Returns the Type definitions for this module */ + public Defs(): TDefinitions { + return CreateType({ $defs: this.ResolveDefinitionsWithIdentifiers() }, this.options) as never + } + /** `[Json]` Imports a Type by Key. */ + public Import(key: Key, options?: SchemaOptions): TImport { + return CreateType({ [Kind]: 'Import', $defs: this.ResolveDefinitionsWithIdentifiers(), $ref: key }, options) as never + } + /** `[Internal]` For each definition, assign an `$id` property. */ + private ResolveDefinitionsWithIdentifiers() { + return globalThis.Object.getOwnPropertyNames(this.$defs).reduce((Result, Key) => ( + { ...Result, [Key]: { ...this.$defs[Key], $id: Key }} + ), {}) + } +} +/** `[Json]` Creates a Type Definition Module. */ +export function Module(properties: Properties): TModule { + return new TModule(properties) +} diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index 3edd35b3d..f61f04fac 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -26,29 +26,19 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { CreateType } from '../create/type' import type { TSchema, SchemaOptions } from '../schema/index' -import type { Static } from '../static/index' +import { CreateType } from '../create/type' import { Kind } from '../symbols/index' -// ------------------------------------------------------------------ -// ValueGuard -// ------------------------------------------------------------------ -import { IsString, IsUndefined } from '../guard/value' + // ------------------------------------------------------------------ // TRef // ------------------------------------------------------------------ -export interface TRef extends TSchema { +export interface TRef extends TSchema { [Kind]: 'Ref' - static: Static - $ref: string + static: unknown + $ref: Ref } /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ -export function Ref(schema: T, options?: SchemaOptions): TRef -/** `[Json]` Creates a Ref type. */ -export function Ref($ref: string, options?: SchemaOptions): TRef -/** `[Json]` Creates a Ref type. */ -export function Ref(unresolved: TSchema | string, options?: SchemaOptions) { - if (IsString(unresolved)) return CreateType({ [Kind]: 'Ref', $ref: unresolved }, options) - if (IsUndefined(unresolved.$id)) throw new Error('Reference target type must specify an $id') - return CreateType({ [Kind]: 'Ref', $ref: unresolved.$id! }, options) +export function Ref($ref: Ref, options?: SchemaOptions): TRef { + return CreateType({ [Kind]: 'Ref', $ref }, options) as never } diff --git a/src/type/static/static.ts b/src/type/static/static.ts index 7e346b7ac..2ba1f1bdc 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -77,7 +77,7 @@ export type TDecodeType = ( T extends TPromise ? TPromise> : T extends TRecord ? TRecord> : T extends TRecursive ? TRecursive> : - T extends TRef ? TRef> : + T extends TRef ? TRef : T extends TTuple ? TTuple> : T extends TUnion ? TUnion> : T diff --git a/src/type/strict/index.ts b/src/type/strict/index.ts deleted file mode 100644 index 24555f714..000000000 --- a/src/type/strict/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/type - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './strict' diff --git a/src/type/strict/strict.ts b/src/type/strict/strict.ts deleted file mode 100644 index 96da5a1e1..000000000 --- a/src/type/strict/strict.ts +++ /dev/null @@ -1,44 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/type - -The MIT License (MIT) - -Copyright (c) 2017-2024 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import type { TSchema } from '../schema/index' - -export type TStrict = T - -/** - * @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended - * to use the JSON parse/stringify to remove compositing symbols if needed. This - * is how Strict works internally. - * - * ```typescript - * JSON.parse(JSON.stringify(Type.String())) - * ``` - */ -export function Strict(schema: T): TStrict { - return JSON.parse(JSON.stringify(schema)) -} diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 0650ebc96..7613dc3f4 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -31,7 +31,6 @@ import { Array, type TArray, type ArrayOptions } from '../array/index' import { Boolean, type TBoolean } from '../boolean/index' import { Composite, type TComposite } from '../composite/index' import { Const, type TConst } from '../const/index' -import { Deref, type TDeref } from '../deref/index' import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index' import { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from '../exclude/index' import { Extends, type TExtends, type TExtendsFromMappedKey, type TExtendsFromMappedResult } from '../extends/index' @@ -47,6 +46,7 @@ import { Never, type TNever } from '../never/index' import { Not, type TNot } from '../not/index' import { Null, type TNull } from '../null/index' import { type TMappedKey } from '../mapped/index' +import { Module, TModule } from '../module/index' import { Number, type TNumber, type NumberOptions } from '../number/index' import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' import { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from '../omit/index' @@ -61,7 +61,6 @@ import { Ref, type TRef } from '../ref/index' import { Required, type TRequired, type TRequiredFromMappedResult } from '../required/index' import { Rest, type TRest } from '../rest/index' import { type TSchema, type SchemaOptions } from '../schema/index' -import { Strict, type TStrict } from '../strict/index' import { String, type TString, type StringOptions } from '../string/index' import { TemplateLiteral, type TTemplateLiteral, type TTemplateLiteralKind, type TTemplateLiteralSyntax } from '../template-literal/index' import { Transform, TransformDecodeBuilder } from '../transform/index' @@ -72,21 +71,6 @@ import { Unsafe, type TUnsafe, type UnsafeOptions } from '../unsafe/index' /** Json Type Builder with Static Resolution for TypeScript */ export class JsonTypeBuilder { - // ------------------------------------------------------------------------ - // Strict - // ------------------------------------------------------------------------ - /** - * @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended - * to use the JSON parse/stringify to remove compositing symbols if needed. This - * is how Strict works internally. - * - * ```typescript - * JSON.parse(JSON.stringify(Type.String())) - * ``` - */ - public Strict(schema: T): TStrict { - return Strict(schema) - } // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ @@ -145,10 +129,6 @@ export class JsonTypeBuilder { public Const(value: T, options?: SchemaOptions): TConst { return Const(value, options) } - /** `[Json]` Creates a dereferenced type */ - public Deref(schema: T, references: TSchema[]): TDeref { - return Deref(schema, references) - } /** `[Json]` Creates a Enum type */ public Enum>(item: T, options?: SchemaOptions): TEnum { return Enum(item, options) @@ -227,6 +207,10 @@ export class JsonTypeBuilder { public Mapped(key: any, map: TMappedFunction, options?: ObjectOptions): any { return Mapped(key, map, options) } + /** `[Json]` Creates a Type Definition Module. */ + public Module(properties: Properties): TModule { + return Module(properties) + } /** `[Json]` Creates a Never type */ public Never(options?: SchemaOptions): TNever { return Never(options) @@ -287,13 +271,9 @@ export class JsonTypeBuilder { public Recursive(callback: (thisType: TThis) => T, options?: SchemaOptions): TRecursive { return Recursive(callback, options) } - /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ - public Ref(schema: T, options?: SchemaOptions): TRef - /** `[Json]` Creates a Ref type. */ - public Ref($ref: string, options?: SchemaOptions): TRef /** `[Json]` Creates a Ref type. */ - public Ref(unresolved: TSchema | string, options?: SchemaOptions) { - return Ref(unresolved as any, options) + public Ref($ref: Ref, options?: SchemaOptions): TRef { + return Ref($ref, options) } /** `[Json]` Constructs a type where all properties are required */ public Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult diff --git a/src/type/type/type.ts b/src/type/type/type.ts index 8dd7cc893..d00686cc6 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -40,7 +40,6 @@ export { Const } from '../const/index' export { Constructor } from '../constructor/index' export { ConstructorParameters } from '../constructor-parameters/index' export { Date } from '../date/index' -export { Deref } from '../deref/index' export { Enum } from '../enum/index' export { Exclude } from '../exclude/index' export { Extends } from '../extends/index' @@ -55,6 +54,7 @@ export { Iterator } from '../iterator/index' export { KeyOf } from '../keyof/index' export { Literal } from '../literal/index' export { Mapped } from '../mapped/index' +export { Module } from '../module/index' export { Never } from '../never/index' export { Not } from '../not/index' export { Null } from '../null/index' @@ -76,7 +76,6 @@ export { Required } from '../required/index' export { Rest } from '../rest/index' export { ReturnType } from '../return-type/index' export { String } from '../string/index' -export { Strict } from '../strict/index' export { Symbol } from '../symbol/index' export { TemplateLiteral } from '../template-literal/index' export { Transform } from '../transform/index' diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 78ac75433..a6f9d9891 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -32,12 +32,13 @@ import { Kind } from '../../type/symbols/index' import { Create } from '../create/index' import { Check } from '../check/index' import { Clone } from '../clone/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import type { TSchema } from '../../type/schema/index' import type { Static } from '../../type/static/index' import type { TArray } from '../../type/array/index' import type { TConstructor } from '../../type/constructor/index' +import type { TImport } from '../../type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TObject } from '../../type/object/index' import type { TRecord } from '../../type/record/index' @@ -133,6 +134,11 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any } return result } +function FromImport(schema: TImport, references: TSchema[], value: unknown): boolean { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions], value) +} function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { const created = Create(schema, references) const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value @@ -187,7 +193,7 @@ function FromUnion(schema: TUnion, references: TSchema[], value: any): any { return Check(schema, references, value) ? Clone(value) : CastUnion(schema, references, value) } function Visit(schema: TSchema, references: TSchema[], value: any): any { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? Pushref(schema, references) : references const schema_ = schema as any switch (schema[Kind]) { // -------------------------------------------------------------- @@ -197,6 +203,8 @@ function Visit(schema: TSchema, references: TSchema[], value: any): any { return FromArray(schema_, references_, value) case 'Constructor': return FromConstructor(schema_, references_, value) + case 'Import': + return FromImport(schema_, references_, value) case 'Intersect': return FromIntersect(schema_, references_, value) case 'Never': diff --git a/src/value/check/check.ts b/src/value/check/check.ts index 436c0f047..b1af0dd26 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -27,7 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeSystemPolicy } from '../../system/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Hash } from '../hash/index' import { Kind } from '../../type/symbols/index' import { KeyOfPattern } from '../../type/keyof/index' @@ -48,6 +48,7 @@ import type { TInteger } from '../../type/integer/index' import type { TIntersect } from '../../type/intersect/index' import type { TIterator } from '../../type/iterator/index' import type { TLiteral } from '../../type/literal/index' +import type { TImport } from '../../type/module/index' import { Never, type TNever } from '../../type/never/index' import type { TNot } from '../../type/not/index' import type { TNull } from '../../type/null/index' @@ -185,6 +186,11 @@ function FromDate(schema: TDate, references: TSchema[], value: any): boolean { function FromFunction(schema: TFunction, references: TSchema[], value: any): boolean { return IsFunction(value) } +function FromImport(schema: TImport, references: TSchema[], value: any): boolean { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions], value) +} function FromInteger(schema: TInteger, references: TSchema[], value: any): boolean { if (!IsInteger(value)) { return false @@ -415,7 +421,7 @@ function FromKind(schema: TSchema, references: TSchema[], value: unknown): boole return func(schema, value) } function Visit(schema: T, references: TSchema[], value: any): boolean { - const references_ = IsDefined(schema.$id) ? [...references, schema] : references + const references_ = IsDefined(schema.$id) ? Pushref(schema, references) : references const schema_ = schema as any switch (schema_[Kind]) { case 'Any': @@ -434,6 +440,8 @@ function Visit(schema: T, references: TSchema[], value: any): return FromDate(schema_, references_, value) case 'Function': return FromFunction(schema_, references_, value) + case 'Import': + return FromImport(schema_, references_, value) case 'Integer': return FromInteger(schema_, references_, value) case 'Intersect': diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index d3be4a79d..341cf025e 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -29,11 +29,12 @@ THE SOFTWARE. import { KeyOfPropertyKeys } from '../../type/keyof/index' import { Check } from '../check/index' import { Clone } from '../clone/index' -import { Deref } from '../deref/index' +import { Deref, Pushref } from '../deref/index' import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' +import type { TImport } from 'src/type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TObject } from '../../type/object/index' import type { TRecord } from '../../type/record/index' @@ -60,6 +61,7 @@ import { import { IsKind } from '../../type/guard/kind' + // ------------------------------------------------------------------ // IsCheckable // ------------------------------------------------------------------ @@ -73,6 +75,11 @@ function FromArray(schema: TArray, references: TSchema[], value: unknown): any { if (!IsArray(value)) return value return value.map((value) => Visit(schema.items, references, value)) } +function FromImport(schema: TImport, references: TSchema[], value: unknown): any { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions], value) +} function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { const unevaluatedProperties = schema.unevaluatedProperties as TSchema const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value))) @@ -149,11 +156,13 @@ function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any { return value } function Visit(schema: TSchema, references: TSchema[], value: unknown): unknown { - const references_ = IsString(schema.$id) ? [...references, schema] : references + const references_ = IsString(schema.$id) ? Pushref(schema, references) : references const schema_ = schema as any switch (schema_[Kind]) { case 'Array': return FromArray(schema_, references_, value) + case 'Import': + return FromImport(schema_, references_, value) case 'Intersect': return FromIntersect(schema_, references_, value) case 'Object': diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index a5ddf7b0a..1cfaa1081 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -38,6 +38,7 @@ import type { TBoolean } from '../../type/boolean/index' import type { TDate } from '../../type/date/index' import type { TInteger } from '../../type/integer/index' import type { TIntersect } from '../../type/intersect/index' +import type { TImport } from '../../type/module/index' import type { TLiteral } from '../../type/literal/index' import type { TNull } from '../../type/null/index' import type { TNumber } from '../../type/number/index' @@ -182,6 +183,11 @@ function FromBoolean(schema: TBoolean, references: TSchema[], value: any): unkno function FromDate(schema: TDate, references: TSchema[], value: any): unknown { return TryConvertDate(value) } +function FromImport(schema: TImport, references: TSchema[], value: unknown): unknown { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions], value) +} function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown { return TryConvertInteger(value) } @@ -261,6 +267,8 @@ function Visit(schema: TSchema, references: TSchema[], value: any): unknown { return FromBoolean(schema_, references_, value) case 'Date': return FromDate(schema_, references_, value) + case 'Import': + return FromImport(schema_, references_, value) case 'Integer': return FromInteger(schema_, references_, value) case 'Intersect': diff --git a/src/value/create/create.ts b/src/value/create/create.ts index a1c24c578..478aab130 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -45,6 +45,7 @@ import type { TBoolean } from '../../type/boolean/index' import type { TDate } from '../../type/date/index' import type { TConstructor } from '../../type/constructor/index' import type { TFunction } from '../../type/function/index' +import type { TImport } from '../../type/module/index' import type { TInteger } from '../../type/integer/index' import type { TIntersect } from '../../type/intersect/index' import type { TIterator } from '../../type/iterator/index' @@ -167,6 +168,11 @@ function FromFunction(schema: TFunction, references: TSchema[]): any { return () => Visit(schema.returns, references) } } +function FromImport(schema: TImport, references: TSchema[]): any { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions]) +} function FromInteger(schema: TInteger, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return FromDefault(schema.default) @@ -411,6 +417,8 @@ function Visit(schema: TSchema, references: TSchema[]): unknown { return FromDate(schema_, references_) case 'Function': return FromFunction(schema_, references_) + case 'Import': + return FromImport(schema_, references_) case 'Integer': return FromInteger(schema_, references_) case 'Intersect': diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 19b4b6496..30ddbc95b 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -33,6 +33,7 @@ import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' +import type { TImport } from '../../type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TObject } from '../../type/object/index' import type { TRecord } from '../../type/record/index' @@ -78,6 +79,11 @@ function FromDate(schema: TArray, references: TSchema[], value: unknown): any { // special case intercept for dates return IsDate(value) ? value : ValueOrDefault(schema, value) } +function FromImport(schema: TImport, references: TSchema[], value: unknown): any { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + return Visit(target, [...references, ...definitions], value) +} function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any { const defaulted = ValueOrDefault(schema, value) return schema.allOf.reduce((acc, schema) => { @@ -161,6 +167,8 @@ function Visit(schema: TSchema, references: TSchema[], value: unknown): any { return FromArray(schema_, references_, value) case 'Date': return FromDate(schema_, references_, value) + case 'Import': + return FromImport(schema_, references_, value) case 'Intersect': return FromIntersect(schema_, references_, value) case 'Object': diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index be56d12bc..2498058b1 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -36,6 +36,7 @@ import { Check } from '../check/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' +import type { TImport } from '../../type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TNot } from '../../type/not/index' import type { TObject } from '../../type/object/index' @@ -82,7 +83,7 @@ export class TransformDecodeError extends TypeBoxError { // Decode // ------------------------------------------------------------------ // prettier-ignore -function Default(schema: TSchema, path: string, value: any) { +function Default(schema: TSchema, path: string, value: any): unknown { try { return IsTransform(schema) ? schema[TransformKind].Decode(value) : value } catch (error) { @@ -90,13 +91,13 @@ function Default(schema: TSchema, path: string, value: any) { } } // prettier-ignore -function FromArray(schema: TArray, references: TSchema[], path: string, value: any): any { +function FromArray(schema: TArray, references: TSchema[], path: string, value: any): unknown { return (IsArray(value)) ? Default(schema, path, value.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value))) : Default(schema, path, value) } // prettier-ignore -function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { +function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): unknown { if (!IsObject(value) || IsValueType(value)) return Default(schema, path, value) const knownEntries = KeyOfPropertyEntries(schema) const knownKeys = knownEntries.map(entry => entry[0]) @@ -115,11 +116,20 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string, } return Default(schema, path, unknownProperties) } -function FromNot(schema: TNot, references: TSchema[], path: string, value: any) { +// prettier-ignore +function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + const transform = schema[TransformKind as never] + // Note: we need to re-spec the target as TSchema + [TransformKind] + const transformTarget = { [TransformKind]: transform, ...target } as TSchema + return Visit(transformTarget as never, [...references, ...definitions], path, value) +} +function FromNot(schema: TNot, references: TSchema[], path: string, value: any): unknown { return Default(schema, path, Visit(schema.not, references, path, value)) } // prettier-ignore -function FromObject(schema: TObject, references: TSchema[], path: string, value: any) { +function FromObject(schema: TObject, references: TSchema[], path: string, value: any): unknown { if (!IsObject(value)) return Default(schema, path, value) const knownKeys = KeyOfPropertyKeys(schema) as string[] const knownProperties = { ...value } as Record @@ -147,7 +157,7 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value: return Default(schema, path, unknownProperties) } // prettier-ignore -function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) { +function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any): unknown { if (!IsObject(value)) return Default(schema, path, value) const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] const knownKeys = new RegExp(pattern) @@ -167,23 +177,23 @@ function FromRecord(schema: TRecord, references: TSchema[], path: string, value: return Default(schema, path, unknownProperties) } // prettier-ignore -function FromRef(schema: TRef, references: TSchema[], path: string, value: any) { +function FromRef(schema: TRef, references: TSchema[], path: string, value: any): unknown { const target = Deref(schema, references) return Default(schema, path, Visit(target, references, path, value)) } // prettier-ignore -function FromThis(schema: TThis, references: TSchema[], path: string, value: any) { +function FromThis(schema: TThis, references: TSchema[], path: string, value: any): unknown { const target = Deref(schema, references) return Default(schema, path, Visit(target, references, path, value)) } // prettier-ignore -function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any) { +function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any): unknown { return (IsArray(value) && IsArray(schema.items)) ? Default(schema, path, schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value[index]))) : Default(schema, path, value) } // prettier-ignore -function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any) { +function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): unknown { for (const subschema of schema.anyOf) { if (!Check(subschema, references, value)) continue // note: ensure interior is decoded first @@ -199,6 +209,8 @@ function Visit(schema: TSchema, references: TSchema[], path: string, value: any) switch (schema[Kind]) { case 'Array': return FromArray(schema_, references_, path, value) + case 'Import': + return FromImport(schema_, references_, path, value) case 'Intersect': return FromIntersect(schema_, references_, path, value) case 'Not': diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index cb626f916..1f033dc79 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -36,6 +36,7 @@ import { Check } from '../check/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' +import type { TImport } from '../../type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TNot } from '../../type/not/index' import type { TObject } from '../../type/object/index' @@ -96,6 +97,15 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a : defaulted } // prettier-ignore +function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { + const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const target = schema.$defs[schema.$ref] as TSchema + const transform = schema[TransformKind as never] + // Note: we need to re-spec the target as TSchema + [TransformKind] + const transformTarget = { [TransformKind]: transform, ...target } as TSchema + return Visit(transformTarget as never, [...references, ...definitions], path, value) +} +// prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) if (!IsObject(value) || IsValueType(value)) return defaulted @@ -210,6 +220,8 @@ function Visit(schema: TSchema, references: TSchema[], path: string, value: any) switch (schema[Kind]) { case 'Array': return FromArray(schema_, references_, path, value) + case 'Import': + return FromImport(schema_, references_, path, value) case 'Intersect': return FromIntersect(schema_, references_, path, value) case 'Not': diff --git a/test/runtime/compiler-ajv/index.ts b/test/runtime/compiler-ajv/index.ts index ae974b96d..3963d5c22 100644 --- a/test/runtime/compiler-ajv/index.ts +++ b/test/runtime/compiler-ajv/index.ts @@ -9,6 +9,7 @@ import './integer' import './intersect' import './keyof' import './literal' +import './module' import './never' import './not' import './null' diff --git a/test/runtime/compiler-ajv/module.ts b/test/runtime/compiler-ajv/module.ts new file mode 100644 index 000000000..9de6813b7 --- /dev/null +++ b/test/runtime/compiler-ajv/module.ts @@ -0,0 +1,78 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('compiler-ajv/Module', () => { + it('Should validate string', () => { + const Module = Type.Module({ + A: Type.String(), + }) + const T = Module.Import('A') + Ok(T, 'hello') + Fail(T, true) + }) + it('Should validate referenced string', () => { + const Module = Type.Module({ + A: Type.String(), + B: Type.Ref('A'), + }) + const T = Module.Import('B') + Ok(T, 'hello') + Fail(T, true) + }) + it('Should validate self referential', () => { + const Module = Type.Module({ + A: Type.Object({ + nodes: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }) + Fail(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] }) + Fail(T, true) + }) + it('Should validate mutual recursive', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Union([Type.Ref('A'), Type.Null()]), + }), + }) + const T = Module.Import('A') + Ok(T, { b: { a: null } }) + Ok(T, { b: { a: { b: { a: null } } } }) + Fail(T, { b: { a: 1 } }) + Fail(T, { b: { a: { b: { a: 1 } } } }) + Fail(T, true) + }) + it('Should validate mutual recursive (Array)', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Ok(T, { b: { a: [{ b: { a: [] } }] } }) + Fail(T, { b: { a: [{ b: { a: [null] } }] } }) + Fail(T, true) + }) + it('Should validate deep referential', () => { + const Module = Type.Module({ + A: Type.Ref('B'), + B: Type.Ref('C'), + C: Type.Ref('D'), + D: Type.Ref('E'), + E: Type.Ref('F'), + F: Type.Ref('G'), + G: Type.Ref('H'), + H: Type.Literal('hello'), + }) + const T = Module.Import('A') + Ok(T, 'hello') + Fail(T, 'world') + }) +}) diff --git a/test/runtime/compiler-ajv/ref.ts b/test/runtime/compiler-ajv/ref.ts index d9bf5c954..564dbc197 100644 --- a/test/runtime/compiler-ajv/ref.ts +++ b/test/runtime/compiler-ajv/ref.ts @@ -11,7 +11,7 @@ describe('compiler-ajv/Ref', () => { }, { $id: 'T' }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Ok( R, { @@ -31,7 +31,7 @@ describe('compiler-ajv/Ref', () => { }, { $id: 'T' }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Fail( R, { @@ -54,7 +54,7 @@ describe('compiler-ajv/Ref', () => { x: Type.Number(), y: Type.Number(), z: Type.Number(), - r: Type.Optional(Type.Ref(R)), + r: Type.Optional(Type.Ref(R.$id!)), }, { $id: 'T' }, ) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index 487c94a60..21decd7fa 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -16,6 +16,7 @@ import './iterator' import './keyof' import './kind' import './literal' +import './module' import './never' import './not' import './null' diff --git a/test/runtime/compiler/module.ts b/test/runtime/compiler/module.ts new file mode 100644 index 000000000..02fcb4d45 --- /dev/null +++ b/test/runtime/compiler/module.ts @@ -0,0 +1,78 @@ +import { Type } from '@sinclair/typebox' +import { Ok, Fail } from './validate' + +describe('compiler/Module', () => { + it('Should validate string', () => { + const Module = Type.Module({ + A: Type.String(), + }) + const T = Module.Import('A') + Ok(T, 'hello') + Fail(T, true) + }) + it('Should validate referenced string', () => { + const Module = Type.Module({ + A: Type.String(), + B: Type.Ref('A'), + }) + const T = Module.Import('B') + Ok(T, 'hello') + Fail(T, true) + }) + it('Should validate self referential', () => { + const Module = Type.Module({ + A: Type.Object({ + nodes: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }) + Fail(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] }) + Fail(T, true) + }) + it('Should validate mutual recursive', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Union([Type.Ref('A'), Type.Null()]), + }), + }) + const T = Module.Import('A') + Ok(T, { b: { a: null } }) + Ok(T, { b: { a: { b: { a: null } } } }) + Fail(T, { b: { a: 1 } }) + Fail(T, { b: { a: { b: { a: 1 } } } }) + Fail(T, true) + }) + it('Should validate mutual recursive (Array)', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Ok(T, { b: { a: [{ b: { a: [] } }] } }) + Fail(T, { b: { a: [{ b: { a: [null] } }] } }) + Fail(T, true) + }) + it('Should validate deep referential', () => { + const Module = Type.Module({ + A: Type.Ref('B'), + B: Type.Ref('C'), + C: Type.Ref('D'), + D: Type.Ref('E'), + E: Type.Ref('F'), + F: Type.Ref('G'), + G: Type.Ref('H'), + H: Type.Literal('hello'), + }) + const T = Module.Import('A') + Ok(T, 'hello') + Fail(T, 'world') + }) +}) diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index 771a7b4f6..1cd002f80 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -12,7 +12,7 @@ describe('compiler/Ref', () => { }, { $id: Assert.NextId() }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Ok( R, { @@ -32,7 +32,7 @@ describe('compiler/Ref', () => { }, { $id: Assert.NextId() }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Fail( R, { @@ -54,7 +54,7 @@ describe('compiler/Ref', () => { x: Type.Number(), y: Type.Number(), z: Type.Number(), - r: Type.Optional(Type.Ref(T)), + r: Type.Optional(Type.Ref(T.$id!)), }, { $id: 'T' }, ) @@ -70,7 +70,7 @@ describe('compiler/Ref', () => { nodes: Type.Array(Node), }), ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Ok(R, { id: '', nodes: [{ id: '', nodes: [] }] }, [T]) Fail(R, { id: '', nodes: [{ id: 1, nodes: [] }] }, [T]) }) diff --git a/test/runtime/compiler/unicode.ts b/test/runtime/compiler/unicode.ts index 0e3e569d4..f5ef1718f 100644 --- a/test/runtime/compiler/unicode.ts +++ b/test/runtime/compiler/unicode.ts @@ -31,7 +31,7 @@ describe('compiler/Unicode', () => { }, ) const T = Type.Object({ - vector: Type.Ref(R), + vector: Type.Ref(R.$id!), }) Ok( T, diff --git a/test/runtime/syntax/syntax.ts b/test/runtime/syntax/syntax.ts index 11e28298d..ae7056752 100644 --- a/test/runtime/syntax/syntax.ts +++ b/test/runtime/syntax/syntax.ts @@ -1,10 +1,100 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, TModule } from '@sinclair/typebox' import { Parse } from '@sinclair/typebox/syntax' import { Assert } from '../assert/index' // prettier-ignore -describe('parse/Parse', () => { +describe('syntax/Parse', () => { + // ---------------------------------------------------------------- + // Type Alias + // ---------------------------------------------------------------- + it('Should parse Type Alias 1', () => { + const T = Parse('type A = 1') + Assert.IsTrue(TypeGuard.IsLiteral(T.A)) + Assert.IsTrue(T.A.const === 1) + }) + it('Should parse Type Alias 2', () => { + const T = Parse('export type A = 1') + Assert.IsTrue(TypeGuard.IsLiteral(T.A)) + Assert.IsTrue(T.A.const === 1) + }) + // ---------------------------------------------------------------- + // Interface + // ---------------------------------------------------------------- + it('Should parse Interface 1', () => { + const T = Parse('interface A { x: 1 }') + Assert.IsTrue(TypeGuard.IsObject(T.A)) + Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x)) + Assert.IsTrue(T.A.properties.x.const === 1) + }) + it('Should parse Interface 2', () => { + const T = Parse('export interface A { x: 1 }') + Assert.IsTrue(TypeGuard.IsObject(T.A)) + Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x)) + Assert.IsTrue(T.A.properties.x.const === 1) + }) + // ---------------------------------------------------------------- + // Module + // ---------------------------------------------------------------- + it('Should parse Module 1', () => { + const T = Parse('module {}') + Assert.IsTrue(T instanceof TModule) + }) + it('Should parse Module 2', () => { + const T = Parse('export module {}') + Assert.IsTrue(T instanceof TModule) + }) + it('Should parse Module 3', () => { + const T = Parse('module A {}') + Assert.IsTrue(T instanceof TModule) + }) + it('Should parse Module 4', () => { + const T = Parse('export module A {}') + Assert.IsTrue(T instanceof TModule) + }) + it('Should parse Module 5', () => { + const T = Parse(`export module A { + export type A = number + }`) + const A = T.Import('A') + Assert.IsTrue(T instanceof TModule) + Assert.IsTrue(TypeGuard.IsImport(A)) + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsNumber(N)) + }) + it('Should parse Module 6', () => { + const T = Parse(`export module A { + export interface A { x: number } + }`) + const A = T.Import('A') + Assert.IsTrue(T instanceof TModule) + Assert.IsTrue(TypeGuard.IsImport(A)) + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsObject(N)) + Assert.IsTrue(TypeGuard.IsNumber(N.properties.x)) + }) + it('Should parse Module 7', () => { + const T = Parse(`export module A { + export interface A { x: number } + export type B = number + }`) + // A + const A = T.Import('A') + Assert.IsTrue(T instanceof TModule) + Assert.IsTrue(TypeGuard.IsImport(A)) + const N1 = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsObject(N1)) + Assert.IsTrue(TypeGuard.IsNumber(N1.properties.x)) + // B + const B = T.Import('B') + Assert.IsTrue(T instanceof TModule) + Assert.IsTrue(TypeGuard.IsImport(B)) + const N2 = B.$defs[B.$ref] + Assert.IsTrue(TypeGuard.IsNumber(N2)) + }) + // ---------------------------------------------------------------- + // Type Expressions + // ---------------------------------------------------------------- it('Should parse Any', () => { const T = Parse(`any`) Assert.IsTrue(TypeGuard.IsAny(T)) diff --git a/test/runtime/type/guard/kind/deref.ts b/test/runtime/type/guard/kind/deref.ts deleted file mode 100644 index 98e6811d9..000000000 --- a/test/runtime/type/guard/kind/deref.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { KindGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import { Assert } from '../../../assert/index' - -describe('guard/kind/TDeref', () => { - it('Should should deref 1', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const D = Type.Deref(R, [T]) - Assert.IsTrue(KindGuard.IsString(D)) - Assert.IsFalse('$id' in D) - }) - it('Should should deref 2', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Object({ - x: R, - y: R, - }) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsObject(D)) - Assert.IsTrue(KindGuard.IsString(D.properties.x)) - Assert.IsTrue(KindGuard.IsString(D.properties.y)) - Assert.IsFalse('$id' in D.properties.x) - Assert.IsFalse('$id' in D.properties.y) - }) - it('Should should deref 3', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Array(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsArray(D)) - Assert.IsTrue(KindGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 4', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.AsyncIterator(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsAsyncIterator(D)) - Assert.IsTrue(KindGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 5', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Iterator(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsIterator(D)) - Assert.IsTrue(KindGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 6', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Function([R], R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsFunction(D)) - Assert.IsTrue(KindGuard.IsString(D.parameters[0])) - Assert.IsTrue(KindGuard.IsString(D.returns)) - Assert.IsFalse('$id' in D.parameters[0]) - Assert.IsFalse('$id' in D.returns) - }) - it('Should should deref 7', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Constructor([R], R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsConstructor(D)) - Assert.IsTrue(KindGuard.IsString(D.parameters[0])) - Assert.IsTrue(KindGuard.IsString(D.returns)) - Assert.IsFalse('$id' in D.parameters[0]) - Assert.IsFalse('$id' in D.returns) - }) - it('Should should deref 8', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Promise(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(KindGuard.IsPromise(D)) - Assert.IsTrue(KindGuard.IsString(D.item)) - Assert.IsFalse('$id' in D.item) - }) - it('Should should deref 9', () => { - const T = Type.String({ $id: 'T' }) - const R1 = Type.Ref(T, { $id: 'R1' }) - const R2 = Type.Ref(R1, { $id: 'R2' }) - const R3 = Type.Ref(R2, { $id: 'R3' }) - const R4 = Type.Ref(R3, { $id: 'R4' }) - const R5 = Type.Ref(R4, { $id: 'R5' }) - const R6 = Type.Ref(R5, { $id: 'R6' }) - const O = Type.Array(R6) - const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T]) - Assert.IsTrue(KindGuard.IsArray(D)) - Assert.IsTrue(KindGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 10', () => { - const T = Type.String({ $id: 'T' }) - const R1 = Type.Ref(T, { $id: 'R1' }) - const R2 = Type.Ref(R1, { $id: 'R2' }) - const R3 = Type.Ref(R2, { $id: 'R3' }) - const R4 = Type.Ref(R3, { $id: 'R4' }) - const R5 = Type.Ref(R4, { $id: 'R5' }) - const R6 = Type.Ref(R5, { $id: 'R6' }) - const O = Type.Array(R6) - Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T - }) -}) diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts new file mode 100644 index 000000000..ee430ad6d --- /dev/null +++ b/test/runtime/type/guard/kind/import.ts @@ -0,0 +1,15 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/type/TImport', () => { + it('Should guard for TImport', () => { + const M = Type.Module({ + A: Type.String(), + }) + const I = M.Import('A') + const N = I.$defs[I.$ref] + Assert.IsTrue(KindGuard.IsImport(I)) + Assert.IsTrue(KindGuard.IsString(N)) + }) +}) diff --git a/test/runtime/type/guard/kind/index.ts b/test/runtime/type/guard/kind/index.ts index 271b53f88..ec0073daa 100644 --- a/test/runtime/type/guard/kind/index.ts +++ b/test/runtime/type/guard/kind/index.ts @@ -9,11 +9,11 @@ import './composite' import './const' import './constructor' import './date' -import './deref' import './enum' import './exclude' import './extract' import './function' +import './import' import './indexed' import './integer' import './intersect' diff --git a/test/runtime/type/guard/type/deref.ts b/test/runtime/type/guard/type/deref.ts deleted file mode 100644 index 64f6d7d61..000000000 --- a/test/runtime/type/guard/type/deref.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { TypeGuard } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import { Assert } from '../../../assert/index' - -describe('guard/type/TDeref', () => { - it('Should should deref 1', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const D = Type.Deref(R, [T]) - Assert.IsTrue(TypeGuard.IsString(D)) - Assert.IsFalse('$id' in D) - }) - it('Should should deref 2', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Object({ - x: R, - y: R, - }) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsObject(D)) - Assert.IsTrue(TypeGuard.IsString(D.properties.x)) - Assert.IsTrue(TypeGuard.IsString(D.properties.y)) - Assert.IsFalse('$id' in D.properties.x) - Assert.IsFalse('$id' in D.properties.y) - }) - it('Should should deref 3', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Array(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsArray(D)) - Assert.IsTrue(TypeGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 4', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.AsyncIterator(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsAsyncIterator(D)) - Assert.IsTrue(TypeGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 5', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Iterator(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsIterator(D)) - Assert.IsTrue(TypeGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 6', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Function([R], R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsFunction(D)) - Assert.IsTrue(TypeGuard.IsString(D.parameters[0])) - Assert.IsTrue(TypeGuard.IsString(D.returns)) - Assert.IsFalse('$id' in D.parameters[0]) - Assert.IsFalse('$id' in D.returns) - }) - it('Should should deref 7', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Constructor([R], R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsConstructor(D)) - Assert.IsTrue(TypeGuard.IsString(D.parameters[0])) - Assert.IsTrue(TypeGuard.IsString(D.returns)) - Assert.IsFalse('$id' in D.parameters[0]) - Assert.IsFalse('$id' in D.returns) - }) - it('Should should deref 8', () => { - const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) - const O = Type.Promise(R) - const D = Type.Deref(O, [T]) - Assert.IsTrue(TypeGuard.IsPromise(D)) - Assert.IsTrue(TypeGuard.IsString(D.item)) - Assert.IsFalse('$id' in D.item) - }) - it('Should should deref 9', () => { - const T = Type.String({ $id: 'T' }) - const R1 = Type.Ref(T, { $id: 'R1' }) - const R2 = Type.Ref(R1, { $id: 'R2' }) - const R3 = Type.Ref(R2, { $id: 'R3' }) - const R4 = Type.Ref(R3, { $id: 'R4' }) - const R5 = Type.Ref(R4, { $id: 'R5' }) - const R6 = Type.Ref(R5, { $id: 'R6' }) - const O = Type.Array(R6) - const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T]) - Assert.IsTrue(TypeGuard.IsArray(D)) - Assert.IsTrue(TypeGuard.IsString(D.items)) - Assert.IsFalse('$id' in D.items) - }) - it('Should should deref 10', () => { - const T = Type.String({ $id: 'T' }) - const R1 = Type.Ref(T, { $id: 'R1' }) - const R2 = Type.Ref(R1, { $id: 'R2' }) - const R3 = Type.Ref(R2, { $id: 'R3' }) - const R4 = Type.Ref(R3, { $id: 'R4' }) - const R5 = Type.Ref(R4, { $id: 'R5' }) - const R6 = Type.Ref(R5, { $id: 'R6' }) - const O = Type.Array(R6) - Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T - }) -}) diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts new file mode 100644 index 000000000..fdce650c6 --- /dev/null +++ b/test/runtime/type/guard/type/import.ts @@ -0,0 +1,15 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/type/TImport', () => { + it('Should guard for TImport', () => { + const M = Type.Module({ + A: Type.String(), + }) + const I = M.Import('A') + const N = I.$defs[I.$ref] + Assert.IsTrue(TypeGuard.IsImport(I)) + Assert.IsTrue(TypeGuard.IsString(N)) + }) +}) diff --git a/test/runtime/type/guard/type/index.ts b/test/runtime/type/guard/type/index.ts index 271b53f88..ec0073daa 100644 --- a/test/runtime/type/guard/type/index.ts +++ b/test/runtime/type/guard/type/index.ts @@ -9,11 +9,11 @@ import './composite' import './const' import './constructor' import './date' -import './deref' import './enum' import './exclude' import './extract' import './function' +import './import' import './indexed' import './integer' import './intersect' diff --git a/test/runtime/type/guard/type/ref.ts b/test/runtime/type/guard/type/ref.ts index c864cbd4f..c11b1a39c 100644 --- a/test/runtime/type/guard/type/ref.ts +++ b/test/runtime/type/guard/type/ref.ts @@ -5,7 +5,7 @@ import { Assert } from '../../../assert/index' describe('guard/type/TRef', () => { it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) - const R = TypeGuard.IsRef(Type.Ref(T)) + const R = TypeGuard.IsRef(Type.Ref('T')) Assert.IsTrue(R) }) it('Should not guard for TRef', () => { @@ -14,7 +14,7 @@ describe('guard/type/TRef', () => { }) it('Should not guard for TRef with invalid $ref', () => { const T = Type.Number({ $id: 'T' }) - const S = CloneType(Type.Ref(T)) + const S = CloneType(Type.Ref('T')) // @ts-ignore S.$ref = 1 const R = TypeGuard.IsRef(S) diff --git a/test/runtime/value/cast/import.ts b/test/runtime/value/cast/import.ts new file mode 100644 index 000000000..56a5a9236 --- /dev/null +++ b/test/runtime/value/cast/import.ts @@ -0,0 +1,51 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/cast/Import', () => { + const T = Type.Module({ + A: Type.Number(), + }).Import('A') + + const E = 0 + it('Should upcast from string', () => { + const value = 'world' + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should upcast from number', () => { + const value = 1 + const result = Value.Cast(T, value) + Assert.IsEqual(result, 1) + }) + it('Should upcast from object', () => { + const value = {} + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should upcast from array', () => { + const value = [1] + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should upcast from undefined', () => { + const value = undefined + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should upcast from null', () => { + const value = null + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should upcast from date', () => { + const value = new Date(100) + const result = Value.Cast(T, value) + Assert.IsEqual(result, E) + }) + it('Should preseve', () => { + const value = 123 + const result = Value.Cast(T, value) + Assert.IsEqual(result, 123) + }) +}) diff --git a/test/runtime/value/cast/index.ts b/test/runtime/value/cast/index.ts index 911bd9f17..0016139b8 100644 --- a/test/runtime/value/cast/index.ts +++ b/test/runtime/value/cast/index.ts @@ -6,6 +6,7 @@ import './boolean' import './composite' import './date' import './enum' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index d9ae73020..96b159cdc 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -153,7 +153,7 @@ describe('value/cast/Union', () => { const A = Type.Object({ type: Type.Literal('A') }, { $id: 'A' }) const B = Type.Object({ type: Type.Literal('B'), value: Type.Number() }, { $id: 'B' }) const RA = Type.Union([A, B]) - const RB = Type.Union([Type.Ref(A), Type.Ref(B)]) + const RB = Type.Union([Type.Ref('A'), Type.Ref('B')]) // variant 0 Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'B' }), { type: 'B', value: 0 }) Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'B' }), { type: 'B', value: 0 }) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 9012725f2..27bda1e41 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -15,6 +15,7 @@ import './iterator' import './keyof' import './kind' import './literal' +import './module' import './never' import './not' import './null' diff --git a/test/runtime/value/check/module.ts b/test/runtime/value/check/module.ts new file mode 100644 index 000000000..3a64267de --- /dev/null +++ b/test/runtime/value/check/module.ts @@ -0,0 +1,80 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert' + +describe('value/check/Module', () => { + it('Should validate string', () => { + const Module = Type.Module({ + A: Type.String(), + }) + const T = Module.Import('A') + Assert.IsTrue(Value.Check(T, 'hello')) + Assert.IsFalse(Value.Check(T, true)) + }) + it('Should validate referenced string', () => { + const Module = Type.Module({ + A: Type.String(), + B: Type.Ref('A'), + }) + const T = Module.Import('B') + Assert.IsTrue(Value.Check(T, 'hello')) + Assert.IsFalse(Value.Check(T, true)) + }) + it('Should validate self referential', () => { + const Module = Type.Module({ + A: Type.Object({ + nodes: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Assert.IsTrue(Value.Check(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] })) + Assert.IsFalse(Value.Check(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] })) + Assert.IsFalse(Value.Check(T, true)) + }) + it('Should validate mutual recursive', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Union([Type.Ref('A'), Type.Null()]), + }), + }) + const T = Module.Import('A') + Assert.IsTrue(Value.Check(T, { b: { a: null } })) + Assert.IsTrue(Value.Check(T, { b: { a: { b: { a: null } } } })) + + Assert.IsFalse(Value.Check(T, { b: { a: 1 } })) + Assert.IsFalse(Value.Check(T, { b: { a: { b: { a: 1 } } } })) + Assert.IsFalse(Value.Check(T, true)) + }) + it('Should validate mutual recursive (Array)', () => { + const Module = Type.Module({ + A: Type.Object({ + b: Type.Ref('B'), + }), + B: Type.Object({ + a: Type.Array(Type.Ref('A')), + }), + }) + const T = Module.Import('A') + Assert.IsTrue(Value.Check(T, { b: { a: [{ b: { a: [] } }] } })) + Assert.IsFalse(Value.Check(T, { b: { a: [{ b: { a: [null] } }] } })) + Assert.IsFalse(Value.Check(T, true)) + }) + it('Should validate deep referential', () => { + const Module = Type.Module({ + A: Type.Ref('B'), + B: Type.Ref('C'), + C: Type.Ref('D'), + D: Type.Ref('E'), + E: Type.Ref('F'), + F: Type.Ref('G'), + G: Type.Ref('H'), + H: Type.Literal('hello'), + }) + const T = Module.Import('A') + Assert.IsTrue(Value.Check(T, 'hello')) + Assert.IsFalse(Value.Check(T, 'world')) + }) +}) diff --git a/test/runtime/value/check/ref.ts b/test/runtime/value/check/ref.ts index cc541149d..20156eb8d 100644 --- a/test/runtime/value/check/ref.ts +++ b/test/runtime/value/check/ref.ts @@ -12,7 +12,7 @@ describe('value/check/Ref', () => { }, { $id: Assert.NextId() }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Assert.IsEqual( Value.Check(R, [T], { x: 1, @@ -32,7 +32,7 @@ describe('value/check/Ref', () => { }, { $id: Assert.NextId() }, ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Assert.IsEqual( Value.Check(R, [T], { x: 1, @@ -55,7 +55,7 @@ describe('value/check/Ref', () => { x: Type.Number(), y: Type.Number(), z: Type.Number(), - r: Type.Optional(Type.Ref(T)), + r: Type.Optional(Type.Ref(T.$id!)), }, { $id: 'T' }, ) @@ -76,7 +76,7 @@ describe('value/check/Ref', () => { nodes: Type.Array(Node), }), ) - const R = Type.Ref(T) + const R = Type.Ref(T.$id!) Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true) Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false) }) diff --git a/test/runtime/value/clean/import.ts b/test/runtime/value/clean/import.ts new file mode 100644 index 000000000..1ccc7c81d --- /dev/null +++ b/test/runtime/value/clean/import.ts @@ -0,0 +1,203 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Import', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A') + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A') + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean 4', () => { + const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A') + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Nested + // ---------------------------------------------------------------- + it('Should clean nested 1', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }), + }).Import('A') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean nested 2', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }), + }).Import('A') + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean nested 3', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }), + }).Import('A') + + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + it('Should clean nested 4', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }), + }).Import('A') + const R = Value.Clean(T, { x: { y: null } }) + Assert.IsEqual(R, { x: { y: null } }) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should clean additional properties 1', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties 2', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties 3', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean additional properties 4', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Additional Properties Discard + // ---------------------------------------------------------------- + it('Should clean additional properties discard 1', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties discard 2', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, { k: '', d: null }) + Assert.IsEqual(R, { k: '' }) + }) + it('Should clean additional properties discard 3', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, { k: '', d: null, x: 1 }) + Assert.IsEqual(R, { k: '', x: 1 }) + }) + it('Should clean additional properties discard 4', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ), + }).Import('A') + const R = Value.Clean(T, { k: '', d: null, x: 1, y: 2 }) + Assert.IsEqual(R, { k: '', x: 1, y: 2 }) + }) +}) diff --git a/test/runtime/value/clean/index.ts b/test/runtime/value/clean/index.ts index b290dd224..99af15b9c 100644 --- a/test/runtime/value/clean/index.ts +++ b/test/runtime/value/clean/index.ts @@ -8,6 +8,7 @@ import './constructor' import './date' import './enum' import './function' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/convert/import.ts b/test/runtime/value/convert/import.ts new file mode 100644 index 000000000..f22c506b2 --- /dev/null +++ b/test/runtime/value/convert/import.ts @@ -0,0 +1,29 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/convert/Import', () => { + it('Should convert properties', () => { + const T = Type.Module({ A: Type.Object({ + x: Type.Number(), + y: Type.Boolean(), + z: Type.Boolean() + })}).Import('A') + const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) + Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) + }) + it('Should convert known properties', () => { + const T = Type.Module({ A: Type.Object({ + x: Type.Number(), + y: Type.Boolean() + })}).Import('A') + const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' }) + Assert.IsEqual(R, { x: 42, y: true, z: 'hello' }) + }) + it('Should not convert missing properties', () => { + const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A') + const R = Value.Convert(T, { }) + Assert.IsEqual(R, { }) + }) +}) diff --git a/test/runtime/value/convert/index.ts b/test/runtime/value/convert/index.ts index ec3e1b6e8..fe5f47fd7 100644 --- a/test/runtime/value/convert/index.ts +++ b/test/runtime/value/convert/index.ts @@ -9,6 +9,7 @@ import './kind' import './date' import './enum' import './function' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/create/import.ts b/test/runtime/value/create/import.ts new file mode 100644 index 000000000..b07f96dfa --- /dev/null +++ b/test/runtime/value/create/import.ts @@ -0,0 +1,98 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Import', () => { + it('Should create value', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }), + }).Import('A') + Assert.IsEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + }) + }) + it('Should create value with optional properties', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Optional(Type.Number()), + y: Type.Optional(Type.Number()), + z: Type.Optional(Type.Number()), + }), + }).Import('A') + Assert.IsEqual(Value.Create(T), {}) + }) + it('Should create default with default properties', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + z: Type.Number({ default: 3 }), + }), + }).Import('A') + Assert.IsEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + }) + }) + it('Should create nested object', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + w: Type.Object({ + x: Type.Number({ default: 7 }), + y: Type.Number(), + z: Type.Number(), + }), + }), + }).Import('A') + Assert.IsEqual(Value.Create(T), { + x: 0, + y: 0, + z: 0, + w: { x: 7, y: 0, z: 0 }, + }) + }) + it('Should create with default', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + z: Type.Number(), + }, + { default: { x: 1, y: 2, z: 3 } }, + ), + }).Import('A') + Assert.IsEqual(Value.Create(T), { + x: 1, + y: 2, + z: 3, + }) + }) + // ---------------------------------------------------------------- + // Mutation + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should clone defaults on assignment - no mutation', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + }, + { default: { x: 1 } }, + ), + }).Import('A') + const V = Value.Create(T) + V.x = 123 + Assert.IsEqual(T.$defs.A.default, { x: 1 }) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 222f79342..9962a44f0 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -9,6 +9,7 @@ import './constructor' import './date' import './enum' import './function' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/create/ref.ts b/test/runtime/value/create/ref.ts index ce3d9d155..07142c1a2 100644 --- a/test/runtime/value/create/ref.ts +++ b/test/runtime/value/create/ref.ts @@ -12,7 +12,7 @@ describe('value/create/Ref', () => { }, { $id: 'T', default: 'target' }, ) - const R = Type.Ref(T) + const R = Type.Ref('T') Assert.Throws(() => Value.Create(R)) }) it('Should create ref default if ref default is defined', () => { @@ -24,12 +24,12 @@ describe('value/create/Ref', () => { }, { $id: 'T', default: 'target' }, ) - const R = Type.Ref(T, { default: 'override' }) + const R = Type.Ref('T', { default: 'override' }) Assert.IsEqual(Value.Create(R), 'override') // terminated at R default value }) it('Should dereference remote schema via $ref', () => { - const R = Type.Number({ $id: 'S' }) - const T = Type.Object({ x: Type.Ref(R) }) + const R = Type.Number({ $id: 'R' }) + const T = Type.Object({ x: Type.Ref('R') }) Assert.IsEqual(Value.Create(T, [R]), { x: 0 }) }) }) diff --git a/test/runtime/value/default/import.ts b/test/runtime/value/default/import.ts new file mode 100644 index 000000000..42ae35672 --- /dev/null +++ b/test/runtime/value/default/import.ts @@ -0,0 +1,299 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, CloneType } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Import', () => { + it('Should use default', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ), + }).Import('A') + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ), + }).Import('A') + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------- + it('Should should fully construct object 1', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ), + }).Import('A') + const R = Value.Default(T, undefined) + Assert.IsEqual(R, { x: { x: 1, y: 2 }, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 2', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ), + }).Import('A') + const R = Value.Default(T, { x: null }) + Assert.IsEqual(R, { x: null, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 3', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ), + }).Import('A') + const R = Value.Default(T, { x: { x: null, y: null } }) + Assert.IsEqual(R, { x: { x: null, y: null }, y: { x: 3, y: 4 } }) + }) + // ---------------------------------------------------------------- + // Properties + // ---------------------------------------------------------------- + it('Should use property defaults 1', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: 1 }, + ), + }).Import('A') + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use property defaults 2', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + }).Import('A') + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should use property defaults 3', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }), + }).Import('A') + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should use property defaults 4', () => { + const T = Type.Module({ + A: Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }), + }).Import('A') + const R = Value.Default(T, { x: 3 }) + Assert.IsEqual(R, { x: 3 }) + }) + // ---------------------------------------------------------------- + // AdditionalProperties + // ---------------------------------------------------------------- + it('Should use additional property defaults 1', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ), + }).Import('A') + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use additional property defaults 2', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ), + }).Import('A') + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: 3 }) + }) + it('Should use additional property defaults 3', () => { + const T = Type.Module({ + A: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number(), + }, + ), + }).Import('A') + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: undefined }) + }) + // ---------------------------------------------------------------- + // Mutation + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should retain defaults on operation', () => { + const A = Type.Module({ + A: Type.Object({ + a: Type.Object( + { + b: Type.Array(Type.String(), { default: [] }), + }, + { default: {} }, + ), + }), + }).Import('A') + const value = Value.Default(A, {}) + Assert.IsEqual(value, { a: { b: [] } }) + Assert.IsEqual(A.$defs.A.properties.a.default, {}) + Assert.IsEqual(A.$defs.A.properties.a.properties.b.default, []) + }) + // https://github.com/sinclairzx81/typebox/issues/726 + it('Should retain schematics on operation', () => { + const A = Type.Module({ + A: Type.Object({ + a: Type.Object( + { + b: Type.Array(Type.String(), { default: [] }), + }, + { default: {} }, + ), + }), + }).Import('A') + const B = CloneType(A) + Value.Default(A, {}) + Assert.IsEqual(A, B) + }) + // ---------------------------------------------------------------- + // Traveral: https://github.com/sinclairzx81/typebox/issues/962 + // ---------------------------------------------------------------- + it('Should traverse into an object 1 (initialize)', () => { + const M = Type.Module({ + Y: Type.Object({ y: Type.String({ default: 'y' }) }), + X: Type.Object({ x: Type.Ref('Y') }), + }) + const Y = M.Import('Y') + const X = M.Import('X') + Assert.IsEqual(Value.Default(Y, {}), { y: 'y' }) + Assert.IsEqual(Value.Default(X, { x: {} }), { x: { y: 'y' } }) + }) + it('Should traverse into an object 2 (retain)', () => { + const M = Type.Module({ + Y: Type.Object({ y: Type.String({ default: 'y' }) }), + X: Type.Object({ x: Type.Ref('Y') }), + }) + const Y = M.Import('Y') + const X = M.Import('X') + Assert.IsEqual(Value.Default(X, { x: { y: 1 } }), { x: { y: 1 } }) + }) + it('Should traverse into an object 3 (ignore on undefined)', () => { + const M = Type.Module({ + Y: Type.Object({ y: Type.String({ default: 'y' }) }), + X: Type.Object({ x: Type.Ref('Y') }), + }) + const Y = M.Import('Y') + const X = M.Import('X') + Assert.IsEqual(Value.Default(X, { x: undefined }), { x: undefined }) + }) + // ---------------------------------------------------------------- + // Exterior Object Defaults + // ---------------------------------------------------------------- + it('Should default exterior into an object 1', () => { + const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A') + const R = Value.Default(X, undefined) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should default exterior into an object 2', () => { + const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A') + const R = Value.Default(X, {}) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should default exterior into an object 3', () => { + const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A') + const R = Value.Default(X, { y: 3 }) + Assert.IsEqual(R, { y: 3, x: 1 }) + }) + it('Should default exterior into an object 4', () => { + const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A') + const R = Value.Default(X, { y: 3, x: 7 }) + Assert.IsEqual(R, { y: 3, x: 7 }) + }) + it('Should default exterior into an object 5', () => { + const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A') + const R = Value.Default(X, { x: 2 }) + Assert.IsEqual(R, { x: 2 }) + }) +}) diff --git a/test/runtime/value/default/index.ts b/test/runtime/value/default/index.ts index e553311b7..fd703993c 100644 --- a/test/runtime/value/default/index.ts +++ b/test/runtime/value/default/index.ts @@ -9,6 +9,7 @@ import './constructor' import './date' import './enum' import './function' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/transform/import.ts b/test/runtime/value/transform/import.ts new file mode 100644 index 000000000..a8646dcfc --- /dev/null +++ b/test/runtime/value/transform/import.ts @@ -0,0 +1,157 @@ +import * as Encoder from './_encoder' +import { Assert } from '../../assert' + +import { TypeSystemPolicy } from '@sinclair/typebox/system' +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' + +// prettier-ignore +describe('value/transform/Import', () => { + // -------------------------------------------------------- + // Identity + // -------------------------------------------------------- + const T0 = Type.Transform( + Type.Module({ A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + })}).Import('A'), + ) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode identity', () => { + const R = Encoder.Decode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should encode identity', () => { + const R = Encoder.Encode(T0, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on identity decode', () => { + Assert.Throws(() => Encoder.Decode(T0, undefined)) + }) + // ---------------------------------------------------------- + // Object + // ---------------------------------------------------------- + const T1 = Type.Transform( + Type.Module({ A: Type.Object({ + x: Type.Number(), + y: Type.Number(), + })}).Import('A'), + ) + .Decode((value) => 42) + .Encode((value) => ({ x: 1, y: 2 })) + it('Should decode mapped', () => { + const R = Encoder.Decode(T1, { x: 1, y: 2 }) + Assert.IsEqual(R, 42) + }) + it('Should encode mapped', () => { + const R = Encoder.Encode(T1, null) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on mapped decode', () => { + Assert.Throws(() => Encoder.Decode(T1, undefined)) + }) + // ---------------------------------------------------------- + // Object: Transform Property + // ---------------------------------------------------------- + const N2 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A')) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T2 = Type.Object({ + x: N2, + y: N2, + }) + it('Should decode transform property', () => { + const R = Encoder.Decode(T2, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: '1', y: '2' }) + }) + it('Should encode transform property', () => { + const R = Encoder.Encode(T2, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should throw on decode transform property', () => { + Assert.Throws(() => Encoder.Decode(T2, undefined)) + }) + // ---------------------------------------------------------- + // Object: Transform Property Nested (Twizzle) + // ---------------------------------------------------------- + const N3 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A')) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T3 = Type.Transform( + Type.Object({ + x: N3, + y: N3, + }), + ) + .Decode((value) => ({ x: value.y, y: value.x })) + .Encode((value) => ({ x: value.y, y: value.x })) + it('Should decode transform property nested', () => { + const R = Encoder.Decode(T3, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: '2', y: '1' }) + }) + it('Should encode transform property nested', () => { + const R = Encoder.Encode(T3, { x: '1', y: '2' }) + Assert.IsEqual(R, { x: 2, y: 1 }) + }) + it('Should throw on decode transform property nested', () => { + Assert.Throws(() => Encoder.Decode(T3, undefined)) + }) + // ---------------------------------------------------------- + // Object Additional Properties + // ---------------------------------------------------------- + const N4 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A')) + .Decode((value) => value.toString()) + .Encode((value) => parseInt(value)) + const T4 = Type.Transform( + Type.Object( + { + x: Type.Number(), + }, + { + additionalProperties: N4, + }, + ), + ) + .Decode((value) => value) + .Encode((value) => value) + it('Should decode additional property', () => { + const R = Encoder.Decode(T4, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: '2' }) + }) + it('Should encode additional property', () => { + const R = Encoder.Encode(T4, { x: 1, y: '5' }) + Assert.IsEqual(R, { x: 1, y: 5 }) + }) + it('Should throw on additional property 1', () => { + Assert.Throws(() => Encoder.Decode(T4, undefined)) + }) + it('Should throw on additional property 2', () => { + Assert.Throws(() => Encoder.Decode(T4, { x: 1, y: true })) + }) + // ------------------------------------------------------------ + // Map + // ------------------------------------------------------------ + const T5 = Type.Transform(Type.Module({ A: Type.Object({ x: Type.String(), y: Type.String() })}).Import('A')) + .Decode((value) => new Map(Object.entries(value))) + .Encode((value) => Object.fromEntries(value.entries()) as any) + it('should decode map', () => { + const R = Encoder.Decode(T5, { x: 'hello', y: 'world' }) + Assert.IsInstanceOf(R, Map) + Assert.IsEqual(R.get('x'), 'hello') + Assert.IsEqual(R.get('y'), 'world') + }) + it('should encode map', () => { + const R = Encoder.Encode( + T5, + new Map([ + ['x', 'hello'], + ['y', 'world'], + ]), + ) + Assert.IsEqual(R, { x: 'hello', y: 'world' }) + }) + it('Should throw on map decode', () => { + Assert.Throws(() => Encoder.Decode(T5, {})) + }) +}) diff --git a/test/runtime/value/transform/index.ts b/test/runtime/value/transform/index.ts index bc4fb931a..ff005d5f5 100644 --- a/test/runtime/value/transform/index.ts +++ b/test/runtime/value/transform/index.ts @@ -8,6 +8,7 @@ import './constructor' import './date' import './enum' import './function' +import './import' import './integer' import './intersect' import './iterator' diff --git a/test/runtime/value/transform/ref.ts b/test/runtime/value/transform/ref.ts index 92d252c29..39113f41b 100644 --- a/test/runtime/value/transform/ref.ts +++ b/test/runtime/value/transform/ref.ts @@ -8,7 +8,7 @@ describe('value/transform/Ref', () => { // Identity // -------------------------------------------------------- const N0 = Type.Number({ $id: 'N0' }) - const T0 = Type.Transform(Type.Ref(N0)) + const T0 = Type.Transform(Type.Ref('N0')) .Decode((value) => value) .Encode((value) => value) it('Should decode mapped', () => { @@ -26,7 +26,7 @@ describe('value/transform/Ref', () => { // Mapped // -------------------------------------------------------- const N1 = Type.Number({ $id: 'N1' }) - const T1 = Type.Transform(Type.Ref(N1)) + const T1 = Type.Transform(Type.Unsafe(Type.Ref('N1'))) .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode mapped', () => { @@ -46,7 +46,7 @@ describe('value/transform/Ref', () => { const N2 = Type.Transform(Type.Number({ $id: 'N2' })) .Decode((value) => value + 1) .Encode((value) => value - 1) - const T2 = Type.Transform(Type.Ref(N2)) + const T2 = Type.Transform(Type.Unsafe(Type.Ref('N2'))) .Decode((value) => value + 1) .Encode((value) => value - 1) it('Should decode mapped remote', () => { diff --git a/test/static/deref.ts b/test/static/deref.ts deleted file mode 100644 index 4df0737ef..000000000 --- a/test/static/deref.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Expect } from './assert' -import { Type, TRef, TObject, TNumber } from '@sinclair/typebox' - -// prettier-ignore -const Vector: TObject<{ - x: TNumber; - y: TNumber; -}> = Type.Object({ - x: Type.Number(), - y: Type.Number(), -}, { $id: 'Vector' }) - -// prettier-ignore -const VectorRef: TRef> = Type.Ref(Vector) - -// prettier-ignore -const Vertex: TObject<{ - position: TRef>; - texcoord: TRef>; -}> = Type.Object({ - position: VectorRef, - texcoord: VectorRef, -}) - -// prettier-ignore -const VertexDeref: TObject<{ - position: TObject<{ - x: TNumber; - y: TNumber; - }>; - texcoord: TObject<{ - x: TNumber; - y: TNumber; - }>; -}> = Type.Deref(Vertex, [Vector]) - -// prettier-ignore -Expect(VertexDeref).ToStatic<{ - position: { - x: number; - y: number; - }; - texcoord: { - x: number; - y: number; - }; -}> diff --git a/test/static/ref.ts b/test/static/ref.ts index 47ccd9b29..76aca672b 100644 --- a/test/static/ref.ts +++ b/test/static/ref.ts @@ -3,11 +3,11 @@ import { Type, Static } from '@sinclair/typebox' { const T = Type.String({ $id: 'T' }) - const R = Type.Ref(T) + const R = Type.Ref('T') type T = Static type R = Static Expect(T).ToStatic() - Expect(R).ToStatic() + Expect(R).ToStatic() } From 53c3d7547a0c31fdb67c16896155bdfb48db1b7d Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 14 Nov 2024 00:38:04 +0900 Subject: [PATCH 304/369] Documentation --- readme.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 39fd65056..3adeae926 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@

      Json Schema Type Builder with Static Type Resolution for TypeScript

      - +

      @@ -777,7 +777,8 @@ type T = Static // type T = string | null TypeBox Modules are containers for related types. They function as namespaces and enable internal types to reference each other via string references. Modules support both singular and mutually recursive types. They provide a mechanism to create circular types irrespective of the order in which types are defined. ```typescript -// The following creates a circular recursive type. + +// The following creates a Module of circular recursive Types. const Module = Type.Module({ A: Type.Object({ @@ -791,7 +792,7 @@ const Module = Type.Module({ }), }) -// Module types must be imported before use. +// Module Types must be imported before use. const A = Module.Import('A') // const A: TImport<{...}, 'A'> From 56b760c99eb71dfba4c6b385a27a17c21ca13a1c Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Sun, 17 Nov 2024 05:58:50 +0900 Subject: [PATCH 305/369] Revision 0.34.1 (#1080) * Implement Computed Deref Types for Import * ChangeLog --- changelog/0.34.0.md | 4 + package-lock.json | 4 +- package.json | 2 +- readme.md | 42 +- src/syntax/runtime.ts | 5 +- src/syntax/static.ts | 10 +- src/type/array/array.ts | 2 +- src/type/awaited/awaited.ts | 94 ++-- src/type/computed/computed.ts | 44 ++ src/type/computed/index.ts | 29 ++ src/type/guard/kind.ts | 17 +- src/type/guard/type.ts | 12 +- src/type/indexed/indexed-from-mapped-key.ts | 63 ++- .../indexed/indexed-from-mapped-result.ts | 55 +-- src/type/indexed/indexed-property-keys.ts | 60 +-- src/type/indexed/indexed.ts | 266 +++++++----- src/type/intersect/intersect-evaluated.ts | 74 ++-- src/type/intersect/intersect.ts | 18 +- src/type/keyof/keyof-from-mapped-result.ts | 46 +- src/type/keyof/keyof-property-keys.ts | 94 ++-- src/type/keyof/keyof.ts | 86 ++-- src/type/module/compute.ts | 402 ++++++++++++++++++ src/type/module/infer.ts | 162 +++++++ src/type/module/module.ts | 144 +------ src/type/not/not.ts | 4 +- src/type/omit/omit-from-mapped-key.ts | 73 ++-- src/type/omit/omit-from-mapped-result.ts | 52 +-- src/type/omit/omit.ts | 163 ++++--- src/type/partial/partial.ts | 100 +++-- src/type/pick/pick-from-mapped-key.ts | 71 ++-- src/type/pick/pick-from-mapped-result.ts | 52 +-- src/type/pick/pick.ts | 162 ++++--- src/type/record/record.ts | 51 ++- src/type/required/required.ts | 108 +++-- src/type/tuple/tuple.ts | 8 +- src/type/type/javascript.ts | 26 +- src/type/type/json.ts | 146 +++---- src/type/union/union-evaluated.ts | 70 +-- src/type/union/union.ts | 10 +- src/value/check/check.ts | 4 +- src/value/transform/decode.ts | 4 +- src/value/transform/encode.ts | 4 +- src/value/transform/has.ts | 4 +- test/runtime/type/guard/kind/import.ts | 150 ++++++- test/runtime/type/guard/type/import.ts | 148 ++++++- test/static/pick.ts | 2 +- 46 files changed, 2075 insertions(+), 1072 deletions(-) create mode 100644 src/type/computed/computed.ts create mode 100644 src/type/computed/index.ts create mode 100644 src/type/module/compute.ts create mode 100644 src/type/module/infer.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index fdc9a75ff..e9f754629 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,3 +1,7 @@ +### 0.34.0 +- [Revision 0.34.1](https://github.com/sinclairzx81/typebox/pull/1080) + - Implement Computed Type Deref in Modules + ## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0) ## Overview diff --git a/package-lock.json b/package-lock.json index fbacace2e..3dab50690 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.0", + "version": "0.34.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.0", + "version": "0.34.1", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 44b16b8e3..5c9bf671d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.0", + "version": "0.34.1", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 3adeae926..0d3ec1cb1 100644 --- a/readme.md +++ b/readme.md @@ -1074,22 +1074,32 @@ const T = Type.Object({ // const T: TObject<{ ### Module -Syntax Types support Module parsing, which is useful for processing multiple TypeScript types. Module parsing supports type alias and interface definitions. Generics are currently unsupported as of 0.34.0. +Syntax Types also support Module parsing. This can provide a more terse syntax for creating Module definitions, but comes with an inference performance cost. Module parsing supports interface and type alias definitions. Generics types are currently unsupported. ```typescript -const Foo = Parse(`module Foo { - - export type A = string - - export type B = number - - export type C = A | B +const Module = Parse(`module { + + export interface User { + id: string + name: string + email: string + } + + export type PartialUser = ( + Pick & + Partial> + ) }`) -const C = Foo.Import('C') // const C: TImport<{ - // ... - // }, 'C'> +const PartialUser = Module.Import('PartialUser') // TImport<{...}, 'PartialUser'> + +type PartialUser = Static // type PartialUser = { + // id: string, + // } & { + // name?: string, + // email?: string, + // } ``` @@ -1883,12 +1893,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m ┌──────────────────────┬────────────┬────────────┬─────────────┐ │ (index) │ Compiled │ Minified │ Compression │ ├──────────────────────┼────────────┼────────────┼─────────────┤ -│ typebox/compiler │ '121.7 kb' │ ' 53.4 kb' │ '2.28 x' │ -│ typebox/errors │ ' 75.3 kb' │ ' 33.4 kb' │ '2.25 x' │ -│ typebox/syntax │ '120.1 kb' │ ' 50.5 kb' │ '2.38 x' │ +│ typebox/compiler │ '122.4 kb' │ ' 53.4 kb' │ '2.29 x' │ +│ typebox/errors │ ' 67.6 kb' │ ' 29.6 kb' │ '2.28 x' │ +│ typebox/syntax │ '132.9 kb' │ ' 54.2 kb' │ '2.45 x' │ │ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │ -│ typebox/value │ '160.3 kb' │ ' 67.4 kb' │ '2.38 x' │ -│ typebox │ ' 96.2 kb' │ ' 40.2 kb' │ '2.39 x' │ +│ typebox/value │ '150.1 kb' │ ' 62.2 kb' │ '2.41 x' │ +│ typebox │ '106.8 kb' │ ' 43.2 kb' │ '2.47 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 15001ee48..82db79005 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -324,11 +324,12 @@ const FactorIndexArray = (Type: Types.TSchema, IndexArray: unknown[]): Types.TSc const [Left, Right] = DestructureRight(IndexArray) as [unknown[], Types.TSchema[]] return ( !Types.ValueGuard.IsUndefined(Right) ? ( - Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) : + // note: Indexed types require reimplementation to replace `[number]` indexers + Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) as never : Right.length === 0 ? Types.Array(FactorIndexArray(Type, Left)) : Types.Never() ) : Type - ) + ) } // prettier-ignore const FactorMapping = (KeyOf: boolean, Type: Types.TSchema, IndexArray: unknown[], Extends: Types.TSchema[]) => { diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 0b30b8ead..ddd48ea76 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -776,7 +776,7 @@ type Partial = Static.Tuple<[ // prettier-ignore interface RequiredMapping extends Static.IMapping { output: this['input'] extends ['Required', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TPartial + ? Types.TRequired : never } // prettier-ignore @@ -788,8 +788,8 @@ type Required = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface PickMapping extends Static.IMapping { - output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TPick> + output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] + ? Types.TPick : never } // prettier-ignore @@ -801,8 +801,8 @@ type Pick = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface OmitMapping extends Static.IMapping { - output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TOmit> + output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] + ? Types.TOmit : never } // prettier-ignore diff --git a/src/type/array/array.ts b/src/type/array/array.ts index 388ba25d7..dc29a1858 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -54,6 +54,6 @@ export interface TArray extends TSchema, ArrayOptio items: T } /** `[Json]` Creates an Array type */ -export function Array(items: T, options?: ArrayOptions): TArray { +export function Array(items: Type, options?: ArrayOptions): TArray { return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never } diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index e129b6a52..1a849f14e 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -26,76 +26,96 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +import { CreateType } from '../create/type' +import { Ensure } from '../helpers/index' import type { TSchema, SchemaOptions } from '../schema/index' +import { Computed, type TComputed } from '../computed/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { type TPromise } from '../promise/index' -import { CreateType } from '../create/type' +import { Ref, type TRef } from '../ref/index' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsIntersect, IsUnion, IsPromise } from '../guard/kind' -// ------------------------------------------------------------------ -// FromRest -// ------------------------------------------------------------------ +import { IsIntersect, IsUnion, IsPromise, IsRef, IsComputed } from '../guard/kind' + +// ---------------------------------------------------------------- +// FromComputed +// ---------------------------------------------------------------- // prettier-ignore -type TFromRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc +type TFromComputed = Ensure<( + TComputed<'Awaited', [TComputed]> +)> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Awaited', [Computed(target, parameters)]) as never +} +// ---------------------------------------------------------------- +// Ref +// ---------------------------------------------------------------- +type TFromRef = Ensure]>> // prettier-ignore -function FromRest(T: [...T]) : TFromRest { - return T.map(L => AwaitedResolve(L)) as never +function FromRef($ref: Ref): TFromRef { + return Computed('Awaited', [Ref($ref)]) as never } // ---------------------------------------------------------------- // FromIntersect // ---------------------------------------------------------------- // prettier-ignore -type TFromIntersect = TIntersect> +type TFromIntersect = ( + TIntersect> +) // prettier-ignore -function FromIntersect(T: [...T]): TFromIntersect { - return Intersect(FromRest(T) as TSchema[]) as never +function FromIntersect(types: [...Types]): TFromIntersect { + return Intersect(FromRest(types) as TSchema[]) as never } // ---------------------------------------------------------------- // FromUnion // ---------------------------------------------------------------- // prettier-ignore -type TFromUnion = TUnion> +type TFromUnion = TUnion> // prettier-ignore -function FromUnion(T: [...T]): TFromUnion { - return Union(FromRest(T) as TSchema[]) as never +function FromUnion(types: [...Types]): TFromUnion { + return Union(FromRest(types) as TSchema[]) as never } // ---------------------------------------------------------------- // Promise // ---------------------------------------------------------------- -type TFromPromise = TAwaited +type TFromPromise = TAwaited // prettier-ignore -function FromPromise(T: T): TFromPromise { - return AwaitedResolve(T) as never +function FromPromise(type: Type): TFromPromise { + return Awaited(type) as never } -// ---------------------------------------------------------------- -// AwaitedResolve -// ---------------------------------------------------------------- +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ // prettier-ignore -function AwaitedResolve(T: T): TAwaited { - return ( - IsIntersect(T) ? FromIntersect(T.allOf) : - IsUnion(T) ? FromUnion(T.anyOf) : - IsPromise(T) ? FromPromise(T.item) : - T - ) as never +type TFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromRest]> + : Result +) +// prettier-ignore +function FromRest(types: [...Types]) : TFromRest { + return types.map(type => Awaited(type)) as never } // ------------------------------------------------------------------ // TAwaited // ------------------------------------------------------------------ // prettier-ignore -export type TAwaited = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TPromise ? TAwaited : - T +export type TAwaited = ( + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TIntersect ? TIntersect> : + Type extends TUnion ? TUnion> : + Type extends TPromise ? TAwaited : + Type +) /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ -export function Awaited(T: T, options?: SchemaOptions): TAwaited { - return CreateType(AwaitedResolve(T), options) as never +export function Awaited(type: T, options?: SchemaOptions): TAwaited { + return CreateType( + IsComputed(type) ? FromComputed(type.target, type.parameters) : IsIntersect(type) ? FromIntersect(type.allOf) : IsUnion(type) ? FromUnion(type.anyOf) : IsPromise(type) ? FromPromise(type.item) : IsRef(type) ? FromRef(type.$ref) : type, + options, + ) as never } diff --git a/src/type/computed/computed.ts b/src/type/computed/computed.ts new file mode 100644 index 000000000..2524dc152 --- /dev/null +++ b/src/type/computed/computed.ts @@ -0,0 +1,44 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { CreateType } from '../create/index' +import { Kind } from '../symbols/symbols' + +// ------------------------------------------------------------------ +// Computed +// ------------------------------------------------------------------ +export interface TComputed extends TSchema { + [Kind]: 'Computed' + target: Target + parameters: Parameters +} +/** `[Internal]` Creates a deferred computed type. This type is used exclusively in modules to defer resolution of computable types that contain interior references */ +export function Computed(target: Target, parameters: [...Parameters], options?: SchemaOptions): TComputed { + return CreateType({ [Kind]: 'Computed', target, parameters }, options) as never +} diff --git a/src/type/computed/index.ts b/src/type/computed/index.ts new file mode 100644 index 000000000..4d20b1f87 --- /dev/null +++ b/src/type/computed/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './computed' diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index 2e61a1df9..95e14fc2f 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -29,9 +29,10 @@ THE SOFTWARE. import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TransformOptions } from '../transform/index' -import { TTemplateLiteral } from '../template-literal/index' -import { TArray } from '../array/index' -import { TBoolean } from '../boolean/index' +import type { TTemplateLiteral } from '../template-literal/index' +import type { TArray } from '../array/index' +import type { TBoolean } from '../boolean/index' +import type { TComputed } from '../computed/index' import type { TRecord } from '../record/index' import type { TString } from '../string/index' import type { TUnion } from '../union/index' @@ -44,7 +45,7 @@ import type { TImport } from '../module/index' import type { TInteger } from '../integer/index' import type { TIntersect } from '../intersect/index' import type { TIterator } from '../iterator/index' -import type { TLiteral } from '../literal/index' +import type { TLiteral, TLiteralValue } from '../literal/index' import type { TMappedKey, TMappedResult } from '../mapped/index' import type { TNever } from '../never/index' import type { TNot } from '../not/index' @@ -95,6 +96,10 @@ export function IsBigInt(value: unknown): value is TBigInt { export function IsBoolean(value: unknown): value is TBoolean { return IsKindOf(value, 'Boolean') } +/** `[Kind-Only]` Returns true if the given value is TComputed */ +export function IsComputed(value: unknown): value is TComputed { + return IsKindOf(value, 'Computed') +} /** `[Kind-Only]` Returns true if the given value is TConstructor */ export function IsConstructor(value: unknown): value is TConstructor { return IsKindOf(value, 'Constructor') @@ -143,6 +148,10 @@ export function IsLiteralNumber(value: unknown): value is TLiteral { export function IsLiteralBoolean(value: unknown): value is TLiteral { return IsLiteral(value) && ValueGuard.IsBoolean(value.const) } +/** `[Kind-Only]` Returns true if the given value is TLiteralValue */ +export function IsLiteralValue(value: unknown): value is TLiteralValue { + return ValueGuard.IsBoolean(value) || ValueGuard.IsNumber(value) || ValueGuard.IsString(value) +} /** `[Kind-Only]` Returns true if the given value is TLiteral */ export function IsLiteral(value: unknown): value is TLiteral { return IsKindOf(value, 'Literal') diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index ed8eb54fe..b4564bed2 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -30,9 +30,10 @@ import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TypeBoxError } from '../error/index' import { TransformOptions } from '../transform/index' -import { TTemplateLiteral } from '../template-literal/index' -import { TArray } from '../array/index' -import { TBoolean } from '../boolean/index' +import type { TTemplateLiteral } from '../template-literal/index' +import type { TArray } from '../array/index' +import type { TBoolean } from '../boolean/index' +import type { TComputed } from '../computed/index' import type { TRecord } from '../record/index' import type { TString } from '../string/index' import type { TUnion } from '../union/index' @@ -76,6 +77,7 @@ const KnownTypes = [ 'AsyncIterator', 'BigInt', 'Boolean', + 'Computed', 'Constructor', 'Date', 'Enum', @@ -217,6 +219,10 @@ export function IsBoolean(value: unknown): value is TBoolean { IsOptionalString(value.$id) ) } +/** Returns true if the given value is TComputed */ +export function IsComputed(value: unknown): value is TComputed { + return IsKindOf(value, 'Computed') && IsString(value.target) && ValueGuard.IsArray(value.parameters) && value.parameters.every((schema) => IsSchema(schema)) +} /** Returns true if the given value is TConstructor */ export function IsConstructor(value: unknown): value is TConstructor { // prettier-ignore diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index 4251cea89..b66cf5113 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -37,68 +37,53 @@ import { Clone } from '../clone/value' // MappedIndexPropertyKey // ------------------------------------------------------------------ // prettier-ignore -type TMappedIndexPropertyKey< - T extends TSchema, - K extends PropertyKey -> = { - [_ in K]: TIndex +type TMappedIndexPropertyKey = { + [_ in Key]: TIndex } // prettier-ignore -function MappedIndexPropertyKey< - T extends TSchema, - K extends PropertyKey ->(T: T, K: K, options?: SchemaOptions): TMappedIndexPropertyKey { - return { [K]: Index(T, [K], Clone(options)) } as never +function MappedIndexPropertyKey(type: Type, key: Key, options?: SchemaOptions): TMappedIndexPropertyKey { + return { [key]: Index(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ // MappedIndexPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TMappedIndexPropertyKeys = ( - K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? TMappedIndexPropertyKeys> - : Acc +type TMappedIndexPropertyKeys = ( + PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] + ? TMappedIndexPropertyKeys> + : Result ) // prettier-ignore -function MappedIndexPropertyKeys< - T extends TSchema, - K extends PropertyKey[] ->(T: T, K: [...K], options?: SchemaOptions): TMappedIndexPropertyKeys { - return K.reduce((Acc, L) => { - return { ...Acc, ...MappedIndexPropertyKey(T, L, options) } +function MappedIndexPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedIndexPropertyKeys { + return propertyKeys.reduce((result, left) => { + return { ...result, ...MappedIndexPropertyKey(type, left, options) } }, {} as TProperties) as never } // ------------------------------------------------------------------ // MappedIndexProperties // ------------------------------------------------------------------ // prettier-ignore -type TMappedIndexProperties = Evaluate< - TMappedIndexPropertyKeys +type TMappedIndexProperties = Evaluate< + TMappedIndexPropertyKeys > // prettier-ignore -function MappedIndexProperties< - T extends TSchema, - K extends TMappedKey ->(T: T, K: K, options?: SchemaOptions): TMappedIndexProperties { - return MappedIndexPropertyKeys(T, K.keys, options) as never +function MappedIndexProperties(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedIndexProperties { + return MappedIndexPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ // TIndexFromMappedKey // ------------------------------------------------------------------ // prettier-ignore -export type TIndexFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TMappedIndexProperties +export type TIndexFromMappedKey > = ( - Ensure> + Ensure> ) // prettier-ignore -export function IndexFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TMappedIndexProperties ->(T: T, K: K, options?: SchemaOptions): TMappedResult

      { - const P = MappedIndexProperties(T, K, options) - return MappedResult(P) as never +export function IndexFromMappedKey +>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult { + const properties = MappedIndexProperties(type, mappedKey, options) + return MappedResult(properties) as never } diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index d23fa750d..e2b2b007c 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -36,57 +36,42 @@ import { Index, type TIndex } from './index' // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - T extends TSchema, - P extends TProperties -> = ( - { [K2 in keyof P]: TIndex> } +type TFromProperties = ( + { [K2 in keyof Properties]: TIndex> } ) // prettier-ignore -function FromProperties< - T extends TSchema, - P extends TProperties ->(T: T, P: P, options?: SchemaOptions): TFromProperties { - const Acc = {} as Record - for(const K2 of Object.getOwnPropertyNames(P)) { - Acc[K2] = Index(T, IndexPropertyKeys(P[K2]), options) +function FromProperties(type: Type, properties: Properties, options?: SchemaOptions): TFromProperties { + const result = {} as Record + for(const K2 of Object.getOwnPropertyNames(properties)) { + const keys = IndexPropertyKeys(properties[K2]) + result[K2] = Index(type, keys, options) as never } - return Acc as never + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - T extends TSchema, - R extends TMappedResult -> = ( - TFromProperties +type TFromMappedResult = ( + TFromProperties ) // prettier-ignore -function FromMappedResult< - T extends TSchema, - R extends TMappedResult ->(T: T, R: R, options?: SchemaOptions): TFromMappedResult { - return FromProperties(T, R.properties, options) as never +function FromMappedResult(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult { + return FromProperties(type, mappedResult.properties, options) as never } // ------------------------------------------------------------------ // TIndexFromMappedResult // ------------------------------------------------------------------ // prettier-ignore -export type TIndexFromMappedResult< - T extends TSchema, - R extends TMappedResult, - P extends TProperties = TFromMappedResult +export type TIndexFromMappedResult > = ( - TMappedResult

      + TMappedResult ) // prettier-ignore -export function IndexFromMappedResult< - T extends TSchema, - R extends TMappedResult, - P extends TProperties = TFromMappedResult ->(T: T, R: R, options?: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(T, R, options) - return MappedResult(P) as never +export function IndexFromMappedResult +>(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(type, mappedResult, options) + return MappedResult(properties) as never } diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts index dcb60fad7..ad73541e4 100644 --- a/src/type/indexed/indexed-property-keys.ts +++ b/src/type/indexed/indexed-property-keys.ts @@ -41,38 +41,40 @@ import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../g // FromTemplateLiteral // ------------------------------------------------------------------ // prettier-ignore -type TFromTemplateLiteral> = (R) +type TFromTemplateLiteral +> = Result // prettier-ignore -function FromTemplateLiteral(T: T): TFromTemplateLiteral { - const R = TemplateLiteralGenerate(T) as string[] - return R.map(S => S.toString()) as never +function FromTemplateLiteral(templateLiteral: TemplateLiteral): TFromTemplateLiteral { + const result = TemplateLiteralGenerate(templateLiteral) as string[] + return result.map(S => S.toString()) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromUnion]> - : Acc +type TFromUnion = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromUnion]> + : Result ) // prettier-ignore -function FromUnion(T: T): TFromUnion { - const Acc = [] as string[] - for(const L of T) Acc.push(...IndexPropertyKeys(L)) - return Acc as never +function FromUnion(type: Type): TFromUnion { + const result = [] as string[] + for(const left of type) result.push(...IndexPropertyKeys(left)) + return result as never } // ------------------------------------------------------------------ // FromLiteral // ------------------------------------------------------------------ // prettier-ignore -type TFromLiteral = ( - T extends PropertyKey - ? [`${T}`] +type TFromLiteral = ( + LiteralValue extends PropertyKey + ? [`${LiteralValue}`] : [] ) // prettier-ignore -function FromLiteral(T: T): TFromLiteral { +function FromLiteral(T: LiteralValue): TFromLiteral { return ( [(T as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString() ) as never @@ -81,23 +83,23 @@ function FromLiteral(T: T): TFromLiteral { // IndexedKeyResolve // ------------------------------------------------------------------ // prettier-ignore -export type TIndexPropertyKeys = ( - T extends TTemplateLiteral ? TFromTemplateLiteral : - T extends TUnion ? TFromUnion : - T extends TLiteral ? TFromLiteral : - T extends TNumber ? ['[number]'] : - T extends TInteger ? ['[number]'] : +export type TIndexPropertyKeys : + Type extends TUnion ? TFromUnion : + Type extends TLiteral ? TFromLiteral : + Type extends TNumber ? ['[number]'] : + Type extends TInteger ? ['[number]'] : [] -) +)> = Result /** Returns a tuple of PropertyKeys derived from the given TSchema */ // prettier-ignore -export function IndexPropertyKeys(T: T): TIndexPropertyKeys { +export function IndexPropertyKeys(type: Type): TIndexPropertyKeys { return [...new Set(( - IsTemplateLiteral(T) ? FromTemplateLiteral(T) : - IsUnion(T) ? FromUnion(T.anyOf) : - IsLiteral(T) ? FromLiteral(T.const) : - IsNumber(T) ? ['[number]'] : - IsInteger(T) ? ['[number]'] : + IsTemplateLiteral(type) ? FromTemplateLiteral(type) : + IsUnion(type) ? FromUnion(type.anyOf) : + IsLiteral(type) ? FromLiteral(type.const) : + IsNumber(type) ? ['[number]'] : + IsInteger(type) ? ['[number]'] : [] ))] as never } diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index 6de947666..5598b034c 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -28,62 +28,93 @@ THE SOFTWARE. import { CreateType } from '../create/type' import { type TSchema, SchemaOptions } from '../schema/index' +import { Computed, type TComputed } from '../computed/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { type TObject, type TProperties } from '../object/index' -import { type Assert } from '../helpers/index' +import { type Ensure, type Assert } from '../helpers/index' import { Never, type TNever } from '../never/index' import { type TRecursive } from '../recursive/index' import { type TIntersect } from '../intersect/index' import { TMappedResult, type TMappedKey } from '../mapped/index' -import { type TUnion } from '../union/index' +import { Union, type TUnion } from '../union/index' import { type TTuple } from '../tuple/index' import { type TArray } from '../array/index' +import { Ref, type TRef } from '../ref/index' import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' +// ------------------------------------------------------------------ +// Infrastructure +// ------------------------------------------------------------------ import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys' import { IndexFromMappedKey, type TIndexFromMappedKey } from './indexed-from-mapped-key' import { IndexFromMappedResult, type TIndexFromMappedResult } from './indexed-from-mapped-result' + +// ------------------------------------------------------------------ +// KindGuard // ------------------------------------------------------------------ -// TypeGuard +import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion, IsLiteralValue, IsRef, IsComputed } from '../guard/kind' +import { IsArray as IsArrayValue } from '../guard/value' + +// ------------------------------------------------------------------ +// FromComputed // ------------------------------------------------------------------ -import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion } from '../guard/kind' +// prettier-ignore +// type TFromComputed = Ensure< +// TComputed<'Partial', [TComputed]> +// > +// // prettier-ignore +// function FromComputed(target: Target, parameters: Parameters): TFromComputed { +// return Computed('Partial', [Computed(target, parameters)]) as never +// } +// // ------------------------------------------------------------------ +// // FromRef +// // ------------------------------------------------------------------ +// // prettier-ignore +// type TFromRef = Ensure< +// TComputed<'Partial', [TRef]> +// > +// // prettier-ignore +// function FromRef($ref: Ref): TFromRef { +// return Computed('Partial', [Ref($ref)]) as never +// } // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( +type TFromRest = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest, TSchema>]> - : Acc + ? TFromRest, TSchema>]> + : Result ) // prettier-ignore -function FromRest(T: [...T], K: K): TFromRest { - return T.map(L => IndexFromPropertyKey(L, K)) as never +function FromRest(types: [...Types], key: K): TFromRest { + return types.map(left => IndexFromPropertyKey(left, key)) as never } // ------------------------------------------------------------------ // FromIntersectRest // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersectRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? L extends TNever - ? TFromIntersectRest - : TFromIntersectRest - : Acc +type TFromIntersectRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TNever + ? TFromIntersectRest + : TFromIntersectRest + : Result ) // prettier-ignore -function FromIntersectRest(T: [...T]): TFromIntersectRest { - return T.filter(L => !IsNever(L)) as never +function FromIntersectRest(types: [...Types]): TFromIntersectRest { + return types.filter(left => !IsNever(left)) as never } // prettier-ignore -type TFromIntersect = ( - TIntersectEvaluated>> +type TFromIntersect = ( + TIntersectEvaluated>> ) // prettier-ignore -function FromIntersect(T: [...T], K: K): TFromIntersect { +function FromIntersect(types: [...Types], key: Key): TFromIntersect { return ( - IntersectEvaluated(FromIntersectRest(FromRest(T as TSchema[], K))) + IntersectEvaluated(FromIntersectRest(FromRest(types as TSchema[], key))) ) as never } // ------------------------------------------------------------------ @@ -110,47 +141,51 @@ function FromIntersect(T: [...T], K: // // ------------------------------------------------------------------ // prettier-ignore -type TFromUnionRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? L extends TNever +type TFromUnionRest = + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TNever ? [] - : TFromUnionRest - : Acc + : TFromUnionRest + : Result // prettier-ignore -function FromUnionRest(T: [...T]): TFromUnionRest { +function FromUnionRest(types: [...Types]): TFromUnionRest { return ( - T.some(L => IsNever(L)) + types.some(L => IsNever(L)) ? [] - : T + : types ) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( - TUnionEvaluated>> +type TFromUnion = ( + TUnionEvaluated>> ) // prettier-ignore -function FromUnion(T: [...T], K: K): TFromUnion { +function FromUnion(types: [...Types], key: Key): TFromUnion { return ( - UnionEvaluated(FromUnionRest(FromRest(T as TSchema[], K))) + UnionEvaluated(FromUnionRest(FromRest(types as TSchema[], key))) ) as never } // ------------------------------------------------------------------ // FromTuple // ------------------------------------------------------------------ + // prettier-ignore -type TFromTuple = ( - K extends keyof T ? T[K] : - K extends '[number]' ? TUnionEvaluated : - TNever -) +type TFromTuple : + Key extends keyof Types + ? Types[Key] extends infer Type extends TSchema + ? Type + : TNever + : TNever +)> = Result // prettier-ignore -function FromTuple(T: [...T], K: K): TFromTuple { +function FromTuple(types: [...Types], key: Key): TFromTuple { return ( - K in T ? T[K as number] : - K === '[number]' ? UnionEvaluated(T) : + key === '[number]' ? UnionEvaluated(types) : + key in types ? types[key as number] : Never() ) as never } @@ -158,62 +193,54 @@ function FromTuple(T: [...T], K: K): // FromArray // ------------------------------------------------------------------ // prettier-ignore -type TFromArray = ( - K extends '[number]' - ? T - : TNever +type TFromArray = ( + Key extends '[number]' ? Type : TNever ) // prettier-ignore -function FromArray(T: T, K: K): TFromArray { - return ( - K === '[number]' - ? T - : Never() - ) as never +function FromArray(type: Type, key: Key): TFromArray { + // ... ? + return (key === '[number]' ? type : Never()) as never } // ------------------------------------------------------------------ // FromProperty // ------------------------------------------------------------------ -type AssertPropertyKey = Assert +type AssertPropertyKey = Assert // prettier-ignore -type TFromProperty< - T extends TProperties, - K extends PropertyKey, -> = ( +type TFromProperty}` extends `${AssertPropertyKey}` - ? T[AssertPropertyKey] + : `${AssertPropertyKey}` extends `${AssertPropertyKey}` + ? Properties[AssertPropertyKey] : TNever -) +)> = Result // prettier-ignore -function FromProperty(T: T, K: K): TFromProperty { - return (K in T ? T[K as string] : Never()) as never +function FromProperty(properties: Properties, key: Key): TFromProperty { + return (key in properties ? properties[key as string] : Never()) as never } // ------------------------------------------------------------------ // FromKey // ------------------------------------------------------------------ // prettier-ignore -export type TIndexFromPropertyKey = ( - T extends TRecursive ? TIndexFromPropertyKey : - T extends TIntersect ? TFromIntersect : - T extends TUnion ? TFromUnion : - T extends TTuple ? TFromTuple : - T extends TArray ? TFromArray : - T extends TObject ? TFromProperty : +export type TIndexFromPropertyKey = ( + Type extends TRecursive ? TIndexFromPropertyKey : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TTuple ? TFromTuple : + Type extends TArray ? TFromArray : + Type extends TObject ? TFromProperty : TNever ) // prettier-ignore -export function IndexFromPropertyKey(T: T, K: K): TIndexFromPropertyKey { +export function IndexFromPropertyKey(type: Type, key: Key): TIndexFromPropertyKey { return ( - IsIntersect(T) ? FromIntersect(T.allOf, K) : - IsUnion(T) ? FromUnion(T.anyOf, K) : - IsTuple(T) ? FromTuple(T.items ?? [], K) : - IsArray(T) ? FromArray(T.items, K) : - IsObject(T) ? FromProperty(T.properties, K) : + IsIntersect(type) ? FromIntersect(type.allOf, key) : + IsUnion(type) ? FromUnion(type.anyOf, key) : + IsTuple(type) ? FromTuple(type.items ?? [], key) : + IsArray(type) ? FromArray(type.items, key) : + IsObject(type) ? FromProperty(type.properties, key) : Never() ) as never } @@ -221,52 +248,83 @@ export function IndexFromPropertyKey(T // FromKeys // ------------------------------------------------------------------ // prettier-ignore -export type TIndexFromPropertyKeys = ( - K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] - ? TIndexFromPropertyKeys, TSchema>]> - : Acc +export type TIndexFromPropertyKeys = ( + PropertyKeys extends [infer Left extends PropertyKey, ...infer Right extends PropertyKey[]] + ? TIndexFromPropertyKeys, TSchema>]> + : Result ) // prettier-ignore -export function IndexFromPropertyKeys(T: T, K: [...K]): TIndexFromPropertyKeys { - return K.map(L => IndexFromPropertyKey(T, L)) as never +export function IndexFromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys]): TIndexFromPropertyKeys { + return propertyKeys.map(left => IndexFromPropertyKey(type, left)) as never } // ------------------------------------------------------------------ // FromSchema // ------------------------------------------------------------------ // prettier-ignore -type FromSchema = ( - TUnionEvaluated> +type TFromType, +> = TUnionEvaluated +// prettier-ignore +function FromType(type: Type, propertyKeys: [...PropertyKeys]): TFromType { + const result = IndexFromPropertyKeys(type, propertyKeys as PropertyKey[]) + return UnionEvaluated(result) as never +} +// ------------------------------------------------------------------ +// UnionFromPropertyKeys +// ------------------------------------------------------------------ +// prettier-ignore +type TUnionFromPropertyKeys = ( + PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] + ? Key extends TLiteralValue + ? TUnionFromPropertyKeys]> + : TUnionFromPropertyKeys + : TUnionEvaluated ) // prettier-ignore -function FromSchema(T: T, K: [...K]): FromSchema { - return ( - UnionEvaluated(IndexFromPropertyKeys(T, K as PropertyKey[])) - ) as never +function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { + const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) + return UnionEvaluated(result) as never } // ------------------------------------------------------------------ // TIndex // ------------------------------------------------------------------ +// prettier-ignore (do not export this type) +type TResolvePropertyKeys = Key extends TSchema ? TIndexPropertyKeys : Key +// prettier-ignore (do not export this type) +type TResolveTypeKey = Key extends PropertyKey[] ? TUnionFromPropertyKeys : Key // prettier-ignore -export type TIndex = ( - FromSchema +export type TIndex = ( + Key extends TMappedResult ? TIndexFromMappedResult : + Key extends TMappedKey ? TIndexFromMappedKey : + [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Index', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Index', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Index', [Type, TResolveTypeKey]> : + TFromType> ) /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedResult +export function Index(type: Type, key: readonly [...Key], options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedKey +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index>(T: T, K: K, options?: SchemaOptions): TIndex +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(T: TSchema, K: any, options?: SchemaOptions): any { - // mapped-types - if (IsMappedResult(K)) return IndexFromMappedResult(T, K, options) - if (IsMappedKey(K)) return IndexFromMappedKey(T, K, options) - // prettier-ignore - return CreateType( - IsSchema(K) - ? FromSchema(T, IndexPropertyKeys(K)) - : FromSchema(T, K as string[]) - , options) as never +// prettier-ignore +export function Index(type: any, key: any, options?: SchemaOptions): any { + const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key + const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key + const isTypeRef: boolean = IsRef(type) + const isKeyRef: boolean = IsRef(key) + return ( + IsMappedResult(key) ? IndexFromMappedResult(type, key, options) : + IsMappedKey(key) ? IndexFromMappedKey(type, key, options) : + (isTypeRef && isKeyRef) ? Computed('Index', [type, typeKey], options) : + (!isTypeRef && isKeyRef) ? Computed('Index', [type, typeKey], options) : + (isTypeRef && !isKeyRef) ? Computed('Index', [type, typeKey], options) : + CreateType(FromType(type, propertyKeys), options) + ) as never } diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts index a0c73484b..637009345 100644 --- a/src/type/intersect/intersect-evaluated.ts +++ b/src/type/intersect/intersect-evaluated.ts @@ -46,77 +46,77 @@ import { IsOptional, IsTransform } from '../guard/kind' // IsIntersectOptional // ------------------------------------------------------------------ // prettier-ignore -type TIsIntersectOptional = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? L extends TOptional - ? TIsIntersectOptional +type TIsIntersectOptional = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TOptional + ? TIsIntersectOptional : false : true ) // prettier-ignore -function IsIntersectOptional(T: T): TIsIntersectOptional { - return T.every(L => IsOptional(L)) as never +function IsIntersectOptional(types: [...Types]): TIsIntersectOptional { + return types.every(left => IsOptional(left)) as never } // ------------------------------------------------------------------ // RemoveOptionalFromType // ------------------------------------------------------------------ // prettier-ignore -type TRemoveOptionalFromType = ( - T extends TReadonly ? TReadonly> : - T extends TOptional ? TRemoveOptionalFromType : - T +type TRemoveOptionalFromType = ( + Type extends TReadonly ? TReadonly> : + Type extends TOptional ? TRemoveOptionalFromType : + Type ) // prettier-ignore -function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { +function RemoveOptionalFromType(type: Type): TRemoveOptionalFromType { return ( - Discard(T, [OptionalKind]) + Discard(type, [OptionalKind]) ) as never } // ------------------------------------------------------------------ // RemoveOptionalFromRest // ------------------------------------------------------------------ // prettier-ignore -type TRemoveOptionalFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? L extends TOptional - ? TRemoveOptionalFromRest]> - : TRemoveOptionalFromRest - : Acc +type TRemoveOptionalFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TOptional + ? TRemoveOptionalFromRest]> + : TRemoveOptionalFromRest + : Result ) // prettier-ignore -function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { - return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as never +function RemoveOptionalFromRest(types: [...Types]): TRemoveOptionalFromRest { + return types.map(left => IsOptional(left) ? RemoveOptionalFromType(left) : left) as never } // ------------------------------------------------------------------ // ResolveIntersect // ------------------------------------------------------------------ // prettier-ignore -type TResolveIntersect = ( - TIsIntersectOptional extends true - ? TOptional>> - : TIntersect> +type TResolveIntersect = ( + TIsIntersectOptional extends true + ? TOptional>> + : TIntersect> ) // prettier-ignore -function ResolveIntersect(T: [...T], options: SchemaOptions): TResolveIntersect { +function ResolveIntersect(types: [...Types], options: SchemaOptions): TResolveIntersect { return ( - IsIntersectOptional(T) - ? Optional(IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options)) - : IntersectCreate(RemoveOptionalFromRest(T) as TSchema[], options) + IsIntersectOptional(types) + ? Optional(IntersectCreate(RemoveOptionalFromRest(types) as TSchema[], options)) + : IntersectCreate(RemoveOptionalFromRest(types) as TSchema[], options) ) as never } // ------------------------------------------------------------------ // IntersectEvaluated // ------------------------------------------------------------------ // prettier-ignore -export type TIntersectEvaluated = ( - T extends [] ? TNever : - T extends [TSchema] ? T[0] : - TResolveIntersect +export type TIntersectEvaluated = ( + Types extends [TSchema] ? Types[0] : + Types extends [] ? TNever : + TResolveIntersect ) /** `[Json]` Creates an evaluated Intersect type */ -export function IntersectEvaluated>(T: [...T], options: IntersectOptions = {}): R { - if (T.length === 0) return Never(options) as never - if (T.length === 1) return CreateType(T[0], options) as never - if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') - return ResolveIntersect(T, options) as never +export function IntersectEvaluated>(types: [...Types], options: IntersectOptions = {}): Result { + if (types.length === 1) return CreateType(types[0], options) as never + if (types.length === 0) return Never(options) as never + if (types.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') + return ResolveIntersect(types, options) as never } diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts index 66275057f..999d3d2af 100644 --- a/src/type/intersect/intersect.ts +++ b/src/type/intersect/intersect.ts @@ -40,15 +40,15 @@ import { IsTransform } from '../guard/kind' // Intersect // ------------------------------------------------------------------ // prettier-ignore -export type Intersect = ( - T extends [] ? TNever : - T extends [TSchema] ? T[0] : - TIntersect +export type Intersect = ( + Types extends [TSchema] ? Types[0] : + Types extends [] ? TNever : + TIntersect ) /** `[Json]` Creates an evaluated Intersect type */ -export function Intersect(T: [...T], options?: IntersectOptions): Intersect { - if (T.length === 0) return Never(options) as never - if (T.length === 1) return CreateType(T[0], options) as never - if (T.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') - return IntersectCreate(T, options) as never +export function Intersect(types: [...Types], options?: IntersectOptions): Intersect { + if (types.length === 1) return CreateType(types[0], options) as never + if (types.length === 0) return Never(options) as never + if (types.some((schema) => IsTransform(schema))) throw new Error('Cannot intersect transform types') + return IntersectCreate(types, options) as never } diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 9991b7c15..0be659752 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -30,55 +30,47 @@ import type { SchemaOptions } from '../schema/index' import type { Ensure, Evaluate } from '../helpers/index' import type { TProperties } from '../object/index' import { MappedResult, type TMappedResult } from '../mapped/index' -import { KeyOf, type TKeyOf } from './keyof' +import { KeyOf, type TKeyOfFromType } from './keyof' import { Clone } from '../clone/value' // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - K extends TProperties -> = ( - { [K2 in keyof K]: TKeyOf } +type TFromProperties = ( + { [K2 in keyof Properties]: TKeyOfFromType } ) // prettier-ignore -function FromProperties< - K extends TProperties ->(K: K, options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(K)) Acc[K2] = KeyOf(K[K2], Clone(options)) - return Acc as never +function FromProperties(properties: Properties, options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = KeyOf(properties[K2], Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - R extends TMappedResult -> = ( - Evaluate> +type TFromMappedResult = ( + Evaluate> ) // prettier-ignore -function FromMappedResult< - R extends TMappedResult ->(R: R, options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, options) as never +function FromMappedResult(mappedResult: MappedResult, options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, options) as never } // ------------------------------------------------------------------ // KeyOfFromMappedResult // ------------------------------------------------------------------ // prettier-ignore export type TKeyOfFromMappedResult< - R extends TMappedResult, - P extends TProperties = TFromMappedResult + MappedResult extends TMappedResult, + Properties extends TProperties = TFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function KeyOfFromMappedResult< - R extends TMappedResult, - P extends TProperties = TFromMappedResult ->(R: R, options?: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, options) + return MappedResult(properties) as never } diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 28f945bb1..25cfd2df3 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -44,58 +44,56 @@ import { IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsRecord } from '../g // FromRest // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Result ) // prettier-ignore -function FromRest(T: [...T]): TFromRest { - const Acc = [] as PropertyKey[][] - for(const L of T) Acc.push(KeyOfPropertyKeys(L)) - return Acc as never +function FromRest(types: [...Types]): TFromRest { + const result = [] as PropertyKey[][] + for(const L of types) result.push(KeyOfPropertyKeys(L)) + return result as never } // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect< - T extends TSchema[], - C extends PropertyKey[][] = TFromRest, - R extends PropertyKey[] = TSetUnionMany -> = R +type TFromIntersect, + PropertyKeys extends PropertyKey[] = TSetUnionMany +> = PropertyKeys // prettier-ignore -function FromIntersect(T: [...T]): TFromIntersect { - const C = FromRest(T) as PropertyKey[][] - const R = SetUnionMany(C) - return R as never +function FromIntersect(types: [...Types]): TFromIntersect { + const propertyKeysArray = FromRest(types) as PropertyKey[][] + const propertyKeys = SetUnionMany(propertyKeysArray) + return propertyKeys as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion< - T extends TSchema[], - C extends PropertyKey[][] = TFromRest, - R extends PropertyKey[] = TSetIntersectMany -> = R +type TFromUnion, + PropertyKeys extends PropertyKey[] = TSetIntersectMany +> = PropertyKeys // prettier-ignore -function FromUnion(T: [...T]): TFromUnion { - const C = FromRest(T) as PropertyKey[][] - const R = SetIntersectMany(C) - return R as never +function FromUnion(types: [...Types]): TFromUnion { + const propertyKeysArray = FromRest(types) as PropertyKey[][] + const propertyKeys = SetIntersectMany(propertyKeysArray) + return propertyKeys as never } // ------------------------------------------------------------------ // FromTuple // ------------------------------------------------------------------ // prettier-ignore -type TFromTuple = - T extends [infer _ extends TSchema, ...infer R extends TSchema[]] - ? TFromTuple, [...Acc, I]> +type TFromTuple = + Types extends [infer _ extends TSchema, ...infer R extends TSchema[]] + ? TFromTuple, [...Acc, Indexer]> : Acc // prettier-ignore -function FromTuple(T: [...T]): TFromTuple { - return T.map((_, I) => I.toString()) as never +function FromTuple(types: [...Types]): TFromTuple { + return types.map((_, indexer) => indexer.toString()) as never } // ------------------------------------------------------------------ // FromArray @@ -114,11 +112,11 @@ function FromArray<_ extends TSchema>(_: _): TFromArray<_> { // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = ( - UnionToTuple +type TFromProperties = ( + UnionToTuple ) // prettier-ignore -function FromProperties(T: T): TFromProperties { +function FromProperties(T: Properties): TFromProperties { return ( globalThis.Object.getOwnPropertyNames(T) ) as never @@ -140,25 +138,25 @@ function FromPatternProperties(patternProperties: Record): // KeyOfPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOfPropertyKeys = ( - T extends TRecursive ? TKeyOfPropertyKeys : - T extends TIntersect ? TFromIntersect : - T extends TUnion ? TFromUnion : - T extends TTuple ? TFromTuple : - T extends TArray ? TFromArray : - T extends TObject ? TFromProperties : +export type TKeyOfPropertyKeys = ( + Type extends TRecursive ? TKeyOfPropertyKeys : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TTuple ? TFromTuple : + Type extends TArray ? TFromArray : + Type extends TObject ? TFromProperties : [] ) /** Returns a tuple of PropertyKeys derived from the given TSchema. */ // prettier-ignore -export function KeyOfPropertyKeys(T: T): TKeyOfPropertyKeys { +export function KeyOfPropertyKeys(type: Type): TKeyOfPropertyKeys { return ( - IsIntersect(T) ? FromIntersect(T.allOf) : - IsUnion(T) ? FromUnion(T.anyOf) : - IsTuple(T) ? FromTuple(T.items ?? []) : - IsArray(T) ? FromArray(T.items) : - IsObject(T) ? FromProperties(T.properties) : - IsRecord(T) ? FromPatternProperties(T.patternProperties) : + IsIntersect(type) ? FromIntersect(type.allOf) : + IsUnion(type) ? FromUnion(type.anyOf) : + IsTuple(type) ? FromTuple(type.items ?? []) : + IsArray(type) ? FromArray(type.items) : + IsObject(type) ? FromProperties(type.properties) : + IsRecord(type) ? FromPatternProperties(type.patternProperties) : [] ) as never } diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index 2e4c42df6..fbd20df62 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -33,6 +33,8 @@ import type { TMappedResult } from '../mapped/index' import type { SchemaOptions } from '../schema/index' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Number, type TNumber } from '../number/index' +import { Computed, TComputed } from '../computed/index' +import { Ref, type TRef } from '../ref/index' import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from './keyof-property-keys' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from-mapped-result' @@ -40,46 +42,72 @@ import { KeyOfFromMappedResult, type TKeyOfFromMappedResult } from './keyof-from // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult } from '../guard/kind' +import { IsMappedResult, IsRef, IsComputed } from '../guard/kind' +// ------------------------------------------------------------------ +// FromComputed +// ------------------------------------------------------------------ +// prettier-ignore +type TFromComputed = Ensure< + TComputed<'KeyOf', [TComputed]> +> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('KeyOf', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRef = Ensure< + TComputed<'KeyOf', [TRef]> +> +// prettier-ignore +function FromRef($ref: Ref): TFromRef { + return Computed('KeyOf', [Ref($ref)]) as never +} +// ------------------------------------------------------------------ +// KeyOfFromType +// ------------------------------------------------------------------ +// prettier-ignore +/** `[Internal]` Used by KeyOfFromMappedResult */ +export type TKeyOfFromType, + PropertyKeyTypes extends TSchema[] = TKeyOfPropertyKeysToRest, + Result = TUnionEvaluated +> = Ensure +// prettier-ignore +function KeyOfFromType(type: Type, options?: SchemaOptions): TKeyOfFromType { + const propertyKeys = KeyOfPropertyKeys(type) as PropertyKey[] + const propertyKeyTypes = KeyOfPropertyKeysToRest(propertyKeys) + const result = UnionEvaluated(propertyKeyTypes) + return CreateType(result, options) as never +} // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOfPropertyKeysToRest = ( - T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] +export type TKeyOfPropertyKeysToRest = ( + PropertyKeys extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] ? L extends '[number]' - ? TKeyOfPropertyKeysToRest - : TKeyOfPropertyKeysToRest>]> - : Acc + ? TKeyOfPropertyKeysToRest + : TKeyOfPropertyKeysToRest>]> + : Result ) // prettier-ignore -export function KeyOfPropertyKeysToRest(T: [...T]): TKeyOfPropertyKeysToRest { - return T.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never +export function KeyOfPropertyKeysToRest(propertyKeys: [...PropertyKeys]): TKeyOfPropertyKeysToRest { + return propertyKeys.map(L => L === '[number]' ? Number() : Literal(L as TLiteralValue)) as never } // ------------------------------------------------------------------ -// KeyOfTypeResolve +// TKeyOf // ------------------------------------------------------------------ // prettier-ignore -export type TKeyOf< - T extends TSchema, - K extends PropertyKey[] = TKeyOfPropertyKeys, - S extends TSchema[] = TKeyOfPropertyKeysToRest, - U = TUnionEvaluated -> = ( - Ensure +export type TKeyOf = ( + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TMappedResult ? TKeyOfFromMappedResult : + TKeyOfFromType ) /** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: T, options?: SchemaOptions): TKeyOfFromMappedResult -/** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: T, options?: SchemaOptions): TKeyOf -/** `[Json]` Creates a KeyOf type */ -export function KeyOf(T: TSchema, options?: SchemaOptions): never { - if (IsMappedResult(T)) { - return KeyOfFromMappedResult(T, options) as never - } else { - const K = KeyOfPropertyKeys(T) - const S = KeyOfPropertyKeysToRest(K) - const U = UnionEvaluated(S) - return CreateType(U, options) as never - } +export function KeyOf(type: Type, options?: SchemaOptions): TKeyOf { + return (IsComputed(type) ? FromComputed(type.target, type.parameters) : IsRef(type) ? FromRef(type.$ref) : IsMappedResult(type) ? KeyOfFromMappedResult(type, options) : KeyOfFromType(type, options)) as never } diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts new file mode 100644 index 000000000..47bf60851 --- /dev/null +++ b/src/type/module/compute.ts @@ -0,0 +1,402 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { CreateType } from '../create/index' +import { Ensure, Evaluate } from '../helpers/index' +import { type TSchema } from '../schema/index' +import { Array, type TArray } from '../array/index' +import { Awaited, type TAwaited } from '../awaited/index' +import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' +import { TComputed } from '../computed/index' +import { Constructor, type TConstructor } from '../constructor/index' +import { Index, type TIndex } from '../indexed/index' +import { Function, type TFunction } from '../function/index' +import { Intersect, type TIntersect, type TIntersectEvaluated } from '../intersect/index' +import { Iterator, type TIterator } from '../iterator/index' +import { KeyOf, type TKeyOf } from '../keyof/index' +import { Object, type TObject, type TProperties } from '../object/index' +import { Omit, type TOmit } from '../omit/index' +import { Pick, type TPick } from '../pick/index' +import { Never, type TNever } from '../never/index' +import { Partial, TPartial } from '../partial/index' +import { Record, type TRecordOrObject } from '../record/index' +import { type TRef } from '../ref/index' +import { Required, TRequired } from '../required/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Union, type TUnion, type TUnionEvaluated } from '../union/index' + +// ------------------------------------------------------------------ +// KindGuard +// ------------------------------------------------------------------ +import * as KindGuard from '../guard/kind' + +// ------------------------------------------------------------------ +// DerefParameters +// +// Dereferences TComputed parameters. It is important to note that +// dereferencing anything other than these parameters may result +// inference and evaluation problems, potentially resulting in a +// stack overflow. All open TRef types must be preserved !!! +// +// ------------------------------------------------------------------ +// prettier-ignore +type TDerefParameters = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TRef + ? TDerefParameters]> + : TDerefParameters]> + : Result +) +// prettier-ignore +function DerefParameters(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { + return types.map((type) => { + return KindGuard.IsRef(type) + ? Deref(moduleProperties, type.$ref) + : FromType(moduleProperties, type) + }) as never +} +// prettier-ignore +type TDeref + ? TDeref + : TFromType + : TNever +)> = Result +// prettier-ignore +function Deref(moduleProperties: ModuleProperties, ref: Ref): TDeref { + return ( + ref in moduleProperties + ? KindGuard.IsRef(moduleProperties[ref]) + ? Deref(moduleProperties, moduleProperties[ref].$ref) + : FromType(moduleProperties, moduleProperties[ref]) + : Never() + ) as never +} +// ------------------------------------------------------------------ +// Awaited +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAwaited = ( + Parameters extends [infer T0 extends TSchema] ? TAwaited : never +) +// prettier-ignore +function FromAwaited(parameters: Parameters): TFromAwaited { + return Awaited(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Index +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIndex = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TIndex : never +) +// prettier-ignore +function FromIndex(parameters: Parameters): TFromIndex { + return Index(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// KeyOf +// ------------------------------------------------------------------ +// prettier-ignore +type TFromKeyOf = ( + Parameters extends [infer T0 extends TSchema] ? TKeyOf : never +) +// prettier-ignore +function FromKeyOf(parameters: Parameters): TFromAwaited { + return KeyOf(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Partial +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPartial = ( + Parameters extends [infer T0 extends TSchema] ? TPartial : never +) +// prettier-ignore +function FromPartial(parameters: Parameters): TFromPartial { + return Partial(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Omit +// ------------------------------------------------------------------ +// prettier-ignore +type TFromOmit = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TOmit : never +) +// prettier-ignore +function FromOmit(parameters: Parameters): TFromPick { + return Omit(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// Pick +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPick = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TPick : never +) +// prettier-ignore +function FromPick(parameters: Parameters): TFromPick { + return Pick(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// Record +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRecord = ( + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TRecordOrObject : never +) +// prettier-ignore +function FromRecord(parameters: Parameters): TFromPick { + return Record(parameters[0], parameters[1]) as never +} +// ------------------------------------------------------------------ +// Required +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRequired = ( + Parameters extends [infer T0 extends TSchema] ? TRequired : never +) +// prettier-ignore +function FromRequired(parameters: Parameters): TFromPick { + return Required(parameters[0]) as never +} +// ------------------------------------------------------------------ +// Computed +// ------------------------------------------------------------------ +// prettier-ignore +type TFromComputed +> = ( + Target extends 'Awaited' ? TFromAwaited : + Target extends 'Index' ? TFromIndex : + Target extends 'KeyOf' ? TFromKeyOf : + Target extends 'Partial' ? TFromPartial : + Target extends 'Omit' ? TFromOmit : + Target extends 'Pick' ? TFromPick : + Target extends 'Record' ? TFromRecord : + Target extends 'Required' ? TFromRequired : + TNever +) +// prettier-ignore +function FromComputed(moduleProperties: ModuleProperties, target: Target, parameters: Parameters): TFromComputed { + const dereferenced = DerefParameters(moduleProperties, parameters) + return ( + target === 'Awaited' ? FromAwaited(dereferenced) : + target === 'Index' ? FromIndex(dereferenced) : + target === 'KeyOf' ? FromKeyOf(dereferenced) : + target === 'Partial' ? FromPartial(dereferenced) : + target === 'Omit' ? FromOmit(dereferenced) : + target === 'Pick' ? FromPick(dereferenced) : + target === 'Record' ? FromRecord(dereferenced) : + target === 'Required' ? FromRequired(dereferenced) : + Never() + ) as never +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject = Ensure +}>>> +function FromObject(moduleProperties: ModuleProperties, properties: Properties): TFromObject { + return Object( + globalThis.Object.keys(properties).reduce((result, key) => { + return { ...result, [key]: FromType(moduleProperties, properties[key]) as never } + }, {} as TProperties), + ) as never +} +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +type TFromConstructor = ( + TConstructor, TFromType> +) +// prettier-ignore +function FromConstructor( + moduleProperties: ModuleProperties, + parameters: [...Parameters], + instanceType: InstanceType, +): TFromConstructor { + return Constructor(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, instanceType) as never) as never +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TFromFunction = Ensure< + Ensure, TFromType>> +> +// prettier-ignore +function FromFunction( + moduleProperties: ModuleProperties, + parameters: [...Parameters], + returnType: ReturnType, +): TFromFunction { + return Function(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, returnType) as never) as never +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + Ensure>> +) +function FromTuple(moduleProperties: ModuleProperties, types: [...Types]): TFromTuple { + return Tuple(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntersect = ( + Ensure>> +) +function FromIntersect(moduleProperties: ModuleProperties, types: [...Types]): TFromIntersect { + return Intersect(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion = ( + Ensure>> +) +function FromUnion(moduleProperties: ModuleProperties, types: [...Types]): TFromUnion { + return Union(FromRest(moduleProperties, types as never)) as never +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray = ( + Ensure>> +) +function FromArray(moduleProperties: ModuleProperties, type: Type): TFromArray { + return Array(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAsyncIterator = ( + TAsyncIterator> +) +function FromAsyncIterator(moduleProperties: ModuleProperties, type: Type): TFromAsyncIterator { + return AsyncIterator(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIterator = ( + TIterator> +) +function FromIterator(moduleProperties: ModuleProperties, type: Type): TFromIterator { + return Iterator(FromType(moduleProperties, type)) +} +// ------------------------------------------------------------------ +// Rest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromRest]> + : Result +) +function FromRest(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { + return types.map((type) => FromType(moduleProperties, type)) as never +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromType = ( + Type extends TComputed ? TFromComputed : + Type extends TObject ? TFromObject : + Type extends TConstructor ? TFromConstructor : + Type extends TFunction ? TFromFunction : + Type extends TTuple ? TFromTuple : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TArray ? TFromArray : + Type extends TAsyncIterator ? TFromAsyncIterator : + Type extends TIterator ? TFromIterator : + Type +) +// prettier-ignore +export function FromType(moduleProperties: ModuleProperties, type: Type): TFromType { + return ( + // Note: The 'as never' is required due to excessive resolution of TIndex. In fact TIndex, TPick, TOmit and + // all need re-implementation to remove the PropertyKey[] selector. Reimplementation of these types should + // be a priority as there is a potential for the current inference to break on TS compiler changes. + KindGuard.IsComputed(type) ? CreateType(FromComputed(moduleProperties, type.target, type.parameters) as never) : + KindGuard.IsObject(type) ? CreateType(FromObject(moduleProperties, type.properties), type) : + KindGuard.IsConstructor(type) ? CreateType(FromConstructor(moduleProperties, type.parameters, type.returns), type) : + KindGuard.IsFunction(type) ? CreateType(FromFunction(moduleProperties, type.parameters, type.returns), type) : + KindGuard.IsTuple(type)? CreateType(FromTuple(moduleProperties, type.items || []), type) : + KindGuard.IsIntersect(type) ? CreateType(FromIntersect(moduleProperties, type.allOf), type) : + KindGuard.IsUnion(type) ? CreateType(FromUnion(moduleProperties, type.anyOf), type) : + KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : + KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : + KindGuard.IsIterator(type) ? CreateType(FromIterator(moduleProperties, type.items), type) : + type + ) as never +} +// ------------------------------------------------------------------ +// ComputeType +// ------------------------------------------------------------------ +// prettier-ignore +export type TComputeType = ( + Key extends keyof ModuleProperties + ? TFromType + : TNever +) +// prettier-ignore +export function ComputeType(moduleProperties: ModuleProperties, key: Key): TComputeType { + return ( + key in moduleProperties + ? FromType(moduleProperties, moduleProperties[key as keyof ModuleProperties]) + : Never() + ) as never +} +// ------------------------------------------------------------------ +// ComputeModuleProperties +// ------------------------------------------------------------------ +// prettier-ignore +export type TComputeModuleProperties = Evaluate<{ + [Key in keyof ModuleProperties]: TComputeType +}> +// prettier-ignore +export function ComputeModuleProperties(moduleProperties: ModuleProperties): TComputeModuleProperties { + return globalThis.Object.getOwnPropertyNames(moduleProperties).reduce((result, key) => { + return {...result, [key]: ComputeType(moduleProperties, key) } + }, {} as TProperties) as never +} diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts new file mode 100644 index 000000000..082b7f733 --- /dev/null +++ b/src/type/module/infer.ts @@ -0,0 +1,162 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Ensure, Evaluate } from '../helpers/index' +import { TSchema } from '../schema/index' +import { TArray } from '../array/index' +import { TAsyncIterator } from '../async-iterator/index' +import { TConstructor } from '../constructor/index' +import { TFunction } from '../function/index' +import { TIntersect } from '../intersect/index' +import { TIterator } from '../iterator/index' +import { TObject, TProperties } from '../object/index' +import { TOptional } from '../optional/index' +import { TReadonly } from '../readonly/index' +import { TRef } from '../ref/index' +import { TTuple } from '../tuple/index' +import { TUnion } from '../union/index' +import { Static } from '../static/index' + +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TInferArray = ( + Ensure>> +) +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +type TInferAsyncIterator = ( + Ensure>> +) +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +type TInferConstructor = Ensure< + new (...args: TInferTuple) => TInfer +> +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TInferFunction = Ensure< + (...args: TInferTuple) => TInfer +> +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +type TInferIterator = ( + Ensure>> +) +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +type TInferIntersect = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferIntersect> + : Result +) +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +type ReadonlyOptionalPropertyKeys = { [K in keyof Properties]: Properties[K] extends TReadonly ? (Properties[K] extends TOptional ? K : never) : never }[keyof Properties] +type ReadonlyPropertyKeys = { [K in keyof Source]: Source[K] extends TReadonly ? (Source[K] extends TOptional ? never : K) : never }[keyof Source] +type OptionalPropertyKeys = { [K in keyof Source]: Source[K] extends TOptional ? (Source[K] extends TReadonly ? never : K) : never }[keyof Source] +type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> +// prettier-ignore +type InferPropertiesWithModifiers> = Evaluate<( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +)> +// prettier-ignore +type InferProperties = InferPropertiesWithModifiers +}> +// prettier-ignore +type TInferObject = ( + InferProperties +) +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TInferTuple = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferTuple]> + : Result +) +// ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +// prettier-ignore +type TInferRef = ( + Ref extends keyof ModuleProperties ? TInfer : unknown +) +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TInferUnion = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TInferUnion> + : Result +) +// ------------------------------------------------------------------ +// Infer +// ------------------------------------------------------------------ +// prettier-ignore +type TInfer = ( + Type extends TArray ? TInferArray : + Type extends TAsyncIterator ? TInferAsyncIterator : + Type extends TConstructor ? TInferConstructor : + Type extends TFunction ? TInferFunction : + Type extends TIntersect ? TInferIntersect : + Type extends TIterator ? TInferIterator : + Type extends TObject ? TInferObject : + Type extends TRef ? TInferRef : + Type extends TTuple ? TInferTuple : + Type extends TUnion ? TInferUnion : + Static +) +// ------------------------------------------------------------------ +// InferFromModuleKey +// ------------------------------------------------------------------ +/** Inference Path for Imports. This type is used to compute TImport `static` */ +// prettier-ignore +export type TInferFromModuleKey = ( + Key extends keyof ModuleProperties + ? TInfer + : never +) diff --git a/src/type/module/module.ts b/src/type/module/module.ts index 3fef83c80..a4c2544e9 100644 --- a/src/type/module/module.ts +++ b/src/type/module/module.ts @@ -26,124 +26,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Ensure } from '../helpers/index' import { CreateType } from '../create/index' import { Kind } from '../symbols/index' import { SchemaOptions, TSchema } from '../schema/index' -import { TObject, TProperties } from '../object/index' -import { TConstructor } from '../constructor/index' -import { TFunction } from '../function/index' -import { TTuple } from '../tuple/index' -import { TIntersect } from '../intersect/index' -import { TUnion } from '../union/index' -import { TArray } from '../array/index' -import { TAsyncIterator } from '../async-iterator/index' -import { TIterator } from '../iterator/index' -import { TLiteral, TLiteralValue } from '../literal/index' -import { TAny } from '../any/index' -import { TBigInt } from '../bigint/index' -import { TBoolean } from '../boolean/index' -import { TDate } from '../date/index' -import { TInteger } from '../integer/index' -import { TNever } from '../never/index' -import { TNumber } from '../number/index' -import { TNull } from '../null/index' -import { TRef } from '../ref/index' -import { TRegExp } from '../regexp/index' -import { TString } from '../string/index' -import { TSymbol } from '../symbol/index' -import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' -import { TUint8Array } from '../uint8array/index' -import { TUndefined } from '../undefined/index' -import { TUnknown } from '../unknown/index' -import { TVoid } from '../void/index' +import { TProperties } from '../object/index' import { Static } from '../static/index' // ------------------------------------------------------------------ -// Infer +// Module Infrastructure Types // ------------------------------------------------------------------ -// prettier-ignore -type InferImport = ( - Infer -) -// prettier-ignore -type InferRef = ( - Ref extends keyof Properties ? Infer : never -) -// prettier-ignore -type InferObject = { - [K in keyof Properties]: Infer -} & {} -// prettier-ignore -type InferConstructor = Ensure< - new (...args: InferTuple) => Infer -> -// prettier-ignore -type InferFunction = Ensure< - (...args: InferTuple) => Infer -> -// prettier-ignore -type InferTuple = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferTuple]> - : Result -) -// prettier-ignore -type InferIntersect = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferIntersect> - : Result -) -// prettier-ignore -type InferUnion = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? InferUnion> - : Result -) -// prettier-ignore -type InferArray = ( - Ensure>> -) -// prettier-ignore -type InferAsyncIterator = ( - Ensure>> -) -// prettier-ignore -type InferIterator = ( - Ensure>> -) -// prettier-ignore -type Infer = ( - Type extends TImport ? InferImport : - Type extends TRef ? InferRef : - Type extends TObject ? InferObject : - Type extends TConstructor ? InferConstructor : - Type extends TFunction ? InferFunction : - Type extends TTuple ? InferTuple : - Type extends TIntersect ? InferIntersect : - Type extends TUnion ? InferUnion : - Type extends TArray ? InferArray : - Type extends TAsyncIterator ? InferAsyncIterator : - Type extends TIterator ? InferIterator : - Type extends TTemplateLiteral ? Static> : - Type extends TLiteral ? S : - Type extends TAny ? any : - Type extends TBigInt ? bigint : - Type extends TBoolean ? boolean : - Type extends TDate ? Date : - Type extends TInteger ? number : - Type extends TNever ? never : - Type extends TNumber ? number : - Type extends TRegExp ? string : - Type extends TString ? string : - Type extends TSymbol ? symbol : - Type extends TNull ? null : - Type extends TUint8Array ? Uint8Array : - Type extends TUndefined ? undefined : - Type extends TUnknown ? unknown : - Type extends TVoid ? void : - never -) +import { ComputeModuleProperties, TComputeModuleProperties } from './compute' +import { TInferFromModuleKey } from './infer' + // ------------------------------------------------------------------ // Definitions // ------------------------------------------------------------------ @@ -157,7 +51,7 @@ export interface TDefinitions extends TSch // prettier-ignore export interface TImport extends TSchema { [Kind]: 'Import' - static: InferImport + static: TInferFromModuleKey $defs: ModuleProperties $ref: Key } @@ -165,22 +59,22 @@ export interface TImport { - constructor(private readonly $defs: Properties, private readonly options: SchemaOptions = {}) {} - - /** `[Json]` Returns the Type definitions for this module */ - public Defs(): TDefinitions { - return CreateType({ $defs: this.ResolveDefinitionsWithIdentifiers() }, this.options) as never +export class TModule> { + private readonly $defs: ComputedModuleProperties + constructor($defs: ModuleProperties) { + const computed = ComputeModuleProperties($defs) + const identified = this.WithIdentifiers(computed as never) + this.$defs = identified as never } /** `[Json]` Imports a Type by Key. */ - public Import(key: Key, options?: SchemaOptions): TImport { - return CreateType({ [Kind]: 'Import', $defs: this.ResolveDefinitionsWithIdentifiers(), $ref: key }, options) as never + public Import(key: Key, options?: SchemaOptions): TImport { + return CreateType({ [Kind]: 'Import', $defs: this.$defs, $ref: key }, options) as never } - /** `[Internal]` For each definition, assign an `$id` property. */ - private ResolveDefinitionsWithIdentifiers() { - return globalThis.Object.getOwnPropertyNames(this.$defs).reduce((Result, Key) => ( - { ...Result, [Key]: { ...this.$defs[Key], $id: Key }} - ), {}) + // prettier-ignore + private WithIdentifiers($defs: ComputedModuleProperties): ComputedModuleProperties { + return globalThis.Object.getOwnPropertyNames($defs).reduce((result, key) => { + return { ...result, [key]: { ...$defs[key], $id: key }} + }, {}) as never } } /** `[Json]` Creates a Type Definition Module. */ diff --git a/src/type/not/not.ts b/src/type/not/not.ts index 256a3c8eb..eb283dfdf 100644 --- a/src/type/not/not.ts +++ b/src/type/not/not.ts @@ -37,6 +37,6 @@ export interface TNot extends TSchema { not: T } /** `[Json]` Creates a Not type */ -export function Not(not: T, options?: SchemaOptions): TNot { - return CreateType({ [Kind]: 'Not', not }, options) as never +export function Not(type: Type, options?: SchemaOptions): TNot { + return CreateType({ [Kind]: 'Not', not: type }, options) as never } diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index 5eeb68660..2a2a4ae94 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -36,77 +36,52 @@ import { Clone } from '../clone/value' // FromPropertyKey // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKey< - T extends TSchema, - K extends PropertyKey, -> = { - [_ in K]: TOmit - } +type TFromPropertyKey = { + [_ in Key]: TOmit +} // prettier-ignore -function FromPropertyKey< - T extends TSchema, - K extends PropertyKey, ->(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { - return { - [K]: Omit(T, [K], Clone(options)) - } as never +function FromPropertyKey(type: Type, key: Key, options?: SchemaOptions): TFromPropertyKey { + return { [key]: Omit(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKeys< - T extends TSchema, - K extends PropertyKey[], - Acc extends TProperties = {} -> = ( - K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] - ? TFromPropertyKeys> - : Acc +type TFromPropertyKeys = ( + PropertyKeys extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] + ? TFromPropertyKeys> + : Result ) // prettier-ignore -function FromPropertyKeys< - T extends TSchema, - K extends PropertyKey[] ->(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { - return K.reduce((Acc, LK) => { - return { ...Acc, ...FromPropertyKey(T, LK, options) } +function FromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromPropertyKeys { + return propertyKeys.reduce((Acc, LK) => { + return { ...Acc, ...FromPropertyKey(type, LK, options) } }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedKey< - T extends TSchema, - K extends TMappedKey, -> = ( - TFromPropertyKeys +type TFromMappedKey = ( + TFromPropertyKeys ) // prettier-ignore -function FromMappedKey< - T extends TSchema, - K extends TMappedKey, ->(T: T, K: K, options?: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as never +function FromMappedKey(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ // OmitFromMappedKey // ------------------------------------------------------------------ // prettier-ignore -export type TOmitFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey +export type TOmitFromMappedKey > = ( - TMappedResult

      + TMappedResult ) // prettier-ignore -export function OmitFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey ->(T: T, K: K, options?: SchemaOptions): TMappedResult

      { - const P = FromMappedKey(T, K, options) - return MappedResult(P) as never +export function OmitFromMappedKey +>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult { + const properties = FromMappedKey(type, mappedKey, options) + return MappedResult(properties) as never } diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index 205047ea8..5abac5f60 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -37,55 +37,43 @@ import { Clone } from '../clone/value' // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - P extends TProperties, - K extends PropertyKey[], -> = ( - { [K2 in keyof P]: TOmit } +type TFromProperties = ( + { [K2 in keyof Properties]: TOmit } ) // prettier-ignore -function FromProperties< - P extends TProperties, - K extends PropertyKey[], ->(P: P, K: [...K], options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Omit(P[K2], K, Clone(options)) - return Acc as never +function FromProperties(properties: Properties, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = Omit(properties[K2], propertyKeys, Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], -> = ( - Evaluate> +type TFromMappedResult = ( + Evaluate> ) // prettier-ignore -function FromMappedResult< - R extends TMappedResult, - K extends PropertyKey[] ->(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as never +function FromMappedResult(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, propertyKeys, options) as never } // ------------------------------------------------------------------ // TOmitFromMappedResult // ------------------------------------------------------------------ // prettier-ignore export type TOmitFromMappedResult< - T extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function OmitFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options?: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, K, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, propertyKeys, options) + return MappedResult(properties) as never } diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index 99a947706..d52a8b477 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -28,112 +28,167 @@ THE SOFTWARE. import { CreateType } from '../create/type' import { Discard } from '../discard/discard' +import { TransformKind } from '../symbols/symbols' import type { SchemaOptions, TSchema } from '../schema/index' import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' import type { TMappedKey, TMappedResult } from '../mapped/index' +import { Computed, TComputed } from '../computed/index' +import { Literal, TLiteral, TLiteralValue } from '../literal/index' +import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' import { Intersect, type TIntersect } from '../intersect/index' import { Union, type TUnion } from '../union/index' import { Object, type TObject, type TProperties } from '../object/index' -import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' +import { type TRef } from '../ref/index' + +// ------------------------------------------------------------------ +// Mapped +// ------------------------------------------------------------------ import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key' import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result' -import { TransformKind } from '../symbols/symbols' + // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult } from '../guard/kind' +import { IsMappedKey, IsIntersect, IsUnion, IsObject, IsSchema, IsMappedResult, IsLiteralValue, IsRef } from '../guard/kind' +import { IsArray as IsArrayValue } from '../guard/value' // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromIntersect]> - : Acc +type TFromIntersect = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromIntersect]> + : Result ) // prettier-ignore -function FromIntersect(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as never +function FromIntersect(types: Types, propertyKeys: PropertyKeys) { + return types.map((type) => OmitResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( +type TFromUnion = ( T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromUnion]> - : Acc + ? TFromUnion]> + : Result ) // prettier-ignore -function FromUnion(T: T, K: K) { - return T.map((T) => OmitResolve(T, K)) as never +function FromUnion(types: Types, propertyKeys: PropertyKeys) { + return types.map((type) => OmitResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromProperty // ------------------------------------------------------------------ // prettier-ignore -function FromProperty, K extends PropertyKey>(T: T, K: K): TProperties { - const { [K]: _, ...R } = T +function FromProperty(properties: Properties, key: Key): TProperties { + const { [key]: _, ...R } = properties return R } // prettier-ignore -type TFromProperties> = Evaluate> +type TFromProperties> = ( + Evaluate> +) // prettier-ignore -function FromProperties(T: T, K: K) { - return K.reduce((T, K2) => FromProperty(T, K2), T as TProperties) +function FromProperties(properties: Properties, propertyKeys: PropertyKeys) { + return propertyKeys.reduce((T, K2) => FromProperty(T, K2), properties as TProperties) } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure +type TFromObject = Ensure )>> // prettier-ignore -function FromObject(T: T, K: K): TFromObject { - const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) - const properties = FromProperties(T['properties'], K) - return Object(properties, options) as never +function FromObject(properties: Properties, propertyKeys: PropertyKeys): TFromObject { + const options = Discard(properties, [TransformKind, '$id', 'required', 'properties']) + const omittedProperties = FromProperties(properties['properties'], propertyKeys) + return Object(omittedProperties, options) as never } // ------------------------------------------------------------------ -// OmitResolve +// UnionFromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -function OmitResolve(T: T, K: [...K]): TOmit { - return ( - IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : - IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? FromObject(T, K) : - Object({}) - ) as never +type TUnionFromPropertyKeys = ( + PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] + ? Key extends TLiteralValue + ? TUnionFromPropertyKeys]> + : TUnionFromPropertyKeys + : TUnion +) +// prettier-ignore +function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { + const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) + return Union(result) as never } +// ------------------------------------------------------------------ +// TOmitResolve +// ------------------------------------------------------------------ // prettier-ignore -export type TOmit = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject, K> : +export type TOmitResolve = ( + Properties extends TRecursive ? TRecursive> : + Properties extends TIntersect ? TIntersect> : + Properties extends TUnion ? TUnion> : + Properties extends TObject ? TFromObject, PropertyKeys> : TObject<{}> ) +// prettier-ignore +function OmitResolve(properties: Properties, propertyKeys: [...PropertyKeys]): TOmitResolve { + return ( + IsIntersect(properties) ? Intersect(FromIntersect(properties.allOf, propertyKeys)) : + IsUnion(properties) ? Union(FromUnion(properties.anyOf, propertyKeys)) : + IsObject(properties) ? FromObject(properties, propertyKeys) : + Object({}) + ) as never +} // ------------------------------------------------------------------ // TOmit +// +// This mapping logic is to overly complex because of the decision +// to use PropertyKey[] as the default selector. The PropertyKey[] +// did make TMapped types simpler to implement, but a non-TSchema +// selector makes supporting TComputed awkward as it requires +// generalization via TSchema. This type should be reimplemented +// in the next major revision to support TSchema as the primary +// selector. +// // ------------------------------------------------------------------ -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit>(T: T, K: K, options?: SchemaOptions): TOmit -/** `[Json]` Constructs a type whose keys are omitted from the given type */ -export function Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit -export function Omit(T: TSchema, K: any, options?: SchemaOptions): any { - // mapped - if (IsMappedKey(K)) return OmitFromMappedKey(T, K, options) - if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options) - // non-mapped - const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - // special: mapping types require overridable options - return CreateType({ ...OmitResolve(T, I), ...options }) +// prettier-ignore (do not export this type) +type TResolvePropertyKeys = Key extends TSchema ? TIndexPropertyKeys : Key +// prettier-ignore (do not export this type) +type TResolveTypeKey = Key extends PropertyKey[] ? TUnionFromPropertyKeys : Key +// prettier-ignore +export type TOmit = ( + Type extends TMappedResult ? TOmitFromMappedResult> : + Key extends TMappedKey ? TOmitFromMappedKey : + [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Omit', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Omit', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Omit', [Type, TResolveTypeKey]> : + TOmitResolve> +) +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Omit(type: Type, key: readonly [...Key], options?: SchemaOptions): TOmit +/** `[Json]` Constructs a type whose keys are picked from the given type */ +export function Omit(type: Type, key: Key, options?: SchemaOptions): TOmit +/** `[Json]` Constructs a type whose keys are picked from the given type */ +// prettier-ignore +export function Omit(type: any, key: any, options?: SchemaOptions): any { + const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key + const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key + const isTypeRef: boolean = IsRef(type) + const isKeyRef: boolean = IsRef(key) + return ( + IsMappedResult(type) ? OmitFromMappedResult(type, propertyKeys, options) : + IsMappedKey(key) ? OmitFromMappedKey(type, key, options) : + (isTypeRef && isKeyRef) ? Computed('Omit', [type, typeKey], options) : + (!isTypeRef && isKeyRef) ? Computed('Omit', [type, typeKey], options) : + (isTypeRef && !isKeyRef) ? Computed('Omit', [type, typeKey], options) : + CreateType({ ...OmitResolve(type, propertyKeys), ...options }) + ) as never } diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 21bd5cf3e..3cfb48724 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -31,73 +31,99 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { Evaluate, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' import { type TReadonlyOptional } from '../readonly-optional/index' +import { type TComputed, Computed } from '../computed/index' import { type TOptional, Optional } from '../optional/index' import { type TReadonly } from '../readonly/index' import { type TRecursive } from '../recursive/index' import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' +import { type TRef, Ref } from '../ref/index' import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' - import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' +import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' + // ------------------------------------------------------------------ -// FromRest +// FromComputed // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc -) +type TFromComputed = Ensure< + TComputed<'Partial', [TComputed]> +> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Partial', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ // prettier-ignore -function FromRest(T: [...T]): TFromRest { - return T.map(L => PartialResolve(L)) as never +type TFromRef = Ensure< + TComputed<'Partial', [TRef]> +> +// prettier-ignore +function FromRef($ref: Ref): TFromRef { + return Computed('Partial', [Ref($ref)]) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = Evaluate<{ - [K in keyof T]: - T[K] extends (TReadonlyOptional) ? TReadonlyOptional : - T[K] extends (TReadonly) ? TReadonlyOptional : - T[K] extends (TOptional) ? TOptional : - TOptional +type TFromProperties = Evaluate<{ + [K in keyof Properties]: + Properties[K] extends (TReadonlyOptional) ? TReadonlyOptional : + Properties[K] extends (TReadonly) ? TReadonlyOptional : + Properties[K] extends (TOptional) ? TOptional : + TOptional }> // prettier-ignore -function FromProperties(T: T): TFromProperties { - const Acc = {} as TProperties - for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Optional(T[K]) - return Acc as never +function FromProperties(properties: Properties): TFromProperties { + const partialProperties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(properties)) partialProperties[K] = Optional(properties[K]) + return partialProperties as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure = Ensure )>> // prettier-ignore -function FromObject(T: T): TFromObject { +function FromObject(T: Type): TFromObject { const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) const properties = FromProperties(T['properties']) return Object(properties, options) as never } // ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Result +) +// prettier-ignore +function FromRest(types: [...Types]): TFromRest { + return types.map(type => PartialResolve(type)) as never +} +// ------------------------------------------------------------------ // PartialResolve // ------------------------------------------------------------------ // prettier-ignore -function PartialResolve(T: T): TPartial { +function PartialResolve(type: Type): TPartial { return ( - IsIntersect(T) ? Intersect(FromRest(T.allOf)) : - IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? FromObject(T) : + IsComputed(type) ? FromComputed(type.target, type.parameters) : + IsRef(type) ? FromRef(type.$ref) : + IsIntersect(type) ? Intersect(FromRest(type.allOf)) : + IsUnion(type) ? Union(FromRest(type.anyOf)) : + IsObject(type) ? FromObject(type) : Object({}) ) as never } @@ -106,22 +132,24 @@ function PartialResolve(T: T): TPartial { // ------------------------------------------------------------------ // prettier-ignore export type TPartial = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : + T extends TRecursive ? TRecursive> : + T extends TComputed ? TFromComputed : + T extends TRef ? TFromRef : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult +export function Partial(type: MappedResult, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: T, options?: SchemaOptions): TPartial +export function Partial(type: Type, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(T: TSchema, options?: SchemaOptions): any { - if (IsMappedResult(T)) { - return PartialFromMappedResult(T, options) +export function Partial(type: TSchema, options?: SchemaOptions): any { + if (IsMappedResult(type)) { + return PartialFromMappedResult(type, options) } else { // special: mapping types require overridable options - return CreateType({ ...PartialResolve(T), ...options }) + return CreateType({ ...PartialResolve(type), ...options }) } } diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index 062e1e527..86933aa58 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -36,77 +36,54 @@ import { Clone } from '../clone/value' // FromPropertyKey // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKey< - T extends TSchema, - K extends PropertyKey, -> = { - [_ in K]: TPick - } +type TFromPropertyKey = { + [_ in Key]: TPick +} // prettier-ignore -function FromPropertyKey< - T extends TSchema, - K extends PropertyKey, ->(T: T, K: K, options?: SchemaOptions): TFromPropertyKey { +function FromPropertyKey(type: Type, key: Key, options?: SchemaOptions): TFromPropertyKey { return { - [K]: Pick(T, [K], Clone(options)) + [key]: Pick(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ // FromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -type TFromPropertyKeys< - T extends TSchema, - K extends PropertyKey[], - Acc extends TProperties = {} -> = ( - K extends [infer LK extends PropertyKey, ...infer RK extends PropertyKey[]] - ? TFromPropertyKeys> - : Acc +type TFromPropertyKeys = ( + PropertyKeys extends [infer LeftKey extends PropertyKey, ...infer RightKeys extends PropertyKey[]] + ? TFromPropertyKeys> + : Result ) // prettier-ignore -function FromPropertyKeys< - T extends TSchema, - K extends PropertyKey[] ->(T: T, K: [...K], options?: SchemaOptions): TFromPropertyKeys { - return K.reduce((Acc, LK) => { - return { ...Acc, ...FromPropertyKey(T, LK, options) } +function FromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromPropertyKeys { + return propertyKeys.reduce((result, leftKey) => { + return { ...result, ...FromPropertyKey(type, leftKey, options) } }, {} as TProperties) as never } // ------------------------------------------------------------------ // FromMappedKey // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedKey< - T extends TSchema, - K extends TMappedKey, -> = ( - TFromPropertyKeys +type TFromMappedKey = ( + TFromPropertyKeys ) // prettier-ignore -function FromMappedKey< - T extends TSchema, - K extends TMappedKey, ->(T: T, K: K, options?: SchemaOptions): TFromMappedKey { - return FromPropertyKeys(T, K.keys, options) as never +function FromMappedKey(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TFromMappedKey { + return FromPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ // PickFromMappedKey // ------------------------------------------------------------------ // prettier-ignore -export type TPickFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey +export type TPickFromMappedKey > = ( - TMappedResult

      + TMappedResult ) // prettier-ignore -export function PickFromMappedKey< - T extends TSchema, - K extends TMappedKey, - P extends TProperties = TFromMappedKey ->(T: T, K: K, options?: SchemaOptions): TMappedResult

      { - const P = FromMappedKey(T, K, options) - return MappedResult(P) as never +export function PickFromMappedKey +>(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedResult { + const properties = FromMappedKey(type, mappedKey, options) + return MappedResult(properties) as never } diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 3abf5ad17..7fa74f9c4 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -37,55 +37,41 @@ import { Clone } from '../clone/value' // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties< - P extends TProperties, - K extends PropertyKey[], -> = ( - { [K2 in keyof P]: TPick } +type TFromProperties = ( + { [K2 in keyof Properties]: TPick } ) // prettier-ignore -function FromProperties< - P extends TProperties, - K extends PropertyKey[], ->(P: P, K: [...K], options?: SchemaOptions): TFromProperties { - const Acc = {} as TProperties - for(const K2 of globalThis.Object.getOwnPropertyNames(P)) Acc[K2] = Pick(P[K2], K, Clone(options)) - return Acc as never +function FromProperties(properties: Properties, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromProperties { + const result = {} as TProperties + for(const K2 of globalThis.Object.getOwnPropertyNames(properties)) result[K2] = Pick(properties[K2], propertyKeys, Clone(options)) + return result as never } // ------------------------------------------------------------------ // FromMappedResult // ------------------------------------------------------------------ // prettier-ignore -type TFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], -> = ( - Evaluate> +type TFromMappedResult = ( + Evaluate> ) // prettier-ignore -function FromMappedResult< - R extends TMappedResult, - K extends PropertyKey[] ->(R: R, K: [...K], options?: SchemaOptions): TFromMappedResult { - return FromProperties(R.properties, K, options) as never +function FromMappedResult(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TFromMappedResult { + return FromProperties(mappedResult.properties, propertyKeys, options) as never } // ------------------------------------------------------------------ // PickFromMappedResult // ------------------------------------------------------------------ // prettier-ignore -export type TPickFromMappedResult< - T extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult +export type TPickFromMappedResult > = ( - Ensure> + Ensure> ) // prettier-ignore export function PickFromMappedResult< - R extends TMappedResult, - K extends PropertyKey[], - P extends TProperties = TFromMappedResult ->(R: R, K: [...K], options?: SchemaOptions): TMappedResult

      { - const P = FromMappedResult(R, K, options) - return MappedResult(P) as never + MappedResult extends TMappedResult, + PropertyKeys extends PropertyKey[], + Properties extends TProperties = TFromMappedResult +>(mappedResult: MappedResult, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedResult { + const properties = FromMappedResult(mappedResult, propertyKeys, options) + return MappedResult(properties) as never } diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index bde1e17f0..f9b988f46 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -31,59 +31,70 @@ import { Discard } from '../discard/discard' import type { TSchema, SchemaOptions } from '../schema/index' import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index' import { type TRecursive } from '../recursive/index' -import { type TIntersect, Intersect } from '../intersect/index' -import { type TUnion, Union } from '../union/index' -import { type TObject, type TProperties, type TPropertyKey, Object } from '../object/index' -import type { TMappedKey, TMappedResult } from '../mapped/index' +import { Computed, type TComputed } from '../computed/index' +import { Intersect, type TIntersect } from '../intersect/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Object, type TObject, type TProperties, type TPropertyKey } from '../object/index' +import { Union, type TUnion } from '../union/index' +import { type TMappedKey, type TMappedResult } from '../mapped/index' +import { type TRef } from '../ref/index' import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index' -import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' -import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' import { TransformKind } from '../symbols/symbols' // ------------------------------------------------------------------ -// TypeGuard +// Guards +// ------------------------------------------------------------------ +import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema, IsLiteralValue, IsRef } from '../guard/kind' +import { IsArray as IsArrayValue } from 'src/value/guard' + +// ------------------------------------------------------------------ +// Infrastructure // ------------------------------------------------------------------ -import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema } from '../guard/kind' +import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key' +import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result' + // ------------------------------------------------------------------ // FromIntersect // ------------------------------------------------------------------ // prettier-ignore -type FromIntersect = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromIntersect]> - : Acc -function FromIntersect(T: T, K: K) { - return T.map((T) => PickResolve(T, K)) as FromIntersect +type TFromIntersect = + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromIntersect]> + : Result +function FromIntersect(types: Types, propertyKeys: PropertyKeys): TFromIntersect { + return types.map((type) => PickResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromUnion // ------------------------------------------------------------------ // prettier-ignore -type FromUnion = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? FromUnion]> - : Acc +type TFromUnion = + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromUnion]> + : Result // prettier-ignore -function FromUnion(T: T, K: K) { - return T.map((T) => PickResolve(T, K)) as FromUnion +function FromUnion(types: Types, propertyKeys: PropertyKeys): TFromUnion { + return types.map((type) => PickResolve(type, propertyKeys)) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties> = Evaluate> +type TFromProperties> = ( + Evaluate> +) // prettier-ignore -function FromProperties(T: T, K: K): TFromProperties { - const Acc = {} as TProperties - for(const K2 of K) if(K2 in T) Acc[K2 as TPropertyKey] = T[K2 as keyof T] - return Acc as never +function FromProperties(properties: Properties, propertyKeys: PropertyKeys): TFromProperties { + const result = {} as TProperties + for(const K2 of propertyKeys) if(K2 in properties) result[K2 as TPropertyKey] = properties[K2 as keyof Properties] + return result as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure +type TFromObject = Ensure )>> // prettier-ignore function FromObject(T: T, K: K): TFromObject { @@ -92,39 +103,86 @@ function FromObject(T: T, K: K): TFr return Object(properties, options) as never } // ------------------------------------------------------------------ -// PickResolve +// UnionFromPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -function PickResolve(T: T, K: [...K]): TPick { +type TUnionFromPropertyKeys = ( + PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] + ? Key extends TLiteralValue + ? TUnionFromPropertyKeys]> + : TUnionFromPropertyKeys + : TUnion +) +// prettier-ignore +function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { + const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) + return Union(result) as never +} +// ------------------------------------------------------------------ +// TPickResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TPickResolve = ( + Properties extends TRecursive ? TRecursive> : + Properties extends TIntersect ? TIntersect> : + Properties extends TUnion ? TUnion> : + Properties extends TObject ? TFromObject, PropertyKeys> : + TObject<{}> +) +// prettier-ignore +function PickResolve(properties: Properties, propertyKeys: [...PropertyKeys]): TPickResolve { return ( - IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) : - IsUnion(T) ? Union(FromUnion(T.anyOf, K)) : - IsObject(T) ? FromObject(T, K) : + IsIntersect(properties) ? Intersect(FromIntersect(properties.allOf, propertyKeys)) : + IsUnion(properties) ? Union(FromUnion(properties.anyOf, propertyKeys)) : + IsObject(properties) ? FromObject(properties, propertyKeys) : Object({}) ) as never } +// ------------------------------------------------------------------ +// TPick +// +// This mapping logic is to overly complex because of the decision +// to use PropertyKey[] as the default selector. The PropertyKey[] +// did make TMapped types simpler to implement, but a non-TSchema +// selector makes supporting TComputed awkward as it requires +// generalization via TSchema. This type should be reimplemented +// in the next major revision to support TSchema as the primary +// selector. +// +// ------------------------------------------------------------------ +// prettier-ignore (do not export this type) +type TResolvePropertyKeys = Key extends TSchema ? TIndexPropertyKeys : Key +// prettier-ignore (do not export this type) +type TResolveTypeKey = Key extends PropertyKey[] ? TUnionFromPropertyKeys : Key // prettier-ignore -export type TPick = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject, K> : - TObject<{}> - -/** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult +export type TPick = ( + Type extends TMappedResult ? TPickFromMappedResult> : + Key extends TMappedKey ? TPickFromMappedKey : + [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Pick', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Pick', [Type, TResolveTypeKey]> : + [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Pick', [Type, TResolveTypeKey]> : + TPickResolve> +) /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey +export function Pick(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick>(T: T, K: K, options?: SchemaOptions): TPick +export function Pick(type: Type, key: Key, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ -export function Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick -export function Pick(T: TSchema, K: any, options?: SchemaOptions): any { - // mapped - if (IsMappedKey(K)) return PickFromMappedKey(T, K, options) - if (IsMappedResult(T)) return PickFromMappedResult(T, K, options) - // non-mapped - const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[]) - // special: mapping types require overridable options - return CreateType({ ...PickResolve(T, I), ...options }) +// prettier-ignore +export function Pick(type: any, key: any, options?: SchemaOptions): any { + const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key + const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key + const isTypeRef: boolean = IsRef(type) + const isKeyRef: boolean = IsRef(key) + return ( + IsMappedResult(type) ? PickFromMappedResult(type, propertyKeys, options) : + IsMappedKey(key) ? PickFromMappedKey(type, key, options) : + (isTypeRef && isKeyRef) ? Computed('Pick', [type, typeKey], options) : + (!isTypeRef && isKeyRef) ? Computed('Pick', [type, typeKey], options) : + (isTypeRef && !isKeyRef) ? Computed('Pick', [type, typeKey], options) : + CreateType({ ...PickResolve(type, propertyKeys), ...options }) + ) as never } diff --git a/src/type/record/record.ts b/src/type/record/record.ts index e32ae7a26..34a87f74d 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -31,6 +31,7 @@ import type { TSchema } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' import { type TAny } from '../any/index' +import { Computed, type TComputed } from '../computed/index' import { Object, type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions } from '../object/index' import { type TLiteral, type TLiteralValue } from '../literal/index' import { Never, type TNever } from '../never/index' @@ -40,6 +41,7 @@ import { type TString } from '../string/index' import { type TInteger } from '../integer/index' import { type TNumber } from '../number/index' import { type TEnum } from '../enum/index' +import { type TRef } from '../ref/index' import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLiteral } from '../template-literal/index' import { PatternStringExact, PatternNumberExact, PatternNeverExact } from '../patterns/index' @@ -52,7 +54,8 @@ import { IsUndefined } from '../guard/value' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion } from '../guard/kind' +import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion, IsRef, IsComputed } from '../guard/kind' + // ------------------------------------------------------------------ // RecordCreateFromPattern // ------------------------------------------------------------------ @@ -218,34 +221,38 @@ export interface TRecord = - K extends TTemplateLiteral ? TFromTemplateLiteralKey : - K extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) - K extends TUnion ? TFromUnionKey : - K extends TLiteral ? TFromLiteralKey : - K extends TInteger ? TFromIntegerKey : - K extends TNumber ? TFromNumberKey : - K extends TRegExp ? TFromRegExpKey : - K extends TString ? TFromStringKey : - K extends TAny ? TFromAnyKey : - K extends TNever ? TFromNeverKey : +export type TRecordOrObject = + Type extends TRef ? TComputed<'Record', [Key, Type]> : + Key extends TRef ? TComputed<'Record', [Key, Type]> : + Key extends TTemplateLiteral ? TFromTemplateLiteralKey : + Key extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) + Key extends TUnion ? TFromUnionKey : + Key extends TLiteral ? TFromLiteralKey : + Key extends TInteger ? TFromIntegerKey : + Key extends TNumber ? TFromNumberKey : + Key extends TRegExp ? TFromRegExpKey : + Key extends TString ? TFromStringKey : + Key extends TAny ? TFromAnyKey : + Key extends TNever ? TFromNeverKey : TNever // ------------------------------------------------------------------ // TRecordOrObject // ------------------------------------------------------------------ /** `[Json]` Creates a Record type */ -export function Record(K: K, T: T, options: ObjectOptions = {}): TRecordOrObject { +export function Record(key: Key, type: Type, options: ObjectOptions = {}): TRecordOrObject { // prettier-ignore return ( - IsUnion(K) ? FromUnionKey(K.anyOf, T, options) : - IsTemplateLiteral(K) ? FromTemplateLiteralKey(K, T, options) : - IsLiteral(K) ? FromLiteralKey(K.const, T, options) : - IsInteger(K) ? FromIntegerKey(K, T, options) : - IsNumber(K) ? FromNumberKey(K, T, options) : - IsRegExp(K) ? FromRegExpKey(K, T, options) : - IsString(K) ? FromStringKey(K, T, options) : - IsAny(K) ? FromAnyKey(K, T, options) : - IsNever(K) ? FromNeverKey(K, T, options) : + IsRef(type) ? Computed('Record', [key, type]) : + IsRef(key) ? Computed('Record', [key, type]) : + IsUnion(key) ? FromUnionKey(key.anyOf, type, options) : + IsTemplateLiteral(key) ? FromTemplateLiteralKey(key, type, options) : + IsLiteral(key) ? FromLiteralKey(key.const, type, options) : + IsInteger(key) ? FromIntegerKey(key, type, options) : + IsNumber(key) ? FromNumberKey(key, type, options) : + IsRegExp(key) ? FromRegExpKey(key, type, options) : + IsString(key) ? FromStringKey(key, type, options) : + IsAny(key) ? FromAnyKey(key, type, options) : + IsNever(key) ? FromNeverKey(key, type, options) : Never(options) ) as never } diff --git a/src/type/required/required.ts b/src/type/required/required.ts index fab0632f2..7ae6a234d 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -31,12 +31,14 @@ import type { TSchema, SchemaOptions } from '../schema/index' import type { Evaluate, Ensure } from '../helpers/index' import type { TMappedResult } from '../mapped/index' import { type TReadonlyOptional } from '../readonly-optional/index' +import { type TComputed, Computed } from '../computed/index' import { type TOptional } from '../optional/index' import { type TReadonly } from '../readonly/index' import { type TRecursive } from '../recursive/index' +import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' -import { type TObject, type TProperties, Object } from '../object/index' +import { type TRef, Ref } from '../ref/index' import { OptionalKind, TransformKind } from '../symbols/index' import { Discard } from '../discard/index' import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result' @@ -44,60 +46,84 @@ import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './requ // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject } from '../guard/kind' +import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' + // ------------------------------------------------------------------ -// FromRest +// FromComputed // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest]> - : Acc -) +type TFromComputed = Ensure< + TComputed<'Required', [TComputed]> +> +// prettier-ignore +function FromComputed(target: Target, parameters: Parameters): TFromComputed { + return Computed('Required', [Computed(target, parameters)]) as never +} +// ------------------------------------------------------------------ +// FromRef +// ------------------------------------------------------------------ // prettier-ignore -function FromRest(T: [...T]) : TFromRest { - return T.map(L => RequiredResolve(L)) as never +type TFromRef = Ensure< + TComputed<'Required', [TRef]> +> +// prettier-ignore +function FromRef($ref: Ref): TFromRef { + return Computed('Required', [Ref($ref)]) as never } // ------------------------------------------------------------------ // FromProperties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties = Evaluate<{ - [K in keyof T]: - T[K] extends (TReadonlyOptional) ? TReadonly : - T[K] extends (TReadonly) ? TReadonly : - T[K] extends (TOptional) ? S : - T[K] +type TFromProperties = Evaluate<{ + [K in keyof Properties]: + Properties[K] extends (TReadonlyOptional) ? TReadonly : + Properties[K] extends (TReadonly) ? TReadonly : + Properties[K] extends (TOptional) ? S : + Properties[K] }> // prettier-ignore -function FromProperties(T: T) { - const Acc = {} as TProperties - for(const K of globalThis.Object.getOwnPropertyNames(T)) Acc[K] = Discard(T[K], [OptionalKind]) as TSchema - return Acc as never +function FromProperties(properties: Properties) { + const requiredProperties = {} as TProperties + for(const K of globalThis.Object.getOwnPropertyNames(properties)) requiredProperties[K] = Discard(properties[K], [OptionalKind]) as TSchema + return requiredProperties as never } // ------------------------------------------------------------------ // FromObject // ------------------------------------------------------------------ // prettier-ignore -type TFromObject = Ensure = Ensure )>> // prettier-ignore -function FromObject(T: T): TFromObject { - const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) - const properties = FromProperties(T['properties']) +function FromObject(type: Type): TFromObject { + const options = Discard(type, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(type['properties']) return Object(properties, options) as never } // ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRest = ( + Types extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? TFromRest]> + : Result +) +// prettier-ignore +function FromRest(types: [...Types]) : TFromRest { + return types.map(type => RequiredResolve(type)) as never +} +// ------------------------------------------------------------------ // RequiredResolve // ------------------------------------------------------------------ - // prettier-ignore -function RequiredResolve(T: T): TRequired { +function RequiredResolve(type: Type): TRequired { return ( - IsIntersect(T) ? Intersect(FromRest(T.allOf)) : - IsUnion(T) ? Union(FromRest(T.anyOf)) : - IsObject(T) ? FromObject(T) : + IsComputed(type) ? FromComputed(type.target, type.parameters) : + IsRef(type) ? FromRef(type.$ref) : + IsIntersect(type) ? Intersect(FromRest(type.allOf)) : + IsUnion(type) ? Union(FromRest(type.anyOf)) : + IsObject(type) ? FromObject(type) : Object({}) ) as never } @@ -105,23 +131,25 @@ function RequiredResolve(T: T): TRequired { // TRequired // ------------------------------------------------------------------ // prettier-ignore -export type TRequired = ( - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : +export type TRequired = ( + Type extends TRecursive ? TRecursive> : + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TIntersect ? TIntersect> : + Type extends TUnion ? TUnion> : + Type extends TObject ? TFromObject> : TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult +export function Required(type: MappedResult, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): TRequired +export function Required(type: Type, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ -export function Required(T: T, options?: SchemaOptions): never { - if (IsMappedResult(T)) { - return RequiredFromMappedResult(T, options) as never +export function Required(type: Type, options?: SchemaOptions): never { + if (IsMappedResult(type)) { + return RequiredFromMappedResult(type, options) as never } else { // special: mapping types require overridable options - return CreateType({ ...RequiredResolve(T), ...options }) as never + return CreateType({ ...RequiredResolve(type), ...options }) as never } } diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts index 67d564f9d..80b092c79 100644 --- a/src/type/tuple/tuple.ts +++ b/src/type/tuple/tuple.ts @@ -52,11 +52,11 @@ export interface TTuple extends TSchema { maxItems: number } /** `[Json]` Creates a Tuple type */ -export function Tuple(items: [...T], options?: SchemaOptions): TTuple { +export function Tuple(types: [...Types], options?: SchemaOptions): TTuple { // prettier-ignore return CreateType( - items.length > 0 ? - { [Kind]: 'Tuple', type: 'array', items, additionalItems: false, minItems: items.length, maxItems: items.length } : - { [Kind]: 'Tuple', type: 'array', minItems: items.length, maxItems: items.length }, + types.length > 0 ? + { [Kind]: 'Tuple', type: 'array', items: types, additionalItems: false, minItems: types.length, maxItems: types.length } : + { [Kind]: 'Tuple', type: 'array', minItems: types.length, maxItems: types.length }, options) as never } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index fad81ea22..101231eb2 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -49,11 +49,11 @@ import { Void, type TVoid } from '../void/index' /** JavaScript Type Builder with Static Resolution for TypeScript */ export class JavaScriptTypeBuilder extends JsonTypeBuilder { /** `[JavaScript]` Creates a AsyncIterator type */ - public AsyncIterator(items: T, options?: SchemaOptions): TAsyncIterator { + public AsyncIterator(items: Type, options?: SchemaOptions): TAsyncIterator { return AsyncIterator(items, options) } /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ - public Awaited(schema: T, options?: SchemaOptions): TAwaited { + public Awaited(schema: Type, options?: SchemaOptions): TAwaited { return Awaited(schema, options) } /** `[JavaScript]` Creates a BigInt type */ @@ -61,35 +61,35 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return BigInt(options) } /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options?: SchemaOptions): TConstructorParameters { + public ConstructorParameters(schema: Type, options?: SchemaOptions): TConstructorParameters { return ConstructorParameters(schema, options) } /** `[JavaScript]` Creates a Constructor type */ - public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - return Constructor(parameters, returns, options) + public Constructor(parameters: [...Parameters], instanceType: InstanceType, options?: SchemaOptions): TConstructor { + return Constructor(parameters, instanceType, options) } /** `[JavaScript]` Creates a Date type */ public Date(options: DateOptions = {}): TDate { return Date(options) } /** `[JavaScript]` Creates a Function type */ - public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { - return FunctionType(parameters, returns, options) + public Function(parameters: [...Parameters], returnType: ReturnType, options?: SchemaOptions): TFunction { + return FunctionType(parameters, returnType, options) } /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ - public InstanceType>(schema: T, options?: SchemaOptions): TInstanceType { + public InstanceType(schema: Type, options?: SchemaOptions): TInstanceType { return InstanceType(schema, options) } /** `[JavaScript]` Creates an Iterator type */ - public Iterator(items: T, options?: SchemaOptions): TIterator { + public Iterator(items: Type, options?: SchemaOptions): TIterator { return Iterator(items, options) } /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options?: SchemaOptions): TParameters { + public Parameters(schema: Type, options?: SchemaOptions): TParameters { return Parameters(schema, options) } /** `[JavaScript]` Creates a Promise type */ - public Promise(item: T, options?: SchemaOptions): TPromise { + public Promise(item: Type, options?: SchemaOptions): TPromise { return Promise(item, options) } /** `[JavaScript]` Creates a RegExp type */ @@ -101,8 +101,8 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return RegExp(unresolved as any, options) } /** `[JavaScript]` Extracts the ReturnType from the given Function type */ - public ReturnType>(schema: T, options?: SchemaOptions): TReturnType { - return ReturnType(schema, options) + public ReturnType(type: Type, options?: SchemaOptions): TReturnType { + return ReturnType(type, options) } /** `[JavaScript]` Creates a Symbol type */ public Symbol(options?: SchemaOptions): TSymbol { diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 7613dc3f4..acc91ce1f 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -39,7 +39,7 @@ import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type import { Integer, type IntegerOptions, type TInteger } from '../integer/index' import { Intersect, type IntersectOptions } from '../intersect/index' import { Capitalize, Uncapitalize, Lowercase, Uppercase, type TCapitalize, type TUncapitalize, type TLowercase, type TUppercase } from '../intrinsic/index' -import { KeyOf, type TKeyOf, type TKeyOfFromMappedResult } from '../keyof/index' +import { KeyOf, type TKeyOf } from '../keyof/index' import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' import { Mapped, type TMappedFunction, type TMapped, type TMappedResult } from '../mapped/index' import { Never, type TNever } from '../never/index' @@ -49,10 +49,10 @@ import { type TMappedKey } from '../mapped/index' import { Module, TModule } from '../module/index' import { Number, type TNumber, type NumberOptions } from '../number/index' import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index' -import { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from '../omit/index' +import { Omit, type TOmit } from '../omit/index' import { Optional, type TOptionalWithFlag, type TOptionalFromMappedResult } from '../optional/index' import { Partial, type TPartial, type TPartialFromMappedResult } from '../partial/index' -import { Pick, type TPick, type TPickFromMappedKey, type TPickFromMappedResult } from '../pick/index' +import { Pick, type TPick } from '../pick/index' import { Readonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from '../readonly/index' import { ReadonlyOptional, type TReadonlyOptional } from '../readonly-optional/index' import { Record, type TRecordOrObject } from '../record/index' @@ -75,32 +75,32 @@ export class JsonTypeBuilder { // Modifiers // ------------------------------------------------------------------------ /** `[Json]` Creates a Readonly and Optional property */ - public ReadonlyOptional(schema: T): TReadonlyOptional { - return ReadonlyOptional(schema) + public ReadonlyOptional(type: Type): TReadonlyOptional { + return ReadonlyOptional(type) } /** `[Json]` Creates a Readonly property */ - public Readonly(schema: T, enable: F): TReadonlyFromMappedResult + public Readonly(type: Type, enable: Flag): TReadonlyFromMappedResult /** `[Json]` Creates a Readonly property */ - public Readonly(schema: T, enable: F): TReadonlyWithFlag + public Readonly(type: Type, enable: Flag): TReadonlyWithFlag /** `[Json]` Creates a Optional property */ - public Readonly(schema: T): TReadonlyFromMappedResult + public Readonly(type: Type): TReadonlyFromMappedResult /** `[Json]` Creates a Readonly property */ - public Readonly(schema: T): TReadonlyWithFlag + public Readonly(type: Type): TReadonlyWithFlag /** `[Json]` Creates a Readonly property */ - public Readonly(schema: TSchema, enable?: boolean): any { - return Readonly(schema, enable ?? true) + public Readonly(type: TSchema, enable?: boolean): any { + return Readonly(type, enable ?? true) } /** `[Json]` Creates a Optional property */ - public Optional(schema: T, enable: F): TOptionalFromMappedResult + public Optional(type: Type, enable: Flag): TOptionalFromMappedResult /** `[Json]` Creates a Optional property */ - public Optional(schema: T, enable: F): TOptionalWithFlag + public Optional(type: Type, enable: Flag): TOptionalWithFlag /** `[Json]` Creates a Optional property */ - public Optional(schema: T): TOptionalFromMappedResult + public Optional(type: Type): TOptionalFromMappedResult /** `[Json]` Creates a Optional property */ - public Optional(schema: T): TOptionalWithFlag + public Optional(type: Type): TOptionalWithFlag /** `[Json]` Creates a Optional property */ - public Optional(schema: TSchema, enable?: boolean): any { - return Optional(schema, enable ?? true) + public Optional(type: TSchema, enable?: boolean): any { + return Optional(type, enable ?? true) } // ------------------------------------------------------------------------ // Types @@ -110,8 +110,8 @@ export class JsonTypeBuilder { return Any(options) } /** `[Json]` Creates an Array type */ - public Array(schema: T, options?: ArrayOptions): TArray { - return Array(schema, options) + public Array(items: Type, options?: ArrayOptions): TArray { + return Array(items, options) } /** `[Json]` Creates a Boolean type */ public Boolean(options?: SchemaOptions): TBoolean { @@ -164,40 +164,36 @@ export class JsonTypeBuilder { return Extract(type, union, options) } /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedResult + public Index(type: Type, key: readonly [...PropertyKeys], options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(T: T, K: K, options?: SchemaOptions): TIndexFromMappedKey + public Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ - public Index>(T: T, K: K, options?: SchemaOptions): TIndex + public Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(T: T, K: readonly [...K], options?: SchemaOptions): TIndex + public Index(type: Type, key: Key, options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: TSchema, unresolved: any, options?: SchemaOptions): any { - return Index(schema, unresolved, options) + public Index(type: TSchema, key: any, options?: SchemaOptions): any { + return Index(type, key, options) } /** `[Json]` Creates an Integer type */ public Integer(options?: IntegerOptions): TInteger { return Integer(options) } /** `[Json]` Creates an Intersect type */ - public Intersect(T: [...T], options?: IntersectOptions): Intersect { - return Intersect(T, options) + public Intersect(types: [...Types], options?: IntersectOptions): Intersect { + return Intersect(types, options) } /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options?: SchemaOptions): TKeyOfFromMappedResult - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options?: SchemaOptions): TKeyOf - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: TSchema, options?: SchemaOptions): any { - return KeyOf(schema, options) + public KeyOf(type: Type, options?: SchemaOptions): TKeyOf { + return KeyOf(type, options) as never } /** `[Json]` Creates a Literal type */ - public Literal(value: T, options?: SchemaOptions): TLiteral { - return Literal(value, options) + public Literal(literalValue: LiteralValue, options?: SchemaOptions): TLiteral { + return Literal(literalValue, options) } /** `[Json]` Intrinsic function to Lowercase LiteralString types */ - public Lowercase(schema: T, options?: SchemaOptions): TLowercase { - return Lowercase(schema, options) + public Lowercase(type: Type, options?: SchemaOptions): TLowercase { + return Lowercase(type, options) } /** `[Json]` Creates a Mapped object type */ public Mapped, F extends TMappedFunction = TMappedFunction, R extends TMapped = TMapped>(key: K, map: F, options?: ObjectOptions): R @@ -216,8 +212,8 @@ export class JsonTypeBuilder { return Never(options) } /** `[Json]` Creates a Not type */ - public Not(schema: T, options?: SchemaOptions): TNot { - return Not(schema, options) + public Not(type: T, options?: SchemaOptions): TNot { + return Not(type, options) } /** `[Json]` Creates a Null type */ public Null(options?: SchemaOptions): TNull { @@ -231,41 +227,33 @@ export class JsonTypeBuilder { public Object(properties: T, options?: ObjectOptions): TObject { return Object(properties, options) } + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Omit(type: Type, key: readonly [...Key], options?: SchemaOptions): TOmit + /** `[Json]` Constructs a type whose keys are picked from the given type */ + public Omit(type: Type, key: Key, options?: SchemaOptions): TOmit /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit>(T: T, K: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(T: T, K: readonly [...K], options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: TSchema, unresolved: any, options?: SchemaOptions): any { - return Omit(schema, unresolved, options) + public Omit(schema: TSchema, selector: any, options?: SchemaOptions): any { + return Omit(schema, selector, options) } /** `[Json]` Constructs a type where all properties are optional */ - public Partial(T: T, options?: SchemaOptions): TPartialFromMappedResult + public Partial(type: MappedResult, options?: SchemaOptions): TPartialFromMappedResult /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: T, options?: SchemaOptions): TPartial + public Partial(type: Type, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: TSchema, options?: SchemaOptions): any { - return Partial(schema, options) + public Partial(type: TSchema, options?: SchemaOptions): any { + return Partial(type, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick>(T: T, K: K, options?: SchemaOptions): TPick + public Pick(type: Type, key: readonly [...Key], options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(T: T, K: readonly [...K], options?: SchemaOptions): TPick + public Pick(type: Type, key: Key, options?: SchemaOptions): TPick /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: TSchema, unresolved: any, options?: SchemaOptions): any { - return Pick(schema, unresolved, options) + public Pick(type: any, key: any, options?: SchemaOptions): any { + return Pick(type, key, options) } /** `[Json]` Creates a Record type */ - public Record(key: K, schema: T, options?: ObjectOptions): TRecordOrObject { - return Record(key, schema, options) + public Record(key: Key, value: Value, options?: ObjectOptions): TRecordOrObject { + return Record(key, value, options) } /** `[Json]` Creates a Recursive type */ public Recursive(callback: (thisType: TThis) => T, options?: SchemaOptions): TRecursive { @@ -276,44 +264,44 @@ export class JsonTypeBuilder { return Ref($ref, options) } /** `[Json]` Constructs a type where all properties are required */ - public Required(T: T, options?: SchemaOptions): TRequiredFromMappedResult + public Required(type: MappedResult, options?: SchemaOptions): TRequiredFromMappedResult /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: T, options?: SchemaOptions): TRequired + public Required(type: Type, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: TSchema, options?: SchemaOptions): any { - return Required(schema, options) + public Required(type: TSchema, options?: SchemaOptions): any { + return Required(type, options) } /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ - public Rest(schema: T): TRest { - return Rest(schema) + public Rest(type: Type): TRest { + return Rest(type) } /** `[Json]` Creates a String type */ public String(options?: StringOptions): TString { return String(options) } /** `[Json]` Creates a TemplateLiteral type from template dsl string */ - public TemplateLiteral(syntax: T, options?: SchemaOptions): TTemplateLiteralSyntax + public TemplateLiteral(syntax: Syntax, options?: SchemaOptions): TTemplateLiteralSyntax /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral + public TemplateLiteral(kinds: [...Kinds], options?: SchemaOptions): TTemplateLiteral /** `[Json]` Creates a TemplateLiteral type */ public TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options?: SchemaOptions) { return TemplateLiteral(unresolved as any, options) } /** `[Json]` Creates a Transform type */ - public Transform(schema: I): TransformDecodeBuilder { - return Transform(schema) + public Transform(type: Type): TransformDecodeBuilder { + return Transform(type) } /** `[Json]` Creates a Tuple type */ - public Tuple(items: [...T], options?: SchemaOptions): TTuple { - return Tuple(items, options) + public Tuple(types: [...Types], options?: SchemaOptions): TTuple { + return Tuple(types, options) } /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ - public Uncapitalize(schema: T, options?: SchemaOptions): TUncapitalize { - return Uncapitalize(schema, options) + public Uncapitalize(type: Type, options?: SchemaOptions): TUncapitalize { + return Uncapitalize(type, options) } /** `[Json]` Creates a Union type */ - public Union(schemas: [...T], options?: SchemaOptions): Union { - return Union(schemas, options) + public Union(types: [...Types], options?: SchemaOptions): Union { + return Union(types, options) } /** `[Json]` Creates an Unknown type */ public Unknown(options?: SchemaOptions): TUnknown { diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts index 9e4004a20..8ba1eb21b 100644 --- a/src/type/union/union-evaluated.ts +++ b/src/type/union/union-evaluated.ts @@ -44,43 +44,43 @@ import { IsOptional } from '../guard/kind' // IsUnionOptional // ------------------------------------------------------------------ // prettier-ignore -type TIsUnionOptional = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? - L extends TOptional +type TIsUnionOptional = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? + Left extends TOptional ? true - : TIsUnionOptional + : TIsUnionOptional : false ) // prettier-ignore -function IsUnionOptional(T: T): TIsUnionOptional { - return T.some(L => IsOptional(L)) as never +function IsUnionOptional(types: Types): TIsUnionOptional { + return types.some(type => IsOptional(type)) as never } // ------------------------------------------------------------------ // RemoveOptionalFromRest // ------------------------------------------------------------------ // prettier-ignore -type TRemoveOptionalFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? L extends TOptional - ? TRemoveOptionalFromRest]> - : TRemoveOptionalFromRest - : Acc +type TRemoveOptionalFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? Left extends TOptional + ? TRemoveOptionalFromRest]> + : TRemoveOptionalFromRest + : Result ) // prettier-ignore -function RemoveOptionalFromRest(T: T): TRemoveOptionalFromRest { - return T.map(L => IsOptional(L) ? RemoveOptionalFromType(L) : L) as never +function RemoveOptionalFromRest(types: Types): TRemoveOptionalFromRest { + return types.map(left => IsOptional(left) ? RemoveOptionalFromType(left) : left) as never } // ------------------------------------------------------------------ // RemoveOptionalFromType // ------------------------------------------------------------------ // prettier-ignore -type TRemoveOptionalFromType = ( - T extends TReadonly ? TReadonly> : - T extends TOptional ? TRemoveOptionalFromType : - T +type TRemoveOptionalFromType = ( + Type extends TReadonly ? TReadonly> : + Type extends TOptional ? TRemoveOptionalFromType : + Type ) // prettier-ignore -function RemoveOptionalFromType(T: T): TRemoveOptionalFromType { +function RemoveOptionalFromType(T: Type): TRemoveOptionalFromType { return ( Discard(T, [OptionalKind]) ) as never @@ -89,34 +89,38 @@ function RemoveOptionalFromType(T: T): TRemoveOptionalFromTyp // ResolveUnion // ------------------------------------------------------------------ // prettier-ignore -type TResolveUnion> = ( - TIsUnionOptional extends true - ? TOptional> - : TUnion +type TResolveUnion, + IsOptional extends boolean = TIsUnionOptional +> = ( + IsOptional extends true + ? TOptional> + : TUnion ) // prettier-ignore -function ResolveUnion(T: T, options?: SchemaOptions): TResolveUnion { +function ResolveUnion(types: Types, options?: SchemaOptions): TResolveUnion { + const isOptional = IsUnionOptional(types) return ( - IsUnionOptional(T) - ? Optional(UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options)) - : UnionCreate(RemoveOptionalFromRest(T) as TSchema[], options) + isOptional + ? Optional(UnionCreate(RemoveOptionalFromRest(types) as TSchema[], options)) + : UnionCreate(RemoveOptionalFromRest(types) as TSchema[], options) ) as never } // ------------------------------------------------------------------ // Union // ------------------------------------------------------------------ // prettier-ignore -export type TUnionEvaluated = ( - T extends [] ? TNever : - T extends [TSchema] ? T[0] : - TResolveUnion +export type TUnionEvaluated = ( + Types extends [TSchema] ? Types[0] : + Types extends [] ? TNever : + TResolveUnion ) /** `[Json]` Creates an evaluated Union type */ -export function UnionEvaluated>(T: [...T], options?: SchemaOptions): R { +export function UnionEvaluated>(T: [...Types], options?: SchemaOptions): Result { // prettier-ignore return ( - T.length === 0 ? Never(options) : T.length === 1 ? CreateType(T[0], options) : + T.length === 0 ? Never(options) : ResolveUnion(T, options) ) as never } diff --git a/src/type/union/union.ts b/src/type/union/union.ts index a057141c8..bacffbd19 100644 --- a/src/type/union/union.ts +++ b/src/type/union/union.ts @@ -39,11 +39,11 @@ export type Union = ( TUnion ) /** `[Json]` Creates a Union type */ -export function Union(T: [...T], options?: SchemaOptions): Union { +export function Union(types: [...Types], options?: SchemaOptions): Union { // prettier-ignore return ( - T.length === 0 ? Never(options) : - T.length === 1 ? CreateType(T[0], options) : - UnionCreate(T, options) - ) as Union + types.length === 0 ? Never(options) : + types.length === 1 ? CreateType(types[0], options) : + UnionCreate(types, options) + ) as Union } diff --git a/src/value/check/check.ts b/src/value/check/check.ts index b1af0dd26..c5244e22a 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -75,9 +75,9 @@ import type { TVoid } from '../../type/void/index' // ------------------------------------------------------------------ import { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsSchema } from '../../type/guard/type' +import { IsSchema } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 2498058b1..013b0abd1 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -51,9 +51,9 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 1f033dc79..3ac8c54c8 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -51,9 +51,9 @@ import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type' +import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/kind' // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 7481d86a1..5a0d785c3 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -46,9 +46,9 @@ import type { TTuple } from '../../type/tuple/index' import type { TUnion } from '../../type/union/index' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsTransform, IsSchema } from '../../type/guard/type' +import { IsTransform, IsSchema } from '../../type/guard/kind' // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts index ee430ad6d..5feb06523 100644 --- a/test/runtime/type/guard/kind/import.ts +++ b/test/runtime/type/guard/kind/import.ts @@ -2,14 +2,154 @@ import { KindGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' -describe('guard/type/TImport', () => { +describe('guard/kind/TImport', () => { it('Should guard for TImport', () => { - const M = Type.Module({ + const Module = Type.Module({ A: Type.String(), }) - const I = M.Import('A') - const N = I.$defs[I.$ref] - Assert.IsTrue(KindGuard.IsImport(I)) + const A = Module.Import('A') + const N = A.$defs[A.$ref] + Assert.IsTrue(KindGuard.IsImport(A)) Assert.IsTrue(KindGuard.IsString(N)) }) + // ---------------------------------------------------------------- + // Computed: Awaited + // ---------------------------------------------------------------- + it('Should compute for Awaited', () => { + const Module = Type.Module({ + T: Type.Promise(Type.String()), + R: Type.Awaited(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsString(T.$defs['R'])) + }) + // ---------------------------------------------------------------- + // Computed: Index (Note: Pending Reimplementation of Index) + // ---------------------------------------------------------------- + it('Should compute for Index 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + I: Type.Literal('x'), + R: Type.Index(Type.Ref('T'), Type.Ref('I')) as never, // fail + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'])) + }) + // ---------------------------------------------------------------- + // Computed: Omit + // ---------------------------------------------------------------- + it('Should compute for Omit 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + R: Type.Omit(Type.Ref('T'), Type.Literal('x')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.y)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.x === undefined) + }) + it('Should compute for Omit 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.Literal('x'), + R: Type.Omit(Type.Ref('T'), Type.Ref('K')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.y)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.x === undefined) + }) + // ---------------------------------------------------------------- + // Computed: Partial + // ---------------------------------------------------------------- + it('Should compute for Partial', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number() }), + R: Type.Partial(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsTrue(KindGuard.IsOptional(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: Pick + // ---------------------------------------------------------------- + it('Should compute for Pick 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + R: Type.Pick(Type.Ref('T'), Type.Literal('x')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.y === undefined) + }) + it('Should compute for Pick 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.Literal('x'), + R: Type.Pick(Type.Ref('T'), Type.Ref('K')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.y === undefined) + }) + // ---------------------------------------------------------------- + // Computed: Record + // ---------------------------------------------------------------- + it('Should compute for Record 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + R: Type.Record(Type.String(), Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsRecord(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].patternProperties['^(.*)$'].properties.x)) + Assert.IsTrue(KindGuard.IsString(T.$defs['R'].patternProperties['^(.*)$'].properties.y)) + }) + it('Should compute for Record 2', () => { + const Module = Type.Module({ + T: Type.Number(), + K: Type.Union([Type.Literal('x'), Type.Literal('y')]), + R: Type.Record(Type.Ref('K'), Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: Required + // ---------------------------------------------------------------- + it('Should compute for Required', () => { + const Module = Type.Module({ + T: Type.Partial(Type.Object({ x: Type.Number() })), + R: Type.Required(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsFalse(KindGuard.IsOptional(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: KeyOf + // ---------------------------------------------------------------- + it('Should compute for KeyOf', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + R: Type.KeyOf(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsUnion(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsLiteral(T.$defs['R'].anyOf[0])) + Assert.IsTrue(KindGuard.IsLiteral(T.$defs['R'].anyOf[1])) + Assert.IsTrue(T.$defs['R'].anyOf[0].const === 'x') + Assert.IsTrue(T.$defs['R'].anyOf[1].const === 'y') + }) }) diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts index fdce650c6..f38124320 100644 --- a/test/runtime/type/guard/type/import.ts +++ b/test/runtime/type/guard/type/import.ts @@ -4,12 +4,152 @@ import { Assert } from '../../../assert/index' describe('guard/type/TImport', () => { it('Should guard for TImport', () => { - const M = Type.Module({ + const Module = Type.Module({ A: Type.String(), }) - const I = M.Import('A') - const N = I.$defs[I.$ref] - Assert.IsTrue(TypeGuard.IsImport(I)) + const A = Module.Import('A') + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsImport(A)) Assert.IsTrue(TypeGuard.IsString(N)) }) + // ---------------------------------------------------------------- + // Computed: Awaited + // ---------------------------------------------------------------- + it('Should compute for Awaited', () => { + const Module = Type.Module({ + T: Type.Promise(Type.String()), + R: Type.Awaited(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsString(T.$defs['R'])) + }) + // ---------------------------------------------------------------- + // Computed: Index (Note: Pending Reimplementation of Index) + // ---------------------------------------------------------------- + it('Should compute for Index 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + I: Type.Literal('x'), + R: Type.Index(Type.Ref('T'), Type.Ref('I')) as never, // fail + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'])) + }) + // ---------------------------------------------------------------- + // Computed: Omit + // ---------------------------------------------------------------- + it('Should compute for Omit 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + R: Type.Omit(Type.Ref('T'), Type.Literal('x')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.y)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.x === undefined) + }) + it('Should compute for Omit 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.Literal('x'), + R: Type.Omit(Type.Ref('T'), Type.Ref('K')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.y)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.x === undefined) + }) + // ---------------------------------------------------------------- + // Computed: Partial + // ---------------------------------------------------------------- + it('Should compute for Partial', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number() }), + R: Type.Partial(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: Pick + // ---------------------------------------------------------------- + it('Should compute for Pick 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + R: Type.Pick(Type.Ref('T'), Type.Literal('x')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.y === undefined) + }) + it('Should compute for Pick 2', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.Literal('x'), + R: Type.Pick(Type.Ref('T'), Type.Ref('K')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + // @ts-ignore + Assert.IsTrue(T.$defs['R'].properties.y === undefined) + }) + // ---------------------------------------------------------------- + // Computed: Record + // ---------------------------------------------------------------- + it('Should compute for Record 1', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + R: Type.Record(Type.String(), Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsRecord(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].patternProperties['^(.*)$'].properties.x)) + Assert.IsTrue(TypeGuard.IsString(T.$defs['R'].patternProperties['^(.*)$'].properties.y)) + }) + it('Should compute for Record 2', () => { + const Module = Type.Module({ + T: Type.Number(), + K: Type.Union([Type.Literal('x'), Type.Literal('y')]), + R: Type.Record(Type.Ref('K'), Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: Required + // ---------------------------------------------------------------- + it('Should compute for Required', () => { + const Module = Type.Module({ + T: Type.Partial(Type.Object({ x: Type.Number() })), + R: Type.Required(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + Assert.IsFalse(TypeGuard.IsOptional(T.$defs['R'].properties.x)) + }) + // ---------------------------------------------------------------- + // Computed: KeyOf + // ---------------------------------------------------------------- + it('Should compute for KeyOf', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number(), y: Type.String() }), + R: Type.KeyOf(Type.Ref('T')), + }) + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsUnion(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsLiteral(T.$defs['R'].anyOf[0])) + Assert.IsTrue(TypeGuard.IsLiteral(T.$defs['R'].anyOf[1])) + Assert.IsTrue(T.$defs['R'].anyOf[0].const === 'x') + Assert.IsTrue(T.$defs['R'].anyOf[1].const === 'y') + }) }) diff --git a/test/static/pick.ts b/test/static/pick.ts index de29ba4bb..895cdefac 100644 --- a/test/static/pick.ts +++ b/test/static/pick.ts @@ -27,7 +27,7 @@ import { Type, Static } from '@sinclair/typebox' const keys = ['A', 'B'] as const - const T = Type.Pick(A, keys) + const T = Type.Pick(A, ['A', 'B']) type T = Static From b9cc4cdc5e54648c3400314462ea0585a509dfd3 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Sun, 17 Nov 2024 09:34:07 +0900 Subject: [PATCH 306/369] Revision 0.34.2 (#1082) * Fix Import Path * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/type/pick/pick.ts | 2 +- src/value/clean/clean.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index e9f754629..8ac95c625 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,6 +1,8 @@ ### 0.34.0 - [Revision 0.34.1](https://github.com/sinclairzx81/typebox/pull/1080) - Implement Computed Type Deref in Modules +- [Revision 0.34.2](https://github.com/sinclairzx81/typebox/pull/1082) + - Resolve import pathing issue introduced on 0.34.1 ## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0) diff --git a/package-lock.json b/package-lock.json index 3dab50690..90dad4af4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.1", + "version": "0.34.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.1", + "version": "0.34.2", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 5c9bf671d..f3ec946e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.1", + "version": "0.34.2", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index f9b988f46..fc690845f 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -45,7 +45,7 @@ import { TransformKind } from '../symbols/symbols' // Guards // ------------------------------------------------------------------ import { IsMappedKey, IsMappedResult, IsIntersect, IsUnion, IsObject, IsSchema, IsLiteralValue, IsRef } from '../guard/kind' -import { IsArray as IsArrayValue } from 'src/value/guard' +import { IsArray as IsArrayValue } from '../guard/value' // ------------------------------------------------------------------ // Infrastructure diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 341cf025e..41e7f4edb 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -34,7 +34,7 @@ import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index' -import type { TImport } from 'src/type/module/index' +import type { TImport } from '../../type/module/index' import type { TIntersect } from '../../type/intersect/index' import type { TObject } from '../../type/object/index' import type { TRecord } from '../../type/record/index' From 199274271bf626965b681898041ada6d1d7fc498 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Mon, 18 Nov 2024 01:59:25 +0900 Subject: [PATCH 307/369] Revision 0.34.3 (#1083) * Retain Array on Value Default * Version * ChangeLog --- changelog/0.34.0.md | 7 +++++-- package-lock.json | 4 ++-- package.json | 2 +- src/value/default/default.ts | 8 ++++++++ test/runtime/value/default/array.ts | 26 ++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 8ac95c625..147528f62 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,8 +1,11 @@ ### 0.34.0 -- [Revision 0.34.1](https://github.com/sinclairzx81/typebox/pull/1080) - - Implement Computed Type Deref in Modules + +- [Revision 0.34.3](https://github.com/sinclairzx81/typebox/pull/1083) + - Retain Array Elements on Value Default - [Revision 0.34.2](https://github.com/sinclairzx81/typebox/pull/1082) - Resolve import pathing issue introduced on 0.34.1 +- [Revision 0.34.1](https://github.com/sinclairzx81/typebox/pull/1080) + - Implement Computed Type Deref in Modules ## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0) diff --git a/package-lock.json b/package-lock.json index 90dad4af4..0f1d07436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.2", + "version": "0.34.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.2", + "version": "0.34.3", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f3ec946e4..b9148e246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.2", + "version": "0.34.3", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/default/default.ts b/src/value/default/default.ts index 30ddbc95b..e77cd0188 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -68,6 +68,14 @@ function HasDefaultProperty(schema: unknown): schema is TSchema { // Types // ------------------------------------------------------------------ function FromArray(schema: TArray, references: TSchema[], value: unknown): any { + // if the value is an array, we attempt to initialize it's elements + if (IsArray(value)) { + for (let i = 0; i < value.length; i++) { + value[i] = Visit(schema.items, references, value[i]) + } + return value + } + // ... otherwise use default initialization const defaulted = ValueOrDefault(schema, value) if (!IsArray(defaulted)) return defaulted for (let i = 0; i < defaulted.length; i++) { diff --git a/test/runtime/value/default/array.ts b/test/runtime/value/default/array.ts index be385e603..602f6562c 100644 --- a/test/runtime/value/default/array.ts +++ b/test/runtime/value/default/array.ts @@ -21,4 +21,30 @@ describe('value/default/Array', () => { const R = Value.Default(T, [1, undefined, 3]) Assert.IsEqual(R, [1, 2, 3]) }) + // ---------------------------------------------------------------- + // Elements + // ---------------------------------------------------------------- + it('Should should retain array and only initialize undefined elements', () => { + const T = Type.Array(Type.Literal('hello', { default: 'hello' })) + const R = Value.Default(T, [1, undefined, 3]) + Assert.IsEqual(R, [1, 'hello', 3]) + }) + // ---------------------------------------------------------------- + // https://github.com/ubiquity-os-marketplace/command-start-stop/pull/86 + // ---------------------------------------------------------------- + it('Should retain arrays 1', () => { + const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C'), Type.Literal('D')], { default: 'A' }) + const T = Type.Array(U, { default: ['A', 'B', 'C', 'D'], uniqueItems: true }) + Assert.IsEqual(Value.Default(T, undefined), ['A', 'B', 'C', 'D']) + Assert.IsEqual(Value.Default(T, []), []) + Assert.IsEqual(Value.Default(T, ['A']), ['A']) + // initialize undefined element + Assert.IsEqual(Value.Default(T, [undefined, 'B', 'C', 'D']), ['A', 'B', 'C', 'D']) + }) + it('Should retain arrays 2', () => { + const U = Type.Union([Type.Literal('A'), Type.Literal('B'), Type.Literal('C'), Type.Literal('D')], { default: 'A' }) + // undefined first element initialized by union default + const T = Type.Array(U, { default: [undefined, 'B', 'C', 'D'], uniqueItems: true }) + Assert.IsEqual(Value.Default(T, undefined), ['A', 'B', 'C', 'D']) + }) }) From ab4f0db84fe6f49c91e8ee348aa0c8f83398efef Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 18 Nov 2024 14:02:38 +0900 Subject: [PATCH 308/369] Documentation --- readme.md | 70 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/readme.md b/readme.md index 0d3ec1cb1..8b1eeb35a 100644 --- a/readme.md +++ b/readme.md @@ -774,39 +774,47 @@ type T = Static // type T = string | null ### Module Types -TypeBox Modules are containers for related types. They function as namespaces and enable internal types to reference each other via string references. Modules support both singular and mutually recursive types. They provide a mechanism to create circular types irrespective of the order in which types are defined. +TypeBox Modules are containers for related types. They provide a referential namespace, enabling types to reference one another via string identifiers. Modules support both singular and mutually recursive referencing within the context of a module, as well as referential computed types (such as Partial + Ref). All Module types must be imported before use. TypeBox represents an imported type with the `$defs` Json Schema keyword. -```typescript +#### Usage + +The following creates a Module with User and PartialUser types. Note that the PartialUser type is specified as a Partial + Ref to the User type. It is not possible to perform a Partial operation directly on a reference, so TypeBox will return a TComputed type that defers the Partial operation until all types are resolved. The TComputed type is evaluated when calling Import on the Module. -// The following creates a Module of circular recursive Types. +```typescript +// Module with PartialUser and User types const Module = Type.Module({ - A: Type.Object({ - b: Type.Ref('B') // Ref B: - }), - B: Type.Object({ - c: Type.Ref('C') // Ref C: - }), - C: Type.Object({ - a: Type.Ref('A') // Ref A: - }), + + PartialUser: Type.Partial(Type.Ref('User')), // TComputed<'Partial', [TRef<'User'>]> + + User: Type.Object({ // TObject<{ + id: Type.String(), // user: TString, + name: Type.String(), // name: TString, + email: Type.String() // email: TString + }), // }> + }) -// Module Types must be imported before use. +// Types must be imported before use. -const A = Module.Import('A') // const A: TImport<{...}, 'A'> +const User = Module.Import('User') // const User: TImport<{...}, 'User'> -type A = Static // type A = { - // b: { - // c: { - // a: { - // b: ... - // } - // } - // } +type User = Static // type User = { + // id: string, + // name: string, + // email: string + // } + +const PartialUser = Module.Import('PartialUser') // const PartialUser: TImport<{...}, 'PartialUser'> + +type PartialUser = Static // type PartialUser = { + // id?: string, + // name?: string, + // email?: string // } ``` + ### Template Literal Types @@ -1025,7 +1033,7 @@ if(TypeGuard.IsString(T)) { ## Syntax Types -TypeBox provides support for Syntax Types, enabling it to parse TypeScript syntax directly into TypeBox types. Syntax Types serve as a DSL frontend for TypeBox's type builder and are useful for converting existing TypeScript type definitions into Json Schema schematics. +TypeBox provides support for parsing TypeScript syntax directly into TypeBox Json Schema schematics. Syntax Types offer a string based DSL frontend to TypeBox's Type Builder system and can be useful for converting existing TypeScript type definitions into Json Schema schematics without reimplementation via the Type Builder. Syntax Types are provided via optional import. @@ -1106,14 +1114,14 @@ type PartialUser = Static // type PartialUser = { ### Context -The Parse function accepts an initial Context argument, allowing external types to be passed into the parser. +The Parse function takes an optional leading Context object that contains external types. This Context allows the syntax to reference these external types using the property identifiers provided by the Context. The following passes the external type `T` to Parse. ```typescript -const T = Type.Object({ // could be written as: Parse(`{ - x: Type.Number(), // x: number, - y: Type.Number(), // y: number, - z: Type.Number() // z: number -}) // }`) +const T = Type.Object({ // const T: TObject<{ + x: Type.Number(), // x: TNumber, + y: Type.Number(), // y: TNumber, + z: Type.Number() // z: TNumber +}) // }> const A = Parse({ T }, 'Partial') // const A: TObject<{ // x: TOptional, @@ -1140,7 +1148,7 @@ const C = Parse({ T }, 'T & { w: number }') // const C: TIntersect<[TObj ### Static -Syntax Types provide two Static types for inferring TypeScript syntax from strings. +Syntax Types provide two Static types for inferring TypeScript types and TypeBox schematics from strings. ```typescript import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' @@ -1190,7 +1198,7 @@ const B = Parse(`{ }`) ``` -In cases where Syntax Types busts through TypeScript instantiation limits, TypeBox offers a fallback ParseOnly function which will Parse the types at runtime, but not infer the type. This function can also be used for parsing non-constant strings. +In cases where Syntax Types exceed TypeScript's instantiation limits, TypeBox offers a fallback ParseOnly function, which will only parse but not infer. This function can also be used to parse types where the syntax is statically unknown to TypeScript (for example, when loading types from disk). ```typescript import { ParseOnly } from '@sinclair/typebox/syntax' From cc5f2a3cbef835ff528a9cf5361000fdc2e1f6c5 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Tue, 19 Nov 2024 14:03:48 +0900 Subject: [PATCH 309/369] Revision 0.34.4 (#1085) * Module Dereference Path for TEnum * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/type/module/compute.ts | 32 ++++++++++++++++-------------- src/type/module/infer.ts | 2 ++ test/static/import.ts | 40 ++++++++++++++++++++++++++++++++++++++ test/static/index.ts | 1 + 7 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 test/static/import.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 147528f62..ce9473447 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,5 +1,7 @@ ### 0.34.0 +- [Revision 0.34.4](https://github.com/sinclairzx81/typebox/pull/1085) + - Inference Path for Enum within Modules - [Revision 0.34.3](https://github.com/sinclairzx81/typebox/pull/1083) - Retain Array Elements on Value Default - [Revision 0.34.2](https://github.com/sinclairzx81/typebox/pull/1082) diff --git a/package-lock.json b/package-lock.json index 0f1d07436..c6ab24477 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.3", + "version": "0.34.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.3", + "version": "0.34.4", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index b9148e246..98ac92bda 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.3", + "version": "0.34.4", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 47bf60851..1e9bca28d 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -35,6 +35,7 @@ import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' import { TComputed } from '../computed/index' import { Constructor, type TConstructor } from '../constructor/index' import { Index, type TIndex } from '../indexed/index' +import { TEnum, TEnumRecord } from '../enum/index' import { Function, type TFunction } from '../function/index' import { Intersect, type TIntersect, type TIntersectEvaluated } from '../intersect/index' import { Iterator, type TIterator } from '../iterator/index' @@ -339,34 +340,35 @@ function FromRest // ------------------------------------------------------------------ // prettier-ignore export type TFromType = ( - Type extends TComputed ? TFromComputed : - Type extends TObject ? TFromObject : - Type extends TConstructor ? TFromConstructor : - Type extends TFunction ? TFromFunction : - Type extends TTuple ? TFromTuple : - Type extends TIntersect ? TFromIntersect : - Type extends TUnion ? TFromUnion : - Type extends TArray ? TFromArray : - Type extends TAsyncIterator ? TFromAsyncIterator : - Type extends TIterator ? TFromIterator : + Type extends TArray ? TFromArray : + Type extends TAsyncIterator ? TFromAsyncIterator : + Type extends TComputed ? TFromComputed : + Type extends TConstructor ? TFromConstructor : + Type extends TFunction ? TFromFunction : + Type extends TIntersect ? TFromIntersect : + Type extends TIterator ? TFromIterator : + Type extends TObject ? TFromObject : + Type extends TTuple ? TFromTuple : + Type extends TEnum ? Type : // intercept enum before union + Type extends TUnion ? TFromUnion : Type ) // prettier-ignore export function FromType(moduleProperties: ModuleProperties, type: Type): TFromType { return ( + KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : + KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : // Note: The 'as never' is required due to excessive resolution of TIndex. In fact TIndex, TPick, TOmit and // all need re-implementation to remove the PropertyKey[] selector. Reimplementation of these types should // be a priority as there is a potential for the current inference to break on TS compiler changes. KindGuard.IsComputed(type) ? CreateType(FromComputed(moduleProperties, type.target, type.parameters) as never) : - KindGuard.IsObject(type) ? CreateType(FromObject(moduleProperties, type.properties), type) : KindGuard.IsConstructor(type) ? CreateType(FromConstructor(moduleProperties, type.parameters, type.returns), type) : KindGuard.IsFunction(type) ? CreateType(FromFunction(moduleProperties, type.parameters, type.returns), type) : - KindGuard.IsTuple(type)? CreateType(FromTuple(moduleProperties, type.items || []), type) : KindGuard.IsIntersect(type) ? CreateType(FromIntersect(moduleProperties, type.allOf), type) : - KindGuard.IsUnion(type) ? CreateType(FromUnion(moduleProperties, type.anyOf), type) : - KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : - KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : KindGuard.IsIterator(type) ? CreateType(FromIterator(moduleProperties, type.items), type) : + KindGuard.IsObject(type) ? CreateType(FromObject(moduleProperties, type.properties), type) : + KindGuard.IsTuple(type)? CreateType(FromTuple(moduleProperties, type.items || []), type) : + KindGuard.IsUnion(type) ? CreateType(FromUnion(moduleProperties, type.anyOf), type) : type ) as never } diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts index 082b7f733..c71689f6a 100644 --- a/src/type/module/infer.ts +++ b/src/type/module/infer.ts @@ -31,6 +31,7 @@ import { TSchema } from '../schema/index' import { TArray } from '../array/index' import { TAsyncIterator } from '../async-iterator/index' import { TConstructor } from '../constructor/index' +import { TEnum, TEnumRecord } from '../enum/index' import { TFunction } from '../function/index' import { TIntersect } from '../intersect/index' import { TIterator } from '../iterator/index' @@ -147,6 +148,7 @@ type TInfer = ( Type extends TObject ? TInferObject : Type extends TRef ? TInferRef : Type extends TTuple ? TInferTuple : + Type extends TEnum ? Static : // intercept enum before union Type extends TUnion ? TInferUnion : Static ) diff --git a/test/static/import.ts b/test/static/import.ts new file mode 100644 index 000000000..bab132f11 --- /dev/null +++ b/test/static/import.ts @@ -0,0 +1,40 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Enum 1 +// ------------------------------------------------------------------ +{ + enum Enum { + A, + B, + } + + const T = Type.Module({ + T: Type.Object({ + value: Type.Enum(Enum), + }), + }).Import('T') + + Expect(T).ToStatic<{ + value: Enum + }>() +} + +// ------------------------------------------------------------------ +// Enum 2 +// ------------------------------------------------------------------ +{ + const T = Type.Module({ + T: Type.Object({ + value: Type.Enum({ + x: 1, + y: 2, + }), + }), + }).Import('T') + + Expect(T).ToStatic<{ + value: 1 | 2 + }>() +} diff --git a/test/static/index.ts b/test/static/index.ts index 9012f8682..c6af9e754 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -15,6 +15,7 @@ import './enum' import './extract' import './exclude' import './function' +import './import' import './indexed' import './instance-type' import './intersect' From 3bd7217f8645f479c51a8f03b81c9c3b65e24331 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Wed, 20 Nov 2024 15:27:46 +0900 Subject: [PATCH 310/369] Revision 0.34.5 (#1088) * Record Types are not TCompute for TRef Value Type * Apply Options to Import Target * ChangeLog * Version --- changelog/0.34.0.md | 3 +- package-lock.json | 4 +- package.json | 2 +- src/type/module/infer.ts | 20 ++- src/type/module/module.ts | 3 +- src/type/record/record.ts | 156 +++++++++++------------ test/runtime/type/guard/kind/computed.ts | 28 ++++ test/runtime/type/guard/kind/import.ts | 42 +++++- test/runtime/type/guard/kind/index.ts | 1 + test/runtime/type/guard/type/computed.ts | 28 ++++ test/runtime/type/guard/type/import.ts | 44 ++++++- test/runtime/type/guard/type/index.ts | 1 + test/static/import.ts | 65 +++++++++- 13 files changed, 304 insertions(+), 93 deletions(-) create mode 100644 test/runtime/type/guard/kind/computed.ts create mode 100644 test/runtime/type/guard/type/computed.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index ce9473447..a43929440 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,5 +1,6 @@ ### 0.34.0 - +- [Revision 0.34.5](https://github.com/sinclairzx81/typebox/pull/1088) + - Record Types no longer TCompute for TRef Value Type (Modules) - [Revision 0.34.4](https://github.com/sinclairzx81/typebox/pull/1085) - Inference Path for Enum within Modules - [Revision 0.34.3](https://github.com/sinclairzx81/typebox/pull/1083) diff --git a/package-lock.json b/package-lock.json index c6ab24477..61ab83569 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.4", + "version": "0.34.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.4", + "version": "0.34.5", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 98ac92bda..f061e7736 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.4", + "version": "0.34.5", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts index c71689f6a..65ca8fd13 100644 --- a/src/type/module/infer.ts +++ b/src/type/module/infer.ts @@ -37,6 +37,7 @@ import { TIntersect } from '../intersect/index' import { TIterator } from '../iterator/index' import { TObject, TProperties } from '../object/index' import { TOptional } from '../optional/index' +import { TRecord } from '../record/index' import { TReadonly } from '../readonly/index' import { TRef } from '../ref/index' import { TTuple } from '../tuple/index' @@ -83,16 +84,16 @@ type TInferIterator // ------------------------------------------------------------------ // prettier-ignore type TInferIntersect = ( - Types extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TInferIntersect> + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TInferIntersect> : Result ) // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ -type ReadonlyOptionalPropertyKeys = { [K in keyof Properties]: Properties[K] extends TReadonly ? (Properties[K] extends TOptional ? K : never) : never }[keyof Properties] -type ReadonlyPropertyKeys = { [K in keyof Source]: Source[K] extends TReadonly ? (Source[K] extends TOptional ? never : K) : never }[keyof Source] -type OptionalPropertyKeys = { [K in keyof Source]: Source[K] extends TOptional ? (Source[K] extends TReadonly ? never : K) : never }[keyof Source] +type ReadonlyOptionalPropertyKeys = { [Key in keyof Properties]: Properties[Key] extends TReadonly ? (Properties[Key] extends TOptional ? Key : never) : never }[keyof Properties] +type ReadonlyPropertyKeys = { [Key in keyof Source]: Source[Key] extends TReadonly ? (Source[Key] extends TOptional ? never : Key) : never }[keyof Source] +type OptionalPropertyKeys = { [Key in keyof Source]: Source[Key] extends TOptional ? (Source[Key] extends TReadonly ? never : Key) : never }[keyof Source] type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> // prettier-ignore type InferPropertiesWithModifiers> = Evaluate<( @@ -119,6 +120,14 @@ type TInferTuple extends infer Key extends PropertyKey ? Key : never, + InferedType extends unknown = TInfer, +> = Record +// ------------------------------------------------------------------ // Ref // ------------------------------------------------------------------ // prettier-ignore @@ -146,6 +155,7 @@ type TInfer = ( Type extends TIntersect ? TInferIntersect : Type extends TIterator ? TInferIterator : Type extends TObject ? TInferObject : + Type extends TRecord ? TInferRecord : Type extends TRef ? TInferRef : Type extends TTuple ? TInferTuple : Type extends TEnum ? Static : // intercept enum before union diff --git a/src/type/module/module.ts b/src/type/module/module.ts index a4c2544e9..276332c3f 100644 --- a/src/type/module/module.ts +++ b/src/type/module/module.ts @@ -68,7 +68,8 @@ export class TModule(key: Key, options?: SchemaOptions): TImport { - return CreateType({ [Kind]: 'Import', $defs: this.$defs, $ref: key }, options) as never + const $defs = { ...this.$defs, [key]: CreateType(this.$defs[key], options) } + return CreateType({ [Kind]: 'Import', $defs, $ref: key }) as never } // prettier-ignore private WithIdentifiers($defs: ComputedModuleProperties): ComputedModuleProperties { diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 34a87f74d..782269644 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -27,26 +27,27 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' +import { Kind, Hint } from '../symbols/index' import type { TSchema } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' import { type TAny } from '../any/index' -import { Computed, type TComputed } from '../computed/index' -import { Object, type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions } from '../object/index' +import { type TComputed, Computed } from '../computed/index' +import { type TEnumRecord, type TEnum } from '../enum/index' +import { type TInteger } from '../integer/index' import { type TLiteral, type TLiteralValue } from '../literal/index' -import { Never, type TNever } from '../never/index' -import { Union, type TUnion } from '../union/index' +import { type TNever, Never } from '../never/index' +import { type TNumber } from '../number/index' +import { type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions, Object } from '../object/index' +import { type TRef, Ref } from '../ref/index' import { type TRegExp } from '../regexp/index' import { type TString } from '../string/index' -import { type TInteger } from '../integer/index' -import { type TNumber } from '../number/index' -import { type TEnum } from '../enum/index' -import { type TRef } from '../ref/index' +import { type TUnion, Union } from '../union/index' import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLiteral } from '../template-literal/index' import { PatternStringExact, PatternNumberExact, PatternNeverExact } from '../patterns/index' import { IndexPropertyKeys } from '../indexed/index' -import { Kind, Hint } from '../symbols/index' + // ------------------------------------------------------------------ // ValueGuard // ------------------------------------------------------------------ @@ -61,36 +62,32 @@ import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsT // ------------------------------------------------------------------ // prettier-ignore function RecordCreateFromPattern(pattern: string, T: TSchema, options: ObjectOptions): TRecord { - return CreateType({ - [Kind]: 'Record', - type: 'object', - patternProperties: { [pattern]: T } - }, options) as never + return CreateType({ [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: T } }, options) as never } // ------------------------------------------------------------------ // RecordCreateFromKeys // ------------------------------------------------------------------ // prettier-ignore function RecordCreateFromKeys(K: string[], T: TSchema, options: ObjectOptions): TObject { - const Acc = {} as TProperties - for(const K2 of K) Acc[K2] = T - return Object(Acc, { ...options, [Hint]: 'Record' }) + const result = {} as TProperties + for(const K2 of K) result[K2] = T + return Object(result, { ...options, [Hint]: 'Record' }) } // ------------------------------------------------------------------ // FromTemplateLiteralKey (Fast Inference) // ------------------------------------------------------------------ // prettier-ignore -type TFromTemplateLiteralKeyInfinite = Ensure> +type TFromTemplateLiteralKeyInfinite = Ensure> // prettier-ignore -type TFromTemplateLiteralKeyFinite> = ( - Ensure>> +type TFromTemplateLiteralKeyFinite> = ( + Ensure>> ) // prettier-ignore -type TFromTemplateLiteralKey = TIsTemplateLiteralFinite extends false - ? TFromTemplateLiteralKeyInfinite - : TFromTemplateLiteralKeyFinite +type TFromTemplateLiteralKey = TIsTemplateLiteralFinite extends false + ? TFromTemplateLiteralKeyInfinite + : TFromTemplateLiteralKeyFinite // prettier-ignore -function FromTemplateLiteralKey(K: K, T: T, options: ObjectOptions): TFromTemplateLiteralKey { +function FromTemplateLiteralKey(K: Key, T: Type, options: ObjectOptions): TFromTemplateLiteralKey { return ( IsTemplateLiteralFinite(K) ? RecordCreateFromKeys(IndexPropertyKeys(K), T, options) @@ -101,140 +98,142 @@ function FromTemplateLiteralKey(K // FromEnumKey (Special Case) // ------------------------------------------------------------------ // prettier-ignore -type TFromEnumKey, T extends TSchema> = Ensure> +type TFromEnumKey, Type extends TSchema> = Ensure> // ------------------------------------------------------------------ // FromUnionKey // ------------------------------------------------------------------ // prettier-ignore -type TFromUnionKeyLiteralString, T extends TSchema> = { [_ in K['const']]: T } +type TFromUnionKeyLiteralString, Type extends TSchema> = { [_ in Key['const']]: Type } // prettier-ignore -type TFromUnionKeyLiteralNumber, T extends TSchema> = { [_ in K['const']]: T } +type TFromUnionKeyLiteralNumber, Type extends TSchema> = { [_ in Key['const']]: Type } // prettier-ignore -type TFromUnionKeyRest = - K extends [infer L extends TSchema, ...infer R extends TSchema[]] ? ( - L extends TUnion ? TFromUnionKeyRest & TFromUnionKeyRest : - L extends TLiteral ? TFromUnionKeyLiteralString & TFromUnionKeyRest : - L extends TLiteral ? TFromUnionKeyLiteralNumber & TFromUnionKeyRest : +type TFromUnionKeyRest = + Keys extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? ( + Left extends TUnion ? TFromUnionKeyRest & TFromUnionKeyRest : + Left extends TLiteral ? TFromUnionKeyLiteralString & TFromUnionKeyRest : + Left extends TLiteral ? TFromUnionKeyLiteralNumber & TFromUnionKeyRest : {}) : {} // prettier-ignore -type TFromUnionKey> = ( +type TFromUnionKey> = ( Ensure>> ) // prettier-ignore -function FromUnionKey(K: K, T: T, options: ObjectOptions): TFromUnionKey { - return RecordCreateFromKeys(IndexPropertyKeys(Union(K)), T, options) as never +function FromUnionKey(key: Key, type: Type, options: ObjectOptions): TFromUnionKey { + return RecordCreateFromKeys(IndexPropertyKeys(Union(key)), type, options) as never } // ------------------------------------------------------------------ // FromLiteralKey // ------------------------------------------------------------------ // prettier-ignore -type TFromLiteralKey = ( - Ensure]: T }>> +type TFromLiteralKey = ( + Ensure]: Type }>> ) // prettier-ignore -function FromLiteralKey(K: K, T: T, options: ObjectOptions): TFromLiteralKey { - return RecordCreateFromKeys([(K as string).toString()], T, options) as never +function FromLiteralKey(key: Key, type: Type, options: ObjectOptions): TFromLiteralKey { + return RecordCreateFromKeys([(key as string).toString()], type, options) as never } // ------------------------------------------------------------------ // TFromRegExpKey // ------------------------------------------------------------------ // prettier-ignore -type TFromRegExpKey<_ extends TRegExp, T extends TSchema> = ( - Ensure> +type TFromRegExpKey<_Key extends TRegExp, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromRegExpKey(K: K, T: T, options: ObjectOptions): TFromRegExpKey { - return RecordCreateFromPattern(K.source, T, options) as never +function FromRegExpKey(key: Key, type: Type, options: ObjectOptions): TFromRegExpKey { + return RecordCreateFromPattern(key.source, type, options) as never } // ------------------------------------------------------------------ // FromStringKey // ------------------------------------------------------------------ // prettier-ignore -type TFromStringKey<_ extends TString, T extends TSchema> = ( - Ensure> +type TFromStringKey<_Key extends TString, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromStringKey(K: K, T: T, options: ObjectOptions): TFromStringKey { - const pattern = IsUndefined(K.pattern) ? PatternStringExact : K.pattern - return RecordCreateFromPattern(pattern, T, options) as never +function FromStringKey(key: Key, type: Type, options: ObjectOptions): TFromStringKey { + const pattern = IsUndefined(key.pattern) ? PatternStringExact : key.pattern + return RecordCreateFromPattern(pattern, type, options) as never } // ------------------------------------------------------------------ // FromAnyKey // ------------------------------------------------------------------ // prettier-ignore -type TFromAnyKey<_ extends TAny, T extends TSchema> = ( - Ensure> +type TFromAnyKey<_Key extends TAny, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromAnyKey(K: K, T: T, options: ObjectOptions): TFromAnyKey { - return RecordCreateFromPattern(PatternStringExact, T, options) as never +function FromAnyKey(_: Key, type: Type, options: ObjectOptions): TFromAnyKey { + return RecordCreateFromPattern(PatternStringExact, type, options) as never } // ------------------------------------------------------------------ // FromNeverKey // ------------------------------------------------------------------ // prettier-ignore -type TFromNeverKey<_ extends TNever, T extends TSchema> = ( - Ensure> +type TFromNeverKey<_Key extends TNever, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromNeverKey(K: K, T: T, options: ObjectOptions): TFromNeverKey { - return RecordCreateFromPattern(PatternNeverExact, T, options) as never +function FromNeverKey(_key: Key, type: Type, options: ObjectOptions): TFromNeverKey { + return RecordCreateFromPattern(PatternNeverExact, type, options) as never } // ------------------------------------------------------------------ // FromIntegerKey // ------------------------------------------------------------------ // prettier-ignore -type TFromIntegerKey<_ extends TSchema, T extends TSchema> = ( - Ensure> +type TFromIntegerKey<_Key extends TSchema, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromIntegerKey(_: K, T: T, options: ObjectOptions): TFromIntegerKey { - return RecordCreateFromPattern(PatternNumberExact, T, options) as never +function FromIntegerKey(_key: Key, type: Type, options: ObjectOptions): TFromIntegerKey { + return RecordCreateFromPattern(PatternNumberExact, type, options) as never } // ------------------------------------------------------------------ // FromNumberKey // ------------------------------------------------------------------ // prettier-ignore -type TFromNumberKey<_ extends TSchema, T extends TSchema> = ( - Ensure> +type TFromNumberKey<_Key extends TSchema, Type extends TSchema> = ( + Ensure> ) // prettier-ignore -function FromNumberKey(_: K, T: T, options: ObjectOptions): TFromNumberKey { - return RecordCreateFromPattern(PatternNumberExact, T, options) as never +function FromNumberKey(_: Key, type: Type, options: ObjectOptions): TFromNumberKey { + return RecordCreateFromPattern(PatternNumberExact, type, options) as never } // ------------------------------------------------------------------ // TRecord // ------------------------------------------------------------------ // prettier-ignore -type RecordStatic = ( - Evaluate<{ [_ in Assert, PropertyKey>]: Static; }> +type RecordStatic = ( + Evaluate<{ [_ in Assert, PropertyKey>]: Static; }> ) // prettier-ignore -export interface TRecord extends TSchema { +export interface TRecord extends TSchema { [Kind]: 'Record' - static: RecordStatic + static: RecordStatic type: 'object' - patternProperties: { [pattern: string]: T } + patternProperties: { [pattern: string]: Type } additionalProperties: TAdditionalProperties } // ------------------------------------------------------------------ // TRecordOrObject // ------------------------------------------------------------------ // prettier-ignore -export type TRecordOrObject = - Type extends TRef ? TComputed<'Record', [Key, Type]> : - Key extends TRef ? TComputed<'Record', [Key, Type]> : +export type TRecordOrObject = ( + Type extends TComputed ? TComputed<'Record', [Key, TComputed]> : + Key extends TComputed ? TComputed<'Record', [TComputed, Type]> : + Key extends TRef ? TComputed<'Record', [TRef, Type]> : Key extends TTemplateLiteral ? TFromTemplateLiteralKey : - Key extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) - Key extends TUnion ? TFromUnionKey : - Key extends TLiteral ? TFromLiteralKey : + Key extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) + Key extends TUnion ? TFromUnionKey : + Key extends TLiteral ? TFromLiteralKey : Key extends TInteger ? TFromIntegerKey : Key extends TNumber ? TFromNumberKey : Key extends TRegExp ? TFromRegExpKey : Key extends TString ? TFromStringKey : Key extends TAny ? TFromAnyKey : Key extends TNever ? TFromNeverKey : - TNever + Key +) // ------------------------------------------------------------------ // TRecordOrObject // ------------------------------------------------------------------ @@ -242,8 +241,9 @@ export type TRecordOrObject = export function Record(key: Key, type: Type, options: ObjectOptions = {}): TRecordOrObject { // prettier-ignore return ( - IsRef(type) ? Computed('Record', [key, type]) : - IsRef(key) ? Computed('Record', [key, type]) : + IsComputed(type) ? Computed('Record', [key, Computed(type.target, type.parameters)], options) : + IsComputed(key) ? Computed('Record', [Computed(type.target, type.parameters), type], options) : + IsRef(key) ? Computed('Record', [Ref(key.$ref), type]) : IsUnion(key) ? FromUnionKey(key.anyOf, type, options) : IsTemplateLiteral(key) ? FromTemplateLiteralKey(key, type, options) : IsLiteral(key) ? FromLiteralKey(key.const, type, options) : diff --git a/test/runtime/type/guard/kind/computed.ts b/test/runtime/type/guard/kind/computed.ts new file mode 100644 index 000000000..64a69047f --- /dev/null +++ b/test/runtime/type/guard/kind/computed.ts @@ -0,0 +1,28 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TComputed', () => { + // ---------------------------------------------------------------- + // Record + // ---------------------------------------------------------------- + it('Should guard for Record 1', () => { + const T = Type.Record(Type.String(), Type.String()) + Assert.IsTrue(KindGuard.IsRecord(T)) + }) + // TRecord> is not computed. + it('Should guard for Record 3', () => { + const T = Type.Record(Type.String(), Type.Ref('A')) + Assert.IsTrue(KindGuard.IsRecord(T)) + }) + // TRecord]> is computed due to interior computed. + it('Should guard for Record 3', () => { + const T = Type.Record(Type.String(), Type.Partial(Type.Ref('A'))) + Assert.IsTrue(KindGuard.IsComputed(T)) + }) + // TRecord, TSchema> is computed as schematics may be transformed to TObject if finite + it('Should guard for Record 4', () => { + const T = Type.Record(Type.Ref('A'), Type.String()) + Assert.IsTrue(KindGuard.IsComputed(T)) + }) +}) diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts index 5feb06523..7474e26fd 100644 --- a/test/runtime/type/guard/kind/import.ts +++ b/test/runtime/type/guard/kind/import.ts @@ -13,6 +13,41 @@ describe('guard/kind/TImport', () => { Assert.IsTrue(KindGuard.IsString(N)) }) // ---------------------------------------------------------------- + // Computed: Options + // ---------------------------------------------------------------- + it('Should guard for TImport with Options 1', () => { + const Module = Type.Module({ + A: Type.String(), + }) + const A = Module.Import('A', { format: 'string' }) + const N = A.$defs[A.$ref] + Assert.IsTrue(KindGuard.IsImport(A)) + Assert.IsTrue(KindGuard.IsString(N)) + Assert.IsTrue(N.format === 'string') + }) + it('Should guard for TImport with Options 2', () => { + const Module = Type.Module({ + R: Type.Object({ x: Type.Number() }), + A: Type.Ref('R'), + }) + const A = Module.Import('A', { test: 'test' }) + const N = A.$defs[A.$ref] + Assert.IsTrue(KindGuard.IsImport(A)) + Assert.IsTrue(KindGuard.IsRef(N)) + Assert.IsTrue(N.test === 'test') + }) + it('Should guard for TImport with Options 3', () => { + const Module = Type.Module({ + R: Type.Object({ x: Type.Number() }), + A: Type.Partial(Type.Ref('R')), + }) + const A = Module.Import('A', { additionalProperties: false }) + const N = A.$defs[A.$ref] + Assert.IsTrue(KindGuard.IsImport(A)) + Assert.IsTrue(KindGuard.IsObject(N)) + Assert.IsTrue(N.additionalProperties === false) + }) + // ---------------------------------------------------------------- // Computed: Awaited // ---------------------------------------------------------------- it('Should compute for Awaited', () => { @@ -110,8 +145,11 @@ describe('guard/kind/TImport', () => { }) const T = Module.Import('R') Assert.IsTrue(KindGuard.IsRecord(T.$defs['R'])) - Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].patternProperties['^(.*)$'].properties.x)) - Assert.IsTrue(KindGuard.IsString(T.$defs['R'].patternProperties['^(.*)$'].properties.y)) + // note: TRecord> are not computed. Only the Key is + // computed as TypeBox needs to make a deferred call to transform from + // TRecord to TObject for finite keys. + Assert.IsTrue(KindGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) + Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) it('Should compute for Record 2', () => { const Module = Type.Module({ diff --git a/test/runtime/type/guard/kind/index.ts b/test/runtime/type/guard/kind/index.ts index ec0073daa..d3d241ded 100644 --- a/test/runtime/type/guard/kind/index.ts +++ b/test/runtime/type/guard/kind/index.ts @@ -6,6 +6,7 @@ import './bigint' import './boolean' import './capitalize' import './composite' +import './computed' import './const' import './constructor' import './date' diff --git a/test/runtime/type/guard/type/computed.ts b/test/runtime/type/guard/type/computed.ts new file mode 100644 index 000000000..fc3724264 --- /dev/null +++ b/test/runtime/type/guard/type/computed.ts @@ -0,0 +1,28 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/type/TComputed', () => { + // ---------------------------------------------------------------- + // Record + // ---------------------------------------------------------------- + it('Should guard for Record 1', () => { + const T = Type.Record(Type.String(), Type.String()) + Assert.IsTrue(KindGuard.IsRecord(T)) + }) + // TRecord> is not computed. + it('Should guard for Record 3', () => { + const T = Type.Record(Type.String(), Type.Ref('A')) + Assert.IsTrue(KindGuard.IsRecord(T)) + }) + // TRecord]> is computed due to interior computed. + it('Should guard for Record 3', () => { + const T = Type.Record(Type.String(), Type.Partial(Type.Ref('A'))) + Assert.IsTrue(KindGuard.IsComputed(T)) + }) + // TRecord, TSchema> is computed as schematics may be transformed to TObject if finite + it('Should guard for Record 4', () => { + const T = Type.Record(Type.Ref('A'), Type.String()) + Assert.IsTrue(KindGuard.IsComputed(T)) + }) +}) diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts index f38124320..05b56739c 100644 --- a/test/runtime/type/guard/type/import.ts +++ b/test/runtime/type/guard/type/import.ts @@ -13,6 +13,41 @@ describe('guard/type/TImport', () => { Assert.IsTrue(TypeGuard.IsString(N)) }) // ---------------------------------------------------------------- + // Computed: Options + // ---------------------------------------------------------------- + it('Should guard for TImport with Options 1', () => { + const Module = Type.Module({ + A: Type.String(), + }) + const A = Module.Import('A', { format: 'string' }) + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsImport(A)) + Assert.IsTrue(TypeGuard.IsString(N)) + Assert.IsTrue(N.format === 'string') + }) + it('Should guard for TImport with Options 2', () => { + const Module = Type.Module({ + R: Type.Object({ x: Type.Number() }), + A: Type.Ref('R'), + }) + const A = Module.Import('A', { test: 'test' }) + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsImport(A)) + Assert.IsTrue(TypeGuard.IsRef(N)) + Assert.IsTrue(N.test === 'test') + }) + it('Should guard for TImport with Options 3', () => { + const Module = Type.Module({ + R: Type.Object({ x: Type.Number() }), + A: Type.Partial(Type.Ref('R')), + }) + const A = Module.Import('A', { additionalProperties: false }) + const N = A.$defs[A.$ref] + Assert.IsTrue(TypeGuard.IsImport(A)) + Assert.IsTrue(TypeGuard.IsObject(N)) + Assert.IsTrue(N.additionalProperties === false) + }) + // ---------------------------------------------------------------- // Computed: Awaited // ---------------------------------------------------------------- it('Should compute for Awaited', () => { @@ -109,9 +144,14 @@ describe('guard/type/TImport', () => { R: Type.Record(Type.String(), Type.Ref('T')), }) const T = Module.Import('R') + + console.dir(T, { depth: 100 }) Assert.IsTrue(TypeGuard.IsRecord(T.$defs['R'])) - Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].patternProperties['^(.*)$'].properties.x)) - Assert.IsTrue(TypeGuard.IsString(T.$defs['R'].patternProperties['^(.*)$'].properties.y)) + // note: TRecord> are not computed. Only the Key is + // computed as TypeBox needs to make a deferred call to transform from + // TRecord to TObject for finite keys. + Assert.IsTrue(TypeGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) + Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) it('Should compute for Record 2', () => { const Module = Type.Module({ diff --git a/test/runtime/type/guard/type/index.ts b/test/runtime/type/guard/type/index.ts index ec0073daa..d3d241ded 100644 --- a/test/runtime/type/guard/type/index.ts +++ b/test/runtime/type/guard/type/index.ts @@ -6,6 +6,7 @@ import './bigint' import './boolean' import './capitalize' import './composite' +import './computed' import './const' import './constructor' import './date' diff --git a/test/static/import.ts b/test/static/import.ts index bab132f11..0998817b0 100644 --- a/test/static/import.ts +++ b/test/static/import.ts @@ -1,5 +1,5 @@ import { Expect } from './assert' -import { Type } from '@sinclair/typebox' +import { Type, Static } from '@sinclair/typebox' // ------------------------------------------------------------------ // Enum 1 @@ -38,3 +38,66 @@ import { Type } from '@sinclair/typebox' value: 1 | 2 }>() } +// ------------------------------------------------------------------ +// Record 1 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Module({ + R: Type.Object({ x: Type.Number(), y: Type.Number() }), + T: Type.Record(Type.String(), Type.Ref('R')), + }).Import('T') + + type T = Static + Expect(T).ToStatic<{ + [key: string]: { x: number, y: number } + }>() +} +// ------------------------------------------------------------------ +// Record 2 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Module({ + R: Type.Object({ x: Type.Number(), y: Type.Number() }), + T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))), + }).Import('T') + + type T = Static + Expect(T).ToStatic<{ + [key: string]: { x?: number, y?: number } + }>() +} +// ------------------------------------------------------------------ +// Record 3 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Module({ + R: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.Number(), + T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), + }).Import('T') + + type T = Static + Expect(T).ToStatic<{ + [key: number]: { x?: number, y?: number } + }>() +} +// ------------------------------------------------------------------ +// Record 4 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Module({ + R: Type.Object({ x: Type.Number(), y: Type.Number() }), + K: Type.TemplateLiteral('${A|B|C}'), + T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), + }).Import('T') + type T = Static + Expect(T).ToStatic<{ + A: { x?: number, y?: number }, + B: { x?: number, y?: number }, + C: { x?: number, y?: number } + }>() +} From 2c5dbab611afa5be38af1144e4b1134548a5daa7 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 21 Nov 2024 02:29:16 +0900 Subject: [PATCH 311/369] Revision 0.34.6 (#1090) * Add Computed To Type and Kind Guards * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/type/guard/kind.ts | 1 + src/type/guard/type.ts | 9 ++++++++- src/type/module/compute.ts | 4 ++-- test/runtime/type/guard/kind/computed.ts | 8 ++++++++ test/runtime/type/guard/type/computed.ts | 18 +++++++++++++----- 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index a43929440..77f5d0146 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.6](https://github.com/sinclairzx81/typebox/pull/1090) + - Add Computed To Type and Kind Guards (IsSchema) - [Revision 0.34.5](https://github.com/sinclairzx81/typebox/pull/1088) - Record Types no longer TCompute for TRef Value Type (Modules) - [Revision 0.34.4](https://github.com/sinclairzx81/typebox/pull/1085) diff --git a/package-lock.json b/package-lock.json index 61ab83569..4b4175ebb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.5", + "version": "0.34.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.5", + "version": "0.34.6", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f061e7736..7ccb40d08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.5", + "version": "0.34.6", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index 95e14fc2f..ed3f68f2f 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -265,6 +265,7 @@ export function IsSchema(value: unknown): value is TSchema { IsBoolean(value) || IsBigInt(value) || IsAsyncIterator(value) || + IsComputed(value) || IsConstructor(value) || IsDate(value) || IsFunction(value) || diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index b4564bed2..ebd4b8f8f 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -221,7 +221,13 @@ export function IsBoolean(value: unknown): value is TBoolean { } /** Returns true if the given value is TComputed */ export function IsComputed(value: unknown): value is TComputed { - return IsKindOf(value, 'Computed') && IsString(value.target) && ValueGuard.IsArray(value.parameters) && value.parameters.every((schema) => IsSchema(schema)) + // prettier-ignore + return ( + IsKindOf(value, 'Computed') && + ValueGuard.IsString(value.target) && + ValueGuard.IsArray(value.parameters) && + value.parameters.every((schema) => IsSchema(schema)) + ) } /** Returns true if the given value is TConstructor */ export function IsConstructor(value: unknown): value is TConstructor { @@ -606,6 +612,7 @@ export function IsSchema(value: unknown): value is TSchema { IsBoolean(value) || IsBigInt(value) || IsAsyncIterator(value) || + IsComputed(value) || IsConstructor(value) || IsDate(value) || IsFunction(value) || diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 1e9bca28d..94a134829 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -36,7 +36,7 @@ import { TComputed } from '../computed/index' import { Constructor, type TConstructor } from '../constructor/index' import { Index, type TIndex } from '../indexed/index' import { TEnum, TEnumRecord } from '../enum/index' -import { Function, type TFunction } from '../function/index' +import { Function as FunctionType, type TFunction } from '../function/index' import { Intersect, type TIntersect, type TIntersectEvaluated } from '../intersect/index' import { Iterator, type TIterator } from '../iterator/index' import { KeyOf, type TKeyOf } from '../keyof/index' @@ -261,7 +261,7 @@ function FromFunction { - return Function(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, returnType) as never) as never + return FunctionType(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, returnType) as never) as never } // ------------------------------------------------------------------ // Tuple diff --git a/test/runtime/type/guard/kind/computed.ts b/test/runtime/type/guard/kind/computed.ts index 64a69047f..38262833e 100644 --- a/test/runtime/type/guard/kind/computed.ts +++ b/test/runtime/type/guard/kind/computed.ts @@ -3,6 +3,14 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/kind/TComputed', () => { + // ---------------------------------------------------------------- + // Schema + // ---------------------------------------------------------------- + it('Should guard for Schema', () => { + const T = Type.Partial(Type.Ref('A')) + Assert.IsTrue(KindGuard.IsComputed(T)) + Assert.IsTrue(KindGuard.IsSchema(T)) + }) // ---------------------------------------------------------------- // Record // ---------------------------------------------------------------- diff --git a/test/runtime/type/guard/type/computed.ts b/test/runtime/type/guard/type/computed.ts index fc3724264..34de3be67 100644 --- a/test/runtime/type/guard/type/computed.ts +++ b/test/runtime/type/guard/type/computed.ts @@ -1,28 +1,36 @@ -import { KindGuard } from '@sinclair/typebox' +import { TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TComputed', () => { + // ---------------------------------------------------------------- + // Schema + // ---------------------------------------------------------------- + it('Should guard for Schema', () => { + const T = Type.Partial(Type.Ref('A')) + Assert.IsTrue(TypeGuard.IsComputed(T)) + Assert.IsTrue(TypeGuard.IsSchema(T)) + }) // ---------------------------------------------------------------- // Record // ---------------------------------------------------------------- it('Should guard for Record 1', () => { const T = Type.Record(Type.String(), Type.String()) - Assert.IsTrue(KindGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsRecord(T)) }) // TRecord> is not computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Ref('A')) - Assert.IsTrue(KindGuard.IsRecord(T)) + Assert.IsTrue(TypeGuard.IsRecord(T)) }) // TRecord]> is computed due to interior computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Partial(Type.Ref('A'))) - Assert.IsTrue(KindGuard.IsComputed(T)) + Assert.IsTrue(TypeGuard.IsComputed(T)) }) // TRecord, TSchema> is computed as schematics may be transformed to TObject if finite it('Should guard for Record 4', () => { const T = Type.Record(Type.Ref('A'), Type.String()) - Assert.IsTrue(KindGuard.IsComputed(T)) + Assert.IsTrue(TypeGuard.IsComputed(T)) }) }) From b13b3c33fbf5ae70dd35fd0e775951ddb03dbfcf Mon Sep 17 00:00:00 2001 From: Nathan Smith Date: Wed, 20 Nov 2024 23:50:00 -0500 Subject: [PATCH 312/369] Documentation (#1091) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 8b1eeb35a..740e7c3e9 100644 --- a/readme.md +++ b/readme.md @@ -337,7 +337,7 @@ The following table lists the supported Json types. These types are fully compat │ }), │ y: number │ required: ['x'], │ │ Type.Object({ │ } │ properties: { │ │ y: Type.Number() │ │ x: { │ -│ ]) │ │ type: 'number' │ +│ }) │ │ type: 'number' │ │ ]) │ │ } │ │ │ │ } │ │ │ │ }, { │ From b05c59c13bc95679a5ec1c202e7007e2aa127f1a Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 21 Nov 2024 13:53:20 +0900 Subject: [PATCH 313/369] Revision 0.34.7 (#1093) * Revert Ref(Schema) Signature with Deprecation Notice * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- readme.md | 10 +--------- src/type/ref/ref.ts | 28 +++++++++++++++++++++++++++- src/type/type/json.ts | 27 +++++++++++++++++++++++---- test/runtime/compiler-ajv/ref.ts | 12 ++++++++++++ test/runtime/compiler/ref.ts | 12 ++++++++++++ test/runtime/type/guard/kind/ref.ts | 21 +++++++++++++++++++++ test/runtime/type/guard/type/ref.ts | 21 +++++++++++++++++++++ test/runtime/value/check/ref.ts | 12 ++++++++++++ 11 files changed, 134 insertions(+), 17 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 77f5d0146..6a2ea89e7 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.7](https://github.com/sinclairzx81/typebox/pull/1093) + - Revert Ref(Schema) Signature with Deprecation Notice - [Revision 0.34.6](https://github.com/sinclairzx81/typebox/pull/1090) - Add Computed To Type and Kind Guards (IsSchema) - [Revision 0.34.5](https://github.com/sinclairzx81/typebox/pull/1088) diff --git a/package-lock.json b/package-lock.json index 4b4175ebb..a3eedbdaf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.6", + "version": "0.34.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.6", + "version": "0.34.7", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 7ccb40d08..e630cd735 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.6", + "version": "0.34.7", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 740e7c3e9..c6438cb85 100644 --- a/readme.md +++ b/readme.md @@ -534,15 +534,7 @@ The following table lists the supported Json types. These types are fully compat │ │ │ } │ │ │ │ │ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Object({ │ type T = { │ const R = { │ -│ x: Type.Number(), │ x: number, │ $ref: 'T' │ -│ y: Type.Number() │ y: number │ } │ -│ }, { $id: 'T' }) | } │ │ -│ │ │ │ -│ const R = Type.Ref(T) │ type R = T │ │ -│ │ │ │ -│ │ │ │ -│ │ │ │ +│ const R = Type.Ref('T') │ type R = unknown │ const R = { $ref: 'T' } │ │ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index f61f04fac..f50f6a7ed 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -27,8 +27,11 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' +import { TypeBoxError } from '../error/index' import { CreateType } from '../create/type' import { Kind } from '../symbols/index' +import { TUnsafe } from '../unsafe/index' +import { Static } from '../static/index' // ------------------------------------------------------------------ // TRef @@ -38,7 +41,30 @@ export interface TRef extends TSchema { static: unknown $ref: Ref } + +export type TRefUnsafe = TUnsafe> + +/** `[Json]` Creates a Ref type.*/ +export function Ref($ref: Ref, options?: SchemaOptions): TRef +/** + * @deprecated `[Json]` Creates a Ref type. The referenced type MUST contain a $id. The Ref(TSchema) signature was + * deprecated on 0.34.0 in support of a new type referencing model (Module). Existing implementations using Ref(TSchema) + * can migrate using the following. The Ref(TSchema) validation behavior of Ref will be preserved how the construction + * of legacy Ref(TSchema) will require wrapping in TUnsafe (where TUnsafe is used for inference only) + * + * ```typescript + * const R = Type.Ref(T) + * ``` + * to + * + * ```typescript + * const R = Type.Unsafe>(T.$id) + * ``` + */ +export function Ref(type: Type, options?: SchemaOptions): TRefUnsafe /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ -export function Ref($ref: Ref, options?: SchemaOptions): TRef { +export function Ref(...args: any[]): unknown { + const [$ref, options] = typeof args[0] === 'string' ? [args[0], args[1]] : [args[0].$id, args[1]] + if (typeof $ref !== 'string') throw new TypeBoxError('Ref: $ref must be a string') return CreateType({ [Kind]: 'Ref', $ref }, options) as never } diff --git a/src/type/type/json.ts b/src/type/type/json.ts index acc91ce1f..f7efb99c4 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -57,7 +57,7 @@ import { Readonly, type TReadonlyWithFlag, type TReadonlyFromMappedResult } from import { ReadonlyOptional, type TReadonlyOptional } from '../readonly-optional/index' import { Record, type TRecordOrObject } from '../record/index' import { Recursive, type TRecursive, type TThis } from '../recursive/index' -import { Ref, type TRef } from '../ref/index' +import { Ref, type TRef, type TRefUnsafe } from '../ref/index' import { Required, type TRequired, type TRequiredFromMappedResult } from '../required/index' import { Rest, type TRest } from '../rest/index' import { type TSchema, type SchemaOptions } from '../schema/index' @@ -259,9 +259,28 @@ export class JsonTypeBuilder { public Recursive(callback: (thisType: TThis) => T, options?: SchemaOptions): TRecursive { return Recursive(callback, options) } - /** `[Json]` Creates a Ref type. */ - public Ref($ref: Ref, options?: SchemaOptions): TRef { - return Ref($ref, options) + + /** `[Json]` Creates a Ref type.*/ + public Ref($ref: Ref, options?: SchemaOptions): TRef + /** + * @deprecated `[Json]` Creates a Ref type. The referenced type MUST contain a $id. The Ref(TSchema) signature was + * deprecated on 0.34.0 in support of a new type referencing model (Module). Existing implementations using Ref(TSchema) + * can migrate using the following. The Ref(TSchema) validation behavior of Ref will be preserved how the construction + * of legacy Ref(TSchema) will require wrapping in TUnsafe (where TUnsafe is used for inference only) + * + * ```typescript + * const R = Type.Ref(T) + * ``` + * to + * + * ```typescript + * const R = Type.Unsafe>(T.$id) + * ``` + */ + public Ref(type: Type, options?: SchemaOptions): TRefUnsafe + /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ + public Ref(...args: any[]): unknown { + return Ref(args[0] as string, args[1]) } /** `[Json]` Constructs a type where all properties are required */ public Required(type: MappedResult, options?: SchemaOptions): TRequiredFromMappedResult diff --git a/test/runtime/compiler-ajv/ref.ts b/test/runtime/compiler-ajv/ref.ts index 564dbc197..ac0815b13 100644 --- a/test/runtime/compiler-ajv/ref.ts +++ b/test/runtime/compiler-ajv/ref.ts @@ -2,6 +2,18 @@ import { Type } from '@sinclair/typebox' import { Ok, Fail } from './validate' describe('compiler-ajv/Ref', () => { + // ---------------------------------------------------------------- + // Deprecated + // ---------------------------------------------------------------- + it('Should validate for Ref(Schema)', () => { + const T = Type.Number({ $id: 'T' }) + const R = Type.Ref(T) + Ok(R, 1234, [T]) + Fail(R, 'hello', [T]) + }) + // ---------------------------------------------------------------- + // Standard + // ---------------------------------------------------------------- it('Should should validate when referencing a type', () => { const T = Type.Object( { diff --git a/test/runtime/compiler/ref.ts b/test/runtime/compiler/ref.ts index 1cd002f80..75470f8e5 100644 --- a/test/runtime/compiler/ref.ts +++ b/test/runtime/compiler/ref.ts @@ -3,6 +3,18 @@ import { Ok, Fail } from './validate' import { Assert } from '../assert/index' describe('compiler/Ref', () => { + // ---------------------------------------------------------------- + // Deprecated + // ---------------------------------------------------------------- + it('Should validate for Ref(Schema)', () => { + const T = Type.Number({ $id: 'T' }) + const R = Type.Ref(T) + Ok(R, 1234, [T]) + Fail(R, 'hello', [T]) + }) + // ---------------------------------------------------------------- + // Standard + // ---------------------------------------------------------------- it('Should should validate when referencing a type', () => { const T = Type.Object( { diff --git a/test/runtime/type/guard/kind/ref.ts b/test/runtime/type/guard/kind/ref.ts index 441d412cd..60d674634 100644 --- a/test/runtime/type/guard/kind/ref.ts +++ b/test/runtime/type/guard/kind/ref.ts @@ -3,6 +3,27 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/kind/TRef', () => { + // ---------------------------------------------------------------- + // Deprecated + // ---------------------------------------------------------------- + it('Should guard for Ref(Schema) 1', () => { + const T = Type.Number({ $id: 'T' }) + const R = Type.Ref(T) + Assert.IsTrue(KindGuard.IsRef(R)) + Assert.IsTrue(typeof R['$ref'] === 'string') + }) + it('Should guard for Ref(Schema) 2', () => { + const T = Type.Number() + Assert.Throws(() => Type.Ref(T)) + }) + it('Should guard for Ref(Schema) 3', () => { + // @ts-ignore + const T = Type.Number({ $id: null }) + Assert.Throws(() => Type.Ref(T)) + }) + // ---------------------------------------------------------------- + // Standard + // ---------------------------------------------------------------- it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = KindGuard.IsRef(Type.Ref(T)) diff --git a/test/runtime/type/guard/type/ref.ts b/test/runtime/type/guard/type/ref.ts index c11b1a39c..0228f01af 100644 --- a/test/runtime/type/guard/type/ref.ts +++ b/test/runtime/type/guard/type/ref.ts @@ -3,6 +3,27 @@ import { Type, CloneType } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TRef', () => { + // ---------------------------------------------------------------- + // Deprecated + // ---------------------------------------------------------------- + it('Should guard for Ref(Schema) 1', () => { + const T = Type.Number({ $id: 'T' }) + const R = Type.Ref(T) + Assert.IsTrue(TypeGuard.IsRef(R)) + Assert.IsTrue(typeof R['$ref'] === 'string') + }) + it('Should guard for Ref(Schema) 2', () => { + const T = Type.Number() + Assert.Throws(() => Type.Ref(T)) + }) + it('Should guard for Ref(Schema) 3', () => { + // @ts-ignore + const T = Type.Number({ $id: null }) + Assert.Throws(() => Type.Ref(T)) + }) + // ---------------------------------------------------------------- + // Standard + // ---------------------------------------------------------------- it('Should guard for TRef', () => { const T = Type.Number({ $id: 'T' }) const R = TypeGuard.IsRef(Type.Ref('T')) diff --git a/test/runtime/value/check/ref.ts b/test/runtime/value/check/ref.ts index 20156eb8d..aa7d76f03 100644 --- a/test/runtime/value/check/ref.ts +++ b/test/runtime/value/check/ref.ts @@ -3,6 +3,18 @@ import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('value/check/Ref', () => { + // ---------------------------------------------------------------- + // Deprecated + // ---------------------------------------------------------------- + it('Should validate for Ref(Schema)', () => { + const T = Type.Number({ $id: 'T' }) + const R = Type.Ref(T) + Assert.IsTrue(Value.Check(T, [T], 1234)) + Assert.IsFalse(Value.Check(T, [T], 'hello')) + }) + // ---------------------------------------------------------------- + // Standard + // ---------------------------------------------------------------- it('Should should validate when referencing a type', () => { const T = Type.Object( { From 81f0a28ad7aad4ba45693d47dc212fa81b9089b2 Mon Sep 17 00:00:00 2001 From: Michael Kaeser <87946992+michaelcollabai@users.noreply.github.com> Date: Sat, 23 Nov 2024 08:50:25 +0100 Subject: [PATCH 314/369] Fix for Computed Readonly and Optional Properties (#1096) * Fix importing optional properties * Readonly and Optional Unwrap for Computed Type * Comment for Modifier Reapply on Unwrap --------- Co-authored-by: sinclair --- src/type/module/compute.ts | 12 ++++ test/runtime/compiler-ajv/module.ts | 31 +++++++++ test/runtime/compiler/module.ts | 31 +++++++++ test/runtime/type/guard/kind/import.ts | 87 +++++++++++++++++++++++++ test/runtime/type/guard/type/import.ts | 89 +++++++++++++++++++++++++- test/runtime/value/check/module.ts | 31 +++++++++ test/static/import.ts | 62 ++++++++++++++++++ 7 files changed, 341 insertions(+), 2 deletions(-) diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 94a134829..b2ee6e785 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/index' +import { Discard } from '../discard/index' import { Ensure, Evaluate } from '../helpers/index' import { type TSchema } from '../schema/index' import { Array, type TArray } from '../array/index' @@ -42,15 +43,18 @@ import { Iterator, type TIterator } from '../iterator/index' import { KeyOf, type TKeyOf } from '../keyof/index' import { Object, type TObject, type TProperties } from '../object/index' import { Omit, type TOmit } from '../omit/index' +import { type TOptional } from '../optional/index' import { Pick, type TPick } from '../pick/index' import { Never, type TNever } from '../never/index' import { Partial, TPartial } from '../partial/index' +import { type TReadonly } from '../readonly/index' import { Record, type TRecordOrObject } from '../record/index' import { type TRef } from '../ref/index' import { Required, TRequired } from '../required/index' import { Tuple, type TTuple } from '../tuple/index' import { Union, type TUnion, type TUnionEvaluated } from '../union/index' +import { OptionalKind, ReadonlyKind } from '../symbols/index' // ------------------------------------------------------------------ // KindGuard // ------------------------------------------------------------------ @@ -340,6 +344,10 @@ function FromRest // ------------------------------------------------------------------ // prettier-ignore export type TFromType = ( + // Modifier Unwrap + Type extends TOptional ? TOptional> : + Type extends TReadonly ? TReadonly> : + // Traveral Type extends TArray ? TFromArray : Type extends TAsyncIterator ? TFromAsyncIterator : Type extends TComputed ? TFromComputed : @@ -356,6 +364,10 @@ export type TFromType(moduleProperties: ModuleProperties, type: Type): TFromType { return ( + // Modifier Unwrap - Reapplied via CreateType Options + KindGuard.IsOptional(type) ? CreateType(FromType(moduleProperties, Discard(type, [OptionalKind]) as TSchema) as never, type) : + KindGuard.IsReadonly(type) ? CreateType(FromType(moduleProperties, Discard(type, [ReadonlyKind]) as TSchema) as never, type) : + // Traveral KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : // Note: The 'as never' is required due to excessive resolution of TIndex. In fact TIndex, TPick, TOmit and diff --git a/test/runtime/compiler-ajv/module.ts b/test/runtime/compiler-ajv/module.ts index 9de6813b7..756c09f36 100644 --- a/test/runtime/compiler-ajv/module.ts +++ b/test/runtime/compiler-ajv/module.ts @@ -75,4 +75,35 @@ describe('compiler-ajv/Module', () => { Ok(T, 'hello') Fail(T, 'world') }) + // ---------------------------------------------------------------- + // Modifiers + // ---------------------------------------------------------------- + it('Should validate objects with property modifiers 1', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null(), + }), + }) + const T = Module.Import('T') + Ok(T, { x: null, y: null, w: null }) + Ok(T, { y: null, w: null }) + Fail(T, { x: 1, y: null, w: null }) + }) + it('Should validate objects with property modifiers 2', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()), + }), + }) + const T = Module.Import('T') + Ok(T, { x: [null], y: [null], w: [null] }) + Ok(T, { y: [null], w: [null] }) + Fail(T, { x: [1], y: [null], w: [null] }) + }) }) diff --git a/test/runtime/compiler/module.ts b/test/runtime/compiler/module.ts index 02fcb4d45..32b455605 100644 --- a/test/runtime/compiler/module.ts +++ b/test/runtime/compiler/module.ts @@ -75,4 +75,35 @@ describe('compiler/Module', () => { Ok(T, 'hello') Fail(T, 'world') }) + // ---------------------------------------------------------------- + // Modifiers + // ---------------------------------------------------------------- + it('Should validate objects with property modifiers 1', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null(), + }), + }) + const T = Module.Import('T') + Ok(T, { x: null, y: null, w: null }) + Ok(T, { y: null, w: null }) + Fail(T, { x: 1, y: null, w: null }) + }) + it('Should validate objects with property modifiers 2', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()), + }), + }) + const T = Module.Import('T') + Ok(T, { x: [null], y: [null], w: [null] }) + Ok(T, { y: [null], w: [null] }) + Fail(T, { x: [1], y: [null], w: [null] }) + }) }) diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts index 7474e26fd..55bd572e7 100644 --- a/test/runtime/type/guard/kind/import.ts +++ b/test/runtime/type/guard/kind/import.ts @@ -190,4 +190,91 @@ describe('guard/kind/TImport', () => { Assert.IsTrue(T.$defs['R'].anyOf[0].const === 'x') Assert.IsTrue(T.$defs['R'].anyOf[1].const === 'y') }) + // ---------------------------------------------------------------- + // Modifiers: 1 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 1', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null(), + }), + }) + const T = Module.Import('T') + const R = T.$defs[T.$ref] + Assert.IsTrue(KindGuard.IsObject(R)) + + Assert.IsTrue(KindGuard.IsNull(R.properties.x)) + Assert.IsTrue(KindGuard.IsReadonly(R.properties.x)) + Assert.IsTrue(KindGuard.IsOptional(R.properties.x)) + + Assert.IsTrue(KindGuard.IsNull(R.properties.y)) + Assert.IsTrue(KindGuard.IsReadonly(R.properties.y)) + Assert.IsFalse(KindGuard.IsOptional(R.properties.y)) + + Assert.IsTrue(KindGuard.IsNull(R.properties.z)) + Assert.IsTrue(KindGuard.IsOptional(R.properties.z)) + Assert.IsFalse(KindGuard.IsReadonly(R.properties.z)) + + Assert.IsTrue(KindGuard.IsNull(R.properties.w)) + Assert.IsFalse(KindGuard.IsOptional(R.properties.w)) + Assert.IsFalse(KindGuard.IsReadonly(R.properties.w)) + }) + // ---------------------------------------------------------------- + // Modifiers: 2 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 2', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()), + }), + }) + const T = Module.Import('T') + const R = T.$defs[T.$ref] + Assert.IsTrue(KindGuard.IsObject(R)) + + Assert.IsTrue(KindGuard.IsArray(R.properties.x)) + Assert.IsTrue(KindGuard.IsNull(R.properties.x.items)) + Assert.IsTrue(KindGuard.IsReadonly(R.properties.x)) + Assert.IsTrue(KindGuard.IsOptional(R.properties.x)) + + Assert.IsTrue(KindGuard.IsArray(R.properties.y)) + Assert.IsTrue(KindGuard.IsNull(R.properties.y.items)) + Assert.IsTrue(KindGuard.IsReadonly(R.properties.y)) + Assert.IsFalse(KindGuard.IsOptional(R.properties.y)) + + Assert.IsTrue(KindGuard.IsArray(R.properties.z)) + Assert.IsTrue(KindGuard.IsNull(R.properties.z.items)) + Assert.IsTrue(KindGuard.IsOptional(R.properties.z)) + Assert.IsFalse(KindGuard.IsReadonly(R.properties.z)) + + Assert.IsTrue(KindGuard.IsArray(R.properties.w)) + Assert.IsTrue(KindGuard.IsNull(R.properties.w.items)) + Assert.IsFalse(KindGuard.IsOptional(R.properties.w)) + Assert.IsFalse(KindGuard.IsReadonly(R.properties.w)) + }) + // ---------------------------------------------------------------- + // Modifiers: 3 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 3', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.Array(Type.Null()), + }), + // Computed Partial + U: Type.Partial(Type.Ref('T')), + }) + const T = Module.Import('U') + const R = T.$defs[T.$ref] + Assert.IsTrue(KindGuard.IsObject(R)) + + Assert.IsTrue(KindGuard.IsArray(R.properties.x)) + Assert.IsTrue(KindGuard.IsNull(R.properties.x.items)) + Assert.IsTrue(KindGuard.IsOptional(R.properties.x)) + }) }) diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts index 05b56739c..5388e2f29 100644 --- a/test/runtime/type/guard/type/import.ts +++ b/test/runtime/type/guard/type/import.ts @@ -144,8 +144,6 @@ describe('guard/type/TImport', () => { R: Type.Record(Type.String(), Type.Ref('T')), }) const T = Module.Import('R') - - console.dir(T, { depth: 100 }) Assert.IsTrue(TypeGuard.IsRecord(T.$defs['R'])) // note: TRecord> are not computed. Only the Key is // computed as TypeBox needs to make a deferred call to transform from @@ -192,4 +190,91 @@ describe('guard/type/TImport', () => { Assert.IsTrue(T.$defs['R'].anyOf[0].const === 'x') Assert.IsTrue(T.$defs['R'].anyOf[1].const === 'y') }) + // ---------------------------------------------------------------- + // Modifiers: 1 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 1', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null(), + }), + }) + const T = Module.Import('T') + const R = T.$defs[T.$ref] + Assert.IsTrue(TypeGuard.IsObject(R)) + + Assert.IsTrue(TypeGuard.IsNull(R.properties.x)) + Assert.IsTrue(TypeGuard.IsReadonly(R.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(R.properties.x)) + + Assert.IsTrue(TypeGuard.IsNull(R.properties.y)) + Assert.IsTrue(TypeGuard.IsReadonly(R.properties.y)) + Assert.IsFalse(TypeGuard.IsOptional(R.properties.y)) + + Assert.IsTrue(TypeGuard.IsNull(R.properties.z)) + Assert.IsTrue(TypeGuard.IsOptional(R.properties.z)) + Assert.IsFalse(TypeGuard.IsReadonly(R.properties.z)) + + Assert.IsTrue(TypeGuard.IsNull(R.properties.w)) + Assert.IsFalse(TypeGuard.IsOptional(R.properties.w)) + Assert.IsFalse(TypeGuard.IsReadonly(R.properties.w)) + }) + // ---------------------------------------------------------------- + // Modifiers: 2 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 2', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()), + }), + }) + const T = Module.Import('T') + const R = T.$defs[T.$ref] + Assert.IsTrue(TypeGuard.IsObject(R)) + + Assert.IsTrue(TypeGuard.IsArray(R.properties.x)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.x.items)) + Assert.IsTrue(TypeGuard.IsReadonly(R.properties.x)) + Assert.IsTrue(TypeGuard.IsOptional(R.properties.x)) + + Assert.IsTrue(TypeGuard.IsArray(R.properties.y)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.y.items)) + Assert.IsTrue(TypeGuard.IsReadonly(R.properties.y)) + Assert.IsFalse(TypeGuard.IsOptional(R.properties.y)) + + Assert.IsTrue(TypeGuard.IsArray(R.properties.z)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.z.items)) + Assert.IsTrue(TypeGuard.IsOptional(R.properties.z)) + Assert.IsFalse(TypeGuard.IsReadonly(R.properties.z)) + + Assert.IsTrue(TypeGuard.IsArray(R.properties.w)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.w.items)) + Assert.IsFalse(TypeGuard.IsOptional(R.properties.w)) + Assert.IsFalse(TypeGuard.IsReadonly(R.properties.w)) + }) + // ---------------------------------------------------------------- + // Modifiers: 3 + // ---------------------------------------------------------------- + it('Should compute for Modifiers 3', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.Array(Type.Null()), + }), + // Computed Partial + U: Type.Partial(Type.Ref('T')), + }) + const T = Module.Import('U') + const R = T.$defs[T.$ref] + Assert.IsTrue(TypeGuard.IsObject(R)) + + Assert.IsTrue(TypeGuard.IsArray(R.properties.x)) + Assert.IsTrue(TypeGuard.IsNull(R.properties.x.items)) + Assert.IsTrue(TypeGuard.IsOptional(R.properties.x)) + }) }) diff --git a/test/runtime/value/check/module.ts b/test/runtime/value/check/module.ts index 3a64267de..371b302d0 100644 --- a/test/runtime/value/check/module.ts +++ b/test/runtime/value/check/module.ts @@ -77,4 +77,35 @@ describe('value/check/Module', () => { Assert.IsTrue(Value.Check(T, 'hello')) Assert.IsFalse(Value.Check(T, 'world')) }) + // ---------------------------------------------------------------- + // Modifiers + // ---------------------------------------------------------------- + it('Should validate objects with property modifiers 1', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null(), + }), + }) + const T = Module.Import('T') + Assert.IsTrue(Value.Check(T, { x: null, y: null, w: null })) + Assert.IsTrue(Value.Check(T, { y: null, w: null })) + Assert.IsFalse(Value.Check(T, { x: 1, y: null, w: null })) + }) + it('Should validate objects with property modifiers 2', () => { + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()), + }), + }) + const T = Module.Import('T') + Assert.IsTrue(Value.Check(T, { x: [null], y: [null], w: [null] })) + Assert.IsTrue(Value.Check(T, { y: [null], w: [null] })) + Assert.IsFalse(Value.Check(T, { x: [1], y: [null], w: [null] })) + }) }) diff --git a/test/static/import.ts b/test/static/import.ts index 0998817b0..ab546c453 100644 --- a/test/static/import.ts +++ b/test/static/import.ts @@ -101,3 +101,65 @@ import { Type, Static } from '@sinclair/typebox' C: { x?: number, y?: number } }>() } +// ------------------------------------------------------------------ +// Modifiers 1 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Null()), + y: Type.Readonly(Type.Null()), + z: Type.Optional(Type.Null()), + w: Type.Null() + }) + }) + const T = Module.Import('T') + type T = Static + Expect(T).ToStatic<{ + readonly x?: null, + readonly y: null, + z?: null, + w: null + }>() +} +// ------------------------------------------------------------------ +// Modifiers 2 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const Module = Type.Module({ + T: Type.Object({ + x: Type.ReadonlyOptional(Type.Array(Type.Null())), + y: Type.Readonly(Type.Array(Type.Null())), + z: Type.Optional(Type.Array(Type.Null())), + w: Type.Array(Type.Null()) + }) + }) + const T = Module.Import('T') + type T = Static + Expect(T).ToStatic<{ + readonly x?: null[], + readonly y: null[], + z?:null[], + w: null[] + }>() +} +// ------------------------------------------------------------------ +// Modifiers 3 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const Module = Type.Module({ + T: Type.Object({ + x: Type.Array(Type.Null()) + }), + // Computed Partial + U: Type.Partial(Type.Ref('T')) + }) + const T = Module.Import('U') + type T = Static + Expect(T).ToStatic<{ + x?: null[], + }>() +} From 10968abfed00577c7a71855ee09f46789502bcc9 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Sat, 23 Nov 2024 17:05:03 +0900 Subject: [PATCH 315/369] Revision 0.34.8 (#1098) * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 6a2ea89e7..755d04bf3 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.8](https://github.com/sinclairzx81/typebox/pull/1098) + - Fix for Computed Readonly and Optional Properties - [Revision 0.34.7](https://github.com/sinclairzx81/typebox/pull/1093) - Revert Ref(Schema) Signature with Deprecation Notice - [Revision 0.34.6](https://github.com/sinclairzx81/typebox/pull/1090) diff --git a/package-lock.json b/package-lock.json index a3eedbdaf..0257ff706 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.7", + "version": "0.34.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.7", + "version": "0.34.8", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e630cd735..b12b19f6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.7", + "version": "0.34.8", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From c4b84552a20bc9adf1f1336691ac0ba6b4afee4a Mon Sep 17 00:00:00 2001 From: sinclair Date: Mon, 25 Nov 2024 15:59:55 +0900 Subject: [PATCH 316/369] TypeScript 5.7.2 --- hammer.mjs | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index 82755bffb..02dacedfc 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -32,7 +32,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 0257ff706..8d9666303 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.6.3" + "typescript": "^5.7.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index b12b19f6b..b841d7895 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.6.3" + "typescript": "^5.7.2" } } From 428f906f304ef17bedbc41e062e0b576a241c8c2 Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 26 Nov 2024 14:26:05 +0900 Subject: [PATCH 317/369] Documentation --- readme.md | 90 +++++++++++++++++-------------------------------------- 1 file changed, 27 insertions(+), 63 deletions(-) diff --git a/readme.md b/readme.md index c6438cb85..4004ece9f 100644 --- a/readme.md +++ b/readme.md @@ -78,9 +78,6 @@ License MIT - [Unsafe](#types-unsafe) - [Syntax](#syntax) - [Parse](#syntax-parse) - - [Compose](#syntax-compose) - - [Context](#syntax-context) - - [Module](#syntax-module) - [Static](#syntax-static) - [Limits](#syntax-limits) - [Values](#values) @@ -1025,9 +1022,9 @@ if(TypeGuard.IsString(T)) { ## Syntax Types -TypeBox provides support for parsing TypeScript syntax directly into TypeBox Json Schema schematics. Syntax Types offer a string based DSL frontend to TypeBox's Type Builder system and can be useful for converting existing TypeScript type definitions into Json Schema schematics without reimplementation via the Type Builder. +Syntax Types is a feature that enables TypeBox to parse TypeScript type annotations directly into Json Schema. This feature works both at runtime and statically within the type system. Syntax Types use Json Schema as the Abstract Syntax Tree (AST) parse target for TypeScript types. Syntax Types are designed to offer a syntactical frontend to the standard Type Builder API. -Syntax Types are provided via optional import. +This feature is available via optional import. ```typescript import { Parse } from '@sinclair/typebox/syntax' @@ -1037,7 +1034,7 @@ import { Parse } from '@sinclair/typebox/syntax' ### Parse -Use the Parse function to convert a TypeScript string into a TypeBox type. TypeBox will infer the appropriate TSchema type or return undefined if there is a syntax error. +Use the Parse function to transform a TypeScript type annotation into a TypeBox type. This function will return the parsed TypeBox type or undefined on error. ```typescript const A = Parse('string') // const A: TString @@ -1054,45 +1051,46 @@ const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ // }> ``` - - - - -### Compose - -Syntax Types are designed to be interchangeable with standard Types. +Syntax Types can be composed with Standard Types. ```typescript const T = Type.Object({ // const T: TObject<{ x: Parse('number'), // x: TNumber, - y: Parse('number'), // y: TNumber, - z: Parse('number') // z: TNumber + y: Parse('string'), // y: TString, + z: Parse('boolean') // z: TBoolean }) // }> ``` - +It is also possible to pass Standard Types to Syntax Types in the following way. -### Module +```typescript +const X = Type.Number() +const Y = Type.String() +const Z = Type.Boolean() + +const T = Parse({ X, Y, Z }, `{ + x: X, + y: Y, + z: Z +}`) +``` -Syntax Types also support Module parsing. This can provide a more terse syntax for creating Module definitions, but comes with an inference performance cost. Module parsing supports interface and type alias definitions. Generics types are currently unsupported. +Syntax Types can also be used to parse Module types. ```typescript -const Module = Parse(`module { - +const Foo = Parse(`module Foo { + + export type PartialUser = Pick & Partial> + export interface User { id: string name: string email: string } - - export type PartialUser = ( - Pick & - Partial> - ) }`) -const PartialUser = Module.Import('PartialUser') // TImport<{...}, 'PartialUser'> +const PartialUser = Foo.Import('PartialUser') // TImport<{...}, 'PartialUser'> type PartialUser = Static // type PartialUser = { // id: string, @@ -1102,45 +1100,11 @@ type PartialUser = Static // type PartialUser = { // } ``` - - -### Context - -The Parse function takes an optional leading Context object that contains external types. This Context allows the syntax to reference these external types using the property identifiers provided by the Context. The following passes the external type `T` to Parse. - -```typescript -const T = Type.Object({ // const T: TObject<{ - x: Type.Number(), // x: TNumber, - y: Type.Number(), // y: TNumber, - z: Type.Number() // z: TNumber -}) // }> - -const A = Parse({ T }, 'Partial') // const A: TObject<{ - // x: TOptional, - // y: TOptional, - // z: TOptional - // }> - -const B = Parse({ T }, 'keyof T') // const B: TUnion<[ - // TLiteral<'x'>, - // TLiteral<'y'>, - // TLiteral<'z'> - // ]> - -const C = Parse({ T }, 'T & { w: number }') // const C: TIntersect<[TObject<{ - // x: TNumber; - // y: TNumber; - // z: TNumber; - // }>, TObject<{ - // w: TNumber; - // }>]> -``` - ### Static -Syntax Types provide two Static types for inferring TypeScript types and TypeBox schematics from strings. +Syntax Types provide two Static types specific to inferring TypeBox and TypeScript types from strings. ```typescript import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' @@ -1154,8 +1118,8 @@ type S = StaticParseAsSchema<{}, '{ x: number }'> // type S: TObject<{ // Will infer as a type type T = StaticParseAsType<{}, '{ x: number }'> // type T = { - // x: number - // + // x: number + // } ``` From cd9712c463a385ac3df8b2980b6adafaab249a66 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Tue, 26 Nov 2024 19:42:36 +0900 Subject: [PATCH 318/369] Revision 0.34.9 (#1101) * Parse Pipeline and TypeCheck Schema and Reference Accessor Functions * Documentation * Version --- changelog/0.34.0.md | 3 + package-lock.json | 4 +- package.json | 2 +- readme.md | 22 +++--- src/compiler/compiler.ts | 8 +++ src/value/parse/parse.ts | 106 ++++++++++++++++++++++------- test/runtime/compiler/__members.ts | 20 ++++++ test/runtime/compiler/index.ts | 1 + test/runtime/value/parse/parse.ts | 39 ++++++++++- 9 files changed, 167 insertions(+), 38 deletions(-) create mode 100644 test/runtime/compiler/__members.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 755d04bf3..7e5e46939 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,7 @@ ### 0.34.0 +- [Revision 0.34.9](https://github.com/sinclairzx81/typebox/pull/1101) + - User Defined Parse Pipelines + - Access to Schema and References on TypeCheck - [Revision 0.34.8](https://github.com/sinclairzx81/typebox/pull/1098) - Fix for Computed Readonly and Optional Properties - [Revision 0.34.7](https://github.com/sinclairzx81/typebox/pull/1093) diff --git a/package-lock.json b/package-lock.json index 8d9666303..a962edf2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.8", + "version": "0.34.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.8", + "version": "0.34.9", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index b841d7895..279b3dd59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.8", + "version": "0.34.9", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 4004ece9f..6dc0ceb83 100644 --- a/readme.md +++ b/readme.md @@ -1325,26 +1325,28 @@ const B = Value.Encode(Type.String(), 42) // throw ### Parse -Use the Parse function to parse a value or throw if invalid. This function internally uses Default, Clean, Convert and Decode to make a best effort attempt to parse the value into the expected type. This function should not be used in performance critical code paths. +Use the Parse function to parse a value. This function calls the `Clone` `Clean`, `Default`, `Convert`, `Assert` and `Decode` Value functions in this exact order to process a value. ```typescript -const T = Type.Object({ x: Type.Number({ default: 0 }), y: Type.Number({ default: 0 }) }) +const R = Value.Parse(Type.String(), 'hello') // const R: string = "hello" -// Default +const E = Value.Parse(Type.String(), undefined) // throws AssertError +``` -const A = Value.Parse(T, { }) // const A = { x: 0, y: 0 } +You can override the order in which functions are are run, or omit functions entirely in the following way. -// Convert +```typescript +// Runs no functions. -const B = Value.Parse(T, { x: '1', y: '2' }) // const B = { x: 1, y: 2 } +const R = Value.Parse([], Type.String(), 12345) -// Clean +// Runs the Assert() function. -const C = Value.Parse(T, { x: 1, y: 2, z: 3 }) // const C = { x: 1, y: 2 } +const E = Value.Parse(['Assert'], Type.String(), 12345) -// Assert +// Runs the Convert() function followed by the Assert() function. -const D = Value.Parse(T, undefined) // throws AssertError +const S = Value.Parse(['Convert', 'Assert'], Type.String(), 12345) ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 6e438702b..7450c8aab 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -97,6 +97,14 @@ export class TypeCheck { public Code(): string { return this.code } + /** Returns the schema type used to validate */ + public Schema(): T { + return this.schema + } + /** Returns reference types used to validate */ + public References(): TSchema[] { + return this.references + } /** Returns an iterator for each error in this value. */ public Errors(value: unknown): ValueErrorIterator { return Errors(this.schema, this.references, value) diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts index 13477a4c9..be07f5b81 100644 --- a/src/value/parse/parse.ts +++ b/src/value/parse/parse.ts @@ -26,43 +26,103 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TransformDecode, HasTransform } from '../transform/index' +import { TypeBoxError } from '../../type/error/index' +import { TransformDecode, TransformEncode, HasTransform } from '../transform/index' import { TSchema } from '../../type/schema/index' import { StaticDecode } from '../../type/static/index' -import { Assert } from '../assert/assert' -import { Default } from '../default/default' -import { Convert } from '../convert/convert' -import { Clean } from '../clean/clean' +import { Assert } from '../assert/index' +import { Default } from '../default/index' +import { Convert } from '../convert/index' +import { Clean } from '../clean/index' import { Clone } from '../clone/index' // ------------------------------------------------------------------ -// ParseReducer +// Guards // ------------------------------------------------------------------ -type ReducerFunction = (schema: TSchema, references: TSchema[], value: unknown) => unknown +import { IsArray, IsUndefined } from '../guard/index' + +// ------------------------------------------------------------------ +// Error +// ------------------------------------------------------------------ +export class ParseError extends TypeBoxError { + constructor(message: string) { + super(message) + } +} + +// ------------------------------------------------------------------ +// ParseRegistry +// ------------------------------------------------------------------ +export type TParseOperation = 'Clone' | 'Clean' | 'Default' | 'Convert' | 'Assert' | 'Decode' | ({} & string) +export type TParseFunction = (type: TSchema, references: TSchema[], value: unknown) => unknown // prettier-ignore -const ParseReducer: ReducerFunction[] = [ - (_schema, _references, value) => Clone(value), - (schema, references, value) => Default(schema, references, value), - (schema, references, value) => Clean(schema, references, value), - (schema, references, value) => Convert(schema, references, value), - (schema, references, value) => { Assert(schema, references, value); return value }, - (schema, references, value) => (HasTransform(schema, references) ? TransformDecode(schema, references, value) : value), -] +export namespace ParseRegistry { + const registry = new Map([ + ['Clone', (_type, _references, value: unknown) => Clone(value)], + ['Clean', (type, references, value: unknown) => Clean(type, references, value)], + ['Default', (type, references, value: unknown) => Default(type, references, value)], + ['Convert', (type, references, value: unknown) => Convert(type, references, value)], + ['Assert', (type, references, value: unknown) => { Assert(type, references, value); return value }], + ['Decode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformDecode(type, references, value) : value)], + ['Encode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformEncode(type, references, value) : value)], + ]) + // Deletes an entry from the registry + export function Delete(key: string): void { + registry.delete(key) + } + // Sets an entry in the registry + export function Set(key: string, callback: TParseFunction): void { + registry.set(key, callback) + } + // Gets an entry in the registry + export function Get(key: string): TParseFunction | undefined { + return registry.get(key) + } +} +// ------------------------------------------------------------------ +// Default Parse Sequence +// ------------------------------------------------------------------ +// prettier-ignore +export const ParseDefault = [ + 'Clone', + 'Clean', + 'Default', + 'Convert', + 'Assert', + 'Decode' +] as const + // ------------------------------------------------------------------ // ParseValue // ------------------------------------------------------------------ -function ParseValue>(schema: T, references: TSchema[], value: unknown): R { - return ParseReducer.reduce((value, reducer) => reducer(schema, references, value), value) as R +function ParseValue = StaticDecode>(operations: TParseOperation[], type: Type, references: TSchema[], value: unknown): Result { + return operations.reduce((value, operationKey) => { + const operation = ParseRegistry.Get(operationKey) + if (IsUndefined(operation)) throw new ParseError(`Unable to find Parse operation '${operationKey}'`) + return operation(type, references, value) + }, value) as Result } + // ------------------------------------------------------------------ // Parse // ------------------------------------------------------------------ -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, references: TSchema[], value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ -export function Parse>(schema: T, value: unknown): R -/** Parses a value or throws an `AssertError` if invalid. */ +/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */ +export function Parse, Result extends Output = Output>(schema: Type, references: TSchema[], value: unknown): Result +/** Parses a value using the default parse pipeline. Will throws an `AssertError` if invalid. */ +export function Parse, Result extends Output = Output>(schema: Type, value: unknown): Result +/** Parses a value using the specified operations. */ +export function Parse(operations: TParseOperation[], schema: Type, references: TSchema[], value: unknown): unknown +/** Parses a value using the specified operations. */ +export function Parse(operations: TParseOperation[], schema: Type, value: unknown): unknown +/** Parses a value */ export function Parse(...args: any[]): unknown { - return args.length === 3 ? ParseValue(args[0], args[1], args[2]) : ParseValue(args[0], [], args[1]) + // prettier-ignore + const [operations, schema, references, value] = ( + args.length === 4 ? [args[0], args[1], args[2], args[3]] : + args.length === 3 ? IsArray(args[0]) ? [args[0], args[1], [], args[2]] : [ParseDefault, args[0], args[1], args[2]] : + args.length === 2 ? [ParseDefault, args[0], [], args[1]] : + (() => { throw new ParseError('Invalid Arguments') })() + ) + return ParseValue(operations, schema, references, value) } diff --git a/test/runtime/compiler/__members.ts b/test/runtime/compiler/__members.ts new file mode 100644 index 000000000..6ade7f40b --- /dev/null +++ b/test/runtime/compiler/__members.ts @@ -0,0 +1,20 @@ +import { TypeCompiler } from '@sinclair/typebox/compiler' +import { Type, TypeGuard, ValueGuard } from '@sinclair/typebox' +import { Assert } from '../assert/index' + +describe('compiler/TypeCheckMembers', () => { + it('Should return Schema', () => { + const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()]) + Assert.IsTrue(TypeGuard.IsNumber(A.Schema())) + }) + it('Should return References', () => { + const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()]) + Assert.IsTrue(TypeGuard.IsNumber(A.Schema())) + Assert.IsTrue(TypeGuard.IsString(A.References()[0])) + Assert.IsTrue(TypeGuard.IsBoolean(A.References()[1])) + }) + it('Should return Code', () => { + const A = TypeCompiler.Compile(Type.Number(), [Type.String(), Type.Boolean()]) + Assert.IsTrue(ValueGuard.IsString(A.Code())) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index 21decd7fa..a39e51760 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,3 +1,4 @@ +import './__members' import './any' import './array' import './async-iterator' diff --git a/test/runtime/value/parse/parse.ts b/test/runtime/value/parse/parse.ts index 5c8be9dd7..2bf7106de 100644 --- a/test/runtime/value/parse/parse.ts +++ b/test/runtime/value/parse/parse.ts @@ -1,5 +1,5 @@ -import { Value, AssertError } from '@sinclair/typebox/value' -import { Type } from '@sinclair/typebox' +import { Value, AssertError, ParseRegistry } from '@sinclair/typebox/value' +import { Type, TypeGuard } from '@sinclair/typebox' import { Assert } from '../../assert/index' // prettier-ignore @@ -87,4 +87,39 @@ describe('value/Parse', () => { const X = Value.Parse(T, 'world') Assert.IsEqual(X, 'hello') }) + // ---------------------------------------------------------------- + // Operations + // ---------------------------------------------------------------- + it('Should run operations 1', () => { + const A = Type.Object({ x: Type.Number() }) + const I = { x: 1 } + const O = Value.Parse([], A, I) + Assert.IsTrue(I === O) + }) + it('Should run operations 2', () => { + const A = Type.Object({ x: Type.Number() }) + const I = { x: 1 } + const O = Value.Parse(['Clone'], A, I) + Assert.IsTrue(I !== O) + }) + it('Should run operations 3', () => { + ParseRegistry.Set('Intercept', ( schema, references, value) => { throw 1 }) + const A = Type.Object({ x: Type.Number() }) + Assert.Throws(() => Value.Parse(['Intercept'], A, null)) + ParseRegistry.Delete('Intercept') + const F = ParseRegistry.Get('Intercept') + Assert.IsEqual(F, undefined) + }) + it('Should run operations 4', () => { + ParseRegistry.Set('Intercept', ( schema, references, value) => { + Assert.IsEqual(value, 12345) + Assert.IsTrue(TypeGuard.IsNumber(schema)) + Assert.IsTrue(TypeGuard.IsString(references[0])) + }) + Value.Parse(['Intercept'], Type.Number(), [Type.String()], 12345) + ParseRegistry.Delete('Intercept') + }) + it('Should run operations 5', () => { + Assert.Throws(() => Value.Parse(['Intercept'], Type.String(), null)) + }) }) From 010d1818ba7c1ae7f3973ead35dfc6946eac6447 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 27 Nov 2024 02:40:09 +0900 Subject: [PATCH 319/369] Documentation --- readme.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index 6dc0ceb83..35d2c912e 100644 --- a/readme.md +++ b/readme.md @@ -1022,7 +1022,7 @@ if(TypeGuard.IsString(T)) { ## Syntax Types -Syntax Types is a feature that enables TypeBox to parse TypeScript type annotations directly into Json Schema. This feature works both at runtime and statically within the type system. Syntax Types use Json Schema as the Abstract Syntax Tree (AST) parse target for TypeScript types. Syntax Types are designed to offer a syntactical frontend to the standard Type Builder API. +TypeBox has support for parsing TypeScript type annotations directly into TypeBox types. This feature supports both runtime and static parsing, with TypeBox implementing TypeScript parsers within the TypeScript type system itself. Syntax Types use the TypeBox Json Schema representations as an AST target for TypeScript types, providing a direct mapping between TypeScript syntax and Json Schema. Syntax Types are offered as a syntactical frontend to the Standard Type Builder API. This feature is available via optional import. @@ -1051,7 +1051,7 @@ const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ // }> ``` -Syntax Types can be composed with Standard Types. +Syntax Types can compose with Standard Types created via the Type Builder API ```typescript const T = Type.Object({ // const T: TObject<{ @@ -1061,7 +1061,7 @@ const T = Type.Object({ // const T: TObject<{ }) // }> ``` -It is also possible to pass Standard Types to Syntax Types in the following way. +Standard Types can also be passed to and referenced within Syntax Types. ```typescript const X = Type.Number() @@ -1075,7 +1075,7 @@ const T = Parse({ X, Y, Z }, `{ }`) ``` -Syntax Types can also be used to parse Module types. +Syntax Types also support Module parsing. ```typescript const Foo = Parse(`module Foo { @@ -1127,7 +1127,7 @@ type T = StaticParseAsType<{}, '{ x: number }'> // type T = { ### Limitations -Syntax Types work by having TypeBox parse TypeScript syntax within the TypeScript type system. This approach can place some strain on the TypeScript compiler and language service, potentially affecting responsiveness. While TypeBox makes a best-effort attempt to optimize for Syntax Types, users should be mindful of the following structures: +TypeBox parses TypeScript types directly within the TypeScript type system. This process does come with an inference cost, which scales with the size and complexity of the types being parsed. Although TypeBox strives to optimize Syntax Types, users should be aware of the following structures: ```typescript // Excessively wide structures will result in instantiation limits exceeding @@ -1154,12 +1154,12 @@ const B = Parse(`{ }`) ``` -In cases where Syntax Types exceed TypeScript's instantiation limits, TypeBox offers a fallback ParseOnly function, which will only parse but not infer. This function can also be used to parse types where the syntax is statically unknown to TypeScript (for example, when loading types from disk). +If Syntax Types exceed TypeScript's instantiation limits, users are advised to fall back to the Standard Type Builder API. Alternatively, TypeBox offers a `ParseOnly` function that parses the TypeScript syntax at runtime without statically inferring the schema. ```typescript import { ParseOnly } from '@sinclair/typebox/syntax' -// Where A is TSchema | undefined +// const A: TSchema | undefined const A = ParseOnly(`{ x: { @@ -1172,7 +1172,7 @@ const A = ParseOnly(`{ }`) ``` -For more information on TypeBox's parsing infrastructure, refer to the [ParseBox](https://github.com/sinclairzx81/parsebox) project. +For more information on static parsing, refer to the [ParseBox](https://github.com/sinclairzx81/parsebox) project. @@ -1333,7 +1333,7 @@ const R = Value.Parse(Type.String(), 'hello') // const R: string = "hello" const E = Value.Parse(Type.String(), undefined) // throws AssertError ``` -You can override the order in which functions are are run, or omit functions entirely in the following way. +You can override the order in which functions are run, or omit functions entirely using the following. ```typescript // Runs no functions. From 39765414b4316a1e9630f238173d5e36b865d843 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Wed, 4 Dec 2024 17:18:00 +0900 Subject: [PATCH 320/369] Revision 0.34.10 (#1107) * Add Remote Build Task * Use Overloads for Index Signature * Tidy Record Module Inference * Add Encode to Parse Operation Type * Fix Record Key mapping when using Import * Version * ChangeLog --- changelog/0.34.0.md | 5 + hammer.mjs | 25 ++ package-lock.json | 4 +- package.json | 3 +- src/type/indexed/indexed-from-mapped-key.ts | 11 +- .../indexed/indexed-from-mapped-result.ts | 3 +- src/type/indexed/indexed-property-keys.ts | 26 +-- src/type/indexed/indexed.ts | 218 ++++++++---------- src/type/module/compute.ts | 18 +- src/type/module/infer.ts | 2 +- src/type/record/record.ts | 6 +- src/type/type/json.ts | 16 +- src/value/parse/parse.ts | 2 +- 13 files changed, 176 insertions(+), 163 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 7e5e46939..0b821e451 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,9 @@ ### 0.34.0 +- [Revision 0.34.10](https://github.com/sinclairzx81/typebox/pull/1107) + - Fix Declaration Emit for Index and Mapped Types + - Fix Record Inference Presentation when Embedded in Modules + - Fix Record Mapping when using TImport as Key + - Add Encode to Parse Operation List - [Revision 0.34.9](https://github.com/sinclairzx81/typebox/pull/1101) - User Defined Parse Pipelines - Access to Schema and References on TypeCheck diff --git a/hammer.mjs b/hammer.mjs index 02dacedfc..26f758006 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -9,12 +9,14 @@ export async function clean() { await folder('node_modules/typebox').delete() await folder('target').delete() } + // ------------------------------------------------------------------------------- // Format // ------------------------------------------------------------------------------- export async function format() { await shell('prettier --no-semi --single-quote --print-width 240 --trailing-comma all --write src test task example/index.ts') } + // ------------------------------------------------------------------------------- // Start // ------------------------------------------------------------------------------- @@ -28,6 +30,7 @@ export async function benchmark() { await Benchmark.compression() await Benchmark.measurement() } + // ------------------------------------------------------------------------------- // Test // ------------------------------------------------------------------------------- @@ -49,6 +52,7 @@ export async function test(filter = '') { await test_static() await test_runtime(filter) } + // ------------------------------------------------------------------------------- // Build // ------------------------------------------------------------------------------- @@ -69,6 +73,25 @@ export async function build(target = 'target/build') { await shell(`cd ${target} && npm pack`) await build_check(target) } + +// ------------------------------------------------------------------------------- +// Build To +// ------------------------------------------------------------------------------- +export async function build_to(remote = 'target/remote', target = 'target/build') { + await clean() + await Promise.all([ + Build.Package.build(target), + Build.Esm.build(target), + Build.Cjs.build(target), + ]) + await folder(target).add('readme.md') + await folder(target).add('license') + await shell(`cd ${target} && npm pack`) + const { version } = JSON.parse(Fs.readFileSync('package.json', 'utf8')) + const filename = `${target}/sinclair-typebox-${version}.tgz` + await folder(remote).add(filename) +} + // ------------------------------------------------------------------------------- // Install // ------------------------------------------------------------------------------- @@ -77,6 +100,7 @@ export async function install_local() { await build('target/typebox') await folder('node_modules').add('target/typebox') } + // ------------------------------------------------------------- // Publish // ------------------------------------------------------------- @@ -87,6 +111,7 @@ export async function publish(otp, target = 'target/build') { await shell(`git tag ${version}`) await shell(`git push origin ${version}`) } + // ------------------------------------------------------------- // Publish-Dev // ------------------------------------------------------------- diff --git a/package-lock.json b/package-lock.json index a962edf2f..acfa45500 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.9", + "version": "0.34.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.9", + "version": "0.34.10", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 279b3dd59..8b857c362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.9", + "version": "0.34.10", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -20,6 +20,7 @@ "test:runtime": "hammer task test_runtime", "install:local": "hammer task install_local", "benchmark": "hammer task benchmark", + "build:to": "hammer task build_to", "build": "hammer task build", "test": "hammer task test", "clean": "hammer task clean", diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index b66cf5113..a523032f8 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -41,8 +41,7 @@ type TMappedIndexPropertyKey = { [_ in Key]: TIndex } // prettier-ignore -function MappedIndexPropertyKey(type: Type, key: Key, options?: SchemaOptions): TMappedIndexPropertyKey { +function MappedIndexPropertyKey(type: Type, key: Key, options?: SchemaOptions): TMappedIndexPropertyKey { return { [key]: Index(type, [key], Clone(options)) } as never } // ------------------------------------------------------------------ @@ -55,7 +54,10 @@ type TMappedIndexPropertyKeys(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedIndexPropertyKeys { +function MappedIndexPropertyKeys< + Type extends TSchema, + PropertyKeys extends PropertyKey[] +>(type: Type, propertyKeys: [...PropertyKeys], options?: SchemaOptions): TMappedIndexPropertyKeys { return propertyKeys.reduce((result, left) => { return { ...result, ...MappedIndexPropertyKey(type, left, options) } }, {} as TProperties) as never @@ -68,7 +70,8 @@ type TMappedIndexProperties TMappedIndexPropertyKeys > // prettier-ignore -function MappedIndexProperties(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedIndexProperties { +function MappedIndexProperties(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TMappedIndexProperties { return MappedIndexPropertyKeys(type, mappedKey.keys, options) as never } // ------------------------------------------------------------------ diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index e2b2b007c..200493649 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -43,8 +43,7 @@ type TFromProperties = ( function FromProperties(type: Type, properties: Properties, options?: SchemaOptions): TFromProperties { const result = {} as Record for(const K2 of Object.getOwnPropertyNames(properties)) { - const keys = IndexPropertyKeys(properties[K2]) - result[K2] = Index(type, keys, options) as never + result[K2] = Index(type, IndexPropertyKeys(properties[K2]), options) } return result as never } diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts index ad73541e4..d5f2e7315 100644 --- a/src/type/indexed/indexed-property-keys.ts +++ b/src/type/indexed/indexed-property-keys.ts @@ -41,13 +41,11 @@ import { IsTemplateLiteral, IsUnion, IsLiteral, IsNumber, IsInteger } from '../g // FromTemplateLiteral // ------------------------------------------------------------------ // prettier-ignore -type TFromTemplateLiteral -> = Result +type TFromTemplateLiteral> = (Keys) // prettier-ignore function FromTemplateLiteral(templateLiteral: TemplateLiteral): TFromTemplateLiteral { - const result = TemplateLiteralGenerate(templateLiteral) as string[] - return result.map(S => S.toString()) as never + const keys = TemplateLiteralGenerate(templateLiteral) as string[] + return keys.map(key => key.toString()) as never } // ------------------------------------------------------------------ // FromUnion @@ -59,9 +57,9 @@ type TFromUnion = ( : Result ) // prettier-ignore -function FromUnion(type: Type): TFromUnion { +function FromUnion(types: Types): TFromUnion { const result = [] as string[] - for(const left of type) result.push(...IndexPropertyKeys(left)) + for(const type of types) result.push(...IndexPropertyKeys(type)) return result as never } // ------------------------------------------------------------------ @@ -74,23 +72,23 @@ type TFromLiteral = ( : [] ) // prettier-ignore -function FromLiteral(T: LiteralValue): TFromLiteral { +function FromLiteral(literalValue: LiteralValue): TFromLiteral { return ( - [(T as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString() + [(literalValue as string).toString()] // TS 5.4 observes TLiteralValue as not having a toString() ) as never } // ------------------------------------------------------------------ -// IndexedKeyResolve +// IndexPropertyKeys // ------------------------------------------------------------------ // prettier-ignore -export type TIndexPropertyKeys : +export type TIndexPropertyKeys = ( + Type extends TTemplateLiteral ? TFromTemplateLiteral : Type extends TUnion ? TFromUnion : - Type extends TLiteral ? TFromLiteral : + Type extends TLiteral ? TFromLiteral : Type extends TNumber ? ['[number]'] : Type extends TInteger ? ['[number]'] : [] -)> = Result +) /** Returns a tuple of PropertyKeys derived from the given TSchema */ // prettier-ignore export function IndexPropertyKeys(type: Type): TIndexPropertyKeys { diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index 5598b034c..541c6e734 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -27,70 +27,45 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' +import { TypeBoxError } from '../error/index' + import { type TSchema, SchemaOptions } from '../schema/index' -import { Computed, type TComputed } from '../computed/index' -import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { type Assert } from '../helpers/index' +import { type TComputed, Computed } from '../computed/index' +import { type TNever, Never } from '../never/index' +import { type TArray } from '../array/index' +import { type TIntersect } from '../intersect/index' +import { type TMappedResult, type TMappedKey } from '../mapped/index' import { type TObject, type TProperties } from '../object/index' -import { type Ensure, type Assert } from '../helpers/index' -import { Never, type TNever } from '../never/index' +import { type TUnion } from '../union/index' import { type TRecursive } from '../recursive/index' -import { type TIntersect } from '../intersect/index' -import { TMappedResult, type TMappedKey } from '../mapped/index' -import { Union, type TUnion } from '../union/index' +import { type TRef } from '../ref/index' import { type TTuple } from '../tuple/index' -import { type TArray } from '../array/index' -import { Ref, type TRef } from '../ref/index' + import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index' import { UnionEvaluated, type TUnionEvaluated } from '../union/index' -// ------------------------------------------------------------------ -// Infrastructure -// ------------------------------------------------------------------ import { IndexPropertyKeys, type TIndexPropertyKeys } from './indexed-property-keys' import { IndexFromMappedKey, type TIndexFromMappedKey } from './indexed-from-mapped-key' import { IndexFromMappedResult, type TIndexFromMappedResult } from './indexed-from-mapped-result' // ------------------------------------------------------------------ -// KindGuard +// TypeGuard // ------------------------------------------------------------------ -import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion, IsLiteralValue, IsRef, IsComputed } from '../guard/kind' -import { IsArray as IsArrayValue } from '../guard/value' - -// ------------------------------------------------------------------ -// FromComputed -// ------------------------------------------------------------------ -// prettier-ignore -// type TFromComputed = Ensure< -// TComputed<'Partial', [TComputed]> -// > -// // prettier-ignore -// function FromComputed(target: Target, parameters: Parameters): TFromComputed { -// return Computed('Partial', [Computed(target, parameters)]) as never -// } -// // ------------------------------------------------------------------ -// // FromRef -// // ------------------------------------------------------------------ -// // prettier-ignore -// type TFromRef = Ensure< -// TComputed<'Partial', [TRef]> -// > -// // prettier-ignore -// function FromRef($ref: Ref): TFromRef { -// return Computed('Partial', [Ref($ref)]) as never -// } +import { IsArray, IsIntersect, IsObject, IsMappedKey, IsMappedResult, IsNever, IsSchema, IsTuple, IsUnion, IsRef } from '../guard/kind' // ------------------------------------------------------------------ // FromRest // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TFromRest, TSchema>]> +type TFromRest = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromRest, TSchema>]> : Result ) // prettier-ignore -function FromRest(types: [...Types], key: K): TFromRest { - return types.map(left => IndexFromPropertyKey(left, key)) as never +function FromRest(types: [...Types], key: Key): TFromRest { + return types.map(type => IndexFromPropertyKey(type, key)) as never } // ------------------------------------------------------------------ // FromIntersectRest @@ -105,7 +80,7 @@ type TFromIntersectRest ) // prettier-ignore function FromIntersectRest(types: [...Types]): TFromIntersectRest { - return types.filter(left => !IsNever(left)) as never + return types.filter(type => !IsNever(type)) as never } // prettier-ignore type TFromIntersect = ( @@ -171,21 +146,17 @@ function FromUnion(types: [... // ------------------------------------------------------------------ // FromTuple // ------------------------------------------------------------------ - // prettier-ignore -type TFromTuple : - Key extends keyof Types - ? Types[Key] extends infer Type extends TSchema - ? Type - : TNever - : TNever -)> = Result +type TFromTuple = ( + Key extends keyof Types ? Types[Key] : + Key extends '[number]' ? TUnionEvaluated : + TNever +) // prettier-ignore function FromTuple(types: [...Types], key: Key): TFromTuple { return ( - key === '[number]' ? UnionEvaluated(types) : key in types ? types[key as number] : + key === '[number]' ? UnionEvaluated(types) : Never() ) as never } @@ -194,20 +165,25 @@ function FromTuple(types: [... // ------------------------------------------------------------------ // prettier-ignore type TFromArray = ( - Key extends '[number]' ? Type : TNever + Key extends '[number]' + ? Type + : TNever ) // prettier-ignore -function FromArray(type: Type, key: Key): TFromArray { - // ... ? - return (key === '[number]' ? type : Never()) as never +function FromArray(type: Type, key: Key): TFromArray { + return ( + key === '[number]' + ? type + : Never() + ) as never } // ------------------------------------------------------------------ // FromProperty // ------------------------------------------------------------------ -type AssertPropertyKey = Assert +type AssertPropertyKey = Assert // prettier-ignore -type TFromProperty = ( // evaluate for string keys Key extends keyof Properties ? Properties[Key] @@ -215,32 +191,32 @@ type TFromProperty}` extends `${AssertPropertyKey}` ? Properties[AssertPropertyKey] : TNever -)> = Result +) // prettier-ignore -function FromProperty(properties: Properties, key: Key): TFromProperty { - return (key in properties ? properties[key as string] : Never()) as never +function FromProperty(properties: Properties, propertyKey: Key): TFromProperty { + return (propertyKey in properties ? properties[propertyKey as string] : Never()) as never } // ------------------------------------------------------------------ // FromKey // ------------------------------------------------------------------ // prettier-ignore export type TIndexFromPropertyKey = ( - Type extends TRecursive ? TIndexFromPropertyKey : - Type extends TIntersect ? TFromIntersect : - Type extends TUnion ? TFromUnion : - Type extends TTuple ? TFromTuple : - Type extends TArray ? TFromArray : - Type extends TObject ? TFromProperty : + Type extends TRecursive ? TIndexFromPropertyKey : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TTuple ? TFromTuple : + Type extends TArray ? TFromArray : + Type extends TObject ? TFromProperty : TNever ) // prettier-ignore -export function IndexFromPropertyKey(type: Type, key: Key): TIndexFromPropertyKey { +export function IndexFromPropertyKey(type: Type, propertyKey: Key): TIndexFromPropertyKey { return ( - IsIntersect(type) ? FromIntersect(type.allOf, key) : - IsUnion(type) ? FromUnion(type.anyOf, key) : - IsTuple(type) ? FromTuple(type.items ?? [], key) : - IsArray(type) ? FromArray(type.items, key) : - IsObject(type) ? FromProperty(type.properties, key) : + IsIntersect(type) ? FromIntersect(type.allOf, propertyKey) : + IsUnion(type) ? FromUnion(type.anyOf, propertyKey) : + IsTuple(type) ? FromTuple(type.items ?? [], propertyKey) : + IsArray(type) ? FromArray(type.items, propertyKey) : + IsObject(type) ? FromProperty(type.properties, propertyKey) : Never() ) as never } @@ -255,76 +231,70 @@ export type TIndexFromPropertyKeys(type: Type, propertyKeys: [...PropertyKeys]): TIndexFromPropertyKeys { - return propertyKeys.map(left => IndexFromPropertyKey(type, left)) as never + return propertyKeys.map(propertyKey => IndexFromPropertyKey(type, propertyKey)) as never } // ------------------------------------------------------------------ // FromSchema // ------------------------------------------------------------------ // prettier-ignore -type TFromType, -> = TUnionEvaluated +type FromSchema = ( + TUnionEvaluated> +) // prettier-ignore -function FromType(type: Type, propertyKeys: [...PropertyKeys]): TFromType { - const result = IndexFromPropertyKeys(type, propertyKeys as PropertyKey[]) - return UnionEvaluated(result) as never +function FromSchema(type: Type, propertyKeys: [...PropertyKeys]): FromSchema { + return ( + UnionEvaluated(IndexFromPropertyKeys(type, propertyKeys as PropertyKey[])) + ) as never } // ------------------------------------------------------------------ -// UnionFromPropertyKeys +// FromSchema // ------------------------------------------------------------------ // prettier-ignore -type TUnionFromPropertyKeys = ( - PropertyKeys extends [infer Key extends PropertyKey, ...infer Rest extends PropertyKey[]] - ? Key extends TLiteralValue - ? TUnionFromPropertyKeys]> - : TUnionFromPropertyKeys - : TUnionEvaluated +export type TIndexFromComputed = ( + TComputed<'Index', [Type, Key]> ) // prettier-ignore -function UnionFromPropertyKeys(propertyKeys: PropertyKeys): TUnionFromPropertyKeys { - const result = propertyKeys.reduce((result, key) => IsLiteralValue(key) ? [...result, Literal(key)]: result, [] as TLiteral[]) - return UnionEvaluated(result) as never +export function IndexFromComputed(type: Type, key: Key): TIndexFromComputed { + return Computed('Index', [type, key]) } // ------------------------------------------------------------------ // TIndex // ------------------------------------------------------------------ -// prettier-ignore (do not export this type) -type TResolvePropertyKeys = Key extends TSchema ? TIndexPropertyKeys : Key -// prettier-ignore (do not export this type) -type TResolveTypeKey = Key extends PropertyKey[] ? TUnionFromPropertyKeys : Key // prettier-ignore -export type TIndex = ( - Key extends TMappedResult ? TIndexFromMappedResult : - Key extends TMappedKey ? TIndexFromMappedKey : - [IsTypeRef, IsKeyRef] extends [true, true] ? TComputed<'Index', [Type, TResolveTypeKey]> : - [IsTypeRef, IsKeyRef] extends [false, true] ? TComputed<'Index', [Type, TResolveTypeKey]> : - [IsTypeRef, IsKeyRef] extends [true, false] ? TComputed<'Index', [Type, TResolveTypeKey]> : - TFromType> +export type TIndex = ( + FromSchema ) /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(type: Type, key: readonly [...Key], options?: SchemaOptions): TIndex +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex +export function Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ -export function Index(type: Type, key: Key, options?: SchemaOptions): TIndex +export function Index(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TIndexFromMappedResult /** `[Json]` Returns an Indexed property type for the given keys */ -// prettier-ignore -export function Index(type: any, key: any, options?: SchemaOptions): any { - const typeKey: TSchema = IsArrayValue(key) ? UnionFromPropertyKeys(key as PropertyKey[]) : key - const propertyKeys: PropertyKey[] = IsSchema(key) ? IndexPropertyKeys(key) : key - const isTypeRef: boolean = IsRef(type) - const isKeyRef: boolean = IsRef(key) - return ( - IsMappedResult(key) ? IndexFromMappedResult(type, key, options) : - IsMappedKey(key) ? IndexFromMappedKey(type, key, options) : - (isTypeRef && isKeyRef) ? Computed('Index', [type, typeKey], options) : - (!isTypeRef && isKeyRef) ? Computed('Index', [type, typeKey], options) : - (isTypeRef && !isKeyRef) ? Computed('Index', [type, typeKey], options) : - CreateType(FromType(type, propertyKeys), options) - ) as never +export function Index(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TIndexFromMappedResult +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TIndexFromMappedKey +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index>(T: Type, K: Key, options?: SchemaOptions): TIndex +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(type: Type, propertyKeys: readonly [...PropertyKeys], options?: SchemaOptions): TIndex +/** `[Json]` Returns an Indexed property type for the given keys */ +export function Index(type: TSchema, key: any, options?: SchemaOptions): any { + // computed-type + if (IsRef(type) || IsRef(key)) { + const error = `Index types using Ref parameters require both Type and Key to be of TSchema` + if (!IsSchema(type) || !IsSchema(key)) throw new TypeBoxError(error) + return Computed('Index', [type, key]) + } + // mapped-types + if (IsMappedResult(key)) return IndexFromMappedResult(type, key, options) + if (IsMappedKey(key)) return IndexFromMappedKey(type, key, options) + // prettier-ignore + return CreateType( + IsSchema(key) + ? FromSchema(type, IndexPropertyKeys(key)) + : FromSchema(type, key as string[]) + , options) as never } diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index b2ee6e785..8527ab135 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -35,8 +35,8 @@ import { Awaited, type TAwaited } from '../awaited/index' import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' import { TComputed } from '../computed/index' import { Constructor, type TConstructor } from '../constructor/index' -import { Index, type TIndex } from '../indexed/index' -import { TEnum, TEnumRecord } from '../enum/index' +import { Index, type TIndex, type TIndexPropertyKeys } from '../indexed/index' +import { TEnum, type TEnumRecord } from '../enum/index' import { Function as FunctionType, type TFunction } from '../function/index' import { Intersect, type TIntersect, type TIntersectEvaluated } from '../intersect/index' import { Iterator, type TIterator } from '../iterator/index' @@ -119,7 +119,14 @@ function FromAwaited(parameters: Parameters): TFro // ------------------------------------------------------------------ // prettier-ignore type TFromIndex = ( - Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TIndex : never + Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] + // Note: This inferred result check is required to mitgate a "as never" + // assertion on FromComputed resolution. This should be removed when + // reimplementing TIndex to use TSchema as the primary key indexer. + ? TIndex> extends infer Result extends TSchema + ? Result + : never + : never ) // prettier-ignore function FromIndex(parameters: Parameters): TFromIndex { @@ -370,10 +377,7 @@ export function FromType extends infer Key extends PropertyKey ? Key : never, InferedType extends unknown = TInfer, -> = Record +> = Ensure<{ [_ in InferredKey]: InferedType }> // ------------------------------------------------------------------ // Ref // ------------------------------------------------------------------ diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 782269644..8e7da8257 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -204,7 +204,9 @@ function FromNumberKey(_: Key, type: // ------------------------------------------------------------------ // prettier-ignore type RecordStatic = ( - Evaluate<{ [_ in Assert, PropertyKey>]: Static; }> + // Note: We would return a Record here, but recursive Record types will + // break when T is self recursive. We can mitigate this by using a Mapped instead. + Evaluate<{ [_ in Assert, PropertyKey>]: Static }> ) // prettier-ignore export interface TRecord extends TSchema { @@ -232,7 +234,7 @@ export type TRecordOrObject = ( Key extends TString ? TFromStringKey : Key extends TAny ? TFromAnyKey : Key extends TNever ? TFromNeverKey : - Key + TNever ) // ------------------------------------------------------------------ // TRecordOrObject diff --git a/src/type/type/json.ts b/src/type/type/json.ts index f7efb99c4..8cbb88ef2 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -35,7 +35,7 @@ import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index' import { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from '../exclude/index' import { Extends, type TExtends, type TExtendsFromMappedKey, type TExtendsFromMappedResult } from '../extends/index' import { Extract, type TExtract, type TExtractFromMappedResult, type TExtractFromTemplateLiteral } from '../extract/index' -import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult } from '../indexed/index' +import { Index, TIndex, type TIndexPropertyKeys, type TIndexFromMappedKey, type TIndexFromMappedResult, type TIndexFromComputed } from '../indexed/index' import { Integer, type IntegerOptions, type TInteger } from '../integer/index' import { Intersect, type IntersectOptions } from '../intersect/index' import { Capitalize, Uncapitalize, Lowercase, Uppercase, type TCapitalize, type TUncapitalize, type TLowercase, type TUppercase } from '../intrinsic/index' @@ -164,13 +164,19 @@ export class JsonTypeBuilder { return Extract(type, union, options) } /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(type: Type, key: readonly [...PropertyKeys], options?: SchemaOptions): TIndex + public Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(type: Type, key: Key, options?: SchemaOptions): TIndex + public Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(type: Type, key: Key, options?: SchemaOptions): TIndex + public Index(type: Type, key: Key, options?: SchemaOptions): TIndexFromComputed /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(type: Type, key: Key, options?: SchemaOptions): TIndex + public Index(type: Type, mappedResult: MappedResult, options?: SchemaOptions): TIndexFromMappedResult + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(type: Type, mappedKey: MappedKey, options?: SchemaOptions): TIndexFromMappedKey + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index>(T: Type, K: Key, options?: SchemaOptions): TIndex + /** `[Json]` Returns an Indexed property type for the given keys */ + public Index(type: Type, propertyKeys: readonly [...PropertyKeys], options?: SchemaOptions): TIndex /** `[Json]` Returns an Indexed property type for the given keys */ public Index(type: TSchema, key: any, options?: SchemaOptions): any { return Index(type, key, options) diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts index be07f5b81..e28d53de8 100644 --- a/src/value/parse/parse.ts +++ b/src/value/parse/parse.ts @@ -53,7 +53,7 @@ export class ParseError extends TypeBoxError { // ------------------------------------------------------------------ // ParseRegistry // ------------------------------------------------------------------ -export type TParseOperation = 'Clone' | 'Clean' | 'Default' | 'Convert' | 'Assert' | 'Decode' | ({} & string) +export type TParseOperation = 'Clone' | 'Clean' | 'Default' | 'Convert' | 'Assert' | 'Decode' | 'Encode' | ({} & string) export type TParseFunction = (type: TSchema, references: TSchema[], value: unknown) => unknown // prettier-ignore From 88a48198c56021ffd8f74cb655f1220a3e68fba3 Mon Sep 17 00:00:00 2001 From: Haydn Paterson Date: Thu, 5 Dec 2024 14:24:44 +0900 Subject: [PATCH 321/369] Revision 0.34.11 (#1110) * Fix Compile For Deep Referential Module Types * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 21 ++++++++--------- test/runtime/compiler-ajv/module.ts | 35 +++++++++++++++++++++++++++++ test/runtime/compiler/module.ts | 35 +++++++++++++++++++++++++++++ test/runtime/value/check/module.ts | 35 +++++++++++++++++++++++++++++ 7 files changed, 121 insertions(+), 13 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 0b821e451..b7774bfec 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.11](https://github.com/sinclairzx81/typebox/pull/1110) + - Fix Compiler Emit for Deeply Referential Module Types - [Revision 0.34.10](https://github.com/sinclairzx81/typebox/pull/1107) - Fix Declaration Emit for Index and Mapped Types - Fix Record Inference Presentation when Embedded in Modules diff --git a/package-lock.json b/package-lock.json index acfa45500..37145ec05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.10", + "version": "0.34.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.10", + "version": "0.34.11", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 8b857c362..2734e73ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.10", + "version": "0.34.11", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 7450c8aab..5edf8fd7b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -59,7 +59,7 @@ import type { TNumber } from '../type/number/index' import type { TObject } from '../type/object/index' import type { TPromise } from '../type/promise/index' import type { TRecord } from '../type/record/index' -import type { TRef } from '../type/ref/index' +import { Ref, type TRef } from '../type/ref/index' import type { TRegExp } from '../type/regexp/index' import type { TTemplateLiteral } from '../type/template-literal/index' import type { TThis } from '../type/recursive/index' @@ -298,9 +298,10 @@ export namespace TypeCompiler { yield `(typeof ${value} === 'function')` } function* FromImport(schema: TImport, references: TSchema[], value: string): IterableIterator { - const definitions = globalThis.Object.values(schema.$defs) as TSchema[] - const target = schema.$defs[schema.$ref] as TSchema - yield* Visit(target, [...references, ...definitions], value) + const members = globalThis.Object.getOwnPropertyNames(schema.$defs).reduce((result, key) => { + return [...result, schema.$defs[key as never] as TSchema] + }, [] as TSchema[]) + yield* Visit(Ref(schema.$ref), [...references, ...members], value) } function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { yield `Number.isInteger(${value})` @@ -399,12 +400,8 @@ export namespace TypeCompiler { function* FromRef(schema: TRef, references: TSchema[], value: string): IterableIterator { const target = Deref(schema, references) // Reference: If we have seen this reference before we can just yield and return the function call. - // If this isn't the case we defer to visit to generate and set the _recursion_end_for_ for subsequent - // passes. This operation is very awkward as we are using the functions state to store values to - // enable self referential types to terminate. This needs to be refactored. - const recursiveEnd = `_recursion_end_for_${schema.$ref}` - if (state.functions.has(recursiveEnd)) return yield `${CreateFunctionName(schema.$ref)}(${value})` - state.functions.set(recursiveEnd, '') // terminate recursion here by setting the name. + // If this isn't the case we defer to visit to generate and set the function for subsequent passes. + if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})` yield* Visit(target, references, value) } function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator { @@ -481,6 +478,10 @@ export namespace TypeCompiler { if (state.functions.has(functionName)) { return yield `${functionName}(${value})` } else { + // Note: In the case of cyclic types, we need to create a 'functions' record + // to prevent infinitely re-visiting the CreateFunction. Subsequent attempts + // to visit will be caught by the above condition. + state.functions.set(functionName, '') const functionCode = CreateFunction(functionName, schema, references, 'value', false) state.functions.set(functionName, functionCode) return yield `${functionName}(${value})` diff --git a/test/runtime/compiler-ajv/module.ts b/test/runtime/compiler-ajv/module.ts index 756c09f36..0797dd451 100644 --- a/test/runtime/compiler-ajv/module.ts +++ b/test/runtime/compiler-ajv/module.ts @@ -106,4 +106,39 @@ describe('compiler-ajv/Module', () => { Ok(T, { y: [null], w: [null] }) Fail(T, { x: [1], y: [null], w: [null] }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1109 + // ---------------------------------------------------------------- + it('Should validate deep referential 1', () => { + const Module = Type.Module({ + A: Type.Union([Type.Literal('Foo'), Type.Literal('Bar')]), + B: Type.Ref('A'), + C: Type.Object({ ref: Type.Ref('B') }), + D: Type.Union([Type.Ref('B'), Type.Ref('C')]), + }) + Ok(Module.Import('A') as never, 'Foo') + Ok(Module.Import('A') as never, 'Bar') + Ok(Module.Import('B') as never, 'Foo') + Ok(Module.Import('B') as never, 'Bar') + Ok(Module.Import('C') as never, { ref: 'Foo' }) + Ok(Module.Import('C') as never, { ref: 'Bar' }) + Ok(Module.Import('D') as never, 'Foo') + Ok(Module.Import('D') as never, 'Bar') + Ok(Module.Import('D') as never, { ref: 'Foo' }) + Ok(Module.Import('D') as never, { ref: 'Bar' }) + }) + it('Should validate deep referential 2', () => { + const Module = Type.Module({ + A: Type.Literal('Foo'), + B: Type.Ref('A'), + C: Type.Ref('B'), + D: Type.Ref('C'), + E: Type.Ref('D'), + }) + Ok(Module.Import('A'), 'Foo') + Ok(Module.Import('B'), 'Foo') + Ok(Module.Import('C'), 'Foo') + Ok(Module.Import('D'), 'Foo') + Ok(Module.Import('E'), 'Foo') + }) }) diff --git a/test/runtime/compiler/module.ts b/test/runtime/compiler/module.ts index 32b455605..2121a9d5d 100644 --- a/test/runtime/compiler/module.ts +++ b/test/runtime/compiler/module.ts @@ -106,4 +106,39 @@ describe('compiler/Module', () => { Ok(T, { y: [null], w: [null] }) Fail(T, { x: [1], y: [null], w: [null] }) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1109 + // ---------------------------------------------------------------- + it('Should validate deep referential 1', () => { + const Module = Type.Module({ + A: Type.Union([Type.Literal('Foo'), Type.Literal('Bar')]), + B: Type.Ref('A'), + C: Type.Object({ ref: Type.Ref('B') }), + D: Type.Union([Type.Ref('B'), Type.Ref('C')]), + }) + Ok(Module.Import('A') as never, 'Foo') + Ok(Module.Import('A') as never, 'Bar') + Ok(Module.Import('B') as never, 'Foo') + Ok(Module.Import('B') as never, 'Bar') + Ok(Module.Import('C') as never, { ref: 'Foo' }) + Ok(Module.Import('C') as never, { ref: 'Bar' }) + Ok(Module.Import('D') as never, 'Foo') + Ok(Module.Import('D') as never, 'Bar') + Ok(Module.Import('D') as never, { ref: 'Foo' }) + Ok(Module.Import('D') as never, { ref: 'Bar' }) + }) + it('Should validate deep referential 2', () => { + const Module = Type.Module({ + A: Type.Literal('Foo'), + B: Type.Ref('A'), + C: Type.Ref('B'), + D: Type.Ref('C'), + E: Type.Ref('D'), + }) + Ok(Module.Import('A'), 'Foo') + Ok(Module.Import('B'), 'Foo') + Ok(Module.Import('C'), 'Foo') + Ok(Module.Import('D'), 'Foo') + Ok(Module.Import('E'), 'Foo') + }) }) diff --git a/test/runtime/value/check/module.ts b/test/runtime/value/check/module.ts index 371b302d0..af80b0e68 100644 --- a/test/runtime/value/check/module.ts +++ b/test/runtime/value/check/module.ts @@ -108,4 +108,39 @@ describe('value/check/Module', () => { Assert.IsTrue(Value.Check(T, { y: [null], w: [null] })) Assert.IsFalse(Value.Check(T, { x: [1], y: [null], w: [null] })) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1109 + // ---------------------------------------------------------------- + it('Should validate deep referential 1', () => { + const Module = Type.Module({ + A: Type.Union([Type.Literal('Foo'), Type.Literal('Bar')]), + B: Type.Ref('A'), + C: Type.Object({ ref: Type.Ref('B') }), + D: Type.Union([Type.Ref('B'), Type.Ref('C')]), + }) + Assert.IsTrue(Value.Check(Module.Import('A') as never, 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('A') as never, 'Bar')) + Assert.IsTrue(Value.Check(Module.Import('B') as never, 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('B') as never, 'Bar')) + Assert.IsTrue(Value.Check(Module.Import('C') as never, { ref: 'Foo' })) + Assert.IsTrue(Value.Check(Module.Import('C') as never, { ref: 'Bar' })) + Assert.IsTrue(Value.Check(Module.Import('D') as never, 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('D') as never, 'Bar')) + Assert.IsTrue(Value.Check(Module.Import('D') as never, { ref: 'Foo' })) + Assert.IsTrue(Value.Check(Module.Import('D') as never, { ref: 'Bar' })) + }) + it('Should validate deep referential 2', () => { + const Module = Type.Module({ + A: Type.Literal('Foo'), + B: Type.Ref('A'), + C: Type.Ref('B'), + D: Type.Ref('C'), + E: Type.Ref('D'), + }) + Assert.IsTrue(Value.Check(Module.Import('A'), 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('B'), 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('C'), 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('D'), 'Foo')) + Assert.IsTrue(Value.Check(Module.Import('E'), 'Foo')) + }) }) From dd3fc5ff3200dfd76dc3b97252cf766409294181 Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 11 Dec 2024 13:17:43 +0900 Subject: [PATCH 322/369] Prototype: Discriminated Union --- example/prototypes/discriminated-union.ts | 96 +++++++++++++++++++++++ example/prototypes/index.ts | 1 + 2 files changed, 97 insertions(+) create mode 100644 example/prototypes/discriminated-union.ts diff --git a/example/prototypes/discriminated-union.ts b/example/prototypes/discriminated-union.ts new file mode 100644 index 000000000..22da0f486 --- /dev/null +++ b/example/prototypes/discriminated-union.ts @@ -0,0 +1,96 @@ + +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Static, Kind, TSchema, TObject, SchemaOptions, CreateType, TLiteral, TypeRegistry, ValueGuard, KindGuard, TUnion } from '@sinclair/typebox' +import { GetErrorFunction, SetErrorFunction } from 'src/errors/function' +import { Value } from '@sinclair/typebox/value' + +// ------------------------------------------------------------------ +// DiscriminatedUnionError +// ------------------------------------------------------------------ +const errorFunction = GetErrorFunction() + +// prettier-ignore +SetErrorFunction((parameter) => { + if (parameter.schema[Kind] !== 'DiscriminatedUnion') { + return errorFunction(parameter) + } + const union = parameter.schema as TDiscriminatedUnion + // Try generate error when value matches known discriminator literal + if (ValueGuard.IsObject(parameter.value) && union.discriminator in parameter.value) { + const variant = parameter.schema.anyOf.find((variant: TSchema) => union.discriminator in variant.properties + && (variant.properties[union.discriminator] as TLiteral).const === + (parameter.value as Record)[union.discriminator]) + if (KindGuard.IsSchema(variant)) { + const literal = variant.properties[union.discriminator] + return `Invalid value for DiscriminatedUnion variant '${literal.const}'` + } + } + // Return generic error containing possible discriminator types. + const options = union.anyOf.map(object => object.properties[union.discriminator].const) as string[] + return `Expected value of ${options.map(option => `'${option}'`).join(', ')} for DiscriminatedUnion` +}) + +// ------------------------------------------------------------------ +// TDiscriminatedUnionObject +// +// Constructs a base TObject type requiring 1 discriminator property +// ------------------------------------------------------------------ +// prettier-ignore +type TDiscriminatedUnionProperties = { + [_ in Discriminator]: TLiteral +} +// prettier-ignore +type TDiscriminatedUnionObject = TObject> + +// ------------------------------------------------------------------ +// DiscriminatedUnion +// ------------------------------------------------------------------ +// prettier-ignore +TypeRegistry.Set('DiscriminatedUnion', (schema: TDiscriminatedUnion, value) => { + return schema.anyOf.some(variant => Value.Check(variant, [], value)) +}) +// prettier-ignore +export interface TDiscriminatedUnion extends TSchema { + [Kind]: 'DiscriminatedUnion' + static: Static> + discriminator: Discriminator + anyOf: Types +} + +/** Creates a DiscriminatedUnion. */ +// prettier-ignore +export function DiscriminatedUnion[]>( + discriminator: Discriminator, types: [...Types], options?: SchemaOptions +): TDiscriminatedUnion { + return CreateType({ [Kind]: 'DiscriminatedUnion', anyOf: types, discriminator }, options) as never +} + + + diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index 179da98fd..d5d9aeda4 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -26,6 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ +export * from './discriminated-union' export * from './from-schema' export * from './partial-deep' export * from './union-enum' From cd61fa92c03c60d9bf38e50951a63409625796fa Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 18 Dec 2024 19:20:39 +0900 Subject: [PATCH 323/369] Revision 0.34.12 (#1120) * Fix Mutate Object Check | Documentation * ChangeLog --- changelog/0.34.0.md | 3 ++ package-lock.json | 4 +-- package.json | 2 +- readme.md | 52 +++++++++++++++++++++-------- src/value/mutate/mutate.ts | 14 +++++--- test/runtime/value/mutate/mutate.ts | 28 ++++++++++++++++ 6 files changed, 83 insertions(+), 20 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index b7774bfec..4f7a9f483 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,7 @@ ### 0.34.0 +- [Revision 0.34.12](https://github.com/sinclairzx81/typebox/pull/1120) + - [1119](https://github.com/sinclairzx81/typebox/issues/1119) Fix for Mutate Object Comparison + - [1117](https://github.com/sinclairzx81/typebox/issues/1117) Re-Add Type.Recursive Documentation - [Revision 0.34.11](https://github.com/sinclairzx81/typebox/pull/1110) - Fix Compiler Emit for Deeply Referential Module Types - [Revision 0.34.10](https://github.com/sinclairzx81/typebox/pull/1107) diff --git a/package-lock.json b/package-lock.json index 37145ec05..42b48a004 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.11", + "version": "0.34.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.11", + "version": "0.34.12", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 2734e73ea..1bac1b697 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.11", + "version": "0.34.12", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 35d2c912e..1d44e671f 100644 --- a/readme.md +++ b/readme.md @@ -68,6 +68,7 @@ License MIT - [Options](#types-options) - [Properties](#types-properties) - [Generics](#types-generics) + - [Recursive](#types-recursive) - [Modules](#types-modules) - [Template Literal](#types-template-literal) - [Indexed](#types-indexed) @@ -759,21 +760,51 @@ const T = Nullable(Type.String()) // const T = { type T = Static // type T = string | null ``` - + -### Module Types +### Recursive Types -TypeBox Modules are containers for related types. They provide a referential namespace, enabling types to reference one another via string identifiers. Modules support both singular and mutually recursive referencing within the context of a module, as well as referential computed types (such as Partial + Ref). All Module types must be imported before use. TypeBox represents an imported type with the `$defs` Json Schema keyword. +Use the Recursive function to create a singular recursive type. -#### Usage +```typescript +const Node = Type.Recursive(Self => Type.Object({ // const Node = { + id: Type.String(), // $id: 'Node', + nodes: Type.Array(Self) // type: 'object', +}), { $id: 'Node' }) // properties: { + // id: { + // type: 'string' + // }, + // nodes: { + // type: 'array', + // items: { + // $ref: 'Node' + // } + // } + // }, + // required: [ + // 'id', + // 'nodes' + // ] + // } -The following creates a Module with User and PartialUser types. Note that the PartialUser type is specified as a Partial + Ref to the User type. It is not possible to perform a Partial operation directly on a reference, so TypeBox will return a TComputed type that defers the Partial operation until all types are resolved. The TComputed type is evaluated when calling Import on the Module. +type Node = Static // type Node = { + // id: string + // nodes: Node[] + // } -```typescript -// Module with PartialUser and User types +function test(node: Node) { + const id = node.nodes[0].nodes[0].id // id is string +} +``` -const Module = Type.Module({ + + +### Module Types +Module types are containers for a set of referential types. Modules act as namespaces, enabling types to reference one another via string identifiers. Modules support both singular and mutually recursive references, as well as deferred dereferencing for computed types such as Partial. Types imported from a module are expressed using the Json Schema `$defs` keyword. + +```typescript +const Module = Type.Module({ PartialUser: Type.Partial(Type.Ref('User')), // TComputed<'Partial', [TRef<'User'>]> User: Type.Object({ // TObject<{ @@ -781,11 +812,7 @@ const Module = Type.Module({ name: Type.String(), // name: TString, email: Type.String() // email: TString }), // }> - }) - -// Types must be imported before use. - const User = Module.Import('User') // const User: TImport<{...}, 'User'> type User = Static // type User = { @@ -803,7 +830,6 @@ type PartialUser = Static // type PartialUser = { // } ``` - ### Template Literal Types diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index ed35d9cfd..b5ad3472a 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -31,6 +31,12 @@ import { ValuePointer } from '../pointer/index' import { Clone } from '../clone/index' import { TypeBoxError } from '../../type/error/index' +// ------------------------------------------------------------------ +// IsStandardObject +// ------------------------------------------------------------------ +function IsStandardObject(value: unknown): value is Record { + return IsObject(value) && !IsArray(value) +} // ------------------------------------------------------------------ // Errors // ------------------------------------------------------------------ @@ -44,7 +50,7 @@ export class ValueMutateError extends TypeBoxError { // ------------------------------------------------------------------ export type Mutable = { [key: string]: unknown } | unknown[] function ObjectType(root: Mutable, path: string, current: unknown, next: Record) { - if (!IsObject(current)) { + if (!IsStandardObject(current)) { ValuePointer.Set(root, path, Clone(next)) } else { const currentKeys = Object.getOwnPropertyNames(current) @@ -90,7 +96,7 @@ function ValueType(root: Mutable, path: string, current: unknown, next: unknown) function Visit(root: Mutable, path: string, current: unknown, next: unknown) { if (IsArray(next)) return ArrayType(root, path, current, next) if (IsTypedArray(next)) return TypedArrayType(root, path, current, next) - if (IsObject(next)) return ObjectType(root, path, current, next) + if (IsStandardObject(next)) return ObjectType(root, path, current, next) if (IsValueType(next)) return ValueType(root, path, current, next) } // ------------------------------------------------------------------ @@ -102,8 +108,8 @@ function IsNonMutableValue(value: unknown): value is Mutable { function IsMismatchedValue(current: unknown, next: unknown) { // prettier-ignore return ( - (IsObject(current) && IsArray(next)) || - (IsArray(current) && IsObject(next)) + (IsStandardObject(current) && IsArray(next)) || + (IsArray(current) && IsStandardObject(next)) ) } // ------------------------------------------------------------------ diff --git a/test/runtime/value/mutate/mutate.ts b/test/runtime/value/mutate/mutate.ts index f90fcd8f5..db4d65661 100644 --- a/test/runtime/value/mutate/mutate.ts +++ b/test/runtime/value/mutate/mutate.ts @@ -85,4 +85,32 @@ describe('value/mutate/Mutate', () => { Assert.NotEqual(A.x, X) Assert.IsEqual(A.x, [1, 2, 3]) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1119 + // ---------------------------------------------------------------- + it('Should mutate array 1', () => { + const A: unknown[] = [] + Value.Mutate(A, []) + Assert.IsEqual(A, []) + }) + it('Should mutate array 2', () => { + const A: unknown[] = [] + Value.Mutate(A, [1]) + Assert.IsEqual(A, [1]) + }) + it('Should mutate array 3', () => { + const A: unknown[] = [1, 2, 3] + Value.Mutate(A, [1, 2]) + Assert.IsEqual(A, [1, 2]) + }) + it('Should mutate array 4', () => { + const A: unknown[] = [1, 2, 3] + Value.Mutate(A, [1, 2, 3, 4]) + Assert.IsEqual(A, [1, 2, 3, 4]) + }) + it('Should mutate array 5', () => { + const A: unknown[] = [1, 2, 3] + Value.Mutate(A, [{}, {}, {}, [1, 2, 3]]) + Assert.IsEqual(A, [{}, {}, {}, [1, 2, 3]]) + }) }) From 598c1d3facc9f7f6f493428c9ed36d3479e5fda9 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 19 Dec 2024 17:27:54 +0900 Subject: [PATCH 324/369] Documentation (#1123) - Ref Documentation Update --- src/type/ref/ref.ts | 25 +++++++++++++++++++------ src/type/type/json.ts | 25 +++++++++++++++++++------ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index f50f6a7ed..85e0e125c 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -47,18 +47,31 @@ export type TRefUnsafe = TUnsafe> /** `[Json]` Creates a Ref type.*/ export function Ref($ref: Ref, options?: SchemaOptions): TRef /** - * @deprecated `[Json]` Creates a Ref type. The referenced type MUST contain a $id. The Ref(TSchema) signature was - * deprecated on 0.34.0 in support of a new type referencing model (Module). Existing implementations using Ref(TSchema) - * can migrate using the following. The Ref(TSchema) validation behavior of Ref will be preserved how the construction - * of legacy Ref(TSchema) will require wrapping in TUnsafe (where TUnsafe is used for inference only) + * @deprecated `[Json]` Creates a Ref type. This signature was deprecated in 0.34.0 where Ref requires callers to pass + * a `string` value for the reference (and not a schema). + * + * To adhere to the 0.34.0 signature, Ref implementations should be updated to the following. * * ```typescript + * // pre-0.34.0 + * + * const T = Type.String({ $id: 'T' }) + * * const R = Type.Ref(T) * ``` - * to + * should be changed to the following + * + * ```typescript + * // post-0.34.0 + * + * const T = Type.String({ $id: 'T' }) + * + * const R = Type.Unsafe>(Type.Ref('T')) + * ``` + * You can also create a generic function to replicate the pre-0.34.0 signature if required * * ```typescript - * const R = Type.Unsafe>(T.$id) + * const LegacyRef = (schema: T) => Type.Unsafe>(Type.Ref(schema.$id!)) * ``` */ export function Ref(type: Type, options?: SchemaOptions): TRefUnsafe diff --git a/src/type/type/json.ts b/src/type/type/json.ts index 8cbb88ef2..afc3650de 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -269,18 +269,31 @@ export class JsonTypeBuilder { /** `[Json]` Creates a Ref type.*/ public Ref($ref: Ref, options?: SchemaOptions): TRef /** - * @deprecated `[Json]` Creates a Ref type. The referenced type MUST contain a $id. The Ref(TSchema) signature was - * deprecated on 0.34.0 in support of a new type referencing model (Module). Existing implementations using Ref(TSchema) - * can migrate using the following. The Ref(TSchema) validation behavior of Ref will be preserved how the construction - * of legacy Ref(TSchema) will require wrapping in TUnsafe (where TUnsafe is used for inference only) + * @deprecated `[Json]` Creates a Ref type. This signature was deprecated in 0.34.0 where Ref requires callers to pass + * a `string` value for the reference (and not a schema). + * + * To adhere to the 0.34.0 signature, Ref implementations should be updated to the following. * * ```typescript + * // pre-0.34.0 + * + * const T = Type.String({ $id: 'T' }) + * * const R = Type.Ref(T) * ``` - * to + * should be changed to the following + * + * ```typescript + * // post-0.34.0 + * + * const T = Type.String({ $id: 'T' }) + * + * const R = Type.Unsafe>(Type.Ref('T')) + * ``` + * You can also create a generic function to replicate the pre-0.34.0 signature if required * * ```typescript - * const R = Type.Unsafe>(T.$id) + * const LegacyRef = (schema: T) => Type.Unsafe>(Type.Ref(schema.$id!)) * ``` */ public Ref(type: Type, options?: SchemaOptions): TRefUnsafe From 6d54d12420ee8badaa4cb2a3492af4cba214eb95 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 21 Dec 2024 03:40:45 +0900 Subject: [PATCH 325/369] Revision 0.34.13 (#1124) * Fix: Convert Symbol Check for TypeScript 5.8.0 * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/value/convert/convert.ts | 7 +------ 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 4f7a9f483..3c68b589d 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.13](https://github.com/sinclairzx81/typebox/pull/1124) + - Pre emptive fix for TypeScript 5.8.0-nightly to resolve symbol narrowing on Convert. - [Revision 0.34.12](https://github.com/sinclairzx81/typebox/pull/1120) - [1119](https://github.com/sinclairzx81/typebox/issues/1119) Fix for Mutate Object Comparison - [1117](https://github.com/sinclairzx81/typebox/issues/1117) Re-Add Type.Recursive Documentation diff --git a/package-lock.json b/package-lock.json index 42b48a004..82a40a27d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.12", + "version": "0.34.13", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.12", + "version": "0.34.13", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 1bac1b697..e40785943 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.12", + "version": "0.34.13", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 1cfaa1081..9461bd7dd 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -57,11 +57,6 @@ import type { TUndefined } from '../../type/undefined/index' // ------------------------------------------------------------------ import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol, HasPropertyKey } from '../guard/index' -// ------------------------------------------------------------------ -// TypeGuard -// ------------------------------------------------------------------ -import { IsOptional } from '../../type/guard/kind' - // ------------------------------------------------------------------ // Conversions // ------------------------------------------------------------------ @@ -124,7 +119,7 @@ function TryConvertBigInt(value: unknown) { return IsStringNumeric(value) ? BigInt(truncateInteger(value)) : IsNumber(value) ? BigInt(Math.trunc(value)) : IsValueFalse(value) ? BigInt(0) : IsValueTrue(value) ? BigInt(1) : value } function TryConvertString(value: unknown) { - return IsValueToString(value) ? value.toString() : IsSymbol(value) && value.description !== undefined ? value.description.toString() : value + return IsSymbol(value) && value.description !== undefined ? value.description.toString() : IsValueToString(value) ? value.toString() : value } function TryConvertNumber(value: unknown) { return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value From bcfee3d0b1a7253c50fd52ec32d28a197d77682e Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 14 Jan 2025 17:10:09 +0900 Subject: [PATCH 326/369] Prototype: Options and RecursiveMap (#1137) --- example/prototypes/index.ts | 2 + example/prototypes/options.ts | 40 +++++++ example/prototypes/readme.md | 50 +++++++++ example/prototypes/recursive-map.ts | 156 ++++++++++++++++++++++++++++ example/standard/readme.md | 66 +++--------- example/standard/standard.ts | 63 +++++++---- 6 files changed, 304 insertions(+), 73 deletions(-) create mode 100644 example/prototypes/options.ts create mode 100644 example/prototypes/recursive-map.ts diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index d5d9aeda4..dd5f572ee 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -28,6 +28,8 @@ THE SOFTWARE. export * from './discriminated-union' export * from './from-schema' +export * from './options' export * from './partial-deep' export * from './union-enum' export * from './union-oneof' +export * from './recursive-map' diff --git a/example/prototypes/options.ts b/example/prototypes/options.ts new file mode 100644 index 000000000..0270acbb7 --- /dev/null +++ b/example/prototypes/options.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema, CloneType } from '@sinclair/typebox' + +// prettier-ignore +export type TOptions> = ( + Type & Options +) + +/** `[Prototype]` Augments a schema with additional generics aware properties */ +// prettier-ignore +export function Options>(type: Type, options: Options): TOptions { + return CloneType(type, options) as never +} diff --git a/example/prototypes/readme.md b/example/prototypes/readme.md index cd95b34fb..f3aea908b 100644 --- a/example/prototypes/readme.md +++ b/example/prototypes/readme.md @@ -69,4 +69,54 @@ const T = UnionOneOf([ // const T = { type T = Static // type T = 'A' | 'B' | 'C' +``` + +## Options + +By default, TypeBox does not represent arbituary options as generics aware properties. However, there are cases where having options observable to the type system can be useful, for example conditionally mapping schematics based on custom metadata. The Options function makes user defined options generics aware. + +```typescript +import { Options } from './prototypes' + +const A = Options(Type.String(), { foo: 1 }) // Options + +type A = typeof A extends { foo: number } ? true : false // true: foo property is observable to the type system +``` + +## Recursive Map + +The Recursive Map type enables deep structural remapping of a type and it's internal constituents. This type accepts a TSchema type and a mapping type function (expressed via HKT). The HKT is applied when traversing the type and it's interior. The mapping HKT can apply conditional tests to each visited type to remap into a new form. The following augments a schematic via Options, and conditionally remaps any schema with an default annotation to make it optional. + +```typescript +import { Type, TOptional, Static, TSchema } from '@sinclair/typebox' + +import { TRecursiveMap, TMappingType, Options } from './prototypes' + +// ------------------------------------------------------------------ +// StaticDefault +// ------------------------------------------------------------------ +export interface StaticDefaultMapping extends TMappingType { + output: ( + this['input'] extends TSchema // if input schematic contains an default + ? this['input'] extends { default: unknown } // annotation, remap it to be optional, + ? TOptional // otherwise just return the schema as is. + : this['input'] + : this['input'] + ) +} +export type StaticDefault = ( + Static> +) + +// ------------------------------------------------------------------ +// Usage +// ------------------------------------------------------------------ + +const T = Type.Object({ + x: Options(Type.String(), { default: 'hello' }), + y: Type.String() +}) + +type T = StaticDefault // { x?: string, y: string } +type S = Static // { x: string, y: string } ``` \ No newline at end of file diff --git a/example/prototypes/recursive-map.ts b/example/prototypes/recursive-map.ts new file mode 100644 index 000000000..0443bed91 --- /dev/null +++ b/example/prototypes/recursive-map.ts @@ -0,0 +1,156 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Mapping: Functions and Type +// ------------------------------------------------------------------ +export type TMappingFunction = (schema: Types.TSchema) => Types.TSchema + +export interface TMappingType { + input: unknown + output: unknown +} +// ------------------------------------------------------------------ +// Record Parameters +// ------------------------------------------------------------------ +function GetRecordPattern(record: Types.TRecord): string { + return globalThis.Object.getOwnPropertyNames(record.patternProperties)[0] +} +function GetRecordKey(record: Types.TRecord): Types.TSchema { + const pattern = GetRecordPattern(record) + return ( + pattern === Types.PatternStringExact ? Types.String() : + pattern === Types.PatternNumberExact ? Types.Number() : + pattern === Types.PatternBooleanExact ? Types.Boolean() : + Types.String({ pattern }) + ) +} +function GetRecordValue(record: Types.TRecord): Types.TSchema { + return record.patternProperties[GetRecordPattern(record)] +} +// ------------------------------------------------------------------ +// Traversal +// ------------------------------------------------------------------ +// prettier-ignore +type TApply = Result +// prettier-ignore +type TFromProperties +}> = Result +function FromProperties(properties: Types.TProperties, func: TMappingFunction): Types.TProperties { + return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { + return {...result, [key]: RecursiveMap(properties[key], func) } + }, {}) +} +// prettier-ignore +type TFromRest = ( + Types extends [infer Left extends Types.TSchema, ...infer Right extends Types.TSchema[]] + ? TFromRest]> + : Result +) +function FromRest(types: Types.TSchema[], func: TMappingFunction): Types.TSchema[] { + return types.map(type => RecursiveMap(type, func)) +} +// prettier-ignore +type TFromType +)> = Result +function FromType(type: Types.TSchema, func: TMappingFunction): Types.TSchema { + return func(type) +} +// ------------------------------------------------------------------ +// TRecursiveMap +// ------------------------------------------------------------------ +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ +// prettier-ignore +export type TRecursiveMap, + // Maps the Interior Parameterized Types + Interior extends Types.TSchema = ( + Exterior extends Types.TConstructor ? Types.TConstructor, TFromType> : + Exterior extends Types.TFunction ? Types.TFunction, TFromType> : + Exterior extends Types.TIntersect ? Types.TIntersect> : + Exterior extends Types.TUnion ? Types.TUnion> : + Exterior extends Types.TTuple ? Types.TTuple> : + Exterior extends Types.TArray ? Types.TArray>: + Exterior extends Types.TAsyncIterator ? Types.TAsyncIterator> : + Exterior extends Types.TIterator ? Types.TIterator> : + Exterior extends Types.TPromise ? Types.TPromise> : + Exterior extends Types.TObject ? Types.TObject> : + Exterior extends Types.TRecord ? Types.TRecordOrObject, TFromType> : + Exterior + ), + // Modifiers Derived from Exterior Type Mapping + IsOptional extends number = Exterior extends Types.TOptional ? 1 : 0, + IsReadonly extends number = Exterior extends Types.TReadonly ? 1 : 0, + Result extends Types.TSchema = ( + [IsReadonly, IsOptional] extends [1, 1] ? Types.TReadonlyOptional : + [IsReadonly, IsOptional] extends [0, 1] ? Types.TOptional : + [IsReadonly, IsOptional] extends [1, 0] ? Types.TReadonly : + Interior + ) +> = Result +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ +// prettier-ignore +export function RecursiveMap(type: Types.TSchema, func: TMappingFunction): Types.TSchema { + // Maps the Exterior Type + const exterior = Types.CloneType(FromType(type, func), type) + // Maps the Interior Parameterized Types + const interior = ( + Types.KindGuard.IsConstructor(type) ? Types.Constructor(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : + Types.KindGuard.IsFunction(type) ? Types.Function(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : + Types.KindGuard.IsIntersect(type) ? Types.Intersect(FromRest(type.allOf, func), exterior) : + Types.KindGuard.IsUnion(type) ? Types.Union(FromRest(type.anyOf, func), exterior) : + Types.KindGuard.IsTuple(type) ? Types.Tuple(FromRest(type.items || [], func), exterior) : + Types.KindGuard.IsArray(type) ? Types.Array(FromType(type.items, func), exterior) : + Types.KindGuard.IsAsyncIterator(type) ? Types.AsyncIterator(FromType(type.items, func), exterior) : + Types.KindGuard.IsIterator(type) ? Types.Iterator(FromType(type.items, func), exterior) : + Types.KindGuard.IsPromise(type) ? Types.Promise(FromType(type.items, func), exterior) : + Types.KindGuard.IsObject(type) ? Types.Object(FromProperties(type.properties, func), exterior) : + Types.KindGuard.IsRecord(type) ? Types.Record(FromType(GetRecordKey(type), func), FromType(GetRecordValue(type), func), exterior) : + Types.CloneType(exterior, exterior) + ) + // Modifiers Derived from Exterior Type Mapping + const isOptional = Types.KindGuard.IsOptional(exterior) + const isReadonly = Types.KindGuard.IsOptional(exterior) + return ( + isOptional && isReadonly ? Types.ReadonlyOptional(interior) : + isOptional ? Types.Optional(interior) : + isReadonly ? Types.Readonly(interior) : + interior + ) +} + + + diff --git a/example/standard/readme.md b/example/standard/readme.md index 343d60616..6dc310269 100644 --- a/example/standard/readme.md +++ b/example/standard/readme.md @@ -1,60 +1,24 @@ ### Standard Schema -This example is a reference implementation of [Standard Schema](https://github.com/standard-schema/standard-schema) for TypeBox. - -### Overview - -This example provides a reference implementation for the Standard Schema specification. Despite the name, this specification is NOT focused on schematics. Rather, it defines a set of common TypeScript interfaces that libraries are expected to implement to be considered "Standard" for framework integration, as defined by the specification's authors. - -The TypeBox project has some concerns about the Standard Schema specification, particularly regarding its avoidance to separate concerns between schematics and the logic used to validate those schematics. TypeBox does respect such separation for direct interoperability with industry standard validators as well as to allow intermediate processing of types (Compile). Additionally, augmenting schematics in the way proposed by Standard Schema would render Json Schema invalidated (which is a general concern for interoperability with Json Schema validation infrastructure, such as Ajv). - -The Standard Schema specification is currently in its RFC stage. TypeBox advocates for renaming the specification to better reflect its purpose, such as "Common Interface for Type Integration." Additionally, the requirement for type libraries to adopt a common interface for integration warrants review. The reference project link provided below demonstrates an alternative approach that would enable integration of all type libraries (including those not immediately compatible with Standard Schema) to be integrated into frameworks (such as tRPC) without modification to a library's core structure. - -[Type Adapters](https://github.com/sinclairzx81/type-adapters) +Reference implementation of [Standard Schema](https://github.com/standard-schema/standard-schema) for TypeBox. ### Example -The Standard Schema function will augment TypeBox's Json Schema with runtime validation methods. These methods are assigned to the sub property `~standard`. Once a type is augmented, it should no longer be considered valid Json Schema. +The following example augments a TypeBox schema with the required `~standard` interface. The `~standard` interface is applied via non-enumerable configuration enabling the schematics to continue to be used with strict compliant validators such as Ajv that would otherwise reject the non-standard `~standard` keyword. ```typescript -import { Type } from '@sinclair/typebox' import { StandardSchema } from './standard' +import { Type } from '@sinclair/typebox' -// The Standard Schema function will augment a TypeBox type with runtime validation -// logic to validate / parse a value (as mandated by the Standard Schema interfaces). -// Calling this function will render the json-schema schematics invalidated, specifically -// the non-standard keyword `~standard` which ideally should be expressed as a non -// serializable symbol (Ajv strict) - -const A = StandardSchema(Type.Object({ // const A = { - x: Type.Number(), // '~standard': { version: 1, vendor: 'TypeBox', validate: [Function: validate] }, - y: Type.Number(), // type: 'object', - z: Type.Number(), // properties: { -})) // x: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, - // y: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, - // z: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' } +const T = StandardSchema(Type.Object({ // const A = { + x: Type.Number(), // (non-enumerable) '~standard': { + y: Type.Number(), // version: 1, + z: Type.Number(), // vendor: 'TypeBox', +})) // validate: [Function: validate] // }, - // required: [ 'x', 'y', 'z' ], - // [Symbol(TypeBox.Kind)]: 'Object' - // } - -const R = A['~standard'].validate({ x: 1, y: 2, z: 3 }) // const R = { value: { x: 1, y: 2, z: 3 }, issues: [] } -``` - -### Ajv Strict - -Applying Standard Schema to a TypeBox types renders them unusable in Ajv. The issue is due to the `~standard` property being un-assignable as a keyword (due to the leading `~`) - -```typescript -import Ajv from 'ajv' - -const ajv = new Ajv().addKeyword('~standard') // cannot be defined as keyword. - -const A = StandardSchema(Type.Object({ // const A = { - x: Type.Number(), // '~standard': { version: 1, vendor: 'TypeBox', validate: [Function: validate] }, - y: Type.Number(), // type: 'object', - z: Type.Number(), // properties: { -})) // x: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, + // type: 'object', + // properties: { + // x: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, // y: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' }, // z: { type: 'number', [Symbol(TypeBox.Kind)]: 'Number' } // }, @@ -62,6 +26,8 @@ const A = StandardSchema(Type.Object({ // const A = { // [Symbol(TypeBox.Kind)]: 'Object' // } - -ajv.validate(A, { x: 1, y: 2, z: 3 }) // Error: Keyword ~standard has invalid name -``` +const R = T['~standard'].validate({ x: 1, y: 2, z: 3 }) // const R = { + // value: { x: 1, y: 2, z: 3 }, + // issues: [] + // } +``` \ No newline at end of file diff --git a/example/standard/standard.ts b/example/standard/standard.ts index b61dcbba0..ef5ca384e 100644 --- a/example/standard/standard.ts +++ b/example/standard/standard.ts @@ -26,43 +26,60 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { AssertError, Value, ValueError } from '@sinclair/typebox/value' +import { AssertError, Value, ValueError, ValueErrorType } from '@sinclair/typebox/value' import { TSchema, StaticDecode, CloneType } from '@sinclair/typebox' // ------------------------------------------------------------------ -// StandardSchema: Common +// StandardSchema // ------------------------------------------------------------------ interface StandardResult { value: Output - issues: [ValueError[]] + issues: ValueError[] } interface StandardSchema { - readonly "~standard": StandardSchemaProps; + readonly "~standard": StandardSchemaProperties } - -interface StandardSchemaProps { - readonly version: 1; - readonly vendor: 'TypeBox'; - readonly validate: (value: unknown) => StandardResult; - readonly types?: undefined; +interface StandardSchemaProperties { + readonly version: 1 + readonly vendor: 'TypeBox' + readonly validate: (value: unknown) => StandardResult + readonly types?: undefined } // ------------------------------------------------------------------ -// StandardSchema: TypeBox +// Issues // ------------------------------------------------------------------ -export type TStandardSchema> = Result -/** - * Augments a Json Schema with runtime validation logic required by StandardSchema. Wrapping a type in this way renders the type - * incompatible with the Json Schema specification. - */ -export function StandardSchema(schema: Type, references: TSchema[] = []): TStandardSchema> { - const validate = (value: unknown) => { +// prettier-ignore +function CreateIssues(schema: TSchema, value: unknown, error: unknown): ValueError[] { + const isAssertError = error instanceof AssertError ? error : undefined + return !isAssertError + ? [{errors: [], message: 'Unknown error', path: '/', type: ValueErrorType.Kind, schema, value }] + : [...isAssertError.Errors()] +} +// ------------------------------------------------------------------ +// Validate +// ------------------------------------------------------------------ +// prettier-ignore +function CreateValidator(schema: Type, references: TSchema[]): (value: unknown) => StandardResult> { + return (value: unknown): StandardResult> => { try { - return { value: Value.Parse(schema, references, value) } + return { value: Value.Parse(schema, references, value), issues: [] } } catch (error) { - const asserted = error instanceof AssertError ? error : undefined - return asserted ? { issues: [...asserted.Errors()] } : { issues: ['Unknown error']} + return { value: undefined, issues: CreateIssues(schema, value, error) } } } - const standard = { version: 1, vendor: 'TypeBox', validate } - return CloneType(schema, { ['~standard']: standard }) as never +} +// ------------------------------------------------------------------ +// StandardSchema +// ------------------------------------------------------------------ +/** Augments a TypeBox type with the `~standard` validation interface. */ +export type TStandardSchema = ( + Input & StandardSchema +) +/** Augments a TypeBox type with the `~standard` validation interface. */ +export function StandardSchema(schema: Type, references: TSchema[] = []): TStandardSchema> { + const standard = { version: 1, vendor: 'TypeBox', validate: CreateValidator(schema, references) } + return Object.defineProperty(CloneType(schema), "~standard", { + enumerable: false, + value: standard + }) as never } \ No newline at end of file From cc6471a0bc08baa02295c0c26a200efbde7c5fe3 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 16 Jan 2025 14:57:01 +0900 Subject: [PATCH 327/369] Annual License Update (#1138) Updates the license year range to 2017-2025. --- example/annotation/annotation.ts | 2 +- example/collections/array.ts | 2 +- example/collections/index.ts | 2 +- example/collections/map.ts | 2 +- example/collections/set.ts | 2 +- example/formats/index.ts | 2 +- example/prototypes/discriminated-union.ts | 2 +- example/prototypes/from-schema.ts | 2 +- example/prototypes/index.ts | 2 +- example/prototypes/options.ts | 2 +- example/prototypes/partial-deep.ts | 2 +- example/prototypes/recursive-map.ts | 2 +- example/prototypes/union-enum.ts | 2 +- example/prototypes/union-oneof.ts | 2 +- example/typedef/index.ts | 2 +- example/typedef/typedef.ts | 2 +- license | 2 +- src/compiler/compiler.ts | 2 +- src/compiler/index.ts | 2 +- src/errors/errors.ts | 2 +- src/errors/function.ts | 2 +- src/errors/index.ts | 2 +- src/index.ts | 2 +- src/syntax/index.ts | 2 +- src/syntax/parse.ts | 2 +- src/syntax/runtime.ts | 2 +- src/syntax/static.ts | 2 +- src/system/index.ts | 2 +- src/system/policy.ts | 2 +- src/system/system.ts | 2 +- src/type/any/any.ts | 2 +- src/type/any/index.ts | 2 +- src/type/array/array.ts | 2 +- src/type/array/index.ts | 2 +- src/type/async-iterator/async-iterator.ts | 2 +- src/type/async-iterator/index.ts | 2 +- src/type/awaited/awaited.ts | 2 +- src/type/awaited/index.ts | 2 +- src/type/bigint/bigint.ts | 2 +- src/type/bigint/index.ts | 2 +- src/type/boolean/boolean.ts | 2 +- src/type/boolean/index.ts | 2 +- src/type/clone/index.ts | 2 +- src/type/clone/type.ts | 2 +- src/type/clone/value.ts | 2 +- src/type/composite/composite.ts | 2 +- src/type/composite/index.ts | 2 +- src/type/computed/computed.ts | 2 +- src/type/computed/index.ts | 2 +- src/type/const/const.ts | 2 +- src/type/const/index.ts | 2 +- src/type/constructor-parameters/constructor-parameters.ts | 2 +- src/type/constructor-parameters/index.ts | 2 +- src/type/constructor/constructor.ts | 2 +- src/type/constructor/index.ts | 2 +- src/type/create/immutable.ts | 2 +- src/type/create/index.ts | 2 +- src/type/create/type.ts | 2 +- src/type/date/date.ts | 2 +- src/type/date/index.ts | 2 +- src/type/discard/discard.ts | 2 +- src/type/discard/index.ts | 2 +- src/type/enum/enum.ts | 2 +- src/type/enum/index.ts | 2 +- src/type/error/error.ts | 2 +- src/type/error/index.ts | 2 +- src/type/exclude/exclude-from-mapped-result.ts | 2 +- src/type/exclude/exclude-from-template-literal.ts | 2 +- src/type/exclude/exclude.ts | 2 +- src/type/exclude/index.ts | 2 +- src/type/extends/extends-check.ts | 2 +- src/type/extends/extends-from-mapped-key.ts | 2 +- src/type/extends/extends-from-mapped-result.ts | 2 +- src/type/extends/extends-undefined.ts | 2 +- src/type/extends/extends.ts | 2 +- src/type/extends/index.ts | 2 +- src/type/extract/extract-from-mapped-result.ts | 2 +- src/type/extract/extract-from-template-literal.ts | 2 +- src/type/extract/extract.ts | 2 +- src/type/extract/index.ts | 2 +- src/type/function/function.ts | 2 +- src/type/function/index.ts | 2 +- src/type/guard/index.ts | 2 +- src/type/guard/kind.ts | 2 +- src/type/guard/type.ts | 2 +- src/type/guard/value.ts | 2 +- src/type/helpers/helpers.ts | 2 +- src/type/helpers/index.ts | 2 +- src/type/index.ts | 2 +- src/type/indexed/index.ts | 2 +- src/type/indexed/indexed-from-mapped-key.ts | 2 +- src/type/indexed/indexed-from-mapped-result.ts | 2 +- src/type/indexed/indexed-property-keys.ts | 2 +- src/type/indexed/indexed.ts | 2 +- src/type/instance-type/index.ts | 2 +- src/type/instance-type/instance-type.ts | 2 +- src/type/integer/index.ts | 2 +- src/type/integer/integer.ts | 2 +- src/type/intersect/index.ts | 2 +- src/type/intersect/intersect-create.ts | 2 +- src/type/intersect/intersect-evaluated.ts | 2 +- src/type/intersect/intersect-type.ts | 2 +- src/type/intersect/intersect.ts | 2 +- src/type/intrinsic/capitalize.ts | 2 +- src/type/intrinsic/index.ts | 2 +- src/type/intrinsic/intrinsic-from-mapped-key.ts | 2 +- src/type/intrinsic/intrinsic.ts | 2 +- src/type/intrinsic/lowercase.ts | 2 +- src/type/intrinsic/uncapitalize.ts | 2 +- src/type/intrinsic/uppercase.ts | 2 +- src/type/iterator/index.ts | 2 +- src/type/iterator/iterator.ts | 2 +- src/type/keyof/index.ts | 2 +- src/type/keyof/keyof-from-mapped-result.ts | 2 +- src/type/keyof/keyof-property-entries.ts | 2 +- src/type/keyof/keyof-property-keys.ts | 2 +- src/type/keyof/keyof.ts | 2 +- src/type/literal/index.ts | 2 +- src/type/literal/literal.ts | 2 +- src/type/mapped/index.ts | 2 +- src/type/mapped/mapped-key.ts | 2 +- src/type/mapped/mapped-result.ts | 2 +- src/type/mapped/mapped.ts | 2 +- src/type/module/compute.ts | 2 +- src/type/module/index.ts | 2 +- src/type/module/infer.ts | 2 +- src/type/module/module.ts | 2 +- src/type/never/index.ts | 2 +- src/type/never/never.ts | 2 +- src/type/not/index.ts | 2 +- src/type/not/not.ts | 2 +- src/type/null/index.ts | 2 +- src/type/null/null.ts | 2 +- src/type/number/index.ts | 2 +- src/type/number/number.ts | 2 +- src/type/object/index.ts | 2 +- src/type/object/object.ts | 2 +- src/type/omit/index.ts | 2 +- src/type/omit/omit-from-mapped-key.ts | 2 +- src/type/omit/omit-from-mapped-result.ts | 2 +- src/type/omit/omit.ts | 2 +- src/type/optional/index.ts | 2 +- src/type/optional/optional-from-mapped-result.ts | 2 +- src/type/optional/optional.ts | 2 +- src/type/parameters/index.ts | 2 +- src/type/parameters/parameters.ts | 2 +- src/type/partial/index.ts | 2 +- src/type/partial/partial-from-mapped-result.ts | 2 +- src/type/partial/partial.ts | 2 +- src/type/patterns/index.ts | 2 +- src/type/patterns/patterns.ts | 2 +- src/type/pick/index.ts | 2 +- src/type/pick/pick-from-mapped-key.ts | 2 +- src/type/pick/pick-from-mapped-result.ts | 2 +- src/type/pick/pick.ts | 2 +- src/type/promise/index.ts | 2 +- src/type/promise/promise.ts | 2 +- src/type/readonly-optional/index.ts | 2 +- src/type/readonly-optional/readonly-optional.ts | 2 +- src/type/readonly/index.ts | 2 +- src/type/readonly/readonly-from-mapped-result.ts | 2 +- src/type/readonly/readonly.ts | 2 +- src/type/record/index.ts | 2 +- src/type/record/record.ts | 2 +- src/type/recursive/index.ts | 2 +- src/type/recursive/recursive.ts | 2 +- src/type/ref/index.ts | 2 +- src/type/ref/ref.ts | 2 +- src/type/regexp/index.ts | 2 +- src/type/regexp/regexp.ts | 2 +- src/type/registry/format.ts | 2 +- src/type/registry/index.ts | 2 +- src/type/registry/type.ts | 2 +- src/type/required/index.ts | 2 +- src/type/required/required-from-mapped-result.ts | 2 +- src/type/required/required.ts | 2 +- src/type/rest/index.ts | 2 +- src/type/rest/rest.ts | 2 +- src/type/return-type/index.ts | 2 +- src/type/return-type/return-type.ts | 2 +- src/type/schema/anyschema.ts | 2 +- src/type/schema/index.ts | 2 +- src/type/schema/schema.ts | 2 +- src/type/sets/index.ts | 2 +- src/type/sets/set.ts | 2 +- src/type/static/index.ts | 2 +- src/type/static/static.ts | 2 +- src/type/string/index.ts | 2 +- src/type/string/string.ts | 2 +- src/type/symbol/index.ts | 2 +- src/type/symbol/symbol.ts | 2 +- src/type/symbols/index.ts | 2 +- src/type/symbols/symbols.ts | 2 +- src/type/template-literal/finite.ts | 2 +- src/type/template-literal/generate.ts | 2 +- src/type/template-literal/index.ts | 2 +- src/type/template-literal/parse.ts | 2 +- src/type/template-literal/pattern.ts | 2 +- src/type/template-literal/syntax.ts | 2 +- src/type/template-literal/template-literal.ts | 2 +- src/type/template-literal/union.ts | 2 +- src/type/transform/index.ts | 2 +- src/type/transform/transform.ts | 2 +- src/type/tuple/index.ts | 2 +- src/type/tuple/tuple.ts | 2 +- src/type/type/index.ts | 2 +- src/type/type/javascript.ts | 2 +- src/type/type/json.ts | 2 +- src/type/type/type.ts | 2 +- src/type/uint8array/index.ts | 2 +- src/type/uint8array/uint8array.ts | 2 +- src/type/undefined/index.ts | 2 +- src/type/undefined/undefined.ts | 2 +- src/type/union/index.ts | 2 +- src/type/union/union-create.ts | 2 +- src/type/union/union-evaluated.ts | 2 +- src/type/union/union-type.ts | 2 +- src/type/union/union.ts | 2 +- src/type/unknown/index.ts | 2 +- src/type/unknown/unknown.ts | 2 +- src/type/unsafe/index.ts | 2 +- src/type/unsafe/unsafe.ts | 2 +- src/type/void/index.ts | 2 +- src/type/void/void.ts | 2 +- src/value/assert/assert.ts | 2 +- src/value/assert/index.ts | 2 +- src/value/cast/cast.ts | 2 +- src/value/cast/index.ts | 2 +- src/value/check/check.ts | 2 +- src/value/check/index.ts | 2 +- src/value/clean/clean.ts | 2 +- src/value/clean/index.ts | 2 +- src/value/clone/clone.ts | 2 +- src/value/clone/index.ts | 2 +- src/value/convert/convert.ts | 2 +- src/value/convert/index.ts | 2 +- src/value/create/create.ts | 2 +- src/value/create/index.ts | 2 +- src/value/decode/decode.ts | 2 +- src/value/decode/index.ts | 2 +- src/value/default/default.ts | 2 +- src/value/default/index.ts | 2 +- src/value/delta/delta.ts | 2 +- src/value/delta/index.ts | 2 +- src/value/deref/deref.ts | 2 +- src/value/deref/index.ts | 2 +- src/value/encode/encode.ts | 2 +- src/value/encode/index.ts | 2 +- src/value/equal/equal.ts | 2 +- src/value/equal/index.ts | 2 +- src/value/guard/guard.ts | 2 +- src/value/guard/index.ts | 2 +- src/value/hash/hash.ts | 2 +- src/value/hash/index.ts | 2 +- src/value/index.ts | 2 +- src/value/mutate/index.ts | 2 +- src/value/mutate/mutate.ts | 2 +- src/value/parse/index.ts | 2 +- src/value/parse/parse.ts | 2 +- src/value/pointer/index.ts | 2 +- src/value/pointer/pointer.ts | 2 +- src/value/transform/decode.ts | 2 +- src/value/transform/encode.ts | 2 +- src/value/transform/has.ts | 2 +- src/value/transform/index.ts | 2 +- src/value/value/index.ts | 2 +- src/value/value/value.ts | 2 +- task/build/cjs/build.ts | 2 +- task/build/cjs/compile.ts | 2 +- task/build/esm/build.ts | 2 +- task/build/esm/compile.ts | 2 +- task/build/esm/convert-to-esm.ts | 2 +- task/build/index.ts | 2 +- task/build/notices/remove-notices.ts | 2 +- task/build/package/build.ts | 2 +- task/build/package/create-package-json-redirect.ts | 2 +- task/build/package/create-package-json.ts | 2 +- 277 files changed, 277 insertions(+), 277 deletions(-) diff --git a/example/annotation/annotation.ts b/example/annotation/annotation.ts index 9e38f70b6..90b8f7a20 100644 --- a/example/annotation/annotation.ts +++ b/example/annotation/annotation.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/array.ts b/example/collections/array.ts index be13f9284..0549cda89 100644 --- a/example/collections/array.ts +++ b/example/collections/array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/index.ts b/example/collections/index.ts index 6069c8c37..a5187f8b5 100644 --- a/example/collections/index.ts +++ b/example/collections/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/map.ts b/example/collections/map.ts index 47957795b..ac5174f88 100644 --- a/example/collections/map.ts +++ b/example/collections/map.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/collections/set.ts b/example/collections/set.ts index 730583ae3..70f44bb91 100644 --- a/example/collections/set.ts +++ b/example/collections/set.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/formats/index.ts b/example/formats/index.ts index 2ac3f6f51..cd002284d 100644 --- a/example/formats/index.ts +++ b/example/formats/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/discriminated-union.ts b/example/prototypes/discriminated-union.ts index 22da0f486..8aae1f3f2 100644 --- a/example/prototypes/discriminated-union.ts +++ b/example/prototypes/discriminated-union.ts @@ -5,7 +5,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/from-schema.ts b/example/prototypes/from-schema.ts index 94a4716b2..f44c36735 100644 --- a/example/prototypes/from-schema.ts +++ b/example/prototypes/from-schema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index dd5f572ee..e7119a8aa 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/options.ts b/example/prototypes/options.ts index 0270acbb7..bf09281ad 100644 --- a/example/prototypes/options.ts +++ b/example/prototypes/options.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/partial-deep.ts b/example/prototypes/partial-deep.ts index 5fc707f80..8637b52dc 100644 --- a/example/prototypes/partial-deep.ts +++ b/example/prototypes/partial-deep.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/recursive-map.ts b/example/prototypes/recursive-map.ts index 0443bed91..dfa35b004 100644 --- a/example/prototypes/recursive-map.ts +++ b/example/prototypes/recursive-map.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/union-enum.ts b/example/prototypes/union-enum.ts index 91c768c52..ee15f15a4 100644 --- a/example/prototypes/union-enum.ts +++ b/example/prototypes/union-enum.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/prototypes/union-oneof.ts b/example/prototypes/union-oneof.ts index 818d3e457..ec14fac94 100644 --- a/example/prototypes/union-oneof.ts +++ b/example/prototypes/union-oneof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/typedef/index.ts b/example/typedef/index.ts index a23b035fe..daac6020a 100644 --- a/example/typedef/index.ts +++ b/example/typedef/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/example/typedef/typedef.ts b/example/typedef/typedef.ts index 6682e5e38..f0b74da5f 100644 --- a/example/typedef/typedef.ts +++ b/example/typedef/typedef.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/license b/license index 7c58af806..5737c52ed 100644 --- a/license +++ b/license @@ -4,7 +4,7 @@ Json Schema Type Builder with Static Type Resolution for TypeScript The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 5edf8fd7b..706667ee7 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/compiler/index.ts b/src/compiler/index.ts index d1a93e868..4bdcefef7 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/errors.ts b/src/errors/errors.ts index 9bb1f6cd1..de62cd970 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/function.ts b/src/errors/function.ts index 536f3e682..39ac67220 100644 --- a/src/errors/function.ts +++ b/src/errors/function.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/errors/index.ts b/src/errors/index.ts index 288d17757..4c499cc76 100644 --- a/src/errors/index.ts +++ b/src/errors/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/index.ts b/src/index.ts index 90525bd71..f17cc7d15 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/syntax/index.ts b/src/syntax/index.ts index ae2cee724..8a2445bfb 100644 --- a/src/syntax/index.ts +++ b/src/syntax/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/syntax/parse.ts b/src/syntax/parse.ts index 1ec0d41fd..d58922e7b 100644 --- a/src/syntax/parse.ts +++ b/src/syntax/parse.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 82db79005..2f5801f20 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/syntax/static.ts b/src/syntax/static.ts index ddd48ea76..9d8bb0a38 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/index.ts b/src/system/index.ts index 70c91ccf2..b9a805ba6 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/policy.ts b/src/system/policy.ts index 8501462c5..88eb012c8 100644 --- a/src/system/policy.ts +++ b/src/system/policy.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/system/system.ts b/src/system/system.ts index 98b3647c6..b11de693d 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/any/any.ts b/src/type/any/any.ts index f5d7d907c..8adc89cca 100644 --- a/src/type/any/any.ts +++ b/src/type/any/any.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/any/index.ts b/src/type/any/index.ts index fe7f59848..400a3ef5c 100644 --- a/src/type/any/index.ts +++ b/src/type/any/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/array/array.ts b/src/type/array/array.ts index dc29a1858..bab2d7704 100644 --- a/src/type/array/array.ts +++ b/src/type/array/array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/array/index.ts b/src/type/array/index.ts index d19c2e2be..46e91d8a2 100644 --- a/src/type/array/index.ts +++ b/src/type/array/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts index 2a97d73a0..7a885fc77 100644 --- a/src/type/async-iterator/async-iterator.ts +++ b/src/type/async-iterator/async-iterator.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/async-iterator/index.ts b/src/type/async-iterator/index.ts index 9a61d47dc..6a0c51edf 100644 --- a/src/type/async-iterator/index.ts +++ b/src/type/async-iterator/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts index 1a849f14e..37a84cad5 100644 --- a/src/type/awaited/awaited.ts +++ b/src/type/awaited/awaited.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/awaited/index.ts b/src/type/awaited/index.ts index 186c08da5..6d753bb9c 100644 --- a/src/type/awaited/index.ts +++ b/src/type/awaited/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts index e98491b2a..ad0af34c9 100644 --- a/src/type/bigint/bigint.ts +++ b/src/type/bigint/bigint.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/bigint/index.ts b/src/type/bigint/index.ts index 8d4c4b5d6..fca1fe8e8 100644 --- a/src/type/bigint/index.ts +++ b/src/type/bigint/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts index acbf44207..2e582ef08 100644 --- a/src/type/boolean/boolean.ts +++ b/src/type/boolean/boolean.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/boolean/index.ts b/src/type/boolean/index.ts index 4de112e06..d8620d4d7 100644 --- a/src/type/boolean/index.ts +++ b/src/type/boolean/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/index.ts b/src/type/clone/index.ts index ee3c7bfec..c4df1782d 100644 --- a/src/type/clone/index.ts +++ b/src/type/clone/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts index 18a10f3ec..8cb09431b 100644 --- a/src/type/clone/type.ts +++ b/src/type/clone/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/clone/value.ts b/src/type/clone/value.ts index 0a062cb38..a20ff5f1e 100644 --- a/src/type/clone/value.ts +++ b/src/type/clone/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts index 26b4406b5..8b9833399 100644 --- a/src/type/composite/composite.ts +++ b/src/type/composite/composite.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/composite/index.ts b/src/type/composite/index.ts index 141f3e337..55de8e6ec 100644 --- a/src/type/composite/index.ts +++ b/src/type/composite/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/computed/computed.ts b/src/type/computed/computed.ts index 2524dc152..bc8cfcb79 100644 --- a/src/type/computed/computed.ts +++ b/src/type/computed/computed.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/computed/index.ts b/src/type/computed/index.ts index 4d20b1f87..6c75b7205 100644 --- a/src/type/computed/index.ts +++ b/src/type/computed/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/const/const.ts b/src/type/const/const.ts index fcaa788a3..91e3df4f1 100644 --- a/src/type/const/const.ts +++ b/src/type/const/const.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/const/index.ts b/src/type/const/index.ts index 90b0ba9ca..2d24ed90f 100644 --- a/src/type/const/index.ts +++ b/src/type/const/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts index 3ce3e0034..b15718f1d 100644 --- a/src/type/constructor-parameters/constructor-parameters.ts +++ b/src/type/constructor-parameters/constructor-parameters.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor-parameters/index.ts b/src/type/constructor-parameters/index.ts index dbc9c6c0a..f5898f6b5 100644 --- a/src/type/constructor-parameters/index.ts +++ b/src/type/constructor-parameters/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts index 6904b8541..31539fbf0 100644 --- a/src/type/constructor/constructor.ts +++ b/src/type/constructor/constructor.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/constructor/index.ts b/src/type/constructor/index.ts index 3269a4e78..1f5b0684e 100644 --- a/src/type/constructor/index.ts +++ b/src/type/constructor/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/create/immutable.ts b/src/type/create/immutable.ts index d2069efe7..b4041b94c 100644 --- a/src/type/create/immutable.ts +++ b/src/type/create/immutable.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/create/index.ts b/src/type/create/index.ts index 53780a6ef..ea69721de 100644 --- a/src/type/create/index.ts +++ b/src/type/create/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/create/type.ts b/src/type/create/type.ts index cb8371abd..253231d35 100644 --- a/src/type/create/type.ts +++ b/src/type/create/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/date/date.ts b/src/type/date/date.ts index 44b9d0d3d..040e64e8e 100644 --- a/src/type/date/date.ts +++ b/src/type/date/date.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/date/index.ts b/src/type/date/index.ts index d73b06d48..6aff28867 100644 --- a/src/type/date/index.ts +++ b/src/type/date/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/discard/discard.ts b/src/type/discard/discard.ts index cf1688ddd..821f9b40c 100644 --- a/src/type/discard/discard.ts +++ b/src/type/discard/discard.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/discard/index.ts b/src/type/discard/index.ts index 54c7051b2..0e9830749 100644 --- a/src/type/discard/index.ts +++ b/src/type/discard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts index 1cd8a5867..87f88e22c 100644 --- a/src/type/enum/enum.ts +++ b/src/type/enum/enum.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/enum/index.ts b/src/type/enum/index.ts index a77209132..8ad69431d 100644 --- a/src/type/enum/index.ts +++ b/src/type/enum/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/error/error.ts b/src/type/error/error.ts index f72140b3d..443a56338 100644 --- a/src/type/error/error.ts +++ b/src/type/error/error.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/error/index.ts b/src/type/error/index.ts index 4cf885dad..2e03c77bd 100644 --- a/src/type/error/index.ts +++ b/src/type/error/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/exclude-from-mapped-result.ts b/src/type/exclude/exclude-from-mapped-result.ts index 8985e467e..ab8fef55a 100644 --- a/src/type/exclude/exclude-from-mapped-result.ts +++ b/src/type/exclude/exclude-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/exclude-from-template-literal.ts b/src/type/exclude/exclude-from-template-literal.ts index 707a6c254..a80f10a2f 100644 --- a/src/type/exclude/exclude-from-template-literal.ts +++ b/src/type/exclude/exclude-from-template-literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts index daee3ea1f..663cf73bc 100644 --- a/src/type/exclude/exclude.ts +++ b/src/type/exclude/exclude.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/exclude/index.ts b/src/type/exclude/index.ts index 610ff43a2..5e725fb54 100644 --- a/src/type/exclude/index.ts +++ b/src/type/exclude/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-check.ts b/src/type/extends/extends-check.ts index dc461eff4..e78bbc318 100644 --- a/src/type/extends/extends-check.ts +++ b/src/type/extends/extends-check.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-from-mapped-key.ts b/src/type/extends/extends-from-mapped-key.ts index 506ebc854..697a2d37f 100644 --- a/src/type/extends/extends-from-mapped-key.ts +++ b/src/type/extends/extends-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-from-mapped-result.ts b/src/type/extends/extends-from-mapped-result.ts index 02a1dc84b..a7f4fb40b 100644 --- a/src/type/extends/extends-from-mapped-result.ts +++ b/src/type/extends/extends-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends-undefined.ts b/src/type/extends/extends-undefined.ts index 23c51776d..4d6f41cd9 100644 --- a/src/type/extends/extends-undefined.ts +++ b/src/type/extends/extends-undefined.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts index 9048d1eab..ae9d264a7 100644 --- a/src/type/extends/extends.ts +++ b/src/type/extends/extends.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extends/index.ts b/src/type/extends/index.ts index aa0bc3d6c..1a8957763 100644 --- a/src/type/extends/index.ts +++ b/src/type/extends/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/extract-from-mapped-result.ts b/src/type/extract/extract-from-mapped-result.ts index b49720a37..640ee37dd 100644 --- a/src/type/extract/extract-from-mapped-result.ts +++ b/src/type/extract/extract-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/extract-from-template-literal.ts b/src/type/extract/extract-from-template-literal.ts index 82cc52dd9..e3e9f8024 100644 --- a/src/type/extract/extract-from-template-literal.ts +++ b/src/type/extract/extract-from-template-literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts index 8c94045b2..aaed115ee 100644 --- a/src/type/extract/extract.ts +++ b/src/type/extract/extract.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/extract/index.ts b/src/type/extract/index.ts index 38bc0a813..c8f052d88 100644 --- a/src/type/extract/index.ts +++ b/src/type/extract/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/function/function.ts b/src/type/function/function.ts index 150fc7338..6feffaed4 100644 --- a/src/type/function/function.ts +++ b/src/type/function/function.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/function/index.ts b/src/type/function/index.ts index 21b4ad556..948381971 100644 --- a/src/type/function/index.ts +++ b/src/type/function/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/index.ts b/src/type/guard/index.ts index feeaa6296..9745d55cd 100644 --- a/src/type/guard/index.ts +++ b/src/type/guard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index ed3f68f2f..9ca359fdb 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index ebd4b8f8f..521522ffd 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts index 4e4f9d1e2..93ae57fe8 100644 --- a/src/type/guard/value.ts +++ b/src/type/guard/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts index 1ab44dc30..a3cb86466 100644 --- a/src/type/helpers/helpers.ts +++ b/src/type/helpers/helpers.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/helpers/index.ts b/src/type/helpers/index.ts index 8e6b80464..31d3e5185 100644 --- a/src/type/helpers/index.ts +++ b/src/type/helpers/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/index.ts b/src/type/index.ts index d9d5d5076..661e36678 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/index.ts b/src/type/indexed/index.ts index ed02a2814..b13a8c9b5 100644 --- a/src/type/indexed/index.ts +++ b/src/type/indexed/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-from-mapped-key.ts b/src/type/indexed/indexed-from-mapped-key.ts index a523032f8..ae99317eb 100644 --- a/src/type/indexed/indexed-from-mapped-key.ts +++ b/src/type/indexed/indexed-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-from-mapped-result.ts b/src/type/indexed/indexed-from-mapped-result.ts index 200493649..b5b3d67ca 100644 --- a/src/type/indexed/indexed-from-mapped-result.ts +++ b/src/type/indexed/indexed-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed-property-keys.ts b/src/type/indexed/indexed-property-keys.ts index d5f2e7315..e04866414 100644 --- a/src/type/indexed/indexed-property-keys.ts +++ b/src/type/indexed/indexed-property-keys.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts index 541c6e734..1e6603bf5 100644 --- a/src/type/indexed/indexed.ts +++ b/src/type/indexed/indexed.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/instance-type/index.ts b/src/type/instance-type/index.ts index a16ba2e50..35a3a954d 100644 --- a/src/type/instance-type/index.ts +++ b/src/type/instance-type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts index 853c8da61..ece1382c9 100644 --- a/src/type/instance-type/instance-type.ts +++ b/src/type/instance-type/instance-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/integer/index.ts b/src/type/integer/index.ts index 82c3e2cce..8c78b4721 100644 --- a/src/type/integer/index.ts +++ b/src/type/integer/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts index 8b15618d3..245037e1e 100644 --- a/src/type/integer/integer.ts +++ b/src/type/integer/integer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/index.ts b/src/type/intersect/index.ts index e9ec6eaea..67a0a8911 100644 --- a/src/type/intersect/index.ts +++ b/src/type/intersect/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-create.ts b/src/type/intersect/intersect-create.ts index 6b38ce830..59f6cf64e 100644 --- a/src/type/intersect/intersect-create.ts +++ b/src/type/intersect/intersect-create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-evaluated.ts b/src/type/intersect/intersect-evaluated.ts index 637009345..2ac4a3c0d 100644 --- a/src/type/intersect/intersect-evaluated.ts +++ b/src/type/intersect/intersect-evaluated.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect-type.ts b/src/type/intersect/intersect-type.ts index 1b1f6a5de..80b2c25e5 100644 --- a/src/type/intersect/intersect-type.ts +++ b/src/type/intersect/intersect-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts index 999d3d2af..63dd76b09 100644 --- a/src/type/intersect/intersect.ts +++ b/src/type/intersect/intersect.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/capitalize.ts b/src/type/intrinsic/capitalize.ts index 48acdc23d..51309eac6 100644 --- a/src/type/intrinsic/capitalize.ts +++ b/src/type/intrinsic/capitalize.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/index.ts b/src/type/intrinsic/index.ts index 8a67cea42..c55fa12d6 100644 --- a/src/type/intrinsic/index.ts +++ b/src/type/intrinsic/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/intrinsic-from-mapped-key.ts b/src/type/intrinsic/intrinsic-from-mapped-key.ts index 16afb241e..80c873451 100644 --- a/src/type/intrinsic/intrinsic-from-mapped-key.ts +++ b/src/type/intrinsic/intrinsic-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts index bbb139be6..aca5d386c 100644 --- a/src/type/intrinsic/intrinsic.ts +++ b/src/type/intrinsic/intrinsic.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/lowercase.ts b/src/type/intrinsic/lowercase.ts index 745c4ff22..6837496f8 100644 --- a/src/type/intrinsic/lowercase.ts +++ b/src/type/intrinsic/lowercase.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/uncapitalize.ts b/src/type/intrinsic/uncapitalize.ts index 3024f9672..c9af91f12 100644 --- a/src/type/intrinsic/uncapitalize.ts +++ b/src/type/intrinsic/uncapitalize.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/intrinsic/uppercase.ts b/src/type/intrinsic/uppercase.ts index bde519cc6..34eb578bc 100644 --- a/src/type/intrinsic/uppercase.ts +++ b/src/type/intrinsic/uppercase.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/iterator/index.ts b/src/type/iterator/index.ts index 1a6c6fd01..03694ff57 100644 --- a/src/type/iterator/index.ts +++ b/src/type/iterator/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts index da528a8ae..f2d5fd99f 100644 --- a/src/type/iterator/iterator.ts +++ b/src/type/iterator/iterator.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index 67512251b..8480f6853 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof-from-mapped-result.ts b/src/type/keyof/keyof-from-mapped-result.ts index 0be659752..fb96a9ba7 100644 --- a/src/type/keyof/keyof-from-mapped-result.ts +++ b/src/type/keyof/keyof-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof-property-entries.ts b/src/type/keyof/keyof-property-entries.ts index f8eb236f9..8e2361c1b 100644 --- a/src/type/keyof/keyof-property-entries.ts +++ b/src/type/keyof/keyof-property-entries.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof-property-keys.ts b/src/type/keyof/keyof-property-keys.ts index 25cfd2df3..0130ff934 100644 --- a/src/type/keyof/keyof-property-keys.ts +++ b/src/type/keyof/keyof-property-keys.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts index fbd20df62..9a8b26d30 100644 --- a/src/type/keyof/keyof.ts +++ b/src/type/keyof/keyof.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/literal/index.ts b/src/type/literal/index.ts index a3102cc94..0892b18c8 100644 --- a/src/type/literal/index.ts +++ b/src/type/literal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts index 5a17d8959..32e1eed20 100644 --- a/src/type/literal/literal.ts +++ b/src/type/literal/literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/index.ts b/src/type/mapped/index.ts index 9999cfd84..e5bf772b6 100644 --- a/src/type/mapped/index.ts +++ b/src/type/mapped/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped-key.ts b/src/type/mapped/mapped-key.ts index 42a9a6926..e8dd24f3f 100644 --- a/src/type/mapped/mapped-key.ts +++ b/src/type/mapped/mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped-result.ts b/src/type/mapped/mapped-result.ts index 7faf21d75..29d5a3c82 100644 --- a/src/type/mapped/mapped-result.ts +++ b/src/type/mapped/mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts index 7efdd2139..5dfc725f9 100644 --- a/src/type/mapped/mapped.ts +++ b/src/type/mapped/mapped.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 8527ab135..9ca66adf0 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/module/index.ts b/src/type/module/index.ts index ce46d5a0c..ba33fc039 100644 --- a/src/type/module/index.ts +++ b/src/type/module/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts index 2a663919f..e0fff5e0f 100644 --- a/src/type/module/infer.ts +++ b/src/type/module/infer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/module/module.ts b/src/type/module/module.ts index 276332c3f..afc34fbc1 100644 --- a/src/type/module/module.ts +++ b/src/type/module/module.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/never/index.ts b/src/type/never/index.ts index d8937194f..72210457c 100644 --- a/src/type/never/index.ts +++ b/src/type/never/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/never/never.ts b/src/type/never/never.ts index f2df6451e..fb75cda84 100644 --- a/src/type/never/never.ts +++ b/src/type/never/never.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/not/index.ts b/src/type/not/index.ts index 34f540ae0..49e085461 100644 --- a/src/type/not/index.ts +++ b/src/type/not/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/not/not.ts b/src/type/not/not.ts index eb283dfdf..4ee1ef139 100644 --- a/src/type/not/not.ts +++ b/src/type/not/not.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/null/index.ts b/src/type/null/index.ts index fe04d45b7..a57dac3de 100644 --- a/src/type/null/index.ts +++ b/src/type/null/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/null/null.ts b/src/type/null/null.ts index f953d04ec..f51fccfd7 100644 --- a/src/type/null/null.ts +++ b/src/type/null/null.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/number/index.ts b/src/type/number/index.ts index 2cb1382f0..3d335f9ba 100644 --- a/src/type/number/index.ts +++ b/src/type/number/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/number/number.ts b/src/type/number/number.ts index 0ef585560..8d92776cc 100644 --- a/src/type/number/number.ts +++ b/src/type/number/number.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/object/index.ts b/src/type/object/index.ts index ac0958f80..97eac7b24 100644 --- a/src/type/object/index.ts +++ b/src/type/object/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/object/object.ts b/src/type/object/object.ts index 965ab8295..a0745ee25 100644 --- a/src/type/object/object.ts +++ b/src/type/object/object.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/index.ts b/src/type/omit/index.ts index fc10b7329..40be9e17a 100644 --- a/src/type/omit/index.ts +++ b/src/type/omit/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit-from-mapped-key.ts b/src/type/omit/omit-from-mapped-key.ts index 2a2a4ae94..08df5f145 100644 --- a/src/type/omit/omit-from-mapped-key.ts +++ b/src/type/omit/omit-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit-from-mapped-result.ts b/src/type/omit/omit-from-mapped-result.ts index 5abac5f60..4bc99f029 100644 --- a/src/type/omit/omit-from-mapped-result.ts +++ b/src/type/omit/omit-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts index d52a8b477..c7c866640 100644 --- a/src/type/omit/omit.ts +++ b/src/type/omit/omit.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/index.ts b/src/type/optional/index.ts index ef2545c59..a9da96fea 100644 --- a/src/type/optional/index.ts +++ b/src/type/optional/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/optional-from-mapped-result.ts b/src/type/optional/optional-from-mapped-result.ts index b5bb6966a..fe04a71fb 100644 --- a/src/type/optional/optional-from-mapped-result.ts +++ b/src/type/optional/optional-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts index a481135b7..09a8951d7 100644 --- a/src/type/optional/optional.ts +++ b/src/type/optional/optional.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/parameters/index.ts b/src/type/parameters/index.ts index fc04a3a98..60fa18ad5 100644 --- a/src/type/parameters/index.ts +++ b/src/type/parameters/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts index a7a0daa45..d36ed9a18 100644 --- a/src/type/parameters/parameters.ts +++ b/src/type/parameters/parameters.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/index.ts b/src/type/partial/index.ts index 953dfdbbb..af260312a 100644 --- a/src/type/partial/index.ts +++ b/src/type/partial/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/partial-from-mapped-result.ts b/src/type/partial/partial-from-mapped-result.ts index af60533b8..65f6ba978 100644 --- a/src/type/partial/partial-from-mapped-result.ts +++ b/src/type/partial/partial-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 3cfb48724..52af56364 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/patterns/index.ts b/src/type/patterns/index.ts index 240e6e9ca..619eb64e1 100644 --- a/src/type/patterns/index.ts +++ b/src/type/patterns/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/patterns/patterns.ts b/src/type/patterns/patterns.ts index 9c9e910a2..bb44c60fb 100644 --- a/src/type/patterns/patterns.ts +++ b/src/type/patterns/patterns.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/index.ts b/src/type/pick/index.ts index ac52efa52..8e659b8f4 100644 --- a/src/type/pick/index.ts +++ b/src/type/pick/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick-from-mapped-key.ts b/src/type/pick/pick-from-mapped-key.ts index 86933aa58..df17792e5 100644 --- a/src/type/pick/pick-from-mapped-key.ts +++ b/src/type/pick/pick-from-mapped-key.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick-from-mapped-result.ts b/src/type/pick/pick-from-mapped-result.ts index 7fa74f9c4..75b9dbd04 100644 --- a/src/type/pick/pick-from-mapped-result.ts +++ b/src/type/pick/pick-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts index fc690845f..b7042fb69 100644 --- a/src/type/pick/pick.ts +++ b/src/type/pick/pick.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/promise/index.ts b/src/type/promise/index.ts index ee5555e88..1436cfa71 100644 --- a/src/type/promise/index.ts +++ b/src/type/promise/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts index 4a2488408..cb2311c61 100644 --- a/src/type/promise/promise.ts +++ b/src/type/promise/promise.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly-optional/index.ts b/src/type/readonly-optional/index.ts index 594463f54..8e2481eff 100644 --- a/src/type/readonly-optional/index.ts +++ b/src/type/readonly-optional/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly-optional/readonly-optional.ts b/src/type/readonly-optional/readonly-optional.ts index 5520539e8..d53f74b50 100644 --- a/src/type/readonly-optional/readonly-optional.ts +++ b/src/type/readonly-optional/readonly-optional.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/index.ts b/src/type/readonly/index.ts index c21de9d35..356f84b10 100644 --- a/src/type/readonly/index.ts +++ b/src/type/readonly/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/readonly-from-mapped-result.ts b/src/type/readonly/readonly-from-mapped-result.ts index cd0865aaa..62134e8e9 100644 --- a/src/type/readonly/readonly-from-mapped-result.ts +++ b/src/type/readonly/readonly-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts index 47d9ee0da..bf77263ed 100644 --- a/src/type/readonly/readonly.ts +++ b/src/type/readonly/readonly.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/record/index.ts b/src/type/record/index.ts index 6b0a78848..7ba836747 100644 --- a/src/type/record/index.ts +++ b/src/type/record/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 8e7da8257..265495a66 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/recursive/index.ts b/src/type/recursive/index.ts index f23634a44..b1e22c649 100644 --- a/src/type/recursive/index.ts +++ b/src/type/recursive/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts index 50df4f320..35f36253a 100644 --- a/src/type/recursive/recursive.ts +++ b/src/type/recursive/recursive.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/ref/index.ts b/src/type/ref/index.ts index 606ede084..6daaa62b3 100644 --- a/src/type/ref/index.ts +++ b/src/type/ref/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts index 85e0e125c..dd4f37b21 100644 --- a/src/type/ref/ref.ts +++ b/src/type/ref/ref.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/regexp/index.ts b/src/type/regexp/index.ts index 578ac9777..a2f473c62 100644 --- a/src/type/regexp/index.ts +++ b/src/type/regexp/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts index 6f005e696..7fc24ea27 100644 --- a/src/type/regexp/regexp.ts +++ b/src/type/regexp/regexp.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/format.ts b/src/type/registry/format.ts index 99b893bbe..af818ae19 100644 --- a/src/type/registry/format.ts +++ b/src/type/registry/format.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/index.ts b/src/type/registry/index.ts index f16716fd9..cdb2ac472 100644 --- a/src/type/registry/index.ts +++ b/src/type/registry/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/registry/type.ts b/src/type/registry/type.ts index 35078a856..955357a5b 100644 --- a/src/type/registry/type.ts +++ b/src/type/registry/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/index.ts b/src/type/required/index.ts index e6d6901ff..4570618f2 100644 --- a/src/type/required/index.ts +++ b/src/type/required/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/required-from-mapped-result.ts b/src/type/required/required-from-mapped-result.ts index 147508846..c5c2c30e7 100644 --- a/src/type/required/required-from-mapped-result.ts +++ b/src/type/required/required-from-mapped-result.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 7ae6a234d..25d4c0491 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/rest/index.ts b/src/type/rest/index.ts index e62da1430..f4cfc39eb 100644 --- a/src/type/rest/index.ts +++ b/src/type/rest/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts index d437fa2da..e467ed0c7 100644 --- a/src/type/rest/rest.ts +++ b/src/type/rest/rest.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/return-type/index.ts b/src/type/return-type/index.ts index 799891a64..18d2290dd 100644 --- a/src/type/return-type/index.ts +++ b/src/type/return-type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts index f868d2fcf..17dd44757 100644 --- a/src/type/return-type/return-type.ts +++ b/src/type/return-type/return-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/anyschema.ts b/src/type/schema/anyschema.ts index 908295fc1..5a1d45675 100644 --- a/src/type/schema/anyschema.ts +++ b/src/type/schema/anyschema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/index.ts b/src/type/schema/index.ts index d3ce4acee..8279e8b4c 100644 --- a/src/type/schema/index.ts +++ b/src/type/schema/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/schema/schema.ts b/src/type/schema/schema.ts index 6cba033d7..f4c967ed2 100644 --- a/src/type/schema/schema.ts +++ b/src/type/schema/schema.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/sets/index.ts b/src/type/sets/index.ts index c793d0851..df79d6aff 100644 --- a/src/type/sets/index.ts +++ b/src/type/sets/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/sets/set.ts b/src/type/sets/set.ts index 35b697975..186d3f1c9 100644 --- a/src/type/sets/set.ts +++ b/src/type/sets/set.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/static/index.ts b/src/type/static/index.ts index 94f485cd1..70c29b49d 100644 --- a/src/type/static/index.ts +++ b/src/type/static/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/static/static.ts b/src/type/static/static.ts index 2ba1f1bdc..84999f184 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/string/index.ts b/src/type/string/index.ts index a21b1efbe..48ecacda4 100644 --- a/src/type/string/index.ts +++ b/src/type/string/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/string/string.ts b/src/type/string/string.ts index c1d6061b9..52f2ae113 100644 --- a/src/type/string/string.ts +++ b/src/type/string/string.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbol/index.ts b/src/type/symbol/index.ts index 0ba6f5542..2e308a833 100644 --- a/src/type/symbol/index.ts +++ b/src/type/symbol/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts index 5a65b23b9..e60dae6e0 100644 --- a/src/type/symbol/symbol.ts +++ b/src/type/symbol/symbol.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbols/index.ts b/src/type/symbols/index.ts index 7ee40afb8..627854d7a 100644 --- a/src/type/symbols/index.ts +++ b/src/type/symbols/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/symbols/symbols.ts b/src/type/symbols/symbols.ts index 11530fedb..8b47d1be6 100644 --- a/src/type/symbols/symbols.ts +++ b/src/type/symbols/symbols.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/finite.ts b/src/type/template-literal/finite.ts index 194e15f93..fa5f5ea27 100644 --- a/src/type/template-literal/finite.ts +++ b/src/type/template-literal/finite.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts index 22d87a99a..56a0a0c6a 100644 --- a/src/type/template-literal/generate.ts +++ b/src/type/template-literal/generate.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/index.ts b/src/type/template-literal/index.ts index 8f6a05c3e..c06e4a639 100644 --- a/src/type/template-literal/index.ts +++ b/src/type/template-literal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/parse.ts b/src/type/template-literal/parse.ts index 6a36c013e..1a6620f5d 100644 --- a/src/type/template-literal/parse.ts +++ b/src/type/template-literal/parse.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts index 03ab3c76f..4898583f0 100644 --- a/src/type/template-literal/pattern.ts +++ b/src/type/template-literal/pattern.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts index 3352b10b6..7f4804602 100644 --- a/src/type/template-literal/syntax.ts +++ b/src/type/template-literal/syntax.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts index 7eadc534d..794009f88 100644 --- a/src/type/template-literal/template-literal.ts +++ b/src/type/template-literal/template-literal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/template-literal/union.ts b/src/type/template-literal/union.ts index dba883805..44cae3781 100644 --- a/src/type/template-literal/union.ts +++ b/src/type/template-literal/union.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/transform/index.ts b/src/type/transform/index.ts index 949f78cfe..da318ecf7 100644 --- a/src/type/transform/index.ts +++ b/src/type/transform/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts index eb7c561cd..b89ac5f9b 100644 --- a/src/type/transform/transform.ts +++ b/src/type/transform/transform.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/tuple/index.ts b/src/type/tuple/index.ts index 54d2b14ab..ffe9c5a9e 100644 --- a/src/type/tuple/index.ts +++ b/src/type/tuple/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts index 80b092c79..59caac85f 100644 --- a/src/type/tuple/tuple.ts +++ b/src/type/tuple/tuple.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/index.ts b/src/type/type/index.ts index dbe0febc8..3c82b269a 100644 --- a/src/type/type/index.ts +++ b/src/type/type/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index 101231eb2..a7d6573d4 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/json.ts b/src/type/type/json.ts index afc3650de..c29e046a0 100644 --- a/src/type/type/json.ts +++ b/src/type/type/json.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/type/type.ts b/src/type/type/type.ts index d00686cc6..85ed02710 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/uint8array/index.ts b/src/type/uint8array/index.ts index fc3256037..b81391707 100644 --- a/src/type/uint8array/index.ts +++ b/src/type/uint8array/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts index 09c57ab01..8a8f31845 100644 --- a/src/type/uint8array/uint8array.ts +++ b/src/type/uint8array/uint8array.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/undefined/index.ts b/src/type/undefined/index.ts index 728a1232d..209e971cd 100644 --- a/src/type/undefined/index.ts +++ b/src/type/undefined/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts index efd2a7649..66919ebac 100644 --- a/src/type/undefined/undefined.ts +++ b/src/type/undefined/undefined.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/index.ts b/src/type/union/index.ts index b49ca2d45..bc694b24d 100644 --- a/src/type/union/index.ts +++ b/src/type/union/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-create.ts b/src/type/union/union-create.ts index 856f1ffc4..0989fd967 100644 --- a/src/type/union/union-create.ts +++ b/src/type/union/union-create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-evaluated.ts b/src/type/union/union-evaluated.ts index 8ba1eb21b..b3441294b 100644 --- a/src/type/union/union-evaluated.ts +++ b/src/type/union/union-evaluated.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union-type.ts b/src/type/union/union-type.ts index 68f0cb19e..245791fda 100644 --- a/src/type/union/union-type.ts +++ b/src/type/union/union-type.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/union/union.ts b/src/type/union/union.ts index bacffbd19..e91ba8c5a 100644 --- a/src/type/union/union.ts +++ b/src/type/union/union.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unknown/index.ts b/src/type/unknown/index.ts index 80a7b68e7..6070bc428 100644 --- a/src/type/unknown/index.ts +++ b/src/type/unknown/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts index f91f9613d..4c2406996 100644 --- a/src/type/unknown/unknown.ts +++ b/src/type/unknown/unknown.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unsafe/index.ts b/src/type/unsafe/index.ts index 395fd75bf..6f3db72a5 100644 --- a/src/type/unsafe/index.ts +++ b/src/type/unsafe/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts index 4a52650d8..133a1927d 100644 --- a/src/type/unsafe/unsafe.ts +++ b/src/type/unsafe/unsafe.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/void/index.ts b/src/type/void/index.ts index e6bc946ae..ee941af09 100644 --- a/src/type/void/index.ts +++ b/src/type/void/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/type/void/void.ts b/src/type/void/void.ts index 0672480b9..dec74a5e5 100644 --- a/src/type/void/void.ts +++ b/src/type/void/void.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/assert/assert.ts b/src/value/assert/assert.ts index 07a50db10..57c0eebba 100644 --- a/src/value/assert/assert.ts +++ b/src/value/assert/assert.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/assert/index.ts b/src/value/assert/index.ts index c2b2c88b3..c7adc3b1e 100644 --- a/src/value/assert/index.ts +++ b/src/value/assert/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index a6f9d9891..671050421 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/cast/index.ts b/src/value/cast/index.ts index 575c46a49..9cdbd9556 100644 --- a/src/value/cast/index.ts +++ b/src/value/cast/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/check/check.ts b/src/value/check/check.ts index c5244e22a..f55475c64 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/check/index.ts b/src/value/check/index.ts index 04975a6ba..9fe61e89e 100644 --- a/src/value/check/index.ts +++ b/src/value/check/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts index 41e7f4edb..6a692e347 100644 --- a/src/value/clean/clean.ts +++ b/src/value/clean/clean.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clean/index.ts b/src/value/clean/index.ts index 81d0b0910..89ecef46e 100644 --- a/src/value/clean/index.ts +++ b/src/value/clean/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index 370fdac49..7ea2261b4 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/clone/index.ts b/src/value/clone/index.ts index 22d2dd12b..c6563b3cb 100644 --- a/src/value/clone/index.ts +++ b/src/value/clone/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 9461bd7dd..ca0a96c51 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/convert/index.ts b/src/value/convert/index.ts index 3a88a32f7..9ff2227d1 100644 --- a/src/value/convert/index.ts +++ b/src/value/convert/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 478aab130..b56ab26fa 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/create/index.ts b/src/value/create/index.ts index 706ec0023..adeb52ec6 100644 --- a/src/value/create/index.ts +++ b/src/value/create/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/decode/decode.ts b/src/value/decode/decode.ts index c1cc8a248..0ff9a238a 100644 --- a/src/value/decode/decode.ts +++ b/src/value/decode/decode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/decode/index.ts b/src/value/decode/index.ts index 4372c79ae..6c82cdbe7 100644 --- a/src/value/decode/index.ts +++ b/src/value/decode/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/default/default.ts b/src/value/default/default.ts index e77cd0188..5737c8d91 100644 --- a/src/value/default/default.ts +++ b/src/value/default/default.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/default/index.ts b/src/value/default/index.ts index 62ca5b16f..9619cd8c5 100644 --- a/src/value/default/index.ts +++ b/src/value/default/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/delta/delta.ts b/src/value/delta/delta.ts index fd65c57cc..dc9d41034 100644 --- a/src/value/delta/delta.ts +++ b/src/value/delta/delta.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/delta/index.ts b/src/value/delta/index.ts index 0ea44bee9..64b7dd1d7 100644 --- a/src/value/delta/index.ts +++ b/src/value/delta/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/deref/deref.ts b/src/value/deref/deref.ts index 2f7d594ad..df2d80881 100644 --- a/src/value/deref/deref.ts +++ b/src/value/deref/deref.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/deref/index.ts b/src/value/deref/index.ts index 99003df72..0603b9430 100644 --- a/src/value/deref/index.ts +++ b/src/value/deref/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/encode/encode.ts b/src/value/encode/encode.ts index 357effe66..be52b4a62 100644 --- a/src/value/encode/encode.ts +++ b/src/value/encode/encode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/encode/index.ts b/src/value/encode/index.ts index 4b57d9b32..e4eb54b1c 100644 --- a/src/value/encode/index.ts +++ b/src/value/encode/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/equal/equal.ts b/src/value/equal/equal.ts index 8646fd739..2cceefd82 100644 --- a/src/value/equal/equal.ts +++ b/src/value/equal/equal.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/equal/index.ts b/src/value/equal/index.ts index 7a4ad2de7..17c146250 100644 --- a/src/value/equal/index.ts +++ b/src/value/equal/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 8d5fa25d0..7ad705a9b 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/guard/index.ts b/src/value/guard/index.ts index 439925ae1..d00b7dc7b 100644 --- a/src/value/guard/index.ts +++ b/src/value/guard/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/hash/hash.ts b/src/value/hash/hash.ts index eb86c7c72..b72c039ea 100644 --- a/src/value/hash/hash.ts +++ b/src/value/hash/hash.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/hash/index.ts b/src/value/hash/index.ts index 8f83cb1ac..c7122183b 100644 --- a/src/value/hash/index.ts +++ b/src/value/hash/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/index.ts b/src/value/index.ts index a1b4e46f5..0f91d6e9f 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/mutate/index.ts b/src/value/mutate/index.ts index ef0e67394..d999d10eb 100644 --- a/src/value/mutate/index.ts +++ b/src/value/mutate/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/mutate/mutate.ts b/src/value/mutate/mutate.ts index b5ad3472a..5b7128dc4 100644 --- a/src/value/mutate/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/parse/index.ts b/src/value/parse/index.ts index 3d329f1ce..a5f6ba890 100644 --- a/src/value/parse/index.ts +++ b/src/value/parse/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts index e28d53de8..9654f1d4e 100644 --- a/src/value/parse/parse.ts +++ b/src/value/parse/parse.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/pointer/index.ts b/src/value/pointer/index.ts index c64016d47..f0828dcb6 100644 --- a/src/value/pointer/index.ts +++ b/src/value/pointer/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/pointer/pointer.ts b/src/value/pointer/pointer.ts index 6b8d7aa6f..aa1b7e39c 100644 --- a/src/value/pointer/pointer.ts +++ b/src/value/pointer/pointer.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 013b0abd1..f9a120ae4 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 3ac8c54c8..1852c5ffe 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 5a0d785c3..549a39e9e 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/transform/index.ts b/src/value/transform/index.ts index e3bd75d2a..1759d820d 100644 --- a/src/value/transform/index.ts +++ b/src/value/transform/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/value/index.ts b/src/value/value/index.ts index eabf61e7b..af4861374 100644 --- a/src/value/value/index.ts +++ b/src/value/value/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/value/value/value.ts b/src/value/value/value.ts index ab0e06b3d..d1d5f6319 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/cjs/build.ts b/task/build/cjs/build.ts index 65f1c8410..061043cea 100644 --- a/task/build/cjs/build.ts +++ b/task/build/cjs/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/cjs/compile.ts b/task/build/cjs/compile.ts index bd089ec21..c70aa0d6f 100644 --- a/task/build/cjs/compile.ts +++ b/task/build/cjs/compile.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/esm/build.ts b/task/build/esm/build.ts index 04a0eb450..ff1c5db63 100644 --- a/task/build/esm/build.ts +++ b/task/build/esm/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/esm/compile.ts b/task/build/esm/compile.ts index 08e0129e5..35a9d3722 100644 --- a/task/build/esm/compile.ts +++ b/task/build/esm/compile.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/esm/convert-to-esm.ts b/task/build/esm/convert-to-esm.ts index 9878a4fe6..21df0eac6 100644 --- a/task/build/esm/convert-to-esm.ts +++ b/task/build/esm/convert-to-esm.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/index.ts b/task/build/index.ts index e55ce6705..2d9d295d9 100644 --- a/task/build/index.ts +++ b/task/build/index.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/notices/remove-notices.ts b/task/build/notices/remove-notices.ts index 96b4dfb2d..3d101ae46 100644 --- a/task/build/notices/remove-notices.ts +++ b/task/build/notices/remove-notices.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/package/build.ts b/task/build/package/build.ts index 0e05ede91..305ebbd1a 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/package/create-package-json-redirect.ts b/task/build/package/create-package-json-redirect.ts index f586763f3..298477082 100644 --- a/task/build/package/create-package-json-redirect.ts +++ b/task/build/package/create-package-json-redirect.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/task/build/package/create-package-json.ts b/task/build/package/create-package-json.ts index 3fc0a186e..863e922c5 100644 --- a/task/build/package/create-package-json.ts +++ b/task/build/package/create-package-json.ts @@ -4,7 +4,7 @@ The MIT License (MIT) -Copyright (c) 2017-2024 Haydn Paterson (sinclair) +Copyright (c) 2017-2025 Haydn Paterson (sinclair) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 870ab417fb69775e3b490d4457aa5963b6f16673 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 16 Jan 2025 17:20:57 +0900 Subject: [PATCH 328/369] Revision 0.34.14 (#1140) * Fix Compiler Promise Check * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/compiler/compiler.ts | 2 +- src/value/guard/guard.ts | 16 ++++++++-------- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 3c68b589d..97f0a53fb 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.14](https://github.com/sinclairzx81/typebox/pull/1140) + - [1139](https://github.com/sinclairzx81/typebox/issues/1139) Update TypeCompiler Check for Promise (use instanceof Promise over Thenable check) - [Revision 0.34.13](https://github.com/sinclairzx81/typebox/pull/1124) - Pre emptive fix for TypeScript 5.8.0-nightly to resolve symbol narrowing on Convert. - [Revision 0.34.12](https://github.com/sinclairzx81/typebox/pull/1120) diff --git a/package-lock.json b/package-lock.json index 82a40a27d..830c835ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.13", + "version": "0.34.14", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.13", + "version": "0.34.14", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e40785943..1c2dafe03 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.13", + "version": "0.34.14", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 706667ee7..eb5c67c40 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -384,7 +384,7 @@ export namespace TypeCompiler { } } function* FromPromise(schema: TPromise, references: TSchema[], value: string): IterableIterator { - yield `(typeof value === 'object' && typeof ${value}.then === 'function')` + yield `${value} instanceof Promise` } function* FromRecord(schema: TRecord, references: TSchema[], value: string): IterableIterator { yield Policy.IsRecordLike(value) diff --git a/src/value/guard/guard.ts b/src/value/guard/guard.ts index 7ad705a9b..56410e709 100644 --- a/src/value/guard/guard.ts +++ b/src/value/guard/guard.ts @@ -50,18 +50,18 @@ export type TypedArrayType = // -------------------------------------------------------------------------- /** Returns true if this value is an async iterator */ export function IsAsyncIterator(value: unknown): value is AsyncIterableIterator { - return IsObject(value) && Symbol.asyncIterator in value + return IsObject(value) && globalThis.Symbol.asyncIterator in value } /** Returns true if this value is an iterator */ export function IsIterator(value: unknown): value is IterableIterator { - return IsObject(value) && Symbol.iterator in value + return IsObject(value) && globalThis.Symbol.iterator in value } // -------------------------------------------------------------------------- // Object Instances // -------------------------------------------------------------------------- /** Returns true if this value is not an instance of a class */ export function IsStandardObject(value: unknown): value is ObjectType { - return IsObject(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) + return IsObject(value) && (globalThis.Object.getPrototypeOf(value) === Object.prototype || globalThis.Object.getPrototypeOf(value) === null) } /** Returns true if this value is an instance of a class */ export function IsInstanceObject(value: unknown): value is ObjectType { @@ -72,11 +72,11 @@ export function IsInstanceObject(value: unknown): value is ObjectType { // -------------------------------------------------------------------------- /** Returns true if this value is a Promise */ export function IsPromise(value: unknown): value is Promise { - return value instanceof Promise + return value instanceof globalThis.Promise } /** Returns true if this value is a Date */ export function IsDate(value: unknown): value is Date { - return value instanceof Date && Number.isFinite(value.getTime()) + return value instanceof Date && globalThis.Number.isFinite(value.getTime()) } /** Returns true if this value is an instance of Map */ export function IsMap(value: unknown): value is Map { @@ -92,7 +92,7 @@ export function IsRegExp(value: unknown): value is RegExp { } /** Returns true if this value is a typed array */ export function IsTypedArray(value: unknown): value is TypedArrayType { - return ArrayBuffer.isView(value) + return globalThis.ArrayBuffer.isView(value) } /** Returns true if the value is a Int8Array */ export function IsInt8Array(value: unknown): value is Int8Array { @@ -154,7 +154,7 @@ export function IsObject(value: unknown): value is ObjectType { } /** Returns true if this value is an array, but not a typed array */ export function IsArray(value: unknown): value is ArrayType { - return Array.isArray(value) && !ArrayBuffer.isView(value) + return globalThis.Array.isArray(value) && !globalThis.ArrayBuffer.isView(value) } /** Returns true if this value is an undefined */ export function IsUndefined(value: unknown): value is undefined { @@ -174,7 +174,7 @@ export function IsNumber(value: unknown): value is number { } /** Returns true if this value is an integer */ export function IsInteger(value: unknown): value is number { - return Number.isInteger(value) + return globalThis.Number.isInteger(value) } /** Returns true if this value is bigint */ export function IsBigInt(value: unknown): value is bigint { From 9ea7eb0493c1f483322d882d84b7f4e8950fc3a5 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 30 Jan 2025 13:55:26 +0900 Subject: [PATCH 329/369] Documentation - Update Ecosystem --- readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.md b/readme.md index 1d44e671f..b33def232 100644 --- a/readme.md +++ b/readme.md @@ -1779,7 +1779,6 @@ The following is a list of community packages that offer general tooling, extend | [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | -| [typebox-validators](https://github.com/jtlapp/typebox-validators) | Advanced validators supporting discriminated and heterogeneous unions | From e772a4f3c6958a6358f48617b49dd72d16a796ad Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 30 Jan 2025 14:25:13 +0900 Subject: [PATCH 330/369] Revision 0.34.15 (#1148) * Use Math.Trunc to avoid 32-bit coersion for Integer * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/value/convert/convert.ts | 2 +- test/runtime/value/convert/integer.ts | 23 +++++++++++++++++++++++ 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 97f0a53fb..703528ec2 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.15](https://github.com/sinclairzx81/typebox/pull/1148) + - [1147](https://github.com/sinclairzx81/typebox/issues/1147) Fix incorrect truncation for integers that exceed 32-bit values in Value.Convert - [Revision 0.34.14](https://github.com/sinclairzx81/typebox/pull/1140) - [1139](https://github.com/sinclairzx81/typebox/issues/1139) Update TypeCompiler Check for Promise (use instanceof Promise over Thenable check) - [Revision 0.34.13](https://github.com/sinclairzx81/typebox/pull/1124) diff --git a/package-lock.json b/package-lock.json index 830c835ee..b2abe8da4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.14", + "version": "0.34.15", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.14", + "version": "0.34.15", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 1c2dafe03..734199b76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.14", + "version": "0.34.15", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index ca0a96c51..752ba37a4 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -125,7 +125,7 @@ function TryConvertNumber(value: unknown) { return IsStringNumeric(value) ? parseFloat(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value } function TryConvertInteger(value: unknown) { - return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? value | 0 : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value + return IsStringNumeric(value) ? parseInt(value) : IsNumber(value) ? Math.trunc(value) : IsValueTrue(value) ? 1 : IsValueFalse(value) ? 0 : value } function TryConvertNull(value: unknown) { return IsString(value) && value.toLowerCase() === 'null' ? null : value diff --git a/test/runtime/value/convert/integer.ts b/test/runtime/value/convert/integer.ts index 8f17ff4c1..abfc59792 100644 --- a/test/runtime/value/convert/integer.ts +++ b/test/runtime/value/convert/integer.ts @@ -76,4 +76,27 @@ describe('value/convert/Integer', () => { const result = Value.Convert(Type.Integer(), value) Assert.IsEqual(result, []) }) + // ---------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1147 + // ---------------------------------------------------------- + it('Should convert large Integer 1', () => { + const N = 1738213389080 + const R = Value.Convert(Type.Integer(), N) + Assert.IsEqual(R, N) + }) + it('Should convert large Integer 2', () => { + const N = 1738213389080.5555 + const R = Value.Convert(Type.Integer(), N) + Assert.IsEqual(R, 1738213389080) + }) + it('Should convert large Integer 3', () => { + const N = '1738213389080' + const R = Value.Convert(Type.Integer(), N) + Assert.IsEqual(R, 1738213389080) + }) + it('Should convert large Integer 3', () => { + const N = '1738213389080.555' + const R = Value.Convert(Type.Integer(), N) + Assert.IsEqual(R, 1738213389080) + }) }) From b5e1d146ed2547eaa79a2dac7ccd4e559773ef41 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 6 Feb 2025 18:48:30 +0900 Subject: [PATCH 331/369] Revision 0.34.16 (#1156) * Export Parsing Infrastructure * TypeScript 5.7.3 * ChangeLog * Documentation --- changelog/0.34.0.md | 2 + example/index.ts | 4 +- hammer.mjs | 2 +- package-lock.json | 18 +- package.json | 4 +- readme.md | 246 ++++-------- src/{syntax/parsebox => parser}/index.ts | 0 .../parsebox => parser}/runtime/guard.ts | 0 .../parsebox => parser}/runtime/index.ts | 0 .../parsebox => parser}/runtime/module.ts | 0 .../parsebox => parser}/runtime/parse.ts | 0 .../parsebox => parser}/runtime/token.ts | 0 .../parsebox => parser}/runtime/types.ts | 0 .../parsebox => parser}/static/index.ts | 0 .../parsebox => parser}/static/parse.ts | 0 .../parsebox => parser}/static/token.ts | 0 .../parsebox => parser}/static/types.ts | 0 src/syntax/index.ts | 2 +- src/syntax/parse.ts | 60 --- src/syntax/runtime.ts | 374 ++++++------------ src/syntax/static.ts | 353 +++++------------ src/syntax/syntax.ts | 105 +++++ src/tsconfig.json | 2 +- .../constructor-parameters.ts | 14 +- src/type/instance-type/instance-type.ts | 19 +- src/type/parameters/parameters.ts | 15 +- src/type/return-type/return-type.ts | 19 +- src/type/type/javascript.ts | 8 +- task/build/package/build.ts | 2 +- test/runtime/syntax/syntax.ts | 235 ++++------- tsconfig.json | 1 + 31 files changed, 549 insertions(+), 936 deletions(-) rename src/{syntax/parsebox => parser}/index.ts (100%) rename src/{syntax/parsebox => parser}/runtime/guard.ts (100%) rename src/{syntax/parsebox => parser}/runtime/index.ts (100%) rename src/{syntax/parsebox => parser}/runtime/module.ts (100%) rename src/{syntax/parsebox => parser}/runtime/parse.ts (100%) rename src/{syntax/parsebox => parser}/runtime/token.ts (100%) rename src/{syntax/parsebox => parser}/runtime/types.ts (100%) rename src/{syntax/parsebox => parser}/static/index.ts (100%) rename src/{syntax/parsebox => parser}/static/parse.ts (100%) rename src/{syntax/parsebox => parser}/static/token.ts (100%) rename src/{syntax/parsebox => parser}/static/types.ts (100%) delete mode 100644 src/syntax/parse.ts create mode 100644 src/syntax/syntax.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 703528ec2..b3645d6e9 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.16](https://github.com/sinclairzx81/typebox/pull/1156) + - Export TypeBox String Parsing Infrastructure - [Revision 0.34.15](https://github.com/sinclairzx81/typebox/pull/1148) - [1147](https://github.com/sinclairzx81/typebox/issues/1147) Fix incorrect truncation for integers that exceed 32-bit values in Value.Convert - [Revision 0.34.14](https://github.com/sinclairzx81/typebox/pull/1140) diff --git a/example/index.ts b/example/index.ts index 9d228684a..ff155a02a 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,8 +1,8 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValuePointer } from '@sinclair/typebox/value' -import { Parse, StaticParseAsType } from '@sinclair/typebox/syntax' import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' +import { Syntax } from '@sinclair/typebox/syntax' // ----------------------------------------------------------- // Create: Type @@ -22,7 +22,7 @@ console.log(T) // Parse: Type // ----------------------------------------------------------- -const S = Parse({ T }, `{ x: T, y: T, z: T }`) +const S = Syntax({ T }, `{ x: T, y: T, z: T }`) type S = Static diff --git a/hammer.mjs b/hammer.mjs index 26f758006..cc8bd42f6 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -35,7 +35,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', '5.7.2', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index b2abe8da4..373aed4f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.15", + "version": "0.34.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.15", + "version": "0.34.16", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.7.2" + "typescript": "^5.7.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1576,9 +1576,9 @@ } }, "node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2738,9 +2738,9 @@ "dev": true }, "typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 734199b76..1bf33bd7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.15", + "version": "0.34.16", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -38,6 +38,6 @@ "ajv-formats": "^2.1.1", "mocha": "^10.4.0", "prettier": "^2.7.1", - "typescript": "^5.7.2" + "typescript": "^5.7.3" } } diff --git a/readme.md b/readme.md index b33def232..37fd0b36e 100644 --- a/readme.md +++ b/readme.md @@ -77,10 +77,6 @@ License MIT - [Transform](#types-transform) - [Guard](#types-guard) - [Unsafe](#types-unsafe) -- [Syntax](#syntax) - - [Parse](#syntax-parse) - - [Static](#syntax-static) - - [Limits](#syntax-limits) - [Values](#values) - [Assert](#values-assert) - [Create](#values-create) @@ -100,6 +96,11 @@ License MIT - [Errors](#values-errors) - [Mutate](#values-mutate) - [Pointer](#values-pointer) +- [Syntax](#syntax) + - [Type](#syntax-type) + - [Options](#syntax-options) + - [Parameters](#syntax-parameters) + - [Generics](#syntax-generics) - [TypeRegistry](#typeregistry) - [Type](#typeregistry-type) - [Format](#typeregistry-format) @@ -1044,162 +1045,6 @@ if(TypeGuard.IsString(T)) { } ``` - - -## Syntax Types - -TypeBox has support for parsing TypeScript type annotations directly into TypeBox types. This feature supports both runtime and static parsing, with TypeBox implementing TypeScript parsers within the TypeScript type system itself. Syntax Types use the TypeBox Json Schema representations as an AST target for TypeScript types, providing a direct mapping between TypeScript syntax and Json Schema. Syntax Types are offered as a syntactical frontend to the Standard Type Builder API. - -This feature is available via optional import. - -```typescript -import { Parse } from '@sinclair/typebox/syntax' -``` - - - -### Parse - -Use the Parse function to transform a TypeScript type annotation into a TypeBox type. This function will return the parsed TypeBox type or undefined on error. - -```typescript -const A = Parse('string') // const A: TString - -const B = Parse('[1, 2, 3]') // const B: TTuple<[ - // TLiteral<1>, - // TLiteral<2>, - // TLiteral<3> - // ]> - -const C = Parse(`{ x: number, y: number }`) // const C: TObject<{ - // x: TNumber - // y: TNumber - // }> -``` - -Syntax Types can compose with Standard Types created via the Type Builder API - -```typescript -const T = Type.Object({ // const T: TObject<{ - x: Parse('number'), // x: TNumber, - y: Parse('string'), // y: TString, - z: Parse('boolean') // z: TBoolean -}) // }> -``` - -Standard Types can also be passed to and referenced within Syntax Types. - -```typescript -const X = Type.Number() -const Y = Type.String() -const Z = Type.Boolean() - -const T = Parse({ X, Y, Z }, `{ - x: X, - y: Y, - z: Z -}`) -``` - -Syntax Types also support Module parsing. - -```typescript -const Foo = Parse(`module Foo { - - export type PartialUser = Pick & Partial> - - export interface User { - id: string - name: string - email: string - } - -}`) - -const PartialUser = Foo.Import('PartialUser') // TImport<{...}, 'PartialUser'> - -type PartialUser = Static // type PartialUser = { - // id: string, - // } & { - // name?: string, - // email?: string, - // } -``` - - - -### Static - -Syntax Types provide two Static types specific to inferring TypeBox and TypeScript types from strings. - -```typescript -import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax' - -// Will infer as a TSchema - -type S = StaticParseAsSchema<{}, '{ x: number }'> // type S: TObject<{ - // x: TNumber - // }> - -// Will infer as a type - -type T = StaticParseAsType<{}, '{ x: number }'> // type T = { - // x: number - // } -``` - - - - -### Limitations - -TypeBox parses TypeScript types directly within the TypeScript type system. This process does come with an inference cost, which scales with the size and complexity of the types being parsed. Although TypeBox strives to optimize Syntax Types, users should be aware of the following structures: - -```typescript -// Excessively wide structures will result in instantiation limits exceeding -const A = Parse(`[ - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, - 0, 1, 2, 3, 4, 5, 6, 7, -]`) - -// Excessively nested structures will result in instantiation limits exceeding -const B = Parse(`{ - x: { - y: { - z: { - w: 1 <-- Type instantiation is excessively deep and possibly infinite. - } - } - } -}`) -``` - -If Syntax Types exceed TypeScript's instantiation limits, users are advised to fall back to the Standard Type Builder API. Alternatively, TypeBox offers a `ParseOnly` function that parses the TypeScript syntax at runtime without statically inferring the schema. - -```typescript -import { ParseOnly } from '@sinclair/typebox/syntax' - -// const A: TSchema | undefined - -const A = ParseOnly(`{ - x: { - y: { - z: { - w: 1 - } - } - } -}`) -``` - -For more information on static parsing, refer to the [ParseBox](https://github.com/sinclairzx81/parsebox) project. - ## Values @@ -1490,6 +1335,87 @@ ValuePointer.Set(A, '/y', 1) // A' = { x: 1, y: 1, z: 0 ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1 } ``` + + + + +## Syntax Types + +TypeBox provides optional support for runtime and type level parsing from TypeScript syntax. + +```typescript +import { Syntax } from '@sinclair/typebox/syntax' +``` + + + +### Type + +Use the Syntax function to create TypeBox type from TypeScript syntax. + +```typescript +const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{ + // x: TNumber + // y: TNumber + // }> +``` + + + +### Options + +Options can be passed to types on the last parameter + +```typescript +const T = Syntax(`number`, { // const T = { + minimum: 0, // type: 'number', + maximum: 10 // minimum: 0, +}) // maximum: 10 + // } +``` + + + +### Parameters + +Syntax types can be parameterized to accept exterior types. + +```typescript +const T = Syntax('number') + +const S = Syntax({ T }, `{ x: T, y: T, z: T }`) // const S: TObject<{ + // x: TNumber, + // y: TNumber, + // z: TNumber + // }> +``` + + + +### Generics + +Generic syntax types can be created using parameterized types. + +```typescript +// Generic Syntax Type + +const Vector = (T: T) => Syntax({ T: Syntax(T) }, `{ + x: T, + y: T, + z: T +}`) + + +// Instanced Generic Syntax Type + +const NumberVector = Vector('number') // const NumberVector: TObject<{ + // x: TNumber, + // y: TNumber, + // z: TNumber + // }> +``` + + ## TypeRegistry diff --git a/src/syntax/parsebox/index.ts b/src/parser/index.ts similarity index 100% rename from src/syntax/parsebox/index.ts rename to src/parser/index.ts diff --git a/src/syntax/parsebox/runtime/guard.ts b/src/parser/runtime/guard.ts similarity index 100% rename from src/syntax/parsebox/runtime/guard.ts rename to src/parser/runtime/guard.ts diff --git a/src/syntax/parsebox/runtime/index.ts b/src/parser/runtime/index.ts similarity index 100% rename from src/syntax/parsebox/runtime/index.ts rename to src/parser/runtime/index.ts diff --git a/src/syntax/parsebox/runtime/module.ts b/src/parser/runtime/module.ts similarity index 100% rename from src/syntax/parsebox/runtime/module.ts rename to src/parser/runtime/module.ts diff --git a/src/syntax/parsebox/runtime/parse.ts b/src/parser/runtime/parse.ts similarity index 100% rename from src/syntax/parsebox/runtime/parse.ts rename to src/parser/runtime/parse.ts diff --git a/src/syntax/parsebox/runtime/token.ts b/src/parser/runtime/token.ts similarity index 100% rename from src/syntax/parsebox/runtime/token.ts rename to src/parser/runtime/token.ts diff --git a/src/syntax/parsebox/runtime/types.ts b/src/parser/runtime/types.ts similarity index 100% rename from src/syntax/parsebox/runtime/types.ts rename to src/parser/runtime/types.ts diff --git a/src/syntax/parsebox/static/index.ts b/src/parser/static/index.ts similarity index 100% rename from src/syntax/parsebox/static/index.ts rename to src/parser/static/index.ts diff --git a/src/syntax/parsebox/static/parse.ts b/src/parser/static/parse.ts similarity index 100% rename from src/syntax/parsebox/static/parse.ts rename to src/parser/static/parse.ts diff --git a/src/syntax/parsebox/static/token.ts b/src/parser/static/token.ts similarity index 100% rename from src/syntax/parsebox/static/token.ts rename to src/parser/static/token.ts diff --git a/src/syntax/parsebox/static/types.ts b/src/parser/static/types.ts similarity index 100% rename from src/syntax/parsebox/static/types.ts rename to src/parser/static/types.ts diff --git a/src/syntax/index.ts b/src/syntax/index.ts index 8a2445bfb..d7fc819f7 100644 --- a/src/syntax/index.ts +++ b/src/syntax/index.ts @@ -26,4 +26,4 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -export * from './parse' +export * from './syntax' diff --git a/src/syntax/parse.ts b/src/syntax/parse.ts deleted file mode 100644 index d58922e7b..000000000 --- a/src/syntax/parse.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/syntax - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '../type/index' -import { Static } from './parsebox/index' -import { Module } from './runtime' -import { Main } from './static' - -/** `[Syntax]` Infers a TypeBox type from TypeScript syntax. */ -export type StaticParseAsSchema, Code extends string> = Static.Parse[0] - -/** `[Syntax]` Infers a TypeScript type from TypeScript syntax. */ -export type StaticParseAsType, Code extends string> = StaticParseAsSchema extends infer Type extends Types.TSchema ? Types.StaticDecode : undefined - -/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ -export function Parse, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): StaticParseAsSchema -/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ -export function Parse(code: Code, options?: Types.SchemaOptions): StaticParseAsSchema<{}, Code> -/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */ -export function Parse(...args: any[]): never { - return ParseOnly.apply(null, args as never) as never -} - -/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ -export function ParseOnly, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined -/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax */ -export function ParseOnly(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined -/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */ -export function ParseOnly(...args: any[]): Types.TSchema | undefined { - const withContext = typeof args[0] === 'string' ? false : true - const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] - const type = Module.Parse('Main', code, context)[0] as Types.TSchema | undefined - // Note: Parsing may return either a ModuleInstance or Type. We only apply options on the Type. - return Types.KindGuard.IsSchema(type) ? Types.CloneType(type, options) : type -} diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 2f5801f20..8a62f2014 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Runtime } from './parsebox/index' -import * as Types from '../type/index' +import { Runtime } from '../parser/index' +import * as t from '../type/index' // ------------------------------------------------------------------ // Tokens @@ -62,164 +62,40 @@ function DestructureRight(values: T[]): [T[], T | undefined] { // ------------------------------------------------------------------ // Deref // ------------------------------------------------------------------ -const Deref = (context: Types.TProperties, key: string): Types.TSchema => { - return key in context ? context[key] : Types.Ref(key) +const Deref = (context: t.TProperties, key: string): t.TSchema => { + return key in context ? context[key] : t.Ref(key) } // ------------------------------------------------------------------ -// ExportModifier -// ------------------------------------------------------------------ -// prettier-ignore -const ExportModifierMapping = (values: unknown[]) => { - return values.length === 1 -} -// prettier-ignore -const ExportModifier = Runtime.Union([ - Runtime.Tuple([Runtime.Const('export')]), Runtime.Tuple([]) -], ExportModifierMapping) - -// ------------------------------------------------------------------ -// TypeAliasDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -const TypeAliasDeclarationMapping = (_Export: boolean, _Keyword: 'type', Ident: string, _Equals: typeof Equals, Type: Types.TSchema) => { - return { [Ident]: Type } -} -// prettier-ignore -const TypeAliasDeclaration = Runtime.Tuple([ - Runtime.Ref('ExportModifier'), - Runtime.Const('type'), - Runtime.Ident(), - Runtime.Const(Equals), - Runtime.Ref('Type') -], value => TypeAliasDeclarationMapping(...value)) - -// ------------------------------------------------------------------ -// HeritageList -// ------------------------------------------------------------------ -// prettier-ignore (note, heritage list should disallow trailing comma) -const HeritageListDelimiter = Runtime.Union([Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), Runtime.Tuple([Runtime.Const(Comma)])]) -// prettier-ignore -const HeritageListMapping = (values: string[], context: Types.TProperties): Types.TSchema[] => { - return ( - values.length === 3 ? [Deref(context, values[0]), ...values[2] as never] : - values.length === 1 ? [Deref(context, values[0])] : - [] - ) as never -} -// prettier-ignore -const HeritageList = Runtime.Union([ - Runtime.Tuple([Runtime.Ident(), HeritageListDelimiter, Runtime.Ref('HeritageList')]), - Runtime.Tuple([Runtime.Ident()]), - Runtime.Tuple([]) -], HeritageListMapping) -// ------------------------------------------------------------------ -// Heritage -// ------------------------------------------------------------------ -// prettier-ignore -const HeritageMapping = (values: unknown[]): unknown[] => { - return (values.length === 2 ? values[1] : []) as never -} -// prettier-ignore -const Heritage = Runtime.Union([ - Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('HeritageList')]), - Runtime.Tuple([]) -], HeritageMapping) -// ------------------------------------------------------------------ -// InterfaceDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -const InterfaceDeclarationMapping = (_0: boolean, _1: 'interface', Ident: string, Heritage: Types.TRef[], _4: typeof LBrace, Properties: Types.TProperties, _6: typeof RBrace) => { - return { [Ident]: Types.Intersect([...Heritage, Types.Object(Properties)]) } -} -// prettier-ignore -const InterfaceDeclaration = Runtime.Tuple([ - Runtime.Ref('ExportModifier'), - Runtime.Const('interface'), - Runtime.Ident(), - Runtime.Ref('Heritage'), - Runtime.Const(LBrace), - Runtime.Ref('Properties'), - Runtime.Const(RBrace), -], values => InterfaceDeclarationMapping(...values)) - -// ------------------------------------------------------------------ -// ModuleType -// ------------------------------------------------------------------ -// prettier-ignore -const ModuleType = Runtime.Union([ - Runtime.Ref('InterfaceDeclaration'), - Runtime.Ref('TypeAliasDeclaration') -]) -// ------------------------------------------------------------------ -// ModuleProperties -// ------------------------------------------------------------------ -// prettier-ignore -const ModulePropertiesDelimiter = Runtime.Union([ - Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]), - Runtime.Tuple([Runtime.Const(SemiColon)]), - Runtime.Tuple([Runtime.Const(Newline)]), -]) -// prettier-ignore -const ModulePropertiesMapping = (values: unknown[]): Types.TProperties => { - return ( - values.length === 3 ? { ...values[0] as Types.TProperties, ...values[2] as Types.TProperties }: - values.length === 1 ? values[0] as Types.TProperties : - {} as Types.TProperties - ) -} -// prettier-ignore -const ModuleProperties = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('ModuleType'), ModulePropertiesDelimiter, Runtime.Ref('ModuleProperties')]), - Runtime.Tuple([Runtime.Ref('ModuleType')]), - Runtime.Tuple([]), -], ModulePropertiesMapping) -// ------------------------------------------------------------------ -// ModuleDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -const ModuleIdentifier = Runtime.Union([ - Runtime.Tuple([Runtime.Ident()]), - Runtime.Tuple([]) -]) -// prettier-ignore -const ModuleDeclarationMapping = (_1: boolean, _2: 'module', _Ident: string[], _3: typeof LBrace, Properties: Types.TProperties, _5: typeof RBrace) => { - return Types.Module(Properties) -} -// prettier-ignore -const ModuleDeclaration = Runtime.Tuple([ - Runtime.Ref('ExportModifier'), Runtime.Const('module'), ModuleIdentifier, Runtime.Const(LBrace), Runtime.Ref('ModuleProperties'), Runtime.Const(RBrace) -], values => ModuleDeclarationMapping(...values)) -// ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ // prettier-ignore -const Reference = Runtime.Ident((value, context: Types.TProperties) => Deref(context, value)) +const Reference = Runtime.Ident((value, context: t.TProperties) => Deref(context, value)) // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ // prettier-ignore const Literal = Runtime.Union([ - Runtime.Union([Runtime.Const('true'), Runtime.Const('false')], value => Types.Literal(value === 'true')), - Runtime.Number(value => Types.Literal(parseFloat(value))), - Runtime.String([SingleQuote, DoubleQuote, Tilde], value => Types.Literal(value)) + Runtime.Union([Runtime.Const('true'), Runtime.Const('false')], value => t.Literal(value === 'true')), + Runtime.Number(value => t.Literal(parseFloat(value))), + Runtime.String([SingleQuote, DoubleQuote, Tilde], value => t.Literal(value)) ]) // ------------------------------------------------------------------ // Keyword // ------------------------------------------------------------------ // prettier-ignore const Keyword = Runtime.Union([ - Runtime.Const('any', Runtime.As(Types.Any())), - Runtime.Const('bigint', Runtime.As(Types.BigInt())), - Runtime.Const('boolean', Runtime.As(Types.Boolean())), - Runtime.Const('integer', Runtime.As(Types.Integer())), - Runtime.Const('never', Runtime.As(Types.Never())), - Runtime.Const('null', Runtime.As(Types.Null())), - Runtime.Const('number', Runtime.As(Types.Number())), - Runtime.Const('string', Runtime.As(Types.String())), - Runtime.Const('symbol', Runtime.As(Types.Symbol())), - Runtime.Const('undefined', Runtime.As(Types.Undefined())), - Runtime.Const('unknown', Runtime.As(Types.Unknown())), - Runtime.Const('void', Runtime.As(Types.Void())), + Runtime.Const('any', Runtime.As(t.Any())), + Runtime.Const('bigint', Runtime.As(t.BigInt())), + Runtime.Const('boolean', Runtime.As(t.Boolean())), + Runtime.Const('integer', Runtime.As(t.Integer())), + Runtime.Const('never', Runtime.As(t.Never())), + Runtime.Const('null', Runtime.As(t.Null())), + Runtime.Const('number', Runtime.As(t.Number())), + Runtime.Const('string', Runtime.As(t.String())), + Runtime.Const('symbol', Runtime.As(t.Symbol())), + Runtime.Const('undefined', Runtime.As(t.Undefined())), + Runtime.Const('unknown', Runtime.As(t.Unknown())), + Runtime.Const('void', Runtime.As(t.Void())), ]) // ------------------------------------------------------------------ // KeyOf @@ -314,54 +190,54 @@ const Base = Runtime.Union([ // Factor // ------------------------------------------------------------------ // prettier-ignore -const FactorExtends = (Type: Types.TSchema, Extends: Types.TSchema[]) => { +const FactorExtends = (Type: t.TSchema, Extends: t.TSchema[]) => { return Extends.length === 3 - ? Types.Extends(Type, Extends[0], Extends[1], Extends[2]) + ? t.Extends(Type, Extends[0], Extends[1], Extends[2]) : Type } // prettier-ignore -const FactorIndexArray = (Type: Types.TSchema, IndexArray: unknown[]): Types.TSchema => { - const [Left, Right] = DestructureRight(IndexArray) as [unknown[], Types.TSchema[]] +const FactorIndexArray = (Type: t.TSchema, IndexArray: unknown[]): t.TSchema => { + const [Left, Right] = DestructureRight(IndexArray) as [unknown[], t.TSchema[]] return ( - !Types.ValueGuard.IsUndefined(Right) ? ( + !t.ValueGuard.IsUndefined(Right) ? ( // note: Indexed types require reimplementation to replace `[number]` indexers - Right.length === 1 ? Types.Index(FactorIndexArray(Type, Left), Right[0]) as never : - Right.length === 0 ? Types.Array(FactorIndexArray(Type, Left)) : - Types.Never() + Right.length === 1 ? t.Index(FactorIndexArray(Type, Left), Right[0]) as never : + Right.length === 0 ? t.Array(FactorIndexArray(Type, Left)) : + t.Never() ) : Type ) } // prettier-ignore -const FactorMapping = (KeyOf: boolean, Type: Types.TSchema, IndexArray: unknown[], Extends: Types.TSchema[]) => { +const FactorMapping = (KeyOf: boolean, Type: t.TSchema, IndexArray: unknown[], Extends: t.TSchema[]) => { return KeyOf - ? FactorExtends(Types.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) + ? FactorExtends(t.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) : FactorExtends(FactorIndexArray(Type, IndexArray), Extends) } // prettier-ignore const Factor = Runtime.Tuple([ Runtime.Ref('KeyOf'), - Runtime.Ref('Base'), + Runtime.Ref('Base'), Runtime.Ref('IndexArray'), - Runtime.Ref('Extends') + Runtime.Ref('Extends') ], values => FactorMapping(...values)) // ------------------------------------------------------------------ // Expr // ------------------------------------------------------------------ // prettier-ignore -function ExprBinaryMapping(Left: Types.TSchema, Rest: unknown[]): Types.TSchema { +function ExprBinaryMapping(Left: t.TSchema, Rest: unknown[]): t.TSchema { return ( Rest.length === 3 ? (() => { - const [Operator, Right, Next] = Rest as [string, Types.TSchema, unknown[]] + const [Operator, Right, Next] = Rest as [string, t.TSchema, unknown[]] const Schema = ExprBinaryMapping(Right, Next) if (Operator === '&') { - return Types.TypeGuard.IsIntersect(Schema) - ? Types.Intersect([Left, ...Schema.allOf]) - : Types.Intersect([Left, Schema]) + return t.TypeGuard.IsIntersect(Schema) + ? t.Intersect([Left, ...Schema.allOf]) + : t.Intersect([Left, Schema]) } if (Operator === '|') { - return Types.TypeGuard.IsUnion(Schema) - ? Types.Union([Left, ...Schema.anyOf]) - : Types.Union([Left, Schema]) + return t.TypeGuard.IsUnion(Schema) + ? t.Union([Left, ...Schema.anyOf]) + : t.Union([Left, Schema]) } throw 1 })() : Left @@ -374,7 +250,7 @@ const ExprTermTail = Runtime.Union([ ]) // prettier-ignore const ExprTerm = Runtime.Tuple([ - Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') + Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') ], value => ExprBinaryMapping(...value)) // prettier-ignore const ExprTail = Runtime.Union([ @@ -383,7 +259,7 @@ const ExprTail = Runtime.Union([ ]) // prettier-ignore const Expr = Runtime.Tuple([ - Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') + Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') ], value => ExprBinaryMapping(...value)) // ------------------------------------------------------------------ @@ -397,11 +273,11 @@ const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, const Readonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], (value) => value.length > 0) const Optional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], (value) => value.length > 0) // prettier-ignore -const PropertyMapping = (IsReadonly: boolean, Key: string, IsOptional: boolean, _: typeof Colon, Type: Types.TSchema) => ({ +const PropertyMapping = (IsReadonly: boolean, Key: string, IsOptional: boolean, _: typeof Colon, Type: t.TSchema) => ({ [Key]: ( - IsReadonly && IsOptional ? Types.ReadonlyOptional(Type) : - IsReadonly && !IsOptional ? Types.Readonly(Type) : - !IsReadonly && IsOptional ? Types.Optional(Type) : + IsReadonly && IsOptional ? t.ReadonlyOptional(Type) : + IsReadonly && !IsOptional ? t.Readonly(Type) : + !IsReadonly && IsOptional ? t.Optional(Type) : Type ) }) @@ -411,7 +287,7 @@ const Property = Runtime.Tuple([ Runtime.Ref('PropertyKey'), Runtime.Ref('Optional'), Runtime.Const(Colon), - Runtime.Ref('Type'), + Runtime.Ref('Type'), ], value => PropertyMapping(...value)) // prettier-ignore const PropertyDelimiter = Runtime.Union([ @@ -423,9 +299,9 @@ const PropertyDelimiter = Runtime.Union([ ]) // prettier-ignore const Properties = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), - Runtime.Tuple([Runtime.Ref('Property')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), + Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), + Runtime.Tuple([Runtime.Ref('Property')]), Runtime.Tuple([]) ], values => ( values.length === 3 ? { ...values[0], ...values[2] } : @@ -437,11 +313,11 @@ const Properties = Runtime.Union([ // Object // ------------------------------------------------------------------ // prettier-ignore -const ObjectMapping = (_0: typeof LBrace, Properties: Types.TProperties, _2: typeof RBrace) => Types.Object(Properties) +const ObjectMapping = (_0: typeof LBrace, Properties: t.TProperties, _2: typeof RBrace) => t.Object(Properties) // prettier-ignore const _Object = Runtime.Tuple([ Runtime.Const(LBrace), - Runtime.Ref('Properties'), + Runtime.Ref('Properties'), Runtime.Const(RBrace) ], values => ObjectMapping(...values)) @@ -463,16 +339,16 @@ const Elements = Runtime.Union([ // prettier-ignore const Tuple = Runtime.Tuple([ Runtime.Const(LBracket), - Runtime.Ref('Elements'), + Runtime.Ref('Elements'), Runtime.Const(RBracket) -], value => Types.Tuple(value[1])) +], value => t.Tuple(value[1])) // ------------------------------------------------------------------ // Parameters // ------------------------------------------------------------------ // prettier-ignore const Parameter = Runtime.Tuple([ - Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') + Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') ], value => value[2]) // prettier-ignore const Parameters = Runtime.Union([ @@ -493,28 +369,28 @@ const Parameters = Runtime.Union([ const Constructor = Runtime.Tuple([ Runtime.Const('new'), Runtime.Const(LParen), - Runtime.Ref('Parameters'), + Runtime.Ref('Parameters'), Runtime.Const(RParen), Runtime.Const('=>'), - Runtime.Ref('Type') -], value => Types.Constructor(value[2], value[5])) + Runtime.Ref('Type') +], value => t.Constructor(value[2], value[5])) // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ // prettier-ignore const Function = Runtime.Tuple([ Runtime.Const(LParen), - Runtime.Ref('Parameters'), + Runtime.Ref('Parameters'), Runtime.Const(RParen), Runtime.Const('=>'), - Runtime.Ref('Type') -], value => Types.Function(value[1], value[4])) + Runtime.Ref('Type') +], value => t.Function(value[1], value[4])) // ------------------------------------------------------------------ // Mapped (requires deferred types) // ------------------------------------------------------------------ // prettier-ignore const MappedMapping = (values: unknown[]) => { - return Types.Literal('Mapped types not supported') + return t.Literal('Mapped types not supported') } // prettier-ignore const Mapped = Runtime.Tuple([ @@ -522,10 +398,10 @@ const Mapped = Runtime.Tuple([ Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RBrace) ], MappedMapping) // ------------------------------------------------------------------ @@ -535,9 +411,9 @@ const Mapped = Runtime.Tuple([ const AsyncIterator = Runtime.Tuple([ Runtime.Const('AsyncIterator'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.AsyncIterator(value[2])) +], value => t.AsyncIterator(value[2])) // ------------------------------------------------------------------ // Iterator // ------------------------------------------------------------------ @@ -545,9 +421,9 @@ const AsyncIterator = Runtime.Tuple([ const Iterator = Runtime.Tuple([ Runtime.Const('Iterator'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Iterator(value[2])) +], value => t.Iterator(value[2])) // ------------------------------------------------------------------ // ConstructorParameters // ------------------------------------------------------------------ @@ -555,9 +431,9 @@ const Iterator = Runtime.Tuple([ const ConstructorParameters = Runtime.Tuple([ Runtime.Const('ConstructorParameters'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.ConstructorParameters(value[2])) +], value => t.ConstructorParameters(value[2])) // ------------------------------------------------------------------ // Parameters // ------------------------------------------------------------------ @@ -565,9 +441,9 @@ const ConstructorParameters = Runtime.Tuple([ const FunctionParameters = Runtime.Tuple([ Runtime.Const('Parameters'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Parameters(value[2])) +], value => t.Parameters(value[2])) // ------------------------------------------------------------------ // InstanceType // ------------------------------------------------------------------ @@ -575,9 +451,9 @@ const FunctionParameters = Runtime.Tuple([ const InstanceType = Runtime.Tuple([ Runtime.Const('InstanceType'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.InstanceType(value[2])) +], value => t.InstanceType(value[2])) // ------------------------------------------------------------------ // ReturnType // ------------------------------------------------------------------ @@ -585,9 +461,9 @@ const InstanceType = Runtime.Tuple([ const ReturnType = Runtime.Tuple([ Runtime.Const('ReturnType'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.ReturnType(value[2])) +], value => t.ReturnType(value[2])) // ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ @@ -595,9 +471,9 @@ const ReturnType = Runtime.Tuple([ const Awaited = Runtime.Tuple([ Runtime.Const('Awaited'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Awaited(value[2])) +], value => t.Awaited(value[2])) // ------------------------------------------------------------------ // Array // ------------------------------------------------------------------ @@ -605,9 +481,9 @@ const Awaited = Runtime.Tuple([ const Array = Runtime.Tuple([ Runtime.Const('Array'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Array(value[2])) +], value => t.Array(value[2])) // ------------------------------------------------------------------ // Record // ------------------------------------------------------------------ @@ -615,11 +491,11 @@ const Array = Runtime.Tuple([ const Record = Runtime.Tuple([ Runtime.Const('Record'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(Comma), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Record(value[2], value[4])) +], value => t.Record(value[2], value[4])) // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ @@ -627,9 +503,9 @@ const Record = Runtime.Tuple([ const Promise = Runtime.Tuple([ Runtime.Const('Promise'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Promise(value[2])) +], value => t.Promise(value[2])) // ------------------------------------------------------------------ // Partial // ------------------------------------------------------------------ @@ -637,9 +513,9 @@ const Promise = Runtime.Tuple([ const Partial = Runtime.Tuple([ Runtime.Const('Partial'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Partial(value[2])) +], value => t.Partial(value[2])) // ------------------------------------------------------------------ // Required // ------------------------------------------------------------------ @@ -647,9 +523,9 @@ const Partial = Runtime.Tuple([ const Required = Runtime.Tuple([ Runtime.Const('Required'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Required(value[2])) +], value => t.Required(value[2])) // ------------------------------------------------------------------ // Pick // ------------------------------------------------------------------ @@ -657,11 +533,11 @@ const Required = Runtime.Tuple([ const Pick = Runtime.Tuple([ Runtime.Const('Pick'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(Comma), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Pick(value[2], value[4])) +], value => t.Pick(value[2], value[4])) // ------------------------------------------------------------------ // Omit // ------------------------------------------------------------------ @@ -669,11 +545,11 @@ const Pick = Runtime.Tuple([ const Omit = Runtime.Tuple([ Runtime.Const('Omit'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(Comma), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Omit(value[2], value[4])) +], value => t.Omit(value[2], value[4])) // ------------------------------------------------------------------ // Exclude // ------------------------------------------------------------------ @@ -681,11 +557,11 @@ const Omit = Runtime.Tuple([ const Exclude = Runtime.Tuple([ Runtime.Const('Exclude'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(Comma), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Exclude(value[2], value[4])) +], value => t.Exclude(value[2], value[4])) // ------------------------------------------------------------------ // Extract // ------------------------------------------------------------------ @@ -693,11 +569,11 @@ const Exclude = Runtime.Tuple([ const Extract = Runtime.Tuple([ Runtime.Const('Extract'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(Comma), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Extract(value[2], value[4])) +], value => t.Extract(value[2], value[4])) // ------------------------------------------------------------------ // Uppercase // ------------------------------------------------------------------ @@ -705,9 +581,9 @@ const Extract = Runtime.Tuple([ const Uppercase = Runtime.Tuple([ Runtime.Const('Uppercase'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Uppercase(value[2])) +], value => t.Uppercase(value[2])) // ------------------------------------------------------------------ // Lowercase // ------------------------------------------------------------------ @@ -715,9 +591,9 @@ const Uppercase = Runtime.Tuple([ const Lowercase = Runtime.Tuple([ Runtime.Const('Lowercase'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Lowercase(value[2])) +], value => t.Lowercase(value[2])) // ------------------------------------------------------------------ // Capitalize // ------------------------------------------------------------------ @@ -725,9 +601,9 @@ const Lowercase = Runtime.Tuple([ const Capitalize = Runtime.Tuple([ Runtime.Const('Capitalize'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Capitalize(value[2])) +], value => t.Capitalize(value[2])) // ------------------------------------------------------------------ // Uncapitalize // ------------------------------------------------------------------ @@ -735,45 +611,22 @@ const Capitalize = Runtime.Tuple([ const Uncapitalize = Runtime.Tuple([ Runtime.Const('Uncapitalize'), Runtime.Const(LAngle), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => Types.Uncapitalize(value[2])) +], value => t.Uncapitalize(value[2])) // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ -const Date = Runtime.Const('Date', Runtime.As(Types.Date())) +const Date = Runtime.Const('Date', Runtime.As(t.Date())) // ------------------------------------------------------------------ // Uint8Array // ------------------------------------------------------------------ -const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(Types.Uint8Array())) - -// ------------------------------------------------------------------ -// Main -// ------------------------------------------------------------------ -// prettier-ignore -const Main = Runtime.Union([ - ModuleDeclaration, - TypeAliasDeclaration, - InterfaceDeclaration, - Type -]) +const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array())) // ------------------------------------------------------------------ // Module // ------------------------------------------------------------------ // prettier-ignore export const Module = new Runtime.Module({ - // ---------------------------------------------------------------- - // Modules, Interfaces and Type Aliases - // ---------------------------------------------------------------- - ExportModifier, - HeritageList, - Heritage, - InterfaceDeclaration, - TypeAliasDeclaration, - ModuleType, - ModuleProperties, - ModuleDeclaration, - // ---------------------------------------------------------------- // Type Expressions // ---------------------------------------------------------------- @@ -826,9 +679,4 @@ export const Module = new Runtime.Module({ Date, Uint8Array, Reference, - - // ---------------------------------------------------------------- - // Main - // ---------------------------------------------------------------- - Main }) diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 9d8bb0a38..4cd125565 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Static } from './parsebox/index' -import * as Types from '../type/index' +import { Static } from '../parser/index' +import * as t from '../type/index' // ------------------------------------------------------------------ // Tokens @@ -139,147 +139,15 @@ type Delimit = // Deref // ------------------------------------------------------------------ // prettier-ignore -type Deref = ( - Ref extends keyof Context ? Context[Ref] : Types.TRef +type Deref = ( + Ref extends keyof Context ? Context[Ref] : t.TRef ) // ------------------------------------------------------------------ -// ExportModifier -// ------------------------------------------------------------------ -// prettier-ignore -interface ExportModifierMapping extends Static.IMapping { - output: this['input'] extends [string] ? true : false -} -// prettier-ignore -type ExportModifier = Static.Union<[ - Static.Tuple<[Static.Const<'export'>]>, - Static.Tuple<[]>, -], ExportModifierMapping> - -// ------------------------------------------------------------------ -// TypeAliasDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -interface TypeAliasDeclarationMapping extends Static.IMapping { - output: this['input'] extends [infer _Export extends boolean, 'type', infer Ident extends string, Equals, infer Type extends Types.TSchema] - ? { [_ in Ident]: Type } - : never -} -// prettier-ignore -type TypeAliasDeclaration = Static.Tuple<[ - ExportModifier, Static.Const<'type'>, Static.Ident, Static.Const, Type -], TypeAliasDeclarationMapping> - -// ------------------------------------------------------------------ -// HeritageList -// ------------------------------------------------------------------ -// prettier-ignore (note, heritage list should disallow trailing comma) -type HeritageListDelimiter = Static.Union<[Static.Tuple<[Static.Const, Static.Const]>, Static.Tuple<[Static.Const]>]> -// prettier-ignore -type HeritageListReduce = ( - Values extends [infer Ref extends string, ...infer Rest extends string[]] - ? HeritageListReduce]> - : Result -) -// prettier-ignore -interface HeritageListMapping extends Static.IMapping { - output: ( - this['context'] extends Types.TProperties ? - this['input'] extends string[] - ? HeritageListReduce - : [] - : [] - ) -} -// prettier-ignore -type HeritageList = Static.Union<[Delimit], HeritageListMapping> -// ------------------------------------------------------------------ -// Heritage -// ------------------------------------------------------------------ -// prettier-ignore -interface HeritageMapping extends Static.IMapping { - output: this['input'] extends ['extends', infer List extends Types.TSchema[]] ? List : [] -} -// prettier-ignore -type Heritage = Static.Union<[ - Static.Tuple<[Static.Const<'extends'>, HeritageList]>, - Static.Tuple<[]> -], HeritageMapping> -// ------------------------------------------------------------------ -// InterfaceDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -interface InterfaceDeclarationMapping extends Static.IMapping { - output: this['input'] extends [boolean, 'interface', infer Ident extends string, infer Heritage extends Types.TSchema[], LBrace, infer Properties extends Types.TProperties, RBrace] - ? { [_ in Ident]: Types.TIntersectEvaluated<[...Heritage, Types.TObject]> } - : never -} -// prettier-ignore -type InterfaceDeclaration = Static.Tuple<[ - ExportModifier, - Static.Const<'interface'>, - Static.Ident, - Heritage, - Static.Const, - Properties, - Static.Const, -], InterfaceDeclarationMapping> -// ------------------------------------------------------------------ -// ModuleType -// ------------------------------------------------------------------ -// prettier-ignore -type ModuleType = Static.Union<[ - InterfaceDeclaration, - TypeAliasDeclaration -]> -// ------------------------------------------------------------------ -// ModuleProperties -// ------------------------------------------------------------------ -// prettier-ignore -type ModulePropertiesDelimiter = Static.Union<[ - Static.Tuple<[Static.Const, Static.Const]>, - Static.Tuple<[Static.Const]>, - Static.Tuple<[Static.Const]>, -]> -// prettier-ignore -type ModulePropertiesReduce = ( - Value extends [infer ModuleType extends Types.TProperties, unknown[], ...infer Rest extends unknown[]] ? ModulePropertiesReduce : - Value extends [infer ModuleType extends Types.TProperties] ? ModulePropertiesReduce<[], Result & ModuleType> : - Types.Evaluate -) -// prettier-ignore -interface ModulePropertiesMapping extends Static.IMapping { - output: this['input'] extends unknown[] ? ModulePropertiesReduce : never -} -// prettier-ignore -type ModuleProperties = Static.Union<[ - Static.Tuple<[ModuleType, ModulePropertiesDelimiter, ModuleProperties]>, - Static.Tuple<[ModuleType]>, - Static.Tuple<[]>, -], ModulePropertiesMapping> -// ------------------------------------------------------------------ -// ModuleDeclaration -// ------------------------------------------------------------------ -// prettier-ignore -type ModuleIdentifier = Static.Union<[ - Static.Tuple<[Static.Ident]>, - Static.Tuple<[]> -]> -// prettier-ignore -interface ModuleDeclarationMapping extends Static.IMapping { - output: this['input'] extends [boolean, 'module', infer _Ident extends string[], LBrace, infer Properties extends Types.TProperties, RBrace] - ? Types.TModule - : never -} -// prettier-ignore -type ModuleDeclaration = Static.Tuple<[ - ExportModifier, Static.Const<'module'>, ModuleIdentifier, Static.Const, ModuleProperties, Static.Const -], ModuleDeclarationMapping> -// ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ // prettier-ignore interface ReferenceMapping extends Static.IMapping { - output: this['context'] extends Types.TProperties + output: this['context'] extends t.TProperties ? this['input'] extends string ? Deref : never @@ -291,15 +159,15 @@ type Reference = Static.Ident // ------------------------------------------------------------------ // prettier-ignore interface LiteralBooleanMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends boolean}` ? Types.TLiteral : never + output: this['input'] extends `${infer S extends boolean}` ? t.TLiteral : never } // prettier-ignore interface LiteralNumberMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends number}` ? Types.TLiteral : never + output: this['input'] extends `${infer S extends number}` ? t.TLiteral : never } // prettier-ignore interface LiteralStringMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends string}` ? Types.TLiteral : never + output: this['input'] extends `${infer S extends string}` ? t.TLiteral : never } // prettier-ignore type Literal = Static.Union<[ @@ -312,18 +180,18 @@ type Literal = Static.Union<[ // ------------------------------------------------------------------ // prettier-ignore type Keyword = Static.Union<[ - Static.Const<'any', Static.As>, - Static.Const<'bigint', Static.As>, - Static.Const<'boolean', Static.As>, - Static.Const<'integer', Static.As>, - Static.Const<'never', Static.As>, - Static.Const<'null', Static.As>, - Static.Const<'number', Static.As>, - Static.Const<'string', Static.As>, - Static.Const<'symbol', Static.As>, - Static.Const<'undefined', Static.As>, - Static.Const<'unknown', Static.As>, - Static.Const<'void', Static.As>, + Static.Const<'any', Static.As>, + Static.Const<'bigint', Static.As>, + Static.Const<'boolean', Static.As>, + Static.Const<'integer', Static.As>, + Static.Const<'never', Static.As>, + Static.Const<'null', Static.As>, + Static.Const<'number', Static.As>, + Static.Const<'string', Static.As>, + Static.Const<'symbol', Static.As>, + Static.Const<'undefined', Static.As>, + Static.Const<'unknown', Static.As>, + Static.Const<'void', Static.As>, ]> // ------------------------------------------------------------------ // KeyOf @@ -343,7 +211,7 @@ type KeyOf = Static.Union<[ // prettier-ignore interface IndexArrayMapping extends Static.IMapping { output: ( - this['input'] extends [LBracket, infer Type extends Types.TSchema, RBracket, infer Rest extends unknown[]] ? [[Type], ...Rest] : + this['input'] extends [LBracket, infer Type extends t.TSchema, RBracket, infer Rest extends unknown[]] ? [[Type], ...Rest] : this['input'] extends [LBracket, RBracket, infer Rest extends unknown[]] ? [[], ...Rest] : [] ) @@ -360,7 +228,7 @@ type IndexArray = Static.Union<[ // ------------------------------------------------------------------ // prettier-ignore interface ExtendsMapping extends Static.IMapping { - output: this['input'] extends ['extends', infer Type extends Types.TSchema, Question, infer True extends Types.TSchema, Colon, infer False extends Types.TSchema] + output: this['input'] extends ['extends', infer Type extends t.TSchema, Question, infer True extends t.TSchema, Colon, infer False extends t.TSchema] ? [Type, True, False] : [] } @@ -375,8 +243,8 @@ type Extends = Static.Union<[ // prettier-ignore interface BaseMapping extends Static.IMapping { output: ( - this['input'] extends [LParen, infer Type extends Types.TSchema, RParen] ? Type : - this['input'] extends [infer Type extends Types.TSchema] ? Type : + this['input'] extends [LParen, infer Type extends t.TSchema, RParen] ? Type : + this['input'] extends [infer Type extends t.TSchema] ? Type : never ) } @@ -424,24 +292,24 @@ type Base = Static.Union<[ // Factor // ------------------------------------------------------------------ // prettier-ignore -type FactorExtends = ( - Extends extends [infer Right extends Types.TSchema, infer True extends Types.TSchema, infer False extends Types.TSchema] - ? Types.TExtends +type FactorExtends = ( + Extends extends [infer Right extends t.TSchema, infer True extends t.TSchema, infer False extends t.TSchema] + ? t.TExtends : Type ) // prettier-ignore -type FactorIndexArray = ( - IndexArray extends [...infer Left extends unknown[], infer Right extends Types.TSchema[]] ? ( - Right extends [infer Indexer extends Types.TSchema] ? Types.TIndex, Types.TIndexPropertyKeys> : - Right extends [] ? Types.TArray> : - Types.TNever +type FactorIndexArray = ( + IndexArray extends [...infer Left extends unknown[], infer Right extends t.TSchema[]] ? ( + Right extends [infer Indexer extends t.TSchema] ? t.TIndex, t.TIndexPropertyKeys> : + Right extends [] ? t.TArray> : + t.TNever ) : Type ) // prettier-ignore interface FactorMapping extends Static.IMapping { - output: this['input'] extends [infer KeyOf extends boolean, infer Type extends Types.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] + output: this['input'] extends [infer KeyOf extends boolean, infer Type extends t.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] ? KeyOf extends true - ? FactorExtends>, Extends> + ? FactorExtends>, Extends> : FactorExtends, Extends> : never } @@ -453,18 +321,18 @@ type Factor = Static.Tuple<[ // Expr // ------------------------------------------------------------------ // prettier-ignore -type ExprBinaryReduce = ( - Rest extends [infer Operator extends unknown, infer Right extends Types.TSchema, infer Next extends unknown[]] ? ( - ExprBinaryReduce extends infer Schema extends Types.TSchema ? ( +type ExprBinaryReduce = ( + Rest extends [infer Operator extends unknown, infer Right extends t.TSchema, infer Next extends unknown[]] ? ( + ExprBinaryReduce extends infer Schema extends t.TSchema ? ( Operator extends '&' ? ( - Schema extends Types.TIntersect - ? Types.TIntersect<[Left, ...Types]> - : Types.TIntersect<[Left, Schema]> + Schema extends t.TIntersect + ? t.TIntersect<[Left, ...Types]> + : t.TIntersect<[Left, Schema]> ) : Operator extends '|' ? ( - Schema extends Types.TUnion - ? Types.TUnion<[Left, ...Types]> - : Types.TUnion<[Left, Schema]> + Schema extends t.TUnion + ? t.TUnion<[Left, ...Types]> + : t.TUnion<[Left, Schema]> ) : never ) : never ) : Left @@ -472,7 +340,7 @@ type ExprBinaryReduce = ( // prettier-ignore interface ExprBinaryMapping extends Static.IMapping { output: ( - this['input'] extends [infer Left extends Types.TSchema, infer Rest extends unknown[]] + this['input'] extends [infer Left extends t.TSchema, infer Rest extends unknown[]] ? ExprBinaryReduce : [] ) @@ -498,7 +366,7 @@ type Expr = Static.Tuple<[ // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ -type Type = Expr +export type Type = Expr // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ @@ -520,12 +388,12 @@ interface OptionalMapping extends Static.IMapping { type Optional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], OptionalMapping> // prettier-ignore interface PropertyMapping extends Static.IMapping { - output: this['input'] extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends Types.TSchema] + output: this['input'] extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends t.TSchema] ? { [_ in Key]: ( - [IsReadonly, IsOptional] extends [true, true] ? Types.TReadonlyOptional : - [IsReadonly, IsOptional] extends [true, false] ? Types.TReadonly : - [IsReadonly, IsOptional] extends [false, true] ? Types.TOptional : + [IsReadonly, IsOptional] extends [true, true] ? t.TReadonlyOptional : + [IsReadonly, IsOptional] extends [true, false] ? t.TReadonly : + [IsReadonly, IsOptional] extends [false, true] ? t.TOptional : Type ) } : never @@ -540,14 +408,14 @@ type PropertyDelimiter = Static.Union<[ Static.Tuple<[Static.Const]>, ]> // prettier-ignore -type PropertiesReduce = ( - PropertiesArray extends [infer Left extends Types.TProperties, ...infer Right extends Types.TProperties[]] - ? PropertiesReduce> +type PropertiesReduce = ( + PropertiesArray extends [infer Left extends t.TProperties, ...infer Right extends t.TProperties[]] + ? PropertiesReduce> : Result ) // prettier-ignore interface PropertiesMapping extends Static.IMapping { - output: this['input'] extends Types.TProperties[] ? PropertiesReduce : never + output: this['input'] extends t.TProperties[] ? PropertiesReduce : never } type Properties = Static.Union<[Delimit], PropertiesMapping> // ------------------------------------------------------------------ @@ -555,8 +423,8 @@ type Properties = Static.Union<[Delimit], Propertie // ------------------------------------------------------------------ // prettier-ignore interface ObjectMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer Properties extends Types.TProperties, unknown] - ? Types.TObject + output: this['input'] extends [unknown, infer Properties extends t.TProperties, unknown] + ? t.TObject : never } // prettier-ignore @@ -569,7 +437,7 @@ type Object = Static.Tuple<[ type Elements = Delimit> // prettier-ignore interface TupleMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer Elements extends Types.TSchema[], unknown] ? Types.TTuple : never + output: this['input'] extends [unknown, infer Elements extends t.TSchema[], unknown] ? t.TTuple : never } // prettier-ignore type Tuple = Static.Tuple<[ @@ -579,7 +447,7 @@ type Tuple = Static.Tuple<[ // Parameters // ------------------------------------------------------------------ interface ParameterMapping extends Static.IMapping { - output: this['input'] extends [string, Colon, infer Type extends Types.TSchema] ? Type : never + output: this['input'] extends [string, Colon, infer Type extends t.TSchema] ? Type : never } // prettier-ignore type Parameter = Static.Tuple<[ @@ -592,8 +460,8 @@ type Parameters = Delimit> // ------------------------------------------------------------------ // prettier-ignore interface FunctionMapping extends Static.IMapping { - output: this['input'] extends [LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer ReturnType extends Types.TSchema] - ? Types.TFunction + output: this['input'] extends [LParen, infer Parameters extends t.TSchema[], RParen, '=>', infer ReturnType extends t.TSchema] + ? t.TFunction : never } // prettier-ignore @@ -605,8 +473,8 @@ type Function = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ConstructorMapping extends Static.IMapping { - output: this['input'] extends ['new', LParen, infer Parameters extends Types.TSchema[], RParen, '=>', infer InstanceType extends Types.TSchema] - ? Types.TConstructor + output: this['input'] extends ['new', LParen, infer Parameters extends t.TSchema[], RParen, '=>', infer InstanceType extends t.TSchema] + ? t.TConstructor : never } // prettier-ignore @@ -618,8 +486,8 @@ type Constructor = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface MappedMapping extends Static.IMapping { - output: this['input'] extends [LBrace, LBracket, infer _Key extends string, 'in', infer _Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace] - ? Types.TLiteral<'Mapped types not supported'> + output: this['input'] extends [LBrace, LBracket, infer _Key extends string, 'in', infer _Right extends t.TSchema, RBracket, Colon, infer Type extends t.TSchema, RBrace] + ? t.TLiteral<'Mapped types not supported'> : this['input'] } // prettier-ignore @@ -631,8 +499,8 @@ type Mapped = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ArrayMapping extends Static.IMapping { - output: this['input'] extends ['Array', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TArray + output: this['input'] extends ['Array', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TArray : never } // prettier-ignore @@ -644,8 +512,8 @@ type Array = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface AsyncIteratorMapping extends Static.IMapping { - output: this['input'] extends ['AsyncIterator', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TAsyncIterator + output: this['input'] extends ['AsyncIterator', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TAsyncIterator : never } // prettier-ignore @@ -657,8 +525,8 @@ type AsyncIterator = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface IteratorMapping extends Static.IMapping { - output: this['input'] extends ['Iterator', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TIterator + output: this['input'] extends ['Iterator', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TIterator : never } // prettier-ignore @@ -671,8 +539,8 @@ type Iterator = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ConstructorParametersMapping extends Static.IMapping { - output: this['input'] extends ['ConstructorParameters', LAngle, infer Type extends Types.TConstructor, RAngle] - ? Types.TConstructorParameters + output: this['input'] extends ['ConstructorParameters', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TConstructorParameters : never } // prettier-ignore @@ -684,8 +552,8 @@ type ConstructorParameters = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface FunctionParametersMapping extends Static.IMapping { - output: this['input'] extends ['Parameters', LAngle, infer Type extends Types.TFunction, RAngle] - ? Types.TParameters + output: this['input'] extends ['Parameters', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TParameters : never } // prettier-ignore @@ -697,8 +565,8 @@ type FunctionParameters = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface InstanceTypeMapping extends Static.IMapping { - output: this['input'] extends ['InstanceType', LAngle, infer Type extends Types.TConstructor, RAngle] - ? Types.TInstanceType + output: this['input'] extends ['InstanceType', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TInstanceType : never } // prettier-ignore @@ -710,8 +578,8 @@ type InstanceType = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ReturnTypeMapping extends Static.IMapping { - output: this['input'] extends ['ReturnType', LAngle, infer Type extends Types.TFunction, RAngle] - ? Types.TReturnType + output: this['input'] extends ['ReturnType', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TReturnType : never } // prettier-ignore @@ -723,8 +591,8 @@ type ReturnType = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface AwaitedMapping extends Static.IMapping { - output: this['input'] extends ['Awaited', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TAwaited + output: this['input'] extends ['Awaited', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TAwaited : never } // prettier-ignore @@ -736,8 +604,8 @@ type Awaited = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface PromiseMapping extends Static.IMapping { - output: this['input'] extends ['Promise', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TPromise + output: this['input'] extends ['Promise', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TPromise : never } // prettier-ignore @@ -749,8 +617,8 @@ type Promise = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface RecordMapping extends Static.IMapping { - output: this['input'] extends ['Record', LAngle, infer Key extends Types.TSchema, Comma, infer Type extends Types.TSchema, RAngle] - ? Types.TRecord + output: this['input'] extends ['Record', LAngle, infer Key extends t.TSchema, Comma, infer Type extends t.TSchema, RAngle] + ? t.TRecord : never } // prettier-ignore @@ -762,8 +630,8 @@ type Record = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface PartialMapping extends Static.IMapping { - output: this['input'] extends ['Partial', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TPartial + output: this['input'] extends ['Partial', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TPartial : never } // prettier-ignore @@ -775,8 +643,8 @@ type Partial = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface RequiredMapping extends Static.IMapping { - output: this['input'] extends ['Required', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TRequired + output: this['input'] extends ['Required', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TRequired : never } // prettier-ignore @@ -788,8 +656,8 @@ type Required = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface PickMapping extends Static.IMapping { - output: this['input'] extends ['Pick', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] - ? Types.TPick + output: this['input'] extends ['Pick', LAngle, infer Type extends t.TSchema, Comma, infer Key extends t.TSchema, RAngle] + ? t.TPick : never } // prettier-ignore @@ -801,8 +669,8 @@ type Pick = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface OmitMapping extends Static.IMapping { - output: this['input'] extends ['Omit', LAngle, infer Type extends Types.TSchema, Comma, infer Key extends Types.TSchema, RAngle] - ? Types.TOmit + output: this['input'] extends ['Omit', LAngle, infer Type extends t.TSchema, Comma, infer Key extends t.TSchema, RAngle] + ? t.TOmit : never } // prettier-ignore @@ -814,8 +682,8 @@ type Omit = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ExcludeMapping extends Static.IMapping { - output: this['input'] extends ['Exclude', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TExclude + output: this['input'] extends ['Exclude', LAngle, infer Type extends t.TSchema, Comma, infer PropertyKey extends t.TSchema, RAngle] + ? t.TExclude : never } // prettier-ignore @@ -827,8 +695,8 @@ type Exclude = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface ExtractMapping extends Static.IMapping { - output: this['input'] extends ['Extract', LAngle, infer Type extends Types.TSchema, Comma, infer PropertyKey extends Types.TSchema, RAngle] - ? Types.TExtract + output: this['input'] extends ['Extract', LAngle, infer Type extends t.TSchema, Comma, infer PropertyKey extends t.TSchema, RAngle] + ? t.TExtract : never } // prettier-ignore @@ -840,8 +708,8 @@ type Extract = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface UppercaseMapping extends Static.IMapping { - output: this['input'] extends ['Uppercase', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TUppercase + output: this['input'] extends ['Uppercase', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TUppercase : never } // prettier-ignore @@ -853,8 +721,8 @@ type Uppercase = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface LowercaseMapping extends Static.IMapping { - output: this['input'] extends ['Lowercase', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TLowercase + output: this['input'] extends ['Lowercase', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TLowercase : never } // prettier-ignore @@ -866,8 +734,8 @@ type Lowercase = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface CapitalizeMapping extends Static.IMapping { - output: this['input'] extends ['Capitalize', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TCapitalize + output: this['input'] extends ['Capitalize', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TCapitalize : never } // prettier-ignore @@ -879,8 +747,8 @@ type Capitalize = Static.Tuple<[ // ------------------------------------------------------------------ // prettier-ignore interface UncapitalizeMapping extends Static.IMapping { - output: this['input'] extends ['Uncapitalize', LAngle, infer Type extends Types.TSchema, RAngle] - ? Types.TUncapitalize + output: this['input'] extends ['Uncapitalize', LAngle, infer Type extends t.TSchema, RAngle] + ? t.TUncapitalize : never } // prettier-ignore @@ -890,19 +758,8 @@ type Uncapitalize = Static.Tuple<[ // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ -type Date = Static.Const<'Date', Static.As> +type Date = Static.Const<'Date', Static.As> // ------------------------------------------------------------------ // Uint8Array // ------------------------------------------------------------------ -type Uint8Array = Static.Const<'Uint8Array', Static.As> - -// ------------------------------------------------------------------ -// Main -// ------------------------------------------------------------------ -// prettier-ignore -export type Main = Static.Union<[ - ModuleDeclaration, - TypeAliasDeclaration, - InterfaceDeclaration, - Type -]> +type Uint8Array = Static.Const<'Uint8Array', Static.As> diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts new file mode 100644 index 000000000..4d5550cb3 --- /dev/null +++ b/src/syntax/syntax.ts @@ -0,0 +1,105 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/syntax + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '../type/index' +import { Static } from '../parser/index' +import { Module } from './runtime' +import { Type } from './static' + +// ------------------------------------------------------------------ +// NoInfer +// ------------------------------------------------------------------ +/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ +export function NoInfer, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ +export function NoInfer(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ +// prettier-ignore +export function NoInfer(...args: any[]): Types.TSchema | undefined { + const withContext = typeof args[0] === 'string' ? false : true + const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] + const type = Module.Parse('Type', code, context)[0] + return Types.KindGuard.IsSchema(type) + ? Types.CloneType(type, options) + : Types.Never(options) +} +// ------------------------------------------------------------------ +// Syntax +// ------------------------------------------------------------------ +/** Infers a TSchema type from TypeScript syntax. */ +// prettier-ignore +export type TSyntax, Code extends string> = ( + Static.Parse extends [infer Type extends Types.TSchema, string] + ? Type + : Types.TNever +) +/** Parses a TSchema type from TypeScript syntax */ +export function Syntax, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax +/** Parses a TSchema type from TypeScript syntax */ +export function Syntax(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code> +/** Parses a TSchema type from TypeScript syntax */ +export function Syntax(...args: any[]): never { + return NoInfer.apply(null, args as never) as never +} +// ------------------------------------------------------------------ +// Deprecated +// ------------------------------------------------------------------ +/** + * Parses a TSchema type from Syntax. + * @deprecated Use Syntax() function + */ +export function Parse, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax +/** + * Parses a TSchema type from Syntax. + * @deprecated Use Syntax() function + */ +export function Parse(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code> +/** + * Parses a TSchema type from Syntax. + * @deprecated Use Syntax() function + */ +export function Parse(...args: any[]): never { + return NoInfer.apply(null, args as never) as never +} +/** + * Parses a TSchema from TypeScript Syntax + * @deprecated Use NoInfer() function + */ +export function ParseOnly, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** + * Parses a TSchema from TypeScript Syntax + * @deprecated Use NoInfer() function + */ +export function ParseOnly(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +/** + * Parses a TSchema from TypeScript Syntax + * @deprecated Use NoInfer() function + */ +export function ParseOnly(...args: any[]): Types.TSchema | undefined { + return NoInfer.apply(null, args as never) as never +} diff --git a/src/tsconfig.json b/src/tsconfig.json index 89a62a897..e65c06406 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "errors/index.ts", "syntax/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] + "files": ["compiler/index.ts", "errors/index.ts", "parser/index.ts", "syntax/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] } diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts index b15718f1d..3947c8539 100644 --- a/src/type/constructor-parameters/constructor-parameters.ts +++ b/src/type/constructor-parameters/constructor-parameters.ts @@ -27,19 +27,21 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import type { TSchema, SchemaOptions } from '../schema/index' -import type { Ensure } from '../helpers/index' import type { TConstructor } from '../constructor/index' import { Tuple, type TTuple } from '../tuple/index' +import { Never, type TNever } from '../never/index' +import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ // ConstructorParameters // ------------------------------------------------------------------ // prettier-ignore -export type TConstructorParameters> = ( - Ensure> +export type TConstructorParameters = ( + Type extends TConstructor + ? TTuple + : TNever ) - /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ -export function ConstructorParameters>(schema: T, options?: SchemaOptions): TConstructorParameters { - return Tuple(schema.parameters, options) +export function ConstructorParameters(schema: Type, options?: SchemaOptions): TConstructorParameters { + return (KindGuard.IsConstructor(schema) ? Tuple(schema.parameters, options) : Never(options)) as never } diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts index ece1382c9..7bba6663c 100644 --- a/src/type/instance-type/instance-type.ts +++ b/src/type/instance-type/instance-type.ts @@ -27,12 +27,19 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' -import type { TSchema, SchemaOptions } from '../schema/index' -import type { TConstructor } from '../constructor/index' - -export type TInstanceType> = T['returns'] +import { type TSchema, SchemaOptions } from '../schema/index' +import { type TConstructor } from '../constructor/index' +import { type TNever, Never } from '../never/index' +import * as KindGuard from '../guard/kind' + +// prettier-ignore +export type TInstanceType + ? InstanceType + : TNever +> = Result /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ -export function InstanceType>(schema: T, options?: SchemaOptions): TInstanceType { - return CreateType(schema.returns, options) +export function InstanceType(schema: Type, options?: SchemaOptions): TInstanceType { + return (KindGuard.IsConstructor(schema) ? CreateType(schema.returns, options) : Never(options)) as never } diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts index d36ed9a18..02ec79500 100644 --- a/src/type/parameters/parameters.ts +++ b/src/type/parameters/parameters.ts @@ -28,15 +28,20 @@ THE SOFTWARE. import type { TSchema, SchemaOptions } from '../schema/index' import type { TFunction } from '../function/index' -import type { Ensure } from '../helpers/index' import { Tuple, type TTuple } from '../tuple/index' +import { Never, type TNever } from '../never/index' +import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ // Parameters // ------------------------------------------------------------------ -export type TParameters = Ensure> - +// prettier-ignore +export type TParameters = ( + Type extends TFunction + ? TTuple + : TNever +) /** `[JavaScript]` Extracts the Parameters from the given Function type */ -export function Parameters>(schema: T, options?: SchemaOptions): TParameters { - return Tuple(schema.parameters, options) +export function Parameters(schema: Type, options?: SchemaOptions): TParameters { + return (KindGuard.IsFunction(schema) ? Tuple(schema.parameters, options) : Never()) as never } diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts index 17dd44757..1b44968dd 100644 --- a/src/type/return-type/return-type.ts +++ b/src/type/return-type/return-type.ts @@ -27,12 +27,19 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/type' -import type { SchemaOptions } from '../schema/index' -import type { TFunction } from '../function/index' - -export type TReturnType = T['returns'] +import { type TSchema, type SchemaOptions } from '../schema/index' +import { type TFunction } from '../function/index' +import { type TNever, Never } from '../never/index' +import * as KindGuard from '../guard/kind' + +// prettier-ignore +export type TReturnType + ? ReturnType + : TNever +> = Result /** `[JavaScript]` Extracts the ReturnType from the given Function type */ -export function ReturnType>(schema: T, options?: SchemaOptions): TReturnType { - return CreateType(schema.returns, options) as never +export function ReturnType(schema: Type, options?: SchemaOptions): TReturnType { + return (KindGuard.IsFunction(schema) ? CreateType(schema.returns, options) : Never(options)) as never } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index a7d6573d4..13705e5d7 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -61,7 +61,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return BigInt(options) } /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters(schema: Type, options?: SchemaOptions): TConstructorParameters { + public ConstructorParameters(schema: Type, options?: SchemaOptions): TConstructorParameters { return ConstructorParameters(schema, options) } /** `[JavaScript]` Creates a Constructor type */ @@ -77,7 +77,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return FunctionType(parameters, returnType, options) } /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ - public InstanceType(schema: Type, options?: SchemaOptions): TInstanceType { + public InstanceType(schema: Type, options?: SchemaOptions): TInstanceType { return InstanceType(schema, options) } /** `[JavaScript]` Creates an Iterator type */ @@ -85,7 +85,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return Iterator(items, options) } /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters(schema: Type, options?: SchemaOptions): TParameters { + public Parameters(schema: Type, options?: SchemaOptions): TParameters { return Parameters(schema, options) } /** `[JavaScript]` Creates a Promise type */ @@ -101,7 +101,7 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { return RegExp(unresolved as any, options) } /** `[JavaScript]` Extracts the ReturnType from the given Function type */ - public ReturnType(type: Type, options?: SchemaOptions): TReturnType { + public ReturnType(type: Type, options?: SchemaOptions): TReturnType { return ReturnType(type, options) } /** `[JavaScript]` Creates a Symbol type */ diff --git a/task/build/package/build.ts b/task/build/package/build.ts index 305ebbd1a..a4b3c5880 100644 --- a/task/build/package/build.ts +++ b/task/build/package/build.ts @@ -32,7 +32,7 @@ import { createPackageJson } from './create-package-json' /** Builds package.json and redirect directories */ export async function build(target: string) { console.log('building...package.json') - const submodules = ['compiler', 'errors', 'syntax', 'system', 'type', 'value'] + const submodules = ['compiler', 'errors', 'parser', 'syntax', 'system', 'type', 'value'] await createPackageJsonRedirect(target, submodules) await createPackageJson(target, submodules) } diff --git a/test/runtime/syntax/syntax.ts b/test/runtime/syntax/syntax.ts index ae7056752..6f66c2ec1 100644 --- a/test/runtime/syntax/syntax.ts +++ b/test/runtime/syntax/syntax.ts @@ -1,156 +1,69 @@ import { TypeGuard } from '@sinclair/typebox' -import { Type, TModule } from '@sinclair/typebox' -import { Parse } from '@sinclair/typebox/syntax' +import { Type } from '@sinclair/typebox' +import { Syntax } from '@sinclair/typebox/syntax' import { Assert } from '../assert/index' // prettier-ignore -describe('syntax/Parse', () => { - // ---------------------------------------------------------------- - // Type Alias - // ---------------------------------------------------------------- - it('Should parse Type Alias 1', () => { - const T = Parse('type A = 1') - Assert.IsTrue(TypeGuard.IsLiteral(T.A)) - Assert.IsTrue(T.A.const === 1) - }) - it('Should parse Type Alias 2', () => { - const T = Parse('export type A = 1') - Assert.IsTrue(TypeGuard.IsLiteral(T.A)) - Assert.IsTrue(T.A.const === 1) - }) - // ---------------------------------------------------------------- - // Interface - // ---------------------------------------------------------------- - it('Should parse Interface 1', () => { - const T = Parse('interface A { x: 1 }') - Assert.IsTrue(TypeGuard.IsObject(T.A)) - Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x)) - Assert.IsTrue(T.A.properties.x.const === 1) - }) - it('Should parse Interface 2', () => { - const T = Parse('export interface A { x: 1 }') - Assert.IsTrue(TypeGuard.IsObject(T.A)) - Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x)) - Assert.IsTrue(T.A.properties.x.const === 1) - }) - // ---------------------------------------------------------------- - // Module - // ---------------------------------------------------------------- - it('Should parse Module 1', () => { - const T = Parse('module {}') - Assert.IsTrue(T instanceof TModule) - }) - it('Should parse Module 2', () => { - const T = Parse('export module {}') - Assert.IsTrue(T instanceof TModule) - }) - it('Should parse Module 3', () => { - const T = Parse('module A {}') - Assert.IsTrue(T instanceof TModule) - }) - it('Should parse Module 4', () => { - const T = Parse('export module A {}') - Assert.IsTrue(T instanceof TModule) - }) - it('Should parse Module 5', () => { - const T = Parse(`export module A { - export type A = number - }`) - const A = T.Import('A') - Assert.IsTrue(T instanceof TModule) - Assert.IsTrue(TypeGuard.IsImport(A)) - const N = A.$defs[A.$ref] - Assert.IsTrue(TypeGuard.IsNumber(N)) - }) - it('Should parse Module 6', () => { - const T = Parse(`export module A { - export interface A { x: number } - }`) - const A = T.Import('A') - Assert.IsTrue(T instanceof TModule) - Assert.IsTrue(TypeGuard.IsImport(A)) - const N = A.$defs[A.$ref] - Assert.IsTrue(TypeGuard.IsObject(N)) - Assert.IsTrue(TypeGuard.IsNumber(N.properties.x)) - }) - it('Should parse Module 7', () => { - const T = Parse(`export module A { - export interface A { x: number } - export type B = number - }`) - // A - const A = T.Import('A') - Assert.IsTrue(T instanceof TModule) - Assert.IsTrue(TypeGuard.IsImport(A)) - const N1 = A.$defs[A.$ref] - Assert.IsTrue(TypeGuard.IsObject(N1)) - Assert.IsTrue(TypeGuard.IsNumber(N1.properties.x)) - // B - const B = T.Import('B') - Assert.IsTrue(T instanceof TModule) - Assert.IsTrue(TypeGuard.IsImport(B)) - const N2 = B.$defs[B.$ref] - Assert.IsTrue(TypeGuard.IsNumber(N2)) - }) +describe('syntax/Syntax', () => { // ---------------------------------------------------------------- // Type Expressions // ---------------------------------------------------------------- it('Should parse Any', () => { - const T = Parse(`any`) + const T = Syntax(`any`) Assert.IsTrue(TypeGuard.IsAny(T)) }) it('Should parse Array 1', () => { - const T = Parse(`number[]`) + const T = Syntax(`number[]`) Assert.IsTrue(TypeGuard.IsArray(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) it('Should parse Array 2', () => { - const T = Parse(`Array`) + const T = Syntax(`Array`) Assert.IsTrue(TypeGuard.IsArray(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) it('Should parse AsyncIterator', () => { - const T = Parse(`AsyncIterator`) + const T = Syntax(`AsyncIterator`) Assert.IsTrue(TypeGuard.IsAsyncIterator(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) it('Should parse Awaited', () => { - const T = Parse(`Awaited>`) + const T = Syntax(`Awaited>`) Assert.IsTrue(TypeGuard.IsNumber(T)) }) it('Should parse BigInt', () => { - const T = Parse(`bigint`) + const T = Syntax(`bigint`) Assert.IsTrue(TypeGuard.IsBigInt(T)) }) it('Should parse Boolean', () => { - const T = Parse(`boolean`) + const T = Syntax(`boolean`) Assert.IsTrue(TypeGuard.IsBoolean(T)) }) it('Should parse ConstructorParameters', () => { - const T = Parse(`ConstructorParameters boolean>`) + const T = Syntax(`ConstructorParameters boolean>`) Assert.IsTrue(TypeGuard.IsTuple(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) Assert.IsTrue(TypeGuard.IsString(T.items![1])) }) it('Should parse Constructor', () => { - const T = Parse(`new (a: number, b: string) => boolean`) + const T = Syntax(`new (a: number, b: string) => boolean`) Assert.IsTrue(TypeGuard.IsConstructor(T)) Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) }) it('Should parse Date', () => { - const T = Parse(`Date`) + const T = Syntax(`Date`) Assert.IsTrue(TypeGuard.IsDate(T)) }) it('Should parse Exclude', () => { - const T = Parse(`Exclude<1 | 2 | 3, 1>`) + const T = Syntax(`Exclude<1 | 2 | 3, 1>`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === 2) Assert.IsTrue(T.anyOf[1].const === 3) }) it('Should parse Extract', () => { - const T = Parse(`Extract<1 | 2 | 3, 1 | 2>`) + const T = Syntax(`Extract<1 | 2 | 3, 1 | 2>`) Assert.IsTrue(TypeGuard.IsUnion(T)) // @ts-ignore fix: incorrect union order (result of UnionToTuple, replace with Tuple destructuring) Assert.IsTrue(T.anyOf[0].const === 1) @@ -158,144 +71,144 @@ describe('syntax/Parse', () => { Assert.IsTrue(T.anyOf[1].const === 2) }) it('Should parse Function', () => { - const T = Parse(`(a: number, b: string) => boolean`) + const T = Syntax(`(a: number, b: string) => boolean`) Assert.IsTrue(TypeGuard.IsFunction(T)) Assert.IsTrue(TypeGuard.IsNumber(T.parameters[0])) Assert.IsTrue(TypeGuard.IsString(T.parameters[1])) Assert.IsTrue(TypeGuard.IsBoolean(T.returns)) }) it('Should parse Indexed 1', () => { - const T = Parse(`{ x: 1, y: 2, z: 3 }['x']`) + const T = Syntax(`{ x: 1, y: 2, z: 3 }['x']`) Assert.IsTrue(T.const === 1) }) it('Should parse Indexed 2', () => { - const T = Parse(`{ x: 1, y: 2, z: 3 }['x' | 'y' | 'z']`) + const T = Syntax(`{ x: 1, y: 2, z: 3 }['x' | 'y' | 'z']`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === 1) Assert.IsTrue(T.anyOf[1].const === 2) Assert.IsTrue(T.anyOf[2].const === 3) }) it('Should parse Indexed 3', () => { - const T = Parse(`{ x: 1, y: 2, z: 3 }`) - const S = Parse({ T }, `T[keyof T]`) + const T = Syntax(`{ x: 1, y: 2, z: 3 }`) + const S = Syntax({ T }, `T[keyof T]`) Assert.IsTrue(TypeGuard.IsUnion(S)) Assert.IsTrue(S.anyOf[0].const === 1) Assert.IsTrue(S.anyOf[1].const === 2) Assert.IsTrue(S.anyOf[2].const === 3) }) it('Should parse Indexed 4', () => { - const T = Parse(`['A', 'B', 'C']`) - const S = Parse({ T }, `T[number]`) + const T = Syntax(`['A', 'B', 'C']`) + const S = Syntax({ T }, `T[number]`) Assert.IsTrue(TypeGuard.IsUnion(S)) Assert.IsTrue(S.anyOf[0].const === 'A') Assert.IsTrue(S.anyOf[1].const === 'B') Assert.IsTrue(S.anyOf[2].const === 'C') }) it('Should parse Integer', () => { - const T = Parse(`integer`) + const T = Syntax(`integer`) Assert.IsTrue(TypeGuard.IsInteger(T)) }) it('Should parse Intersect 1', () => { - const T = Parse(`1 & 2`) + const T = Syntax(`1 & 2`) Assert.IsTrue(TypeGuard.IsIntersect(T)) Assert.IsTrue(T.allOf[0].const === 1) Assert.IsTrue(T.allOf[1].const === 2) }) it('Should parse Intersect 2', () => { - const T = Parse(`1 & (2 & 3)`) // expect flatten + const T = Syntax(`1 & (2 & 3)`) // expect flatten Assert.IsTrue(TypeGuard.IsIntersect(T)) Assert.IsTrue(T.allOf[0].const === 1) Assert.IsTrue(T.allOf[1].const === 2) Assert.IsTrue(T.allOf[2].const === 3) }) it('Should parse Intersect 3', () => { - const T = Parse(`(1 | 2) & 3`) // operator precedence + const T = Syntax(`(1 | 2) & 3`) // operator precedence Assert.IsTrue(TypeGuard.IsIntersect(T)) Assert.IsTrue(TypeGuard.IsUnion(T.allOf[0])) Assert.IsTrue(T.allOf[1].const === 3) }) it('Should parse InstanceType 1', () => { - const T = Parse(`InstanceType { x: 1, y: 2 }>`) + const T = Syntax(`InstanceType { x: 1, y: 2 }>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(T.properties.x.const === 1) Assert.IsTrue(T.properties.y.const === 2) }) it('Should parse InstanceType 2', () => { - const T = Parse(`InstanceType`) // generalization issue - Assert.IsTrue(T === undefined) + const T = Syntax(`InstanceType`) // generalization issue + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should parse Iterator', () => { - const T = Parse(`Iterator`) + const T = Syntax(`Iterator`) Assert.IsTrue(TypeGuard.IsIterator(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items)) }) it('Should parse KeyOf 1', () => { - const T = Parse(`keyof { x: 1, y: 2 }`) + const T = Syntax(`keyof { x: 1, y: 2 }`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === 'x') Assert.IsTrue(T.anyOf[1].const === 'y') }) it('Should parse KeyOf 2', () => { - const T = Parse(`keyof [0, 1]`) + const T = Syntax(`keyof [0, 1]`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === '0') Assert.IsTrue(T.anyOf[1].const === '1') }) it('Should parse KeyOf 3', () => { - const T = Parse(`{ x: 1, y: 2 }`) - const S = Parse({ T }, `keyof T`) + const T = Syntax(`{ x: 1, y: 2 }`) + const S = Syntax({ T }, `keyof T`) Assert.IsTrue(TypeGuard.IsUnion(S)) Assert.IsTrue(S.anyOf[0].const === 'x') Assert.IsTrue(S.anyOf[1].const === 'y') }) it('Should parse Literal Boolean 1', () => { - const T = Parse(`true`) + const T = Syntax(`true`) Assert.IsTrue(T.const === true) }) it('Should parse Literal Boolean 2', () => { - const T = Parse(`false`) + const T = Syntax(`false`) Assert.IsTrue(T.const === false) }) it('Should parse Literal Number', () => { - const T = Parse(`1`) + const T = Syntax(`1`) Assert.IsTrue(T.const === 1) }) it('Should parse Literal String', () => { - const T = Parse(`'1'`) + const T = Syntax(`'1'`) Assert.IsTrue(T.const === '1') }) it('Should parse Mapped (Pending)', () => { - const T = Parse(`{ [K in 1 | 2 | 3]: K }`) + const T = Syntax(`{ [K in 1 | 2 | 3]: K }`) Assert.IsTrue(T.const === 'Mapped types not supported') }) it('Should parse Never', () => { - const T = Parse(`never`) + const T = Syntax(`never`) Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should parse Null', () => { - const T = Parse(`null`) + const T = Syntax(`null`) Assert.IsTrue(TypeGuard.IsNull(T)) }) it('Should parse Number', () => { - const T = Parse(`number`) + const T = Syntax(`number`) Assert.IsTrue(TypeGuard.IsNumber(T)) }) it('Should parse Object 1', () => { - const T = Parse(`{x: boolean, y: number, z: string, }`) + const T = Syntax(`{x: boolean, y: number, z: string, }`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsTrue(TypeGuard.IsString(T.properties.z)) }) it('Should parse Object 2', () => { - const T = Parse(`{x: boolean; y: number; z: string; }`) + const T = Syntax(`{x: boolean; y: number; z: string; }`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsTrue(TypeGuard.IsString(T.properties.z)) }) it('Should parse Object 3', () => { - const T = Parse(`{ + const T = Syntax(`{ x: boolean y: number z: string @@ -306,7 +219,7 @@ describe('syntax/Parse', () => { Assert.IsTrue(TypeGuard.IsString(T.properties.z)) }) it('Should parse Object 4', () => { - const T = Parse(`{ + const T = Syntax(`{ x: boolean; y: number; z: string; @@ -317,7 +230,7 @@ describe('syntax/Parse', () => { Assert.IsTrue(TypeGuard.IsString(T.properties.z)) }) it('Should parse Object 5', () => { - const T = Parse(`{ + const T = Syntax(`{ x: boolean, y: number, z: string, @@ -328,51 +241,51 @@ describe('syntax/Parse', () => { Assert.IsTrue(TypeGuard.IsString(T.properties.z)) }) it('Should parse Omit 1', () => { - const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z'>`) + const T = Syntax(`Omit<{ x: boolean, y: number, z: string }, 'z'>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsTrue(('z' in T.properties) === false) }) it('Should parse Omit 2', () => { - const T = Parse(`Omit<{ x: boolean, y: number, z: string }, 'z' | 'y'>`) + const T = Syntax(`Omit<{ x: boolean, y: number, z: string }, 'z' | 'y'>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(('y' in T.properties) === false) Assert.IsTrue(('z' in T.properties) === false) }) it('Should parse Parameters', () => { - const T = Parse(`Parameters<(a: number, b: string) => boolean>`) + const T = Syntax(`Parameters<(a: number, b: string) => boolean>`) Assert.IsTrue(TypeGuard.IsTuple(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) Assert.IsTrue(TypeGuard.IsString(T.items![1])) }) it('Should parse Partial', () => { - const T = Parse(`Partial<{ x: boolean, y: number, z: string }>`) + const T = Syntax(`Partial<{ x: boolean, y: number, z: string }>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(('required' in T) === false) }) it('Should parse Pick 1', () => { - const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x' | 'y'>`) + const T = Syntax(`Pick<{ x: boolean, y: number, z: string }, 'x' | 'y'>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) Assert.IsTrue(('z' in T.properties) === false) }) it('Should parse Pick 2', () => { - const T = Parse(`Pick<{ x: boolean, y: number, z: string }, 'x'>`) + const T = Syntax(`Pick<{ x: boolean, y: number, z: string }, 'x'>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(('y' in T.properties) === false) Assert.IsTrue(('z' in T.properties) === false) }) it('Should parse Promise', () => { - const T = Parse(`Promise`) + const T = Syntax(`Promise`) Assert.IsTrue(TypeGuard.IsPromise(T)) Assert.IsTrue(TypeGuard.IsNumber(T.item)) }) it('Should parse ReadonlyOptional', () => { - const T = Parse(`{ readonly x?: boolean, readonly y?: number }`) + const T = Syntax(`{ readonly x?: boolean, readonly y?: number }`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) @@ -382,7 +295,7 @@ describe('syntax/Parse', () => { Assert.IsTrue(TypeGuard.IsOptional(T.properties.y)) }) it('Should parse Readonly', () => { - const T = Parse(`{ readonly x: boolean, readonly y: number }`) + const T = Syntax(`{ readonly x: boolean, readonly y: number }`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsBoolean(T.properties.x)) Assert.IsTrue(TypeGuard.IsReadonly(T.properties.x)) @@ -390,58 +303,58 @@ describe('syntax/Parse', () => { Assert.IsTrue(TypeGuard.IsReadonly(T.properties.y)) }) it('Should parse Record 1', () => { - const T = Parse(`Record`) + const T = Syntax(`Record`) Assert.IsTrue(TypeGuard.IsRecord(T)) Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(.*)$'])) }) it('Should parse Record 2', () => { - const T = Parse(`Record`) + const T = Syntax(`Record`) Assert.IsTrue(TypeGuard.IsRecord(T)) Assert.IsTrue(TypeGuard.IsNumber(T.patternProperties['^(0|[1-9][0-9]*)$'])) }) it('Should parse Record 3', () => { - const T = Parse(`Record<'x' | 'y', number>`) + const T = Syntax(`Record<'x' | 'y', number>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.x)) Assert.IsTrue(TypeGuard.IsNumber(T.properties.y)) }) it('Should parse Recursive', () => { - const T = Type.Recursive(This => Parse({ This }, `{ id: string, nodes: This[] }`)) + const T = Type.Recursive(This => Syntax({ This }, `{ id: string, nodes: This[] }`)) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(TypeGuard.IsString(T.properties.id)) Assert.IsTrue(TypeGuard.IsArray(T.properties.nodes)) Assert.IsTrue(TypeGuard.IsThis(T.properties.nodes.items)) }) it('Should parse Ref', () => { - const T = Parse('foo') + const T = Syntax('foo') Assert.IsTrue(TypeGuard.IsRef(T)) Assert.IsTrue(T.$ref === 'foo') }) it('Should parse Required', () => { - const T = Parse(`Required<{ x?: boolean, y?: number, z?: string }>`) + const T = Syntax(`Required<{ x?: boolean, y?: number, z?: string }>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsEqual(T.required, ['x', 'y', 'z']) }) it('Should parse ReturnType 1', () => { - const T = Parse(`ReturnType<() => { x: 1, y: 2 }>`) + const T = Syntax(`ReturnType<() => { x: 1, y: 2 }>`) Assert.IsTrue(TypeGuard.IsObject(T)) Assert.IsTrue(T.properties.x.const === 1) Assert.IsTrue(T.properties.y.const === 2) }) it('Should parse ReturnType 2', () => { - const T = Parse(`ReturnType`) // generalization issue - Assert.IsTrue(T === undefined) + const T = Syntax(`ReturnType`) // generalization issue + Assert.IsTrue(TypeGuard.IsNever(T)) }) it('Should parse String', () => { - const T = Parse(`string`) + const T = Syntax(`string`) Assert.IsTrue(TypeGuard.IsString(T)) }) it('Should parse Symbol', () => { - const T = Parse(`symbol`) + const T = Syntax(`symbol`) Assert.IsTrue(TypeGuard.IsSymbol(T)) }) it('Should parse Tuple', () => { - const T = Parse(`[0, 1, 2, 3]`) + const T = Syntax(`[0, 1, 2, 3]`) Assert.IsTrue(TypeGuard.IsTuple(T)) Assert.IsTrue(T.items![0].const === 0) Assert.IsTrue(T.items![1].const === 1) @@ -449,32 +362,32 @@ describe('syntax/Parse', () => { Assert.IsTrue(T.items![3].const === 3) }) it('Should parse Uint8Array', () => { - const T = Parse(`Uint8Array`) + const T = Syntax(`Uint8Array`) Assert.IsTrue(TypeGuard.IsUint8Array(T)) }) it('Should parse Undefined', () => { - const T = Parse(`undefined`) + const T = Syntax(`undefined`) Assert.IsTrue(TypeGuard.IsUndefined(T)) }) it('Should parse Union 1', () => { - const T = Parse(`1 | 2`) + const T = Syntax(`1 | 2`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === 1) Assert.IsTrue(T.anyOf[1].const === 2) }) it('Should parse Union 2', () => { - const T = Parse(`1 | (2 | 3)`) + const T = Syntax(`1 | (2 | 3)`) Assert.IsTrue(TypeGuard.IsUnion(T)) Assert.IsTrue(T.anyOf[0].const === 1) Assert.IsTrue(T.anyOf[1].const === 2) Assert.IsTrue(T.anyOf[2].const === 3) }) it('Should parse Unknown', () => { - const T = Parse(`unknown`) + const T = Syntax(`unknown`) Assert.IsTrue(TypeGuard.IsUnknown(T)) }) it('Should parse Void', () => { - const T = Parse(`void`) + const T = Syntax(`void`) Assert.IsTrue(TypeGuard.IsVoid(T)) }) }) diff --git a/tsconfig.json b/tsconfig.json index 34a4b8f21..786056048 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "paths": { "@sinclair/typebox/compiler": ["src/compiler/index.ts"], "@sinclair/typebox/errors": ["src/errors/index.ts"], + "@sinclair/typebox/parser": ["src/parser/index.ts"], "@sinclair/typebox/syntax": ["src/syntax/index.ts"], "@sinclair/typebox/system": ["src/system/index.ts"], "@sinclair/typebox/type": ["src/type/index.ts"], From 6616988aa097e39eb5d1e5e42752365e673b5fea Mon Sep 17 00:00:00 2001 From: Garik Suess Date: Wed, 12 Feb 2025 05:18:28 +0100 Subject: [PATCH 332/369] Documentation (#1160) - Ecosystem: Add typebox-cli --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 37fd0b36e..1dd918493 100644 --- a/readme.md +++ b/readme.md @@ -1705,6 +1705,7 @@ The following is a list of community packages that offer general tooling, extend | [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | +| [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with typebox from the CLI | From 1caee147d894f68e187f75d0abe18bd58c94da3d Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 12 Feb 2025 13:20:12 +0900 Subject: [PATCH 333/369] Documentation --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 1dd918493..23e033c2b 100644 --- a/readme.md +++ b/readme.md @@ -1704,8 +1704,9 @@ The following is a list of community packages that offer general tooling, extend | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | -| [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | | [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with typebox from the CLI | +| [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | + From 44e02afe636609b1b1dd28f696e70574dcce59c0 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 12 Feb 2025 21:24:40 +0900 Subject: [PATCH 334/369] Revision 0.34.17 (#1162) * Add Argument and Instantiate Types * ChangeLog * Version --- changelog/0.34.0.md | 2 + example/prototypes/index.ts | 1 - example/prototypes/readme.md | 38 ------ example/prototypes/recursive-map.ts | 156 ----------------------- package-lock.json | 4 +- package.json | 2 +- readme.md | 73 +++++------ src/compiler/compiler.ts | 6 + src/errors/errors.ts | 3 + src/index.ts | 3 + src/syntax/runtime.ts | 36 +++++- src/syntax/static.ts | 41 +++++- src/type/argument/argument.ts | 41 ++++++ src/type/argument/index.ts | 29 +++++ src/type/guard/kind.ts | 19 ++- src/type/guard/type.ts | 24 +++- src/type/index.ts | 2 + src/type/instantiate/index.ts | 29 +++++ src/type/instantiate/instantiate.ts | 96 ++++++++++++++ src/type/record/record.ts | 64 +++++++++- src/type/remap/index.ts | 29 +++++ src/type/remap/remap.ts | 128 +++++++++++++++++++ src/type/type/javascript.ts | 10 ++ src/type/type/type.ts | 3 + src/value/check/check.ts | 9 +- src/value/create/create.ts | 5 + test/runtime/compiler/argument.ts | 41 ++++++ test/runtime/compiler/index.ts | 1 + test/runtime/compiler/validate.ts | 1 + test/runtime/syntax/syntax.ts | 29 +++++ test/runtime/type/guard/kind/argument.ts | 14 ++ test/runtime/type/guard/kind/index.ts | 1 + test/runtime/type/guard/type/argument.ts | 14 ++ test/runtime/type/guard/type/index.ts | 1 + test/runtime/type/guard/type/record.ts | 42 +++++- test/runtime/value/check/argument.ts | 47 +++++++ test/runtime/value/check/index.ts | 1 + test/runtime/value/create/argument.ts | 10 ++ test/runtime/value/create/index.ts | 1 + test/static/argument.ts | 21 +++ test/static/index.ts | 1 + 41 files changed, 816 insertions(+), 262 deletions(-) delete mode 100644 example/prototypes/recursive-map.ts create mode 100644 src/type/argument/argument.ts create mode 100644 src/type/argument/index.ts create mode 100644 src/type/instantiate/index.ts create mode 100644 src/type/instantiate/instantiate.ts create mode 100644 src/type/remap/index.ts create mode 100644 src/type/remap/remap.ts create mode 100644 test/runtime/compiler/argument.ts create mode 100644 test/runtime/type/guard/kind/argument.ts create mode 100644 test/runtime/type/guard/type/argument.ts create mode 100644 test/runtime/value/check/argument.ts create mode 100644 test/runtime/value/create/argument.ts create mode 100644 test/static/argument.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index b3645d6e9..a41e509af 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.17](https://github.com/sinclairzx81/typebox/pull/1162) + - Add Argument() and Instantiate() Types and Instancing via Syntax support. - [Revision 0.34.16](https://github.com/sinclairzx81/typebox/pull/1156) - Export TypeBox String Parsing Infrastructure - [Revision 0.34.15](https://github.com/sinclairzx81/typebox/pull/1148) diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index e7119a8aa..0dfa2301b 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -32,4 +32,3 @@ export * from './options' export * from './partial-deep' export * from './union-enum' export * from './union-oneof' -export * from './recursive-map' diff --git a/example/prototypes/readme.md b/example/prototypes/readme.md index f3aea908b..c514e3456 100644 --- a/example/prototypes/readme.md +++ b/example/prototypes/readme.md @@ -81,42 +81,4 @@ import { Options } from './prototypes' const A = Options(Type.String(), { foo: 1 }) // Options type A = typeof A extends { foo: number } ? true : false // true: foo property is observable to the type system -``` - -## Recursive Map - -The Recursive Map type enables deep structural remapping of a type and it's internal constituents. This type accepts a TSchema type and a mapping type function (expressed via HKT). The HKT is applied when traversing the type and it's interior. The mapping HKT can apply conditional tests to each visited type to remap into a new form. The following augments a schematic via Options, and conditionally remaps any schema with an default annotation to make it optional. - -```typescript -import { Type, TOptional, Static, TSchema } from '@sinclair/typebox' - -import { TRecursiveMap, TMappingType, Options } from './prototypes' - -// ------------------------------------------------------------------ -// StaticDefault -// ------------------------------------------------------------------ -export interface StaticDefaultMapping extends TMappingType { - output: ( - this['input'] extends TSchema // if input schematic contains an default - ? this['input'] extends { default: unknown } // annotation, remap it to be optional, - ? TOptional // otherwise just return the schema as is. - : this['input'] - : this['input'] - ) -} -export type StaticDefault = ( - Static> -) - -// ------------------------------------------------------------------ -// Usage -// ------------------------------------------------------------------ - -const T = Type.Object({ - x: Options(Type.String(), { default: 'hello' }), - y: Type.String() -}) - -type T = StaticDefault // { x?: string, y: string } -type S = Static // { x: string, y: string } ``` \ No newline at end of file diff --git a/example/prototypes/recursive-map.ts b/example/prototypes/recursive-map.ts deleted file mode 100644 index dfa35b004..000000000 --- a/example/prototypes/recursive-map.ts +++ /dev/null @@ -1,156 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/prototypes - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as Types from '@sinclair/typebox' - -// ------------------------------------------------------------------ -// Mapping: Functions and Type -// ------------------------------------------------------------------ -export type TMappingFunction = (schema: Types.TSchema) => Types.TSchema - -export interface TMappingType { - input: unknown - output: unknown -} -// ------------------------------------------------------------------ -// Record Parameters -// ------------------------------------------------------------------ -function GetRecordPattern(record: Types.TRecord): string { - return globalThis.Object.getOwnPropertyNames(record.patternProperties)[0] -} -function GetRecordKey(record: Types.TRecord): Types.TSchema { - const pattern = GetRecordPattern(record) - return ( - pattern === Types.PatternStringExact ? Types.String() : - pattern === Types.PatternNumberExact ? Types.Number() : - pattern === Types.PatternBooleanExact ? Types.Boolean() : - Types.String({ pattern }) - ) -} -function GetRecordValue(record: Types.TRecord): Types.TSchema { - return record.patternProperties[GetRecordPattern(record)] -} -// ------------------------------------------------------------------ -// Traversal -// ------------------------------------------------------------------ -// prettier-ignore -type TApply = Result -// prettier-ignore -type TFromProperties -}> = Result -function FromProperties(properties: Types.TProperties, func: TMappingFunction): Types.TProperties { - return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { - return {...result, [key]: RecursiveMap(properties[key], func) } - }, {}) -} -// prettier-ignore -type TFromRest = ( - Types extends [infer Left extends Types.TSchema, ...infer Right extends Types.TSchema[]] - ? TFromRest]> - : Result -) -function FromRest(types: Types.TSchema[], func: TMappingFunction): Types.TSchema[] { - return types.map(type => RecursiveMap(type, func)) -} -// prettier-ignore -type TFromType -)> = Result -function FromType(type: Types.TSchema, func: TMappingFunction): Types.TSchema { - return func(type) -} -// ------------------------------------------------------------------ -// TRecursiveMap -// ------------------------------------------------------------------ -/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ -// prettier-ignore -export type TRecursiveMap, - // Maps the Interior Parameterized Types - Interior extends Types.TSchema = ( - Exterior extends Types.TConstructor ? Types.TConstructor, TFromType> : - Exterior extends Types.TFunction ? Types.TFunction, TFromType> : - Exterior extends Types.TIntersect ? Types.TIntersect> : - Exterior extends Types.TUnion ? Types.TUnion> : - Exterior extends Types.TTuple ? Types.TTuple> : - Exterior extends Types.TArray ? Types.TArray>: - Exterior extends Types.TAsyncIterator ? Types.TAsyncIterator> : - Exterior extends Types.TIterator ? Types.TIterator> : - Exterior extends Types.TPromise ? Types.TPromise> : - Exterior extends Types.TObject ? Types.TObject> : - Exterior extends Types.TRecord ? Types.TRecordOrObject, TFromType> : - Exterior - ), - // Modifiers Derived from Exterior Type Mapping - IsOptional extends number = Exterior extends Types.TOptional ? 1 : 0, - IsReadonly extends number = Exterior extends Types.TReadonly ? 1 : 0, - Result extends Types.TSchema = ( - [IsReadonly, IsOptional] extends [1, 1] ? Types.TReadonlyOptional : - [IsReadonly, IsOptional] extends [0, 1] ? Types.TOptional : - [IsReadonly, IsOptional] extends [1, 0] ? Types.TReadonly : - Interior - ) -> = Result -/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ -// prettier-ignore -export function RecursiveMap(type: Types.TSchema, func: TMappingFunction): Types.TSchema { - // Maps the Exterior Type - const exterior = Types.CloneType(FromType(type, func), type) - // Maps the Interior Parameterized Types - const interior = ( - Types.KindGuard.IsConstructor(type) ? Types.Constructor(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : - Types.KindGuard.IsFunction(type) ? Types.Function(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : - Types.KindGuard.IsIntersect(type) ? Types.Intersect(FromRest(type.allOf, func), exterior) : - Types.KindGuard.IsUnion(type) ? Types.Union(FromRest(type.anyOf, func), exterior) : - Types.KindGuard.IsTuple(type) ? Types.Tuple(FromRest(type.items || [], func), exterior) : - Types.KindGuard.IsArray(type) ? Types.Array(FromType(type.items, func), exterior) : - Types.KindGuard.IsAsyncIterator(type) ? Types.AsyncIterator(FromType(type.items, func), exterior) : - Types.KindGuard.IsIterator(type) ? Types.Iterator(FromType(type.items, func), exterior) : - Types.KindGuard.IsPromise(type) ? Types.Promise(FromType(type.items, func), exterior) : - Types.KindGuard.IsObject(type) ? Types.Object(FromProperties(type.properties, func), exterior) : - Types.KindGuard.IsRecord(type) ? Types.Record(FromType(GetRecordKey(type), func), FromType(GetRecordValue(type), func), exterior) : - Types.CloneType(exterior, exterior) - ) - // Modifiers Derived from Exterior Type Mapping - const isOptional = Types.KindGuard.IsOptional(exterior) - const isReadonly = Types.KindGuard.IsOptional(exterior) - return ( - isOptional && isReadonly ? Types.ReadonlyOptional(interior) : - isOptional ? Types.Optional(interior) : - isReadonly ? Types.Readonly(interior) : - interior - ) -} - - - diff --git a/package-lock.json b/package-lock.json index 373aed4f7..eab2429fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.16", + "version": "0.34.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.16", + "version": "0.34.17", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 1bf33bd7b..217a3e6d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.16", + "version": "0.34.17", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 23e033c2b..ba0ccc901 100644 --- a/readme.md +++ b/readme.md @@ -731,46 +731,42 @@ Object properties can be modified with Readonly and Optional. The following tabl ### Generic Types -Generic types can be created with functions. TypeBox types extend the TSchema interface so you should constrain parameters to this type. The following creates a generic Vector type. +Generic types can be created with generic functions ```typescript -import { Type, type Static, type TSchema } from '@sinclair/typebox' - -const Vector = (T: T) => - Type.Object({ // type Vector = { - x: T, // x: T, - y: T, // y: T, - z: T // z: T - }) // } +const Nullable = (T: T) => { // type Nullable = T | null + return Type.Union([T, Type.Null()) +} -const NumberVector = Vector(Type.Number()) // type NumberVector = Vector +const T = Nullable(Type.String()) // type T = Nullable ``` -Generic types are often used to create aliases for complex types. The following creates a Nullable generic type. +Generic types can also be created with Argument types ```typescript -const Nullable = (schema: T) => Type.Union([schema, Type.Null()]) - -const T = Nullable(Type.String()) // const T = { - // anyOf: [ - // { type: 'string' }, - // { type: 'null' } - // ] - // } - -type T = Static // type T = string | null +const Vector = Type.Object({ // type Vector = { + x: Type.Argument(0), // x: A_0, + y: Type.Argument(1), // y: A_1, + z: Type.Argument(2), // z: A_2 +}) // } + +const T = Type.Instantiate(Vector, [ // type T = Vector< + Type.Boolean(), // boolean, + Type.Number(), // number, + Type.String() // string +]) // > ``` ### Recursive Types -Use the Recursive function to create a singular recursive type. +Use the Recursive function to create recursive types ```typescript -const Node = Type.Recursive(Self => Type.Object({ // const Node = { +const Node = Type.Recursive(This => Type.Object({ // const Node = { id: Type.String(), // $id: 'Node', - nodes: Type.Array(Self) // type: 'object', + nodes: Type.Array(This) // type: 'object', }), { $id: 'Node' }) // properties: { // id: { // type: 'string' @@ -1394,28 +1390,27 @@ const S = Syntax({ T }, `{ x: T, y: T, z: T }`) // const S: TObject<{ ### Generics -Generic syntax types can be created using parameterized types. +Generic types can be created by passing Argument types as parameters. ```typescript -// Generic Syntax Type - -const Vector = (T: T) => Syntax({ T: Syntax(T) }, `{ - x: T, - y: T, - z: T +// Generic Vector Type + +const Vector = Syntax({ // type Vector = { + X: Type.Argument(0), // x: X + Y: Type.Argument(1), // y: Y, + Z: Type.Argument(2) // z: Z +}, // } +`{ + x: X, + y: Y, + z: Z }`) +// Instanced Vector Type -// Instanced Generic Syntax Type - -const NumberVector = Vector('number') // const NumberVector: TObject<{ - // x: TNumber, - // y: TNumber, - // z: TNumber - // }> +const Up = Syntax({ Vector }, `Vector<0, 1, 0>`) // type Up = Vector<0, 1, 0> ``` - ## TypeRegistry diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index eb5c67c40..05e2e905d 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -41,6 +41,7 @@ import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined' import type { TSchema } from '../type/schema/index' import type { TAsyncIterator } from '../type/async-iterator/index' import type { TAny } from '../type/any/index' +import type { TArgument } from '../type/argument/index' import type { TArray } from '../type/array/index' import type { TBigInt } from '../type/bigint/index' import type { TBoolean } from '../type/boolean/index' @@ -247,6 +248,9 @@ export namespace TypeCompiler { function* FromAny(schema: TAny, references: TSchema[], value: string): IterableIterator { yield 'true' } + function* FromArgument(schema: TArgument, references: TSchema[], value: string): IterableIterator { + yield 'true' + } function* FromArray(schema: TArray, references: TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')] @@ -490,6 +494,8 @@ export namespace TypeCompiler { switch (schema_[Kind]) { case 'Any': return yield* FromAny(schema_, references_, value) + case 'Argument': + return yield* FromArgument(schema_, references_, value) case 'Array': return yield* FromArray(schema_, references_, value) case 'AsyncIterator': diff --git a/src/errors/errors.ts b/src/errors/errors.ts index de62cd970..87ef5e904 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -222,6 +222,7 @@ function Create(errorType: ValueErrorType, schema: TSchema, path: string, value: // Types // -------------------------------------------------------------------------- function* FromAny(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator {} +function* FromArgument(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator {} function* FromArray(schema: TArray, references: TSchema[], path: string, value: any): IterableIterator { if (!IsArray(value)) { return yield Create(ValueErrorType.Array, schema, path, value) @@ -558,6 +559,8 @@ function* Visit(schema: T, references: TSchema[], path: strin switch (schema_[Kind]) { case 'Any': return yield* FromAny(schema_, references_, path, value) + case 'Argument': + return yield* FromArgument(schema_, references_, path, value) case 'Array': return yield* FromArray(schema_, references_, path, value) case 'AsyncIterator': diff --git a/src/index.ts b/src/index.ts index f17cc7d15..847758ecd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,6 +44,7 @@ export * from './type/symbols/index' // ------------------------------------------------------------------ export * from './type/any/index' export * from './type/array/index' +export * from './type/argument/index' export * from './type/async-iterator/index' export * from './type/awaited/index' export * from './type/bigint/index' @@ -60,6 +61,7 @@ export * from './type/extract/index' export * from './type/function/index' export * from './type/indexed/index' export * from './type/instance-type/index' +export * from './type/instantiate/index' export * from './type/integer/index' export * from './type/intersect/index' export * from './type/iterator/index' @@ -85,6 +87,7 @@ export * from './type/record/index' export * from './type/recursive/index' export * from './type/ref/index' export * from './type/regexp/index' +export * from './type/remap/index' export * from './type/required/index' export * from './type/rest/index' export * from './type/return-type/index' diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 8a62f2014..c5366d277 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -60,16 +60,28 @@ function DestructureRight(values: T[]): [T[], T | undefined] { : [values, undefined] } // ------------------------------------------------------------------ -// Deref +// Dereference // ------------------------------------------------------------------ -const Deref = (context: t.TProperties, key: string): t.TSchema => { +const Dereference = (context: t.TProperties, key: string): t.TSchema => { return key in context ? context[key] : t.Ref(key) } // ------------------------------------------------------------------ +// GenericReference +// ------------------------------------------------------------------ +function GenericReferenceMapping(results: unknown[], context: t.TProperties) { + const target = Dereference(context, results[0] as string) + return t.Instantiate(target, results[2] as t.TSchema[]) +} +const GenericReference = Runtime.Tuple([Runtime.Ident(), Runtime.Const(LAngle), Runtime.Ref('Elements'), Runtime.Const(RAngle)], (results, context) => GenericReferenceMapping(results, context)) +// ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ +function ReferenceMapping(result: string, context: t.TProperties) { + const target = Dereference(context, result) + return target +} // prettier-ignore -const Reference = Runtime.Ident((value, context: t.TProperties) => Deref(context, value)) +const Reference = Runtime.Ident((result, context) => ReferenceMapping(result, context)) // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ @@ -166,6 +178,7 @@ const Base = Runtime.Union([ Runtime.Ref('FunctionParameters'), Runtime.Ref('InstanceType'), Runtime.Ref('ReturnType'), + Runtime.Ref('Argument'), Runtime.Ref('Awaited'), Runtime.Ref('Array'), Runtime.Ref('Record'), @@ -182,6 +195,7 @@ const Base = Runtime.Union([ Runtime.Ref('Uncapitalize'), Runtime.Ref('Date'), Runtime.Ref('Uint8Array'), + Runtime.Ref('GenericReference'), Runtime.Ref('Reference') ])]) ], BaseMapping) @@ -465,6 +479,20 @@ const ReturnType = Runtime.Tuple([ Runtime.Const(RAngle), ], value => t.ReturnType(value[2])) // ------------------------------------------------------------------ +// Argument +// ------------------------------------------------------------------ +// prettier-ignore +const Argument = Runtime.Tuple([ + Runtime.Const('Argument'), + Runtime.Const(LAngle), + Runtime.Ref('Type'), + Runtime.Const(RAngle), +], results => { + return t.KindGuard.IsLiteralNumber(results[2]) + ? t.Argument(Math.trunc(results[2].const)) + : t.Never() +}) +// ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ // prettier-ignore @@ -658,6 +686,7 @@ export const Module = new Runtime.Module({ Mapped, AsyncIterator, Iterator, + Argument, Awaited, Array, Record, @@ -678,5 +707,6 @@ export const Module = new Runtime.Module({ Uncapitalize, Date, Uint8Array, + GenericReference, Reference, }) diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 4cd125565..fd06a402e 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -136,20 +136,38 @@ type Delimit = ], DelimitMapping> ) // ------------------------------------------------------------------ -// Deref +// Dereference // ------------------------------------------------------------------ // prettier-ignore -type Deref = ( +type Dereference = ( Ref extends keyof Context ? Context[Ref] : t.TRef ) // ------------------------------------------------------------------ +// GenericReference +// ------------------------------------------------------------------ +// prettier-ignore +interface GenericReferenceMapping extends Static.IMapping { + output: this['context'] extends t.TProperties + ? this['input'] extends [infer Reference extends string, LAngle, infer Parameters extends t.TSchema[], RAngle] + ? t.TInstantiate, [...Parameters]> + : never + : never +} +// prettier-ignore +type GenericReference = Static.Tuple<[ + Static.Ident, + Static.Const, + Elements, + Static.Const, +], GenericReferenceMapping> +// ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ // prettier-ignore interface ReferenceMapping extends Static.IMapping { output: this['context'] extends t.TProperties ? this['input'] extends string - ? Deref + ? Dereference : never : never } @@ -267,6 +285,7 @@ type Base = Static.Union<[ Iterator, ConstructorParameters, FunctionParameters, + Argument, InstanceType, ReturnType, Awaited, @@ -285,6 +304,7 @@ type Base = Static.Union<[ Uncapitalize, Date, Uint8Array, + GenericReference, Reference ]>]> ], BaseMapping> @@ -587,6 +607,21 @@ type ReturnType = Static.Tuple<[ Static.Const<'ReturnType'>, Static.Const, Type, Static.Const, ], ReturnTypeMapping> // ------------------------------------------------------------------ +// Argument +// ------------------------------------------------------------------ +// prettier-ignore +interface ArgumentMapping extends Static.IMapping { + output: this['input'] extends ['Argument', LAngle, infer Type extends t.TSchema, RAngle] + ? Type extends t.TLiteral + ? t.TArgument + : t.TNever + : never +} +// prettier-ignore +type Argument = Static.Tuple<[ + Static.Const<'Argument'>, Static.Const, Type, Static.Const, +], ArgumentMapping> +// ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ // prettier-ignore diff --git a/src/type/argument/argument.ts b/src/type/argument/argument.ts new file mode 100644 index 000000000..a6c63f286 --- /dev/null +++ b/src/type/argument/argument.ts @@ -0,0 +1,41 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { CreateType } from '../create/type' +import type { TSchema } from '../schema/index' +import { Kind } from '../symbols/index' + +export interface TArgument extends TSchema { + [Kind]: 'Argument' + static: unknown + index: Index +} +/** `[JavaScript]` Creates an Argument Type. */ +export function Argument(index: Index): TArgument { + return CreateType({ [Kind]: 'Argument', index }) as never +} diff --git a/src/type/argument/index.ts b/src/type/argument/index.ts new file mode 100644 index 000000000..186ff4ee6 --- /dev/null +++ b/src/type/argument/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './argument' diff --git a/src/type/guard/kind.ts b/src/type/guard/kind.ts index 9ca359fdb..6447412f4 100644 --- a/src/type/guard/kind.ts +++ b/src/type/guard/kind.ts @@ -29,15 +29,13 @@ THE SOFTWARE. import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TransformOptions } from '../transform/index' -import type { TTemplateLiteral } from '../template-literal/index' + +import type { TAny } from '../any/index' +import type { TArgument } from '../argument/index' import type { TArray } from '../array/index' +import type { TAsyncIterator } from '../async-iterator/index' import type { TBoolean } from '../boolean/index' import type { TComputed } from '../computed/index' -import type { TRecord } from '../record/index' -import type { TString } from '../string/index' -import type { TUnion } from '../union/index' -import type { TAny } from '../any/index' -import type { TAsyncIterator } from '../async-iterator/index' import type { TBigInt } from '../bigint/index' import type { TConstructor } from '../constructor/index' import type { TFunction } from '../function/index' @@ -55,14 +53,18 @@ import type { TObject, TProperties } from '../object/index' import type { TOptional } from '../optional/index' import type { TPromise } from '../promise/index' import type { TReadonly } from '../readonly/index' +import type { TRecord } from '../record/index' import type { TRef } from '../ref/index' import type { TRegExp } from '../regexp/index' import type { TSchema } from '../schema/index' +import type { TString } from '../string/index' import type { TSymbol } from '../symbol/index' +import type { TTemplateLiteral } from '../template-literal/index' import type { TTuple } from '../tuple/index' import type { TUint8Array } from '../uint8array/index' import type { TUndefined } from '../undefined/index' import type { TUnknown } from '../unknown/index' +import type { TUnion } from '../union/index' import type { TUnsafe } from '../unsafe/index' import type { TVoid } from '../void/index' import type { TDate } from '../date/index' @@ -80,6 +82,10 @@ export function IsOptional(value: T): value is TOptional { export function IsAny(value: unknown): value is TAny { return IsKindOf(value, 'Any') } +/** `[Kind-Only]` Returns true if the given value is TArgument */ +export function IsArgument(value: unknown): value is TArgument { + return IsKindOf(value, 'Argument') +} /** `[Kind-Only]` Returns true if the given value is TArray */ export function IsArray(value: unknown): value is TArray { return IsKindOf(value, 'Array') @@ -261,6 +267,7 @@ export function IsSchema(value: unknown): value is TSchema { // prettier-ignore return ( IsAny(value) || + IsArgument(value) || IsArray(value) || IsBoolean(value) || IsBigInt(value) || diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts index 521522ffd..9df35670a 100644 --- a/src/type/guard/type.ts +++ b/src/type/guard/type.ts @@ -30,15 +30,13 @@ import * as ValueGuard from './value' import { Kind, Hint, TransformKind, ReadonlyKind, OptionalKind } from '../symbols/index' import { TypeBoxError } from '../error/index' import { TransformOptions } from '../transform/index' -import type { TTemplateLiteral } from '../template-literal/index' + +import type { TAny } from '../any/index' +import type { TArgument } from '../argument/index' import type { TArray } from '../array/index' +import type { TAsyncIterator } from '../async-iterator/index' import type { TBoolean } from '../boolean/index' import type { TComputed } from '../computed/index' -import type { TRecord } from '../record/index' -import type { TString } from '../string/index' -import type { TUnion } from '../union/index' -import type { TAny } from '../any/index' -import type { TAsyncIterator } from '../async-iterator/index' import type { TBigInt } from '../bigint/index' import type { TConstructor } from '../constructor/index' import type { TFunction } from '../function/index' @@ -56,13 +54,17 @@ import type { TObject, TAdditionalProperties, TProperties } from '../object/inde import type { TOptional } from '../optional/index' import type { TPromise } from '../promise/index' import type { TReadonly } from '../readonly/index' +import type { TRecord } from '../record/index' import type { TRef } from '../ref/index' import type { TRegExp } from '../regexp/index' import type { TSchema } from '../schema/index' +import type { TString } from '../string/index' import type { TSymbol } from '../symbol/index' +import type { TTemplateLiteral } from '../template-literal/index' import type { TTuple } from '../tuple/index' import type { TUint8Array } from '../uint8array/index' import type { TUndefined } from '../undefined/index' +import type { TUnion } from '../union/index' import type { TUnknown } from '../unknown/index' import type { TUnsafe } from '../unsafe/index' import type { TVoid } from '../void/index' @@ -72,6 +74,7 @@ import type { TThis } from '../recursive/index' export class TypeGuardUnknownTypeError extends TypeBoxError {} const KnownTypes = [ + 'Argument', 'Any', 'Array', 'AsyncIterator', @@ -171,6 +174,14 @@ export function IsAny(value: unknown): value is TAny { IsOptionalString(value.$id) ) } +/** Returns true if the given value is TArgument */ +export function IsArgument(value: unknown): value is TArgument { + // prettier-ignore + return ( + IsKindOf(value, 'Argument') && + ValueGuard.IsNumber(value.index) + ) +} /** Returns true if the given value is TArray */ export function IsArray(value: unknown): value is TArray { return ( @@ -608,6 +619,7 @@ export function IsSchema(value: unknown): value is TSchema { ValueGuard.IsObject(value) ) && ( IsAny(value) || + IsArgument(value) || IsArray(value) || IsBoolean(value) || IsBigInt(value) || diff --git a/src/type/index.ts b/src/type/index.ts index 661e36678..cafc2a7f6 100644 --- a/src/type/index.ts +++ b/src/type/index.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './any/index' +export * from './argument/index' export * from './array/index' export * from './async-iterator/index' export * from './awaited/index' @@ -49,6 +50,7 @@ export * from './guard/index' export * from './helpers/index' export * from './indexed/index' export * from './instance-type/index' +export * from './instantiate/index' export * from './integer/index' export * from './intersect/index' export * from './intrinsic/index' diff --git a/src/type/instantiate/index.ts b/src/type/instantiate/index.ts new file mode 100644 index 000000000..5f0a519b0 --- /dev/null +++ b/src/type/instantiate/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './instantiate' diff --git a/src/type/instantiate/instantiate.ts b/src/type/instantiate/instantiate.ts new file mode 100644 index 000000000..b42628667 --- /dev/null +++ b/src/type/instantiate/instantiate.ts @@ -0,0 +1,96 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TArgument } from '../argument/index' +import { type TNever, Never } from '../never/index' +import { type TReadonlyOptional, ReadonlyOptional } from '../readonly-optional/index' +import { type TReadonly, Readonly } from '../readonly/index' +import { type TOptional, Optional } from '../optional/index' + +import { Remap, type TRemap, type TMapping as TRemapMapping } from '../remap/index' +import * as KindGuard from '../guard/kind' + +// ------------------------------------------------------------------ +// InstantiateArgument +// ------------------------------------------------------------------ +// prettier-ignore +type TInstantiateArgument ? 1 : 0, + IsArgumentOptional extends number = Argument extends TOptional ? 1 : 0, + Result extends TSchema = ( + [IsArgumentReadonly, IsArgumentOptional] extends [1, 1] ? TReadonlyOptional : + [IsArgumentReadonly, IsArgumentOptional] extends [0, 1] ? TOptional : + [IsArgumentReadonly, IsArgumentOptional] extends [1, 0] ? TReadonly : + Type + ) +> = Result +// prettier-ignore +function InstantiateArgument(argument: Argument, type: Type): TInstantiateArgument { + const isReadonly = KindGuard.IsReadonly(argument) + const isOptional = KindGuard.IsOptional(argument) + return ( + isReadonly && isOptional ? ReadonlyOptional(type) : + isReadonly && !isOptional ? Readonly(type) : + !isReadonly && isOptional ? Optional(type) : + type + ) as never +} +// ------------------------------------------------------------------ +// Instantiate +// ------------------------------------------------------------------ +// prettier-ignore +interface TInstantiateArguments extends TRemapMapping { + output: ( + this['input'] extends TArgument + ? Index extends keyof Arguments + ? Arguments[Index] extends TSchema + ? TInstantiateArgument + : TNever + : TNever + : this['input'] + ) +} +/** `[JavaScript]` Instantiates a type with the given parameters */ +// prettier-ignore +export type TInstantiate = ( + TRemap> +) +/** `[JavaScript]` Instantiates a type with the given parameters */ +// prettier-ignore +export function Instantiate(type: Type, args: [...Arguments]): TInstantiate { + return Remap(type, (type) => { + return KindGuard.IsArgument(type) + ? type.index in args + ? KindGuard.IsSchema(args[type.index]) + ? InstantiateArgument(type, args[type.index]) + : Never() + : Never() + : type + }) as never +} diff --git a/src/type/record/record.ts b/src/type/record/record.ts index 265495a66..e22e180df 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -32,16 +32,17 @@ import type { TSchema } from '../schema/index' import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' import { type TAny } from '../any/index' +import { type TBoolean } from '../boolean/index' import { type TComputed, Computed } from '../computed/index' import { type TEnumRecord, type TEnum } from '../enum/index' import { type TInteger } from '../integer/index' import { type TLiteral, type TLiteralValue } from '../literal/index' import { type TNever, Never } from '../never/index' -import { type TNumber } from '../number/index' +import { type TNumber, Number } from '../number/index' import { type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions, Object } from '../object/index' import { type TRef, Ref } from '../ref/index' import { type TRegExp } from '../regexp/index' -import { type TString } from '../string/index' +import { type TString, String } from '../string/index' import { type TUnion, Union } from '../union/index' import { IsTemplateLiteralFinite, TIsTemplateLiteralFinite, type TTemplateLiteral } from '../template-literal/index' @@ -55,7 +56,7 @@ import { IsUndefined } from '../guard/value' // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsInteger, IsLiteral, IsAny, IsNever, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion, IsRef, IsComputed } from '../guard/kind' +import { IsInteger, IsLiteral, IsAny, IsBoolean, IsNever, IsNumber, IsString, IsRegExp, IsTemplateLiteral, IsUnion, IsRef, IsComputed } from '../guard/kind' // ------------------------------------------------------------------ // RecordCreateFromPattern @@ -178,6 +179,17 @@ function FromNeverKey(_key: Key, type: return RecordCreateFromPattern(PatternNeverExact, type, options) as never } // ------------------------------------------------------------------ +// TromBooleanKey +// ------------------------------------------------------------------ +// prettier-ignore +type TFromBooleanKey<_Key extends TBoolean, Type extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromBooleanKey(_key: Key, type: Type, options: ObjectOptions): TFromBooleanKey { + return Object({ true: type, false: type }, options) +} +// ------------------------------------------------------------------ // FromIntegerKey // ------------------------------------------------------------------ // prettier-ignore @@ -228,6 +240,7 @@ export type TRecordOrObject = ( Key extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) Key extends TUnion ? TFromUnionKey : Key extends TLiteral ? TFromLiteralKey : + Key extends TBoolean ? TFromBooleanKey : Key extends TInteger ? TFromIntegerKey : Key extends TNumber ? TFromNumberKey : Key extends TRegExp ? TFromRegExpKey : @@ -249,6 +262,7 @@ export function Record(key: Key, type IsUnion(key) ? FromUnionKey(key.anyOf, type, options) : IsTemplateLiteral(key) ? FromTemplateLiteralKey(key, type, options) : IsLiteral(key) ? FromLiteralKey(key.const, type, options) : + IsBoolean(key) ? FromBooleanKey(key, type, options) : IsInteger(key) ? FromIntegerKey(key, type, options) : IsNumber(key) ? FromNumberKey(key, type, options) : IsRegExp(key) ? FromRegExpKey(key, type, options) : @@ -258,3 +272,47 @@ export function Record(key: Key, type Never(options) ) as never } + +// ------------------------------------------------------------------ +// Record Utilities +// ------------------------------------------------------------------ +/** Gets the Records Pattern */ +export function RecordPattern(record: TRecord): string { + return globalThis.Object.getOwnPropertyNames(record.patternProperties)[0] +} +// ------------------------------------------------------------------ +// RecordKey +// ------------------------------------------------------------------ +/** Gets the Records Key Type */ +// prettier-ignore +export type TRecordKey = ( + Type extends TRecord + ? ( + Key extends TNumber ? TNumber : + Key extends TString ? TString : + TString + ) : TString +) +/** Gets the Records Key Type */ +// prettier-ignore +export function RecordKey(type: Type): TRecordKey { + const pattern = RecordPattern(type) + return ( + pattern === PatternStringExact ? String() : + pattern === PatternNumberExact ? Number() : + String({ pattern }) + ) as never +} +// ------------------------------------------------------------------ +// RecordValue +// ------------------------------------------------------------------ +/** Gets a Record Value Type */ +// prettier-ignore +export type TRecordValue = ( + Type extends TRecord ? Value : TNever +) +/** Gets a Record Value Type */ +// prettier-ignore +export function RecordValue(type: Type): TRecordValue { + return type.patternProperties[RecordPattern(type)] as never +} diff --git a/src/type/remap/index.ts b/src/type/remap/index.ts new file mode 100644 index 000000000..f05c8c5db --- /dev/null +++ b/src/type/remap/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './remap' diff --git a/src/type/remap/remap.ts b/src/type/remap/remap.ts new file mode 100644 index 000000000..3354b87ca --- /dev/null +++ b/src/type/remap/remap.ts @@ -0,0 +1,128 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as t from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Callback +// ------------------------------------------------------------------ +export type TCallback = (schema: t.TSchema) => t.TSchema +// ------------------------------------------------------------------ +// Mapping +// ------------------------------------------------------------------ +export interface TMapping { + input: unknown + output: unknown +} +// ------------------------------------------------------------------ +// Apply +// ------------------------------------------------------------------ +// prettier-ignore +type TApply = Result +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperties +}> = Result +function FromProperties(properties: t.TProperties, func: TCallback): t.TProperties { + return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { + return { ...result, [key]: Remap(properties[key], func) } + }, {}) +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTypes = ( + Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] + ? TFromTypes]> + : Result +) +function FromTypes(types: t.TSchema[], callback: TCallback): t.TSchema[] { + return types.map((type) => Remap(type, callback)) +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ +// prettier-ignore +type TFromType +)> = Result +function FromType(type: t.TSchema, callback: TCallback): t.TSchema { + return callback(type) +} +// ------------------------------------------------------------------ +// TRemap +// ------------------------------------------------------------------ +/** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ +// prettier-ignore +export type TRemap, + // Maps the Interior Parameterized Types + Result extends t.TSchema = ( + Mapped extends t.TConstructor ? t.TConstructor, TFromType> : + Mapped extends t.TFunction ? t.TFunction, TFromType> : + Mapped extends t.TIntersect ? t.TIntersect> : + Mapped extends t.TUnion ? t.TUnion> : + Mapped extends t.TTuple ? t.TTuple> : + Mapped extends t.TArray ? t.TArray>: + Mapped extends t.TAsyncIterator ? t.TAsyncIterator> : + Mapped extends t.TIterator ? t.TIterator> : + Mapped extends t.TPromise ? t.TPromise> : + Mapped extends t.TObject ? t.TObject> : + Mapped extends t.TRecord ? t.TRecordOrObject, TFromType> : + Mapped + ) +> = Result +/** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ +// prettier-ignore +export function Remap(type: t.TSchema, callback: TCallback): t.TSchema { + // Map incoming type + const mapped = t.CloneType(FromType(type, callback)) + // Return remapped interior + return ( + t.KindGuard.IsConstructor(type) ? t.Constructor(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : + t.KindGuard.IsFunction(type) ? t.Function(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : + t.KindGuard.IsIntersect(type) ? t.Intersect(FromTypes(type.allOf, callback), mapped) : + t.KindGuard.IsUnion(type) ? t.Union(FromTypes(type.anyOf, callback), mapped) : + t.KindGuard.IsTuple(type) ? t.Tuple(FromTypes(type.items || [], callback), mapped) : + t.KindGuard.IsArray(type) ? t.Array(FromType(type.items, callback), mapped) : + t.KindGuard.IsAsyncIterator(type) ? t.AsyncIterator(FromType(type.items, callback), mapped) : + t.KindGuard.IsIterator(type) ? t.Iterator(FromType(type.items, callback), mapped) : + t.KindGuard.IsPromise(type) ? t.Promise(FromType(type.items, callback), mapped) : + t.KindGuard.IsObject(type) ? t.Object(FromProperties(type.properties, callback), mapped) : + t.KindGuard.IsRecord(type) ? t.Record(FromType(t.RecordKey(type), callback), FromType(t.RecordValue(type), callback), mapped) : + t.CloneType(mapped) + ) +} diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts index 13705e5d7..daba64887 100644 --- a/src/type/type/javascript.ts +++ b/src/type/type/javascript.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { JsonTypeBuilder } from './json' +import { Argument, type TArgument } from '../argument/index' import { AsyncIterator, type TAsyncIterator } from '../async-iterator/index' import { Awaited, type TAwaited } from '../awaited/index' import { BigInt, type TBigInt, type BigIntOptions } from '../bigint/index' @@ -35,6 +36,7 @@ import { ConstructorParameters, type TConstructorParameters } from '../construct import { Date, type TDate, type DateOptions } from '../date/index' import { Function as FunctionType, type TFunction } from '../function/index' import { InstanceType, type TInstanceType } from '../instance-type/index' +import { Instantiate, type TInstantiate } from '../instantiate/index' import { Iterator, type TIterator } from '../iterator/index' import { Parameters, type TParameters } from '../parameters/index' import { Promise, type TPromise } from '../promise/index' @@ -48,6 +50,10 @@ import { Void, type TVoid } from '../void/index' /** JavaScript Type Builder with Static Resolution for TypeScript */ export class JavaScriptTypeBuilder extends JsonTypeBuilder { + /** `[JavaScript]` Creates a Generic Argument Type */ + public Argument(index: Index): TArgument { + return Argument(index) + } /** `[JavaScript]` Creates a AsyncIterator type */ public AsyncIterator(items: Type, options?: SchemaOptions): TAsyncIterator { return AsyncIterator(items, options) @@ -80,6 +86,10 @@ export class JavaScriptTypeBuilder extends JsonTypeBuilder { public InstanceType(schema: Type, options?: SchemaOptions): TInstanceType { return InstanceType(schema, options) } + /** `[JavaScript]` Instantiates a type with the given parameters */ + public Instantiate(schema: Type, parameters: [...Parameters]): TInstantiate { + return Instantiate(schema, parameters) + } /** `[JavaScript]` Creates an Iterator type */ public Iterator(items: Type, options?: SchemaOptions): TIterator { return Iterator(items, options) diff --git a/src/type/type/type.ts b/src/type/type/type.ts index 85ed02710..d384cfa33 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -30,6 +30,7 @@ THE SOFTWARE. // Type: Module // ------------------------------------------------------------------ export { Any } from '../any/index' +export { Argument } from '../argument/index' export { Array } from '../array/index' export { AsyncIterator } from '../async-iterator/index' export { Awaited } from '../awaited/index' @@ -47,6 +48,7 @@ export { Extract } from '../extract/index' export { Function } from '../function/index' export { Index } from '../indexed/index' export { InstanceType } from '../instance-type/index' +export { Instantiate } from '../instantiate/index' export { Integer } from '../integer/index' export { Intersect } from '../intersect/index' export { Capitalize, Uncapitalize, Lowercase, Uppercase } from '../intrinsic/index' @@ -72,6 +74,7 @@ export { Record } from '../record/index' export { Recursive } from '../recursive/index' export { Ref } from '../ref/index' export { RegExp } from '../regexp/index' +export { Remap } from '../remap/index' export { Required } from '../required/index' export { Rest } from '../rest/index' export { ReturnType } from '../return-type/index' diff --git a/src/value/check/check.ts b/src/value/check/check.ts index f55475c64..7a61d9ad1 100644 --- a/src/value/check/check.ts +++ b/src/value/check/check.ts @@ -36,9 +36,11 @@ import { TypeRegistry, FormatRegistry } from '../../type/registry/index' import { TypeBoxError } from '../../type/error/index' import type { TSchema } from '../../type/schema/index' -import type { TAsyncIterator } from '../../type/async-iterator/index' + import type { TAny } from '../../type/any/index' +import type { TArgument } from '../../type/argument/index' import type { TArray } from '../../type/array/index' +import type { TAsyncIterator } from '../../type/async-iterator/index' import type { TBigInt } from '../../type/bigint/index' import type { TBoolean } from '../../type/boolean/index' import type { TDate } from '../../type/date/index' @@ -104,6 +106,9 @@ function IsDefined(value: unknown): value is T { function FromAny(schema: TAny, references: TSchema[], value: any): boolean { return true } +function FromArgument(schema: TArgument, references: TSchema[], value: any): boolean { + return true +} function FromArray(schema: TArray, references: TSchema[], value: any): boolean { if (!IsArray(value)) return false if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { @@ -426,6 +431,8 @@ function Visit(schema: T, references: TSchema[], value: any): switch (schema_[Kind]) { case 'Any': return FromAny(schema_, references_, value) + case 'Argument': + return FromArgument(schema_, references_, value) case 'Array': return FromArray(schema_, references_, value) case 'AsyncIterator': diff --git a/src/value/create/create.ts b/src/value/create/create.ts index b56ab26fa..3af0245d9 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -97,6 +97,9 @@ function FromAny(schema: TAny, references: TSchema[]): any { return {} } } +function FromArgument(schema: TAny, references: TSchema[]): any { + return {} +} function FromArray(schema: TArray, references: TSchema[]): any { if (schema.uniqueItems === true && !HasPropertyKey(schema, 'default')) { throw new ValueCreateError(schema, 'Array with the uniqueItems constraint requires a default value') @@ -403,6 +406,8 @@ function Visit(schema: TSchema, references: TSchema[]): unknown { switch (schema_[Kind]) { case 'Any': return FromAny(schema_, references_) + case 'Argument': + return FromArgument(schema_, references_) case 'Array': return FromArray(schema_, references_) case 'AsyncIterator': diff --git a/test/runtime/compiler/argument.ts b/test/runtime/compiler/argument.ts new file mode 100644 index 000000000..502df49c6 --- /dev/null +++ b/test/runtime/compiler/argument.ts @@ -0,0 +1,41 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('compiler/Argument', () => { + it('Should validate number', () => { + const T = Type.Argument(0) + Ok(T, 1) + }) + it('Should validate string', () => { + const T = Type.Argument(0) + Ok(T, 'hello') + }) + it('Should validate boolean', () => { + const T = Type.Argument(0) + Ok(T, true) + }) + it('Should validate array', () => { + const T = Type.Argument(0) + Ok(T, [1, 2, 3]) + }) + it('Should validate object', () => { + const T = Type.Argument(0) + Ok(T, { a: 1, b: 2 }) + }) + it('Should validate null', () => { + const T = Type.Argument(0) + Ok(T, null) + }) + it('Should validate undefined', () => { + const T = Type.Argument(0) + Ok(T, undefined) + }) + it('Should validate bigint', () => { + const T = Type.Argument(0) + Ok(T, BigInt(1)) + }) + it('Should validate symbol', () => { + const T = Type.Argument(0) + Ok(T, Symbol(1)) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index a39e51760..4bb587c30 100644 --- a/test/runtime/compiler/index.ts +++ b/test/runtime/compiler/index.ts @@ -1,5 +1,6 @@ import './__members' import './any' +import './argument' import './array' import './async-iterator' import './bigint' diff --git a/test/runtime/compiler/validate.ts b/test/runtime/compiler/validate.ts index eb3ab6a0e..508d49738 100644 --- a/test/runtime/compiler/validate.ts +++ b/test/runtime/compiler/validate.ts @@ -59,6 +59,7 @@ FormatRegistry.Set('date-time', (value) => isDateTime(value, true)) export function Ok(schema: T, data: unknown, references: any[] = []) { const C = TypeCompiler.Compile(schema, references) const result = C.Check(data) + if (result !== Value.Check(schema, references, data)) { throw Error('Compiler and Value Check disparity') } diff --git a/test/runtime/syntax/syntax.ts b/test/runtime/syntax/syntax.ts index 6f66c2ec1..62749598e 100644 --- a/test/runtime/syntax/syntax.ts +++ b/test/runtime/syntax/syntax.ts @@ -390,4 +390,33 @@ describe('syntax/Syntax', () => { const T = Syntax(`void`) Assert.IsTrue(TypeGuard.IsVoid(T)) }) + // ---------------------------------------------------------------- + // Argument + Instantiation + // ---------------------------------------------------------------- + it('Should parse Argument 0', () => { + const G = Syntax(`[Argument<0>]`) + const T = Syntax({ G }, `G`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsArgument(T.items![0])) + }) + it('Should parse Argument 1', () => { + const G = Syntax(`[Argument<0>]`) + const T = Syntax({ G }, `G`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + }) + it('Should parse Argument 2', () => { + const G = Syntax(`[Argument<0>, Argument<1>]`) + const T = Syntax({ G }, `G`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsString(T.items![1])) + }) + it('Should parse Argument 3', () => { + const G = Syntax(`[Argument<0>, Argument<1>]`) + const T = Syntax({ G }, `G`) + Assert.IsTrue(TypeGuard.IsTuple(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) + Assert.IsTrue(TypeGuard.IsNever(T.items![1])) + }) }) diff --git a/test/runtime/type/guard/kind/argument.ts b/test/runtime/type/guard/kind/argument.ts new file mode 100644 index 000000000..5739e1eee --- /dev/null +++ b/test/runtime/type/guard/kind/argument.ts @@ -0,0 +1,14 @@ +import { KindGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/kind/TArgument', () => { + it('Should guard for TArgument', () => { + const R = KindGuard.IsArgument(Type.Argument(0)) + Assert.IsTrue(R) + }) + it('Should not guard for TArgument', () => { + const R = KindGuard.IsArgument(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/kind/index.ts b/test/runtime/type/guard/kind/index.ts index d3d241ded..d8e59fb7d 100644 --- a/test/runtime/type/guard/kind/index.ts +++ b/test/runtime/type/guard/kind/index.ts @@ -1,4 +1,5 @@ import './any' +import './argument' import './array' import './async-iterator' import './awaited' diff --git a/test/runtime/type/guard/type/argument.ts b/test/runtime/type/guard/type/argument.ts new file mode 100644 index 000000000..904cf8068 --- /dev/null +++ b/test/runtime/type/guard/type/argument.ts @@ -0,0 +1,14 @@ +import { TypeGuard } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../../assert/index' + +describe('guard/type/TArgument', () => { + it('Should guard for TArgument', () => { + const R = TypeGuard.IsArgument(Type.Argument(0)) + Assert.IsTrue(R) + }) + it('Should not guard for TArgument', () => { + const R = TypeGuard.IsArgument(null) + Assert.IsFalse(R) + }) +}) diff --git a/test/runtime/type/guard/type/index.ts b/test/runtime/type/guard/type/index.ts index d3d241ded..d8e59fb7d 100644 --- a/test/runtime/type/guard/type/index.ts +++ b/test/runtime/type/guard/type/index.ts @@ -1,4 +1,5 @@ import './any' +import './argument' import './array' import './async-iterator' import './awaited' diff --git a/test/runtime/type/guard/type/record.ts b/test/runtime/type/guard/type/record.ts index 38c7d1bc4..0bf69119a 100644 --- a/test/runtime/type/guard/type/record.ts +++ b/test/runtime/type/guard/type/record.ts @@ -1,5 +1,5 @@ import { TypeGuard, PatternNumberExact, PatternStringExact, PatternNeverExact, PatternString, PatternNumber } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, RecordKey, RecordValue, RecordPattern } from '@sinclair/typebox' import { Assert } from '../../../assert/index' describe('guard/type/TRecord', () => { @@ -151,16 +151,52 @@ describe('guard/type/TRecord', () => { ) Assert.IsFalse(R) }) - it('Normalize: Should should normalize to TObject for single literal union value', () => { + it('Normalize: Should normalize to TObject for single literal union value', () => { const K = Type.Union([Type.Literal('ok')]) const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) - it('Normalize: Should should normalize to TObject for multi literal union value', () => { + it('Normalize: Should normalize to TObject for multi literal union value', () => { const K = Type.Union([Type.Literal('A'), Type.Literal('B')]) const R = TypeGuard.IsObject(Type.Record(K, Type.Number())) Assert.IsTrue(R) }) + it('Normalize: Should normalize boolean key into true and false', () => { + const K = Type.Boolean() + const R = Type.Record(K, Type.Number()) + Assert.IsTrue(TypeGuard.IsObject(R)) + Assert.IsTrue(TypeGuard.IsNumber(R.properties.true)) + Assert.IsTrue(TypeGuard.IsNumber(R.properties.false)) + }) + // ------------------------------------------------------------------ + // Utility Types + // ------------------------------------------------------------------ + it('Should return RecordPattern', () => { + const R = Type.Record(Type.Number(), Type.Number()) + const K = RecordPattern(R) + Assert.IsTrue(typeof K === 'string') + }) + it('Should return RecordKey (Number)', () => { + const R = Type.Record(Type.Number(), Type.Number()) + const K = RecordKey(R) + Assert.IsTrue(TypeGuard.IsNumber(K)) + }) + it('Should return RecordKey (String)', () => { + const R = Type.Record(Type.String(), Type.Number()) + const K = RecordKey(R) + Assert.IsTrue(TypeGuard.IsString(K)) + }) + it('Should return RecordKey (RegExp)', () => { + const R = Type.Record(Type.RegExp(/(a|b)/), Type.Number()) + const K = RecordKey(R) + Assert.IsTrue(TypeGuard.IsString(K)) // facade + }) + it('Should return RecordValue', () => { + const R = Type.Record(Type.String(), Type.Literal(12345)) + const V = RecordValue(R) + Assert.IsTrue(TypeGuard.IsLiteral(V)) + Assert.IsEqual(V.const, 12345) + }) // ------------------------------------------------------------------ // Evaluated: Dollar Sign Escape // https://github.com/sinclairzx81/typebox/issues/794 diff --git a/test/runtime/value/check/argument.ts b/test/runtime/value/check/argument.ts new file mode 100644 index 000000000..7a49e8bbf --- /dev/null +++ b/test/runtime/value/check/argument.ts @@ -0,0 +1,47 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Argument', () => { + const T = Type.Argument(0) + it('Should pass string', () => { + const value = 'hello' + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass number', () => { + const value = 1 + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass boolean', () => { + const value = true + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass null', () => { + const value = null + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass undefined', () => { + const value = undefined + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass object', () => { + const value = { a: 1 } + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass array', () => { + const value = [1, 2] + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) + it('Should pass Date', () => { + const value = new Date() + const result = Value.Check(T, value) + Assert.IsEqual(result, true) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 27bda1e41..ae71b7d46 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -1,4 +1,5 @@ import './any' +import './argument' import './array' import './async-iterator' import './bigint' diff --git a/test/runtime/value/create/argument.ts b/test/runtime/value/create/argument.ts new file mode 100644 index 000000000..55bd0de07 --- /dev/null +++ b/test/runtime/value/create/argument.ts @@ -0,0 +1,10 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/create/Argument', () => { + it('Should create value', () => { + const T = Type.Argument(0) + Assert.IsEqual(Value.Create(T), {}) + }) +}) diff --git a/test/runtime/value/create/index.ts b/test/runtime/value/create/index.ts index 9962a44f0..78c340b5e 100644 --- a/test/runtime/value/create/index.ts +++ b/test/runtime/value/create/index.ts @@ -1,5 +1,6 @@ import './_deferred' import './any' +import './argument' import './array' import './async-iterator' import './bigint' diff --git a/test/static/argument.ts b/test/static/argument.ts new file mode 100644 index 000000000..588f80827 --- /dev/null +++ b/test/static/argument.ts @@ -0,0 +1,21 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +const T = Type.Object({ + x: Type.Argument(0), + y: Type.Argument(1), + z: Type.Argument(2), +}) +const I = Type.Instantiate(T, [Type.Literal(1), Type.Literal(2), Type.Literal(3)]) +// Infer as Broadest Type (Pending Generic Constraints) +Expect(T).ToStatic<{ + x: unknown + y: unknown + z: unknown +}>() +// Infer as Narrowed Type +Expect(I).ToStatic<{ + x: 1 + y: 2 + z: 3 +}>() diff --git a/test/static/index.ts b/test/static/index.ts index c6af9e754..40c2067b3 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -1,4 +1,5 @@ import './any' +import './argument' import './array' import './async-iterator' import './awaited' From 6b76fb949b764f2c9f55fa9cf1a69586c0fce7db Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 12 Feb 2025 21:41:11 +0900 Subject: [PATCH 335/369] Revision 0.34.18 (#1164) * Fix Remap Imports * ChangeLog --- changelog/0.34.0.md | 2 + package-lock.json | 4 +- package.json | 2 +- src/type/remap/remap.ts | 92 ++++++++++++++++++++++++----------------- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index a41e509af..1394faf14 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.18](https://github.com/sinclairzx81/typebox/pull/1164) + - Hotfix: Internal Remap Imports - [Revision 0.34.17](https://github.com/sinclairzx81/typebox/pull/1162) - Add Argument() and Instantiate() Types and Instancing via Syntax support. - [Revision 0.34.16](https://github.com/sinclairzx81/typebox/pull/1156) diff --git a/package-lock.json b/package-lock.json index eab2429fc..36a3e8e75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.17", + "version": "0.34.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.17", + "version": "0.34.18", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 217a3e6d6..ddefae7aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.17", + "version": "0.34.18", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/remap/remap.ts b/src/type/remap/remap.ts index 3354b87ca..d0c707cfc 100644 --- a/src/type/remap/remap.ts +++ b/src/type/remap/remap.ts @@ -26,12 +26,26 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as t from '@sinclair/typebox' +import { TSchema } from '../schema/index' +import { Object, TObject, TProperties } from '../object/index' +import { Constructor, TConstructor } from '../constructor/index' +import { Function, TFunction } from '../function/index' +import { Intersect, TIntersect } from '../intersect/index' +import { Union, TUnion } from '../union/index' +import { Tuple, TTuple } from '../tuple/index' +import { Array, TArray } from '../array/index' +import { AsyncIterator, TAsyncIterator } from '../async-iterator/index' +import { Iterator, TIterator } from '../iterator/index' +import { Promise, TPromise } from '../promise/index' +import { Record, TRecord, TRecordOrObject, RecordKey, RecordValue } from '../record/index' + +import * as KindGuard from '../guard/kind' +import { CloneType } from '../clone/type' // ------------------------------------------------------------------ // Callback // ------------------------------------------------------------------ -export type TCallback = (schema: t.TSchema) => t.TSchema +export type TCallback = (schema: TSchema) => TSchema // ------------------------------------------------------------------ // Mapping // ------------------------------------------------------------------ @@ -43,18 +57,18 @@ export interface TMapping { // Apply // ------------------------------------------------------------------ // prettier-ignore -type TApply = Result // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ // prettier-ignore -type TFromProperties }> = Result -function FromProperties(properties: t.TProperties, func: TCallback): t.TProperties { +function FromProperties(properties: TProperties, func: TCallback): TProperties { return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { return { ...result, [key]: Remap(properties[key], func) } }, {}) @@ -63,22 +77,22 @@ function FromProperties(properties: t.TProperties, func: TCallback): t.TProperti // Types // ------------------------------------------------------------------ // prettier-ignore -type TFromTypes = ( - Types extends [infer Left extends t.TSchema, ...infer Right extends t.TSchema[]] +type TFromTypes = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? TFromTypes]> : Result ) -function FromTypes(types: t.TSchema[], callback: TCallback): t.TSchema[] { +function FromTypes(types: TSchema[], callback: TCallback): TSchema[] { return types.map((type) => Remap(type, callback)) } // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ // prettier-ignore -type TFromType )> = Result -function FromType(type: t.TSchema, callback: TCallback): t.TSchema { +function FromType(type: TSchema, callback: TCallback): TSchema { return callback(type) } // ------------------------------------------------------------------ @@ -86,43 +100,43 @@ function FromType(type: t.TSchema, callback: TCallback): t.TSchema { // ------------------------------------------------------------------ /** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ // prettier-ignore -export type TRemap, + Mapped extends TSchema = TFromType, // Maps the Interior Parameterized Types - Result extends t.TSchema = ( - Mapped extends t.TConstructor ? t.TConstructor, TFromType> : - Mapped extends t.TFunction ? t.TFunction, TFromType> : - Mapped extends t.TIntersect ? t.TIntersect> : - Mapped extends t.TUnion ? t.TUnion> : - Mapped extends t.TTuple ? t.TTuple> : - Mapped extends t.TArray ? t.TArray>: - Mapped extends t.TAsyncIterator ? t.TAsyncIterator> : - Mapped extends t.TIterator ? t.TIterator> : - Mapped extends t.TPromise ? t.TPromise> : - Mapped extends t.TObject ? t.TObject> : - Mapped extends t.TRecord ? t.TRecordOrObject, TFromType> : + Result extends TSchema = ( + Mapped extends TConstructor ? TConstructor, TFromType> : + Mapped extends TFunction ? TFunction, TFromType> : + Mapped extends TIntersect ? TIntersect> : + Mapped extends TUnion ? TUnion> : + Mapped extends TTuple ? TTuple> : + Mapped extends TArray ? TArray>: + Mapped extends TAsyncIterator ? TAsyncIterator> : + Mapped extends TIterator ? TIterator> : + Mapped extends TPromise ? TPromise> : + Mapped extends TObject ? TObject> : + Mapped extends TRecord ? TRecordOrObject, TFromType> : Mapped ) > = Result /** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ // prettier-ignore -export function Remap(type: t.TSchema, callback: TCallback): t.TSchema { +export function Remap(type: TSchema, callback: TCallback): TSchema { // Map incoming type - const mapped = t.CloneType(FromType(type, callback)) + const mapped = CloneType(FromType(type, callback)) // Return remapped interior return ( - t.KindGuard.IsConstructor(type) ? t.Constructor(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : - t.KindGuard.IsFunction(type) ? t.Function(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : - t.KindGuard.IsIntersect(type) ? t.Intersect(FromTypes(type.allOf, callback), mapped) : - t.KindGuard.IsUnion(type) ? t.Union(FromTypes(type.anyOf, callback), mapped) : - t.KindGuard.IsTuple(type) ? t.Tuple(FromTypes(type.items || [], callback), mapped) : - t.KindGuard.IsArray(type) ? t.Array(FromType(type.items, callback), mapped) : - t.KindGuard.IsAsyncIterator(type) ? t.AsyncIterator(FromType(type.items, callback), mapped) : - t.KindGuard.IsIterator(type) ? t.Iterator(FromType(type.items, callback), mapped) : - t.KindGuard.IsPromise(type) ? t.Promise(FromType(type.items, callback), mapped) : - t.KindGuard.IsObject(type) ? t.Object(FromProperties(type.properties, callback), mapped) : - t.KindGuard.IsRecord(type) ? t.Record(FromType(t.RecordKey(type), callback), FromType(t.RecordValue(type), callback), mapped) : - t.CloneType(mapped) + KindGuard.IsConstructor(type) ? Constructor(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : + KindGuard.IsFunction(type) ? Function(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : + KindGuard.IsIntersect(type) ? Intersect(FromTypes(type.allOf, callback), mapped) : + KindGuard.IsUnion(type) ? Union(FromTypes(type.anyOf, callback), mapped) : + KindGuard.IsTuple(type) ? Tuple(FromTypes(type.items || [], callback), mapped) : + KindGuard.IsArray(type) ? Array(FromType(type.items, callback), mapped) : + KindGuard.IsAsyncIterator(type) ? AsyncIterator(FromType(type.items, callback), mapped) : + KindGuard.IsIterator(type) ? Iterator(FromType(type.items, callback), mapped) : + KindGuard.IsPromise(type) ? Promise(FromType(type.items, callback), mapped) : + KindGuard.IsObject(type) ? Object(FromProperties(type.properties, callback), mapped) : + KindGuard.IsRecord(type) ? Record(FromType(RecordKey(type), callback), FromType(RecordValue(type), callback), mapped) : + CloneType(mapped) ) } From de43700f4677571a42ce6d98a4fe182e8c5de536 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 12 Feb 2025 22:30:09 +0900 Subject: [PATCH 336/369] Revision 0.34.19 (#1166) * Documentation * Version --- package-lock.json | 4 ++-- package.json | 2 +- readme.md | 29 +++++++++++++++-------------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 36a3e8e75..ce8e69fc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.18", + "version": "0.34.19", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.18", + "version": "0.34.19", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index ddefae7aa..93b83a4c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.18", + "version": "0.34.19", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index ba0ccc901..5ee6d1024 100644 --- a/readme.md +++ b/readme.md @@ -1390,25 +1390,26 @@ const S = Syntax({ T }, `{ x: T, y: T, z: T }`) // const S: TObject<{ ### Generics -Generic types can be created by passing Argument types as parameters. +Generic types can be created using Argument types. ```typescript -// Generic Vector Type - -const Vector = Syntax({ // type Vector = { - X: Type.Argument(0), // x: X - Y: Type.Argument(1), // y: Y, - Z: Type.Argument(2) // z: Z -}, // } -`{ - x: X, - y: Y, - z: Z +const Vector = Syntax(`{ + x: Argument<0>, + y: Argument<1>, + z: Argument<2> }`) -// Instanced Vector Type +const Basis = Syntax({ Vector }, `{ + x: Vector<1, 0, 0>, + y: Vector<0, 1, 0>, + z: Vector<0, 0, 1>, +}`) -const Up = Syntax({ Vector }, `Vector<0, 1, 0>`) // type Up = Vector<0, 1, 0> +type Basis = Static // type Basis = { + // x: { x: 1, y: 0, z: 0 }, + // y: { x: 0, y: 1, z: 0 }, + // z: { x: 0, y: 0, z: 1 } + // } ``` From bf4391f0416a1b90d9b734fc05d4ced37e4c58bf Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 13 Feb 2025 01:56:28 +0900 Subject: [PATCH 337/369] Revision 0.34.20 (#1167) * Disable Computed Types on Record * Version * ChangeLog --- changelog/0.34.0.md | 4 ++ package-lock.json | 4 +- package.json | 2 +- src/type/record/record.ts | 8 ---- test/runtime/type/guard/kind/computed.ts | 7 +-- test/runtime/type/guard/kind/import.ts | 22 ++++----- test/runtime/type/guard/type/computed.ts | 7 +-- test/runtime/type/guard/type/import.ts | 22 ++++----- test/static/import.ts | 60 ++++++++++++------------ 9 files changed, 63 insertions(+), 73 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 1394faf14..b10199e25 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,8 @@ ### 0.34.0 +- [Revision 0.34.20](https://github.com/sinclairzx81/typebox/pull/1167) + - Hotfix: Disable Computed Types on Record +- [Revision 0.34.19](https://github.com/sinclairzx81/typebox/pull/1166) + - Hotfix: Republished due to NPM error - [Revision 0.34.18](https://github.com/sinclairzx81/typebox/pull/1164) - Hotfix: Internal Remap Imports - [Revision 0.34.17](https://github.com/sinclairzx81/typebox/pull/1162) diff --git a/package-lock.json b/package-lock.json index ce8e69fc7..d77b8c158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.19", + "version": "0.34.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.19", + "version": "0.34.20", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 93b83a4c5..7c918efa2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.19", + "version": "0.34.20", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/record/record.ts b/src/type/record/record.ts index e22e180df..53d5ead10 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -33,14 +33,12 @@ import type { Static } from '../static/index' import type { Evaluate, Ensure, Assert } from '../helpers/index' import { type TAny } from '../any/index' import { type TBoolean } from '../boolean/index' -import { type TComputed, Computed } from '../computed/index' import { type TEnumRecord, type TEnum } from '../enum/index' import { type TInteger } from '../integer/index' import { type TLiteral, type TLiteralValue } from '../literal/index' import { type TNever, Never } from '../never/index' import { type TNumber, Number } from '../number/index' import { type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions, Object } from '../object/index' -import { type TRef, Ref } from '../ref/index' import { type TRegExp } from '../regexp/index' import { type TString, String } from '../string/index' import { type TUnion, Union } from '../union/index' @@ -233,9 +231,6 @@ export interface TRecord = ( - Type extends TComputed ? TComputed<'Record', [Key, TComputed]> : - Key extends TComputed ? TComputed<'Record', [TComputed, Type]> : - Key extends TRef ? TComputed<'Record', [TRef, Type]> : Key extends TTemplateLiteral ? TFromTemplateLiteralKey : Key extends TEnum ? TFromEnumKey : // (Special: Ensure resolve Enum before Union) Key extends TUnion ? TFromUnionKey : @@ -256,9 +251,6 @@ export type TRecordOrObject = ( export function Record(key: Key, type: Type, options: ObjectOptions = {}): TRecordOrObject { // prettier-ignore return ( - IsComputed(type) ? Computed('Record', [key, Computed(type.target, type.parameters)], options) : - IsComputed(key) ? Computed('Record', [Computed(type.target, type.parameters), type], options) : - IsRef(key) ? Computed('Record', [Ref(key.$ref), type]) : IsUnion(key) ? FromUnionKey(key.anyOf, type, options) : IsTemplateLiteral(key) ? FromTemplateLiteralKey(key, type, options) : IsLiteral(key) ? FromLiteralKey(key.const, type, options) : diff --git a/test/runtime/type/guard/kind/computed.ts b/test/runtime/type/guard/kind/computed.ts index 38262833e..5d7929af9 100644 --- a/test/runtime/type/guard/kind/computed.ts +++ b/test/runtime/type/guard/kind/computed.ts @@ -18,19 +18,16 @@ describe('guard/kind/TComputed', () => { const T = Type.Record(Type.String(), Type.String()) Assert.IsTrue(KindGuard.IsRecord(T)) }) - // TRecord> is not computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Ref('A')) Assert.IsTrue(KindGuard.IsRecord(T)) }) - // TRecord]> is computed due to interior computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Partial(Type.Ref('A'))) - Assert.IsTrue(KindGuard.IsComputed(T)) + Assert.IsTrue(KindGuard.IsRecord(T)) }) - // TRecord, TSchema> is computed as schematics may be transformed to TObject if finite it('Should guard for Record 4', () => { const T = Type.Record(Type.Ref('A'), Type.String()) - Assert.IsTrue(KindGuard.IsComputed(T)) + Assert.IsTrue(KindGuard.IsNever(T)) }) }) diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts index 55bd572e7..529cd5f6c 100644 --- a/test/runtime/type/guard/kind/import.ts +++ b/test/runtime/type/guard/kind/import.ts @@ -151,17 +151,17 @@ describe('guard/kind/TImport', () => { Assert.IsTrue(KindGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) - it('Should compute for Record 2', () => { - const Module = Type.Module({ - T: Type.Number(), - K: Type.Union([Type.Literal('x'), Type.Literal('y')]), - R: Type.Record(Type.Ref('K'), Type.Ref('T')), - }) - const T = Module.Import('R') - Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) - Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) - Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) - }) + // it('Should compute for Record 2', () => { + // const Module = Type.Module({ + // T: Type.Number(), + // K: Type.Union([Type.Literal('x'), Type.Literal('y')]), + // R: Type.Record(Type.Ref('K'), Type.Ref('T')), + // }) + // const T = Module.Import('R') + // Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + // Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + // Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) + // }) // ---------------------------------------------------------------- // Computed: Required // ---------------------------------------------------------------- diff --git a/test/runtime/type/guard/type/computed.ts b/test/runtime/type/guard/type/computed.ts index 34de3be67..d4dd6df24 100644 --- a/test/runtime/type/guard/type/computed.ts +++ b/test/runtime/type/guard/type/computed.ts @@ -18,19 +18,16 @@ describe('guard/type/TComputed', () => { const T = Type.Record(Type.String(), Type.String()) Assert.IsTrue(TypeGuard.IsRecord(T)) }) - // TRecord> is not computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Ref('A')) Assert.IsTrue(TypeGuard.IsRecord(T)) }) - // TRecord]> is computed due to interior computed. it('Should guard for Record 3', () => { const T = Type.Record(Type.String(), Type.Partial(Type.Ref('A'))) - Assert.IsTrue(TypeGuard.IsComputed(T)) + Assert.IsTrue(TypeGuard.IsRecord(T)) }) - // TRecord, TSchema> is computed as schematics may be transformed to TObject if finite it('Should guard for Record 4', () => { const T = Type.Record(Type.Ref('A'), Type.String()) - Assert.IsTrue(TypeGuard.IsComputed(T)) + Assert.IsTrue(TypeGuard.IsNever(T)) }) }) diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts index 5388e2f29..8b0f3b2dc 100644 --- a/test/runtime/type/guard/type/import.ts +++ b/test/runtime/type/guard/type/import.ts @@ -151,17 +151,17 @@ describe('guard/type/TImport', () => { Assert.IsTrue(TypeGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) - it('Should compute for Record 2', () => { - const Module = Type.Module({ - T: Type.Number(), - K: Type.Union([Type.Literal('x'), Type.Literal('y')]), - R: Type.Record(Type.Ref('K'), Type.Ref('T')), - }) - const T = Module.Import('R') - Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) - Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) - Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) - }) + // it('Should compute for Record 2', () => { + // const Module = Type.Module({ + // T: Type.Number(), + // K: Type.Union([Type.Literal('x'), Type.Literal('y')]), + // R: Type.Record(Type.Ref('K'), Type.Ref('T')), + // }) + // const T = Module.Import('R') + // Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + // Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + // Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) + // }) // ---------------------------------------------------------------- // Computed: Required // ---------------------------------------------------------------- diff --git a/test/static/import.ts b/test/static/import.ts index ab546c453..010c3d645 100644 --- a/test/static/import.ts +++ b/test/static/import.ts @@ -58,49 +58,49 @@ import { Type, Static } from '@sinclair/typebox' // ------------------------------------------------------------------ // prettier-ignore { - const T = Type.Module({ - R: Type.Object({ x: Type.Number(), y: Type.Number() }), - T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))), - }).Import('T') + // const T = Type.Module({ + // R: Type.Object({ x: Type.Number(), y: Type.Number() }), + // T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))), + // }).Import('T') - type T = Static - Expect(T).ToStatic<{ - [key: string]: { x?: number, y?: number } - }>() + // type T = Static + // Expect(T).ToStatic<{ + // [key: string]: { x?: number, y?: number } + // }>() } // ------------------------------------------------------------------ // Record 3 // ------------------------------------------------------------------ // prettier-ignore { - const T = Type.Module({ - R: Type.Object({ x: Type.Number(), y: Type.Number() }), - K: Type.Number(), - T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), - }).Import('T') + // const T = Type.Module({ + // R: Type.Object({ x: Type.Number(), y: Type.Number() }), + // K: Type.Number(), + // T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), + // }).Import('T') - type T = Static - Expect(T).ToStatic<{ - [key: number]: { x?: number, y?: number } - }>() + // type T = Static + // Expect(T).ToStatic<{ + // [key: number]: { x?: number, y?: number } + // }>() } // ------------------------------------------------------------------ // Record 4 // ------------------------------------------------------------------ // prettier-ignore -{ - const T = Type.Module({ - R: Type.Object({ x: Type.Number(), y: Type.Number() }), - K: Type.TemplateLiteral('${A|B|C}'), - T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), - }).Import('T') - type T = Static - Expect(T).ToStatic<{ - A: { x?: number, y?: number }, - B: { x?: number, y?: number }, - C: { x?: number, y?: number } - }>() -} +// { +// const T = Type.Module({ +// R: Type.Object({ x: Type.Number(), y: Type.Number() }), +// K: Type.TemplateLiteral('${A|B|C}'), +// T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), +// }).Import('T') +// type T = Static +// Expect(T).ToStatic<{ +// A: { x?: number, y?: number }, +// B: { x?: number, y?: number }, +// C: { x?: number, y?: number } +// }>() +// } // ------------------------------------------------------------------ // Modifiers 1 // ------------------------------------------------------------------ From 1bd8f7810a68658f2b610da9668acd508d991440 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 13 Feb 2025 16:01:34 +0900 Subject: [PATCH 338/369] Revision 0.34.21 (#1168) * Reimplement Computed Record Type * Version --- changelog/0.34.0.md | 4 ++- package-lock.json | 4 +-- package.json | 2 +- src/type/module/compute.ts | 37 +++++++++++-------- src/type/record/record.ts | 25 +++++++------ test/runtime/type/guard/kind/import.ts | 33 +++++++++++------ test/runtime/type/guard/type/import.ts | 33 +++++++++++------ test/static/import.ts | 49 +++++--------------------- 8 files changed, 95 insertions(+), 92 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index b10199e25..f2d9ade16 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,6 +1,8 @@ ### 0.34.0 +- [Revision 0.34.21](https://github.com/sinclairzx81/typebox/pull/1168) + - Reimplement Computed Record Types - [Revision 0.34.20](https://github.com/sinclairzx81/typebox/pull/1167) - - Hotfix: Disable Computed Types on Record + - Hotfix: Disable Computed Record Types - [Revision 0.34.19](https://github.com/sinclairzx81/typebox/pull/1166) - Hotfix: Republished due to NPM error - [Revision 0.34.18](https://github.com/sinclairzx81/typebox/pull/1164) diff --git a/package-lock.json b/package-lock.json index d77b8c158..27a2d1f92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.20", + "version": "0.34.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.20", + "version": "0.34.21", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 7c918efa2..90ccd20e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.20", + "version": "0.34.21", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index 9ca66adf0..abd138af9 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -27,6 +27,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { CreateType } from '../create/index' +import { CloneType } from '../clone/index' import { Discard } from '../discard/index' import { Ensure, Evaluate } from '../helpers/index' import { type TSchema } from '../schema/index' @@ -48,7 +49,7 @@ import { Pick, type TPick } from '../pick/index' import { Never, type TNever } from '../never/index' import { Partial, TPartial } from '../partial/index' import { type TReadonly } from '../readonly/index' -import { Record, type TRecordOrObject } from '../record/index' +import { RecordValue, RecordPattern, type TRecordOrObject, type TRecord } from '../record/index' import { type TRef } from '../ref/index' import { Required, TRequired } from '../required/index' import { Tuple, type TTuple } from '../tuple/index' @@ -177,17 +178,6 @@ function FromPick(parameters: Parameters): TFromPi return Pick(parameters[0], parameters[1]) as never } // ------------------------------------------------------------------ -// Record -// ------------------------------------------------------------------ -// prettier-ignore -type TFromRecord = ( - Parameters extends [infer T0 extends TSchema, infer T1 extends TSchema] ? TRecordOrObject : never -) -// prettier-ignore -function FromRecord(parameters: Parameters): TFromPick { - return Record(parameters[0], parameters[1]) as never -} -// ------------------------------------------------------------------ // Required // ------------------------------------------------------------------ // prettier-ignore @@ -211,7 +201,6 @@ type TFromComputed : Target extends 'Omit' ? TFromOmit : Target extends 'Pick' ? TFromPick : - Target extends 'Record' ? TFromRecord : Target extends 'Required' ? TFromRequired : TNever ) @@ -225,7 +214,6 @@ function FromComputed> +> = Result +// prettier-ignore +function FromRecord(moduleProperties: ModuleProperties, type: Type): never { + const [value, pattern] = [FromType(moduleProperties, RecordValue(type)), RecordPattern(type)] + const result = CloneType(type) + result.patternProperties[pattern] = value + return result as never +} +// ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ // prettier-ignore @@ -363,6 +370,7 @@ export type TFromType ? TFromIntersect : Type extends TIterator ? TFromIterator : Type extends TObject ? TFromObject : + Type extends TRecord ? TFromRecord : Type extends TTuple ? TFromTuple : Type extends TEnum ? Type : // intercept enum before union Type extends TUnion ? TFromUnion : @@ -383,6 +391,7 @@ export function FromType = ( - Type extends TRecord - ? ( - Key extends TNumber ? TNumber : - Key extends TString ? TString : - TString - ) : TString -) +export type TRecordKey ? ( + Key extends TNumber ? TNumber : + Key extends TString ? TString : + TString + ) : TString +> = Result /** Gets the Records Key Type */ // prettier-ignore export function RecordKey(type: Type): TRecordKey { @@ -300,9 +299,13 @@ export function RecordKey(type: Type): TRecordKey { // ------------------------------------------------------------------ /** Gets a Record Value Type */ // prettier-ignore -export type TRecordValue = ( - Type extends TRecord ? Value : TNever -) +export type TRecordValue + ? Value + : TNever + ) +> = Result /** Gets a Record Value Type */ // prettier-ignore export function RecordValue(type: Type): TRecordValue { diff --git a/test/runtime/type/guard/kind/import.ts b/test/runtime/type/guard/kind/import.ts index 529cd5f6c..03921fe9f 100644 --- a/test/runtime/type/guard/kind/import.ts +++ b/test/runtime/type/guard/kind/import.ts @@ -151,17 +151,28 @@ describe('guard/kind/TImport', () => { Assert.IsTrue(KindGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) - // it('Should compute for Record 2', () => { - // const Module = Type.Module({ - // T: Type.Number(), - // K: Type.Union([Type.Literal('x'), Type.Literal('y')]), - // R: Type.Record(Type.Ref('K'), Type.Ref('T')), - // }) - // const T = Module.Import('R') - // Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) - // Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) - // Assert.IsTrue(KindGuard.IsNumber(T.$defs['R'].properties.x)) - // }) + it('Should compute for Record 2', () => { + const Module = Type.Module({ + T: Type.Number(), + R: Type.Record(Type.Union([Type.Literal('x'), Type.Literal('y')]), Type.Ref('T')), + }) + // Retain reference if not computed + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsRef(T.$defs['R'].properties.x)) + Assert.IsTrue(KindGuard.IsRef(T.$defs['R'].properties.y)) + }) + it('Should compute for Record 3', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number() }), + R: Type.Record(Type.Union([Type.Literal('x'), Type.Literal('y')]), Type.Partial(Type.Ref('T'))), + }) + // Dereference if computed + const T = Module.Import('R') + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'].properties.x)) + Assert.IsTrue(KindGuard.IsObject(T.$defs['R'].properties.y)) + }) // ---------------------------------------------------------------- // Computed: Required // ---------------------------------------------------------------- diff --git a/test/runtime/type/guard/type/import.ts b/test/runtime/type/guard/type/import.ts index 8b0f3b2dc..91080764f 100644 --- a/test/runtime/type/guard/type/import.ts +++ b/test/runtime/type/guard/type/import.ts @@ -151,17 +151,28 @@ describe('guard/type/TImport', () => { Assert.IsTrue(TypeGuard.IsRef(T.$defs['R'].patternProperties['^(.*)$'])) Assert.IsTrue(T.$defs['R'].patternProperties['^(.*)$'].$ref === 'T') }) - // it('Should compute for Record 2', () => { - // const Module = Type.Module({ - // T: Type.Number(), - // K: Type.Union([Type.Literal('x'), Type.Literal('y')]), - // R: Type.Record(Type.Ref('K'), Type.Ref('T')), - // }) - // const T = Module.Import('R') - // Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) - // Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) - // Assert.IsTrue(TypeGuard.IsNumber(T.$defs['R'].properties.x)) - // }) + it('Should compute for Record 2', () => { + const Module = Type.Module({ + T: Type.Number(), + R: Type.Record(Type.Union([Type.Literal('x'), Type.Literal('y')]), Type.Ref('T')), + }) + // Retain reference if not computed + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsRef(T.$defs['R'].properties.x)) + Assert.IsTrue(TypeGuard.IsRef(T.$defs['R'].properties.y)) + }) + it('Should compute for Record 3', () => { + const Module = Type.Module({ + T: Type.Object({ x: Type.Number() }), + R: Type.Record(Type.Union([Type.Literal('x'), Type.Literal('y')]), Type.Partial(Type.Ref('T'))), + }) + // Dereference if computed + const T = Module.Import('R') + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'])) + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'].properties.x)) + Assert.IsTrue(TypeGuard.IsObject(T.$defs['R'].properties.y)) + }) // ---------------------------------------------------------------- // Computed: Required // ---------------------------------------------------------------- diff --git a/test/static/import.ts b/test/static/import.ts index 010c3d645..0f30704f2 100644 --- a/test/static/import.ts +++ b/test/static/import.ts @@ -58,50 +58,17 @@ import { Type, Static } from '@sinclair/typebox' // ------------------------------------------------------------------ // prettier-ignore { - // const T = Type.Module({ - // R: Type.Object({ x: Type.Number(), y: Type.Number() }), - // T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))), - // }).Import('T') - - // type T = Static - // Expect(T).ToStatic<{ - // [key: string]: { x?: number, y?: number } - // }>() -} -// ------------------------------------------------------------------ -// Record 3 -// ------------------------------------------------------------------ -// prettier-ignore -{ - // const T = Type.Module({ - // R: Type.Object({ x: Type.Number(), y: Type.Number() }), - // K: Type.Number(), - // T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), - // }).Import('T') + const T = Type.Module({ + R: Type.Object({ x: Type.Number(), y: Type.Number() }), + T: Type.Record(Type.String(), Type.Partial(Type.Ref('R'))), + }).Import('T') - // type T = Static - // Expect(T).ToStatic<{ - // [key: number]: { x?: number, y?: number } - // }>() + type T = Static + Expect(T).ToStatic<{ + [key: string]: { x?: number, y?: number } + }>() } // ------------------------------------------------------------------ -// Record 4 -// ------------------------------------------------------------------ -// prettier-ignore -// { -// const T = Type.Module({ -// R: Type.Object({ x: Type.Number(), y: Type.Number() }), -// K: Type.TemplateLiteral('${A|B|C}'), -// T: Type.Record(Type.Ref('K'), Type.Partial(Type.Ref('R'))), -// }).Import('T') -// type T = Static -// Expect(T).ToStatic<{ -// A: { x?: number, y?: number }, -// B: { x?: number, y?: number }, -// C: { x?: number, y?: number } -// }>() -// } -// ------------------------------------------------------------------ // Modifiers 1 // ------------------------------------------------------------------ // prettier-ignore From 4e97af20bb2263a03fccbdaed34ff066261710de Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 14 Feb 2025 16:36:39 +0900 Subject: [PATCH 339/369] Revision 0.34.22 (#1171) * Intrinsics Passthrough on Required and Partial * ChangeLog * Version --- changelog/0.34.0.md | 2 + package-lock.json | 4 +- package.json | 2 +- src/type/partial/partial.ts | 72 +++++++++++++++++------- src/type/required/required.ts | 48 +++++++++++++--- test/runtime/type/guard/kind/partial.ts | 32 +++++++++++ test/runtime/type/guard/kind/required.ts | 32 +++++++++++ test/runtime/type/guard/type/partial.ts | 32 +++++++++++ test/runtime/type/guard/type/required.ts | 32 +++++++++++ test/static/partial.ts | 18 ++++++ test/static/required.ts | 18 ++++++ 11 files changed, 263 insertions(+), 29 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index f2d9ade16..72c5f68a9 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,6 @@ ### 0.34.0 +- [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171) + - Intrinsic Types Number, String, Boolean, etc Passthrough on Required and Partial Mapping - [Revision 0.34.21](https://github.com/sinclairzx81/typebox/pull/1168) - Reimplement Computed Record Types - [Revision 0.34.20](https://github.com/sinclairzx81/typebox/pull/1167) diff --git a/package-lock.json b/package-lock.json index 27a2d1f92..fee15aeb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.21", + "version": "0.34.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.21", + "version": "0.34.22", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 90ccd20e0..0bad6e6e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.21", + "version": "0.34.22", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts index 52af56364..1f1ac83ac 100644 --- a/src/type/partial/partial.ts +++ b/src/type/partial/partial.ts @@ -39,14 +39,24 @@ import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' import { type TRef, Ref } from '../ref/index' +import { type TBigInt } from '../bigint/index' +import { type TBoolean } from '../boolean/index' +import { type TInteger } from '../integer/index' +import { type TLiteral } from '../literal/index' +import { type TNull } from '../null/index' +import { type TNumber } from '../number/index' +import { type TString } from '../string/index' +import { type TSymbol } from '../symbol/index' +import { type TUndefined } from '../undefined/index' + import { Discard } from '../discard/index' import { TransformKind } from '../symbols/index' import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result' // ------------------------------------------------------------------ -// TypeGuard +// KindGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' +import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ // FromComputed @@ -95,9 +105,9 @@ type TFromObject )>> // prettier-ignore -function FromObject(T: Type): TFromObject { - const options = Discard(T, [TransformKind, '$id', 'required', 'properties']) - const properties = FromProperties(T['properties']) +function FromObject(type: Type): TFromObject { + const options = Discard(type, [TransformKind, '$id', 'required', 'properties']) + const properties = FromProperties(type['properties']) return Object(properties, options) as never } // ------------------------------------------------------------------ @@ -119,11 +129,23 @@ function FromRest(types: [...Types]): TFromRest // prettier-ignore function PartialResolve(type: Type): TPartial { return ( - IsComputed(type) ? FromComputed(type.target, type.parameters) : - IsRef(type) ? FromRef(type.$ref) : - IsIntersect(type) ? Intersect(FromRest(type.allOf)) : - IsUnion(type) ? Union(FromRest(type.anyOf)) : - IsObject(type) ? FromObject(type) : + // Mappable + KindGuard.IsComputed(type) ? FromComputed(type.target, type.parameters) : + KindGuard.IsRef(type) ? FromRef(type.$ref) : + KindGuard.IsIntersect(type) ? Intersect(FromRest(type.allOf)) : + KindGuard.IsUnion(type) ? Union(FromRest(type.anyOf)) : + KindGuard.IsObject(type) ? FromObject(type) : + // Intrinsic + KindGuard.IsBigInt(type) ? type : + KindGuard.IsBoolean(type) ? type : + KindGuard.IsInteger(type) ? type : + KindGuard.IsLiteral(type) ? type : + KindGuard.IsNull(type) ? type : + KindGuard.IsNumber(type) ? type : + KindGuard.IsString(type) ? type : + KindGuard.IsSymbol(type) ? type : + KindGuard.IsUndefined(type) ? type : + // Passthrough Object({}) ) as never } @@ -131,13 +153,25 @@ function PartialResolve(type: Type): TPartial { // TPartial // ------------------------------------------------------------------ // prettier-ignore -export type TPartial = ( - T extends TRecursive ? TRecursive> : - T extends TComputed ? TFromComputed : - T extends TRef ? TFromRef : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TFromObject> : +export type TPartial = ( + // Mappable + Type extends TRecursive ? TRecursive> : + Type extends TComputed ? TFromComputed : + Type extends TRef ? TFromRef : + Type extends TIntersect ? TIntersect> : + Type extends TUnion ? TUnion> : + Type extends TObject ? TFromObject> : + // Intrinsic + Type extends TBigInt ? Type : + Type extends TBoolean ? Type : + Type extends TInteger ? Type : + Type extends TLiteral ? Type : + Type extends TNull ? Type : + Type extends TNumber ? Type : + Type extends TString ? Type : + Type extends TSymbol ? Type : + Type extends TUndefined ? Type : + // Passthrough TObject<{}> ) /** `[Json]` Constructs a type where all properties are optional */ @@ -145,8 +179,8 @@ export function Partial(type: MappedResult, /** `[Json]` Constructs a type where all properties are optional */ export function Partial(type: Type, options?: SchemaOptions): TPartial /** `[Json]` Constructs a type where all properties are optional */ -export function Partial(type: TSchema, options?: SchemaOptions): any { - if (IsMappedResult(type)) { +export function Partial(type: TSchema, options?: SchemaOptions): unknown { + if (KindGuard.IsMappedResult(type)) { return PartialFromMappedResult(type, options) } else { // special: mapping types require overridable options diff --git a/src/type/required/required.ts b/src/type/required/required.ts index 25d4c0491..7ce1b8879 100644 --- a/src/type/required/required.ts +++ b/src/type/required/required.ts @@ -39,6 +39,16 @@ import { type TObject, type TProperties, Object } from '../object/index' import { type TIntersect, Intersect } from '../intersect/index' import { type TUnion, Union } from '../union/index' import { type TRef, Ref } from '../ref/index' +import { type TBigInt } from '../bigint/index' +import { type TBoolean } from '../boolean/index' +import { type TInteger } from '../integer/index' +import { type TLiteral } from '../literal/index' +import { type TNull } from '../null/index' +import { type TNumber } from '../number/index' +import { type TString } from '../string/index' +import { type TSymbol } from '../symbol/index' +import { type TUndefined } from '../undefined/index' + import { OptionalKind, TransformKind } from '../symbols/index' import { Discard } from '../discard/index' import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result' @@ -46,7 +56,7 @@ import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './requ // ------------------------------------------------------------------ // TypeGuard // ------------------------------------------------------------------ -import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind' +import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ // FromComputed @@ -119,11 +129,23 @@ function FromRest(types: [...Types]) : TFromRest // prettier-ignore function RequiredResolve(type: Type): TRequired { return ( - IsComputed(type) ? FromComputed(type.target, type.parameters) : - IsRef(type) ? FromRef(type.$ref) : - IsIntersect(type) ? Intersect(FromRest(type.allOf)) : - IsUnion(type) ? Union(FromRest(type.anyOf)) : - IsObject(type) ? FromObject(type) : + // Mappable + KindGuard.IsComputed(type) ? FromComputed(type.target, type.parameters) : + KindGuard.IsRef(type) ? FromRef(type.$ref) : + KindGuard.IsIntersect(type) ? Intersect(FromRest(type.allOf)) : + KindGuard.IsUnion(type) ? Union(FromRest(type.anyOf)) : + KindGuard.IsObject(type) ? FromObject(type) : + // Intrinsic + KindGuard.IsBigInt(type) ? type : + KindGuard.IsBoolean(type) ? type : + KindGuard.IsInteger(type) ? type : + KindGuard.IsLiteral(type) ? type : + KindGuard.IsNull(type) ? type : + KindGuard.IsNumber(type) ? type : + KindGuard.IsString(type) ? type : + KindGuard.IsSymbol(type) ? type : + KindGuard.IsUndefined(type) ? type : + // Passthrough Object({}) ) as never } @@ -132,12 +154,24 @@ function RequiredResolve(type: Type): TRequired { // ------------------------------------------------------------------ // prettier-ignore export type TRequired = ( + // Mappable Type extends TRecursive ? TRecursive> : Type extends TComputed ? TFromComputed : Type extends TRef ? TFromRef : Type extends TIntersect ? TIntersect> : Type extends TUnion ? TUnion> : Type extends TObject ? TFromObject> : + // Intrinsic + Type extends TBigInt ? Type : + Type extends TBoolean ? Type : + Type extends TInteger ? Type : + Type extends TLiteral ? Type : + Type extends TNull ? Type : + Type extends TNumber ? Type : + Type extends TString ? Type : + Type extends TSymbol ? Type : + Type extends TUndefined ? Type : + // Passthrough TObject<{}> ) /** `[Json]` Constructs a type where all properties are required */ @@ -146,7 +180,7 @@ export function Required(type: MappedResult, export function Required(type: Type, options?: SchemaOptions): TRequired /** `[Json]` Constructs a type where all properties are required */ export function Required(type: Type, options?: SchemaOptions): never { - if (IsMappedResult(type)) { + if (KindGuard.IsMappedResult(type)) { return RequiredFromMappedResult(type, options) as never } else { // special: mapping types require overridable options diff --git a/test/runtime/type/guard/kind/partial.ts b/test/runtime/type/guard/kind/partial.ts index 6520da9d8..f9f4615d4 100644 --- a/test/runtime/type/guard/kind/partial.ts +++ b/test/runtime/type/guard/kind/partial.ts @@ -64,4 +64,36 @@ describe('guard/kind/TPartial', () => { const R = Type.Partial(S) Assert.IsFalse(TransformKind in R) }) + // ------------------------------------------------------------------ + // Intrinsic Passthough + // https://github.com/sinclairzx81/typebox/issues/1169 + // ------------------------------------------------------------------ + it('Should pass through on intrinsic types on union 1', () => { + const T = Type.Partial( + Type.Union([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]), + ) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsObject(T.anyOf[1])) + Assert.IsTrue(KindGuard.IsOptional(T.anyOf[1].properties.x)) + }) + it('Should pass through on intrinsic types on union 2', () => { + const T = Type.Partial( + Type.Union([ + Type.Literal(1), + Type.Object({ + x: Type.Number(), + }), + ]), + ) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsLiteral(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsObject(T.anyOf[1])) + Assert.IsTrue(KindGuard.IsOptional(T.anyOf[1].properties.x)) + }) }) diff --git a/test/runtime/type/guard/kind/required.ts b/test/runtime/type/guard/kind/required.ts index e795a05e1..0bf29e49e 100644 --- a/test/runtime/type/guard/kind/required.ts +++ b/test/runtime/type/guard/kind/required.ts @@ -61,4 +61,36 @@ describe('guard/kind/TRequired', () => { const R = Type.Required(S) Assert.IsFalse(TransformKind in R) }) + // ------------------------------------------------------------------ + // Intrinsic Passthough + // https://github.com/sinclairzx81/typebox/issues/1169 + // ------------------------------------------------------------------ + it('Should pass through on intrinsic types on union 1', () => { + const T = Type.Required( + Type.Union([ + Type.Number(), + Type.Object({ + x: Type.Optional(Type.Number()), + }), + ]), + ) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsObject(T.anyOf[1])) + Assert.IsFalse(KindGuard.IsOptional(T.anyOf[1].properties.x)) + }) + it('Should pass through on intrinsic types on union 2', () => { + const T = Type.Required( + Type.Union([ + Type.Literal(1), + Type.Object({ + x: Type.Optional(Type.Number()), + }), + ]), + ) + Assert.IsTrue(KindGuard.IsUnion(T)) + Assert.IsTrue(KindGuard.IsLiteral(T.anyOf[0])) + Assert.IsTrue(KindGuard.IsObject(T.anyOf[1])) + Assert.IsFalse(KindGuard.IsOptional(T.anyOf[1].properties.x)) + }) }) diff --git a/test/runtime/type/guard/type/partial.ts b/test/runtime/type/guard/type/partial.ts index 1fb138d82..c2cb21312 100644 --- a/test/runtime/type/guard/type/partial.ts +++ b/test/runtime/type/guard/type/partial.ts @@ -73,4 +73,36 @@ describe('guard/type/TPartial', () => { Assert.IsEqual(A.title, 'A') Assert.IsEqual(B.title, 'B') }) + // ------------------------------------------------------------------ + // Intrinsic Passthough + // https://github.com/sinclairzx81/typebox/issues/1169 + // ------------------------------------------------------------------ + it('Should pass through on intrinsic types on union 1', () => { + const T = Type.Partial( + Type.Union([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]), + ) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(T.anyOf[1].properties.x)) + }) + it('Should pass through on intrinsic types on union 2', () => { + const T = Type.Partial( + Type.Union([ + Type.Literal(1), + Type.Object({ + x: Type.Number(), + }), + ]), + ) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1])) + Assert.IsTrue(TypeGuard.IsOptional(T.anyOf[1].properties.x)) + }) }) diff --git a/test/runtime/type/guard/type/required.ts b/test/runtime/type/guard/type/required.ts index b4baf6dd1..3c4869594 100644 --- a/test/runtime/type/guard/type/required.ts +++ b/test/runtime/type/guard/type/required.ts @@ -70,4 +70,36 @@ describe('guard/type/TRequired', () => { Assert.IsEqual(A.title, 'A') Assert.IsEqual(B.title, 'B') }) + // ------------------------------------------------------------------ + // Intrinsic Passthough + // https://github.com/sinclairzx81/typebox/issues/1169 + // ------------------------------------------------------------------ + it('Should pass through on intrinsic types on union 1', () => { + const T = Type.Required( + Type.Union([ + Type.Number(), + Type.Object({ + x: Type.Optional(Type.Number()), + }), + ]), + ) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(T.anyOf[1].properties.x)) + }) + it('Should pass through on intrinsic types on union 2', () => { + const T = Type.Required( + Type.Union([ + Type.Literal(1), + Type.Object({ + x: Type.Optional(Type.Number()), + }), + ]), + ) + Assert.IsTrue(TypeGuard.IsUnion(T)) + Assert.IsTrue(TypeGuard.IsLiteral(T.anyOf[0])) + Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1])) + Assert.IsFalse(TypeGuard.IsOptional(T.anyOf[1].properties.x)) + }) }) diff --git a/test/static/partial.ts b/test/static/partial.ts index f4f81b023..5feacceef 100644 --- a/test/static/partial.ts +++ b/test/static/partial.ts @@ -85,3 +85,21 @@ import * as Types from '@sinclair/typebox' d: Types.TOptional }> = Type.Partial(T) } +// ------------------------------------------------------------------ +// Intrinsic Passthough +// https://github.com/sinclairzx81/typebox/issues/1169 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Partial(Type.Union([Type.Number(), Type.Object({ + x: Type.Number() + })])) + Expect(T).ToStatic +} +// prettier-ignore +{ + const T = Type.Partial(Type.Union([Type.Literal(1), Type.Object({ + x: Type.Number() + })])) + Expect(T).ToStatic<1 | { x?: number }> +} diff --git a/test/static/required.ts b/test/static/required.ts index a35c61f22..47c7a3be7 100644 --- a/test/static/required.ts +++ b/test/static/required.ts @@ -87,3 +87,21 @@ import * as Types from '@sinclair/typebox' d: Types.TNumber }> = Type.Required(T) } +// ------------------------------------------------------------------ +// Intrinsic Passthough +// https://github.com/sinclairzx81/typebox/issues/1169 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const T = Type.Required(Type.Union([Type.Number(), Type.Object({ + x: Type.Optional(Type.Number()) + })])) + Expect(T).ToStatic +} +// prettier-ignore +{ + const T = Type.Required(Type.Union([Type.Literal(1), Type.Object({ + x: Type.Optional(Type.Number()) + })])) + Expect(T).ToStatic<1 | { x: number }> +} From 929efd2652b1fea60e4a5fd78062d207f57c7293 Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 14 Feb 2025 18:45:45 +0900 Subject: [PATCH 340/369] Documentation --- readme.md | 143 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 64 deletions(-) diff --git a/readme.md b/readme.md index 5ee6d1024..bd108c697 100644 --- a/readme.md +++ b/readme.md @@ -64,7 +64,6 @@ License MIT - [Types](#types) - [Json](#types-json) - [JavaScript](#types-javascript) - - [Import](#types-import) - [Options](#types-options) - [Properties](#types-properties) - [Generics](#types-generics) @@ -97,9 +96,9 @@ License MIT - [Mutate](#values-mutate) - [Pointer](#values-pointer) - [Syntax](#syntax) - - [Type](#syntax-type) - - [Options](#syntax-options) + - [Create](#syntax-create) - [Parameters](#syntax-parameters) + - [Options](#syntax-options) - [Generics](#syntax-generics) - [TypeRegistry](#typeregistry) - [Type](#typeregistry-type) @@ -107,6 +106,8 @@ License MIT - [TypeCheck](#typecheck) - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) +- [TypeMap](#typemap) + - [Usage](#typemap-usage) - [TypeSystem](#typesystem) - [Policies](#typesystem-policies) - [Error Function](#error-function) @@ -638,22 +639,6 @@ TypeBox provides an extended type set that can be used to create schematics for └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` - - -### Import - -Import the Type namespace to bring in the full TypeBox type system. This is recommended for most users. - -```typescript -import { Type, type Static } from '@sinclair/typebox' -``` - -You can also selectively import types. This enables modern bundlers to tree shake for unused types. - -```typescript -import { Object, Number, String, Boolean, type Static } from '@sinclair/typebox' -``` - ### Options @@ -741,22 +726,6 @@ const Nullable = (T: T) => { // type Nullable = T | nu const T = Nullable(Type.String()) // type T = Nullable ``` -Generic types can also be created with Argument types - -```typescript -const Vector = Type.Object({ // type Vector = { - x: Type.Argument(0), // x: A_0, - y: Type.Argument(1), // y: A_1, - z: Type.Argument(2), // z: A_2 -}) // } - -const T = Type.Instantiate(Vector, [ // type T = Vector< - Type.Boolean(), // boolean, - Type.Number(), // number, - Type.String() // string -]) // > -``` - ### Recursive Types @@ -1337,30 +1306,50 @@ ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1 ## Syntax Types -TypeBox provides optional support for runtime and type level parsing from TypeScript syntax. +TypeBox has support for parsing TypeScript syntax at runtime as well as statically in the type system. This feature offers a syntactical frontend to the TypeBox type builder. + +Syntax types are available via optional import. ```typescript import { Syntax } from '@sinclair/typebox/syntax' ``` - + + +### Create + +Use the Syntax function to create TypeBox types from TypeScript syntax -### Type +```typescript +const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{ + // x: TNumber, + // y: TNumber + // }> +``` + + + +### Parameters -Use the Syntax function to create TypeBox type from TypeScript syntax. +Syntax types can be parameterized to receive exterior types. ```typescript const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{ - // x: TNumber + // x: TNumber, // y: TNumber // }> + +const S = Syntax({ T }, `Partial`) // const S: TObject<{ + // x: TOptional, + // y: TOptional + // }> ``` ### Options -Options can be passed to types on the last parameter +Options can be passed via the last parameter ```typescript const T = Syntax(`number`, { // const T = { @@ -1370,42 +1359,30 @@ const T = Syntax(`number`, { // const T = { // } ``` - - -### Parameters - -Syntax types can be parameterized to accept exterior types. - -```typescript -const T = Syntax('number') - -const S = Syntax({ T }, `{ x: T, y: T, z: T }`) // const S: TObject<{ - // x: TNumber, - // y: TNumber, - // z: TNumber - // }> -``` - ### Generics -Generic types can be created using Argument types. +Generic types can be created using positional argument types ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgAVAAxwAvClG4AFBQCCUAOYBXED0wAeSwA+CgBKIxMzOHMARlt7TCdXD29fGD9o4LDjUwsAJji0BJxnNy8ff1zMiXCcuAA1HgYYaAKHYqQrABoo6O7zfPxugAMECTg4HAAuKMtOsbhUaZi58YAvJdyJfCGwmsiAISw6Ggam6BpWosckU+aoAmHR8an6xrv07tm4IJWF6dvoAFur1voFfutXmcoEDvsCwVsdtUuLw4IdjgCoBc7MgFEo-MieBAiKijsATm9zoFxtT2Ow4ASSeiKZi4k9qeyOZyudyeTzadSXkgXiDFrC4BDrIN5ryZbK5ez+eNRULpl9RSCJQ9pfKdbqFXS1tMVWLRV8IbF8Nq9da5fzCEA)) ```typescript -const Vector = Syntax(`{ - x: Argument<0>, - y: Argument<1>, - z: Argument<2> +const T0 = Syntax('Argument<0>') +const T1 = Syntax('Argument<1>') +const T2 = Syntax('Argument<2>') + +const Vector = Syntax({ T0, T1, T2 }, `{ + x: T0, + y: T1, + z: T2 }`) -const Basis = Syntax({ Vector }, `{ +const BasisVectors = Syntax({ Vector }, `{ x: Vector<1, 0, 0>, y: Vector<0, 1, 0>, z: Vector<0, 0, 1>, }`) -type Basis = Static // type Basis = { +type BasisVectors = Static // type BasisVectors = { // x: { x: 1, y: 0, z: 0 }, // y: { x: 0, y: 1, z: 0 }, // z: { x: 0, y: 0, z: 1 } @@ -1573,6 +1550,44 @@ const C = TypeCompiler.Code(Type.String()) // const C = `return functi // }` ``` + + +## TypeMap + +TypeBox offers an external package for bi-directional mapping between TypeBox, Valibot, and Zod type libraries. It also includes syntax parsing support for Valibot and Zod and supports the Standard Schema specification. For more details on TypeMap, refer to the project repository. + +[TypeMap Repository](https://github.com/sinclairzx81/typemap) + + + +### Usage + +TypeMap needs to be installed seperately + +```bash +$ npm install @sinclair/typemap +``` + +Once installed it offers advanced structural remapping between various runtime type libraries ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgFQJ5gKYCEIA8A0cAyqgHYwCGBcAWhACZwC+cAZlBCHAOQACAzsFIBjADYVgUAPQx0GEBTDcAUMuERS-eMjgBeFHJy4AFAAMkuAFxxSAVxAAjDFEKprdx88IAvd-adQzKYAlHBwUlJw6pra1sgA8g4AVhjCMAA8CMphObl5+QWFRcW5ETlWKABy-s4A3NkljU3NBWVhblU1UPUtvX3FbXC+nZ7dDf0TE2VMAHyq0VrEesRklCbIoS1lC-BE1twWfqOuRwE+p87MKmoaiwBKy3T0xkTBAHRgFFD8GMZ2oqJNnltrd4HdrFlJltImEKh4Aj0oU1Bh14XVxkiBjChhcxpjGtMwkA)) + +```typescript +import { TypeBox, Syntax, Zod } from '@sinclair/typemap' + +const T = TypeBox(`{ x: number, y: number, z: number }`) // const T: TObject<{ + // x: TNumber; + // y: TNumber; + // z: TNumber; + // }> + +const S = Syntax(T) // const S: '{ x: number, y: number, z: number }' + +const R = Zod(S).parse(null) // const R: { + // x: number; + // y: number; + // z: number; + // } +``` + ## TypeSystem From 89603918259dcc95dac878d3ba758c1bd0414778 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 16 Feb 2025 00:41:38 +0900 Subject: [PATCH 341/369] Revision 0.34.23 (#1174) * Inline Instantiate * Update Mocha * Version * ChangeLog --- changelog/0.32.0.md | 4 +- changelog/0.33.0.md | 5 + changelog/0.34.0.md | 8 + example/prototypes/index.ts | 1 + example/prototypes/readme.md | 37 +- example/prototypes/recursive-map.ts | 153 ++++ package-lock.json | 1000 +++++++++++++++++++++------ package.json | 4 +- src/index.ts | 1 - src/syntax/runtime.ts | 13 +- src/syntax/static.ts | 4 +- src/type/instantiate/instantiate.ts | 296 ++++++-- src/type/remap/index.ts | 29 - src/type/remap/remap.ts | 142 ---- src/type/type/type.ts | 1 - 15 files changed, 1244 insertions(+), 454 deletions(-) create mode 100644 example/prototypes/recursive-map.ts delete mode 100644 src/type/remap/index.ts delete mode 100644 src/type/remap/remap.ts diff --git a/changelog/0.32.0.md b/changelog/0.32.0.md index e2609e3bd..d28eb5e2d 100644 --- a/changelog/0.32.0.md +++ b/changelog/0.32.0.md @@ -1,6 +1,8 @@ +### 0.32.0 +--- -### 0.32.0 +### Revision Updates - [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert. - [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals. diff --git a/changelog/0.33.0.md b/changelog/0.33.0.md index 31140defd..c8c44a896 100644 --- a/changelog/0.33.0.md +++ b/changelog/0.33.0.md @@ -1,4 +1,9 @@ ### 0.33.0 + +--- + +### Revision Updates + - [Revision 0.33.22](https://github.com/sinclairzx81/typebox/pull/1065) - Rename TypeScript parsing infrastructure from `/parse` to `/syntax`. Remove Parse API from top level import. - [Revision 0.33.21](https://github.com/sinclairzx81/typebox/pull/1064) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 72c5f68a9..2550686c4 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -1,4 +1,12 @@ ### 0.34.0 + +--- + +### Revision Updates + + +- [Revision 0.34.23](https://github.com/sinclairzx81/typebox/pull/1174) + - Inline Instantiate Type Logic Local to Instantiate Module - [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171) - Intrinsic Types Number, String, Boolean, etc Passthrough on Required and Partial Mapping - [Revision 0.34.21](https://github.com/sinclairzx81/typebox/pull/1168) diff --git a/example/prototypes/index.ts b/example/prototypes/index.ts index 0dfa2301b..979519c61 100644 --- a/example/prototypes/index.ts +++ b/example/prototypes/index.ts @@ -30,5 +30,6 @@ export * from './discriminated-union' export * from './from-schema' export * from './options' export * from './partial-deep' +export * from './recursive-map' export * from './union-enum' export * from './union-oneof' diff --git a/example/prototypes/readme.md b/example/prototypes/readme.md index c514e3456..bf5ea9060 100644 --- a/example/prototypes/readme.md +++ b/example/prototypes/readme.md @@ -81,4 +81,39 @@ import { Options } from './prototypes' const A = Options(Type.String(), { foo: 1 }) // Options type A = typeof A extends { foo: number } ? true : false // true: foo property is observable to the type system -``` \ No newline at end of file +``` + +## Recursive Map +The Recursive Map type enables deep structural remapping of a type and it's internal constituents. This type accepts a TSchema type and a mapping type function (expressed via HKT). The HKT is applied when traversing the type and it's interior. The mapping HKT can apply conditional tests to each visited type to remap into a new form. The following augments a schematic via Options, and conditionally remaps any schema with an default annotation to make it optional. +```typescript +import { Type, TOptional, Static, TSchema } from '@sinclair/typebox' + +import { TRecursiveMap, TMappingType, Options } from './prototypes' + +// ------------------------------------------------------------------ +// StaticDefault +// ------------------------------------------------------------------ +export interface StaticDefaultMapping extends TMappingType { + output: ( + this['input'] extends TSchema // if input schematic contains an default + ? this['input'] extends { default: unknown } // annotation, remap it to be optional, + ? TOptional // otherwise just return the schema as is. + : this['input'] + : this['input'] + ) +} +export type StaticDefault = ( + Static> +) + +// ------------------------------------------------------------------ +// Usage +// ------------------------------------------------------------------ + +const T = Type.Object({ + x: Options(Type.String(), { default: 'hello' }), + y: Type.String() +}) + +type T = StaticDefault // { x?: string, y: string } +type S = Static // { x: string, y: string } \ No newline at end of file diff --git a/example/prototypes/recursive-map.ts b/example/prototypes/recursive-map.ts new file mode 100644 index 000000000..4ff008309 --- /dev/null +++ b/example/prototypes/recursive-map.ts @@ -0,0 +1,153 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/prototypes + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as Types from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Mapping: Functions and Type +// ------------------------------------------------------------------ +export type TMappingFunction = (schema: Types.TSchema) => Types.TSchema + +export interface TMappingType { + input: unknown + output: unknown +} +// ------------------------------------------------------------------ +// Record Parameters +// ------------------------------------------------------------------ +function GetRecordPattern(record: Types.TRecord): string { + return globalThis.Object.getOwnPropertyNames(record.patternProperties)[0] +} +function GetRecordKey(record: Types.TRecord): Types.TSchema { + const pattern = GetRecordPattern(record) + return ( + pattern === Types.PatternStringExact ? Types.String() : + pattern === Types.PatternNumberExact ? Types.Number() : + pattern === Types.PatternBooleanExact ? Types.Boolean() : + Types.String({ pattern }) + ) +} +function GetRecordValue(record: Types.TRecord): Types.TSchema { + return record.patternProperties[GetRecordPattern(record)] +} +// ------------------------------------------------------------------ +// Traversal +// ------------------------------------------------------------------ +// prettier-ignore +type TApply = Result +// prettier-ignore +type TFromProperties +}> = Result +function FromProperties(properties: Types.TProperties, func: TMappingFunction): Types.TProperties { + return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { + return {...result, [key]: RecursiveMap(properties[key], func) } + }, {}) +} +// prettier-ignore +type TFromRest = ( + Types extends [infer Left extends Types.TSchema, ...infer Right extends Types.TSchema[]] + ? TFromRest]> + : Result +) +function FromRest(types: Types.TSchema[], func: TMappingFunction): Types.TSchema[] { + return types.map(type => RecursiveMap(type, func)) +} +// prettier-ignore +type TFromType +)> = Result +function FromType(type: Types.TSchema, func: TMappingFunction): Types.TSchema { + return func(type) +} +// ------------------------------------------------------------------ +// TRecursiveMap +// ------------------------------------------------------------------ +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ +// prettier-ignore +export type TRecursiveMap, + // Maps the Interior Parameterized Types + Interior extends Types.TSchema = ( + Exterior extends Types.TConstructor ? Types.TConstructor, TFromType> : + Exterior extends Types.TFunction ? Types.TFunction, TFromType> : + Exterior extends Types.TIntersect ? Types.TIntersect> : + Exterior extends Types.TUnion ? Types.TUnion> : + Exterior extends Types.TTuple ? Types.TTuple> : + Exterior extends Types.TArray ? Types.TArray>: + Exterior extends Types.TAsyncIterator ? Types.TAsyncIterator> : + Exterior extends Types.TIterator ? Types.TIterator> : + Exterior extends Types.TPromise ? Types.TPromise> : + Exterior extends Types.TObject ? Types.TObject> : + Exterior extends Types.TRecord ? Types.TRecordOrObject, TFromType> : + Exterior + ), + // Modifiers Derived from Exterior Type Mapping + IsOptional extends number = Exterior extends Types.TOptional ? 1 : 0, + IsReadonly extends number = Exterior extends Types.TReadonly ? 1 : 0, + Result extends Types.TSchema = ( + [IsReadonly, IsOptional] extends [1, 1] ? Types.TReadonlyOptional : + [IsReadonly, IsOptional] extends [0, 1] ? Types.TOptional : + [IsReadonly, IsOptional] extends [1, 0] ? Types.TReadonly : + Interior + ) +> = Result +/** `[Prototype]` Applies a deep recursive map across the given type and sub types. */ +// prettier-ignore +export function RecursiveMap(type: Types.TSchema, func: TMappingFunction): Types.TSchema { + // Maps the Exterior Type + const exterior = Types.CloneType(FromType(type, func), type) + // Maps the Interior Parameterized Types + const interior = ( + Types.KindGuard.IsConstructor(type) ? Types.Constructor(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : + Types.KindGuard.IsFunction(type) ? Types.Function(FromRest(type.parameters, func), FromType(type.returns, func), exterior) : + Types.KindGuard.IsIntersect(type) ? Types.Intersect(FromRest(type.allOf, func), exterior) : + Types.KindGuard.IsUnion(type) ? Types.Union(FromRest(type.anyOf, func), exterior) : + Types.KindGuard.IsTuple(type) ? Types.Tuple(FromRest(type.items || [], func), exterior) : + Types.KindGuard.IsArray(type) ? Types.Array(FromType(type.items, func), exterior) : + Types.KindGuard.IsAsyncIterator(type) ? Types.AsyncIterator(FromType(type.items, func), exterior) : + Types.KindGuard.IsIterator(type) ? Types.Iterator(FromType(type.items, func), exterior) : + Types.KindGuard.IsPromise(type) ? Types.Promise(FromType(type.items, func), exterior) : + Types.KindGuard.IsObject(type) ? Types.Object(FromProperties(type.properties, func), exterior) : + Types.KindGuard.IsRecord(type) ? Types.Record(FromType(GetRecordKey(type), func), FromType(GetRecordValue(type), func), exterior) : + Types.CloneType(exterior, exterior) + ) + // Modifiers Derived from Exterior Type Mapping + const isOptional = Types.KindGuard.IsOptional(exterior) + const isReadonly = Types.KindGuard.IsOptional(exterior) + return ( + isOptional && isReadonly ? Types.ReadonlyOptional(interior) : + isOptional ? Types.Optional(interior) : + isReadonly ? Types.Readonly(interior) : + interior + ) +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fee15aeb9..a7caf3c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.22", + "version": "0.34.23", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.22", + "version": "0.34.23", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -15,7 +15,7 @@ "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "mocha": "^10.4.0", + "mocha": "^11.1.0", "prettier": "^2.7.1", "typescript": "^5.7.3" } @@ -102,6 +102,83 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@sinclair/hammer": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.18.0.tgz", @@ -175,9 +252,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -403,14 +480,34 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/color-convert": { @@ -440,13 +537,27 @@ "node": ">=14" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -457,12 +568,6 @@ } } }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", @@ -476,14 +581,20 @@ } }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -853,9 +964,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -935,11 +1046,21 @@ "flat": "cli.js" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/fsevents": { "version": "2.3.2", @@ -965,20 +1086,20 @@ } }, "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -996,6 +1117,21 @@ "node": ">= 6" } }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1014,23 +1150,6 @@ "he": "bin/he" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1103,6 +1222,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1209,9 +1349,9 @@ } }, "node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -1220,39 +1360,48 @@ "node": ">=10" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", - "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", - "dev": true, - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/ms": { @@ -1285,15 +1434,6 @@ "node": ">=0.10.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -1324,6 +1464,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1333,6 +1479,37 @@ "node": ">=8" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1453,14 +1630,47 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", @@ -1487,6 +1697,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1499,6 +1724,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1624,13 +1862,46 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", @@ -1647,12 +1918,68 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -1669,30 +1996,30 @@ "dev": true }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -1780,6 +2107,61 @@ "dev": true, "optional": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@sinclair/hammer": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/@sinclair/hammer/-/hammer-0.18.0.tgz", @@ -1832,9 +2214,9 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escapes": { @@ -1998,14 +2380,27 @@ } }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "requires": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } } }, "color-convert": { @@ -2029,21 +2424,24 @@ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "ms": "^2.1.3" } }, "decamelize": { @@ -2053,9 +2451,15 @@ "dev": true }, "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, "emoji-regex": { @@ -2240,9 +2644,9 @@ "optional": true }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -2294,11 +2698,15 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } }, "fsevents": { "version": "2.3.2", @@ -2314,16 +2722,28 @@ "dev": true }, "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "dependencies": { + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "glob-parent": { @@ -2347,22 +2767,6 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2411,6 +2815,22 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2483,40 +2903,46 @@ } }, "minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, "mocha": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", - "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", "dev": true, "requires": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" } }, "ms": { @@ -2543,15 +2969,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2570,12 +2987,42 @@ "p-limit": "^3.0.2" } }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2649,14 +3096,35 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" } }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, "skin-tone": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", @@ -2677,6 +3145,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2686,6 +3165,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2773,14 +3261,74 @@ "builtins": "^5.0.0" } }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "wrap-ansi": { - "version": "7.0.0", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, @@ -2790,12 +3338,6 @@ "strip-ansi": "^6.0.0" } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -2809,24 +3351,24 @@ "dev": true }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, "yargs-unparser": { diff --git a/package.json b/package.json index 0bad6e6e8..db1c66b0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.22", + "version": "0.34.23", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -36,7 +36,7 @@ "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "mocha": "^10.4.0", + "mocha": "^11.1.0", "prettier": "^2.7.1", "typescript": "^5.7.3" } diff --git a/src/index.ts b/src/index.ts index 847758ecd..54d6bbc8a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -87,7 +87,6 @@ export * from './type/record/index' export * from './type/recursive/index' export * from './type/ref/index' export * from './type/regexp/index' -export * from './type/remap/index' export * from './type/required/index' export * from './type/rest/index' export * from './type/return-type/index' diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index c5366d277..227393a1b 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -69,10 +69,17 @@ const Dereference = (context: t.TProperties, key: string): t.TSchema => { // GenericReference // ------------------------------------------------------------------ function GenericReferenceMapping(results: unknown[], context: t.TProperties) { - const target = Dereference(context, results[0] as string) - return t.Instantiate(target, results[2] as t.TSchema[]) + const type = Dereference(context, results[0] as string) + const args = results[2] as t.TSchema[] + return t.Instantiate(type, args) } -const GenericReference = Runtime.Tuple([Runtime.Ident(), Runtime.Const(LAngle), Runtime.Ref('Elements'), Runtime.Const(RAngle)], (results, context) => GenericReferenceMapping(results, context)) +// prettier-ignore +const GenericReference = Runtime.Tuple([ + Runtime.Ident(), + Runtime.Const(LAngle), + Runtime.Ref('Elements'), + Runtime.Const(RAngle) +], (results, context) => GenericReferenceMapping(results, context)) // ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ diff --git a/src/syntax/static.ts b/src/syntax/static.ts index fd06a402e..1ace9a445 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -148,8 +148,8 @@ type Dereference = ( // prettier-ignore interface GenericReferenceMapping extends Static.IMapping { output: this['context'] extends t.TProperties - ? this['input'] extends [infer Reference extends string, LAngle, infer Parameters extends t.TSchema[], RAngle] - ? t.TInstantiate, [...Parameters]> + ? this['input'] extends [infer Reference extends string, LAngle, infer Args extends t.TSchema[], RAngle] + ? t.TInstantiate, Args> : never : never } diff --git a/src/type/instantiate/instantiate.ts b/src/type/instantiate/instantiate.ts index b42628667..210df5f66 100644 --- a/src/type/instantiate/instantiate.ts +++ b/src/type/instantiate/instantiate.ts @@ -26,71 +26,281 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import type { TSchema } from '../schema/index' -import type { TArgument } from '../argument/index' +import { CloneType } from '../clone/type' +import { type TSchema } from '../schema/index' +import { type TArgument } from '../argument/index' import { type TNever, Never } from '../never/index' import { type TReadonlyOptional, ReadonlyOptional } from '../readonly-optional/index' import { type TReadonly, Readonly } from '../readonly/index' import { type TOptional, Optional } from '../optional/index' +import { type TConstructor } from '../constructor/index' +import { type TFunction } from '../function/index' +import { type TIntersect } from '../intersect/index' +import { type TUnion } from '../union/index' +import { type TTuple } from '../tuple/index' +import { type TArray } from '../array/index' +import { type TAsyncIterator } from '../async-iterator/index' +import { type TIterator } from '../iterator/index' +import { type TPromise } from '../promise/index' +import { type TObject, type TProperties } from '../object/index' +import { type TRecordOrObject, type TRecord, Record, RecordKey, RecordValue } from '../record/index' -import { Remap, type TRemap, type TMapping as TRemapMapping } from '../remap/index' +import * as ValueGuard from '../guard/value' import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ -// InstantiateArgument +// Constructor // ------------------------------------------------------------------ // prettier-ignore -type TInstantiateArgument ? 1 : 0, - IsArgumentOptional extends number = Argument extends TOptional ? 1 : 0, +type TFromConstructor, TFromType> +> = Result +// prettier-ignore +function FromConstructor(args: TSchema[], type: TConstructor): TConstructor { + type.parameters = FromTypes(args, type.parameters) + type.returns = FromType(args, type.returns) + return type +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +type TFromFunction, TFromType> +> = Result +// prettier-ignore +function FromFunction(args: TSchema[], type: TFunction): TFunction { + type.parameters = FromTypes(args, type.parameters) + type.returns = FromType(args, type.returns) + return type +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIntersect> +> = Result +// prettier-ignore +function FromIntersect(args: TSchema[], type: TIntersect): TIntersect { + type.allOf = FromTypes(args, type.allOf) + return type +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type TFromUnion> +> = Result +// prettier-ignore +function FromUnion(args: TSchema[], type: TUnion): TUnion { + type.anyOf = FromTypes(args, type.anyOf) + return type +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple> +> = Result +// prettier-ignore +function FromTuple(args: TSchema[], type: TTuple): TTuple { + if(ValueGuard.IsUndefined(type.items)) return type + type.items = FromTypes(args, type.items!) + return type +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArray> +> = Result +// prettier-ignore +function FromArray(args: TSchema[], type: TArray): TArray { + type.items = FromType(args, type.items) + return type +} +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromAsyncIterator> +> = Result +// prettier-ignore +function FromAsyncIterator(args: TSchema[], type: TAsyncIterator): TAsyncIterator { + type.items = FromType(args, type.items) + return type +} +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +type TFromIterator> +> = Result +// prettier-ignore +function FromIterator(args: TSchema[], type: TIterator): TIterator { + type.items = FromType(args, type.items) + return type +} +// ------------------------------------------------------------------ +// Promise +// ------------------------------------------------------------------ +// prettier-ignore +type TFromPromise> +> = Result +// prettier-ignore +function FromPromise(args: TSchema[], type: TPromise): TPromise { + type.item = FromType(args, type.item) + return type +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +type TFromObject> +> = Result +// prettier-ignore +function FromObject(args: TSchema[], type: TObject): TObject { + type.properties = FromProperties(args, type.properties) + return type +} +// ------------------------------------------------------------------ +// Object +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRecord, + MappedValue extends TSchema = TFromType, + Result extends TSchema = TRecordOrObject +> = Result +// prettier-ignore +function FromRecord(args: TSchema[], type: TRecord): TRecord { + const mappedKey = FromType(args, RecordKey(type)) + const mappedValue = FromType(args, RecordValue(type)) + const result = Record(mappedKey, mappedValue) + return { ...type, ... result } as never // retain options +} +// ------------------------------------------------------------------ +// Argument +// ------------------------------------------------------------------ +// prettier-ignore +type TFromArgument = ( + Index extends keyof Args ? Args[Index] : TNever +) +// prettier-ignore +function FromArgument(args: TSchema[], argument: TArgument): TSchema { + return argument.index in args ? args[argument.index] : Never() +} +// ------------------------------------------------------------------ +// Property +// ------------------------------------------------------------------ +// prettier-ignore +type TFromProperty ? true : false, + IsOptional extends boolean = Type extends TOptional ? true : false, + Mapped extends TSchema = TFromType, Result extends TSchema = ( - [IsArgumentReadonly, IsArgumentOptional] extends [1, 1] ? TReadonlyOptional : - [IsArgumentReadonly, IsArgumentOptional] extends [0, 1] ? TOptional : - [IsArgumentReadonly, IsArgumentOptional] extends [1, 0] ? TReadonly : - Type - ) + [IsReadonly, IsOptional] extends [true, true] ? TReadonlyOptional : + [IsReadonly, IsOptional] extends [true, false] ? TReadonly : + [IsReadonly, IsOptional] extends [false, true] ? TOptional : + Mapped + ) > = Result // prettier-ignore -function InstantiateArgument(argument: Argument, type: Type): TInstantiateArgument { - const isReadonly = KindGuard.IsReadonly(argument) - const isOptional = KindGuard.IsOptional(argument) +function FromProperty(args: [...Args], type: Type): TFromProperty { + const isReadonly = KindGuard.IsReadonly(type) + const isOptional = KindGuard.IsOptional(type) + const mapped = FromType(args, type) return ( - isReadonly && isOptional ? ReadonlyOptional(type) : - isReadonly && !isOptional ? Readonly(type) : - !isReadonly && isOptional ? Optional(type) : - type + isReadonly && isOptional ? ReadonlyOptional(mapped) : + isReadonly && !isOptional ? Readonly(mapped) : + !isReadonly && isOptional ? Optional(mapped) : + mapped ) as never } // ------------------------------------------------------------------ -// Instantiate +// Properties // ------------------------------------------------------------------ // prettier-ignore -interface TInstantiateArguments extends TRemapMapping { - output: ( - this['input'] extends TArgument - ? Index extends keyof Arguments - ? Arguments[Index] extends TSchema - ? TInstantiateArgument - : TNever - : TNever - : this['input'] - ) +type TFromProperties + } +> = Result +// prettier-ignore +function FromProperties(args: TSchema[], properties: TProperties): TFromProperties { + return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { + return { ...result, [key]: FromProperty(args, properties[key]) } + }, {}) as never } -/** `[JavaScript]` Instantiates a type with the given parameters */ +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +// prettier-ignore +export type TFromTypes = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TFromTypes]> + : Result +) +// prettier-ignore +export function FromTypes(args: [...Args], types: [...Types]): TFromTypes { + return types.map(type => FromType(args, type)) as never +} +// ------------------------------------------------------------------ +// Type +// ------------------------------------------------------------------ // prettier-ignore -export type TInstantiate = ( - TRemap> +export type TFromType = ( + Type extends TConstructor ? TFromConstructor : + Type extends TFunction ? TFromFunction : + Type extends TIntersect ? TFromIntersect : + Type extends TUnion ? TFromUnion : + Type extends TTuple ? TFromTuple : + Type extends TArray ? TFromArray: + Type extends TAsyncIterator ? TFromAsyncIterator : + Type extends TIterator ? TFromIterator : + Type extends TPromise ? TFromPromise : + Type extends TObject ? TFromObject : + Type extends TRecord ? TFromRecord : + Type extends TArgument ? TFromArgument : + Type ) +// prettier-ignore +function FromType(args: [...Args], type: TSchema): TFromType { + return ( + KindGuard.IsConstructor(type) ? FromConstructor(args, type) : + KindGuard.IsFunction(type) ? FromFunction(args, type) : + KindGuard.IsIntersect(type) ? FromIntersect(args, type) : + KindGuard.IsUnion(type) ? FromUnion(args, type) : + KindGuard.IsTuple(type) ? FromTuple(args, type) : + KindGuard.IsArray(type) ? FromArray(args, type) : + KindGuard.IsAsyncIterator(type) ? FromAsyncIterator(args, type) : + KindGuard.IsIterator(type) ? FromIterator(args, type) : + KindGuard.IsPromise(type) ? FromPromise(args, type) : + KindGuard.IsObject(type) ? FromObject(args, type): + KindGuard.IsRecord(type) ? FromRecord(args, type) : + KindGuard.IsArgument(type) ? FromArgument(args, type) : + type + ) as never +} +// ------------------------------------------------------------------ +// Instantiate +// ------------------------------------------------------------------ +/** `[JavaScript]` Instantiates a type with the given parameters */ +// prettier-ignore +export type TInstantiate +> = Result + /** `[JavaScript]` Instantiates a type with the given parameters */ // prettier-ignore -export function Instantiate(type: Type, args: [...Arguments]): TInstantiate { - return Remap(type, (type) => { - return KindGuard.IsArgument(type) - ? type.index in args - ? KindGuard.IsSchema(args[type.index]) - ? InstantiateArgument(type, args[type.index]) - : Never() - : Never() - : type - }) as never +export function Instantiate(type: Type, args: [...Args]): TInstantiate { + return FromType(args, CloneType(type)) } diff --git a/src/type/remap/index.ts b/src/type/remap/index.ts deleted file mode 100644 index f05c8c5db..000000000 --- a/src/type/remap/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/type - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -export * from './remap' diff --git a/src/type/remap/remap.ts b/src/type/remap/remap.ts deleted file mode 100644 index d0c707cfc..000000000 --- a/src/type/remap/remap.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/prototypes - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { TSchema } from '../schema/index' -import { Object, TObject, TProperties } from '../object/index' -import { Constructor, TConstructor } from '../constructor/index' -import { Function, TFunction } from '../function/index' -import { Intersect, TIntersect } from '../intersect/index' -import { Union, TUnion } from '../union/index' -import { Tuple, TTuple } from '../tuple/index' -import { Array, TArray } from '../array/index' -import { AsyncIterator, TAsyncIterator } from '../async-iterator/index' -import { Iterator, TIterator } from '../iterator/index' -import { Promise, TPromise } from '../promise/index' -import { Record, TRecord, TRecordOrObject, RecordKey, RecordValue } from '../record/index' - -import * as KindGuard from '../guard/kind' -import { CloneType } from '../clone/type' - -// ------------------------------------------------------------------ -// Callback -// ------------------------------------------------------------------ -export type TCallback = (schema: TSchema) => TSchema -// ------------------------------------------------------------------ -// Mapping -// ------------------------------------------------------------------ -export interface TMapping { - input: unknown - output: unknown -} -// ------------------------------------------------------------------ -// Apply -// ------------------------------------------------------------------ -// prettier-ignore -type TApply = Result -// ------------------------------------------------------------------ -// Properties -// ------------------------------------------------------------------ -// prettier-ignore -type TFromProperties -}> = Result -function FromProperties(properties: TProperties, func: TCallback): TProperties { - return globalThis.Object.getOwnPropertyNames(properties).reduce((result, key) => { - return { ...result, [key]: Remap(properties[key], func) } - }, {}) -} -// ------------------------------------------------------------------ -// Types -// ------------------------------------------------------------------ -// prettier-ignore -type TFromTypes = ( - Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] - ? TFromTypes]> - : Result -) -function FromTypes(types: TSchema[], callback: TCallback): TSchema[] { - return types.map((type) => Remap(type, callback)) -} -// ------------------------------------------------------------------ -// Type -// ------------------------------------------------------------------ -// prettier-ignore -type TFromType -)> = Result -function FromType(type: TSchema, callback: TCallback): TSchema { - return callback(type) -} -// ------------------------------------------------------------------ -// TRemap -// ------------------------------------------------------------------ -/** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ -// prettier-ignore -export type TRemap, - // Maps the Interior Parameterized Types - Result extends TSchema = ( - Mapped extends TConstructor ? TConstructor, TFromType> : - Mapped extends TFunction ? TFunction, TFromType> : - Mapped extends TIntersect ? TIntersect> : - Mapped extends TUnion ? TUnion> : - Mapped extends TTuple ? TTuple> : - Mapped extends TArray ? TArray>: - Mapped extends TAsyncIterator ? TAsyncIterator> : - Mapped extends TIterator ? TIterator> : - Mapped extends TPromise ? TPromise> : - Mapped extends TObject ? TObject> : - Mapped extends TRecord ? TRecordOrObject, TFromType> : - Mapped - ) -> = Result -/** `[Internal]` Applies a recursive conditional remapping of a type and its sub type constituents */ -// prettier-ignore -export function Remap(type: TSchema, callback: TCallback): TSchema { - // Map incoming type - const mapped = CloneType(FromType(type, callback)) - // Return remapped interior - return ( - KindGuard.IsConstructor(type) ? Constructor(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : - KindGuard.IsFunction(type) ? Function(FromTypes(type.parameters, callback), FromType(type.returns, callback), mapped) : - KindGuard.IsIntersect(type) ? Intersect(FromTypes(type.allOf, callback), mapped) : - KindGuard.IsUnion(type) ? Union(FromTypes(type.anyOf, callback), mapped) : - KindGuard.IsTuple(type) ? Tuple(FromTypes(type.items || [], callback), mapped) : - KindGuard.IsArray(type) ? Array(FromType(type.items, callback), mapped) : - KindGuard.IsAsyncIterator(type) ? AsyncIterator(FromType(type.items, callback), mapped) : - KindGuard.IsIterator(type) ? Iterator(FromType(type.items, callback), mapped) : - KindGuard.IsPromise(type) ? Promise(FromType(type.items, callback), mapped) : - KindGuard.IsObject(type) ? Object(FromProperties(type.properties, callback), mapped) : - KindGuard.IsRecord(type) ? Record(FromType(RecordKey(type), callback), FromType(RecordValue(type), callback), mapped) : - CloneType(mapped) - ) -} diff --git a/src/type/type/type.ts b/src/type/type/type.ts index d384cfa33..83fc32dc2 100644 --- a/src/type/type/type.ts +++ b/src/type/type/type.ts @@ -74,7 +74,6 @@ export { Record } from '../record/index' export { Recursive } from '../recursive/index' export { Ref } from '../ref/index' export { RegExp } from '../regexp/index' -export { Remap } from '../remap/index' export { Required } from '../required/index' export { Rest } from '../rest/index' export { ReturnType } from '../return-type/index' From f41dfe37abcb0ca622a2aa1be408e660ecf36e08 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 16 Feb 2025 04:15:52 +0900 Subject: [PATCH 342/369] Revision 0.34.24 (#1175) * Support Generic Parameter Syntax * Documentation * Version * ChangeLog --- changelog/0.34.0.md | 3 +- package-lock.json | 4 +- package.json | 2 +- readme.md | 56 ++++++++++---------- src/syntax/runtime.ts | 46 +++++++++++++++++ src/syntax/static.ts | 40 +++++++++++++++ src/syntax/syntax.ts | 80 ++++++++++++++++------------- src/type/instantiate/instantiate.ts | 20 ++++---- test/runtime/syntax/syntax.ts | 2 +- test/static/index.ts | 1 + test/static/syntax.ts | 35 +++++++++++++ 11 files changed, 211 insertions(+), 78 deletions(-) create mode 100644 test/static/syntax.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 2550686c4..50b6a5ce4 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -4,7 +4,8 @@ ### Revision Updates - +- [Revision 0.34.24](https://github.com/sinclairzx81/typebox/pull/1175) + - Add Support For Generic Parameter Syntax - [Revision 0.34.23](https://github.com/sinclairzx81/typebox/pull/1174) - Inline Instantiate Type Logic Local to Instantiate Module - [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171) diff --git a/package-lock.json b/package-lock.json index a7caf3c68..5dffd1d6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.23", + "version": "0.34.24", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.23", + "version": "0.34.24", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index db1c66b0e..7281eb530 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.23", + "version": "0.34.24", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index bd108c697..5ffedc827 100644 --- a/readme.md +++ b/readme.md @@ -98,8 +98,8 @@ License MIT - [Syntax](#syntax) - [Create](#syntax-create) - [Parameters](#syntax-parameters) - - [Options](#syntax-options) - [Generics](#syntax-generics) + - [Options](#syntax-options) - [TypeRegistry](#typeregistry) - [Type](#typeregistry-type) - [Format](#typeregistry-format) @@ -1306,9 +1306,9 @@ ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1 ## Syntax Types -TypeBox has support for parsing TypeScript syntax at runtime as well as statically in the type system. This feature offers a syntactical frontend to the TypeBox type builder. +TypeBox includes support for parsing TypeScript annotation syntax into TypeBox schematics. -Syntax types are available via optional import. +This feature is provided via optional import. ```typescript import { Syntax } from '@sinclair/typebox/syntax' @@ -1318,20 +1318,25 @@ import { Syntax } from '@sinclair/typebox/syntax' ### Create -Use the Syntax function to create TypeBox types from TypeScript syntax +Use the Syntax function to create TypeBox types from TypeScript syntax ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgAVOAF4Uo3AAoABkhwAuOOgCuIPjygAaOFR3Lx8-AkcASjgY2Jj2djhjUwt3cwB5PgArHgYYAB4ECTiS0rLyisrYhNi3OHMAOW9fAOKq9o7OuBqY4PqmsKg2rpHR+MT8AD4JCS5eeut5LEUGfLmeCCJ6ybHKmvWFmyLdk86euDrQlv9h07uy876rv1v7t-GCIA)) ```typescript const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{ // x: TNumber, // y: TNumber // }> + +type T = Static // type T = { + // x: number, + // y: number + // } ``` ### Parameters -Syntax types can be parameterized to receive exterior types. +Syntax types can be parameterized to receive exterior types ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgCgJDCOhrwAKnAC8KUbgAUAAyQ4AXHHQBXEHx5QANHFQHjp8wS0BKOK7ev27ODLmKDCgHk+ACseBhgAHgQJd1i4+ITEpLdPN304BQA5EzNLGOSCwqK4VNcbDOz7KHzi2rqPL3wAPikfeRQVNUxNJCV8Ky0ABSxYYCwmCIUm52LUtvhkfyDQ8Kia+o2C0rh0wLAYYFlxycrcpot1zav47fK9g6OJrJzzFuv3m8amoA)) ```typescript const T = Syntax(`{ x: number, y: number }`) // const T: TObject<{ @@ -1345,35 +1350,19 @@ const S = Syntax({ T }, `Partial`) // const S: TObject<{ // }> ``` - - -### Options -Options can be passed via the last parameter - -```typescript -const T = Syntax(`number`, { // const T = { - minimum: 0, // type: 'number', - maximum: 10 // minimum: 0, -}) // maximum: 10 - // } -``` ### Generics -Generic types can be created using positional argument types ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgAVAAxwAvClG4AFBQCCUAOYBXED0wAeSwA+CgBKIxMzOHMARlt7TCdXD29fGD9o4LDjUwsAJji0BJxnNy8ff1zMiXCcuAA1HgYYaAKHYqQrABoo6O7zfPxugAMECTg4HAAuKMtOsbhUaZi58YAvJdyJfCGwmsiAISw6Ggam6BpWosckU+aoAmHR8an6xrv07tm4IJWF6dvoAFur1voFfutXmcoEDvsCwVsdtUuLw4IdjgCoBc7MgFEo-MieBAiKijsATm9zoFxtT2Ow4ASSeiKZi4k9qeyOZyudyeTzadSXkgXiDFrC4BDrIN5ryZbK5ez+eNRULpl9RSCJQ9pfKdbqFXS1tMVWLRV8IbF8Nq9da5fzCEA)) +Syntax types support generic parameters in the following way ([Example](https://www.typescriptlang.org/play/?moduleResolution=99&module=199&ts=5.8.0-beta#code/JYWwDg9gTgLgBAbzgZQJ4DsYEMAecC+cAZlBCHAOQACAzsOgMYA2WwUA9DKmAKYBGEHOxoZsOCgChQkWIhTYYwBgWKly1OoxZtO3foMkSGEdDXgA1HgxjQ4AXhSjcACgAGAHgAaAGjgBNXwAtAD45CTg4HAAuOB84cLhUGID4iIAvGMD4-FcASgkjEzM4ACEsOhpLa2gae0dMFyQqmygCX1cEBOi4Zuh3AEZfAAZh4O8EpJ6rFvcRuEG4IbGEjKnqqFnh337lnPyJLl5S8uBK6Zq65AUld0OeCCJjit6oGlCIiPZ2ODun05fag5Oh8QaCweCIZCoV8Pt0kN0FpM5qshm0ElCMZisSCYRFJvCYnNJgsUWjseSKeDcXBVgTFr4kb5Vv0COjKezsTD8EA)) ```typescript -const T0 = Syntax('Argument<0>') -const T1 = Syntax('Argument<1>') -const T2 = Syntax('Argument<2>') - -const Vector = Syntax({ T0, T1, T2 }, `{ - x: T0, - y: T1, - z: T2 +const Vector = Syntax(` { + x: X, + y: Y, + z: Z }`) const BasisVectors = Syntax({ Vector }, `{ @@ -1386,7 +1375,20 @@ type BasisVectors = Static // type BasisVectors = { // x: { x: 1, y: 0, z: 0 }, // y: { x: 0, y: 1, z: 0 }, // z: { x: 0, y: 0, z: 1 } - // } + // } +``` + + + +### Options + +Options can be passed via the last parameter + +```typescript +const T = Syntax(`number`, { minimum: 42 }) // const T = { + // type: 'number', + // minimum: 42 + // } ``` diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 227393a1b..f13a09aea 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -66,6 +66,46 @@ const Dereference = (context: t.TProperties, key: string): t.TSchema => { return key in context ? context[key] : t.Ref(key) } // ------------------------------------------------------------------ +// GenericArgumentList +// ------------------------------------------------------------------ +// prettier-ignore +const GenericArgumentListMapping = (results: unknown[]) => { + return ( + results.length === 3 ? [results[0], ...results[2] as unknown[]] : + results.length === 2 ? [results[0]] : + results.length === 1 ? [results[0]] : + [] + ) +} +// prettier-ignore +const GenericArgumentList = Runtime.Union([ + Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma), Runtime.Ref('GenericArgumentList')]), + Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma)]), + Runtime.Tuple([Runtime.Ident()]), + Runtime.Tuple([]), +], (results) => GenericArgumentListMapping(results)) +// ------------------------------------------------------------------ +// GenericArguments +// ------------------------------------------------------------------ +// prettier-ignore +const GenericArgumentsContext = (args: string[]) => { + return args.reduce((result, arg, index) => { + return { ...result, [arg]: t.Argument(index) } + }, {}) +} +// prettier-ignore +const GenericArgumentsMapping = (results: unknown[]) => { + return results.length === 3 + ? GenericArgumentsContext(results[1] as string[]) + : {} +} +// prettier-ignore +const GenericArguments = Runtime.Tuple([ + Runtime.Const(LAngle), + Runtime.Ref('GenericArgumentList'), + Runtime.Const(RAngle), +], results => GenericArgumentsMapping(results)) +// ------------------------------------------------------------------ // GenericReference // ------------------------------------------------------------------ function GenericReferenceMapping(results: unknown[], context: t.TProperties) { @@ -657,11 +697,17 @@ const Date = Runtime.Const('Date', Runtime.As(t.Date())) // Uint8Array // ------------------------------------------------------------------ const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array())) + // ------------------------------------------------------------------ // Module // ------------------------------------------------------------------ // prettier-ignore export const Module = new Runtime.Module({ + // ---------------------------------------------------------------- + // Generic Arguments + // ---------------------------------------------------------------- + GenericArgumentList, + GenericArguments, // ---------------------------------------------------------------- // Type Expressions // ---------------------------------------------------------------- diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 1ace9a445..0d06c5bff 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -143,6 +143,46 @@ type Dereference = ( Ref extends keyof Context ? Context[Ref] : t.TRef ) // ------------------------------------------------------------------ +// GenericArgumentList +// ------------------------------------------------------------------ +// prettier-ignore +interface GenericArgumentListMapping extends Static.IMapping { + output: ( + this['input'] extends [infer Ident extends string, Comma, infer Rest extends unknown[]] ? [Ident, ...Rest] : + this['input'] extends [infer Ident extends string, Comma] ? [Ident] : + this['input'] extends [infer Ident extends string] ? [Ident] : + [] + ) +} +// prettier-ignore +type GenericArgumentList = Static.Union<[ + Static.Tuple<[Static.Ident, Static.Const, GenericArgumentList]>, + Static.Tuple<[Static.Ident, Static.Const]>, + Static.Tuple<[Static.Ident]>, + Static.Tuple<[]>, +], GenericArgumentListMapping> +// ------------------------------------------------------------------ +// GenericArguments +// ------------------------------------------------------------------ +// prettier-ignore +type GenericArgumentsContext = ( + Args extends [...infer Left extends string[], infer Right extends string] + ? GenericArgumentsContext }> + : t.Evaluate +) +// prettier-ignore +interface GenericArgumentsMapping extends Static.IMapping { + output: this['input'] extends [LAngle, infer Args extends string[], RAngle] + ? GenericArgumentsContext + : never +} +// prettier-ignore +export type GenericArguments = Static.Tuple<[ + Static.Const, + GenericArgumentList, + Static.Const, +], GenericArgumentsMapping> +// ------------------------------------------------------------------ // GenericReference // ------------------------------------------------------------------ // prettier-ignore diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts index 4d5550cb3..9f6e482ff 100644 --- a/src/syntax/syntax.ts +++ b/src/syntax/syntax.ts @@ -26,43 +26,57 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as Types from '../type/index' +import * as t from '../type/index' import { Static } from '../parser/index' import { Module } from './runtime' -import { Type } from './static' +import { Type, GenericArguments } from './static' +// ------------------------------------------------------------------ +// ParseSyntax: Two-Phase Parse +// ------------------------------------------------------------------ +// prettier-ignore +type TParseSyntax, Code extends string> = ( + Static.Parse extends [infer Args extends t.TProperties, infer Rest extends string] + ? Static.Parse + : Static.Parse +) +// prettier-ignore +function ParseSyntax, Code extends string>(context: Context, code: Code): [] | [t.TSchema, string] { + const results = Module.Parse('GenericArguments', code, {}) // [ArgumentContext, Rest] + return ( + results.length === 2 + ? Module.Parse('Type', results[1], { ...context, ...results[0] }) + : Module.Parse('Type', code, context) + ) as never +} // ------------------------------------------------------------------ // NoInfer // ------------------------------------------------------------------ -/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ -export function NoInfer, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined -/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ -export function NoInfer(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined -/** Parses a TSchema type from TypeScript syntax but does not infer schematics */ +/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +export function NoInfer, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema +/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +export function NoInfer(code: Code, options?: t.SchemaOptions): t.TSchema +/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ // prettier-ignore -export function NoInfer(...args: any[]): Types.TSchema | undefined { +export function NoInfer(...args: any[]): t.TSchema { const withContext = typeof args[0] === 'string' ? false : true const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] - const type = Module.Parse('Type', code, context)[0] - return Types.KindGuard.IsSchema(type) - ? Types.CloneType(type, options) - : Types.Never(options) + const result = ParseSyntax(context, code)[0] + return t.KindGuard.IsSchema(result) + ? t.CloneType(result, options) + : t.Never(options) } -// ------------------------------------------------------------------ -// Syntax -// ------------------------------------------------------------------ -/** Infers a TSchema type from TypeScript syntax. */ + +/** Parses a TypeScript annotation into a TypeBox type */ // prettier-ignore -export type TSyntax, Code extends string> = ( - Static.Parse extends [infer Type extends Types.TSchema, string] - ? Type - : Types.TNever +export type TSyntax, Code extends string> = ( + TParseSyntax extends [infer Type extends t.TSchema, string] ? Type : t.TNever ) -/** Parses a TSchema type from TypeScript syntax */ -export function Syntax, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax -/** Parses a TSchema type from TypeScript syntax */ -export function Syntax(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code> -/** Parses a TSchema type from TypeScript syntax */ +/** Parses a TypeScript annotation into a TypeBox type */ +export function Syntax, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax +/** Parses a TypeScript annotation into a TypeBox type */ +export function Syntax(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation> +/** Parses a TypeScript annotation into a TypeBox type */ export function Syntax(...args: any[]): never { return NoInfer.apply(null, args as never) as never } @@ -70,36 +84,30 @@ export function Syntax(...args: any[]): never { // Deprecated // ------------------------------------------------------------------ /** - * Parses a TSchema type from Syntax. * @deprecated Use Syntax() function */ -export function Parse, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): TSyntax +export function Parse, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax /** - * Parses a TSchema type from Syntax. * @deprecated Use Syntax() function */ -export function Parse(code: Code, options?: Types.SchemaOptions): TSyntax<{}, Code> +export function Parse(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation> /** - * Parses a TSchema type from Syntax. * @deprecated Use Syntax() function */ export function Parse(...args: any[]): never { return NoInfer.apply(null, args as never) as never } /** - * Parses a TSchema from TypeScript Syntax * @deprecated Use NoInfer() function */ -export function ParseOnly, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +export function ParseOnly, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema | undefined /** - * Parses a TSchema from TypeScript Syntax * @deprecated Use NoInfer() function */ -export function ParseOnly(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined +export function ParseOnly(code: Code, options?: t.SchemaOptions): t.TSchema | undefined /** - * Parses a TSchema from TypeScript Syntax * @deprecated Use NoInfer() function */ -export function ParseOnly(...args: any[]): Types.TSchema | undefined { +export function ParseOnly(...args: any[]): t.TSchema | undefined { return NoInfer.apply(null, args as never) as never } diff --git a/src/type/instantiate/instantiate.ts b/src/type/instantiate/instantiate.ts index 210df5f66..e4fd67955 100644 --- a/src/type/instantiate/instantiate.ts +++ b/src/type/instantiate/instantiate.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import { CloneType } from '../clone/type' import { type TSchema } from '../schema/index' import { type TArgument } from '../argument/index' -import { type TNever, Never } from '../never/index' +import { type TUnknown, Unknown } from '../unknown/index' import { type TReadonlyOptional, ReadonlyOptional } from '../readonly-optional/index' import { type TReadonly, Readonly } from '../readonly/index' import { type TOptional, Optional } from '../optional/index' @@ -42,7 +42,7 @@ import { type TArray } from '../array/index' import { type TAsyncIterator } from '../async-iterator/index' import { type TIterator } from '../iterator/index' import { type TPromise } from '../promise/index' -import { type TObject, type TProperties } from '../object/index' +import { type TObject, type TProperties, Object } from '../object/index' import { type TRecordOrObject, type TRecord, Record, RecordKey, RecordValue } from '../record/index' import * as ValueGuard from '../guard/value' @@ -164,12 +164,12 @@ function FromPromise(args: TSchema[], type: TPromise): TPromise { // ------------------------------------------------------------------ // prettier-ignore type TFromObject> -> = Result + Result extends TProperties = TFromProperties +> = TObject // prettier-ignore function FromObject(args: TSchema[], type: TObject): TObject { - type.properties = FromProperties(args, type.properties) - return type + const properties = FromProperties(args, type.properties) + return { ...type, ...Object(properties) } // retain options } // ------------------------------------------------------------------ // Object @@ -191,12 +191,12 @@ function FromRecord(args: TSchema[], type: TRecord): TRecord { // Argument // ------------------------------------------------------------------ // prettier-ignore -type TFromArgument = ( - Index extends keyof Args ? Args[Index] : TNever -) +type TFromArgument = Result // prettier-ignore function FromArgument(args: TSchema[], argument: TArgument): TSchema { - return argument.index in args ? args[argument.index] : Never() + return argument.index in args ? args[argument.index] : Unknown() } // ------------------------------------------------------------------ // Property diff --git a/test/runtime/syntax/syntax.ts b/test/runtime/syntax/syntax.ts index 62749598e..148119a7d 100644 --- a/test/runtime/syntax/syntax.ts +++ b/test/runtime/syntax/syntax.ts @@ -417,6 +417,6 @@ describe('syntax/Syntax', () => { const T = Syntax({ G }, `G`) Assert.IsTrue(TypeGuard.IsTuple(T)) Assert.IsTrue(TypeGuard.IsNumber(T.items![0])) - Assert.IsTrue(TypeGuard.IsNever(T.items![1])) + Assert.IsTrue(TypeGuard.IsUnknown(T.items![1])) }) }) diff --git a/test/static/index.ts b/test/static/index.ts index 40c2067b3..2a27fadee 100644 --- a/test/static/index.ts +++ b/test/static/index.ts @@ -48,6 +48,7 @@ import './rest' import './return-type' import './string' import './symbol' +import './syntax' import './template-literal' import './transform' import './tuple' diff --git a/test/static/syntax.ts b/test/static/syntax.ts new file mode 100644 index 000000000..7b0a84601 --- /dev/null +++ b/test/static/syntax.ts @@ -0,0 +1,35 @@ +import { Expect } from './assert' +import { Syntax } from '@sinclair/typebox/syntax' + +// prettier-ignore +{ + const Basis = Syntax(`{ + x: 1, + y: 2, + z: 3 + }`) + + Expect(Basis).ToStatic<{ + x: 1, + y: 2, + z: 3, + }>() +} +// prettier-ignore +{ + const Vector = Syntax(`{ + x: X, + y: Y, + z: Z + }`) + const Basis = Syntax({ Vector }, `{ + x: Vector<1, 0, 0>, + y: Vector<0, 1, 0>, + z: Vector<0, 0, 1>, + }`) + Expect(Basis).ToStatic<{ + x: { x: 1, y: 0, z: 0 }, + y: { x: 0, y: 1, z: 0 }, + z: { x: 0, y: 0, z: 1 }, + }>() +} From b2eb0de540f9577e2dd3e4b74faab7f2480cdb61 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 16 Feb 2025 15:42:27 +0900 Subject: [PATCH 343/369] Revision 0.34.25 (#1176) * Evaluate Object Type in Instantiate + Documentation * Documentation * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- readme.md | 20 +++++++++++++++++++- src/syntax/syntax.ts | 14 +++++++------- src/type/instantiate/instantiate.ts | 9 +++++---- 6 files changed, 36 insertions(+), 15 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 50b6a5ce4..b511b6f2f 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -4,6 +4,8 @@ ### Revision Updates +- [Revision 0.34.25](https://github.com/sinclairzx81/typebox/pull/1176) + - Evaluate Conditional Expression for Instatiated Object Types - [Revision 0.34.24](https://github.com/sinclairzx81/typebox/pull/1175) - Add Support For Generic Parameter Syntax - [Revision 0.34.23](https://github.com/sinclairzx81/typebox/pull/1174) diff --git a/package-lock.json b/package-lock.json index 5dffd1d6e..f34a66b9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.24", + "version": "0.34.25", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.24", + "version": "0.34.25", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 7281eb530..3c6914331 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.24", + "version": "0.34.25", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index 5ffedc827..ce855410e 100644 --- a/readme.md +++ b/readme.md @@ -100,6 +100,7 @@ License MIT - [Parameters](#syntax-parameters) - [Generics](#syntax-generics) - [Options](#syntax-options) + - [NoInfer](#syntax-no-infer) - [TypeRegistry](#typeregistry) - [Type](#typeregistry-type) - [Format](#typeregistry-format) @@ -1306,7 +1307,7 @@ ValuePointer.Set(A, '/z', 1) // A' = { x: 1, y: 1, z: 1 ## Syntax Types -TypeBox includes support for parsing TypeScript annotation syntax into TypeBox schematics. +TypeBox provides experimental support for parsing TypeScript annotation syntax into TypeBox types. This feature is provided via optional import. @@ -1391,6 +1392,23 @@ const T = Syntax(`number`, { minimum: 42 }) // const T = { // } ``` + + +### NoInfer + +Syntax parsing is an expensive type level operation and can impact on language service performance. Use the NoInfer function parse syntax at runtime only. + +```typescript +import { NoInfer } from '@sinclair/typebox/syntax' + +const T = NoInfer(`number | string`) // const T: TSchema = { + // anyOf: [ + // { type: 'number' }, + // { type: 'string' } + // ] + // } +``` + ## TypeRegistry diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts index 9f6e482ff..3309ff155 100644 --- a/src/syntax/syntax.ts +++ b/src/syntax/syntax.ts @@ -52,11 +52,11 @@ function ParseSyntax, Code extend // ------------------------------------------------------------------ // NoInfer // ------------------------------------------------------------------ -/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ export function NoInfer, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema -/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ export function NoInfer(code: Code, options?: t.SchemaOptions): t.TSchema -/** Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ // prettier-ignore export function NoInfer(...args: any[]): t.TSchema { const withContext = typeof args[0] === 'string' ? false : true @@ -67,16 +67,16 @@ export function NoInfer(...args: any[]): t.TSchema { : t.Never(options) } -/** Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ // prettier-ignore export type TSyntax, Code extends string> = ( TParseSyntax extends [infer Type extends t.TSchema, string] ? Type : t.TNever ) -/** Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ export function Syntax, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax -/** Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ export function Syntax(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation> -/** Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ export function Syntax(...args: any[]): never { return NoInfer.apply(null, args as never) as never } diff --git a/src/type/instantiate/instantiate.ts b/src/type/instantiate/instantiate.ts index e4fd67955..3cb2b4857 100644 --- a/src/type/instantiate/instantiate.ts +++ b/src/type/instantiate/instantiate.ts @@ -164,12 +164,13 @@ function FromPromise(args: TSchema[], type: TPromise): TPromise { // ------------------------------------------------------------------ // prettier-ignore type TFromObject -> = TObject + MappedProperties extends TProperties = TFromProperties, + Result extends TObject = TObject +> = Result // prettier-ignore function FromObject(args: TSchema[], type: TObject): TObject { - const properties = FromProperties(args, type.properties) - return { ...type, ...Object(properties) } // retain options + const mappedProperties = FromProperties(args, type.properties) + return { ...type, ...Object(mappedProperties) } // retain options } // ------------------------------------------------------------------ // Object From 2f1b54aacdb3dce0c9f663a1e94697045f978a4e Mon Sep 17 00:00:00 2001 From: sinclair Date: Wed, 19 Feb 2025 18:47:33 +0900 Subject: [PATCH 344/369] Prototype: FromSchema --- example/prototypes/from-schema.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/example/prototypes/from-schema.ts b/example/prototypes/from-schema.ts index f44c36735..31cc374da 100644 --- a/example/prototypes/from-schema.ts +++ b/example/prototypes/from-schema.ts @@ -41,6 +41,7 @@ const IsSTuple = (value: unknown): value is STuple => Type.ValueGuard.IsObject(v const IsSArray = (value: unknown): value is SArray => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'array') && !Type.ValueGuard.IsArray(value.items) && Type.ValueGuard.IsObject(value.items) const IsSConst = (value: unknown): value is SConst => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsObject(value['const']) const IsSString = (value: unknown): value is SString => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'string') +const IsSRef = (value: unknown): value is SRef => Type.ValueGuard.IsObject(value) && Type.ValueGuard.IsString(value.$ref) const IsSNumber = (value: unknown): value is SNumber => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'number') const IsSInteger = (value: unknown): value is SInteger => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'integer') const IsSBoolean = (value: unknown): value is SBoolean => Type.ValueGuard.IsObject(value) && IsExact(value.type, 'boolean') @@ -58,6 +59,7 @@ type SObject = Readonly<{ type: 'object'; properties: SProperties; required?: re type STuple = Readonly<{ type: 'array'; items: readonly unknown[] }> type SArray = Readonly<{ type: 'array'; items: unknown }> type SConst = Readonly<{ const: SValue }> +type SRef = Readonly<{ $ref: string }> type SString = Readonly<{ type: 'string' }> type SNumber = Readonly<{ type: 'number' }> type SInteger = Readonly<{ type: 'integer' }> @@ -174,6 +176,16 @@ function FromConst(T: T) { return Type.Literal(T.const, T) } // ------------------------------------------------------------------ +// Ref +// ------------------------------------------------------------------ +// prettier-ignore +type TFromRef = ( + Type.Ensure> +) +function FromRef(T: T) { + return Type.Ref(T['$ref']) +} +// ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ type TFromPropertiesIsOptional = unknown extends R ? true : K extends R ? false : true @@ -208,6 +220,7 @@ export type TFromSchema = ( T extends STuple ? TFromTuple : T extends SArray ? TFromArray : T extends SConst ? TFromConst : + T extends SRef ? TFromRef : T extends SString ? Type.TString : T extends SNumber ? Type.TNumber : T extends SInteger ? Type.TInteger : @@ -227,6 +240,7 @@ export function FromSchema(T: T): TFromSchema { IsSTuple(T) ? FromTuple(T) : IsSArray(T) ? FromArray(T) : IsSConst(T) ? FromConst(T) : + IsSRef(T) ? FromRef(T) : IsSString(T) ? Type.String(T) : IsSNumber(T) ? Type.Number(T) : IsSInteger(T) ? Type.Integer(T) : From 9cd581a6b1b7245d1741bb4c95b85cadd23443be Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 19 Feb 2025 21:59:47 +0900 Subject: [PATCH 345/369] Revision 0.34.26 (#1181) * Parse Context Threading * Version * ChangeLog --- changelog/0.34.0.md | 2 + package-lock.json | 4 +- package.json | 2 +- src/parser/runtime/guard.ts | 54 +++++++----- src/parser/runtime/module.ts | 19 +++-- src/parser/runtime/parse.ts | 132 ++++++++++++++++++---------- src/parser/runtime/types.ts | 161 ++++++++++++++++++++++++++--------- src/parser/static/parse.ts | 70 +++++++++++---- src/parser/static/types.ts | 91 ++++++++++++++------ src/syntax/runtime.ts | 22 +++-- src/syntax/static.ts | 19 +++-- src/syntax/syntax.ts | 24 +----- 12 files changed, 400 insertions(+), 200 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index b511b6f2f..45a967947 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -4,6 +4,8 @@ ### Revision Updates +- [Revision 0.34.26](https://github.com/sinclairzx81/typebox/pull/1181) + - Internal: Use Parser Context Threading for Generic Arguments. - [Revision 0.34.25](https://github.com/sinclairzx81/typebox/pull/1176) - Evaluate Conditional Expression for Instatiated Object Types - [Revision 0.34.24](https://github.com/sinclairzx81/typebox/pull/1175) diff --git a/package-lock.json b/package-lock.json index f34a66b9e..502f9362a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.25", + "version": "0.34.26", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.25", + "version": "0.34.26", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 3c6914331..f443150d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.25", + "version": "0.34.26", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/parser/runtime/guard.ts b/src/parser/runtime/guard.ts index fa002e473..a340b8e86 100644 --- a/src/parser/runtime/guard.ts +++ b/src/parser/runtime/guard.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IIdent, INumber, IRef, IString, IConst, ITuple, IUnion } from './types' +import { IArray, IConst, IContext, IIdent, INumber, IOptional, IRef, IString, ITuple, IUnion } from './types' // ------------------------------------------------------------------ // Value Guard @@ -46,51 +46,59 @@ function IsArrayValue(value: unknown): value is unknown[] { // ------------------------------------------------------------------ // Parser Guard // ------------------------------------------------------------------ -/** Returns true if the value is a Tuple Parser */ -// prettier-ignore -export function IsTuple(value: unknown): value is ITuple { - return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Tuple' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) -} -/** Returns true if the value is a Union Parser */ -// prettier-ignore -export function IsUnion(value: unknown): value is IUnion { - return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Union' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +/** Returns true if the value is a Array Parser */ +export function IsArray(value: unknown): value is IArray { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Array' && HasPropertyKey(value, 'parser') && IsObjectValue(value.parser) } /** Returns true if the value is a Const Parser */ -// prettier-ignore export function IsConst(value: unknown): value is IConst { return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Const' && HasPropertyKey(value, 'value') && typeof value.value === 'string' } +/** Returns true if the value is a Context Parser */ +export function IsContext(value: unknown): value is IContext { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Context' && HasPropertyKey(value, 'left') && IsParser(value.left) && HasPropertyKey(value, 'right') && IsParser(value.right) +} /** Returns true if the value is a Ident Parser */ -// prettier-ignore export function IsIdent(value: unknown): value is IIdent { return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ident' } /** Returns true if the value is a Number Parser */ -// prettier-ignore export function IsNumber(value: unknown): value is INumber { return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Number' } +/** Returns true if the value is a Optional Parser */ +export function IsOptional(value: unknown): value is IOptional { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Optional' && HasPropertyKey(value, 'parser') && IsObjectValue(value.parser) +} /** Returns true if the value is a Ref Parser */ -// prettier-ignore export function IsRef(value: unknown): value is IRef { return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Ref' && HasPropertyKey(value, 'ref') && typeof value.ref === 'string' } /** Returns true if the value is a String Parser */ -// prettier-ignore export function IsString(value: unknown): value is IString { return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'String' && HasPropertyKey(value, 'options') && IsArrayValue(value.options) } +/** Returns true if the value is a Tuple Parser */ +export function IsTuple(value: unknown): value is ITuple { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Tuple' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} +/** Returns true if the value is a Union Parser */ +export function IsUnion(value: unknown): value is IUnion { + return IsObjectValue(value) && HasPropertyKey(value, 'type') && value.type === 'Union' && HasPropertyKey(value, 'parsers') && IsArrayValue(value.parsers) +} /** Returns true if the value is a Parser */ -// prettier-ignore export function IsParser(value: unknown) { + // prettier-ignore return ( - IsTuple(value) || - IsUnion(value) || - IsConst(value) || - IsIdent(value) || - IsNumber(value) || - IsRef(value) || - IsString(value) + IsArray(value) || + IsConst(value) || + IsContext(value) || + IsIdent(value) || + IsNumber(value) || + IsOptional(value) || + IsRef(value) || + IsString(value) || + IsTuple(value) || + IsUnion(value) ) } diff --git a/src/parser/runtime/module.ts b/src/parser/runtime/module.ts index 4384ce4f4..b00349b60 100644 --- a/src/parser/runtime/module.ts +++ b/src/parser/runtime/module.ts @@ -32,20 +32,21 @@ import { Parse } from './parse' // ------------------------------------------------------------------ // Module // ------------------------------------------------------------------ -// prettier-ignore export class Module { - constructor(private readonly properties: Properties) { } - + constructor(private readonly properties: Properties) {} + /** Parses using one of the parsers defined on this instance */ - public Parse(key: Key, code: string, context: unknown): [] | [Types.StaticParser, string] + public Parse(key: Key, content: string, context: unknown): [] | [Types.StaticParser, string] /** Parses using one of the parsers defined on this instance */ - public Parse(key: Key, code: string): [] | [Types.StaticParser, string] + public Parse(key: Key, content: string): [] | [Types.StaticParser, string] /** Parses using one of the parsers defined on this instance */ public Parse(...args: any[]): never { - const [key, code, context] = - args.length === 3 ? [args[0], args[1], args[2]] : - args.length === 2 ? [args[0], args[1], undefined] : + // prettier-ignore + const [key, content, context] = ( + args.length === 3 ? [args[0], args[1], args[2]] : + args.length === 2 ? [args[0], args[1], undefined] : (() => { throw Error('Invalid parse arguments') })() - return Parse(this.properties[key], this.properties, code, context) as never + ) + return Parse(this.properties, this.properties[key], content, context) as never } } diff --git a/src/parser/runtime/parse.ts b/src/parser/runtime/parse.ts index 73ecf2f75..e810789cd 100644 --- a/src/parser/runtime/parse.ts +++ b/src/parser/runtime/parse.ts @@ -31,48 +31,67 @@ import * as Token from './token' import * as Types from './types' // ------------------------------------------------------------------ -// Tuple +// Context // ------------------------------------------------------------------ -// prettier-ignore -function ParseTuple(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown[], string] { +function ParseContext(moduleProperties: ModuleProperties, left: Parser, right: Parser, code: string, context: unknown): unknown[] { + const result = ParseParser(moduleProperties, left, code, context) + return result.length === 2 ? ParseParser(moduleProperties, right, result[1], result[0]) : [] +} + +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +function ParseArray(moduleProperties: ModuleProperties, parser: Parser, code: string, context: unknown): unknown[] { const buffer = [] as unknown[] let rest = code - for(const parser of parsers) { - const result = ParseParser(parser, properties, rest, context) - if(result.length === 0) return [] + while (rest.length > 0) { + const result = ParseParser(moduleProperties, parser, rest, context) + if (result.length === 0) return [buffer, rest] buffer.push(result[0]) rest = result[1] } return [buffer, rest] } + // ------------------------------------------------------------------ -// Union +// Const // ------------------------------------------------------------------ -// prettier-ignore -function ParseUnion(parsers: [...Parsers], properties: Properties, code: string, context: unknown): [] | [unknown, string] { - for(const parser of parsers) { - const result = ParseParser(parser, properties, code, context) - if(result.length === 0) continue - return result - } - return [] +function ParseConst(value: Value, code: string, context: unknown): [] | [Value, string] { + return Token.Const(value, code) as never } + // ------------------------------------------------------------------ -// Const +// Ident +// ------------------------------------------------------------------ +function ParseIdent(code: string, _context: unknown): [] | [string, string] { + return Token.Ident(code) +} + +// ------------------------------------------------------------------ +// Number // ------------------------------------------------------------------ // prettier-ignore -function ParseConst(value: Value, code: string, context: unknown): [] | [Value, string] { - return Token.Const(value, code) as never +function ParseNumber(code: string, _context: unknown): [] | [string, string] { + return Token.Number(code) } + +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +function ParseOptional(moduleProperties: ModuleProperties, parser: Parser, code: string, context: unknown): [] | [[unknown] | [], unknown] { + const result = ParseParser(moduleProperties, parser, code, context) + return (result.length === 2 ? [[result[0]], result[1]] : [[], code]) as never +} + // ------------------------------------------------------------------ // Ref // ------------------------------------------------------------------ -// prettier-ignore -function ParseRef(ref: Ref, properties: Properties, code: string, context: unknown): [] | [string, string] { - const parser = properties[ref] - if(!Guard.IsParser(parser)) throw Error(`Cannot dereference parser '${ref}'`) - return ParseParser(parser, properties, code, context) as never +function ParseRef(moduleProperties: ModuleProperties, ref: Ref, code: string, context: unknown): [] | [string, string] { + const parser = moduleProperties[ref] + if (!Guard.IsParser(parser)) throw Error(`Cannot dereference Parser '${ref}'`) + return ParseParser(moduleProperties, parser, code, context) as never } + // ------------------------------------------------------------------ // String // ------------------------------------------------------------------ @@ -80,33 +99,51 @@ function ParseRef(moduleProperties: ModuleProperties, parsers: [...Parsers], code: string, context: unknown): [] | [unknown[], string] { + const buffer = [] as unknown[] + let rest = code + for (const parser of parsers) { + const result = ParseParser(moduleProperties, parser, rest, context) + if (result.length === 0) return [] + buffer.push(result[0]) + rest = result[1] + } + return [buffer, rest] } + // ------------------------------------------------------------------ -// Ident +// Union // ------------------------------------------------------------------ // prettier-ignore -function ParseIdent(code: string, _context: unknown): [] | [string, string] { - return Token.Ident(code) +function ParseUnion(moduleProperties: ModuleProperties, parsers: [...Parsers], code: string, context: unknown): [] | [unknown, string] { + for(const parser of parsers) { + const result = ParseParser(moduleProperties, parser, code, context) + if(result.length === 0) continue + return result + } + return [] } + // ------------------------------------------------------------------ // Parser // ------------------------------------------------------------------ // prettier-ignore -function ParseParser(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] { +function ParseParser(moduleProperties: Types.IModuleProperties, parser: Parser, code: string, context: unknown): [] | [Types.StaticParser, string] { const result = ( - Guard.IsTuple(parser) ? ParseTuple(parser.parsers, properties, code, context) : - Guard.IsUnion(parser) ? ParseUnion(parser.parsers, properties, code, context) : + Guard.IsContext(parser) ? ParseContext(moduleProperties, parser.left, parser.right, code, context) : + Guard.IsArray(parser) ? ParseArray(moduleProperties, parser.parser, code, context) : Guard.IsConst(parser) ? ParseConst(parser.value, code, context) : - Guard.IsRef(parser) ? ParseRef(parser.ref, properties, code, context) : - Guard.IsString(parser) ? ParseString(parser.options, code, context) : Guard.IsIdent(parser) ? ParseIdent(code, context) : Guard.IsNumber(parser) ? ParseNumber(code, context) : + Guard.IsOptional(parser) ? ParseOptional(moduleProperties, parser.parser, code, context) : + Guard.IsRef(parser) ? ParseRef(moduleProperties, parser.ref, code, context) : + Guard.IsString(parser) ? ParseString(parser.options, code, context) : + Guard.IsTuple(parser) ? ParseTuple(moduleProperties, parser.parsers, code, context) : + Guard.IsUnion(parser) ? ParseUnion(moduleProperties, parser.parsers, code, context) : [] ) return ( @@ -115,27 +152,28 @@ function ParseParser(parser: Parser, properties: T : result ) as never } + // ------------------------------------------------------------------ // Parse // ------------------------------------------------------------------ -/** Parses content using the given parser */ +/** Parses content using the given Parser */ // prettier-ignore -export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string, context: unknown): [] | [Types.StaticParser, string] -/** Parses content using the given parser */ +export function Parse(moduleProperties: Types.IModuleProperties, parser: Parser, code: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given Parser */ // prettier-ignore -export function Parse(parser: Parser, properties: Types.IModuleProperties, code: string): [] | [Types.StaticParser, string] -/** Parses content using the given parser */ +export function Parse(moduleProperties: Types.IModuleProperties, parser: Parser, code: string): [] | [Types.StaticParser, string] +/** Parses content using the given Parser */ // prettier-ignore -export function Parse(parser: Parser, code: string, context: unknown): [] | [Types.StaticParser, string] -/** Parses content using the given parser */ +export function Parse(parser: Parser, content: string, context: unknown): [] | [Types.StaticParser, string] +/** Parses content using the given Parser */ // prettier-ignore -export function Parse(parser: Parser, code: string): [] | [Types.StaticParser, string] +export function Parse(parser: Parser, content: string): [] | [Types.StaticParser, string] /** Parses content using the given parser */ // prettier-ignore export function Parse(...args: any[]): never { - const withProperties = typeof args[1] === 'string' ? false : true - const [parser, properties, code, context] = withProperties + const withModuleProperties = typeof args[1] === 'string' ? false : true + const [moduleProperties, parser, content, context] = withModuleProperties ? [args[0], args[1], args[2], args[3]] - : [args[0], {}, args[1], args[2]] - return ParseParser(parser, properties, code, context) as never + : [{}, args[0], args[1], args[2]] + return ParseParser(moduleProperties, parser, content, context) as never } diff --git a/src/parser/runtime/types.ts b/src/parser/runtime/types.ts index 53dd047d0..c013bf5fa 100644 --- a/src/parser/runtime/types.ts +++ b/src/parser/runtime/types.ts @@ -32,6 +32,9 @@ export type IModuleProperties = Record // Static // ------------------------------------------------------------------ +/** Force output static type evaluation for Arrays */ +export type StaticEnsure = T extends infer R ? R : never + /** Infers the Output Parameter for a Parser */ export type StaticParser = Parser extends IParser ? Output : unknown @@ -44,10 +47,8 @@ export type IMapping value /** Maps the output as the given parameter T */ -export const As = - (mapping: T): ((value: unknown) => T) => - (_: unknown) => - mapping +// prettier-ignore +export const As = (mapping: T): ((value: unknown) => T) => (_: unknown) => mapping // ------------------------------------------------------------------ // Parser @@ -57,50 +58,59 @@ export interface IParser { mapping: IMapping } // ------------------------------------------------------------------ -// Tuple +// Context // ------------------------------------------------------------------ -export type TupleParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? TupleParameter]> : Result -export interface ITuple extends IParser { - type: 'Tuple' - parsers: IParser[] +// prettier-ignore +export type ContextParameter<_Left extends IParser, Right extends IParser> = ( + StaticParser +) +export interface IContext extends IParser { + type: 'Context' + left: IParser + right: IParser } -/** Creates a Tuple parser */ -export function Tuple>>(parsers: [...Parsers], mapping: Mapping): ITuple> -/** Creates a Tuple parser */ -export function Tuple(parsers: [...Parsers]): ITuple> -export function Tuple(...args: unknown[]): never { - const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] - return { type: 'Tuple', parsers, mapping } as never +/** `[Context]` Creates a Context Parser */ +export function Context>>(left: Left, right: Right, mapping: Mapping): IContext> +/** `[Context]` Creates a Context Parser */ +export function Context(left: Left, right: Right): IContext> +/** `[Context]` Creates a Context Parser */ +export function Context(...args: unknown[]): never { + const [left, right, mapping] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], args[1], Identity] + return { type: 'Context', left, right, mapping } as never } - // ------------------------------------------------------------------ -// Union +// Array // ------------------------------------------------------------------ -export type UnionParameter = Parsers extends [infer L extends IParser, ...infer R extends IParser[]] ? UnionParameter> : Result -export interface IUnion extends IParser { - type: 'Union' - parsers: IParser[] +// prettier-ignore +export type ArrayParameter = StaticEnsure< + StaticParser[] +> +export interface IArray extends IParser { + type: 'Array' + parser: IParser } -/** Creates a Union parser */ -export function Union>>(parsers: [...Parsers], mapping: Mapping): IUnion> -/** Creates a Union parser */ -export function Union(parsers: [...Parsers]): IUnion> -export function Union(...args: unknown[]): never { - const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] - return { type: 'Union', parsers, mapping } as never +/** `[EBNF]` Creates an Array Parser */ +export function Array>>(parser: Parser, mapping: Mapping): IArray> +/** `[EBNF]` Creates an Array Parser */ +export function Array(parser: Parser): IArray> +/** `[EBNF]` Creates an Array Parser */ +export function Array(...args: unknown[]): never { + const [parser, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Array', parser, mapping } as never } // ------------------------------------------------------------------ -// Token +// Const // ------------------------------------------------------------------ export interface IConst extends IParser { type: 'Const' value: string } -/** Creates a Const parser */ +/** `[TERM]` Creates a Const Parser */ export function Const>(value: Value, mapping: Mapping): IConst> -/** Creates a Const parser */ +/** `[TERM]` Creates a Const Parser */ export function Const(value: Value): IConst +/** `[TERM]` Creates a Const Parser */ export function Const(...args: unknown[]): never { const [value, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] return { type: 'Const', value, mapping } as never @@ -113,10 +123,11 @@ export interface IRef extends IParser type: 'Ref' ref: string } -/** Creates a Ref parser */ +/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */ export function Ref>(ref: string, mapping: Mapping): IRef> -/** Creates a Ref parser */ +/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */ export function Ref(ref: string): IRef +/** `[BNF]` Creates a Ref Parser. This Parser can only be used in the context of a Module */ export function Ref(...args: unknown[]): never { const [ref, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] return { type: 'Ref', ref, mapping } as never @@ -129,10 +140,11 @@ export interface IString extends IParser>(options: string[], mapping: Mapping): IString> -/** Creates a String Parser. Options are an array of permissable quote characters */ +/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */ export function String(options: string[]): IString +/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */ export function String(...params: unknown[]): never { const [options, mapping] = params.length === 2 ? [params[0], params[1]] : [params[0], Identity] return { type: 'String', options, mapping } as never @@ -144,10 +156,11 @@ export function String(...params: unknown[]): never { export interface IIdent extends IParser { type: 'Ident' } -/** Creates an Ident parser */ +/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */ export function Ident>(mapping: Mapping): IIdent> -/** Creates an Ident parser */ +/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */ export function Ident(): IIdent +/** `[TERM]` Creates an Ident Parser where Ident matches any valid JavaScript identifier */ export function Ident(...params: unknown[]): never { const mapping = params.length === 1 ? params[0] : Identity return { type: 'Ident', mapping } as never @@ -159,11 +172,79 @@ export function Ident(...params: unknown[]): never { export interface INumber extends IParser { type: 'Number' } -/** Creates a Number parser */ +/** `[TERM]` Creates an Number Parser */ export function Number>(mapping: Mapping): INumber> -/** Creates a Number parser */ +/** `[TERM]` Creates an Number Parser */ export function Number(): INumber +/** `[TERM]` Creates an Number Parser */ export function Number(...params: unknown[]): never { const mapping = params.length === 1 ? params[0] : Identity return { type: 'Number', mapping } as never } + +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +// prettier-ignore +export type OptionalParameter] | []> = ( + Result +) +export interface IOptional extends IParser { + type: 'Optional' + parser: IParser +} +/** `[EBNF]` Creates an Optional Parser */ +export function Optional>>(parser: Parser, mapping: Mapping): IOptional> +/** `[EBNF]` Creates an Optional Parser */ +export function Optional(parser: Parser): IOptional> +/** `[EBNF]` Creates an Optional Parser */ +export function Optional(...args: unknown[]): never { + const [parser, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Optional', parser, mapping } as never +} + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +export type TupleParameter = StaticEnsure< + Parsers extends [infer Left extends IParser, ...infer Right extends IParser[]] + ? TupleParameter>]> + : Result +> +export interface ITuple extends IParser { + type: 'Tuple' + parsers: IParser[] +} +/** `[BNF]` Creates a Tuple Parser */ +export function Tuple>>(parsers: [...Parsers], mapping: Mapping): ITuple> +/** `[BNF]` Creates a Tuple Parser */ +export function Tuple(parsers: [...Parsers]): ITuple> +/** `[BNF]` Creates a Tuple Parser */ +export function Tuple(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Tuple', parsers, mapping } as never +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +export type UnionParameter = StaticEnsure< + Parsers extends [infer Left extends IParser, ...infer Right extends IParser[]] + ? UnionParameter> + : Result +> +export interface IUnion extends IParser { + type: 'Union' + parsers: IParser[] +} +/** `[BNF]` Creates a Union parser */ +export function Union>>(parsers: [...Parsers], mapping: Mapping): IUnion> +/** `[BNF]` Creates a Union parser */ +export function Union(parsers: [...Parsers]): IUnion> +/** `[BNF]` Creates a Union parser */ +export function Union(...args: unknown[]): never { + const [parsers, mapping] = args.length === 2 ? [args[0], args[1]] : [args[0], Identity] + return { type: 'Union', parsers, mapping } as never +} diff --git a/src/parser/static/parse.ts b/src/parser/static/parse.ts index f4fc12e1e..f19eaad9c 100644 --- a/src/parser/static/parse.ts +++ b/src/parser/static/parse.ts @@ -30,27 +30,23 @@ import * as Tokens from './token' import * as Types from './types' // ------------------------------------------------------------------ -// Tuple +// Context // ------------------------------------------------------------------ // prettier-ignore -type TupleParser = ( - Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] - ? Parse extends [infer Value extends unknown, infer Rest extends string] - ? TupleParser - : [] - : [Result, Code] +type ContextParser = ( + Parse extends [infer Context extends unknown, infer Rest extends string] + ? Parse + : [] ) // ------------------------------------------------------------------ -// Union +// Array // ------------------------------------------------------------------ // prettier-ignore -type UnionParser = ( - Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] - ? Parse extends [infer Value extends unknown, infer Rest extends string] - ? [Value, Rest] - : UnionParser - : [] +type ArrayParser = ( + Parse extends [infer Value1 extends unknown, infer Rest extends string] + ? ArrayParser + : [Result, Code] ) // ------------------------------------------------------------------ @@ -83,6 +79,16 @@ type NumberParser = ( : [] ) +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +// prettier-ignore +type OptionalParser = ( + Parse extends [infer Value extends unknown, infer Rest extends string] + ? [[Value], Rest] + : [[], Code] +) + // ------------------------------------------------------------------ // String // ------------------------------------------------------------------ @@ -93,23 +99,51 @@ type StringParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? TupleParser + : [] + : [Result, Code] +) + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +type UnionParser = ( + Parsers extends [infer Left extends Types.IParser, ...infer Right extends Types.IParser[]] + ? Parse extends [infer Value extends unknown, infer Rest extends string] + ? [Value, Rest] + : UnionParser + : [] +) + // ------------------------------------------------------------------ // Parse // ------------------------------------------------------------------ // prettier-ignore type ParseCode = ( - Type extends Types.Union ? UnionParser : - Type extends Types.Tuple ? TupleParser : - Type extends Types.Const ? ConstParser : - Type extends Types.String ? StringParser : + Type extends Types.Context ? ContextParser : + Type extends Types.Array ? ArrayParser : + Type extends Types.Const ? ConstParser : Type extends Types.Ident ? IdentParser : Type extends Types.Number ? NumberParser : + Type extends Types.Optional ? OptionalParser : + Type extends Types.String ? StringParser : + Type extends Types.Tuple ? TupleParser : + Type extends Types.Union ? UnionParser : [] ) // prettier-ignore type ParseMapping = ( (Parser['mapping'] & { input: Result, context: Context })['output'] ) + /** Parses code with the given parser */ // prettier-ignore export type Parse = ( diff --git a/src/parser/static/types.ts b/src/parser/static/types.ts index 534125505..5adbc306c 100644 --- a/src/parser/static/types.ts +++ b/src/parser/static/types.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/parse +@sinclair/parsebox The MIT License (MIT) @@ -29,16 +29,25 @@ THE SOFTWARE. // ------------------------------------------------------------------ // Mapping // ------------------------------------------------------------------ +/** + * `[ACTION]` Inference mapping base type. Used to specify semantic actions for + * Parser productions. This type is implemented as a higher-kinded type where + * productions are received on the `input` property with mapping assigned + * the `output` property. The parsing context is available on the `context` + * property. + */ export interface IMapping { context: unknown input: unknown output: unknown } -/** Maps input to output. This is the default Mapping */ + +/** `[ACTION]` Default inference mapping. */ export interface Identity extends IMapping { output: this['input'] } -/** Maps the output as the given parameter T */ + +/** `[ACTION]` Maps the given argument `T` as the mapping output */ export interface As extends IMapping { output: T } @@ -50,51 +59,85 @@ export interface IParser { type: string mapping: Mapping } + // ------------------------------------------------------------------ -// Tuple +// Context // ------------------------------------------------------------------ -/** Creates a Tuple Parser */ -export interface Tuple extends IParser { - type: 'Tuple' - parsers: [...Parsers] +/** `[Context]` Creates a Context Parser */ +export interface Context extends IParser { + type: 'Context' + left: Left + right: Right } + // ------------------------------------------------------------------ -// Union +// Array // ------------------------------------------------------------------ -/** Creates a Union Parser */ -export interface Union extends IParser { - type: 'Union' - parsers: [...Parsers] +/** `[EBNF]` Creates an Array Parser */ +export interface Array extends IParser { + type: 'Array' + parser: Parser } + // ------------------------------------------------------------------ // Const // ------------------------------------------------------------------ -/** Creates a Const Parser */ +/** `[TERM]` Creates a Const Parser */ export interface Const extends IParser { type: 'Const' value: Value } -// ------------------------------------------------------------------ -// String -// ------------------------------------------------------------------ -/** Creates a String Parser. Options are an array of permissable quote characters */ -export interface String extends IParser { - type: 'String' - quote: Options -} + // ------------------------------------------------------------------ // Ident // ------------------------------------------------------------------ -/** Creates an Ident Parser. */ +/** `[TERM]` Creates an Ident Parser. */ // prettier-ignore export interface Ident extends IParser { type: 'Ident' } + // ------------------------------------------------------------------ // Number // ------------------------------------------------------------------ -/** Creates a Number Parser. */ +/** `[TERM]` Creates a Number Parser. */ // prettier-ignore export interface Number extends IParser { type: 'Number' } + +// ------------------------------------------------------------------ +// Optional +// ------------------------------------------------------------------ +/** `[EBNF]` Creates a Optional Parser */ +export interface Optional extends IParser { + type: 'Optional' + parser: Parser +} + +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +/** `[TERM]` Creates a String Parser. Options are an array of permissable quote characters */ +export interface String extends IParser { + type: 'String' + quote: Options +} + +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +/** `[BNF]` Creates a Tuple Parser */ +export interface Tuple extends IParser { + type: 'Tuple' + parsers: [...Parsers] +} + +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +/** `[BNF]` Creates a Union Parser */ +export interface Union extends IParser { + type: 'Union' + parsers: [...Parsers] +} diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index f13a09aea..428ae8e7a 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -88,15 +88,15 @@ const GenericArgumentList = Runtime.Union([ // GenericArguments // ------------------------------------------------------------------ // prettier-ignore -const GenericArgumentsContext = (args: string[]) => { +const GenericArgumentsContext = (args: string[], context: t.TProperties) => { return args.reduce((result, arg, index) => { return { ...result, [arg]: t.Argument(index) } - }, {}) + }, context) } // prettier-ignore -const GenericArgumentsMapping = (results: unknown[]) => { +const GenericArgumentsMapping = (results: unknown[], context: t.TProperties) => { return results.length === 3 - ? GenericArgumentsContext(results[1] as string[]) + ? GenericArgumentsContext(results[1] as string[], context) : {} } // prettier-ignore @@ -104,7 +104,7 @@ const GenericArguments = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('GenericArgumentList'), Runtime.Const(RAngle), -], results => GenericArgumentsMapping(results)) +], (results, context) => GenericArgumentsMapping(results, context)) // ------------------------------------------------------------------ // GenericReference // ------------------------------------------------------------------ @@ -326,7 +326,11 @@ const Expr = Runtime.Tuple([ // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ -const Type = Runtime.Ref('Expr') +// prettier-ignore +const Type = Runtime.Union([ + Runtime.Context(Runtime.Ref('GenericArguments'), Runtime.Ref('Expr')), + Runtime.Ref('Expr') +]) // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ @@ -704,12 +708,12 @@ const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array())) // prettier-ignore export const Module = new Runtime.Module({ // ---------------------------------------------------------------- - // Generic Arguments + // Generics // ---------------------------------------------------------------- GenericArgumentList, GenericArguments, // ---------------------------------------------------------------- - // Type Expressions + // Type // ---------------------------------------------------------------- Literal, Keyword, @@ -722,7 +726,7 @@ export const Module = new Runtime.Module({ ExprTerm, ExprTail, Expr, - Type, // Alias for Expr + Type, PropertyKey, Readonly, Optional, diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 0d06c5bff..1e9712f52 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -165,23 +165,26 @@ type GenericArgumentList = Static.Union<[ // GenericArguments // ------------------------------------------------------------------ // prettier-ignore -type GenericArgumentsContext = ( +type GenericArgumentsContext = ( Args extends [...infer Left extends string[], infer Right extends string] - ? GenericArgumentsContext }> - : t.Evaluate + ? GenericArgumentsContext }> + : t.Evaluate ) // prettier-ignore interface GenericArgumentsMapping extends Static.IMapping { output: this['input'] extends [LAngle, infer Args extends string[], RAngle] - ? GenericArgumentsContext + ? this['context'] extends infer Context extends t.TProperties + ? GenericArgumentsContext + : never : never } // prettier-ignore -export type GenericArguments = Static.Tuple<[ +type GenericArguments = Static.Tuple<[ Static.Const, GenericArgumentList, Static.Const, ], GenericArgumentsMapping> + // ------------------------------------------------------------------ // GenericReference // ------------------------------------------------------------------ @@ -426,7 +429,11 @@ type Expr = Static.Tuple<[ // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ -export type Type = Expr +// prettier-ignore +export type Type = Static.Union<[ + Static.Context, + Expr +]> // ------------------------------------------------------------------ // Properties // ------------------------------------------------------------------ diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts index 3309ff155..e467cd2ef 100644 --- a/src/syntax/syntax.ts +++ b/src/syntax/syntax.ts @@ -29,26 +29,8 @@ THE SOFTWARE. import * as t from '../type/index' import { Static } from '../parser/index' import { Module } from './runtime' -import { Type, GenericArguments } from './static' +import { Type } from './static' -// ------------------------------------------------------------------ -// ParseSyntax: Two-Phase Parse -// ------------------------------------------------------------------ -// prettier-ignore -type TParseSyntax, Code extends string> = ( - Static.Parse extends [infer Args extends t.TProperties, infer Rest extends string] - ? Static.Parse - : Static.Parse -) -// prettier-ignore -function ParseSyntax, Code extends string>(context: Context, code: Code): [] | [t.TSchema, string] { - const results = Module.Parse('GenericArguments', code, {}) // [ArgumentContext, Rest] - return ( - results.length === 2 - ? Module.Parse('Type', results[1], { ...context, ...results[0] }) - : Module.Parse('Type', code, context) - ) as never -} // ------------------------------------------------------------------ // NoInfer // ------------------------------------------------------------------ @@ -61,7 +43,7 @@ export function NoInfer(code: Code, options?: t.SchemaOptio export function NoInfer(...args: any[]): t.TSchema { const withContext = typeof args[0] === 'string' ? false : true const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] - const result = ParseSyntax(context, code)[0] + const result = Module.Parse('Type', code, context)[0] return t.KindGuard.IsSchema(result) ? t.CloneType(result, options) : t.Never(options) @@ -70,7 +52,7 @@ export function NoInfer(...args: any[]): t.TSchema { /** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ // prettier-ignore export type TSyntax, Code extends string> = ( - TParseSyntax extends [infer Type extends t.TSchema, string] ? Type : t.TNever + Static.Parse extends [infer Type extends t.TSchema, string] ? Type : t.TNever ) /** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ export function Syntax, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax From 5130ee57a721d58a43dfa180f8603a4530231302 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 20 Feb 2025 16:24:01 +0900 Subject: [PATCH 346/369] Revision 0.34.27 (#1182) * Support Deep Referential Transform Inference Inside Modules * Version * ChangeLog --- changelog/0.34.0.md | 3 +- package-lock.json | 4 +- package.json | 2 +- src/type/module/compute.ts | 208 +++++++++++++++---------- src/type/static/static.ts | 88 +++++++---- src/value/transform/decode.ts | 9 +- src/value/transform/encode.ts | 8 +- src/value/transform/has.ts | 9 ++ test/runtime/value/transform/import.ts | 68 +++++++- test/static/transform.ts | 54 +++++++ 10 files changed, 323 insertions(+), 130 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 45a967947..c0c59d339 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,7 +3,8 @@ --- ### Revision Updates - +- [Revision 0.34.27](https://github.com/sinclairzx81/typebox/pull/1182) + - [1178](https://github.com/sinclairzx81/typebox/issues/1178) Support Deep Referential Transform Inference Inside Modules - [Revision 0.34.26](https://github.com/sinclairzx81/typebox/pull/1181) - Internal: Use Parser Context Threading for Generic Arguments. - [Revision 0.34.25](https://github.com/sinclairzx81/typebox/pull/1176) diff --git a/package-lock.json b/package-lock.json index 502f9362a..ac510958e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.26", + "version": "0.34.27", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.26", + "version": "0.34.27", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f443150d6..e59f3ed4a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.26", + "version": "0.34.27", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/module/compute.ts b/src/type/module/compute.ts index abd138af9..286665597 100644 --- a/src/type/module/compute.ts +++ b/src/type/module/compute.ts @@ -51,18 +51,24 @@ import { Partial, TPartial } from '../partial/index' import { type TReadonly } from '../readonly/index' import { RecordValue, RecordPattern, type TRecordOrObject, type TRecord } from '../record/index' import { type TRef } from '../ref/index' -import { Required, TRequired } from '../required/index' +import { Required, type TRequired } from '../required/index' +import { type TTransform } from '../transform/index' import { Tuple, type TTuple } from '../tuple/index' import { Union, type TUnion, type TUnionEvaluated } from '../union/index' -import { OptionalKind, ReadonlyKind } from '../symbols/index' +// ------------------------------------------------------------------ +// Symbols +// ------------------------------------------------------------------ +import { TransformKind, OptionalKind, ReadonlyKind } from '../symbols/index' + // ------------------------------------------------------------------ // KindGuard // ------------------------------------------------------------------ import * as KindGuard from '../guard/kind' // ------------------------------------------------------------------ -// DerefParameters +// +// DereferenceParameters // // Dereferences TComputed parameters. It is important to note that // dereferencing anything other than these parameters may result @@ -71,39 +77,47 @@ import * as KindGuard from '../guard/kind' // // ------------------------------------------------------------------ // prettier-ignore -type TDerefParameters = ( +type TDereferenceParameters = ( Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? Left extends TRef - ? TDerefParameters]> - : TDerefParameters]> + ? TDereferenceParameters]> + : TDereferenceParameters]> : Result ) // prettier-ignore -function DerefParameters(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { +function DereferenceParameters(moduleProperties: ModuleProperties, types: [...Types]): TSchema[] { return types.map((type) => { return KindGuard.IsRef(type) - ? Deref(moduleProperties, type.$ref) + ? Dereference(moduleProperties, type.$ref) : FromType(moduleProperties, type) }) as never } // prettier-ignore -type TDeref - ? TDeref + ? TDereference : TFromType : TNever )> = Result // prettier-ignore -function Deref(moduleProperties: ModuleProperties, ref: Ref): TDeref { +function Dereference(moduleProperties: ModuleProperties, ref: Ref): TSchema { return ( ref in moduleProperties ? KindGuard.IsRef(moduleProperties[ref]) - ? Deref(moduleProperties, moduleProperties[ref].$ref) + ? Dereference(moduleProperties, moduleProperties[ref].$ref) : FromType(moduleProperties, moduleProperties[ref]) : Never() ) as never } + +// ------------------------------------------------------------------ +// +// TComputed +// +// The following types support deferred evaluation +// +// ------------------------------------------------------------------ // ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ @@ -193,7 +207,7 @@ function FromRequired(parameters: Parameters): TFr // ------------------------------------------------------------------ // prettier-ignore type TFromComputed + Dereferenced extends TSchema[] = TDereferenceParameters > = ( Target extends 'Awaited' ? TFromAwaited : Target extends 'Index' ? TFromIndex : @@ -206,7 +220,7 @@ type TFromComputed(moduleProperties: ModuleProperties, target: Target, parameters: Parameters): TFromComputed { - const dereferenced = DerefParameters(moduleProperties, parameters) + const dereferenced = DereferenceParameters(moduleProperties, parameters) return ( target === 'Awaited' ? FromAwaited(dereferenced) : target === 'Index' ? FromIndex(dereferenced) : @@ -218,45 +232,41 @@ function FromComputed = Ensure -}>>> -function FromObject(moduleProperties: ModuleProperties, properties: Properties): TFromObject { - return Object( - globalThis.Object.keys(properties).reduce((result, key) => { - return { ...result, [key]: FromType(moduleProperties, properties[key]) as never } - }, {} as TProperties), - ) as never +type TFromArray = ( + Ensure>> +) +function FromArray(moduleProperties: ModuleProperties, type: Type): TFromArray { + return Array(FromType(moduleProperties, type)) } // ------------------------------------------------------------------ -// Record -// -// Note: Varying Runtime and Static path here as we need to retain -// constraints on the Record. This requires remapping the entire -// Record in the Runtime path but where the Static path is merely -// a facade for the patternProperties regular expression. +// AsyncIterator // ------------------------------------------------------------------ // prettier-ignore -type TFromRecord> -> = Result -// prettier-ignore -function FromRecord(moduleProperties: ModuleProperties, type: Type): never { - const [value, pattern] = [FromType(moduleProperties, RecordValue(type)), RecordPattern(type)] - const result = CloneType(type) - result.patternProperties[pattern] = value - return result as never +type TFromAsyncIterator = ( + TAsyncIterator> +) +function FromAsyncIterator(moduleProperties: ModuleProperties, type: Type): TFromAsyncIterator { + return AsyncIterator(FromType(moduleProperties, type)) } // ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ // prettier-ignore type TFromConstructor = ( - TConstructor, TFromType> + TConstructor, TFromType> ) // prettier-ignore function FromConstructor( @@ -264,14 +274,14 @@ function FromConstructor { - return Constructor(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, instanceType) as never) as never + return Constructor(FromTypes(moduleProperties, parameters as never), FromType(moduleProperties, instanceType) as never) as never } // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ // prettier-ignore type TFromFunction = Ensure< - Ensure, TFromType>> + Ensure, TFromType>> > // prettier-ignore function FromFunction( @@ -279,78 +289,106 @@ function FromFunction { - return FunctionType(FromRest(moduleProperties, parameters as never), FromType(moduleProperties, returnType) as never) as never + return FunctionType(FromTypes(moduleProperties, parameters as never), FromType(moduleProperties, returnType) as never) as never } // ------------------------------------------------------------------ -// Tuple +// Intersect // ------------------------------------------------------------------ // prettier-ignore -type TFromTuple = ( - Ensure>> +type TFromIntersect = ( + Ensure>> ) -function FromTuple(moduleProperties: ModuleProperties, types: [...Types]): TFromTuple { - return Tuple(FromRest(moduleProperties, types as never)) as never +function FromIntersect(moduleProperties: ModuleProperties, types: [...Types]): TFromIntersect { + return Intersect(FromTypes(moduleProperties, types as never)) as never } // ------------------------------------------------------------------ -// Intersect +// Iterator // ------------------------------------------------------------------ // prettier-ignore -type TFromIntersect = ( - Ensure>> +type TFromIterator = ( + TIterator> ) -function FromIntersect(moduleProperties: ModuleProperties, types: [...Types]): TFromIntersect { - return Intersect(FromRest(moduleProperties, types as never)) as never +function FromIterator(moduleProperties: ModuleProperties, type: Type): TFromIterator { + return Iterator(FromType(moduleProperties, type)) } // ------------------------------------------------------------------ -// Union +// Object // ------------------------------------------------------------------ // prettier-ignore -type TFromUnion = ( - Ensure>> -) -function FromUnion(moduleProperties: ModuleProperties, types: [...Types]): TFromUnion { - return Union(FromRest(moduleProperties, types as never)) as never +type TFromObject = Ensure +}>>> +function FromObject(moduleProperties: ModuleProperties, properties: Properties): TFromObject { + return Object( + globalThis.Object.keys(properties).reduce((result, key) => { + return { ...result, [key]: FromType(moduleProperties, properties[key]) as never } + }, {} as TProperties), + ) as never } // ------------------------------------------------------------------ -// Array +// Record +// +// Note: Varying Runtime and Static path here as we need to retain +// constraints on the Record. This requires remapping the entire +// Record in the Runtime path but where the Static path is merely +// a facade for the patternProperties regular expression. // ------------------------------------------------------------------ // prettier-ignore -type TFromArray = ( - Ensure>> -) -function FromArray(moduleProperties: ModuleProperties, type: Type): TFromArray { - return Array(FromType(moduleProperties, type)) +type TFromRecord> +> = Result +// prettier-ignore +function FromRecord(moduleProperties: ModuleProperties, type: Type): never { + const [value, pattern] = [FromType(moduleProperties, RecordValue(type)), RecordPattern(type)] + const result = CloneType(type) + result.patternProperties[pattern] = value + return result as never } // ------------------------------------------------------------------ -// AsyncIterator +// Transform // ------------------------------------------------------------------ // prettier-ignore -type TFromAsyncIterator = ( - TAsyncIterator> +type TFromTransform + ? TTransform, Output> + : TTransform +> = Result +// prettier-ignore +function FromTransform(moduleProperties: ModuleProperties, transform: Transform): TSchema { + return (KindGuard.IsRef(transform)) + ? { ...Dereference(moduleProperties, transform.$ref), [TransformKind]: transform[TransformKind] } + : transform +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +type TFromTuple = ( + Ensure>> ) -function FromAsyncIterator(moduleProperties: ModuleProperties, type: Type): TFromAsyncIterator { - return AsyncIterator(FromType(moduleProperties, type)) +function FromTuple(moduleProperties: ModuleProperties, types: [...Types]): TFromTuple { + return Tuple(FromTypes(moduleProperties, types as never)) as never } // ------------------------------------------------------------------ -// Iterator +// Union // ------------------------------------------------------------------ // prettier-ignore -type TFromIterator = ( - TIterator> +type TFromUnion = ( + Ensure>> ) -function FromIterator(moduleProperties: ModuleProperties, type: Type): TFromIterator { - return Iterator(FromType(moduleProperties, type)) +function FromUnion(moduleProperties: ModuleProperties, types: [...Types]): TFromUnion { + return Union(FromTypes(moduleProperties, types as never)) as never } // ------------------------------------------------------------------ -// Rest +// Types // ------------------------------------------------------------------ // prettier-ignore -type TFromRest = ( +type TFromTypes = ( Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] - ? TFromRest]> + ? TFromTypes]> : Result ) -function FromRest(moduleProperties: ModuleProperties, types: [...Types]): TFromRest { +function FromTypes(moduleProperties: ModuleProperties, types: [...Types]): TFromTypes { return types.map((type) => FromType(moduleProperties, type)) as never } // ------------------------------------------------------------------ @@ -358,10 +396,12 @@ function FromRest // ------------------------------------------------------------------ // prettier-ignore export type TFromType = ( - // Modifier Unwrap + // Modifier Type extends TOptional ? TOptional> : Type extends TReadonly ? TReadonly> : - // Traveral + // Transform + Type extends TTransform ? TFromTransform : + // Types Type extends TArray ? TFromArray : Type extends TAsyncIterator ? TFromAsyncIterator : Type extends TComputed ? TFromComputed : @@ -379,10 +419,12 @@ export type TFromType(moduleProperties: ModuleProperties, type: Type): TFromType { return ( - // Modifier Unwrap - Reapplied via CreateType Options + // Modifiers KindGuard.IsOptional(type) ? CreateType(FromType(moduleProperties, Discard(type, [OptionalKind]) as TSchema) as never, type) : KindGuard.IsReadonly(type) ? CreateType(FromType(moduleProperties, Discard(type, [ReadonlyKind]) as TSchema) as never, type) : - // Traveral + // Transform + KindGuard.IsTransform(type) ? CreateType(FromTransform(moduleProperties, type), type) : + // Types KindGuard.IsArray(type) ? CreateType(FromArray(moduleProperties, type.items), type) : KindGuard.IsAsyncIterator(type) ? CreateType(FromAsyncIterator(moduleProperties, type.items), type) : KindGuard.IsComputed(type) ? CreateType(FromComputed(moduleProperties, type.target, type.parameters)) : diff --git a/src/type/static/static.ts b/src/type/static/static.ts index 84999f184..b85250600 100644 --- a/src/type/static/static.ts +++ b/src/type/static/static.ts @@ -35,6 +35,7 @@ import type { TConstructor } from '../constructor/index' import type { TEnum } from '../enum/index' import type { TFunction } from '../function/index' import type { TIntersect } from '../intersect/index' +import type { TImport } from '../module/index' import type { TIterator } from '../iterator/index' import type { TNot } from '../not/index' import type { TObject, TProperties } from '../object/index' @@ -47,48 +48,73 @@ import type { TUnion } from '../union/index' import type { TUnsafe } from '../unsafe/index' import type { TSchema } from '../schema/index' import type { TTransform } from '../transform/index' +import type { TNever } from '../never/index' // ------------------------------------------------------------------ -// DecodeType +// Import // ------------------------------------------------------------------ // prettier-ignore -export type TDecodeProperties = { - [K in keyof T]: TDecodeType +type TDecodeImport = ( + Key extends keyof ModuleProperties + ? TDecodeType extends infer Type extends TSchema + ? Type extends TRef + ? TDecodeImport + : Type + : TNever + : TNever +) +// ------------------------------------------------------------------ +// Properties +// ------------------------------------------------------------------ +// prettier-ignore +type TDecodeProperties = { + [Key in keyof Properties]: TDecodeType } +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ // prettier-ignore -export type TDecodeRest = - T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? TDecodeRest]> - : Acc +type TDecodeTypes = ( + Types extends [infer Left extends TSchema, ...infer Right extends TSchema[]] + ? TDecodeTypes]> + : Result +) +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ // prettier-ignore -export type TDecodeType = ( - T extends TOptional ? TOptional> : - T extends TReadonly ? TReadonly> : - T extends TTransform ? TUnsafe : - T extends TArray ? TArray> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor, TDecodeType> : - T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction, TDecodeType> : - T extends TIntersect ? TIntersect> : - T extends TIterator ? TIterator> : - T extends TNot ? TNot> : - T extends TObject ? TObject>> : - T extends TPromise ? TPromise> : - T extends TRecord ? TRecord> : - T extends TRecursive ? TRecursive> : - T extends TRef ? TRef : - T extends TTuple ? TTuple> : - T extends TUnion ? TUnion> : - T +export type TDecodeType = ( + Type extends TOptional ? TOptional> : + Type extends TReadonly ? TReadonly> : + Type extends TTransform ? TUnsafe : + Type extends TArray ? TArray> : + Type extends TAsyncIterator ? TAsyncIterator> : + Type extends TConstructor ? TConstructor, TDecodeType> : + Type extends TEnum ? TEnum : // intercept for union. interior non decodable + Type extends TFunction ? TFunction, TDecodeType> : + Type extends TIntersect ? TIntersect> : + Type extends TImport ? TDecodeImport : + Type extends TIterator ? TIterator> : + Type extends TNot ? TNot> : + Type extends TObject ? TObject>> : + Type extends TPromise ? TPromise> : + Type extends TRecord ? TRecord> : + Type extends TRecursive ? TRecursive> : + Type extends TRef ? TRef : + Type extends TTuple ? TTuple> : + Type extends TUnion ? TUnion> : + Type ) // ------------------------------------------------------------------ // Static // ------------------------------------------------------------------ -export type StaticDecodeIsAny = boolean extends (T extends TSchema ? true : false) ? true : false +export type StaticDecodeIsAny = boolean extends (Type extends TSchema ? true : false) ? true : false /** Creates an decoded static type from a TypeBox type */ -export type StaticDecode = StaticDecodeIsAny extends true ? unknown : Static, P> +// prettier-ignore +export type StaticDecode extends true ? unknown : Static, Params> +> = Result /** Creates an encoded static type from a TypeBox type */ -export type StaticEncode = Static +export type StaticEncode> = Result /** Creates a static type from a TypeBox type */ -export type Static = (T & { params: P })['static'] +export type Static = Result diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index f9a120ae4..c40752ec1 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -118,12 +118,10 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string, } // prettier-ignore function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { - const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const additional = globalThis.Object.values(schema.$defs) as TSchema[] const target = schema.$defs[schema.$ref] as TSchema - const transform = schema[TransformKind as never] - // Note: we need to re-spec the target as TSchema + [TransformKind] - const transformTarget = { [TransformKind]: transform, ...target } as TSchema - return Visit(transformTarget as never, [...references, ...definitions], path, value) + const result = Visit(target, [...references, ...additional], path, value) + return Default(schema, path, result) } function FromNot(schema: TNot, references: TSchema[], path: string, value: any): unknown { return Default(schema, path, Visit(schema.not, references, path, value)) @@ -202,6 +200,7 @@ function FromUnion(schema: TUnion, references: TSchema[], path: string, value: a } return Default(schema, path, value) } + // prettier-ignore function Visit(schema: TSchema, references: TSchema[], path: string, value: any): any { const references_ = Pushref(schema, references) diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 1852c5ffe..ab8de080f 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -98,12 +98,10 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a } // prettier-ignore function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown { - const definitions = globalThis.Object.values(schema.$defs) as TSchema[] + const additional = globalThis.Object.values(schema.$defs) as TSchema[] const target = schema.$defs[schema.$ref] as TSchema - const transform = schema[TransformKind as never] - // Note: we need to re-spec the target as TSchema + [TransformKind] - const transformTarget = { [TransformKind]: transform, ...target } as TSchema - return Visit(transformTarget as never, [...references, ...definitions], path, value) + const result = Default(schema, path, value) + return Visit(target, [...references, ...additional], path, result) } // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 549a39e9e..0acf959c1 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -36,6 +36,7 @@ import type { TConstructor } from '../../type/constructor/index' import type { TFunction } from '../../type/function/index' import type { TIntersect } from '../../type/intersect/index' import type { TIterator } from '../../type/iterator/index' +import type { TImport } from '../../type/module/index' import type { TNot } from '../../type/not/index' import type { TObject } from '../../type/object/index' import type { TPromise } from '../../type/promise/index' @@ -75,6 +76,12 @@ function FromIntersect(schema: TIntersect, references: TSchema[]) { return IsTransform(schema) || IsTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) } // prettier-ignore +function FromImport(schema: TImport, references: TSchema[]) { + const additional = globalThis.Object.getOwnPropertyNames(schema.$defs).reduce((result, key) => [...result, schema.$defs[key as never]], [] as TSchema[]) + const target = schema.$defs[schema.$ref] + return IsTransform(schema) || Visit(target, [...additional, ...references]) +} +// prettier-ignore function FromIterator(schema: TIterator, references: TSchema[]) { return IsTransform(schema) || Visit(schema.items, references) } @@ -135,6 +142,8 @@ function Visit(schema: TSchema, references: TSchema[]): boolean { return FromConstructor(schema_, references_) case 'Function': return FromFunction(schema_, references_) + case 'Import': + return FromImport(schema_, references_) case 'Intersect': return FromIntersect(schema_, references_) case 'Iterator': diff --git a/test/runtime/value/transform/import.ts b/test/runtime/value/transform/import.ts index a8646dcfc..456610f75 100644 --- a/test/runtime/value/transform/import.ts +++ b/test/runtime/value/transform/import.ts @@ -135,13 +135,13 @@ describe('value/transform/Import', () => { const T5 = Type.Transform(Type.Module({ A: Type.Object({ x: Type.String(), y: Type.String() })}).Import('A')) .Decode((value) => new Map(Object.entries(value))) .Encode((value) => Object.fromEntries(value.entries()) as any) - it('should decode map', () => { + it('Should decode map', () => { const R = Encoder.Decode(T5, { x: 'hello', y: 'world' }) Assert.IsInstanceOf(R, Map) Assert.IsEqual(R.get('x'), 'hello') Assert.IsEqual(R.get('y'), 'world') }) - it('should encode map', () => { + it('Should encode map', () => { const R = Encoder.Encode( T5, new Map([ @@ -154,4 +154,68 @@ describe('value/transform/Import', () => { it('Should throw on map decode', () => { Assert.Throws(() => Encoder.Decode(T5, {})) }) + + // ------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1178 + // ------------------------------------------------------------- + // immediate + it('Should transform embedded module codec 1', () => { + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + }).Import('A') + + const D = Value.Decode(T, '123') + const E = Value.Encode(T, 123) + Assert.IsEqual(D, 123) + Assert.IsEqual(E, '123') + }) + // referential + it('Should transform embedded module codec 2', () => { + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + B: Type.Ref('A'), + }).Import('B') + const D = Value.Decode(T, '123') + const E = Value.Encode(T, 123) + Assert.IsEqual(D, 123) + Assert.IsEqual(E, '123') + }) + // deep-referential + it('Should transform embedded module codec 3', () => { + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + B: Type.Ref('A'), + C: Type.Ref('B'), + D: Type.Ref('C'), + E: Type.Ref('D'), + }).Import('E') + const D = Value.Decode(T, '123') + const E = Value.Encode(T, 123) + Assert.IsEqual(D, 123) + Assert.IsEqual(E, '123') + }) + // interior-transform referential + it('Should transform embedded module codec 4', () => { + const T = Type.Module({ + A: Type.String(), + B: Type.Ref('A'), + C: Type.Ref('B'), + T: Type.Transform(Type.Ref('C')) + .Decode((value) => parseInt(value as string)) + .Encode((value) => value.toString()), + X: Type.Ref('T'), + Y: Type.Ref('X'), + Z: Type.Ref('Y') + }).Import('Z') + const D = Value.Decode(T, '123') + const E = Value.Encode(T, 123) + Assert.IsEqual(D, 123) + Assert.IsEqual(E, '123') + }) }) diff --git a/test/static/transform.ts b/test/static/transform.ts index 2f5e2411f..e9dbc2177 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -320,3 +320,57 @@ import { Expect } from './assert' const x1 = c1.Decode({}) const x2 = Value.Decode({} as any, {}) } +// ------------------------------------------------------------- +// https://github.com/sinclairzx81/typebox/issues/1178 +// ------------------------------------------------------------- +// immediate +{ + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + }).Import('A') + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode() +} +// referential +{ + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + B: Type.Ref('A'), + }).Import('B') + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode() +} +// deep-referential +{ + const T = Type.Module({ + A: Type.Transform(Type.String()) + .Decode((value) => parseInt(value)) + .Encode((value) => value.toString()), + B: Type.Ref('A'), + C: Type.Ref('B'), + D: Type.Ref('C'), + E: Type.Ref('D'), + }).Import('E') + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode() +} +// interior-transform referential +{ + const T = Type.Module({ + A: Type.String(), + B: Type.Ref('A'), + C: Type.Ref('B'), + T: Type.Transform(Type.Ref('C')) + .Decode((value) => parseInt(value as string)) + .Encode((value) => value.toString()), + X: Type.Ref('T'), + Y: Type.Ref('X'), + Z: Type.Ref('Y'), + }).Import('Z') + Expect(T).ToStaticDecode() + Expect(T).ToStaticEncode() +} From 713fd483754f315531cc730317506544e6d23f66 Mon Sep 17 00:00:00 2001 From: rqphqel <5072757+rqphqel@users.noreply.github.com> Date: Sun, 23 Feb 2025 05:01:39 +0000 Subject: [PATCH 347/369] Documentation (#1185) - docs: fix typos in readme.md --- readme.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/readme.md b/readme.md index ce855410e..fccb50e12 100644 --- a/readme.md +++ b/readme.md @@ -544,7 +544,7 @@ The following table lists the supported Json types. These types are fully compat ### JavaScript Types -TypeBox provides an extended type set that can be used to create schematics for common JavaScript constructs. These types can not be used with any standard Json Schema validator; but can be used to frame schematics for interfaces that may receive Json validated data. JavaScript types are prefixed with the `[JavaScript]` jsdoc comment for convenience. The following table lists the supported types. +TypeBox provides an extended type set that can be used to create schematics for common JavaScript constructs. These types can not be used with any standard Json Schema validator; but can be used to frame schematics for interfaces that may receive Json validated data. JavaScript types are prefixed with the `[JavaScript]` JSDoc comment for convenience. The following table lists the supported types. ```typescript ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐ @@ -717,11 +717,11 @@ Object properties can be modified with Readonly and Optional. The following tabl ### Generic Types -Generic types can be created with generic functions +Generic types can be created with generic functions. ```typescript const Nullable = (T: T) => { // type Nullable = T | null - return Type.Union([T, Type.Null()) + return Type.Union([T, Type.Null()]) } const T = Nullable(Type.String()) // type T = Nullable @@ -731,7 +731,7 @@ const T = Nullable(Type.String()) // type T = Nullable ### Recursive Types -Use the Recursive function to create recursive types +Use the Recursive function to create recursive types. ```typescript const Node = Type.Recursive(This => Type.Object({ // const Node = { @@ -875,7 +875,7 @@ const C = Type.Index(T, Type.KeyOf(T)) // type C = T[keyof T] ### Mapped Types -TypeBox supports mapped types with the Mapped function. This function accepts two arguments, the first is a union type typically derived from KeyOf, the second is a mapping function that receives a mapping key `K` that can be used to index properties of a type. The following implements a mapped type that remaps each property to be `T | null` +TypeBox supports mapped types with the Mapped function. This function accepts two arguments, the first is a union type typically derived from KeyOf, the second is a mapping function that receives a mapping key `K` that can be used to index properties of a type. The following implements a mapped type that remaps each property to be `T | null`. ```typescript const T = Type.Object({ // type T = { @@ -1123,7 +1123,7 @@ const Z = Value.Default(T, { x: 1 }) // const 'Z = { x: 1, y: ### Cast -Use the Cast function to upcast a value into a target type. This function will retain as much infomation as possible from the original value. The Cast function is intended to be used in data migration scenarios where existing values need to be upgraded to match a modified type. +Use the Cast function to upcast a value into a target type. This function will retain as much information as possible from the original value. The Cast function is intended to be used in data migration scenarios where existing values need to be upgraded to match a modified type. ```typescript const T = Type.Object({ x: Type.Number(), y: Type.Number() }, { additionalProperties: false }) @@ -1203,7 +1203,7 @@ const R = Value.Equal( // const R = true ### Hash -Use the Hash function to create a [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) non cryptographic hash of a value. +Use the Hash function to create a [FNV1A-64](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) non-cryptographic hash of a value. ```typescript const A = Value.Hash({ x: 1, y: 2, z: 3 }) // const A = 2910466848807138541n @@ -1383,7 +1383,7 @@ type BasisVectors = Static // type BasisVectors = { ### Options -Options can be passed via the last parameter +Options can be passed via the last parameter. ```typescript const T = Syntax(`number`, { minimum: 42 }) // const T = { @@ -1455,7 +1455,7 @@ const B = Value.Check(T, 'bar') // const B = false ## TypeCheck -TypeBox types target Json Schema Draft 7 and are compatible with any validator that supports this specification. TypeBox also provides a built in type checking compiler designed specifically for TypeBox types that offers high performance compilation and value checking. +TypeBox types target Json Schema Draft 7 and are compatible with any validator that supports this specification. TypeBox also provides a built-in type checking compiler designed specifically for TypeBox types that offers high performance compilation and value checking. The following sections detail using Ajv and the TypeBox compiler infrastructure. @@ -1574,7 +1574,7 @@ const C = TypeCompiler.Code(Type.String()) // const C = `return functi ## TypeMap -TypeBox offers an external package for bi-directional mapping between TypeBox, Valibot, and Zod type libraries. It also includes syntax parsing support for Valibot and Zod and supports the Standard Schema specification. For more details on TypeMap, refer to the project repository. +TypeBox offers an external package for bidirectional mapping between TypeBox, Valibot, and Zod type libraries. It also includes syntax parsing support for Valibot and Zod and supports the Standard Schema specification. For more details on TypeMap, refer to the project repository. [TypeMap Repository](https://github.com/sinclairzx81/typemap) @@ -1582,7 +1582,7 @@ TypeBox offers an external package for bi-directional mapping between TypeBox, V ### Usage -TypeMap needs to be installed seperately +TypeMap needs to be installed separately ```bash $ npm install @sinclair/typemap @@ -1731,11 +1731,11 @@ The following is a list of community packages that offer general tooling, extend | [json2typebox](https://github.com/hacxy/json2typebox) | Creating TypeBox code from Json Data | | [nominal-typebox](https://github.com/Coder-Spirit/nominal/tree/main/%40coderspirit/nominal-typebox) | Allows devs to integrate nominal types into TypeBox schemas | | [openapi-box](https://github.com/geut/openapi-box) | Generate TypeBox types from OpenApi IDL + Http client library | -| [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to typebox schema matching the database models | +| [prismabox](https://github.com/m1212e/prismabox) | Converts a prisma.schema to TypeBox schema matching the database models | | [schema2typebox](https://github.com/xddq/schema2typebox) | Creating TypeBox code from Json Schemas | | [sveltekit-superforms](https://github.com/ciscoheat/sveltekit-superforms) | A comprehensive SvelteKit form library for server and client validation | | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | -| [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with typebox from the CLI | +| [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with TypeBox from the CLI | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | From a67e380a73fd6185df40aa114277100f54f7d240 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 23 Feb 2025 14:27:57 +0900 Subject: [PATCH 348/369] Revision 0.34.28 (#1187) * Add Cast to Parse Pipeline * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/value/parse/parse.ts | 16 +++++++++------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index c0c59d339..c43d2060b 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.28](https://github.com/sinclairzx81/typebox/pull/1187) + - Add Cast to Configurable Parse Pipeline - [Revision 0.34.27](https://github.com/sinclairzx81/typebox/pull/1182) - [1178](https://github.com/sinclairzx81/typebox/issues/1178) Support Deep Referential Transform Inference Inside Modules - [Revision 0.34.26](https://github.com/sinclairzx81/typebox/pull/1181) diff --git a/package-lock.json b/package-lock.json index ac510958e..f8b32007b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.27", + "version": "0.34.28", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.27", + "version": "0.34.28", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index e59f3ed4a..418e5421f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.27", + "version": "0.34.28", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/parse/parse.ts b/src/value/parse/parse.ts index 9654f1d4e..429b0084c 100644 --- a/src/value/parse/parse.ts +++ b/src/value/parse/parse.ts @@ -31,10 +31,11 @@ import { TransformDecode, TransformEncode, HasTransform } from '../transform/ind import { TSchema } from '../../type/schema/index' import { StaticDecode } from '../../type/static/index' import { Assert } from '../assert/index' -import { Default } from '../default/index' -import { Convert } from '../convert/index' +import { Cast } from '../cast/index' import { Clean } from '../clean/index' import { Clone } from '../clone/index' +import { Convert } from '../convert/index' +import { Default } from '../default/index' // ------------------------------------------------------------------ // Guards @@ -53,18 +54,19 @@ export class ParseError extends TypeBoxError { // ------------------------------------------------------------------ // ParseRegistry // ------------------------------------------------------------------ -export type TParseOperation = 'Clone' | 'Clean' | 'Default' | 'Convert' | 'Assert' | 'Decode' | 'Encode' | ({} & string) +export type TParseOperation = 'Assert' | 'Cast' | 'Clean' | 'Clone' | 'Convert' | 'Decode' | 'Default' | 'Encode' | ({} & string) export type TParseFunction = (type: TSchema, references: TSchema[], value: unknown) => unknown // prettier-ignore export namespace ParseRegistry { const registry = new Map([ - ['Clone', (_type, _references, value: unknown) => Clone(value)], + ['Assert', (type, references, value: unknown) => { Assert(type, references, value); return value }], + ['Cast', (type, references, value: unknown) => Cast(type, references, value)], ['Clean', (type, references, value: unknown) => Clean(type, references, value)], - ['Default', (type, references, value: unknown) => Default(type, references, value)], + ['Clone', (_type, _references, value: unknown) => Clone(value)], ['Convert', (type, references, value: unknown) => Convert(type, references, value)], - ['Assert', (type, references, value: unknown) => { Assert(type, references, value); return value }], ['Decode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformDecode(type, references, value) : value)], + ['Default', (type, references, value: unknown) => Default(type, references, value)], ['Encode', (type, references, value: unknown) => (HasTransform(type, references) ? TransformEncode(type, references, value) : value)], ]) // Deletes an entry from the registry @@ -81,7 +83,7 @@ export namespace ParseRegistry { } } // ------------------------------------------------------------------ -// Default Parse Sequence +// Default Parse Pipeline // ------------------------------------------------------------------ // prettier-ignore export const ParseDefault = [ From 2f20aae454f39efab67606b71d825ecc1cd7c987 Mon Sep 17 00:00:00 2001 From: sinclair Date: Thu, 27 Feb 2025 14:45:52 +0900 Subject: [PATCH 349/369] Update Node Types to 22.13.5 --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8b32007b..b38185996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@arethetypeswrong/cli": "^0.13.2", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", - "@types/node": "^20.10.1", + "@types/node": "^22.13.5", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "mocha": "^11.1.0", @@ -210,12 +210,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", - "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "version": "22.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", + "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", "dev": true, "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/ajv": { @@ -1827,9 +1827,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "node_modules/unicode-emoji-modifier-base": { @@ -2184,12 +2184,12 @@ "dev": true }, "@types/node": { - "version": "20.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", - "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", + "version": "22.13.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", + "integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==", "dev": true, "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "ajv": { @@ -3232,9 +3232,9 @@ "dev": true }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, "unicode-emoji-modifier-base": { diff --git a/package.json b/package.json index 418e5421f..32ac42525 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@arethetypeswrong/cli": "^0.13.2", "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", - "@types/node": "^20.10.1", + "@types/node": "^22.13.5", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "mocha": "^11.1.0", From 75c53ea2cf24610d5e15aa40edf3ce4eab953737 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sat, 1 Mar 2025 21:22:15 +0900 Subject: [PATCH 350/369] TypeScript 5.8.2 --- hammer.mjs | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hammer.mjs b/hammer.mjs index cc8bd42f6..477cc7599 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -35,7 +35,7 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', '5.7.2', 'next', 'latest']) { + for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', '5.7.2', '5.7.3', 'next', 'latest']) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index b38185996..bf521a355 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.7.3" + "typescript": "^5.8.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1814,9 +1814,9 @@ } }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3226,9 +3226,9 @@ "dev": true }, "typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 32ac42525..621ca3f74 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,6 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.7.3" + "typescript": "^5.8.2" } } From f709937ac2da2d181859e785eb882632257eb202 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 10 Mar 2025 22:11:51 +0900 Subject: [PATCH 351/369] Revision 0.34.29 (#1197) * Scalable Delimit Parsing * Version * ChangeLog --- changelog/0.34.0.md | 2 + package-lock.json | 4 +- package.json | 2 +- src/syntax/static.ts | 140 ++++++++++++------------------------------- 4 files changed, 44 insertions(+), 104 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index c43d2060b..47f200f3f 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.29](https://github.com/sinclairzx81/typebox/pull/1197) + - Scalable Delimited Sequence Parsing - [Revision 0.34.28](https://github.com/sinclairzx81/typebox/pull/1187) - Add Cast to Configurable Parse Pipeline - [Revision 0.34.27](https://github.com/sinclairzx81/typebox/pull/1182) diff --git a/package-lock.json b/package-lock.json index bf521a355..a8478796d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.28", + "version": "0.34.29", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.28", + "version": "0.34.29", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 621ca3f74..839902c14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.28", + "version": "0.34.29", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 1e9712f52..50729c558 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -52,89 +52,41 @@ type Equals = '=' // ------------------------------------------------------------------ // Delimit -// -// This type is used to perform a partial breadth match for repeated -// elements in a sequence. It used to mitigate depth+1 traversal for -// each element which helps prevent reaching instantiation limits. The -// technique works by infering as wide as possible on the sequence -// enabling TS to hoist interior matches, but does come at slight -// inference performance cost. The current (infer=9) is configured -// to match 64 terminal tuple elements. -// -// for the given sequence -// -// [a, b, c, d, e, f, g] -// -// ------------------------------------------------------------------ -// -// without breadth mapping (infer=1, depth=6) -// -// [infer a, -// [infer b, -// [infer c, -// [infer d, -// [infer e, -// [infer f, -// [infer g, -// []]]]]]]] -// -// ------------------------------------------------------------------ -// -// with breadth mapping (infer=4, depth=2) -// -// [infer a, infer b, infer c, infer d, -// [infer e, infer f, infer g, -// []]] -// -// -// ------------------------------------------------------------------ -// prettier-ignore -interface DelimitTailMapping<_ = unknown> extends Static.IMapping { - output: ( - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer I, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, I, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer H, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, H, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer G, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, G, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer F, _, infer Rest extends unknown[]] ? [A, B, C, D, E, F, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer E, _, infer Rest extends unknown[]] ? [A, B, C, D, E, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer D, _, infer Rest extends unknown[]] ? [A, B, C, D, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer C, _, infer Rest extends unknown[]] ? [A, B, C, ...Rest] : - this['input'] extends [_, infer A, _, infer B, _, infer Rest extends unknown[]] ? [A, B, ...Rest] : - this['input'] extends [_, infer A, _, infer Rest extends unknown[]] ? [A, ...Rest] : - this['input'] extends [_, infer Rest extends unknown[]] ? [...Rest] : - this['input'] extends [_] ? [] : - [] - ) +// ------------------------------------------------------------------ +// prettier-ignore +type DelimitHeadReduce = ( + Elements extends [infer Left extends unknown, ...infer Right extends unknown[]] + ? Left extends [infer Element, infer _Delimiter] + ? DelimitHeadReduce + : DelimitHeadReduce + : Result +) +// prettier-ignore +interface DelimitHeadMapping extends Static.IMapping { + output: this['input'] extends unknown[] + ? DelimitHeadReduce + : [] } // prettier-ignore -type DelimitTail = Static.Union<[ - Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, T, _, T, _,Delimit]>, - Static.Tuple<[_, T, _, T, _, Delimit]>, - Static.Tuple<[_, T, _, Delimit]>, - Static.Tuple<[_, Delimit]>, - Static.Tuple<[_]>, - Static.Tuple<[]> -], DelimitTailMapping> +type DelimitHead = ( + Static.Array, DelimitHeadMapping> +) +// prettier-ignore +type DelimitTail = Static.Union<[ + Static.Tuple<[Element]>, + Static.Tuple<[]>, +]> // prettier-ignore interface DelimitMapping extends Static.IMapping { - output: ( - this['input'] extends [infer Element extends unknown, infer Rest extends unknown[]] - ? [Element, ...Rest] - : [] - ) + output: this['input'] extends [infer Left extends unknown[], infer Right extends unknown[]] + ? [...Left, ...Right] + : [] } // prettier-ignore -type Delimit = ( - Static.Union<[ - Static.Tuple<[Parser, DelimitTail]>, - Static.Tuple<[]> - ], DelimitMapping> -) +type Delimit = Static.Tuple<[ + DelimitHead, + DelimitTail +], DelimitMapping> // ------------------------------------------------------------------ // Dereference // ------------------------------------------------------------------ @@ -143,25 +95,6 @@ type Dereference = ( Ref extends keyof Context ? Context[Ref] : t.TRef ) // ------------------------------------------------------------------ -// GenericArgumentList -// ------------------------------------------------------------------ -// prettier-ignore -interface GenericArgumentListMapping extends Static.IMapping { - output: ( - this['input'] extends [infer Ident extends string, Comma, infer Rest extends unknown[]] ? [Ident, ...Rest] : - this['input'] extends [infer Ident extends string, Comma] ? [Ident] : - this['input'] extends [infer Ident extends string] ? [Ident] : - [] - ) -} -// prettier-ignore -type GenericArgumentList = Static.Union<[ - Static.Tuple<[Static.Ident, Static.Const, GenericArgumentList]>, - Static.Tuple<[Static.Ident, Static.Const]>, - Static.Tuple<[Static.Ident]>, - Static.Tuple<[]>, -], GenericArgumentListMapping> -// ------------------------------------------------------------------ // GenericArguments // ------------------------------------------------------------------ // prettier-ignore @@ -181,7 +114,7 @@ interface GenericArgumentsMapping extends Static.IMapping { // prettier-ignore type GenericArguments = Static.Tuple<[ Static.Const, - GenericArgumentList, + Delimit>, Static.Const, ], GenericArgumentsMapping> @@ -477,8 +410,8 @@ type PropertyDelimiter = Static.Union<[ // prettier-ignore type PropertiesReduce = ( PropertiesArray extends [infer Left extends t.TProperties, ...infer Right extends t.TProperties[]] - ? PropertiesReduce> - : Result + ? PropertiesReduce + : t.Evaluate ) // prettier-ignore interface PropertiesMapping extends Static.IMapping { @@ -499,12 +432,17 @@ type Object = Static.Tuple<[ Static.Const, Properties, Static.Const ], ObjectMapping> // ------------------------------------------------------------------ -// Tuple +// Elements // ------------------------------------------------------------------ type Elements = Delimit> +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ // prettier-ignore interface TupleMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer Elements extends t.TSchema[], unknown] ? t.TTuple : never + output: this['input'] extends [unknown, infer Elements extends t.TSchema[], unknown] + ? t.TTuple + : never } // prettier-ignore type Tuple = Static.Tuple<[ From 0aac4b83ed5e695bf62224bae85e36e364aedf7d Mon Sep 17 00:00:00 2001 From: sinclair Date: Tue, 11 Mar 2025 15:25:01 +0900 Subject: [PATCH 352/369] Revision 0.34.30 (#1198) --- changelog/0.34.0.md | 6 +- example/index.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- src/syntax/runtime.ts | 353 +++++++++++++++++++++++------------------- src/syntax/static.ts | 231 ++++++++++++++++----------- 6 files changed, 343 insertions(+), 255 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 47f200f3f..7b81c3920 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -2,9 +2,13 @@ --- + + ### Revision Updates +- [Revision 0.34.30](https://github.com/sinclairzx81/typebox/pull/1198) + - Additional Syntax Parsing Optimizations - [Revision 0.34.29](https://github.com/sinclairzx81/typebox/pull/1197) - - Scalable Delimited Sequence Parsing + - Syntax Parsing Optimizations - [Revision 0.34.28](https://github.com/sinclairzx81/typebox/pull/1187) - Add Cast to Configurable Parse Pipeline - [Revision 0.34.27](https://github.com/sinclairzx81/typebox/pull/1182) diff --git a/example/index.ts b/example/index.ts index ff155a02a..58b1d1370 100644 --- a/example/index.ts +++ b/example/index.ts @@ -19,7 +19,7 @@ type T = Static console.log(T) // ----------------------------------------------------------- -// Parse: Type +// Syntax: Type // ----------------------------------------------------------- const S = Syntax({ T }, `{ x: T, y: T, z: T }`) diff --git a/package-lock.json b/package-lock.json index a8478796d..abce9d2f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.29", + "version": "0.34.30", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.29", + "version": "0.34.30", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 839902c14..ca5b738b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.29", + "version": "0.34.30", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts index 428ae8e7a..92703afcc 100644 --- a/src/syntax/runtime.ts +++ b/src/syntax/runtime.ts @@ -59,31 +59,47 @@ function DestructureRight(values: T[]): [T[], T | undefined] { ? [values.slice(0, values.length - 1), values[values.length - 1]] : [values, undefined] } + +// ------------------------------------------------------------------ +// Delimit +// ------------------------------------------------------------------ +// prettier-ignore +const DelimitHeadMapping = (results: unknown[]) => results.reduce((result: unknown[], value) => { + const [element, _delimiter] = value as [unknown, unknown] + return [...result, element] +}, [] as unknown[]) +// prettier-ignore +const DelimitHead = (element: Element, delimiter: Delimiter) => ( + Runtime.Array(Runtime.Tuple([element, delimiter]), DelimitHeadMapping) +) +// prettier-ignore +const DelimitTail = (element: Element) => Runtime.Union([ + Runtime.Tuple([element]), + Runtime.Tuple([]), +]) +// prettier-ignore +const DelimitMapping = (results: [unknown[], unknown[]]) => { + return [...results[0], ...results[1]] +} +// prettier-ignore +const Delimit = (element: Element, delimiter: Delimiter) => Runtime.Tuple([ + DelimitHead(element, delimiter), + DelimitTail(element), +], DelimitMapping) + // ------------------------------------------------------------------ // Dereference // ------------------------------------------------------------------ const Dereference = (context: t.TProperties, key: string): t.TSchema => { return key in context ? context[key] : t.Ref(key) } + // ------------------------------------------------------------------ -// GenericArgumentList +// GenericArgumentsList // ------------------------------------------------------------------ // prettier-ignore -const GenericArgumentListMapping = (results: unknown[]) => { - return ( - results.length === 3 ? [results[0], ...results[2] as unknown[]] : - results.length === 2 ? [results[0]] : - results.length === 1 ? [results[0]] : - [] - ) -} -// prettier-ignore -const GenericArgumentList = Runtime.Union([ - Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma), Runtime.Ref('GenericArgumentList')]), - Runtime.Tuple([Runtime.Ident(), Runtime.Const(Comma)]), - Runtime.Tuple([Runtime.Ident()]), - Runtime.Tuple([]), -], (results) => GenericArgumentListMapping(results)) +const GenericArgumentsList = Delimit(Runtime.Ident(), Runtime.Const(Comma)) + // ------------------------------------------------------------------ // GenericArguments // ------------------------------------------------------------------ @@ -102,9 +118,10 @@ const GenericArgumentsMapping = (results: unknown[], context: t.TProperties) => // prettier-ignore const GenericArguments = Runtime.Tuple([ Runtime.Const(LAngle), - Runtime.Ref('GenericArgumentList'), + Runtime.Ref('GenericArgumentsList'), Runtime.Const(RAngle), ], (results, context) => GenericArgumentsMapping(results, context)) + // ------------------------------------------------------------------ // GenericReference // ------------------------------------------------------------------ @@ -113,13 +130,15 @@ function GenericReferenceMapping(results: unknown[], context: t.TProperties) { const args = results[2] as t.TSchema[] return t.Instantiate(type, args) } +const GenericReferenceParameters = Delimit(Runtime.Ref('Type'), Runtime.Const(Comma)) // prettier-ignore const GenericReference = Runtime.Tuple([ Runtime.Ident(), Runtime.Const(LAngle), - Runtime.Ref('Elements'), + Runtime.Ref('GenericReferenceParameters'), Runtime.Const(RAngle) ], (results, context) => GenericReferenceMapping(results, context)) + // ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ @@ -129,6 +148,7 @@ function ReferenceMapping(result: string, context: t.TProperties) { } // prettier-ignore const Reference = Runtime.Ident((result, context) => ReferenceMapping(result, context)) + // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ @@ -138,24 +158,26 @@ const Literal = Runtime.Union([ Runtime.Number(value => t.Literal(parseFloat(value))), Runtime.String([SingleQuote, DoubleQuote, Tilde], value => t.Literal(value)) ]) + // ------------------------------------------------------------------ // Keyword // ------------------------------------------------------------------ // prettier-ignore const Keyword = Runtime.Union([ - Runtime.Const('any', Runtime.As(t.Any())), - Runtime.Const('bigint', Runtime.As(t.BigInt())), + Runtime.Const('string', Runtime.As(t.String())), + Runtime.Const('number', Runtime.As(t.Number())), Runtime.Const('boolean', Runtime.As(t.Boolean())), + Runtime.Const('undefined', Runtime.As(t.Undefined())), + Runtime.Const('null', Runtime.As(t.Null())), Runtime.Const('integer', Runtime.As(t.Integer())), + Runtime.Const('bigint', Runtime.As(t.BigInt())), + Runtime.Const('unknown', Runtime.As(t.Unknown())), + Runtime.Const('any', Runtime.As(t.Any())), Runtime.Const('never', Runtime.As(t.Never())), - Runtime.Const('null', Runtime.As(t.Null())), - Runtime.Const('number', Runtime.As(t.Number())), - Runtime.Const('string', Runtime.As(t.String())), Runtime.Const('symbol', Runtime.As(t.Symbol())), - Runtime.Const('undefined', Runtime.As(t.Undefined())), - Runtime.Const('unknown', Runtime.As(t.Unknown())), Runtime.Const('void', Runtime.As(t.Void())), ]) + // ------------------------------------------------------------------ // KeyOf // ------------------------------------------------------------------ @@ -167,21 +189,23 @@ const KeyOfMapping = (values: unknown[]) => ( const KeyOf = Runtime.Union([ Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([]) ], KeyOfMapping) + // ------------------------------------------------------------------ // IndexArray // ------------------------------------------------------------------ // prettier-ignore -const IndexArrayMapping = (values: unknown[]) => ( - values.length === 4 ? [[values[1]], ...values[3] as unknown[]] : - values.length === 3 ? [[], ...values[2] as unknown[]] : - [] -) +const IndexArrayMapping = (results: ([unknown, unknown, unknown] | [unknown, unknown])[]) => { + return results.reduce((result: unknown[], current) => { + return current.length === 3 + ? [...result, [current[1]]] + : [...result, []] + }, [] as unknown[]) +} // prettier-ignore -const IndexArray = Runtime.Union([ - Runtime.Tuple([Runtime.Const(LBracket), Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), - Runtime.Tuple([Runtime.Const(LBracket), Runtime.Const(RBracket), Runtime.Ref('IndexArray')]), - Runtime.Tuple([]) -], value => IndexArrayMapping(value)) +const IndexArray = Runtime.Array(Runtime.Union([ + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Ref('Type'), Runtime.Const(RBracket)]), + Runtime.Tuple([Runtime.Const(LBracket), Runtime.Const(RBracket)]), +]), IndexArrayMapping) // ------------------------------------------------------------------ // Extends @@ -197,54 +221,51 @@ const Extends = Runtime.Union([ Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]), Runtime.Tuple([]) ], ExtendsMapping) + // ------------------------------------------------------------------ // Base // ------------------------------------------------------------------ // prettier-ignore -const BaseMapping = (values: unknown[]) => { - return values.length === 3 ? values[1] : values[0] +const BaseMapping = (value: unknown) => { + return t.ValueGuard.IsArray(value) && value.length === 3 + ? value[1] + : value } // prettier-ignore const Base = Runtime.Union([ - Runtime.Tuple([ - Runtime.Const(LParen), - Runtime.Ref('Type'), - Runtime.Const(RParen) - ]), - Runtime.Tuple([Runtime.Union([ - Runtime.Ref('Literal'), - Runtime.Ref('Keyword'), - Runtime.Ref('Object'), - Runtime.Ref('Tuple'), - Runtime.Ref('Constructor'), - Runtime.Ref('Function'), - Runtime.Ref('Mapped'), - Runtime.Ref('AsyncIterator'), - Runtime.Ref('Iterator'), - Runtime.Ref('ConstructorParameters'), - Runtime.Ref('FunctionParameters'), - Runtime.Ref('InstanceType'), - Runtime.Ref('ReturnType'), - Runtime.Ref('Argument'), - Runtime.Ref('Awaited'), - Runtime.Ref('Array'), - Runtime.Ref('Record'), - Runtime.Ref('Promise'), - Runtime.Ref('Partial'), - Runtime.Ref('Required'), - Runtime.Ref('Pick'), - Runtime.Ref('Omit'), - Runtime.Ref('Exclude'), - Runtime.Ref('Extract'), - Runtime.Ref('Uppercase'), - Runtime.Ref('Lowercase'), - Runtime.Ref('Capitalize'), - Runtime.Ref('Uncapitalize'), - Runtime.Ref('Date'), - Runtime.Ref('Uint8Array'), - Runtime.Ref('GenericReference'), - Runtime.Ref('Reference') - ])]) + Runtime.Tuple([Runtime.Const(LParen), Runtime.Ref('Type'), Runtime.Const(RParen)]), + Runtime.Ref('Keyword'), + Runtime.Ref('Object'), + Runtime.Ref('Tuple'), + Runtime.Ref('Literal'), + Runtime.Ref('Constructor'), + Runtime.Ref('Function'), + Runtime.Ref('Mapped'), + Runtime.Ref('AsyncIterator'), + Runtime.Ref('Iterator'), + Runtime.Ref('ConstructorParameters'), + Runtime.Ref('FunctionParameters'), + Runtime.Ref('InstanceType'), + Runtime.Ref('ReturnType'), + Runtime.Ref('Argument'), + Runtime.Ref('Awaited'), + Runtime.Ref('Array'), + Runtime.Ref('Record'), + Runtime.Ref('Promise'), + Runtime.Ref('Partial'), + Runtime.Ref('Required'), + Runtime.Ref('Pick'), + Runtime.Ref('Omit'), + Runtime.Ref('Exclude'), + Runtime.Ref('Extract'), + Runtime.Ref('Uppercase'), + Runtime.Ref('Lowercase'), + Runtime.Ref('Capitalize'), + Runtime.Ref('Uncapitalize'), + Runtime.Ref('Date'), + Runtime.Ref('Uint8Array'), + Runtime.Ref('GenericReference'), + Runtime.Ref('Reference') ], BaseMapping) // ------------------------------------------------------------------ @@ -280,7 +301,8 @@ const Factor = Runtime.Tuple([ Runtime.Ref('Base'), Runtime.Ref('IndexArray'), Runtime.Ref('Extends') -], values => FactorMapping(...values)) +], results => FactorMapping(...results)) + // ------------------------------------------------------------------ // Expr // ------------------------------------------------------------------ @@ -312,7 +334,7 @@ const ExprTermTail = Runtime.Union([ // prettier-ignore const ExprTerm = Runtime.Tuple([ Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') -], value => ExprBinaryMapping(...value)) +], results => ExprBinaryMapping(...results)) // prettier-ignore const ExprTail = Runtime.Union([ Runtime.Tuple([Runtime.Const('|'), Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail')]), @@ -321,7 +343,7 @@ const ExprTail = Runtime.Union([ // prettier-ignore const Expr = Runtime.Tuple([ Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') -], value => ExprBinaryMapping(...value)) +], results => ExprBinaryMapping(...results)) // ------------------------------------------------------------------ // Type @@ -331,8 +353,9 @@ const Type = Runtime.Union([ Runtime.Context(Runtime.Ref('GenericArguments'), Runtime.Ref('Expr')), Runtime.Ref('Expr') ]) + // ------------------------------------------------------------------ -// Properties +// Property // ------------------------------------------------------------------ const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])]) const Readonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], (value) => value.length > 0) @@ -353,7 +376,11 @@ const Property = Runtime.Tuple([ Runtime.Ref('Optional'), Runtime.Const(Colon), Runtime.Ref('Type'), -], value => PropertyMapping(...value)) +], results => PropertyMapping(...results)) + +// ------------------------------------------------------------------ +// PropertyDelimiter +// ------------------------------------------------------------------ // prettier-ignore const PropertyDelimiter = Runtime.Union([ Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), @@ -362,51 +389,43 @@ const PropertyDelimiter = Runtime.Union([ Runtime.Tuple([Runtime.Const(SemiColon)]), Runtime.Tuple([Runtime.Const(Newline)]), ]) -// prettier-ignore -const Properties = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]), - Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]), - Runtime.Tuple([Runtime.Ref('Property')]), - Runtime.Tuple([]) -], values => ( - values.length === 3 ? { ...values[0], ...values[2] } : - values.length === 2 ? values[0] : - values.length === 1 ? values[0] : - {} -)) + +// ------------------------------------------------------------------ +// PropertyList +// ------------------------------------------------------------------ +const PropertyList = Delimit(Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')) + // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ // prettier-ignore -const ObjectMapping = (_0: typeof LBrace, Properties: t.TProperties, _2: typeof RBrace) => t.Object(Properties) +const ObjectMapping = (results: unknown[]) => { + const propertyList = results[1] as t.TProperties[] + return t.Object(propertyList.reduce((result, property) => { + return { ...result, ...property } + }, {} as t.TProperties)) +} // prettier-ignore const _Object = Runtime.Tuple([ Runtime.Const(LBrace), - Runtime.Ref('Properties'), + Runtime.Ref('PropertyList'), Runtime.Const(RBrace) -], values => ObjectMapping(...values)) +], ObjectMapping) + +// ------------------------------------------------------------------ +// ElementList +// ------------------------------------------------------------------ +const ElementList = Delimit(Runtime.Ref('Type'), Runtime.Const(Comma)) // ------------------------------------------------------------------ // Tuple // ------------------------------------------------------------------ // prettier-ignore -const Elements = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma), Runtime.Ref('Elements')]), - Runtime.Tuple([Runtime.Ref('Type'), Runtime.Const(Comma)]), - Runtime.Tuple([Runtime.Ref('Type')]), - Runtime.Tuple([]), -], value => ( - value.length === 3 ? [value[0], ...value[2]] : - value.length === 2 ? [value[0]] : - value.length === 1 ? [value[0]] : - [] -)) -// prettier-ignore const Tuple = Runtime.Tuple([ Runtime.Const(LBracket), - Runtime.Ref('Elements'), + Runtime.Ref('ElementList'), Runtime.Const(RBracket) -], value => t.Tuple(value[1])) +], results => t.Tuple(results[1])) // ------------------------------------------------------------------ // Parameters @@ -414,19 +433,13 @@ const Tuple = Runtime.Tuple([ // prettier-ignore const Parameter = Runtime.Tuple([ Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') -], value => value[2]) -// prettier-ignore -const Parameters = Runtime.Union([ - Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma), Runtime.Ref('Parameters')]), - Runtime.Tuple([Runtime.Ref('Parameter'), Runtime.Const(Comma)]), - Runtime.Tuple([Runtime.Ref('Parameter')]), - Runtime.Tuple([]), -], value => ( - value.length === 3 ? [value[0], ...value[2]] : - value.length === 2 ? [value[0]] : - value.length === 1 ? [value[0]] : - [] -)) +], results => results[2]) + +// ------------------------------------------------------------------ +// ParameterList +// ------------------------------------------------------------------ +const ParameterList = Delimit(Runtime.Ref('Parameter'), Runtime.Const(Comma)) + // ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ @@ -434,27 +447,29 @@ const Parameters = Runtime.Union([ const Constructor = Runtime.Tuple([ Runtime.Const('new'), Runtime.Const(LParen), - Runtime.Ref('Parameters'), + Runtime.Ref('ParameterList'), Runtime.Const(RParen), Runtime.Const('=>'), Runtime.Ref('Type') -], value => t.Constructor(value[2], value[5])) +], results => t.Constructor(results[2], results[5])) + // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ // prettier-ignore const Function = Runtime.Tuple([ Runtime.Const(LParen), - Runtime.Ref('Parameters'), + Runtime.Ref('ParameterList'), Runtime.Const(RParen), Runtime.Const('=>'), Runtime.Ref('Type') -], value => t.Function(value[1], value[4])) +], results => t.Function(results[1], results[4])) + // ------------------------------------------------------------------ // Mapped (requires deferred types) // ------------------------------------------------------------------ // prettier-ignore -const MappedMapping = (values: unknown[]) => { +const MappedMapping = (results: unknown[]) => { return t.Literal('Mapped types not supported') } // prettier-ignore @@ -463,12 +478,13 @@ const Mapped = Runtime.Tuple([ Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), - Runtime.Ref('Type'), + Runtime.Ref('Type'), Runtime.Const(RBrace) ], MappedMapping) + // ------------------------------------------------------------------ // AsyncIterator // ------------------------------------------------------------------ @@ -478,7 +494,8 @@ const AsyncIterator = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.AsyncIterator(value[2])) +], results => t.AsyncIterator(results[2])) + // ------------------------------------------------------------------ // Iterator // ------------------------------------------------------------------ @@ -488,7 +505,8 @@ const Iterator = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Iterator(value[2])) +], results => t.Iterator(results[2])) + // ------------------------------------------------------------------ // ConstructorParameters // ------------------------------------------------------------------ @@ -498,7 +516,8 @@ const ConstructorParameters = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.ConstructorParameters(value[2])) +], results => t.ConstructorParameters(results[2])) + // ------------------------------------------------------------------ // Parameters // ------------------------------------------------------------------ @@ -508,7 +527,8 @@ const FunctionParameters = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Parameters(value[2])) +], results => t.Parameters(results[2])) + // ------------------------------------------------------------------ // InstanceType // ------------------------------------------------------------------ @@ -518,7 +538,8 @@ const InstanceType = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.InstanceType(value[2])) +], results => t.InstanceType(results[2])) + // ------------------------------------------------------------------ // ReturnType // ------------------------------------------------------------------ @@ -528,7 +549,8 @@ const ReturnType = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.ReturnType(value[2])) +], results => t.ReturnType(results[2])) + // ------------------------------------------------------------------ // Argument // ------------------------------------------------------------------ @@ -543,6 +565,7 @@ const Argument = Runtime.Tuple([ ? t.Argument(Math.trunc(results[2].const)) : t.Never() }) + // ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ @@ -552,7 +575,8 @@ const Awaited = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Awaited(value[2])) +], results => t.Awaited(results[2])) + // ------------------------------------------------------------------ // Array // ------------------------------------------------------------------ @@ -562,7 +586,8 @@ const Array = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Array(value[2])) +], results => t.Array(results[2])) + // ------------------------------------------------------------------ // Record // ------------------------------------------------------------------ @@ -574,7 +599,8 @@ const Record = Runtime.Tuple([ Runtime.Const(Comma), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Record(value[2], value[4])) +], results => t.Record(results[2], results[4])) + // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ @@ -584,7 +610,8 @@ const Promise = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Promise(value[2])) +], results => t.Promise(results[2])) + // ------------------------------------------------------------------ // Partial // ------------------------------------------------------------------ @@ -594,7 +621,8 @@ const Partial = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Partial(value[2])) +], results => t.Partial(results[2])) + // ------------------------------------------------------------------ // Required // ------------------------------------------------------------------ @@ -604,7 +632,8 @@ const Required = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Required(value[2])) +], results => t.Required(results[2])) + // ------------------------------------------------------------------ // Pick // ------------------------------------------------------------------ @@ -616,7 +645,8 @@ const Pick = Runtime.Tuple([ Runtime.Const(Comma), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Pick(value[2], value[4])) +], results => t.Pick(results[2], results[4])) + // ------------------------------------------------------------------ // Omit // ------------------------------------------------------------------ @@ -628,7 +658,8 @@ const Omit = Runtime.Tuple([ Runtime.Const(Comma), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Omit(value[2], value[4])) +], results => t.Omit(results[2], results[4])) + // ------------------------------------------------------------------ // Exclude // ------------------------------------------------------------------ @@ -640,7 +671,8 @@ const Exclude = Runtime.Tuple([ Runtime.Const(Comma), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Exclude(value[2], value[4])) +], results => t.Exclude(results[2], results[4])) + // ------------------------------------------------------------------ // Extract // ------------------------------------------------------------------ @@ -652,7 +684,8 @@ const Extract = Runtime.Tuple([ Runtime.Const(Comma), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Extract(value[2], value[4])) +], results => t.Extract(results[2], results[4])) + // ------------------------------------------------------------------ // Uppercase // ------------------------------------------------------------------ @@ -662,7 +695,8 @@ const Uppercase = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Uppercase(value[2])) +], results => t.Uppercase(results[2])) + // ------------------------------------------------------------------ // Lowercase // ------------------------------------------------------------------ @@ -672,7 +706,8 @@ const Lowercase = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Lowercase(value[2])) +], results => t.Lowercase(results[2])) + // ------------------------------------------------------------------ // Capitalize // ------------------------------------------------------------------ @@ -682,7 +717,8 @@ const Capitalize = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Capitalize(value[2])) +], results => t.Capitalize(results[2])) + // ------------------------------------------------------------------ // Uncapitalize // ------------------------------------------------------------------ @@ -692,11 +728,13 @@ const Uncapitalize = Runtime.Tuple([ Runtime.Const(LAngle), Runtime.Ref('Type'), Runtime.Const(RAngle), -], value => t.Uncapitalize(value[2])) +], results => t.Uncapitalize(results[2])) + // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ const Date = Runtime.Const('Date', Runtime.As(t.Date())) + // ------------------------------------------------------------------ // Uint8Array // ------------------------------------------------------------------ @@ -707,14 +745,8 @@ const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array())) // ------------------------------------------------------------------ // prettier-ignore export const Module = new Runtime.Module({ - // ---------------------------------------------------------------- - // Generics - // ---------------------------------------------------------------- - GenericArgumentList, + GenericArgumentsList, GenericArguments, - // ---------------------------------------------------------------- - // Type - // ---------------------------------------------------------------- Literal, Keyword, KeyOf, @@ -732,13 +764,13 @@ export const Module = new Runtime.Module({ Optional, Property, PropertyDelimiter, - Properties, + PropertyList, Object: _Object, - Elements, + ElementList, Tuple, Parameter, + ParameterList, Function, - Parameters, Constructor, Mapped, AsyncIterator, @@ -764,6 +796,7 @@ export const Module = new Runtime.Module({ Uncapitalize, Date, Uint8Array, + GenericReferenceParameters, GenericReference, Reference, }) diff --git a/src/syntax/static.ts b/src/syntax/static.ts index 50729c558..609fc3d7b 100644 --- a/src/syntax/static.ts +++ b/src/syntax/static.ts @@ -87,6 +87,7 @@ type Delimit = DelimitHead, DelimitTail ], DelimitMapping> + // ------------------------------------------------------------------ // Dereference // ------------------------------------------------------------------ @@ -94,6 +95,7 @@ type Delimit = type Dereference = ( Ref extends keyof Context ? Context[Ref] : t.TRef ) + // ------------------------------------------------------------------ // GenericArguments // ------------------------------------------------------------------ @@ -111,10 +113,11 @@ interface GenericArgumentsMapping extends Static.IMapping { : never : never } +type GenericArgumentsList = Delimit> // prettier-ignore type GenericArguments = Static.Tuple<[ Static.Const, - Delimit>, + GenericArgumentsList, Static.Const, ], GenericArgumentsMapping> @@ -129,13 +132,15 @@ interface GenericReferenceMapping extends Static.IMapping { : never : never } +type GenericReferenceParameters = Delimit> // prettier-ignore type GenericReference = Static.Tuple<[ Static.Ident, Static.Const, - Elements, + GenericReferenceParameters, Static.Const, ], GenericReferenceMapping> + // ------------------------------------------------------------------ // Reference // ------------------------------------------------------------------ @@ -148,20 +153,21 @@ interface ReferenceMapping extends Static.IMapping { : never } type Reference = Static.Ident + // ------------------------------------------------------------------ // Literal // ------------------------------------------------------------------ // prettier-ignore interface LiteralBooleanMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends boolean}` ? t.TLiteral : never + output: this['input'] extends `${infer Value extends boolean}` ? t.TLiteral : never } // prettier-ignore interface LiteralNumberMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends number}` ? t.TLiteral : never + output: this['input'] extends `${infer Value extends number}` ? t.TLiteral : never } // prettier-ignore interface LiteralStringMapping extends Static.IMapping { - output: this['input'] extends `${infer S extends string}` ? t.TLiteral : never + output: this['input'] extends `${infer Value extends string}` ? t.TLiteral : never } // prettier-ignore type Literal = Static.Union<[ @@ -169,24 +175,26 @@ type Literal = Static.Union<[ Static.Number, Static.String<[DoubleQuote, SingleQuote, Tilde], LiteralStringMapping>, ]> + // ------------------------------------------------------------------ // Keyword // ------------------------------------------------------------------ // prettier-ignore type Keyword = Static.Union<[ - Static.Const<'any', Static.As>, - Static.Const<'bigint', Static.As>, + Static.Const<'string', Static.As>, + Static.Const<'number', Static.As>, Static.Const<'boolean', Static.As>, + Static.Const<'undefined', Static.As>, + Static.Const<'null', Static.As>, Static.Const<'integer', Static.As>, + Static.Const<'bigint', Static.As>, + Static.Const<'unknown', Static.As>, + Static.Const<'any', Static.As>, Static.Const<'never', Static.As>, - Static.Const<'null', Static.As>, - Static.Const<'number', Static.As>, - Static.Const<'string', Static.As>, Static.Const<'symbol', Static.As>, - Static.Const<'undefined', Static.As>, - Static.Const<'unknown', Static.As>, Static.Const<'void', Static.As>, ]> + // ------------------------------------------------------------------ // KeyOf // ------------------------------------------------------------------ @@ -199,23 +207,29 @@ type KeyOf = Static.Union<[ Static.Tuple<[Static.Const<'keyof'>]>, Static.Tuple<[]> ], KeyOfMapping> + // ------------------------------------------------------------------ // IndexArray // ------------------------------------------------------------------ // prettier-ignore +type IndexArrayReduce = ( + Values extends [infer Left extends unknown, ...infer Right extends unknown[]] + ? Left extends [LBracket, infer Type extends t.TSchema, RBracket] + ? IndexArrayReduce + : IndexArrayReduce + : Result +) +// prettier-ignore interface IndexArrayMapping extends Static.IMapping { - output: ( - this['input'] extends [LBracket, infer Type extends t.TSchema, RBracket, infer Rest extends unknown[]] ? [[Type], ...Rest] : - this['input'] extends [LBracket, RBracket, infer Rest extends unknown[]] ? [[], ...Rest] : - [] - ) + output: this['input'] extends unknown[] + ? IndexArrayReduce + : [] } // prettier-ignore -type IndexArray = Static.Union<[ - Static.Tuple<[Static.Const, Type, Static.Const, IndexArray]>, - Static.Tuple<[Static.Const, Static.Const, IndexArray]>, - Static.Tuple<[]> -], IndexArrayMapping> +type IndexArray = Static.Array, Type, Static.Const,]>, + Static.Tuple<[Static.Const, Static.Const]>, +]>, IndexArrayMapping> // ------------------------------------------------------------------ // Extends @@ -231,6 +245,7 @@ type Extends = Static.Union<[ Static.Tuple<[Static.Const<'extends'>, Type, Static.Const, Type, Static.Const, Type]>, Static.Tuple<[]> ], ExtendsMapping> + // ------------------------------------------------------------------ // Base // ------------------------------------------------------------------ @@ -238,52 +253,47 @@ type Extends = Static.Union<[ interface BaseMapping extends Static.IMapping { output: ( this['input'] extends [LParen, infer Type extends t.TSchema, RParen] ? Type : - this['input'] extends [infer Type extends t.TSchema] ? Type : + this['input'] extends infer Type extends t.TSchema ? Type : never ) } // prettier-ignore type Base = Static.Union<[ - Static.Tuple<[ - Static.Const, - Type, - Static.Const - ]>, - Static.Tuple<[Static.Union<[ - Literal, - Keyword, - Object, - Tuple, - Function, - Constructor, - Mapped, - AsyncIterator, - Iterator, - ConstructorParameters, - FunctionParameters, - Argument, - InstanceType, - ReturnType, - Awaited, - Array, - Record, - Promise, - Partial, - Required, - Pick, - Omit, - Exclude, - Extract, - Lowercase, - Uppercase, - Capitalize, - Uncapitalize, - Date, - Uint8Array, - GenericReference, - Reference - ]>]> + Static.Tuple<[Static.Const, Type, Static.Const]>, + Keyword, + Object, + Tuple, + Literal, + Function, + Constructor, + Mapped, + AsyncIterator, + Iterator, + ConstructorParameters, + FunctionParameters, + Argument, + InstanceType, + ReturnType, + Awaited, + Array, + Record, + Promise, + Partial, + Required, + Pick, + Omit, + Exclude, + Extract, + Lowercase, + Uppercase, + Capitalize, + Uncapitalize, + Date, + Uint8Array, + GenericReference, + Reference ], BaseMapping> + // ------------------------------------------------------------------ // Factor // ------------------------------------------------------------------ @@ -313,6 +323,7 @@ interface FactorMapping extends Static.IMapping { type Factor = Static.Tuple<[ KeyOf, Base, IndexArray, Extends ], FactorMapping> + // ------------------------------------------------------------------ // Expr // ------------------------------------------------------------------ @@ -359,6 +370,7 @@ type ExprTail = Static.Union<[ type Expr = Static.Tuple<[ ExprTerm, ExprTail ], ExprBinaryMapping> + // ------------------------------------------------------------------ // Type // ------------------------------------------------------------------ @@ -367,8 +379,9 @@ export type Type = Static.Union<[ Static.Context, Expr ]> + // ------------------------------------------------------------------ -// Properties +// Property // ------------------------------------------------------------------ // prettier-ignore interface PropertyKeyStringMapping extends Static.IMapping { @@ -399,6 +412,10 @@ interface PropertyMapping extends Static.IMapping { } : never } type Property = Static.Tuple<[Readonly, PropertyKey, Optional, Static.Const, Type], PropertyMapping> + +// ------------------------------------------------------------------ +// PropertyDelimiter +// ------------------------------------------------------------------ // prettier-ignore type PropertyDelimiter = Static.Union<[ Static.Tuple<[Static.Const, Static.Const]>, @@ -407,49 +424,55 @@ type PropertyDelimiter = Static.Union<[ Static.Tuple<[Static.Const]>, Static.Tuple<[Static.Const]>, ]> -// prettier-ignore -type PropertiesReduce = ( - PropertiesArray extends [infer Left extends t.TProperties, ...infer Right extends t.TProperties[]] - ? PropertiesReduce - : t.Evaluate -) -// prettier-ignore -interface PropertiesMapping extends Static.IMapping { - output: this['input'] extends t.TProperties[] ? PropertiesReduce : never -} -type Properties = Static.Union<[Delimit], PropertiesMapping> + +// ------------------------------------------------------------------ +// PropertyList +// ------------------------------------------------------------------ +type PropertyList = Delimit + // ------------------------------------------------------------------ // Object // ------------------------------------------------------------------ // prettier-ignore +type ObjectReduce = ( + PropertiesList extends [infer Left extends t.TProperties, ...infer Right extends t.TProperties[]] + ? ObjectReduce + : t.Evaluate +) +// prettier-ignore interface ObjectMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer Properties extends t.TProperties, unknown] - ? t.TObject + output: this['input'] extends [LBrace, infer PropertyList extends t.TProperties[], RBrace] + ? t.TObject> : never } // prettier-ignore type Object = Static.Tuple<[ - Static.Const, Properties, Static.Const + Static.Const, + PropertyList, + Static.Const ], ObjectMapping> + // ------------------------------------------------------------------ -// Elements +// ElementList // ------------------------------------------------------------------ -type Elements = Delimit> +type ElementList = Delimit> + // ------------------------------------------------------------------ // Tuple // ------------------------------------------------------------------ // prettier-ignore interface TupleMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer Elements extends t.TSchema[], unknown] - ? t.TTuple + output: this['input'] extends [unknown, infer ElementList extends t.TSchema[], unknown] + ? t.TTuple : never } // prettier-ignore type Tuple = Static.Tuple<[ - Static.Const, Elements, Static.Const + Static.Const, ElementList, Static.Const ], TupleMapping> + // ------------------------------------------------------------------ -// Parameters +// Parameter // ------------------------------------------------------------------ interface ParameterMapping extends Static.IMapping { output: this['input'] extends [string, Colon, infer Type extends t.TSchema] ? Type : never @@ -459,33 +482,39 @@ type Parameter = Static.Tuple<[ Static.Ident, Static.Const, Type ], ParameterMapping> -type Parameters = Delimit> +// ------------------------------------------------------------------ +// ParameterList +// ------------------------------------------------------------------ +type ParameterList = Delimit> + // ------------------------------------------------------------------ // Function // ------------------------------------------------------------------ // prettier-ignore interface FunctionMapping extends Static.IMapping { - output: this['input'] extends [LParen, infer Parameters extends t.TSchema[], RParen, '=>', infer ReturnType extends t.TSchema] - ? t.TFunction + output: this['input'] extends [LParen, infer ParameterList extends t.TSchema[], RParen, '=>', infer ReturnType extends t.TSchema] + ? t.TFunction : never } // prettier-ignore type Function = Static.Tuple<[ - Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type + Static.Const, ParameterList, Static.Const, Static.Const<'=>'>, Type ], FunctionMapping> + // ------------------------------------------------------------------ // Constructor // ------------------------------------------------------------------ // prettier-ignore interface ConstructorMapping extends Static.IMapping { - output: this['input'] extends ['new', LParen, infer Parameters extends t.TSchema[], RParen, '=>', infer InstanceType extends t.TSchema] - ? t.TConstructor + output: this['input'] extends ['new', LParen, infer ParameterList extends t.TSchema[], RParen, '=>', infer InstanceType extends t.TSchema] + ? t.TConstructor : never } // prettier-ignore type Constructor = Static.Tuple<[ - Static.Const<'new'>, Static.Const, Parameters, Static.Const, Static.Const<'=>'>, Type + Static.Const<'new'>, Static.Const, ParameterList, Static.Const, Static.Const<'=>'>, Type ], ConstructorMapping> + // ------------------------------------------------------------------ // Mapped (requires deferred types) // ------------------------------------------------------------------ @@ -499,6 +528,7 @@ interface MappedMapping extends Static.IMapping { type Mapped = Static.Tuple<[ Static.Const, Static.Const, Static.Ident, Static.Const<'in'>, Type, Static.Const, Static.Const, Type, Static.Const ], MappedMapping> + // ------------------------------------------------------------------ // Array // ------------------------------------------------------------------ @@ -512,6 +542,7 @@ interface ArrayMapping extends Static.IMapping { type Array = Static.Tuple<[ Static.Const<'Array'>, Static.Const, Type, Static.Const, ], ArrayMapping> + // ------------------------------------------------------------------ // AsyncIterator // ------------------------------------------------------------------ @@ -525,6 +556,7 @@ interface AsyncIteratorMapping extends Static.IMapping { type AsyncIterator = Static.Tuple<[ Static.Const<'AsyncIterator'>, Static.Const, Type, Static.Const, ], AsyncIteratorMapping> + // ------------------------------------------------------------------ // Iterator // ------------------------------------------------------------------ @@ -552,6 +584,7 @@ interface ConstructorParametersMapping extends Static.IMapping { type ConstructorParameters = Static.Tuple<[ Static.Const<'ConstructorParameters'>, Static.Const, Type, Static.Const, ], ConstructorParametersMapping> + // ------------------------------------------------------------------ // FunctionParameters // ------------------------------------------------------------------ @@ -565,6 +598,7 @@ interface FunctionParametersMapping extends Static.IMapping { type FunctionParameters = Static.Tuple<[ Static.Const<'Parameters'>, Static.Const, Type, Static.Const, ], FunctionParametersMapping> + // ------------------------------------------------------------------ // InstanceType // ------------------------------------------------------------------ @@ -578,6 +612,7 @@ interface InstanceTypeMapping extends Static.IMapping { type InstanceType = Static.Tuple<[ Static.Const<'InstanceType'>, Static.Const, Type, Static.Const, ], InstanceTypeMapping> + // ------------------------------------------------------------------ // ReturnType // ------------------------------------------------------------------ @@ -591,6 +626,7 @@ interface ReturnTypeMapping extends Static.IMapping { type ReturnType = Static.Tuple<[ Static.Const<'ReturnType'>, Static.Const, Type, Static.Const, ], ReturnTypeMapping> + // ------------------------------------------------------------------ // Argument // ------------------------------------------------------------------ @@ -606,6 +642,7 @@ interface ArgumentMapping extends Static.IMapping { type Argument = Static.Tuple<[ Static.Const<'Argument'>, Static.Const, Type, Static.Const, ], ArgumentMapping> + // ------------------------------------------------------------------ // Awaited // ------------------------------------------------------------------ @@ -619,6 +656,7 @@ interface AwaitedMapping extends Static.IMapping { type Awaited = Static.Tuple<[ Static.Const<'Awaited'>, Static.Const, Type, Static.Const, ], AwaitedMapping> + // ------------------------------------------------------------------ // Promise // ------------------------------------------------------------------ @@ -632,6 +670,7 @@ interface PromiseMapping extends Static.IMapping { type Promise = Static.Tuple<[ Static.Const<'Promise'>, Static.Const, Type, Static.Const, ], PromiseMapping> + // ------------------------------------------------------------------ // Record // ------------------------------------------------------------------ @@ -645,6 +684,7 @@ interface RecordMapping extends Static.IMapping { type Record = Static.Tuple<[ Static.Const<'Record'>, Static.Const, Type, Static.Const, Type, Static.Const, ], RecordMapping> + // ------------------------------------------------------------------ // Partial // ------------------------------------------------------------------ @@ -658,6 +698,7 @@ interface PartialMapping extends Static.IMapping { type Partial = Static.Tuple<[ Static.Const<'Partial'>, Static.Const, Type, Static.Const, ], PartialMapping> + // ------------------------------------------------------------------ // Required // ------------------------------------------------------------------ @@ -671,6 +712,7 @@ interface RequiredMapping extends Static.IMapping { type Required = Static.Tuple<[ Static.Const<'Required'>, Static.Const, Type, Static.Const, ], RequiredMapping> + // ------------------------------------------------------------------ // Pick // ------------------------------------------------------------------ @@ -684,6 +726,7 @@ interface PickMapping extends Static.IMapping { type Pick = Static.Tuple<[ Static.Const<'Pick'>, Static.Const, Type, Static.Const, Type, Static.Const, ], PickMapping> + // ------------------------------------------------------------------ // Omit // ------------------------------------------------------------------ @@ -697,6 +740,7 @@ interface OmitMapping extends Static.IMapping { type Omit = Static.Tuple<[ Static.Const<'Omit'>, Static.Const, Type, Static.Const, Type, Static.Const ], OmitMapping> + // ------------------------------------------------------------------ // Exclude // ------------------------------------------------------------------ @@ -710,6 +754,7 @@ interface ExcludeMapping extends Static.IMapping { type Exclude = Static.Tuple<[ Static.Const<'Exclude'>, Static.Const, Type, Static.Const, Type, Static.Const ], ExcludeMapping> + // ------------------------------------------------------------------ // Extract // ------------------------------------------------------------------ @@ -723,6 +768,7 @@ interface ExtractMapping extends Static.IMapping { type Extract = Static.Tuple<[ Static.Const<'Extract'>, Static.Const, Type, Static.Const, Type, Static.Const ], ExtractMapping> + // ------------------------------------------------------------------ // Uppercase // ------------------------------------------------------------------ @@ -736,6 +782,7 @@ interface UppercaseMapping extends Static.IMapping { type Uppercase = Static.Tuple<[ Static.Const<'Uppercase'>, Static.Const, Type, Static.Const, ], UppercaseMapping> + // ------------------------------------------------------------------ // Lowercase // ------------------------------------------------------------------ @@ -749,6 +796,7 @@ interface LowercaseMapping extends Static.IMapping { type Lowercase = Static.Tuple<[ Static.Const<'Lowercase'>, Static.Const, Type, Static.Const, ], LowercaseMapping> + // ------------------------------------------------------------------ // Capitalize // ------------------------------------------------------------------ @@ -762,6 +810,7 @@ interface CapitalizeMapping extends Static.IMapping { type Capitalize = Static.Tuple<[ Static.Const<'Capitalize'>, Static.Const, Type, Static.Const, ], CapitalizeMapping> + // ------------------------------------------------------------------ // Uncapitalize // ------------------------------------------------------------------ @@ -775,10 +824,12 @@ interface UncapitalizeMapping extends Static.IMapping { type Uncapitalize = Static.Tuple<[ Static.Const<'Uncapitalize'>, Static.Const, Type, Static.Const, ], UncapitalizeMapping> + // ------------------------------------------------------------------ // Date // ------------------------------------------------------------------ type Date = Static.Const<'Date', Static.As> + // ------------------------------------------------------------------ // Uint8Array // ------------------------------------------------------------------ From 90979d8381716aec36d2cb600572f8b32fce35ee Mon Sep 17 00:00:00 2001 From: Lonli-Lokli Date: Mon, 17 Mar 2025 17:02:42 +0000 Subject: [PATCH 353/369] added @lonli-lokli/fetcher-typebox --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index fccb50e12..ed893e6b7 100644 --- a/readme.md +++ b/readme.md @@ -1737,6 +1737,7 @@ The following is a list of community packages that offer general tooling, extend | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with TypeBox from the CLI | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | +| [@lonli-lokli/fetcher-typebox](https://github.com/Lonli-Lokli/fetcher-ts/tree/master/packages/fetcher-typebox) | A strongly-typed fetch wrapper for TypeScript applications with optional runtime validation using TypeBox | From 0d2c1f49d834e3bb2a5a51dd259d57e761785854 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Mon, 24 Mar 2025 13:37:15 +0900 Subject: [PATCH 354/369] Revision 0.34.31 (#1209) * Use Tail Call Optimized Inference for Record Union Key * Version * ChangeLog * Order Ecosystem List --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 +-- package.json | 2 +- readme.md | 2 +- src/type/record/record.ts | 14 +++++------ test/static/record.ts | 53 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 7b81c3920..425741b4c 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -5,6 +5,8 @@ ### Revision Updates +- [Revision 0.34.31](https://github.com/sinclairzx81/typebox/pull/1209) + - Use Tail Call Optimized Inference for Records with Large Union Keys - [Revision 0.34.30](https://github.com/sinclairzx81/typebox/pull/1198) - Additional Syntax Parsing Optimizations - [Revision 0.34.29](https://github.com/sinclairzx81/typebox/pull/1197) diff --git a/package-lock.json b/package-lock.json index abce9d2f1..17451589e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.30", + "version": "0.34.31", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.30", + "version": "0.34.31", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index ca5b738b2..013f0d344 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.30", + "version": "0.34.31", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/readme.md b/readme.md index ed893e6b7..8bc45b3ca 100644 --- a/readme.md +++ b/readme.md @@ -1726,6 +1726,7 @@ The following is a list of community packages that offer general tooling, extend | [fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox) | Fastify TypeBox integration with the Fastify Type Provider | | [feathersjs](https://github.com/feathersjs/feathers) | The API and real-time application framework | | [fetch-typebox](https://github.com/erfanium/fetch-typebox) | Drop-in replacement for fetch that brings easy integration with TypeBox | +| [@lonli-lokli/fetcher-typebox](https://github.com/Lonli-Lokli/fetcher-ts/tree/master/packages/fetcher-typebox) | A strongly-typed fetch wrapper for TypeScript applications with optional runtime validation using TypeBox | | [h3-typebox](https://github.com/kevinmarrec/h3-typebox) | Schema validation utilities for h3 using TypeBox & Ajv | | [http-wizard](https://github.com/flodlc/http-wizard) | Type safe http client library for Fastify | | [json2typebox](https://github.com/hacxy/json2typebox) | Creating TypeBox code from Json Data | @@ -1737,7 +1738,6 @@ The following is a list of community packages that offer general tooling, extend | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with TypeBox from the CLI | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | -| [@lonli-lokli/fetcher-typebox](https://github.com/Lonli-Lokli/fetcher-ts/tree/master/packages/fetcher-typebox) | A strongly-typed fetch wrapper for TypeScript applications with optional runtime validation using TypeBox | diff --git a/src/type/record/record.ts b/src/type/record/record.ts index f9a4fe540..c118c8ab7 100644 --- a/src/type/record/record.ts +++ b/src/type/record/record.ts @@ -106,15 +106,15 @@ type TFromUnionKeyLiteralString, Type extends TSche // prettier-ignore type TFromUnionKeyLiteralNumber, Type extends TSchema> = { [_ in Key['const']]: Type } // prettier-ignore -type TFromUnionKeyRest = +type TFromUnionKeyVariants = Keys extends [infer Left extends TSchema, ...infer Right extends TSchema[]] ? ( - Left extends TUnion ? TFromUnionKeyRest & TFromUnionKeyRest : - Left extends TLiteral ? TFromUnionKeyLiteralString & TFromUnionKeyRest : - Left extends TLiteral ? TFromUnionKeyLiteralNumber & TFromUnionKeyRest : - {}) : {} + Left extends TUnion ? TFromUnionKeyVariants> : + Left extends TLiteral ? TFromUnionKeyVariants> : + Left extends TLiteral ? TFromUnionKeyVariants> : + {}) : Result // prettier-ignore -type TFromUnionKey> = ( - Ensure>> +type TFromUnionKey> = ( + Ensure>> ) // prettier-ignore function FromUnionKey(key: Key, type: Type, options: ObjectOptions): TFromUnionKey { diff --git a/test/static/record.ts b/test/static/record.ts index ab18b06b4..df66a813e 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -202,3 +202,56 @@ import { Type, Static } from '@sinclair/typebox' const T = Type.Record(K, Type.String()) Expect(T).ToStatic<{}>() } +// ------------------------------------------------------------------ +// Deep Union +// https://github.com/sinclairzx81/typebox/issues/1208 +// ------------------------------------------------------------------ +// prettier-ignore +{ + const A = Type.Record(Type.Union([ + Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3), Type.Literal(4), Type.Literal(5), Type.Literal(6), Type.Literal(7), + Type.Literal(8), Type.Literal(9), Type.Literal(10), Type.Literal(11), Type.Literal(12), Type.Literal(13), Type.Literal(14), Type.Literal(15), + Type.Literal(16), Type.Literal(17), Type.Literal(18), Type.Literal(19), Type.Literal(20), Type.Literal(21), Type.Literal(22), Type.Literal(23), + Type.Literal(24), Type.Literal(25), Type.Literal(26), Type.Literal(27), Type.Literal(28), Type.Literal(29), Type.Literal(30), Type.Literal(31), + Type.Literal(32), Type.Literal(33), Type.Literal(34), Type.Literal(35), Type.Literal(36), Type.Literal(37), Type.Literal(38), Type.Literal(39), + Type.Literal(40), Type.Literal(41), Type.Literal(42), Type.Literal(43), Type.Literal(44), Type.Literal(45), Type.Literal(46), Type.Literal(47), + Type.Literal(48), Type.Literal(49), Type.Literal(50), Type.Literal(51), Type.Literal(52), Type.Literal(53), Type.Literal(54), Type.Literal(55), + Type.Literal(56), Type.Literal(57), Type.Literal(58), Type.Literal(59), Type.Literal(60), Type.Literal(61), Type.Literal(62), Type.Literal(63), // <- x64 + Type.Literal(64), Type.Literal(65), Type.Literal(66), Type.Literal(67), Type.Literal(68), Type.Literal(69), Type.Literal(70), Type.Literal(71), + Type.Literal(72), Type.Literal(73), Type.Literal(74), Type.Literal(75), Type.Literal(76), Type.Literal(77), Type.Literal(78), Type.Literal(79), + Type.Literal(80), Type.Literal(81), Type.Literal(82), Type.Literal(83), Type.Literal(84), Type.Literal(85), Type.Literal(86), Type.Literal(87), + Type.Literal(88), Type.Literal(89), Type.Literal(90), Type.Literal(91), Type.Literal(92), Type.Literal(93), Type.Literal(94), Type.Literal(95), + Type.Literal(96), Type.Literal(97), Type.Literal(98), Type.Literal(99), Type.Literal(100), Type.Literal(101), Type.Literal(102), Type.Literal(103), + Type.Literal(104), Type.Literal(105), Type.Literal(106), Type.Literal(107), Type.Literal(108), Type.Literal(109), Type.Literal(110), Type.Literal(111), + Type.Literal(112), Type.Literal(113), Type.Literal(114), Type.Literal(115), Type.Literal(116), Type.Literal(117), Type.Literal(118), Type.Literal(119), + Type.Literal(120), Type.Literal(121), Type.Literal(122), Type.Literal(123), Type.Literal(124), Type.Literal(125), Type.Literal(126), Type.Literal(127), // <- x128 + ]), Type.String()) + const B = Type.Record(Type.Union([ + Type.Union([ + Type.Literal(0), Type.Literal(1), Type.Literal(2), Type.Literal(3), Type.Literal(4), Type.Literal(5), Type.Literal(6), Type.Literal(7), + Type.Literal(8), Type.Literal(9), Type.Literal(10), Type.Literal(11), Type.Literal(12), Type.Literal(13), Type.Literal(14), Type.Literal(15), + Type.Literal(16), Type.Literal(17), Type.Literal(18), Type.Literal(19), Type.Literal(20), Type.Literal(21), Type.Literal(22), Type.Literal(23), + Type.Literal(24), Type.Literal(25), Type.Literal(26), Type.Literal(27), Type.Literal(28), Type.Literal(29), Type.Literal(30), Type.Literal(31), + ]), + Type.Union([ + Type.Literal(32), Type.Literal(33), Type.Literal(34), Type.Literal(35), Type.Literal(36), Type.Literal(37), Type.Literal(38), Type.Literal(39), + Type.Literal(40), Type.Literal(41), Type.Literal(42), Type.Literal(43), Type.Literal(44), Type.Literal(45), Type.Literal(46), Type.Literal(47), + Type.Literal(48), Type.Literal(49), Type.Literal(50), Type.Literal(51), Type.Literal(52), Type.Literal(53), Type.Literal(54), Type.Literal(55), + Type.Literal(56), Type.Literal(57), Type.Literal(58), Type.Literal(59), Type.Literal(60), Type.Literal(61), Type.Literal(62), Type.Literal(63), // <- x64 + ]), + Type.Union([ + Type.Literal(64), Type.Literal(65), Type.Literal(66), Type.Literal(67), Type.Literal(68), Type.Literal(69), Type.Literal(70), Type.Literal(71), + Type.Literal(72), Type.Literal(73), Type.Literal(74), Type.Literal(75), Type.Literal(76), Type.Literal(77), Type.Literal(78), Type.Literal(79), + Type.Literal(80), Type.Literal(81), Type.Literal(82), Type.Literal(83), Type.Literal(84), Type.Literal(85), Type.Literal(86), Type.Literal(87), + Type.Literal(88), Type.Literal(89), Type.Literal(90), Type.Literal(91), Type.Literal(92), Type.Literal(93), Type.Literal(94), Type.Literal(95), + ]), + Type.Union([ + Type.Literal(96), Type.Literal(97), Type.Literal(98), Type.Literal(99), Type.Literal(100), Type.Literal(101), Type.Literal(102), Type.Literal(103), + Type.Literal(104), Type.Literal(105), Type.Literal(106), Type.Literal(107), Type.Literal(108), Type.Literal(109), Type.Literal(110), Type.Literal(111), + Type.Literal(112), Type.Literal(113), Type.Literal(114), Type.Literal(115), Type.Literal(116), Type.Literal(117), Type.Literal(118), Type.Literal(119), + Type.Literal(120), Type.Literal(121), Type.Literal(122), Type.Literal(123), Type.Literal(124), Type.Literal(125), Type.Literal(126), Type.Literal(127), // <- x128 + ]) + ]), Type.String()) + type A = Static + Expect(B).ToStatic() +} From f958156785350aa052c5f822bc2970d0945d887b Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 2 Apr 2025 17:11:45 +0900 Subject: [PATCH 355/369] Revision 0.34.32 (#1218) * Accelerated Syntax Parser * ChangeLog * Version --- changelog/0.34.0.md | 4 +- package-lock.json | 4 +- package.json | 2 +- src/syntax/mapping.ts | 1030 +++++++++++++++++++++++++ src/syntax/parser.ts | 1686 +++++++++++++++++++++++++++++++++++++++++ src/syntax/runtime.ts | 802 -------------------- src/syntax/static.ts | 836 -------------------- src/syntax/syntax.ts | 63 +- 8 files changed, 2736 insertions(+), 1691 deletions(-) create mode 100644 src/syntax/mapping.ts create mode 100644 src/syntax/parser.ts delete mode 100644 src/syntax/runtime.ts delete mode 100644 src/syntax/static.ts diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 425741b4c..5baecf130 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -2,9 +2,9 @@ --- - - ### Revision Updates +- [Revision 0.34.32](https://github.com/sinclairzx81/typebox/pull/1218) + - Accelerated | High Performance Syntax Parsing - [Revision 0.34.31](https://github.com/sinclairzx81/typebox/pull/1209) - Use Tail Call Optimized Inference for Records with Large Union Keys - [Revision 0.34.30](https://github.com/sinclairzx81/typebox/pull/1198) diff --git a/package-lock.json b/package-lock.json index 17451589e..c76f353fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.31", + "version": "0.34.32", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.31", + "version": "0.34.32", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 013f0d344..b0b1786e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.31", + "version": "0.34.32", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/syntax/mapping.ts b/src/syntax/mapping.ts new file mode 100644 index 000000000..257435f27 --- /dev/null +++ b/src/syntax/mapping.ts @@ -0,0 +1,1030 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/syntax + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import * as T from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// +// Dereference +// +// Referential types pull from the Context or defer dereferencing +// for later execution. This overlaps with module dereferencing, +// where named identifiers in the syntax are deferred until +// instantiation. This code should be revised as part of a +// general-purpose Instantiate module (next revision) +// +// ------------------------------------------------------------------ +// prettier-ignore +type TDereference = ( + Key extends keyof Context ? Context[Key] : T.TRef +) +// prettier-ignore +const Dereference = (context: T.TProperties, key: string): T.TSchema => { + return key in context ? context[key] : T.Ref(key) +} + +// ------------------------------------------------------------------ +// +// Delimited +// +// Delimited sequences use an accumulated buffer to parse sequence +// tokens. This approach is more scalable than using a Union + Tuple +// + Epsilon pattern, as TypeScript can instantiate deeper when +// tail-call recursive accumulators are employed. However, this +// comes with a latent processing cost due to the need to decode +// the accumulated buffer. +// +// - Encoding: [[, ','][], [] | []] +// +// ------------------------------------------------------------------ +// prettier-ignore +type TDelimitedDecode = ( + Input extends [infer Left, ...infer Right] + ? Left extends [infer Item, infer _] + ? TDelimitedDecode + : TDelimitedDecode + : Result +) +// prettier-ignore +type TDelimited + = Input extends [infer Left extends unknown[], infer Right extends unknown[]] + ? TDelimitedDecode<[...Left, ...Right]> + : [] +// prettier-ignore +const DelimitedDecode = (input: ([unknown, unknown] | unknown)[], result: unknown[] = []) => { + return input.reduce((result, left) => { + return T.ValueGuard.IsArray(left) && left.length === 2 + ? [...result, left[0]] + : [...result, left] + }, []) +} +// prettier-ignore +const Delimited = (input: [unknown, unknown]) => { + const [left, right] = input as [unknown[], unknown[]] + return DelimitedDecode([...left, ...right]) +} +// ------------------------------------------------------------------- +// GenericReferenceParameterList: [[Type, ','][], [Type] | []] +// ------------------------------------------------------------------- +// prettier-ignore +export type TGenericReferenceParameterListMapping + = TDelimited +// prettier-ignore +export function GenericReferenceParameterListMapping(input: [unknown, unknown], context: unknown) { + return Delimited(input) +} +// ------------------------------------------------------------------- +// GenericReference: [, '<', GenericReferenceParameterList, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TGenericReferenceMapping'] + ? T.TInstantiate, Args> + : never + : never +> = Result +// prettier-ignore +export function GenericReferenceMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const type = Dereference(context as T.TProperties, input[0] as string) + const args = input[2] as T.TSchema[] + return T.Instantiate(type, args) +} +// ------------------------------------------------------------------- +// GenericArgumentsList: [[, ','][], [] | []] +// ------------------------------------------------------------------- +// prettier-ignore +export type TGenericArgumentsListMapping + = TDelimited +// prettier-ignore +export function GenericArgumentsListMapping(input: [unknown, unknown], context: unknown) { + return Delimited(input) +} +// ------------------------------------------------------------------- +// GenericArguments: ['<', GenericArgumentsList, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +type GenericArgumentsContext = ( + Arguments extends [...infer Left extends string[], infer Right extends string] + ? GenericArgumentsContext }> + : T.Evaluate +) +// prettier-ignore +export type TGenericArgumentsMapping = + Input extends ['<', infer Arguments extends string[], '>'] + ? Context extends infer Context extends T.TProperties + ? GenericArgumentsContext + : never + : never +// ... +// prettier-ignore +const GenericArgumentsContext = (_arguments: string[], context: T.TProperties) => { + return _arguments.reduce((result, arg, index) => { + return { ...result, [arg]: T.Argument(index) } + }, context) +} +// prettier-ignore +export function GenericArgumentsMapping(input: [unknown, unknown, unknown], context: unknown) { + return input.length === 3 + ? GenericArgumentsContext(input[1] as string[], context as T.TProperties) + : {} +} +// ------------------------------------------------------------------- +// KeywordString: 'string' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordStringMapping + = T.TString +// prettier-ignore +export function KeywordStringMapping(input: 'string', context: unknown) { + return T.String() +} +// ------------------------------------------------------------------- +// KeywordNumber: 'number' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordNumberMapping + = T.TNumber +// prettier-ignore +export function KeywordNumberMapping(input: 'number', context: unknown) { + return T.Number() +} +// ------------------------------------------------------------------- +// KeywordBoolean: 'boolean' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordBooleanMapping + = T.TBoolean +// prettier-ignore +export function KeywordBooleanMapping(input: 'boolean', context: unknown) { + return T.Boolean() +} +// ------------------------------------------------------------------- +// KeywordUndefined: 'undefined' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordUndefinedMapping + = T.TUndefined +// prettier-ignore +export function KeywordUndefinedMapping(input: 'undefined', context: unknown) { + return T.Undefined() +} +// ------------------------------------------------------------------- +// KeywordNull: 'null' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordNullMapping + = T.TNull +// prettier-ignore +export function KeywordNullMapping(input: 'null', context: unknown) { + return T.Null() +} +// ------------------------------------------------------------------- +// KeywordInteger: 'integer' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordIntegerMapping + = T.TInteger +// prettier-ignore +export function KeywordIntegerMapping(input: 'integer', context: unknown) { + return T.Integer() +} +// ------------------------------------------------------------------- +// KeywordBigInt: 'bigint' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordBigIntMapping + = T.TBigInt +// prettier-ignore +export function KeywordBigIntMapping(input: 'bigint', context: unknown) { + return T.BigInt() +} +// ------------------------------------------------------------------- +// KeywordUnknown: 'unknown' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordUnknownMapping + = T.TUnknown +// prettier-ignore +export function KeywordUnknownMapping(input: 'unknown', context: unknown) { + return T.Unknown() +} +// ------------------------------------------------------------------- +// KeywordAny: 'any' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordAnyMapping + = T.TAny +// prettier-ignore +export function KeywordAnyMapping(input: 'any', context: unknown) { + return T.Any() +} +// ------------------------------------------------------------------- +// KeywordNever: 'never' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordNeverMapping + = T.TNever +// prettier-ignore +export function KeywordNeverMapping(input: 'never', context: unknown) { + return T.Never() +} +// ------------------------------------------------------------------- +// KeywordSymbol: 'symbol' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordSymbolMapping + = T.TSymbol +// prettier-ignore +export function KeywordSymbolMapping(input: 'symbol', context: unknown) { + return T.Symbol() +} +// ------------------------------------------------------------------- +// KeywordVoid: 'void' +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordVoidMapping + = T.TVoid +// prettier-ignore +export function KeywordVoidMapping(input: 'void', context: unknown) { + return T.Void() +} +// ------------------------------------------------------------------- +// Keyword: KeywordString | KeywordNumber | KeywordBoolean | KeywordUndefined | KeywordNull | KeywordInteger | KeywordBigInt | KeywordUnknown | KeywordAny | KeywordNever | KeywordSymbol | KeywordVoid +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeywordMapping + = Input +// prettier-ignore +export function KeywordMapping(input: unknown, context: unknown) { + return input +} +// ------------------------------------------------------------------- +// LiteralString: +// ------------------------------------------------------------------- +// prettier-ignore +export type TLiteralStringMapping = + Input extends T.TLiteralValue ? T.TLiteral : never +// prettier-ignore +export function LiteralStringMapping(input: string, context: unknown) { + return T.Literal(input) +} +// ------------------------------------------------------------------- +// LiteralNumber: +// ------------------------------------------------------------------- +// prettier-ignore +export type TLiteralNumberMapping = + Input extends `${infer Value extends number}` ? T.TLiteral : never +// prettier-ignore +export function LiteralNumberMapping(input: string, context: unknown) { + return T.Literal(parseFloat(input)) +} +// ------------------------------------------------------------------- +// LiteralBoolean: 'true' | 'false' +// ------------------------------------------------------------------- +// prettier-ignore +export type TLiteralBooleanMapping + = Input extends 'true' ? T.TLiteral : T.TLiteral +// prettier-ignore +export function LiteralBooleanMapping(input: 'true' | 'false', context: unknown) { + return T.Literal(input === 'true') +} +// ------------------------------------------------------------------- +// Literal: LiteralBoolean | LiteralNumber | LiteralString +// ------------------------------------------------------------------- +// prettier-ignore +export type TLiteralMapping + = Input +// prettier-ignore +export function LiteralMapping(input: unknown, context: unknown) { + return input +} +// ------------------------------------------------------------------- +// KeyOf: ['keyof'] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TKeyOfMapping + = Input extends [unknown] ? true : false +// prettier-ignore +export function KeyOfMapping(input: [unknown] | [], context: unknown) { + return input.length > 0 +} +// ------------------------------------------------------------------- +// IndexArray: ['[', Type, ']'] | ['[', ']'][] +// ------------------------------------------------------------------- +// prettier-ignore +type TIndexArrayMappingReduce = ( + Input extends [infer Left extends unknown, ...infer Right extends unknown[]] + ? Left extends ['[', infer Type extends T.TSchema, ']'] + ? TIndexArrayMappingReduce + : TIndexArrayMappingReduce + : Result +) +// prettier-ignore +export type TIndexArrayMapping + = Input extends unknown[] + ? TIndexArrayMappingReduce + : [] +// prettier-ignore +export function IndexArrayMapping(input: ([unknown, unknown, unknown] | [unknown, unknown])[], context: unknown) { + return input.reduce((result: unknown[], current) => { + return current.length === 3 + ? [...result, [current[1]]] + : [...result, []] + }, [] as unknown[]) +} +// ------------------------------------------------------------------- +// Extends: ['extends', Type, '?', Type, ':', Type] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExtendsMapping + = Input extends ['extends', infer Type extends T.TSchema, '?', infer True extends T.TSchema, ':', infer False extends T.TSchema] + ? [Type, True, False] + : [] +// prettier-ignore +export function ExtendsMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown] | [], context: unknown) { + return input.length === 6 + ? [input[1], input[3], input[5]] + : [] +} +// ------------------------------------------------------------------- +// Base: ['(', Type, ')'] | Keyword | Object | Tuple | Literal | Constructor | Function | Mapped | AsyncIterator | Iterator | ConstructorParameters | FunctionParameters | InstanceType | ReturnType | Argument | Awaited | Array | Record | Promise | Partial | Required | Pick | Omit | Exclude | Extract | Uppercase | Lowercase | Capitalize | Uncapitalize | Date | Uint8Array | GenericReference | Reference +// ------------------------------------------------------------------- +// prettier-ignore +export type TBaseMapping = ( + Input extends ['(', infer Type extends T.TSchema, ')'] ? Type : + Input extends infer Type extends T.TSchema ? Type : + never +) +// prettier-ignore +export function BaseMapping(input: [unknown, unknown, unknown] | unknown, context: unknown) { + return T.ValueGuard.IsArray(input) && input.length === 3 ? input[1] : input +} +// ------------------------------------------------------------------- +// Factor: [KeyOf, Base, IndexArray, Extends] +// ------------------------------------------------------------------- +// prettier-ignore +type TFactorIndexArray = ( + IndexArray extends [...infer Left extends unknown[], infer Right extends T.TSchema[]] ? ( + Right extends [infer Indexer extends T.TSchema] ? T.TIndex, T.TIndexPropertyKeys> : + Right extends [] ? T.TArray> : + T.TNever + ) : Type +) +// prettier-ignore +type TFactorExtends = ( + Extends extends [infer Right extends T.TSchema, infer True extends T.TSchema, infer False extends T.TSchema] + ? T.TExtends + : Type +) +// prettier-ignore +export type TFactorMapping = + Input extends [infer KeyOf extends boolean, infer Type extends T.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] + ? KeyOf extends true + ? TFactorExtends>, Extends> + : TFactorExtends, Extends> + : never + +// ... +// prettier-ignore +const FactorIndexArray = (Type: T.TSchema, indexArray: unknown[]): T.TSchema => { + return indexArray.reduceRight((result, right) => { + const _right = right as T.TSchema[] + return ( + _right.length === 1 ? T.Index(result, _right[0]) : + _right.length === 0 ? T.Array(result, _right[0]) : + T.Never() + ) + }, Type) +} +// prettier-ignore +const FactorExtends = (Type: T.TSchema, Extends: T.TSchema[]) => { + return Extends.length === 3 + ? T.Extends(Type, Extends[0], Extends[1], Extends[2]) + : Type +} +// prettier-ignore +export function FactorMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [KeyOf, Type, IndexArray, Extends] = input as [boolean, T.TSchema, unknown[], T.TSchema[]] + return KeyOf + ? FactorExtends(T.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) + : FactorExtends(FactorIndexArray(Type, IndexArray), Extends) +} + +// ------------------------------------------------------------------ +// +// ExprBinaryMapping +// +// TypeBox Union and Intersection types are flattened to prevent +// excessive nesting of `anyOf` and `allOf`, ensuring a more +// readable and presentable type for the user. This function +// recursively reduces Union and Intersection types based on +// binary expressions parsed from input. +// +// ------------------------------------------------------------------ +// prettier-ignore +type TExprBinaryMapping = ( + Rest extends [infer Operator extends unknown, infer Right extends T.TSchema, infer Next extends unknown[]] ? ( + TExprBinaryMapping extends infer Schema extends T.TSchema ? ( + Operator extends '&' ? ( + Schema extends T.TIntersect + ? T.TIntersect<[Left, ...Types]> + : T.TIntersect<[Left, Schema]> + ) : + Operator extends '|' ? ( + Schema extends T.TUnion + ? T.TUnion<[Left, ...Types]> + : T.TUnion<[Left, Schema]> + ) : never + ) : never + ) : Left +) +// prettier-ignore +function ExprBinaryMapping(Left: T.TSchema, Rest: unknown[]): T.TSchema { + return ( + Rest.length === 3 ? (() => { + const [Operator, Right, Next] = Rest as [string, T.TSchema, unknown[]] + const Schema = ExprBinaryMapping(Right, Next) + if (Operator === '&') { + return T.TypeGuard.IsIntersect(Schema) + ? T.Intersect([Left, ...Schema.allOf]) + : T.Intersect([Left, Schema]) + } + if (Operator === '|') { + return T.TypeGuard.IsUnion(Schema) + ? T.Union([Left, ...Schema.anyOf]) + : T.Union([Left, Schema]) + } + throw 1 + })() : Left + ) +} +// ------------------------------------------------------------------- +// ExprTermTail: ['&', Factor, ExprTermTail] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExprTermTailMapping + = Input +// prettier-ignore +export function ExprTermTailMapping(input: [unknown, unknown, unknown] | [], context: unknown) { + return input +} +// ------------------------------------------------------------------- +// ExprTerm: [Factor, ExprTermTail] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExprTermMapping = ( + Input extends [infer Left extends T.TSchema, infer Rest extends unknown[]] + ? TExprBinaryMapping + : [] +) +// prettier-ignore +export function ExprTermMapping(input: [unknown, unknown], context: unknown) { + const [left, rest] = input as [T.TSchema, unknown[]] + return ExprBinaryMapping(left, rest) +} +// ------------------------------------------------------------------- +// ExprTail: ['|', ExprTerm, ExprTail] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExprTailMapping + = Input +// prettier-ignore +export function ExprTailMapping(input: [unknown, unknown, unknown] | [], context: unknown) { + return input +} +// ------------------------------------------------------------------- +// Expr: [ExprTerm, ExprTail] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExprMapping + = Input extends [infer Left extends T.TSchema, infer Rest extends unknown[]] + ? TExprBinaryMapping + : [] +// prettier-ignore +export function ExprMapping(input: [unknown, unknown], context: unknown) { + const [left, rest] = input as [T.TSchema, unknown[]] + return ExprBinaryMapping(left, rest) +} +// ------------------------------------------------------------------- +// Type: GenericArguments -> Expr | Expr +// ------------------------------------------------------------------- +// prettier-ignore +export type TTypeMapping + = Input +// prettier-ignore +export function TypeMapping(input: unknown, context: unknown) { + return input +} +// ------------------------------------------------------------------- +// PropertyKey: | +// ------------------------------------------------------------------- +// prettier-ignore +export type TPropertyKeyMapping + = Input +// prettier-ignore +export function PropertyKeyMapping(input: string, context: unknown) { + return input +} +// ------------------------------------------------------------------- +// Readonly: ['readonly'] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TReadonlyMapping + = Input extends [unknown] ? true : false +// prettier-ignore +export function ReadonlyMapping(input: [unknown] | [], context: unknown) { + return input.length > 0 +} +// ------------------------------------------------------------------- +// Optional: ['?'] | [] +// ------------------------------------------------------------------- +// prettier-ignore +export type TOptionalMapping + = Input extends [unknown] ? true : false +// prettier-ignore +export function OptionalMapping(input: [unknown] | [], context: unknown) { + return input.length > 0 +} +// ------------------------------------------------------------------- +// Property: [Readonly, PropertyKey, Optional, ':', Type] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPropertyMapping + = Input extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends T.TSchema] ? { + [_ in Key]: ( + [IsReadonly, IsOptional] extends [true, true] ? T.TReadonlyOptional : + [IsReadonly, IsOptional] extends [true, false] ? T.TReadonly : + [IsReadonly, IsOptional] extends [false, true] ? T.TOptional : + Type + ) + } : never +// prettier-ignore +export function PropertyMapping(input: [unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [isReadonly, key, isOptional, _colon, type] = input as [boolean, string, boolean, ':', T.TSchema] + return { + [key]: ( + isReadonly && isOptional ? T.ReadonlyOptional(type) : + isReadonly && !isOptional ? T.Readonly(type) : + !isReadonly && isOptional ? T.Optional(type) : + type + ) + } +} +// ------------------------------------------------------------------- +// PropertyDelimiter: [',', '\n'] | [';', '\n'] | [','] | [';'] | ['\n'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPropertyDelimiterMapping + = Input +// prettier-ignore +export function PropertyDelimiterMapping(input: [unknown, unknown] | [unknown], context: unknown) { + return input +} +// ------------------------------------------------------------------- +// PropertyList: [[Property, PropertyDelimiter][], [Property] | []] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPropertyListMapping + = TDelimited +// prettier-ignore +export function PropertyListMapping(input: [unknown, unknown], context: unknown) { + return Delimited(input) +} +// ------------------------------------------------------------------- +// Object: ['{', PropertyList, '}'] +// ------------------------------------------------------------------- +// prettier-ignore +type TObjectMappingReduce = ( + PropertiesList extends [infer Left extends T.TProperties, ...infer Right extends T.TProperties[]] + ? TObjectMappingReduce + : { [Key in keyof Result]: Result[Key] } +) +// prettier-ignore +export type TObjectMapping = + Input extends ['{', infer PropertyList extends T.TProperties[], '}'] + ? T.TObject> + : never +// prettier-ignore +export function ObjectMapping(input: [unknown, unknown, unknown], context: unknown) { + const propertyList = input[1] as T.TProperties[] + return T.Object(propertyList.reduce((result, property) => { + return { ...result, ...property } + }, {} as T.TProperties)) +} +// ------------------------------------------------------------------- +// ElementList: [[Type, ','][], [Type] | []] +// ------------------------------------------------------------------- +// prettier-ignore +export type TElementListMapping + = TDelimited +// prettier-ignore +export function ElementListMapping(input: [unknown, unknown], context: unknown) { + return Delimited(input) +} +// ------------------------------------------------------------------- +// Tuple: ['[', ElementList, ']'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TTupleMapping + = Input extends ['[', infer Types extends T.TSchema[], ']'] ? T.TTuple : never +// prettier-ignore +export function TupleMapping(input: [unknown, unknown, unknown], context: unknown) { + return T.Tuple(input[1] as T.TSchema[]) +} +// ------------------------------------------------------------------- +// Parameter: [, ':', Type] +// ------------------------------------------------------------------- +// prettier-ignore +export type TParameterMapping + = Input extends [string, ':', infer Type extends T.TSchema] ? Type : never +// prettier-ignore +export function ParameterMapping(input: [unknown, unknown, unknown], context: unknown) { + const [_ident, _colon, type] = input as [string, ':', T.TSchema] + return type +} +// ------------------------------------------------------------------- +// ParameterList: [[Parameter, ','][], [Parameter] | []] +// ------------------------------------------------------------------- +// prettier-ignore +export type TParameterListMapping + = TDelimited +// prettier-ignore +export function ParameterListMapping(input: [unknown, unknown], context: unknown) { + return Delimited(input) +} +// ------------------------------------------------------------------- +// Function: ['(', ParameterList, ')', '=>', Type] +// ------------------------------------------------------------------- +// prettier-ignore +export type TFunctionMapping + = Input extends ['(', infer ParameterList extends T.TSchema[], ')', '=>', infer ReturnType extends T.TSchema] + ? T.TFunction + : never +// prettier-ignore +export function FunctionMapping(input: [unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_lparan, parameterList, _rparan, _arrow, returnType] = input as ['(', T.TSchema[], ')', '=>', T.TSchema] + return T.Function(parameterList, returnType) +} +// ------------------------------------------------------------------- +// Constructor: ['new', '(', ParameterList, ')', '=>', Type] +// ------------------------------------------------------------------- +// prettier-ignore +export type TConstructorMapping + = Input extends ['new', '(', infer ParameterList extends T.TSchema[], ')', '=>', infer InstanceType extends T.TSchema] + ? T.TConstructor + : never +// prettier-ignore +export function ConstructorMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_new, _lparan, parameterList, _rparan, _arrow, instanceType] = input as ['new', '(', T.TSchema[], ')', '=>', T.TSchema] + return T.Constructor(parameterList, instanceType) +} +// ------------------------------------------------------------------- +// Mapped: ['{', '[', , 'in', Type, ']', ':', Type, '}'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TMappedMapping + = Input extends ['{', '[', infer _Key extends string, 'in', infer _Right extends T.TSchema, ']', ':', infer _Type extends T.TSchema, '}'] + ? T.TLiteral<'Mapped types not supported'> + : never +// prettier-ignore +export function MappedMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_lbrace, _lbracket, _key, _in, _right, _rbracket, _colon, _type] = input as ['{', '[', string, 'in', T.TSchema, ']', ':', T.TSchema, '}'] + return T.Literal('Mapped types not supported') +} +// ------------------------------------------------------------------- +// AsyncIterator: ['AsyncIterator', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TAsyncIteratorMapping + = Input extends ['AsyncIterator', '<', infer Type extends T.TSchema, '>'] + ? T.TAsyncIterator + : never +// prettier-ignore +export function AsyncIteratorMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['AsyncIterator', '<', T.TSchema, '>'] + return T.AsyncIterator(type) +} +// ------------------------------------------------------------------- +// Iterator: ['Iterator', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TIteratorMapping + = Input extends ['Iterator', '<', infer Type extends T.TSchema, '>'] + ? T.TIterator + : never +// prettier-ignore +export function IteratorMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Iterator', '<', T.TSchema, '>'] + return T.Iterator(type) +} +// ------------------------------------------------------------------- +// Argument: ['Argument', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TArgumentMapping + = Input extends ['Argument', '<', infer Type extends T.TSchema, '>'] + ? Type extends T.TLiteral + ? T.TArgument + : T.TNever + : never +// prettier-ignore +export function ArgumentMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + return T.KindGuard.IsLiteralNumber(input[2]) + ? T.Argument(Math.trunc(input[2].const)) + : T.Never() +} +// ------------------------------------------------------------------- +// Awaited: ['Awaited', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TAwaitedMapping + = Input extends ['Awaited', '<', infer Type extends T.TSchema, '>'] + ? T.TAwaited + : never +// prettier-ignore +export function AwaitedMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Awaited', '<', T.TSchema, '>'] + return T.Awaited(type) +} +// ------------------------------------------------------------------- +// Array: ['Array', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TArrayMapping + = Input extends ['Array', '<', infer Type extends T.TSchema, '>'] + ? T.TArray + : never +// prettier-ignore +export function ArrayMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Array', '<', T.TSchema, '>'] + return T.Array(type) +} +// ------------------------------------------------------------------- +// Record: ['Record', '<', Type, ',', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TRecordMapping + = Input extends ['Record', '<', infer Key extends T.TSchema, ',', infer Type extends T.TSchema, '>'] + ? T.TRecordOrObject + : never +// prettier-ignore +export function RecordMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, key, _comma, type, _rangle] = input as ['Record', '<', T.TSchema, ',', T.TSchema, '>'] + return T.Record(key, type) +} +// ------------------------------------------------------------------- +// Promise: ['Promise', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPromiseMapping + = Input extends ['Promise', '<', infer Type extends T.TSchema, '>'] + ? T.TPromise + : never +// prettier-ignore +export function PromiseMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Promise', '<', T.TSchema, '>'] + return T.Promise(type) +} +// ------------------------------------------------------------------- +// ConstructorParameters: ['ConstructorParameters', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TConstructorParametersMapping + = Input extends ['ConstructorParameters', '<', infer Type extends T.TSchema, '>'] + ? T.TConstructorParameters + : never +// prettier-ignore +export function ConstructorParametersMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['ConstructorParameters', '<', T.TSchema, '>'] + return T.ConstructorParameters(type) +} +// ------------------------------------------------------------------- +// FunctionParameters: ['Parameters', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TFunctionParametersMapping + = Input extends ['Parameters', '<', infer Type extends T.TSchema, '>'] + ? T.TParameters + : never +// prettier-ignore +export function FunctionParametersMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Parameters', '<', T.TSchema, '>'] + return T.Parameters(type) +} +// ------------------------------------------------------------------- +// InstanceType: ['InstanceType', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TInstanceTypeMapping + = Input extends ['InstanceType', '<', infer Type extends T.TSchema, '>'] + ? T.TInstanceType + : never +// prettier-ignore +export function InstanceTypeMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['InstanceType', '<', T.TSchema, '>'] + return T.InstanceType(type) +} +// ------------------------------------------------------------------- +// ReturnType: ['ReturnType', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TReturnTypeMapping + = Input extends ['ReturnType', '<', infer Type extends T.TSchema, '>'] + ? T.TReturnType + : never +// prettier-ignore +export function ReturnTypeMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['ReturnType', '<', T.TSchema, '>'] + return T.ReturnType(type) +} +// ------------------------------------------------------------------- +// Partial: ['Partial', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPartialMapping + = Input extends ['Partial', '<', infer Type extends T.TSchema, '>'] + ? T.TPartial + : never +// prettier-ignore +export function PartialMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Partial', '<', T.TSchema, '>'] + return T.Partial(type) +} +// ------------------------------------------------------------------- +// Required: ['Required', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TRequiredMapping + = Input extends ['Required', '<', infer Type extends T.TSchema, '>'] + ? T.TRequired + : never +// prettier-ignore +export function RequiredMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Required', '<', T.TSchema, '>'] + return T.Required(type) +} +// ------------------------------------------------------------------- +// Pick: ['Pick', '<', Type, ',', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TPickMapping + = Input extends ['Pick', '<', infer Type extends T.TSchema, ',', infer Key extends T.TSchema, '>'] + ? T.TPick + : never +// prettier-ignore +export function PickMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, key, _comma, type, _rangle] = input as ['Pick', '<', T.TSchema, ',', T.TSchema, '>'] + return T.Pick(key, type) +} +// ------------------------------------------------------------------- +// Omit: ['Omit', '<', Type, ',', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TOmitMapping + = Input extends ['Omit', '<', infer Type extends T.TSchema, ',', infer Key extends T.TSchema, '>'] + ? T.TOmit + : never +// prettier-ignore +export function OmitMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, key, _comma, type, _rangle] = input as ['Omit', '<', T.TSchema, ',', T.TSchema, '>'] + return T.Omit(key, type) +} +// ------------------------------------------------------------------- +// Exclude: ['Exclude', '<', Type, ',', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExcludeMapping + = Input extends ['Exclude', '<', infer Type extends T.TSchema, ',', infer Key extends T.TSchema, '>'] + ? T.TExclude + : never +// prettier-ignore +export function ExcludeMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, key, _comma, type, _rangle] = input as ['Exclude', '<', T.TSchema, ',', T.TSchema, '>'] + return T.Exclude(key, type) +} +// ------------------------------------------------------------------- +// Extract: ['Extract', '<', Type, ',', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TExtractMapping + = Input extends ['Extract', '<', infer Type extends T.TSchema, ',', infer Key extends T.TSchema, '>'] + ? T.TExtract + : never +// prettier-ignore +export function ExtractMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, key, _comma, type, _rangle] = input as ['Extract', '<', T.TSchema, ',', T.TSchema, '>'] + return T.Extract(key, type) +} +// ------------------------------------------------------------------- +// Uppercase: ['Uppercase', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TUppercaseMapping + = Input extends ['Uppercase', '<', infer Type extends T.TSchema, '>'] + ? T.TUppercase + : never +// prettier-ignore +export function UppercaseMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Uppercase', '<', T.TSchema, '>'] + return T.Uppercase(type) +} +// ------------------------------------------------------------------- +// Lowercase: ['Lowercase', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TLowercaseMapping + = Input extends ['Lowercase', '<', infer Type extends T.TSchema, '>'] + ? T.TLowercase + : never +// prettier-ignore +export function LowercaseMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Lowercase', '<', T.TSchema, '>'] + return T.Lowercase(type) +} +// ------------------------------------------------------------------- +// Capitalize: ['Capitalize', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TCapitalizeMapping + = Input extends ['Capitalize', '<', infer Type extends T.TSchema, '>'] + ? T.TCapitalize + : never +// prettier-ignore +export function CapitalizeMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Capitalize', '<', T.TSchema, '>'] + return T.Capitalize(type) +} +// ------------------------------------------------------------------- +// Uncapitalize: ['Uncapitalize', '<', Type, '>'] +// ------------------------------------------------------------------- +// prettier-ignore +export type TUncapitalizeMapping + = Input extends ['Uncapitalize', '<', infer Type extends T.TSchema, '>'] + ? T.TUncapitalize + : never +// prettier-ignore +export function UncapitalizeMapping(input: [unknown, unknown, unknown, unknown], context: unknown) { + const [_name, _langle, type, _rangle] = input as ['Uncapitalize', '<', T.TSchema, '>'] + return T.Uncapitalize(type) +} +// ------------------------------------------------------------------- +// Date: 'Date' +// ------------------------------------------------------------------- +// prettier-ignore +export type TDateMapping + = T.TDate +// prettier-ignore +export function DateMapping(input: 'Date', context: unknown) { + return T.Date() +} +// ------------------------------------------------------------------- +// Uint8Array: 'Uint8Array' +// ------------------------------------------------------------------- +// prettier-ignore +export type TUint8ArrayMapping + = T.TUint8Array +// prettier-ignore +export function Uint8ArrayMapping(input: 'Uint8Array', context: unknown) { + return T.Uint8Array() +} +// ------------------------------------------------------------------- +// Reference: +// ------------------------------------------------------------------- +// prettier-ignore +export type TReferenceMapping + = Context extends T.TProperties + ? Input extends string + ? TDereference + : never + : never +// prettier-ignore +export function ReferenceMapping(input: string, context: unknown) { + const target = Dereference(context as T.TProperties, input) + return target +} diff --git a/src/syntax/parser.ts b/src/syntax/parser.ts new file mode 100644 index 000000000..6787f8588 --- /dev/null +++ b/src/syntax/parser.ts @@ -0,0 +1,1686 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/syntax + +The MIT License (MIT) + +Copyright (c) 2017-2025 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { Runtime, Static } from '../parser/index' +import * as T from '../type/index' +import * as S from './mapping' + +// ------------------------------------------------------------------ +// Parser +// +// The following code is optimized inline types generated by a remote +// compiler process. It is readonly and should not be modified. +// ------------------------------------------------------------------ + +export type TGenericReferenceParameterList_0 = ( + TType extends [infer _0, infer Input extends string] ? (Static.Token.Const<',', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0, infer Input extends string] + ? TGenericReferenceParameterList_0 + : [Result, Input] +export type TGenericReferenceParameterList = ( + TGenericReferenceParameterList_0 extends [infer _0, infer Input extends string] + ? ( + (TType extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : [] + ) extends [infer _1, infer Input extends string] + ? [[_0, _1], Input] + : [] + : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TGenericReferenceParameterListMapping<_0, Context>, Input] + : [] +export type TGenericReference = ( + Static.Token.Ident extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TGenericReferenceParameterList extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TGenericReferenceMapping<_0, Context>, Input] + : [] +export type TGenericArgumentsList_0 = ( + Static.Token.Ident extends [infer _0, infer Input extends string] ? (Static.Token.Const<',', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0, infer Input extends string] + ? TGenericArgumentsList_0 + : [Result, Input] +export type TGenericArgumentsList = ( + TGenericArgumentsList_0 extends [infer _0, infer Input extends string] + ? ( + (Static.Token.Ident extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] + ) extends [infer _1, infer Input extends string] + ? [[_0, _1], Input] + : [] + : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TGenericArgumentsListMapping<_0, Context>, Input] + : [] +export type TGenericArguments = ( + Static.Token.Const<'<', Input> extends [infer _0, infer Input extends string] + ? TGenericArgumentsList extends [infer _1, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown], infer Input extends string] + ? [S.TGenericArgumentsMapping<_0, Context>, Input] + : [] +export type TKeywordString = Static.Token.Const<'string', Input> extends [infer _0 extends 'string', infer Input extends string] ? [S.TKeywordStringMapping<_0, Context>, Input] : [] +export type TKeywordNumber = Static.Token.Const<'number', Input> extends [infer _0 extends 'number', infer Input extends string] ? [S.TKeywordNumberMapping<_0, Context>, Input] : [] +export type TKeywordBoolean = Static.Token.Const<'boolean', Input> extends [infer _0 extends 'boolean', infer Input extends string] + ? [S.TKeywordBooleanMapping<_0, Context>, Input] + : [] +export type TKeywordUndefined = Static.Token.Const<'undefined', Input> extends [infer _0 extends 'undefined', infer Input extends string] + ? [S.TKeywordUndefinedMapping<_0, Context>, Input] + : [] +export type TKeywordNull = Static.Token.Const<'null', Input> extends [infer _0 extends 'null', infer Input extends string] ? [S.TKeywordNullMapping<_0, Context>, Input] : [] +export type TKeywordInteger = Static.Token.Const<'integer', Input> extends [infer _0 extends 'integer', infer Input extends string] + ? [S.TKeywordIntegerMapping<_0, Context>, Input] + : [] +export type TKeywordBigInt = Static.Token.Const<'bigint', Input> extends [infer _0 extends 'bigint', infer Input extends string] ? [S.TKeywordBigIntMapping<_0, Context>, Input] : [] +export type TKeywordUnknown = Static.Token.Const<'unknown', Input> extends [infer _0 extends 'unknown', infer Input extends string] + ? [S.TKeywordUnknownMapping<_0, Context>, Input] + : [] +export type TKeywordAny = Static.Token.Const<'any', Input> extends [infer _0 extends 'any', infer Input extends string] ? [S.TKeywordAnyMapping<_0, Context>, Input] : [] +export type TKeywordNever = Static.Token.Const<'never', Input> extends [infer _0 extends 'never', infer Input extends string] ? [S.TKeywordNeverMapping<_0, Context>, Input] : [] +export type TKeywordSymbol = Static.Token.Const<'symbol', Input> extends [infer _0 extends 'symbol', infer Input extends string] ? [S.TKeywordSymbolMapping<_0, Context>, Input] : [] +export type TKeywordVoid = Static.Token.Const<'void', Input> extends [infer _0 extends 'void', infer Input extends string] ? [S.TKeywordVoidMapping<_0, Context>, Input] : [] +export type TKeyword = ( + TKeywordString extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordNumber extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordBoolean extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordUndefined extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordNull extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordInteger extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordBigInt extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordUnknown extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordAny extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordNever extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordSymbol extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeywordVoid extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends unknown, infer Input extends string] + ? [S.TKeywordMapping<_0, Context>, Input] + : [] +export type TLiteralString = Static.Token.String<["'", '"', '`'], Input> extends [infer _0 extends string, infer Input extends string] + ? [S.TLiteralStringMapping<_0, Context>, Input] + : [] +export type TLiteralNumber = Static.Token.Number extends [infer _0 extends string, infer Input extends string] ? [S.TLiteralNumberMapping<_0, Context>, Input] : [] +export type TLiteralBoolean = ( + Static.Token.Const<'true', Input> extends [infer _0, infer Input extends string] ? [_0, Input] : Static.Token.Const<'false', Input> extends [infer _0, infer Input extends string] ? [_0, Input] : [] +) extends [infer _0 extends 'true' | 'false', infer Input extends string] + ? [S.TLiteralBooleanMapping<_0, Context>, Input] + : [] +export type TLiteral = ( + TLiteralBoolean extends [infer _0, infer Input extends string] + ? [_0, Input] + : TLiteralNumber extends [infer _0, infer Input extends string] + ? [_0, Input] + : TLiteralString extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends unknown, infer Input extends string] + ? [S.TLiteralMapping<_0, Context>, Input] + : [] +export type TKeyOf = ( + (Static.Token.Const<'keyof', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown] | [], infer Input extends string] + ? [S.TKeyOfMapping<_0, Context>, Input] + : [] +export type TIndexArray_0 = ( + ( + Static.Token.Const<'[', Input> extends [infer _0, infer Input extends string] + ? TType extends [infer _1, infer Input extends string] + ? Static.Token.Const<']', Input> extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] + ) extends [infer _0, infer Input extends string] + ? [_0, Input] + : (Static.Token.Const<'[', Input> extends [infer _0, infer Input extends string] ? (Static.Token.Const<']', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : []) extends [ + infer _0, + infer Input extends string, + ] + ? [_0, Input] + : [] +) extends [infer _0, infer Input extends string] + ? TIndexArray_0 + : [Result, Input] +export type TIndexArray = TIndexArray_0 extends [infer _0 extends ([unknown, unknown, unknown] | [unknown, unknown])[], infer Input extends string] + ? [S.TIndexArrayMapping<_0, Context>, Input] + : [] +export type TExtends = ( + ( + Static.Token.Const<'extends', Input> extends [infer _0, infer Input extends string] + ? TType extends [infer _1, infer Input extends string] + ? Static.Token.Const<'?', Input> extends [infer _2, infer Input extends string] + ? TType extends [infer _3, infer Input extends string] + ? Static.Token.Const<':', Input> extends [infer _4, infer Input extends string] + ? TType extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] + ) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown] | [], infer Input extends string] + ? [S.TExtendsMapping<_0, Context>, Input] + : [] +export type TBase = ( + ( + Static.Token.Const<'(', Input> extends [infer _0, infer Input extends string] + ? TType extends [infer _1, infer Input extends string] + ? Static.Token.Const<')', Input> extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] + ) extends [infer _0, infer Input extends string] + ? [_0, Input] + : TKeyword extends [infer _0, infer Input extends string] + ? [_0, Input] + : TObject extends [infer _0, infer Input extends string] + ? [_0, Input] + : TTuple extends [infer _0, infer Input extends string] + ? [_0, Input] + : TLiteral extends [infer _0, infer Input extends string] + ? [_0, Input] + : TConstructor extends [infer _0, infer Input extends string] + ? [_0, Input] + : TFunction extends [infer _0, infer Input extends string] + ? [_0, Input] + : TMapped extends [infer _0, infer Input extends string] + ? [_0, Input] + : TAsyncIterator extends [infer _0, infer Input extends string] + ? [_0, Input] + : TIterator extends [infer _0, infer Input extends string] + ? [_0, Input] + : TConstructorParameters extends [infer _0, infer Input extends string] + ? [_0, Input] + : TFunctionParameters extends [infer _0, infer Input extends string] + ? [_0, Input] + : TInstanceType extends [infer _0, infer Input extends string] + ? [_0, Input] + : TReturnType extends [infer _0, infer Input extends string] + ? [_0, Input] + : TArgument extends [infer _0, infer Input extends string] + ? [_0, Input] + : TAwaited extends [infer _0, infer Input extends string] + ? [_0, Input] + : TArray extends [infer _0, infer Input extends string] + ? [_0, Input] + : TRecord extends [infer _0, infer Input extends string] + ? [_0, Input] + : TPromise extends [infer _0, infer Input extends string] + ? [_0, Input] + : TPartial extends [infer _0, infer Input extends string] + ? [_0, Input] + : TRequired extends [infer _0, infer Input extends string] + ? [_0, Input] + : TPick extends [infer _0, infer Input extends string] + ? [_0, Input] + : TOmit extends [infer _0, infer Input extends string] + ? [_0, Input] + : TExclude extends [infer _0, infer Input extends string] + ? [_0, Input] + : TExtract extends [infer _0, infer Input extends string] + ? [_0, Input] + : TUppercase extends [infer _0, infer Input extends string] + ? [_0, Input] + : TLowercase extends [infer _0, infer Input extends string] + ? [_0, Input] + : TCapitalize extends [infer _0, infer Input extends string] + ? [_0, Input] + : TUncapitalize extends [infer _0, infer Input extends string] + ? [_0, Input] + : TDate extends [infer _0, infer Input extends string] + ? [_0, Input] + : TUint8Array extends [infer _0, infer Input extends string] + ? [_0, Input] + : TGenericReference extends [infer _0, infer Input extends string] + ? [_0, Input] + : TReference extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown, unknown, unknown] | unknown, infer Input extends string] + ? [S.TBaseMapping<_0, Context>, Input] + : [] +export type TFactor = ( + TKeyOf extends [infer _0, infer Input extends string] + ? TBase extends [infer _1, infer Input extends string] + ? TIndexArray extends [infer _2, infer Input extends string] + ? TExtends extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TFactorMapping<_0, Context>, Input] + : [] +export type TExprTermTail = ( + ( + Static.Token.Const<'&', Input> extends [infer _0, infer Input extends string] + ? TFactor extends [infer _1, infer Input extends string] + ? TExprTermTail extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] + ) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown, unknown, unknown] | [], infer Input extends string] + ? [S.TExprTermTailMapping<_0, Context>, Input] + : [] +export type TExprTerm = ( + TFactor extends [infer _0, infer Input extends string] ? (TExprTermTail extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TExprTermMapping<_0, Context>, Input] + : [] +export type TExprTail = ( + ( + Static.Token.Const<'|', Input> extends [infer _0, infer Input extends string] + ? TExprTerm extends [infer _1, infer Input extends string] + ? TExprTail extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] + ) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown, unknown, unknown] | [], infer Input extends string] + ? [S.TExprTailMapping<_0, Context>, Input] + : [] +export type TExpr = ( + TExprTerm extends [infer _0, infer Input extends string] ? (TExprTail extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TExprMapping<_0, Context>, Input] + : [] +export type TType = ( + TGenericArguments extends [infer _0 extends T.TProperties, infer Input extends string] + ? TExpr + : [] extends [infer _0, infer Input extends string] + ? [_0, Input] + : TExpr extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends unknown, infer Input extends string] + ? [S.TTypeMapping<_0, Context>, Input] + : [] +export type TPropertyKey = ( + Static.Token.Ident extends [infer _0, infer Input extends string] ? [_0, Input] : Static.Token.String<["'", '"'], Input> extends [infer _0, infer Input extends string] ? [_0, Input] : [] +) extends [infer _0 extends string, infer Input extends string] + ? [S.TPropertyKeyMapping<_0, Context>, Input] + : [] +export type TReadonly = ( + (Static.Token.Const<'readonly', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown] | [], infer Input extends string] + ? [S.TReadonlyMapping<_0, Context>, Input] + : [] +export type TOptional = ( + (Static.Token.Const<'?', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown] | [], infer Input extends string] + ? [S.TOptionalMapping<_0, Context>, Input] + : [] +export type TProperty = ( + TReadonly extends [infer _0, infer Input extends string] + ? TPropertyKey extends [infer _1, infer Input extends string] + ? TOptional extends [infer _2, infer Input extends string] + ? Static.Token.Const<':', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? [[_0, _1, _2, _3, _4], Input] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TPropertyMapping<_0, Context>, Input] + : [] +export type TPropertyDelimiter = ( + (Static.Token.Const<',', Input> extends [infer _0, infer Input extends string] ? (Static.Token.Const<'\n', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : []) extends [ + infer _0, + infer Input extends string, + ] + ? [_0, Input] + : (Static.Token.Const<';', Input> extends [infer _0, infer Input extends string] ? (Static.Token.Const<'\n', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : []) extends [ + infer _0, + infer Input extends string, + ] + ? [_0, Input] + : (Static.Token.Const<',', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : (Static.Token.Const<';', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : (Static.Token.Const<'\n', Input> extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] +) extends [infer _0 extends [unknown, unknown] | [unknown], infer Input extends string] + ? [S.TPropertyDelimiterMapping<_0, Context>, Input] + : [] +export type TPropertyList_0 = ( + TProperty extends [infer _0, infer Input extends string] ? (TPropertyDelimiter extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0, infer Input extends string] + ? TPropertyList_0 + : [Result, Input] +export type TPropertyList = ( + TPropertyList_0 extends [infer _0, infer Input extends string] + ? ( + (TProperty extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] + ) extends [infer _1, infer Input extends string] + ? [[_0, _1], Input] + : [] + : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TPropertyListMapping<_0, Context>, Input] + : [] +export type TObject = ( + Static.Token.Const<'{', Input> extends [infer _0, infer Input extends string] + ? TPropertyList extends [infer _1, infer Input extends string] + ? Static.Token.Const<'}', Input> extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown], infer Input extends string] + ? [S.TObjectMapping<_0, Context>, Input] + : [] +export type TElementList_0 = ( + TType extends [infer _0, infer Input extends string] ? (Static.Token.Const<',', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0, infer Input extends string] + ? TElementList_0 + : [Result, Input] +export type TElementList = ( + TElementList_0 extends [infer _0, infer Input extends string] + ? ( + (TType extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : [] + ) extends [infer _1, infer Input extends string] + ? [[_0, _1], Input] + : [] + : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TElementListMapping<_0, Context>, Input] + : [] +export type TTuple = ( + Static.Token.Const<'[', Input> extends [infer _0, infer Input extends string] + ? TElementList extends [infer _1, infer Input extends string] + ? Static.Token.Const<']', Input> extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown], infer Input extends string] + ? [S.TTupleMapping<_0, Context>, Input] + : [] +export type TParameter = ( + Static.Token.Ident extends [infer _0, infer Input extends string] + ? Static.Token.Const<':', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? [[_0, _1, _2], Input] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown], infer Input extends string] + ? [S.TParameterMapping<_0, Context>, Input] + : [] +export type TParameterList_0 = ( + TParameter extends [infer _0, infer Input extends string] ? (Static.Token.Const<',', Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : [] +) extends [infer _0, infer Input extends string] + ? TParameterList_0 + : [Result, Input] +export type TParameterList = ( + TParameterList_0 extends [infer _0, infer Input extends string] + ? ( + (TParameter extends [infer _0, infer Input extends string] ? [[_0], Input] : []) extends [infer _0, infer Input extends string] + ? [_0, Input] + : [[], Input] extends [infer _0, infer Input extends string] + ? [_0, Input] + : [] + ) extends [infer _1, infer Input extends string] + ? [[_0, _1], Input] + : [] + : [] +) extends [infer _0 extends [unknown, unknown], infer Input extends string] + ? [S.TParameterListMapping<_0, Context>, Input] + : [] +export type TFunction = ( + Static.Token.Const<'(', Input> extends [infer _0, infer Input extends string] + ? TParameterList extends [infer _1, infer Input extends string] + ? Static.Token.Const<')', Input> extends [infer _2, infer Input extends string] + ? Static.Token.Const<'=>', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? [[_0, _1, _2, _3, _4], Input] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TFunctionMapping<_0, Context>, Input] + : [] +export type TConstructor = ( + Static.Token.Const<'new', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'(', Input> extends [infer _1, infer Input extends string] + ? TParameterList extends [infer _2, infer Input extends string] + ? Static.Token.Const<')', Input> extends [infer _3, infer Input extends string] + ? Static.Token.Const<'=>', Input> extends [infer _4, infer Input extends string] + ? TType extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TConstructorMapping<_0, Context>, Input] + : [] +export type TMapped = ( + Static.Token.Const<'{', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'[', Input> extends [infer _1, infer Input extends string] + ? Static.Token.Ident extends [infer _2, infer Input extends string] + ? Static.Token.Const<'in', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<']', Input> extends [infer _5, infer Input extends string] + ? Static.Token.Const<':', Input> extends [infer _6, infer Input extends string] + ? TType extends [infer _7, infer Input extends string] + ? Static.Token.Const<'}', Input> extends [infer _8, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5, _6, _7, _8], Input] + : [] + : [] + : [] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TMappedMapping<_0, Context>, Input] + : [] +export type TAsyncIterator = ( + Static.Token.Const<'AsyncIterator', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TAsyncIteratorMapping<_0, Context>, Input] + : [] +export type TIterator = ( + Static.Token.Const<'Iterator', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TIteratorMapping<_0, Context>, Input] + : [] +export type TArgument = ( + Static.Token.Const<'Argument', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TArgumentMapping<_0, Context>, Input] + : [] +export type TAwaited = ( + Static.Token.Const<'Awaited', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TAwaitedMapping<_0, Context>, Input] + : [] +export type TArray = ( + Static.Token.Const<'Array', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TArrayMapping<_0, Context>, Input] + : [] +export type TRecord = ( + Static.Token.Const<'Record', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<',', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TRecordMapping<_0, Context>, Input] + : [] +export type TPromise = ( + Static.Token.Const<'Promise', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TPromiseMapping<_0, Context>, Input] + : [] +export type TConstructorParameters = ( + Static.Token.Const<'ConstructorParameters', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TConstructorParametersMapping<_0, Context>, Input] + : [] +export type TFunctionParameters = ( + Static.Token.Const<'Parameters', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TFunctionParametersMapping<_0, Context>, Input] + : [] +export type TInstanceType = ( + Static.Token.Const<'InstanceType', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TInstanceTypeMapping<_0, Context>, Input] + : [] +export type TReturnType = ( + Static.Token.Const<'ReturnType', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TReturnTypeMapping<_0, Context>, Input] + : [] +export type TPartial = ( + Static.Token.Const<'Partial', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TPartialMapping<_0, Context>, Input] + : [] +export type TRequired = ( + Static.Token.Const<'Required', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TRequiredMapping<_0, Context>, Input] + : [] +export type TPick = ( + Static.Token.Const<'Pick', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<',', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TPickMapping<_0, Context>, Input] + : [] +export type TOmit = ( + Static.Token.Const<'Omit', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<',', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TOmitMapping<_0, Context>, Input] + : [] +export type TExclude = ( + Static.Token.Const<'Exclude', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<',', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TExcludeMapping<_0, Context>, Input] + : [] +export type TExtract = ( + Static.Token.Const<'Extract', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<',', Input> extends [infer _3, infer Input extends string] + ? TType extends [infer _4, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _5, infer Input extends string] + ? [[_0, _1, _2, _3, _4, _5], Input] + : [] + : [] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TExtractMapping<_0, Context>, Input] + : [] +export type TUppercase = ( + Static.Token.Const<'Uppercase', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TUppercaseMapping<_0, Context>, Input] + : [] +export type TLowercase = ( + Static.Token.Const<'Lowercase', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TLowercaseMapping<_0, Context>, Input] + : [] +export type TCapitalize = ( + Static.Token.Const<'Capitalize', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TCapitalizeMapping<_0, Context>, Input] + : [] +export type TUncapitalize = ( + Static.Token.Const<'Uncapitalize', Input> extends [infer _0, infer Input extends string] + ? Static.Token.Const<'<', Input> extends [infer _1, infer Input extends string] + ? TType extends [infer _2, infer Input extends string] + ? Static.Token.Const<'>', Input> extends [infer _3, infer Input extends string] + ? [[_0, _1, _2, _3], Input] + : [] + : [] + : [] + : [] +) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] + ? [S.TUncapitalizeMapping<_0, Context>, Input] + : [] +export type TDate = Static.Token.Const<'Date', Input> extends [infer _0 extends 'Date', infer Input extends string] ? [S.TDateMapping<_0, Context>, Input] : [] +export type TUint8Array = Static.Token.Const<'Uint8Array', Input> extends [infer _0 extends 'Uint8Array', infer Input extends string] + ? [S.TUint8ArrayMapping<_0, Context>, Input] + : [] +export type TReference = Static.Token.Ident extends [infer _0 extends string, infer Input extends string] ? [S.TReferenceMapping<_0, Context>, Input] : [] +const If = (result: [unknown, string] | [], left: (input: [unknown, string]) => [unknown, string] | [], right: () => [unknown, string] | [] = () => []): [unknown, string] | [] => (result.length === 2 ? left(result) : right()) + +export const GenericReferenceParameterList_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If(Type(input, context), ([_0, input]) => If(Runtime.Token.Const(',', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => GenericReferenceParameterList_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const GenericReferenceParameterList = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(GenericReferenceParameterList_0(input, context), ([_0, input]) => + If( + If( + If(Type(input, context), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_1, input]) => [[_0, _1], input], + ), + ), + ([_0, input]) => [S.GenericReferenceParameterListMapping(_0 as [unknown, unknown], context), input], + ) +export const GenericReference = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Ident(input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(GenericReferenceParameterList(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.GenericReferenceMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const GenericArgumentsList_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If(Runtime.Token.Ident(input), ([_0, input]) => If(Runtime.Token.Const(',', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => GenericArgumentsList_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const GenericArgumentsList = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(GenericArgumentsList_0(input, context), ([_0, input]) => + If( + If( + If(Runtime.Token.Ident(input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_1, input]) => [[_0, _1], input], + ), + ), + ([_0, input]) => [S.GenericArgumentsListMapping(_0 as [unknown, unknown], context), input], + ) +export const GenericArguments = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('<', input), ([_0, input]) => If(GenericArgumentsList(input, context), ([_1, input]) => If(Runtime.Token.Const('>', input), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [S.GenericArgumentsMapping(_0 as [unknown, unknown, unknown], context), input], + ) +export const KeywordString = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('string', input), ([_0, input]) => [S.KeywordStringMapping(_0 as 'string', context), input]) +export const KeywordNumber = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('number', input), ([_0, input]) => [S.KeywordNumberMapping(_0 as 'number', context), input]) +export const KeywordBoolean = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('boolean', input), ([_0, input]) => [S.KeywordBooleanMapping(_0 as 'boolean', context), input]) +export const KeywordUndefined = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('undefined', input), ([_0, input]) => [S.KeywordUndefinedMapping(_0 as 'undefined', context), input]) +export const KeywordNull = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('null', input), ([_0, input]) => [S.KeywordNullMapping(_0 as 'null', context), input]) +export const KeywordInteger = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('integer', input), ([_0, input]) => [S.KeywordIntegerMapping(_0 as 'integer', context), input]) +export const KeywordBigInt = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('bigint', input), ([_0, input]) => [S.KeywordBigIntMapping(_0 as 'bigint', context), input]) +export const KeywordUnknown = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('unknown', input), ([_0, input]) => [S.KeywordUnknownMapping(_0 as 'unknown', context), input]) +export const KeywordAny = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('any', input), ([_0, input]) => [S.KeywordAnyMapping(_0 as 'any', context), input]) +export const KeywordNever = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('never', input), ([_0, input]) => [S.KeywordNeverMapping(_0 as 'never', context), input]) +export const KeywordSymbol = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('symbol', input), ([_0, input]) => [S.KeywordSymbolMapping(_0 as 'symbol', context), input]) +export const KeywordVoid = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('void', input), ([_0, input]) => [S.KeywordVoidMapping(_0 as 'void', context), input]) +export const Keyword = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + KeywordString(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordNumber(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordBoolean(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordUndefined(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordNull(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordInteger(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordBigInt(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordUnknown(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordAny(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordNever(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordSymbol(input, context), + ([_0, input]) => [_0, input], + () => + If( + KeywordVoid(input, context), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ([_0, input]) => [S.KeywordMapping(_0 as unknown, context), input], + ) +export const LiteralString = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.String(["'", '"', '`'], input), ([_0, input]) => [S.LiteralStringMapping(_0 as string, context), input]) +export const LiteralNumber = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Number(input), ([_0, input]) => [S.LiteralNumberMapping(_0 as string, context), input]) +export const LiteralBoolean = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + Runtime.Token.Const('true', input), + ([_0, input]) => [_0, input], + () => + If( + Runtime.Token.Const('false', input), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.LiteralBooleanMapping(_0 as 'true' | 'false', context), input], + ) +export const Literal = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + LiteralBoolean(input, context), + ([_0, input]) => [_0, input], + () => + If( + LiteralNumber(input, context), + ([_0, input]) => [_0, input], + () => + If( + LiteralString(input, context), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ), + ([_0, input]) => [S.LiteralMapping(_0 as unknown, context), input], + ) +export const KeyOf = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('keyof', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.KeyOfMapping(_0 as [unknown] | [], context), input], + ) +export const IndexArray_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If( + If(Runtime.Token.Const('[', input), ([_0, input]) => If(Type(input, context), ([_1, input]) => If(Runtime.Token.Const(']', input), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [_0, input], + () => + If( + If(Runtime.Token.Const('[', input), ([_0, input]) => If(Runtime.Token.Const(']', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => IndexArray_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const IndexArray = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If(IndexArray_0(input, context), ([_0, input]) => [S.IndexArrayMapping(_0 as ([unknown, unknown, unknown] | [unknown, unknown])[], context), input]) +export const Extends = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('extends', input), ([_0, input]) => + If(Type(input, context), ([_1, input]) => + If(Runtime.Token.Const('?', input), ([_2, input]) => If(Type(input, context), ([_3, input]) => If(Runtime.Token.Const(':', input), ([_4, input]) => If(Type(input, context), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.ExtendsMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown] | [], context), input], + ) +export const Base = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('(', input), ([_0, input]) => If(Type(input, context), ([_1, input]) => If(Runtime.Token.Const(')', input), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [_0, input], + () => + If( + Keyword(input, context), + ([_0, input]) => [_0, input], + () => + If( + _Object(input, context), + ([_0, input]) => [_0, input], + () => + If( + Tuple(input, context), + ([_0, input]) => [_0, input], + () => + If( + Literal(input, context), + ([_0, input]) => [_0, input], + () => + If( + Constructor(input, context), + ([_0, input]) => [_0, input], + () => + If( + Function(input, context), + ([_0, input]) => [_0, input], + () => + If( + Mapped(input, context), + ([_0, input]) => [_0, input], + () => + If( + AsyncIterator(input, context), + ([_0, input]) => [_0, input], + () => + If( + Iterator(input, context), + ([_0, input]) => [_0, input], + () => + If( + ConstructorParameters(input, context), + ([_0, input]) => [_0, input], + () => + If( + FunctionParameters(input, context), + ([_0, input]) => [_0, input], + () => + If( + InstanceType(input, context), + ([_0, input]) => [_0, input], + () => + If( + ReturnType(input, context), + ([_0, input]) => [_0, input], + () => + If( + Argument(input, context), + ([_0, input]) => [_0, input], + () => + If( + Awaited(input, context), + ([_0, input]) => [_0, input], + () => + If( + Array(input, context), + ([_0, input]) => [_0, input], + () => + If( + Record(input, context), + ([_0, input]) => [_0, input], + () => + If( + Promise(input, context), + ([_0, input]) => [_0, input], + () => + If( + Partial(input, context), + ([_0, input]) => [_0, input], + () => + If( + Required(input, context), + ([_0, input]) => [_0, input], + () => + If( + Pick(input, context), + ([_0, input]) => [_0, input], + () => + If( + Omit(input, context), + ([_0, input]) => [_0, input], + () => + If( + Exclude(input, context), + ([_0, input]) => [_0, input], + () => + If( + Extract(input, context), + ([_0, input]) => [_0, input], + () => + If( + Uppercase(input, context), + ([_0, input]) => [_0, input], + () => + If( + Lowercase(input, context), + ([_0, input]) => [_0, input], + () => + If( + Capitalize(input, context), + ([_0, input]) => [_0, input], + () => + If( + Uncapitalize(input, context), + ([_0, input]) => [_0, input], + () => + If( + Date(input, context), + ([_0, input]) => [_0, input], + () => + If( + Uint8Array(input, context), + ([_0, input]) => [_0, input], + () => + If( + GenericReference(input, context), + ([_0, input]) => [_0, input], + () => + If( + Reference(input, context), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ), + ([_0, input]) => [S.BaseMapping(_0 as [unknown, unknown, unknown] | unknown, context), input], + ) +export const Factor = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(KeyOf(input, context), ([_0, input]) => If(Base(input, context), ([_1, input]) => If(IndexArray(input, context), ([_2, input]) => If(Extends(input, context), ([_3, input]) => [[_0, _1, _2, _3], input])))), + ([_0, input]) => [S.FactorMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const ExprTermTail = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('&', input), ([_0, input]) => If(Factor(input, context), ([_1, input]) => If(ExprTermTail(input, context), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.ExprTermTailMapping(_0 as [unknown, unknown, unknown] | [], context), input], + ) +export const ExprTerm = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Factor(input, context), ([_0, input]) => If(ExprTermTail(input, context), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => [S.ExprTermMapping(_0 as [unknown, unknown], context), input], + ) +export const ExprTail = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('|', input), ([_0, input]) => If(ExprTerm(input, context), ([_1, input]) => If(ExprTail(input, context), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.ExprTailMapping(_0 as [unknown, unknown, unknown] | [], context), input], + ) +export const Expr = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(ExprTerm(input, context), ([_0, input]) => If(ExprTail(input, context), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => [S.ExprMapping(_0 as [unknown, unknown], context), input], + ) +export const Type = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If( + GenericArguments(input, context), + ([_0, input]) => Expr(input, _0 as T.TProperties), + () => [], + ), + ([_0, input]) => [_0, input], + () => + If( + Expr(input, context), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.TypeMapping(_0 as unknown, context), input], + ) +export const PropertyKey = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + Runtime.Token.Ident(input), + ([_0, input]) => [_0, input], + () => + If( + Runtime.Token.String(["'", '"'], input), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.PropertyKeyMapping(_0 as string, context), input], + ) +export const Readonly = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('readonly', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.ReadonlyMapping(_0 as [unknown] | [], context), input], + ) +export const Optional = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const('?', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_0, input]) => [S.OptionalMapping(_0 as [unknown] | [], context), input], + ) +export const Property = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Readonly(input, context), ([_0, input]) => + If(PropertyKey(input, context), ([_1, input]) => If(Optional(input, context), ([_2, input]) => If(Runtime.Token.Const(':', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => [[_0, _1, _2, _3, _4], input])))), + ), + ([_0, input]) => [S.PropertyMapping(_0 as [unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const PropertyDelimiter = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If( + If(Runtime.Token.Const(',', input), ([_0, input]) => If(Runtime.Token.Const('\n', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => [_0, input], + () => + If( + If(Runtime.Token.Const(';', input), ([_0, input]) => If(Runtime.Token.Const('\n', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => [_0, input], + () => + If( + If(Runtime.Token.Const(',', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + If(Runtime.Token.Const(';', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + If(Runtime.Token.Const('\n', input), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => [], + ), + ), + ), + ), + ), + ([_0, input]) => [S.PropertyDelimiterMapping(_0 as [unknown, unknown] | [unknown], context), input], + ) +export const PropertyList_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If(Property(input, context), ([_0, input]) => If(PropertyDelimiter(input, context), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => PropertyList_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const PropertyList = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(PropertyList_0(input, context), ([_0, input]) => + If( + If( + If(Property(input, context), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_1, input]) => [[_0, _1], input], + ), + ), + ([_0, input]) => [S.PropertyListMapping(_0 as [unknown, unknown], context), input], + ) +export const _Object = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('{', input), ([_0, input]) => If(PropertyList(input, context), ([_1, input]) => If(Runtime.Token.Const('}', input), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [S.ObjectMapping(_0 as [unknown, unknown, unknown], context), input], + ) +export const ElementList_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If(Type(input, context), ([_0, input]) => If(Runtime.Token.Const(',', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => ElementList_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const ElementList = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(ElementList_0(input, context), ([_0, input]) => + If( + If( + If(Type(input, context), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_1, input]) => [[_0, _1], input], + ), + ), + ([_0, input]) => [S.ElementListMapping(_0 as [unknown, unknown], context), input], + ) +export const Tuple = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('[', input), ([_0, input]) => If(ElementList(input, context), ([_1, input]) => If(Runtime.Token.Const(']', input), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [S.TupleMapping(_0 as [unknown, unknown, unknown], context), input], + ) +export const Parameter = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Ident(input), ([_0, input]) => If(Runtime.Token.Const(':', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => [[_0, _1, _2], input]))), + ([_0, input]) => [S.ParameterMapping(_0 as [unknown, unknown, unknown], context), input], + ) +export const ParameterList_0 = (input: string, context: T.TProperties, result: unknown[] = []): [unknown[], string] => + If( + If(Parameter(input, context), ([_0, input]) => If(Runtime.Token.Const(',', input), ([_1, input]) => [[_0, _1], input])), + ([_0, input]) => ParameterList_0(input, context, [...result, _0]), + () => [result, input], + ) as [unknown[], string] +export const ParameterList = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(ParameterList_0(input, context), ([_0, input]) => + If( + If( + If(Parameter(input, context), ([_0, input]) => [[_0], input]), + ([_0, input]) => [_0, input], + () => + If( + [[], input], + ([_0, input]) => [_0, input], + () => [], + ), + ), + ([_1, input]) => [[_0, _1], input], + ), + ), + ([_0, input]) => [S.ParameterListMapping(_0 as [unknown, unknown], context), input], + ) +export const Function = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('(', input), ([_0, input]) => + If(ParameterList(input, context), ([_1, input]) => If(Runtime.Token.Const(')', input), ([_2, input]) => If(Runtime.Token.Const('=>', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => [[_0, _1, _2, _3, _4], input])))), + ), + ([_0, input]) => [S.FunctionMapping(_0 as [unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Constructor = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('new', input), ([_0, input]) => + If(Runtime.Token.Const('(', input), ([_1, input]) => + If(ParameterList(input, context), ([_2, input]) => + If(Runtime.Token.Const(')', input), ([_3, input]) => If(Runtime.Token.Const('=>', input), ([_4, input]) => If(Type(input, context), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input]))), + ), + ), + ), + ([_0, input]) => [S.ConstructorMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Mapped = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('{', input), ([_0, input]) => + If(Runtime.Token.Const('[', input), ([_1, input]) => + If(Runtime.Token.Ident(input), ([_2, input]) => + If(Runtime.Token.Const('in', input), ([_3, input]) => + If(Type(input, context), ([_4, input]) => + If(Runtime.Token.Const(']', input), ([_5, input]) => + If(Runtime.Token.Const(':', input), ([_6, input]) => If(Type(input, context), ([_7, input]) => If(Runtime.Token.Const('}', input), ([_8, input]) => [[_0, _1, _2, _3, _4, _5, _6, _7, _8], input]))), + ), + ), + ), + ), + ), + ), + ([_0, input]) => [S.MappedMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const AsyncIterator = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('AsyncIterator', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.AsyncIteratorMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Iterator = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Iterator', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.IteratorMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Argument = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Argument', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.ArgumentMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Awaited = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Awaited', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.AwaitedMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Array = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Array', input), ([_0, input]) => If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input])))), + ([_0, input]) => [S.ArrayMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Record = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Record', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => + If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const(',', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => If(Runtime.Token.Const('>', input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [S.RecordMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Promise = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Promise', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.PromiseMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const ConstructorParameters = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('ConstructorParameters', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.ConstructorParametersMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const FunctionParameters = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Parameters', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.FunctionParametersMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const InstanceType = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('InstanceType', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.InstanceTypeMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const ReturnType = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('ReturnType', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.ReturnTypeMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Partial = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Partial', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.PartialMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Required = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Required', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.RequiredMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Pick = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Pick', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => + If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const(',', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => If(Runtime.Token.Const('>', input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [S.PickMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Omit = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Omit', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => + If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const(',', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => If(Runtime.Token.Const('>', input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [S.OmitMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Exclude = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Exclude', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => + If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const(',', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => If(Runtime.Token.Const('>', input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [S.ExcludeMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Extract = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Extract', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => + If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const(',', input), ([_3, input]) => If(Type(input, context), ([_4, input]) => If(Runtime.Token.Const('>', input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))), + ), + ), + ([_0, input]) => [S.ExtractMapping(_0 as [unknown, unknown, unknown, unknown, unknown, unknown], context), input], + ) +export const Uppercase = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Uppercase', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.UppercaseMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Lowercase = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Lowercase', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.LowercaseMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Capitalize = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Capitalize', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.CapitalizeMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Uncapitalize = (input: string, context: T.TProperties = {}): [unknown, string] | [] => + If( + If(Runtime.Token.Const('Uncapitalize', input), ([_0, input]) => + If(Runtime.Token.Const('<', input), ([_1, input]) => If(Type(input, context), ([_2, input]) => If(Runtime.Token.Const('>', input), ([_3, input]) => [[_0, _1, _2, _3], input]))), + ), + ([_0, input]) => [S.UncapitalizeMapping(_0 as [unknown, unknown, unknown, unknown], context), input], + ) +export const Date = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('Date', input), ([_0, input]) => [S.DateMapping(_0 as 'Date', context), input]) +export const Uint8Array = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Const('Uint8Array', input), ([_0, input]) => [S.Uint8ArrayMapping(_0 as 'Uint8Array', context), input]) +export const Reference = (input: string, context: T.TProperties = {}): [unknown, string] | [] => If(Runtime.Token.Ident(input), ([_0, input]) => [S.ReferenceMapping(_0 as string, context), input]) diff --git a/src/syntax/runtime.ts b/src/syntax/runtime.ts deleted file mode 100644 index 92703afcc..000000000 --- a/src/syntax/runtime.ts +++ /dev/null @@ -1,802 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/syntax - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Runtime } from '../parser/index' -import * as t from '../type/index' - -// ------------------------------------------------------------------ -// Tokens -// ------------------------------------------------------------------ -const Newline = '\n' -const LBracket = '[' -const RBracket = ']' -const LParen = '(' -const RParen = ')' -const LBrace = '{' -const RBrace = '}' -const LAngle = '<' -const RAngle = '>' -const Question = '?' -const Colon = ':' -const Comma = ',' -const SemiColon = ';' -const SingleQuote = "'" -const DoubleQuote = '"' -const Tilde = '`' -const Equals = '=' - -// ------------------------------------------------------------------ -// DestructureRight -// ------------------------------------------------------------------ -// prettier-ignore -function DestructureRight(values: T[]): [T[], T | undefined] { - return (values.length > 0) - ? [values.slice(0, values.length - 1), values[values.length - 1]] - : [values, undefined] -} - -// ------------------------------------------------------------------ -// Delimit -// ------------------------------------------------------------------ -// prettier-ignore -const DelimitHeadMapping = (results: unknown[]) => results.reduce((result: unknown[], value) => { - const [element, _delimiter] = value as [unknown, unknown] - return [...result, element] -}, [] as unknown[]) -// prettier-ignore -const DelimitHead = (element: Element, delimiter: Delimiter) => ( - Runtime.Array(Runtime.Tuple([element, delimiter]), DelimitHeadMapping) -) -// prettier-ignore -const DelimitTail = (element: Element) => Runtime.Union([ - Runtime.Tuple([element]), - Runtime.Tuple([]), -]) -// prettier-ignore -const DelimitMapping = (results: [unknown[], unknown[]]) => { - return [...results[0], ...results[1]] -} -// prettier-ignore -const Delimit = (element: Element, delimiter: Delimiter) => Runtime.Tuple([ - DelimitHead(element, delimiter), - DelimitTail(element), -], DelimitMapping) - -// ------------------------------------------------------------------ -// Dereference -// ------------------------------------------------------------------ -const Dereference = (context: t.TProperties, key: string): t.TSchema => { - return key in context ? context[key] : t.Ref(key) -} - -// ------------------------------------------------------------------ -// GenericArgumentsList -// ------------------------------------------------------------------ -// prettier-ignore -const GenericArgumentsList = Delimit(Runtime.Ident(), Runtime.Const(Comma)) - -// ------------------------------------------------------------------ -// GenericArguments -// ------------------------------------------------------------------ -// prettier-ignore -const GenericArgumentsContext = (args: string[], context: t.TProperties) => { - return args.reduce((result, arg, index) => { - return { ...result, [arg]: t.Argument(index) } - }, context) -} -// prettier-ignore -const GenericArgumentsMapping = (results: unknown[], context: t.TProperties) => { - return results.length === 3 - ? GenericArgumentsContext(results[1] as string[], context) - : {} -} -// prettier-ignore -const GenericArguments = Runtime.Tuple([ - Runtime.Const(LAngle), - Runtime.Ref('GenericArgumentsList'), - Runtime.Const(RAngle), -], (results, context) => GenericArgumentsMapping(results, context)) - -// ------------------------------------------------------------------ -// GenericReference -// ------------------------------------------------------------------ -function GenericReferenceMapping(results: unknown[], context: t.TProperties) { - const type = Dereference(context, results[0] as string) - const args = results[2] as t.TSchema[] - return t.Instantiate(type, args) -} -const GenericReferenceParameters = Delimit(Runtime.Ref('Type'), Runtime.Const(Comma)) -// prettier-ignore -const GenericReference = Runtime.Tuple([ - Runtime.Ident(), - Runtime.Const(LAngle), - Runtime.Ref('GenericReferenceParameters'), - Runtime.Const(RAngle) -], (results, context) => GenericReferenceMapping(results, context)) - -// ------------------------------------------------------------------ -// Reference -// ------------------------------------------------------------------ -function ReferenceMapping(result: string, context: t.TProperties) { - const target = Dereference(context, result) - return target -} -// prettier-ignore -const Reference = Runtime.Ident((result, context) => ReferenceMapping(result, context)) - -// ------------------------------------------------------------------ -// Literal -// ------------------------------------------------------------------ -// prettier-ignore -const Literal = Runtime.Union([ - Runtime.Union([Runtime.Const('true'), Runtime.Const('false')], value => t.Literal(value === 'true')), - Runtime.Number(value => t.Literal(parseFloat(value))), - Runtime.String([SingleQuote, DoubleQuote, Tilde], value => t.Literal(value)) -]) - -// ------------------------------------------------------------------ -// Keyword -// ------------------------------------------------------------------ -// prettier-ignore -const Keyword = Runtime.Union([ - Runtime.Const('string', Runtime.As(t.String())), - Runtime.Const('number', Runtime.As(t.Number())), - Runtime.Const('boolean', Runtime.As(t.Boolean())), - Runtime.Const('undefined', Runtime.As(t.Undefined())), - Runtime.Const('null', Runtime.As(t.Null())), - Runtime.Const('integer', Runtime.As(t.Integer())), - Runtime.Const('bigint', Runtime.As(t.BigInt())), - Runtime.Const('unknown', Runtime.As(t.Unknown())), - Runtime.Const('any', Runtime.As(t.Any())), - Runtime.Const('never', Runtime.As(t.Never())), - Runtime.Const('symbol', Runtime.As(t.Symbol())), - Runtime.Const('void', Runtime.As(t.Void())), -]) - -// ------------------------------------------------------------------ -// KeyOf -// ------------------------------------------------------------------ -// prettier-ignore -const KeyOfMapping = (values: unknown[]) => ( - values.length > 0 -) -// prettier-ignore -const KeyOf = Runtime.Union([ - Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([]) -], KeyOfMapping) - -// ------------------------------------------------------------------ -// IndexArray -// ------------------------------------------------------------------ -// prettier-ignore -const IndexArrayMapping = (results: ([unknown, unknown, unknown] | [unknown, unknown])[]) => { - return results.reduce((result: unknown[], current) => { - return current.length === 3 - ? [...result, [current[1]]] - : [...result, []] - }, [] as unknown[]) -} -// prettier-ignore -const IndexArray = Runtime.Array(Runtime.Union([ - Runtime.Tuple([Runtime.Const(LBracket), Runtime.Ref('Type'), Runtime.Const(RBracket)]), - Runtime.Tuple([Runtime.Const(LBracket), Runtime.Const(RBracket)]), -]), IndexArrayMapping) - -// ------------------------------------------------------------------ -// Extends -// ------------------------------------------------------------------ -// prettier-ignore -const ExtendsMapping = (values: unknown[]) => { - return values.length === 6 - ? [values[1], values[3], values[5]] - : [] -} -// prettier-ignore -const Extends = Runtime.Union([ - Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]), - Runtime.Tuple([]) -], ExtendsMapping) - -// ------------------------------------------------------------------ -// Base -// ------------------------------------------------------------------ -// prettier-ignore -const BaseMapping = (value: unknown) => { - return t.ValueGuard.IsArray(value) && value.length === 3 - ? value[1] - : value -} -// prettier-ignore -const Base = Runtime.Union([ - Runtime.Tuple([Runtime.Const(LParen), Runtime.Ref('Type'), Runtime.Const(RParen)]), - Runtime.Ref('Keyword'), - Runtime.Ref('Object'), - Runtime.Ref('Tuple'), - Runtime.Ref('Literal'), - Runtime.Ref('Constructor'), - Runtime.Ref('Function'), - Runtime.Ref('Mapped'), - Runtime.Ref('AsyncIterator'), - Runtime.Ref('Iterator'), - Runtime.Ref('ConstructorParameters'), - Runtime.Ref('FunctionParameters'), - Runtime.Ref('InstanceType'), - Runtime.Ref('ReturnType'), - Runtime.Ref('Argument'), - Runtime.Ref('Awaited'), - Runtime.Ref('Array'), - Runtime.Ref('Record'), - Runtime.Ref('Promise'), - Runtime.Ref('Partial'), - Runtime.Ref('Required'), - Runtime.Ref('Pick'), - Runtime.Ref('Omit'), - Runtime.Ref('Exclude'), - Runtime.Ref('Extract'), - Runtime.Ref('Uppercase'), - Runtime.Ref('Lowercase'), - Runtime.Ref('Capitalize'), - Runtime.Ref('Uncapitalize'), - Runtime.Ref('Date'), - Runtime.Ref('Uint8Array'), - Runtime.Ref('GenericReference'), - Runtime.Ref('Reference') -], BaseMapping) - -// ------------------------------------------------------------------ -// Factor -// ------------------------------------------------------------------ -// prettier-ignore -const FactorExtends = (Type: t.TSchema, Extends: t.TSchema[]) => { - return Extends.length === 3 - ? t.Extends(Type, Extends[0], Extends[1], Extends[2]) - : Type -} -// prettier-ignore -const FactorIndexArray = (Type: t.TSchema, IndexArray: unknown[]): t.TSchema => { - const [Left, Right] = DestructureRight(IndexArray) as [unknown[], t.TSchema[]] - return ( - !t.ValueGuard.IsUndefined(Right) ? ( - // note: Indexed types require reimplementation to replace `[number]` indexers - Right.length === 1 ? t.Index(FactorIndexArray(Type, Left), Right[0]) as never : - Right.length === 0 ? t.Array(FactorIndexArray(Type, Left)) : - t.Never() - ) : Type - ) -} -// prettier-ignore -const FactorMapping = (KeyOf: boolean, Type: t.TSchema, IndexArray: unknown[], Extends: t.TSchema[]) => { - return KeyOf - ? FactorExtends(t.KeyOf(FactorIndexArray(Type, IndexArray)), Extends) - : FactorExtends(FactorIndexArray(Type, IndexArray), Extends) -} -// prettier-ignore -const Factor = Runtime.Tuple([ - Runtime.Ref('KeyOf'), - Runtime.Ref('Base'), - Runtime.Ref('IndexArray'), - Runtime.Ref('Extends') -], results => FactorMapping(...results)) - -// ------------------------------------------------------------------ -// Expr -// ------------------------------------------------------------------ -// prettier-ignore -function ExprBinaryMapping(Left: t.TSchema, Rest: unknown[]): t.TSchema { - return ( - Rest.length === 3 ? (() => { - const [Operator, Right, Next] = Rest as [string, t.TSchema, unknown[]] - const Schema = ExprBinaryMapping(Right, Next) - if (Operator === '&') { - return t.TypeGuard.IsIntersect(Schema) - ? t.Intersect([Left, ...Schema.allOf]) - : t.Intersect([Left, Schema]) - } - if (Operator === '|') { - return t.TypeGuard.IsUnion(Schema) - ? t.Union([Left, ...Schema.anyOf]) - : t.Union([Left, Schema]) - } - throw 1 - })() : Left - ) -} -// prettier-ignore -const ExprTermTail = Runtime.Union([ - Runtime.Tuple([Runtime.Const('&'), Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail')]), - Runtime.Tuple([]) -]) -// prettier-ignore -const ExprTerm = Runtime.Tuple([ - Runtime.Ref('Factor'), Runtime.Ref('ExprTermTail') -], results => ExprBinaryMapping(...results)) -// prettier-ignore -const ExprTail = Runtime.Union([ - Runtime.Tuple([Runtime.Const('|'), Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail')]), - Runtime.Tuple([]) -]) -// prettier-ignore -const Expr = Runtime.Tuple([ - Runtime.Ref('ExprTerm'), Runtime.Ref('ExprTail') -], results => ExprBinaryMapping(...results)) - -// ------------------------------------------------------------------ -// Type -// ------------------------------------------------------------------ -// prettier-ignore -const Type = Runtime.Union([ - Runtime.Context(Runtime.Ref('GenericArguments'), Runtime.Ref('Expr')), - Runtime.Ref('Expr') -]) - -// ------------------------------------------------------------------ -// Property -// ------------------------------------------------------------------ -const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])]) -const Readonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], (value) => value.length > 0) -const Optional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], (value) => value.length > 0) -// prettier-ignore -const PropertyMapping = (IsReadonly: boolean, Key: string, IsOptional: boolean, _: typeof Colon, Type: t.TSchema) => ({ - [Key]: ( - IsReadonly && IsOptional ? t.ReadonlyOptional(Type) : - IsReadonly && !IsOptional ? t.Readonly(Type) : - !IsReadonly && IsOptional ? t.Optional(Type) : - Type - ) -}) -// prettier-ignore -const Property = Runtime.Tuple([ - Runtime.Ref('Readonly'), - Runtime.Ref('PropertyKey'), - Runtime.Ref('Optional'), - Runtime.Const(Colon), - Runtime.Ref('Type'), -], results => PropertyMapping(...results)) - -// ------------------------------------------------------------------ -// PropertyDelimiter -// ------------------------------------------------------------------ -// prettier-ignore -const PropertyDelimiter = Runtime.Union([ - Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), - Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]), - Runtime.Tuple([Runtime.Const(Comma)]), - Runtime.Tuple([Runtime.Const(SemiColon)]), - Runtime.Tuple([Runtime.Const(Newline)]), -]) - -// ------------------------------------------------------------------ -// PropertyList -// ------------------------------------------------------------------ -const PropertyList = Delimit(Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')) - -// ------------------------------------------------------------------ -// Object -// ------------------------------------------------------------------ -// prettier-ignore -const ObjectMapping = (results: unknown[]) => { - const propertyList = results[1] as t.TProperties[] - return t.Object(propertyList.reduce((result, property) => { - return { ...result, ...property } - }, {} as t.TProperties)) -} -// prettier-ignore -const _Object = Runtime.Tuple([ - Runtime.Const(LBrace), - Runtime.Ref('PropertyList'), - Runtime.Const(RBrace) -], ObjectMapping) - -// ------------------------------------------------------------------ -// ElementList -// ------------------------------------------------------------------ -const ElementList = Delimit(Runtime.Ref('Type'), Runtime.Const(Comma)) - -// ------------------------------------------------------------------ -// Tuple -// ------------------------------------------------------------------ -// prettier-ignore -const Tuple = Runtime.Tuple([ - Runtime.Const(LBracket), - Runtime.Ref('ElementList'), - Runtime.Const(RBracket) -], results => t.Tuple(results[1])) - -// ------------------------------------------------------------------ -// Parameters -// ------------------------------------------------------------------ -// prettier-ignore -const Parameter = Runtime.Tuple([ - Runtime.Ident(), Runtime.Const(Colon), Runtime.Ref('Type') -], results => results[2]) - -// ------------------------------------------------------------------ -// ParameterList -// ------------------------------------------------------------------ -const ParameterList = Delimit(Runtime.Ref('Parameter'), Runtime.Const(Comma)) - -// ------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------ -// prettier-ignore -const Constructor = Runtime.Tuple([ - Runtime.Const('new'), - Runtime.Const(LParen), - Runtime.Ref('ParameterList'), - Runtime.Const(RParen), - Runtime.Const('=>'), - Runtime.Ref('Type') -], results => t.Constructor(results[2], results[5])) - -// ------------------------------------------------------------------ -// Function -// ------------------------------------------------------------------ -// prettier-ignore -const Function = Runtime.Tuple([ - Runtime.Const(LParen), - Runtime.Ref('ParameterList'), - Runtime.Const(RParen), - Runtime.Const('=>'), - Runtime.Ref('Type') -], results => t.Function(results[1], results[4])) - -// ------------------------------------------------------------------ -// Mapped (requires deferred types) -// ------------------------------------------------------------------ -// prettier-ignore -const MappedMapping = (results: unknown[]) => { - return t.Literal('Mapped types not supported') -} -// prettier-ignore -const Mapped = Runtime.Tuple([ - Runtime.Const(LBrace), - Runtime.Const(LBracket), - Runtime.Ident(), - Runtime.Const('in'), - Runtime.Ref('Type'), - Runtime.Const(RBracket), - Runtime.Const(Colon), - Runtime.Ref('Type'), - Runtime.Const(RBrace) -], MappedMapping) - -// ------------------------------------------------------------------ -// AsyncIterator -// ------------------------------------------------------------------ -// prettier-ignore -const AsyncIterator = Runtime.Tuple([ - Runtime.Const('AsyncIterator'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.AsyncIterator(results[2])) - -// ------------------------------------------------------------------ -// Iterator -// ------------------------------------------------------------------ -// prettier-ignore -const Iterator = Runtime.Tuple([ - Runtime.Const('Iterator'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Iterator(results[2])) - -// ------------------------------------------------------------------ -// ConstructorParameters -// ------------------------------------------------------------------ -// prettier-ignore -const ConstructorParameters = Runtime.Tuple([ - Runtime.Const('ConstructorParameters'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.ConstructorParameters(results[2])) - -// ------------------------------------------------------------------ -// Parameters -// ------------------------------------------------------------------ -// prettier-ignore -const FunctionParameters = Runtime.Tuple([ - Runtime.Const('Parameters'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Parameters(results[2])) - -// ------------------------------------------------------------------ -// InstanceType -// ------------------------------------------------------------------ -// prettier-ignore -const InstanceType = Runtime.Tuple([ - Runtime.Const('InstanceType'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.InstanceType(results[2])) - -// ------------------------------------------------------------------ -// ReturnType -// ------------------------------------------------------------------ -// prettier-ignore -const ReturnType = Runtime.Tuple([ - Runtime.Const('ReturnType'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.ReturnType(results[2])) - -// ------------------------------------------------------------------ -// Argument -// ------------------------------------------------------------------ -// prettier-ignore -const Argument = Runtime.Tuple([ - Runtime.Const('Argument'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => { - return t.KindGuard.IsLiteralNumber(results[2]) - ? t.Argument(Math.trunc(results[2].const)) - : t.Never() -}) - -// ------------------------------------------------------------------ -// Awaited -// ------------------------------------------------------------------ -// prettier-ignore -const Awaited = Runtime.Tuple([ - Runtime.Const('Awaited'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Awaited(results[2])) - -// ------------------------------------------------------------------ -// Array -// ------------------------------------------------------------------ -// prettier-ignore -const Array = Runtime.Tuple([ - Runtime.Const('Array'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Array(results[2])) - -// ------------------------------------------------------------------ -// Record -// ------------------------------------------------------------------ -// prettier-ignore -const Record = Runtime.Tuple([ - Runtime.Const('Record'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(Comma), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Record(results[2], results[4])) - -// ------------------------------------------------------------------ -// Promise -// ------------------------------------------------------------------ -// prettier-ignore -const Promise = Runtime.Tuple([ - Runtime.Const('Promise'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Promise(results[2])) - -// ------------------------------------------------------------------ -// Partial -// ------------------------------------------------------------------ -// prettier-ignore -const Partial = Runtime.Tuple([ - Runtime.Const('Partial'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Partial(results[2])) - -// ------------------------------------------------------------------ -// Required -// ------------------------------------------------------------------ -// prettier-ignore -const Required = Runtime.Tuple([ - Runtime.Const('Required'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Required(results[2])) - -// ------------------------------------------------------------------ -// Pick -// ------------------------------------------------------------------ -// prettier-ignore -const Pick = Runtime.Tuple([ - Runtime.Const('Pick'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(Comma), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Pick(results[2], results[4])) - -// ------------------------------------------------------------------ -// Omit -// ------------------------------------------------------------------ -// prettier-ignore -const Omit = Runtime.Tuple([ - Runtime.Const('Omit'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(Comma), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Omit(results[2], results[4])) - -// ------------------------------------------------------------------ -// Exclude -// ------------------------------------------------------------------ -// prettier-ignore -const Exclude = Runtime.Tuple([ - Runtime.Const('Exclude'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(Comma), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Exclude(results[2], results[4])) - -// ------------------------------------------------------------------ -// Extract -// ------------------------------------------------------------------ -// prettier-ignore -const Extract = Runtime.Tuple([ - Runtime.Const('Extract'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(Comma), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Extract(results[2], results[4])) - -// ------------------------------------------------------------------ -// Uppercase -// ------------------------------------------------------------------ -// prettier-ignore -const Uppercase = Runtime.Tuple([ - Runtime.Const('Uppercase'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Uppercase(results[2])) - -// ------------------------------------------------------------------ -// Lowercase -// ------------------------------------------------------------------ -// prettier-ignore -const Lowercase = Runtime.Tuple([ - Runtime.Const('Lowercase'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Lowercase(results[2])) - -// ------------------------------------------------------------------ -// Capitalize -// ------------------------------------------------------------------ -// prettier-ignore -const Capitalize = Runtime.Tuple([ - Runtime.Const('Capitalize'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Capitalize(results[2])) - -// ------------------------------------------------------------------ -// Uncapitalize -// ------------------------------------------------------------------ -// prettier-ignore -const Uncapitalize = Runtime.Tuple([ - Runtime.Const('Uncapitalize'), - Runtime.Const(LAngle), - Runtime.Ref('Type'), - Runtime.Const(RAngle), -], results => t.Uncapitalize(results[2])) - -// ------------------------------------------------------------------ -// Date -// ------------------------------------------------------------------ -const Date = Runtime.Const('Date', Runtime.As(t.Date())) - -// ------------------------------------------------------------------ -// Uint8Array -// ------------------------------------------------------------------ -const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(t.Uint8Array())) - -// ------------------------------------------------------------------ -// Module -// ------------------------------------------------------------------ -// prettier-ignore -export const Module = new Runtime.Module({ - GenericArgumentsList, - GenericArguments, - Literal, - Keyword, - KeyOf, - IndexArray, - Extends, - Base, - Factor, - ExprTermTail, - ExprTerm, - ExprTail, - Expr, - Type, - PropertyKey, - Readonly, - Optional, - Property, - PropertyDelimiter, - PropertyList, - Object: _Object, - ElementList, - Tuple, - Parameter, - ParameterList, - Function, - Constructor, - Mapped, - AsyncIterator, - Iterator, - Argument, - Awaited, - Array, - Record, - Promise, - ConstructorParameters, - FunctionParameters, - InstanceType, - ReturnType, - Partial, - Required, - Pick, - Omit, - Exclude, - Extract, - Uppercase, - Lowercase, - Capitalize, - Uncapitalize, - Date, - Uint8Array, - GenericReferenceParameters, - GenericReference, - Reference, -}) diff --git a/src/syntax/static.ts b/src/syntax/static.ts deleted file mode 100644 index 609fc3d7b..000000000 --- a/src/syntax/static.ts +++ /dev/null @@ -1,836 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/syntax - -The MIT License (MIT) - -Copyright (c) 2017-2025 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { Static } from '../parser/index' -import * as t from '../type/index' - -// ------------------------------------------------------------------ -// Tokens -// ------------------------------------------------------------------ -type Newline = '\n' -type LBracket = '[' -type RBracket = ']' -type LParen = '(' -type RParen = ')' -type LBrace = '{' -type RBrace = '}' -type LAngle = '<' -type RAngle = '>' -type Question = '?' -type Colon = ':' -type Comma = ',' -type SemiColon = ';' -type SingleQuote = "'" -type DoubleQuote = '"' -type Tilde = '`' -type Equals = '=' - -// ------------------------------------------------------------------ -// Delimit -// ------------------------------------------------------------------ -// prettier-ignore -type DelimitHeadReduce = ( - Elements extends [infer Left extends unknown, ...infer Right extends unknown[]] - ? Left extends [infer Element, infer _Delimiter] - ? DelimitHeadReduce - : DelimitHeadReduce - : Result -) -// prettier-ignore -interface DelimitHeadMapping extends Static.IMapping { - output: this['input'] extends unknown[] - ? DelimitHeadReduce - : [] -} -// prettier-ignore -type DelimitHead = ( - Static.Array, DelimitHeadMapping> -) -// prettier-ignore -type DelimitTail = Static.Union<[ - Static.Tuple<[Element]>, - Static.Tuple<[]>, -]> -// prettier-ignore -interface DelimitMapping extends Static.IMapping { - output: this['input'] extends [infer Left extends unknown[], infer Right extends unknown[]] - ? [...Left, ...Right] - : [] -} -// prettier-ignore -type Delimit = Static.Tuple<[ - DelimitHead, - DelimitTail -], DelimitMapping> - -// ------------------------------------------------------------------ -// Dereference -// ------------------------------------------------------------------ -// prettier-ignore -type Dereference = ( - Ref extends keyof Context ? Context[Ref] : t.TRef -) - -// ------------------------------------------------------------------ -// GenericArguments -// ------------------------------------------------------------------ -// prettier-ignore -type GenericArgumentsContext = ( - Args extends [...infer Left extends string[], infer Right extends string] - ? GenericArgumentsContext }> - : t.Evaluate -) -// prettier-ignore -interface GenericArgumentsMapping extends Static.IMapping { - output: this['input'] extends [LAngle, infer Args extends string[], RAngle] - ? this['context'] extends infer Context extends t.TProperties - ? GenericArgumentsContext - : never - : never -} -type GenericArgumentsList = Delimit> -// prettier-ignore -type GenericArguments = Static.Tuple<[ - Static.Const, - GenericArgumentsList, - Static.Const, -], GenericArgumentsMapping> - -// ------------------------------------------------------------------ -// GenericReference -// ------------------------------------------------------------------ -// prettier-ignore -interface GenericReferenceMapping extends Static.IMapping { - output: this['context'] extends t.TProperties - ? this['input'] extends [infer Reference extends string, LAngle, infer Args extends t.TSchema[], RAngle] - ? t.TInstantiate, Args> - : never - : never -} -type GenericReferenceParameters = Delimit> -// prettier-ignore -type GenericReference = Static.Tuple<[ - Static.Ident, - Static.Const, - GenericReferenceParameters, - Static.Const, -], GenericReferenceMapping> - -// ------------------------------------------------------------------ -// Reference -// ------------------------------------------------------------------ -// prettier-ignore -interface ReferenceMapping extends Static.IMapping { - output: this['context'] extends t.TProperties - ? this['input'] extends string - ? Dereference - : never - : never -} -type Reference = Static.Ident - -// ------------------------------------------------------------------ -// Literal -// ------------------------------------------------------------------ -// prettier-ignore -interface LiteralBooleanMapping extends Static.IMapping { - output: this['input'] extends `${infer Value extends boolean}` ? t.TLiteral : never -} -// prettier-ignore -interface LiteralNumberMapping extends Static.IMapping { - output: this['input'] extends `${infer Value extends number}` ? t.TLiteral : never -} -// prettier-ignore -interface LiteralStringMapping extends Static.IMapping { - output: this['input'] extends `${infer Value extends string}` ? t.TLiteral : never -} -// prettier-ignore -type Literal = Static.Union<[ - Static.Union<[Static.Const<'true'>, Static.Const<'false'>], LiteralBooleanMapping>, - Static.Number, - Static.String<[DoubleQuote, SingleQuote, Tilde], LiteralStringMapping>, -]> - -// ------------------------------------------------------------------ -// Keyword -// ------------------------------------------------------------------ -// prettier-ignore -type Keyword = Static.Union<[ - Static.Const<'string', Static.As>, - Static.Const<'number', Static.As>, - Static.Const<'boolean', Static.As>, - Static.Const<'undefined', Static.As>, - Static.Const<'null', Static.As>, - Static.Const<'integer', Static.As>, - Static.Const<'bigint', Static.As>, - Static.Const<'unknown', Static.As>, - Static.Const<'any', Static.As>, - Static.Const<'never', Static.As>, - Static.Const<'symbol', Static.As>, - Static.Const<'void', Static.As>, -]> - -// ------------------------------------------------------------------ -// KeyOf -// ------------------------------------------------------------------ -// prettier-ignore -interface KeyOfMapping extends Static.IMapping { - output: this['input'] extends [] ? false : true -} -// prettier-ignore -type KeyOf = Static.Union<[ - Static.Tuple<[Static.Const<'keyof'>]>, - Static.Tuple<[]> -], KeyOfMapping> - -// ------------------------------------------------------------------ -// IndexArray -// ------------------------------------------------------------------ -// prettier-ignore -type IndexArrayReduce = ( - Values extends [infer Left extends unknown, ...infer Right extends unknown[]] - ? Left extends [LBracket, infer Type extends t.TSchema, RBracket] - ? IndexArrayReduce - : IndexArrayReduce - : Result -) -// prettier-ignore -interface IndexArrayMapping extends Static.IMapping { - output: this['input'] extends unknown[] - ? IndexArrayReduce - : [] -} -// prettier-ignore -type IndexArray = Static.Array, Type, Static.Const,]>, - Static.Tuple<[Static.Const, Static.Const]>, -]>, IndexArrayMapping> - -// ------------------------------------------------------------------ -// Extends -// ------------------------------------------------------------------ -// prettier-ignore -interface ExtendsMapping extends Static.IMapping { - output: this['input'] extends ['extends', infer Type extends t.TSchema, Question, infer True extends t.TSchema, Colon, infer False extends t.TSchema] - ? [Type, True, False] - : [] -} -// prettier-ignore -type Extends = Static.Union<[ - Static.Tuple<[Static.Const<'extends'>, Type, Static.Const, Type, Static.Const, Type]>, - Static.Tuple<[]> -], ExtendsMapping> - -// ------------------------------------------------------------------ -// Base -// ------------------------------------------------------------------ -// prettier-ignore -interface BaseMapping extends Static.IMapping { - output: ( - this['input'] extends [LParen, infer Type extends t.TSchema, RParen] ? Type : - this['input'] extends infer Type extends t.TSchema ? Type : - never - ) -} -// prettier-ignore -type Base = Static.Union<[ - Static.Tuple<[Static.Const, Type, Static.Const]>, - Keyword, - Object, - Tuple, - Literal, - Function, - Constructor, - Mapped, - AsyncIterator, - Iterator, - ConstructorParameters, - FunctionParameters, - Argument, - InstanceType, - ReturnType, - Awaited, - Array, - Record, - Promise, - Partial, - Required, - Pick, - Omit, - Exclude, - Extract, - Lowercase, - Uppercase, - Capitalize, - Uncapitalize, - Date, - Uint8Array, - GenericReference, - Reference -], BaseMapping> - -// ------------------------------------------------------------------ -// Factor -// ------------------------------------------------------------------ -// prettier-ignore -type FactorExtends = ( - Extends extends [infer Right extends t.TSchema, infer True extends t.TSchema, infer False extends t.TSchema] - ? t.TExtends - : Type -) -// prettier-ignore -type FactorIndexArray = ( - IndexArray extends [...infer Left extends unknown[], infer Right extends t.TSchema[]] ? ( - Right extends [infer Indexer extends t.TSchema] ? t.TIndex, t.TIndexPropertyKeys> : - Right extends [] ? t.TArray> : - t.TNever - ) : Type -) -// prettier-ignore -interface FactorMapping extends Static.IMapping { - output: this['input'] extends [infer KeyOf extends boolean, infer Type extends t.TSchema, infer IndexArray extends unknown[], infer Extends extends unknown[]] - ? KeyOf extends true - ? FactorExtends>, Extends> - : FactorExtends, Extends> - : never -} -// prettier-ignore -type Factor = Static.Tuple<[ - KeyOf, Base, IndexArray, Extends -], FactorMapping> - -// ------------------------------------------------------------------ -// Expr -// ------------------------------------------------------------------ -// prettier-ignore -type ExprBinaryReduce = ( - Rest extends [infer Operator extends unknown, infer Right extends t.TSchema, infer Next extends unknown[]] ? ( - ExprBinaryReduce extends infer Schema extends t.TSchema ? ( - Operator extends '&' ? ( - Schema extends t.TIntersect - ? t.TIntersect<[Left, ...Types]> - : t.TIntersect<[Left, Schema]> - ) : - Operator extends '|' ? ( - Schema extends t.TUnion - ? t.TUnion<[Left, ...Types]> - : t.TUnion<[Left, Schema]> - ) : never - ) : never - ) : Left -) -// prettier-ignore -interface ExprBinaryMapping extends Static.IMapping { - output: ( - this['input'] extends [infer Left extends t.TSchema, infer Rest extends unknown[]] - ? ExprBinaryReduce - : [] - ) -} -// prettier-ignore -type ExprTermTail = Static.Union<[ - Static.Tuple<[Static.Const<'&'>, Factor, ExprTermTail]>, - Static.Tuple<[]> -]> -// prettier-ignore -type ExprTerm = Static.Tuple<[ - Factor, ExprTermTail -], ExprBinaryMapping> -// prettier-ignore -type ExprTail = Static.Union<[ - Static.Tuple<[Static.Const<'|'>, ExprTerm, ExprTail]>, - Static.Tuple<[]> -]> -// prettier-ignore -type Expr = Static.Tuple<[ - ExprTerm, ExprTail -], ExprBinaryMapping> - -// ------------------------------------------------------------------ -// Type -// ------------------------------------------------------------------ -// prettier-ignore -export type Type = Static.Union<[ - Static.Context, - Expr -]> - -// ------------------------------------------------------------------ -// Property -// ------------------------------------------------------------------ -// prettier-ignore -interface PropertyKeyStringMapping extends Static.IMapping { - output: this['input'] -} -type PropertyKeyString = Static.String<[SingleQuote, DoubleQuote], PropertyKeyStringMapping> -type PropertyKey = Static.Union<[Static.Ident, PropertyKeyString]> -// prettier-ignore -interface ReadonlyMapping extends Static.IMapping { - output: this['input'] extends ['readonly'] ? true : false -} -type Readonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], ReadonlyMapping> -// prettier-ignore -interface OptionalMapping extends Static.IMapping { - output: this['input'] extends [Question] ? true : false -} -type Optional = Static.Union<[Static.Tuple<[Static.Const]>, Static.Tuple<[]>], OptionalMapping> -// prettier-ignore -interface PropertyMapping extends Static.IMapping { - output: this['input'] extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends t.TSchema] - ? { - [_ in Key]: ( - [IsReadonly, IsOptional] extends [true, true] ? t.TReadonlyOptional : - [IsReadonly, IsOptional] extends [true, false] ? t.TReadonly : - [IsReadonly, IsOptional] extends [false, true] ? t.TOptional : - Type - ) - } : never -} -type Property = Static.Tuple<[Readonly, PropertyKey, Optional, Static.Const, Type], PropertyMapping> - -// ------------------------------------------------------------------ -// PropertyDelimiter -// ------------------------------------------------------------------ -// prettier-ignore -type PropertyDelimiter = Static.Union<[ - Static.Tuple<[Static.Const, Static.Const]>, - Static.Tuple<[Static.Const, Static.Const]>, - Static.Tuple<[Static.Const]>, - Static.Tuple<[Static.Const]>, - Static.Tuple<[Static.Const]>, -]> - -// ------------------------------------------------------------------ -// PropertyList -// ------------------------------------------------------------------ -type PropertyList = Delimit - -// ------------------------------------------------------------------ -// Object -// ------------------------------------------------------------------ -// prettier-ignore -type ObjectReduce = ( - PropertiesList extends [infer Left extends t.TProperties, ...infer Right extends t.TProperties[]] - ? ObjectReduce - : t.Evaluate -) -// prettier-ignore -interface ObjectMapping extends Static.IMapping { - output: this['input'] extends [LBrace, infer PropertyList extends t.TProperties[], RBrace] - ? t.TObject> - : never -} -// prettier-ignore -type Object = Static.Tuple<[ - Static.Const, - PropertyList, - Static.Const -], ObjectMapping> - -// ------------------------------------------------------------------ -// ElementList -// ------------------------------------------------------------------ -type ElementList = Delimit> - -// ------------------------------------------------------------------ -// Tuple -// ------------------------------------------------------------------ -// prettier-ignore -interface TupleMapping extends Static.IMapping { - output: this['input'] extends [unknown, infer ElementList extends t.TSchema[], unknown] - ? t.TTuple - : never -} -// prettier-ignore -type Tuple = Static.Tuple<[ - Static.Const, ElementList, Static.Const -], TupleMapping> - -// ------------------------------------------------------------------ -// Parameter -// ------------------------------------------------------------------ -interface ParameterMapping extends Static.IMapping { - output: this['input'] extends [string, Colon, infer Type extends t.TSchema] ? Type : never -} -// prettier-ignore -type Parameter = Static.Tuple<[ - Static.Ident, Static.Const, Type -], ParameterMapping> - -// ------------------------------------------------------------------ -// ParameterList -// ------------------------------------------------------------------ -type ParameterList = Delimit> - -// ------------------------------------------------------------------ -// Function -// ------------------------------------------------------------------ -// prettier-ignore -interface FunctionMapping extends Static.IMapping { - output: this['input'] extends [LParen, infer ParameterList extends t.TSchema[], RParen, '=>', infer ReturnType extends t.TSchema] - ? t.TFunction - : never -} -// prettier-ignore -type Function = Static.Tuple<[ - Static.Const, ParameterList, Static.Const, Static.Const<'=>'>, Type -], FunctionMapping> - -// ------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------ -// prettier-ignore -interface ConstructorMapping extends Static.IMapping { - output: this['input'] extends ['new', LParen, infer ParameterList extends t.TSchema[], RParen, '=>', infer InstanceType extends t.TSchema] - ? t.TConstructor - : never -} -// prettier-ignore -type Constructor = Static.Tuple<[ - Static.Const<'new'>, Static.Const, ParameterList, Static.Const, Static.Const<'=>'>, Type -], ConstructorMapping> - -// ------------------------------------------------------------------ -// Mapped (requires deferred types) -// ------------------------------------------------------------------ -// prettier-ignore -interface MappedMapping extends Static.IMapping { - output: this['input'] extends [LBrace, LBracket, infer _Key extends string, 'in', infer _Right extends t.TSchema, RBracket, Colon, infer Type extends t.TSchema, RBrace] - ? t.TLiteral<'Mapped types not supported'> - : this['input'] -} -// prettier-ignore -type Mapped = Static.Tuple<[ - Static.Const, Static.Const, Static.Ident, Static.Const<'in'>, Type, Static.Const, Static.Const, Type, Static.Const -], MappedMapping> - -// ------------------------------------------------------------------ -// Array -// ------------------------------------------------------------------ -// prettier-ignore -interface ArrayMapping extends Static.IMapping { - output: this['input'] extends ['Array', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TArray - : never -} -// prettier-ignore -type Array = Static.Tuple<[ - Static.Const<'Array'>, Static.Const, Type, Static.Const, -], ArrayMapping> - -// ------------------------------------------------------------------ -// AsyncIterator -// ------------------------------------------------------------------ -// prettier-ignore -interface AsyncIteratorMapping extends Static.IMapping { - output: this['input'] extends ['AsyncIterator', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TAsyncIterator - : never -} -// prettier-ignore -type AsyncIterator = Static.Tuple<[ - Static.Const<'AsyncIterator'>, Static.Const, Type, Static.Const, -], AsyncIteratorMapping> - -// ------------------------------------------------------------------ -// Iterator -// ------------------------------------------------------------------ -// prettier-ignore -interface IteratorMapping extends Static.IMapping { - output: this['input'] extends ['Iterator', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TIterator - : never -} -// prettier-ignore -type Iterator = Static.Tuple<[ - Static.Const<'Iterator'>, Static.Const, Type, Static.Const, -], IteratorMapping> - -// ------------------------------------------------------------------ -// ConstructorParameters -// ------------------------------------------------------------------ -// prettier-ignore -interface ConstructorParametersMapping extends Static.IMapping { - output: this['input'] extends ['ConstructorParameters', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TConstructorParameters - : never -} -// prettier-ignore -type ConstructorParameters = Static.Tuple<[ - Static.Const<'ConstructorParameters'>, Static.Const, Type, Static.Const, -], ConstructorParametersMapping> - -// ------------------------------------------------------------------ -// FunctionParameters -// ------------------------------------------------------------------ -// prettier-ignore -interface FunctionParametersMapping extends Static.IMapping { - output: this['input'] extends ['Parameters', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TParameters - : never -} -// prettier-ignore -type FunctionParameters = Static.Tuple<[ - Static.Const<'Parameters'>, Static.Const, Type, Static.Const, -], FunctionParametersMapping> - -// ------------------------------------------------------------------ -// InstanceType -// ------------------------------------------------------------------ -// prettier-ignore -interface InstanceTypeMapping extends Static.IMapping { - output: this['input'] extends ['InstanceType', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TInstanceType - : never -} -// prettier-ignore -type InstanceType = Static.Tuple<[ - Static.Const<'InstanceType'>, Static.Const, Type, Static.Const, -], InstanceTypeMapping> - -// ------------------------------------------------------------------ -// ReturnType -// ------------------------------------------------------------------ -// prettier-ignore -interface ReturnTypeMapping extends Static.IMapping { - output: this['input'] extends ['ReturnType', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TReturnType - : never -} -// prettier-ignore -type ReturnType = Static.Tuple<[ - Static.Const<'ReturnType'>, Static.Const, Type, Static.Const, -], ReturnTypeMapping> - -// ------------------------------------------------------------------ -// Argument -// ------------------------------------------------------------------ -// prettier-ignore -interface ArgumentMapping extends Static.IMapping { - output: this['input'] extends ['Argument', LAngle, infer Type extends t.TSchema, RAngle] - ? Type extends t.TLiteral - ? t.TArgument - : t.TNever - : never -} -// prettier-ignore -type Argument = Static.Tuple<[ - Static.Const<'Argument'>, Static.Const, Type, Static.Const, -], ArgumentMapping> - -// ------------------------------------------------------------------ -// Awaited -// ------------------------------------------------------------------ -// prettier-ignore -interface AwaitedMapping extends Static.IMapping { - output: this['input'] extends ['Awaited', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TAwaited - : never -} -// prettier-ignore -type Awaited = Static.Tuple<[ - Static.Const<'Awaited'>, Static.Const, Type, Static.Const, -], AwaitedMapping> - -// ------------------------------------------------------------------ -// Promise -// ------------------------------------------------------------------ -// prettier-ignore -interface PromiseMapping extends Static.IMapping { - output: this['input'] extends ['Promise', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TPromise - : never -} -// prettier-ignore -type Promise = Static.Tuple<[ - Static.Const<'Promise'>, Static.Const, Type, Static.Const, -], PromiseMapping> - -// ------------------------------------------------------------------ -// Record -// ------------------------------------------------------------------ -// prettier-ignore -interface RecordMapping extends Static.IMapping { - output: this['input'] extends ['Record', LAngle, infer Key extends t.TSchema, Comma, infer Type extends t.TSchema, RAngle] - ? t.TRecord - : never -} -// prettier-ignore -type Record = Static.Tuple<[ - Static.Const<'Record'>, Static.Const, Type, Static.Const, Type, Static.Const, -], RecordMapping> - -// ------------------------------------------------------------------ -// Partial -// ------------------------------------------------------------------ -// prettier-ignore -interface PartialMapping extends Static.IMapping { - output: this['input'] extends ['Partial', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TPartial - : never -} -// prettier-ignore -type Partial = Static.Tuple<[ - Static.Const<'Partial'>, Static.Const, Type, Static.Const, -], PartialMapping> - -// ------------------------------------------------------------------ -// Required -// ------------------------------------------------------------------ -// prettier-ignore -interface RequiredMapping extends Static.IMapping { - output: this['input'] extends ['Required', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TRequired - : never -} -// prettier-ignore -type Required = Static.Tuple<[ - Static.Const<'Required'>, Static.Const, Type, Static.Const, -], RequiredMapping> - -// ------------------------------------------------------------------ -// Pick -// ------------------------------------------------------------------ -// prettier-ignore -interface PickMapping extends Static.IMapping { - output: this['input'] extends ['Pick', LAngle, infer Type extends t.TSchema, Comma, infer Key extends t.TSchema, RAngle] - ? t.TPick - : never -} -// prettier-ignore -type Pick = Static.Tuple<[ - Static.Const<'Pick'>, Static.Const, Type, Static.Const, Type, Static.Const, -], PickMapping> - -// ------------------------------------------------------------------ -// Omit -// ------------------------------------------------------------------ -// prettier-ignore -interface OmitMapping extends Static.IMapping { - output: this['input'] extends ['Omit', LAngle, infer Type extends t.TSchema, Comma, infer Key extends t.TSchema, RAngle] - ? t.TOmit - : never -} -// prettier-ignore -type Omit = Static.Tuple<[ - Static.Const<'Omit'>, Static.Const, Type, Static.Const, Type, Static.Const -], OmitMapping> - -// ------------------------------------------------------------------ -// Exclude -// ------------------------------------------------------------------ -// prettier-ignore -interface ExcludeMapping extends Static.IMapping { - output: this['input'] extends ['Exclude', LAngle, infer Type extends t.TSchema, Comma, infer PropertyKey extends t.TSchema, RAngle] - ? t.TExclude - : never -} -// prettier-ignore -type Exclude = Static.Tuple<[ - Static.Const<'Exclude'>, Static.Const, Type, Static.Const, Type, Static.Const -], ExcludeMapping> - -// ------------------------------------------------------------------ -// Extract -// ------------------------------------------------------------------ -// prettier-ignore -interface ExtractMapping extends Static.IMapping { - output: this['input'] extends ['Extract', LAngle, infer Type extends t.TSchema, Comma, infer PropertyKey extends t.TSchema, RAngle] - ? t.TExtract - : never -} -// prettier-ignore -type Extract = Static.Tuple<[ - Static.Const<'Extract'>, Static.Const, Type, Static.Const, Type, Static.Const -], ExtractMapping> - -// ------------------------------------------------------------------ -// Uppercase -// ------------------------------------------------------------------ -// prettier-ignore -interface UppercaseMapping extends Static.IMapping { - output: this['input'] extends ['Uppercase', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TUppercase - : never -} -// prettier-ignore -type Uppercase = Static.Tuple<[ - Static.Const<'Uppercase'>, Static.Const, Type, Static.Const, -], UppercaseMapping> - -// ------------------------------------------------------------------ -// Lowercase -// ------------------------------------------------------------------ -// prettier-ignore -interface LowercaseMapping extends Static.IMapping { - output: this['input'] extends ['Lowercase', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TLowercase - : never -} -// prettier-ignore -type Lowercase = Static.Tuple<[ - Static.Const<'Lowercase'>, Static.Const, Type, Static.Const, -], LowercaseMapping> - -// ------------------------------------------------------------------ -// Capitalize -// ------------------------------------------------------------------ -// prettier-ignore -interface CapitalizeMapping extends Static.IMapping { - output: this['input'] extends ['Capitalize', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TCapitalize - : never -} -// prettier-ignore -type Capitalize = Static.Tuple<[ - Static.Const<'Capitalize'>, Static.Const, Type, Static.Const, -], CapitalizeMapping> - -// ------------------------------------------------------------------ -// Uncapitalize -// ------------------------------------------------------------------ -// prettier-ignore -interface UncapitalizeMapping extends Static.IMapping { - output: this['input'] extends ['Uncapitalize', LAngle, infer Type extends t.TSchema, RAngle] - ? t.TUncapitalize - : never -} -// prettier-ignore -type Uncapitalize = Static.Tuple<[ - Static.Const<'Uncapitalize'>, Static.Const, Type, Static.Const, -], UncapitalizeMapping> - -// ------------------------------------------------------------------ -// Date -// ------------------------------------------------------------------ -type Date = Static.Const<'Date', Static.As> - -// ------------------------------------------------------------------ -// Uint8Array -// ------------------------------------------------------------------ -type Uint8Array = Static.Const<'Uint8Array', Static.As> diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts index e467cd2ef..82a6d9624 100644 --- a/src/syntax/syntax.ts +++ b/src/syntax/syntax.ts @@ -26,70 +26,37 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as t from '../type/index' -import { Static } from '../parser/index' -import { Module } from './runtime' -import { Type } from './static' +import * as t from '@sinclair/typebox' +import { Type, TType } from './parser' // ------------------------------------------------------------------ // NoInfer // ------------------------------------------------------------------ -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ -export function NoInfer, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ -export function NoInfer(code: Code, options?: t.SchemaOptions): t.TSchema -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type but does not infer schematics */ +/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */ +export function NoInfer, Input extends string>(context: Context, input: Input, options?: t.SchemaOptions): t.TSchema +/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */ +export function NoInfer(input: Input, options?: t.SchemaOptions): t.TSchema +/** `[Experimental]` Parses type expressions into TypeBox types but does not infer */ // prettier-ignore export function NoInfer(...args: any[]): t.TSchema { const withContext = typeof args[0] === 'string' ? false : true const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}] - const result = Module.Parse('Type', code, context)[0] + const result = Type(code, context)[0] return t.KindGuard.IsSchema(result) ? t.CloneType(result, options) : t.Never(options) } -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses type expressions into TypeBox types */ // prettier-ignore export type TSyntax, Code extends string> = ( - Static.Parse extends [infer Type extends t.TSchema, string] ? Type : t.TNever + TType extends [infer Type extends t.TSchema, string] ? Type : t.TNever ) -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ -export function Syntax, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ -export function Syntax(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation> -/** `[Experimental]` Parses a TypeScript annotation into a TypeBox type */ +/** `[Experimental]` Parses type expressions into TypeBox types */ +export function Syntax, Input extends string>(context: Context, input: Input, options?: t.SchemaOptions): TSyntax +/** `[Experimental]` Parses type expressions into TypeBox types */ +export function Syntax(annotation: Input, options?: t.SchemaOptions): TSyntax<{}, Input> +/** `[Experimental]` Parses type expressions into TypeBox types */ export function Syntax(...args: any[]): never { return NoInfer.apply(null, args as never) as never } -// ------------------------------------------------------------------ -// Deprecated -// ------------------------------------------------------------------ -/** - * @deprecated Use Syntax() function - */ -export function Parse, Annotation extends string>(context: Context, annotation: Annotation, options?: t.SchemaOptions): TSyntax -/** - * @deprecated Use Syntax() function - */ -export function Parse(annotation: Annotation, options?: t.SchemaOptions): TSyntax<{}, Annotation> -/** - * @deprecated Use Syntax() function - */ -export function Parse(...args: any[]): never { - return NoInfer.apply(null, args as never) as never -} -/** - * @deprecated Use NoInfer() function - */ -export function ParseOnly, Code extends string>(context: Context, code: Code, options?: t.SchemaOptions): t.TSchema | undefined -/** - * @deprecated Use NoInfer() function - */ -export function ParseOnly(code: Code, options?: t.SchemaOptions): t.TSchema | undefined -/** - * @deprecated Use NoInfer() function - */ -export function ParseOnly(...args: any[]): t.TSchema | undefined { - return NoInfer.apply(null, args as never) as never -} From 5a5431439f7d5ca6b494d0d18fbfd7b1a356d67c Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 2 Apr 2025 21:18:30 +0900 Subject: [PATCH 356/369] Revision 0.34.33 (#1220) * Fix Import Specifier * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/syntax/mapping.ts | 2 +- src/syntax/syntax.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 5baecf130..10428a8ad 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.33](https://github.com/sinclairzx81/typebox/pull/1220) + - Hotfix: Correct Invalid Import Specifier - [Revision 0.34.32](https://github.com/sinclairzx81/typebox/pull/1218) - Accelerated | High Performance Syntax Parsing - [Revision 0.34.31](https://github.com/sinclairzx81/typebox/pull/1209) diff --git a/package-lock.json b/package-lock.json index c76f353fa..d293b7ecc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.32", + "version": "0.34.33", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.32", + "version": "0.34.33", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index b0b1786e1..3b573eccc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.32", + "version": "0.34.33", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/syntax/mapping.ts b/src/syntax/mapping.ts index 257435f27..60061759d 100644 --- a/src/syntax/mapping.ts +++ b/src/syntax/mapping.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as T from '@sinclair/typebox' +import * as T from '../type/index' // ------------------------------------------------------------------ // diff --git a/src/syntax/syntax.ts b/src/syntax/syntax.ts index 82a6d9624..82be0075c 100644 --- a/src/syntax/syntax.ts +++ b/src/syntax/syntax.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import * as t from '@sinclair/typebox' +import * as t from '../type/index' import { Type, TType } from './parser' // ------------------------------------------------------------------ From 7b45ec1c4706e41068b8b5d7529efb86499b1424 Mon Sep 17 00:00:00 2001 From: Antonio Silva Date: Fri, 13 Jun 2025 00:13:32 -0300 Subject: [PATCH 357/369] Support Inference of Ref inside Recursive inside Module (#1263) --- src/type/module/infer.ts | 2 ++ test/static/import.ts | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/type/module/infer.ts b/src/type/module/infer.ts index e0fff5e0f..8c8708d20 100644 --- a/src/type/module/infer.ts +++ b/src/type/module/infer.ts @@ -43,6 +43,7 @@ import { TRef } from '../ref/index' import { TTuple } from '../tuple/index' import { TUnion } from '../union/index' import { Static } from '../static/index' +import { TRecursive } from '../recursive/index' // ------------------------------------------------------------------ // Array @@ -160,6 +161,7 @@ type TInfer = ( Type extends TTuple ? TInferTuple : Type extends TEnum ? Static : // intercept enum before union Type extends TUnion ? TInferUnion : + Type extends TRecursive ? TInfer : Static ) // ------------------------------------------------------------------ diff --git a/test/static/import.ts b/test/static/import.ts index 0f30704f2..dec8e8ec2 100644 --- a/test/static/import.ts +++ b/test/static/import.ts @@ -130,3 +130,26 @@ import { Type, Static } from '@sinclair/typebox' x?: null[], }>() } +// ------------------------------------------------------------------ +// Ref inside Recursive +// ------------------------------------------------------------------ +// prettier-ignore +{ + const Module = Type.Module({ + T: Type.Recursive((_) => + Type.Object({ + M: Type.Ref("U"), + }) + ), + U: Type.Union([ + Type.Literal("A"), + Type.Literal("B") + ]), + }); + + const T = Module.Import("T"); + type T = Static; + Expect(T).ToStatic<{ + M: 'A'|'B' + }>(); +} From 394927d8ade58ca5214765a0d4bee13825f1235d Mon Sep 17 00:00:00 2001 From: sinclair Date: Fri, 13 Jun 2025 12:17:40 +0900 Subject: [PATCH 358/369] Revision 0.34.34 --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 10428a8ad..975953c6e 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.34](https://github.com/sinclairzx81/typebox/pull/1263) + - Support Inference of Ref inside Recursive inside Module - [Revision 0.34.33](https://github.com/sinclairzx81/typebox/pull/1220) - Hotfix: Correct Invalid Import Specifier - [Revision 0.34.32](https://github.com/sinclairzx81/typebox/pull/1218) diff --git a/package-lock.json b/package-lock.json index d293b7ecc..cae1614e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.33", + "version": "0.34.34", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.33", + "version": "0.34.34", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 3b573eccc..a2d46f405 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.33", + "version": "0.34.34", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From 2406c7462cd42d286a60e01f0940bf992a77cb45 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 13 Jun 2025 13:58:56 +0900 Subject: [PATCH 359/369] Revision 0.34.35 (#1265) * Deep Assign on Intersect Cast * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/value/cast/cast.ts | 20 +++++++++++++++++--- test/runtime/value/cast/intersect.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 975953c6e..6df506b25 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.35](https://github.com/sinclairzx81/typebox/pull/1265) + - Deep Assign on Intersect Cast - [Revision 0.34.34](https://github.com/sinclairzx81/typebox/pull/1263) - Support Inference of Ref inside Recursive inside Module - [Revision 0.34.33](https://github.com/sinclairzx81/typebox/pull/1220) diff --git a/package-lock.json b/package-lock.json index cae1614e2..426d2ced1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.34", + "version": "0.34.35", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.34", + "version": "0.34.35", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index a2d46f405..7642679e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.34", + "version": "0.34.35", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 671050421..728aded2b 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -139,10 +139,24 @@ function FromImport(schema: TImport, references: TSchema[], value: unknown): boo const target = schema.$defs[schema.$ref] as TSchema return Visit(target, [...references, ...definitions], value) } + +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +function IntersectAssign(correct: unknown, value: unknown): unknown { + // trust correct on mismatch | value on non-object + if ((IsObject(correct) && !IsObject(value)) || (!IsObject(correct) && IsObject(value))) return correct + if (!IsObject(correct) || !IsObject(value)) return value + return globalThis.Object.getOwnPropertyNames(correct).reduce((result, key) => { + const property = key in value ? IntersectAssign(correct[key], value[key]) : correct[key] + return { ...result, [key]: property } + }, {}) +} function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any { - const created = Create(schema, references) - const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value - return Check(schema, references, mapped) ? mapped : Create(schema, references) + if (Check(schema, references, value)) return value + const correct = Create(schema, references) + const assigned = IntersectAssign(correct, value) + return Check(schema, references, assigned) ? assigned : correct } function FromNever(schema: TNever, references: TSchema[], value: any): any { throw new ValueCastError(schema, 'Never types cannot be cast') diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index 82044975f..ff5f5895b 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -73,4 +73,31 @@ describe('value/cast/Intersect', () => { const V = Value.Cast(T, 2000) Assert.IsEqual(V, 2000) }) + + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1264 + // ---------------------------------------------------------------- + it('Should preserve intersected properties', () => { + const T = Type.Intersect([ + Type.Object({}), + Type.Object({ + name: Type.String(), + age: Type.Optional(Type.Number()), + location: Type.Object({ + lat: Type.Number(), + long: Type.Number(), + }), + greeting: Type.String(), + }), + ]) + const V0 = Value.Cast(T, { greeting: 'Hello' }) + const V1 = Value.Cast(T, { location: null, greeting: 'Hello' }) + const V2 = Value.Cast(T, { location: { lat: 1 }, greeting: 'Hello' }) + const V3 = Value.Cast(T, { location: { lat: 1, long: 1 }, greeting: 'Hello' }) + + Assert.IsEqual(V0, { name: '', location: { lat: 0, long: 0 }, greeting: 'Hello' }) + Assert.IsEqual(V1, { name: '', location: { lat: 0, long: 0 }, greeting: 'Hello' }) + Assert.IsEqual(V2, { name: '', location: { lat: 1, long: 0 }, greeting: 'Hello' }) + Assert.IsEqual(V3, { name: '', location: { lat: 1, long: 1 }, greeting: 'Hello' }) + }) }) From cb3f898093148c41f1140219d58d00b5f9512852 Mon Sep 17 00:00:00 2001 From: Ilya Medvedev Date: Sun, 22 Jun 2025 06:16:51 +0200 Subject: [PATCH 360/369] Ecosystem (#1274) Added a link to a plugin that allows you to create fake data based on TypeBox schemas. - docs: add `typebox-schema-faker` to the ecosystem (#1274) --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 8bc45b3ca..18bbc55e7 100644 --- a/readme.md +++ b/readme.md @@ -1738,6 +1738,7 @@ The following is a list of community packages that offer general tooling, extend | [ts2typebox](https://github.com/xddq/ts2typebox) | Creating TypeBox code from Typescript types | | [typebox-cli](https://github.com/gsuess/typebox-cli) | Generate Schema with TypeBox from the CLI | | [typebox-form-parser](https://github.com/jtlapp/typebox-form-parser) | Parses form and query data based on TypeBox schemas | +| [typebox-schema-faker](https://github.com/iam-medvedev/typebox-schema-faker) | Generate fake data from TypeBox schemas for testing, prototyping and development | From b40ba2c751cc3b16fb8060d266267600a993628a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 22 Jun 2025 14:49:16 +0900 Subject: [PATCH 361/369] Revision 0.34.36 (#1276) * Fix Record Intersect on Cast * TypeScript 5.8.3 * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ hammer.mjs | 8 +++++++- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/value/create/create.ts | 6 ------ test/runtime/value/cast/intersect.ts | 9 +++++++++ 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 6df506b25..b769a4f0c 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.36](https://github.com/sinclairzx81/typebox/pull/1276) + - Fix Record Intersect on Cast - [Revision 0.34.35](https://github.com/sinclairzx81/typebox/pull/1265) - Deep Assign on Intersect Cast - [Revision 0.34.34](https://github.com/sinclairzx81/typebox/pull/1263) diff --git a/hammer.mjs b/hammer.mjs index 477cc7599..5bde41f39 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -35,7 +35,13 @@ export async function benchmark() { // Test // ------------------------------------------------------------------------------- export async function test_typescript() { - for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', '5.7.2', '5.7.3', 'next', 'latest']) { + for (const version of [ + '4.9.5', '5.0.4', '5.1.3', '5.1.6', + '5.2.2', '5.3.2', '5.3.3', '5.4.3', + '5.4.5', '5.5.2', '5.5.3', '5.5.4', + '5.6.2', '5.6.3', '5.7.2', '5.7.3', + '5.8.2', 'next', 'latest' + ]) { await shell(`npm install typescript@${version} --no-save`) await test_static() } diff --git a/package-lock.json b/package-lock.json index 426d2ced1..4b5499e44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.35", + "version": "0.34.36", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.35", + "version": "0.34.36", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.8.2" + "typescript": "^5.8.3" } }, "node_modules/@andrewbranch/untar.js": { @@ -1814,9 +1814,9 @@ } }, "node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3226,9 +3226,9 @@ "dev": true }, "typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 7642679e8..09380c4d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.35", + "version": "0.34.36", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -38,6 +38,6 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.8.2" + "typescript": "^5.8.3" } } diff --git a/src/value/create/create.ts b/src/value/create/create.ts index 3af0245d9..9c75b0d50 100644 --- a/src/value/create/create.ts +++ b/src/value/create/create.ts @@ -269,14 +269,8 @@ function FromPromise(schema: TPromise, references: TSchema[]): any { } } function FromRecord(schema: TRecord, references: TSchema[]): any { - const [keyPattern, valueSchema] = Object.entries(schema.patternProperties)[0] if (HasPropertyKey(schema, 'default')) { return FromDefault(schema.default) - } else if (!(keyPattern === PatternStringExact || keyPattern === PatternNumberExact)) { - const propertyKeys = keyPattern.slice(1, keyPattern.length - 1).split('|') - const Acc = {} as Record - for (const key of propertyKeys) Acc[key] = Visit(valueSchema, references) - return Acc } else { return {} } diff --git a/test/runtime/value/cast/intersect.ts b/test/runtime/value/cast/intersect.ts index ff5f5895b..bed80df65 100644 --- a/test/runtime/value/cast/intersect.ts +++ b/test/runtime/value/cast/intersect.ts @@ -100,4 +100,13 @@ describe('value/cast/Intersect', () => { Assert.IsEqual(V2, { name: '', location: { lat: 1, long: 0 }, greeting: 'Hello' }) Assert.IsEqual(V3, { name: '', location: { lat: 1, long: 1 }, greeting: 'Hello' }) }) + + // -------------------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1269#issuecomment-2993924180 + // -------------------------------------------------------------------------- + it('Should Cast with intersected Record', () => { + const T = Type.Intersect([Type.Record(Type.TemplateLiteral('x-${string}'), Type.Unknown()), Type.Object({ name: Type.String() })]) + const R = Value.Cast(T, {}) + Assert.IsEqual(R, { name: '' }) + }) }) From 978339122870fdd8300171e62994e8afcf1e9cda Mon Sep 17 00:00:00 2001 From: Redis Stasa <91160178+DemonHa@users.noreply.github.com> Date: Tue, 24 Jun 2025 01:13:26 -0400 Subject: [PATCH 362/369] Fix: Correctly score nested unions (#1273) --- src/value/cast/cast.ts | 7 +- test/runtime/value/cast/union.ts | 186 +++++++++++++++++++++++++++++++ 2 files changed, 192 insertions(+), 1 deletion(-) diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 728aded2b..8b31f5771 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -76,6 +76,11 @@ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number const exists = keys.includes(key) ? point : 0 return acc + (literal + checks + exists) }, 0) + } else if (schema[Kind] === "Union") { + const schemas = schema.anyOf.map((schema: TSchema) => + Deref(schema, references) + ) + return Math.max(...schemas.map((schema: TSchema) => ScoreUnion(schema, references, value))) } else { return Check(schema, references, value) ? 1 : 0 } @@ -127,7 +132,7 @@ function FromArray(schema: TArray, references: TSchema[], value: any): any { function FromConstructor(schema: TConstructor, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Create(schema, references) const required = new Set(schema.returns.required || []) - const result = function () {} + const result = function () { } for (const [key, property] of Object.entries(schema.returns.properties)) { if (!required.has(key) && value.prototype[key] === undefined) continue result.prototype[key] = Visit(property as TSchema, references, value.prototype[key]) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index 96b159cdc..ca73c089d 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -161,4 +161,190 @@ describe('value/cast/Union', () => { Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'A' }), { type: 'A' }) Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'A' }), { type: 'A' }) }) + + // ------------------------------------------------------------------------ + // ref: https://github.com/sinclairzx81/typebox/issues/1268 + // ------------------------------------------------------------------------ + it('should correctly score nested union types #1', () => { + const A = + Type.Union([ + Type.Union([ + Type.Object({ + type: Type.Literal('a'), + name: Type.String(), + in: Type.String(), + }), + Type.Object({ + type: Type.Literal('b'), + description: Type.Optional(Type.String()), + nested: Type.Object({ + a: Type.String(), + b: Type.Optional(Type.String()), + }), + }), + ]), + Type.Object({ + $ref: Type.String(), + description: Type.Optional(Type.String()), + }), + ], + + ); + + Assert.IsEqual(Value.Cast(A, { + type: 'b', + description: 'Hello World', + nested: { + b: 'hello', + }, + }), { + type: 'b', + description: 'Hello World', + nested: { a: '', b: 'hello' }, + }); + }); + + it('should correctly score nested union types #2', () => { + const A = Type.Union([ + Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop3: Type.String(), + }), + Type.Object({ + prop1: Type.String(), + prop4: Type.String(), + prop5: Type.String(), + }) + ]), + Type.Union([ + Type.Object({ + prop6: Type.String(), + prop7: Type.String(), + prop8: Type.String(), + }), + Type.Object({ + prop1: Type.String(), + prop9: Type.String(), + prop10: Type.String(), + }), + ]), + ]) + + // Picks the first union variant when the score is equal + Assert.IsEqual(Value.Cast(A, { + prop1: '' + }), { + prop1: '', + prop2: '', + prop3: '', + }); + + Assert.IsEqual(Value.Cast(A, { + prop1: '', + prop4: '' + }), { + prop1: '', + prop4: '', + prop5: '', + }); + + Assert.IsEqual(Value.Cast(A, { + prop6: '', + }), { + prop6: '', + prop7: '', + prop8: '', + }); + }); + + it('should correctly score nested union types #3', () => { + const A = Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop3: Type.String(), + }), + Type.Object({ + prop4: Type.String(), + prop5: Type.String(), + prop6: Type.String(), + }), + Type.Union([ + Type.Object({ + prop4: Type.String(), + prop5: Type.String(), + prop6: Type.String(), + }), + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop7: Type.String(), + prop8: Type.String(), + }), + ]), + ]) + + Assert.IsEqual(Value.Cast(A, { + prop1: '', + prop2: '', + prop7: '' + }), { + prop1: '', + prop2: '', + prop7: '', + prop8: '', + }); + }); + + it('should correctly score nested union types #4', () => { + const A = Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop3: Type.String(), + }), + Type.Union([ + Type.Object({ + prop4: Type.String(), + prop5: Type.String(), + prop6: Type.String(), + }), + Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop7: Type.String(), + prop8: Type.String(), + }), + Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop9: Type.String(), + prop10: Type.String(), + }), + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop11: Type.String(), + prop12: Type.String(), + }) + ]) + ]) + ]), + ]) + + Assert.IsEqual(Value.Cast(A, { + prop1: '', + prop2: '', + prop9: '' + }), { + prop1: '', + prop2: '', + prop9: '', + prop10: '', + }); + }) }) From 30cfe8904d8e7ea3da549783f66d32ae1f867b6a Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Tue, 24 Jun 2025 14:27:54 +0900 Subject: [PATCH 363/369] Revision 0.34.37 (#1278) * Format * Refactor * Version * ChangeLog --- changelog/0.34.0.md | 2 + package-lock.json | 4 +- package.json | 2 +- src/value/cast/cast.ts | 11 +- test/runtime/value/cast/union.ts | 173 +++++++++++++++++-------------- 5 files changed, 104 insertions(+), 88 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index b769a4f0c..2d8a0fe58 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.37](https://github.com/sinclairzx81/typebox/pull/1278) + - Fix Support nested Union selection when scoring for Cast - [Revision 0.34.36](https://github.com/sinclairzx81/typebox/pull/1276) - Fix Record Intersect on Cast - [Revision 0.34.35](https://github.com/sinclairzx81/typebox/pull/1265) diff --git a/package-lock.json b/package-lock.json index 4b5499e44..b4eac3353 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.36", + "version": "0.34.37", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.36", + "version": "0.34.37", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 09380c4d0..6ebc41259 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.36", + "version": "0.34.37", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 8b31f5771..86149da79 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -76,11 +76,10 @@ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number const exists = keys.includes(key) ? point : 0 return acc + (literal + checks + exists) }, 0) - } else if (schema[Kind] === "Union") { - const schemas = schema.anyOf.map((schema: TSchema) => - Deref(schema, references) - ) - return Math.max(...schemas.map((schema: TSchema) => ScoreUnion(schema, references, value))) + } else if (schema[Kind] === 'Union') { + const schemas = schema.anyOf.map((schema: TSchema) => Deref(schema, references)) + const scores = schemas.map((schema: TSchema) => ScoreUnion(schema, references, value)) + return Math.max(...scores) } else { return Check(schema, references, value) ? 1 : 0 } @@ -132,7 +131,7 @@ function FromArray(schema: TArray, references: TSchema[], value: any): any { function FromConstructor(schema: TConstructor, references: TSchema[], value: any): any { if (Check(schema, references, value)) return Create(schema, references) const required = new Set(schema.returns.required || []) - const result = function () { } + const result = function () {} for (const [key, property] of Object.entries(schema.returns.properties)) { if (!required.has(key) && value.prototype[key] === undefined) continue result.prototype[key] = Visit(property as TSchema, references, value.prototype[key]) diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index ca73c089d..fa86fb0ba 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -166,43 +166,43 @@ describe('value/cast/Union', () => { // ref: https://github.com/sinclairzx81/typebox/issues/1268 // ------------------------------------------------------------------------ it('should correctly score nested union types #1', () => { - const A = + const A = Type.Union([ Type.Union([ - Type.Union([ - Type.Object({ - type: Type.Literal('a'), - name: Type.String(), - in: Type.String(), - }), - Type.Object({ - type: Type.Literal('b'), - description: Type.Optional(Type.String()), - nested: Type.Object({ - a: Type.String(), - b: Type.Optional(Type.String()), - }), - }), - ]), Type.Object({ - $ref: Type.String(), + type: Type.Literal('a'), + name: Type.String(), + in: Type.String(), + }), + Type.Object({ + type: Type.Literal('b'), description: Type.Optional(Type.String()), + nested: Type.Object({ + a: Type.String(), + b: Type.Optional(Type.String()), + }), }), - ], - - ); + ]), + Type.Object({ + $ref: Type.String(), + description: Type.Optional(Type.String()), + }), + ]) - Assert.IsEqual(Value.Cast(A, { - type: 'b', - description: 'Hello World', - nested: { - b: 'hello', + Assert.IsEqual( + Value.Cast(A, { + type: 'b', + description: 'Hello World', + nested: { + b: 'hello', + }, + }), + { + type: 'b', + description: 'Hello World', + nested: { a: '', b: 'hello' }, }, - }), { - type: 'b', - description: 'Hello World', - nested: { a: '', b: 'hello' }, - }); - }); + ) + }) it('should correctly score nested union types #2', () => { const A = Type.Union([ @@ -216,7 +216,7 @@ describe('value/cast/Union', () => { prop1: Type.String(), prop4: Type.String(), prop5: Type.String(), - }) + }), ]), Type.Union([ Type.Object({ @@ -233,31 +233,40 @@ describe('value/cast/Union', () => { ]) // Picks the first union variant when the score is equal - Assert.IsEqual(Value.Cast(A, { - prop1: '' - }), { - prop1: '', - prop2: '', - prop3: '', - }); + Assert.IsEqual( + Value.Cast(A, { + prop1: '', + }), + { + prop1: '', + prop2: '', + prop3: '', + }, + ) - Assert.IsEqual(Value.Cast(A, { - prop1: '', - prop4: '' - }), { - prop1: '', - prop4: '', - prop5: '', - }); + Assert.IsEqual( + Value.Cast(A, { + prop1: '', + prop4: '', + }), + { + prop1: '', + prop4: '', + prop5: '', + }, + ) - Assert.IsEqual(Value.Cast(A, { - prop6: '', - }), { - prop6: '', - prop7: '', - prop8: '', - }); - }); + Assert.IsEqual( + Value.Cast(A, { + prop6: '', + }), + { + prop6: '', + prop7: '', + prop8: '', + }, + ) + }) it('should correctly score nested union types #3', () => { const A = Type.Union([ @@ -286,17 +295,20 @@ describe('value/cast/Union', () => { ]), ]) - Assert.IsEqual(Value.Cast(A, { - prop1: '', - prop2: '', - prop7: '' - }), { - prop1: '', - prop2: '', - prop7: '', - prop8: '', - }); - }); + Assert.IsEqual( + Value.Cast(A, { + prop1: '', + prop2: '', + prop7: '', + }), + { + prop1: '', + prop2: '', + prop7: '', + prop8: '', + }, + ) + }) it('should correctly score nested union types #4', () => { const A = Type.Union([ @@ -330,21 +342,24 @@ describe('value/cast/Union', () => { prop2: Type.String(), prop11: Type.String(), prop12: Type.String(), - }) - ]) - ]) + }), + ]), + ]), ]), ]) - Assert.IsEqual(Value.Cast(A, { - prop1: '', - prop2: '', - prop9: '' - }), { - prop1: '', - prop2: '', - prop9: '', - prop10: '', - }); + Assert.IsEqual( + Value.Cast(A, { + prop1: '', + prop2: '', + prop9: '', + }), + { + prop1: '', + prop2: '', + prop9: '', + prop10: '', + }, + ) }) }) From 97e315439690ef84f85d75cc9179163b2cd0e55a Mon Sep 17 00:00:00 2001 From: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com> Date: Wed, 16 Jul 2025 03:06:34 +0200 Subject: [PATCH 364/369] Preserve exact type matches in union conversion (#1282) Modify Value.Convert to check for exact type matches before attempting conversion in union types. This prevents unnecessary type coercion when the input value already matches one of the union schema types. Fixes #1281 Signed-off-by: Ramtin Mesgari <26694963+iamramtin@users.noreply.github.com> --- src/value/convert/convert.ts | 8 +++++++ test/runtime/value/convert/union.ts | 37 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 752ba37a4..7b183e599 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -243,6 +243,14 @@ function FromUndefined(schema: TUndefined, references: TSchema[], value: any): u return TryConvertUndefined(value) } function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown { + // Check if original value already matches one of the union variants + for (const subschema of schema.anyOf) { + if (Check(subschema, references, value)) { + return value + } + } + + // Attempt conversion for each variant for (const subschema of schema.anyOf) { const converted = Visit(subschema, references, Clone(value)) if (!Check(subschema, references, converted)) continue diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index ef020b69a..fbf207702 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -40,4 +40,41 @@ describe('value/convert/Union', () => { Assert.IsEqual(B, { b: 1, c: 2 }) Assert.IsEqual(C, { a: 1, b: '2', c: 3 }) // note: matching on first }) + it('Should preserve number type in string-first union when value already matches', () => { + const T = Type.Union([Type.String(), Type.Number()]) + const numValue = 42 + const strValue = 'hello' + + const A = Value.Convert(T, numValue) + const B = Value.Convert(T, strValue) + + Assert.IsEqual(typeof A, 'number') + Assert.IsEqual(typeof B, 'string') + Assert.IsEqual(A, 42) + Assert.IsEqual(B, 'hello') + }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1281 + // Union conversion should preserve original type if valid + // ---------------------------------------------------------------- + it('Should preserve original type when value already matches union variant', () => { + const T1 = Type.Object({ + data: Type.Array(Type.Record(Type.String(), Type.Union([Type.String(), Type.Number(), Type.Null()]))), + }) + const T2 = Type.Object({ + data: Type.Array(Type.Record(Type.String(), Type.Union([Type.Number(), Type.String(), Type.Null()]))), + }) + const testData = { + data: [{ key1: 'hello', key2: 42, key3: null }], + } + + const A = Value.Convert(T1, testData) + const B = Value.Convert(T2, testData) + + // Both should preserve the original number type + Assert.IsEqual(typeof (A as any).data[0].key2, 'number') + Assert.IsEqual(typeof (B as any).data[0].key2, 'number') + Assert.IsEqual((A as any).data[0].key2, 42) + Assert.IsEqual((B as any).data[0].key2, 42) + }) }) From 60fdafe857089ac908b5fe7741c15c62260b651f Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Wed, 16 Jul 2025 11:45:37 +0900 Subject: [PATCH 365/369] Revision 0.34.38 (#1285) * ChangeLog * Version --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 2d8a0fe58..9efa8bfd8 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.38](https://github.com/sinclairzx81/typebox/pull/1282) + - Preserve exact type matches in Union conversion - [Revision 0.34.37](https://github.com/sinclairzx81/typebox/pull/1278) - Fix Support nested Union selection when scoring for Cast - [Revision 0.34.36](https://github.com/sinclairzx81/typebox/pull/1276) diff --git a/package-lock.json b/package-lock.json index b4eac3353..2e670f791 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.37", + "version": "0.34.38", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.37", + "version": "0.34.38", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 6ebc41259..2f8ae4f38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.37", + "version": "0.34.38", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", From c54a0f8c20372fff6789054d1d82287a0a13e161 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Fri, 15 Aug 2025 12:12:11 +0900 Subject: [PATCH 366/369] Revision 0.34.39 (#1296) * Guard Array in Object Conversion * Guard Array in Record Conversion * Version | Changelog * TypeScript 5.9.2 --- changelog/0.34.0.md | 2 ++ hammer.mjs | 2 +- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/value/convert/convert.ts | 4 ++-- test/runtime/value/convert/union.ts | 22 ++++++++++++++++++++++ 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 9efa8bfd8..ac7e893ca 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.39](https://github.com/sinclairzx81/typebox/pull/1296) + - Guard for Array in Object and Record conversion - [Revision 0.34.38](https://github.com/sinclairzx81/typebox/pull/1282) - Preserve exact type matches in Union conversion - [Revision 0.34.37](https://github.com/sinclairzx81/typebox/pull/1278) diff --git a/hammer.mjs b/hammer.mjs index 5bde41f39..7ed360b62 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -40,7 +40,7 @@ export async function test_typescript() { '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', '5.5.4', '5.6.2', '5.6.3', '5.7.2', '5.7.3', - '5.8.2', 'next', 'latest' + '5.8.2', '5.8.3', 'next', 'latest' ]) { await shell(`npm install typescript@${version} --no-save`) await test_static() diff --git a/package-lock.json b/package-lock.json index 2e670f791..42240284a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.38", + "version": "0.34.39", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.38", + "version": "0.34.39", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", @@ -17,7 +17,7 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.8.3" + "typescript": "^5.9.2" } }, "node_modules/@andrewbranch/untar.js": { @@ -1814,9 +1814,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3226,9 +3226,9 @@ "dev": true }, "typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 2f8ae4f38..8c57dd0f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.38", + "version": "0.34.39", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -38,6 +38,6 @@ "ajv-formats": "^2.1.1", "mocha": "^11.1.0", "prettier": "^2.7.1", - "typescript": "^5.8.3" + "typescript": "^5.9.2" } } diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index 7b183e599..b23c0bba8 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -200,7 +200,7 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown } // prettier-ignore function FromObject(schema: TObject, references: TSchema[], value: any): unknown { - if(!IsObject(value)) return value + if(!IsObject(value) || IsArray(value)) return value for(const propertyKey of Object.getOwnPropertyNames(schema.properties)) { if(!HasPropertyKey(value, propertyKey)) continue value[propertyKey] = Visit(schema.properties[propertyKey], references, value[propertyKey]) @@ -208,7 +208,7 @@ function FromObject(schema: TObject, references: TSchema[], value: any): unknown return value } function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown { - const isConvertable = IsObject(value) + const isConvertable = IsObject(value) && !IsArray(value) if (!isConvertable) return value const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] diff --git a/test/runtime/value/convert/union.ts b/test/runtime/value/convert/union.ts index fbf207702..03483da86 100644 --- a/test/runtime/value/convert/union.ts +++ b/test/runtime/value/convert/union.ts @@ -77,4 +77,26 @@ describe('value/convert/Union', () => { Assert.IsEqual((A as any).data[0].key2, 42) Assert.IsEqual((B as any).data[0].key2, 42) }) + // ---------------------------------------------------------------- + // https://github.com/sinclairzx81/typebox/issues/1295 + // ---------------------------------------------------------------- + it('Should guard against Array conversion in Object', () => { + const T = Type.Union([ + Type.Object({ + type: Type.Literal('A'), + values: Type.Union([Type.String(), Type.Number()]), + }), + Type.Object({ + type: Type.Literal('B'), + values: Type.String(), + }), + ]) + const converted = Value.Convert(T, [{ type: 'A', values: 1 }]) + Assert.IsEqual(converted, [{ type: 'A', values: 1 }]) + }) + it('Should guard against Array conversion in Record', () => { + const T = Type.Union([Type.Record(Type.String({ pattern: '^values$' }), Type.Union([Type.String(), Type.Number()])), Type.Record(Type.String({ pattern: '^type$' }), Type.Union([Type.String(), Type.Number()]))]) + const converted = Value.Convert(T, [{ type: 'A', values: 1 }]) + Assert.IsEqual(converted, [{ type: 'A', values: 1 }]) + }) }) From 16f1d0a1652e40513d08e20686df11bae5c77bd1 Mon Sep 17 00:00:00 2001 From: Redis Stasa <91160178+DemonHa@users.noreply.github.com> Date: Sat, 16 Aug 2025 23:16:14 -0400 Subject: [PATCH 367/369] Cast: Use Uniform over Reciprocal weighting on Union Select (#1293) * fix: correctly score nested unions * chore: add reference to the test cases --- src/value/cast/cast.ts | 18 ++++---- test/runtime/value/cast/union.ts | 70 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 86149da79..01123fb70 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -57,23 +57,21 @@ export class ValueCastError extends TypeBoxError { } } // ------------------------------------------------------------------ -// The following will score a schema against a value. For objects, -// the score is the tally of points awarded for each property of -// the value. Property points are (1.0 / propertyCount) to prevent -// large property counts biasing results. Properties that match -// literal values are maximally awarded as literals are typically -// used as union discriminator fields. +// The following logic assigns a score to a schema based on how well it matches a given value. +// For object types, the score is calculated by evaluating each property of the value against +// the schema's properties. To avoid bias towards objects with many properties, each property +// contributes equally to the total score. Properties that exactly match literal values receive +// the highest possible score, as literals are often used as discriminators in union types. // ------------------------------------------------------------------ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number { if (schema[Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) { const object = schema as TObject const keys = Object.getOwnPropertyNames(value) const entries = Object.entries(object.properties) - const [point, max] = [1 / entries.length, entries.length] return entries.reduce((acc, [key, schema]) => { - const literal = schema[Kind] === 'Literal' && schema.const === value[key] ? max : 0 - const checks = Check(schema, references, value[key]) ? point : 0 - const exists = keys.includes(key) ? point : 0 + const literal = schema[Kind] === 'Literal' && schema.const === value[key] ? 100 : 0 + const checks = Check(schema, references, value[key]) ? 10 : 0 + const exists = keys.includes(key) ? 1 : 0 return acc + (literal + checks + exists) }, 0) } else if (schema[Kind] === 'Union') { diff --git a/test/runtime/value/cast/union.ts b/test/runtime/value/cast/union.ts index fa86fb0ba..dcc16ceef 100644 --- a/test/runtime/value/cast/union.ts +++ b/test/runtime/value/cast/union.ts @@ -362,4 +362,74 @@ describe('value/cast/Union', () => { }, ) }) + + // ------------------------------------------------------------------------ + // ref: https://github.com/sinclairzx81/typebox/issues/1292 + // ------------------------------------------------------------------------ + it('should correctly score object unions with shared properties #1', () => { + const schema = Type.Union([ + Type.Object({ + summary: Type.Optional(Type.String()), + description: Type.Optional(Type.String()), + parameters: Type.Optional(Type.Array(Type.Any())), + responses: Type.Optional(Type.Record(Type.String(), Type.Any())), + requestBody: Type.Optional(Type.Any()), + }), + Type.Object({ + $ref: Type.String(), + summary: Type.Optional(Type.String()), + }), + ]) + + Assert.IsEqual( + Value.Cast(schema, { + summary: 'Test Summary', + parameters: {}, + }), + { + summary: 'Test Summary', + parameters: [], + }, + ) + }) + + it('should correctly score object unions with shared properties #2', () => { + const A = Type.Union([ + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop3: Type.String(), + }), + Type.Object({ + prop1: Type.String(), + prop2: Type.String(), + prop4: Type.String(), + prop5: Type.String(), + prop6: Type.String(), + prop7: Type.String(), + prop8: Type.String(), + prop9: Type.String(), + prop10: Type.String(), + }), + ]) + + Assert.IsEqual( + Value.Cast(A, { + prop1: '', + prop2: '', + prop7: '', + }), + { + prop1: '', + prop2: '', + prop4: '', + prop5: '', + prop6: '', + prop7: '', + prop8: '', + prop9: '', + prop10: '', + }, + ) + }) }) From 81c3686da5379dbc9562354e86f41c4c472eca50 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sun, 17 Aug 2025 12:29:20 +0900 Subject: [PATCH 368/369] Revision 0.34.40 (#1298) * ChangeLog * Version * Formatting --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- src/value/cast/cast.ts | 12 +++++++----- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index ac7e893ca..364ee9309 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.40](https://github.com/sinclairzx81/typebox/pull/1293) + - Use Uniform over Reciprocal weighting on Cast Union Select - [Revision 0.34.39](https://github.com/sinclairzx81/typebox/pull/1296) - Guard for Array in Object and Record conversion - [Revision 0.34.38](https://github.com/sinclairzx81/typebox/pull/1282) diff --git a/package-lock.json b/package-lock.json index 42240284a..ef0388b1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.39", + "version": "0.34.40", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.39", + "version": "0.34.40", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index 8c57dd0f4..c4f790fcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.39", + "version": "0.34.40", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/value/cast/cast.ts b/src/value/cast/cast.ts index 01123fb70..28fe3028f 100644 --- a/src/value/cast/cast.ts +++ b/src/value/cast/cast.ts @@ -57,11 +57,13 @@ export class ValueCastError extends TypeBoxError { } } // ------------------------------------------------------------------ -// The following logic assigns a score to a schema based on how well it matches a given value. -// For object types, the score is calculated by evaluating each property of the value against -// the schema's properties. To avoid bias towards objects with many properties, each property -// contributes equally to the total score. Properties that exactly match literal values receive -// the highest possible score, as literals are often used as discriminators in union types. +// The following logic assigns a score to a schema based on how well +// it matches a given value. For object types, the score is calculated +// by evaluating each property of the value against the schema's +// properties. To avoid bias towards objects with many properties, +// each property contributes equally to the total score. Properties +// that exactly match literal values receive the highest possible +// score, as literals are often used as discriminators in union types. // ------------------------------------------------------------------ function ScoreUnion(schema: TSchema, references: TSchema[], value: any): number { if (schema[Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) { From e0ec98c9dbbf6f6b8777f121a2da38f2d09cf2ed Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Sat, 30 Aug 2025 04:14:00 +0900 Subject: [PATCH 369/369] Revision 0.34.41 (#1310) * Update Module Resolution for TS7 Node10 Deprecation * Version * ChangeLog --- changelog/0.34.0.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- task/build/cjs/compile.ts | 3 ++- task/build/esm/compile.ts | 1 + task/build/package/create-package-json-redirect.ts | 2 +- tsconfig.json | 4 ++-- 7 files changed, 11 insertions(+), 7 deletions(-) diff --git a/changelog/0.34.0.md b/changelog/0.34.0.md index 364ee9309..db216fb8e 100644 --- a/changelog/0.34.0.md +++ b/changelog/0.34.0.md @@ -3,6 +3,8 @@ --- ### Revision Updates +- [Revision 0.34.41](https://github.com/sinclairzx81/typebox/pull/1310) + - Disable Node10 Module Resolution | TS7 Deprecation Warning. - [Revision 0.34.40](https://github.com/sinclairzx81/typebox/pull/1293) - Use Uniform over Reciprocal weighting on Cast Union Select - [Revision 0.34.39](https://github.com/sinclairzx81/typebox/pull/1296) diff --git a/package-lock.json b/package-lock.json index ef0388b1d..ed66a3f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.34.40", + "version": "0.34.41", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.34.40", + "version": "0.34.41", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index c4f790fcc..fc3c42e3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.34.40", + "version": "0.34.41", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/task/build/cjs/compile.ts b/task/build/cjs/compile.ts index c70aa0d6f..75bd85f59 100644 --- a/task/build/cjs/compile.ts +++ b/task/build/cjs/compile.ts @@ -33,7 +33,8 @@ export async function compile(target: string) { const options = [ `--outDir ${target}`, '--target ES2020', - '--module CommonJS', + '--module Node16', + '--moduleResolution Node16', '--declaration', ].join(' ') await shell(`tsc -p ./src/tsconfig.json ${options}`) diff --git a/task/build/esm/compile.ts b/task/build/esm/compile.ts index 35a9d3722..6d88cfa27 100644 --- a/task/build/esm/compile.ts +++ b/task/build/esm/compile.ts @@ -34,6 +34,7 @@ export async function compile(target: string) { `--outDir ${target}`, '--target ES2020', '--module ESNext', + '--moduleResolution Bundler', '--declaration', ].join(' ') await shell(`tsc -p ./src/tsconfig.json ${options}`) diff --git a/task/build/package/create-package-json-redirect.ts b/task/build/package/create-package-json-redirect.ts index 298477082..5b17ef98a 100644 --- a/task/build/package/create-package-json-redirect.ts +++ b/task/build/package/create-package-json-redirect.ts @@ -38,7 +38,7 @@ function writeRedirect(target: string, submodule: string) { } // -------------------------------------------------------------------------------------------------------------------------- // Builds redirect directories for earlier versions of Node. Note that TypeScript will use these directories to -// resolve types when tsconfig.json is configured for `moduleResolution: 'node'`. This approach is referred to as +// resolve types when tsconfig.json is configured for `moduleResolution: 'Node16'`. This approach is referred to as // `package-json-redirect` and enables correct type resolution in lieu of a correct end user configuration. // // https://github.com/andrewbranch/example-subpath-exports-ts-compat/tree/main/examples/node_modules/package-json-redirects diff --git a/tsconfig.json b/tsconfig.json index 786056048..80620ddf5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,8 @@ "compilerOptions": { "strict": true, "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Node", + "module": "Node16", + "moduleResolution": "Node16", "baseUrl": ".", "paths": { "@sinclair/typebox/compiler": ["src/compiler/index.ts"],

      @Z=8jhQFms!4!w%@;MJ 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 z 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 z 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 z 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℞MErnxhTip%KcyEc0%3x3jhEh07*naRFMo@FxS4;7%qH+^aCFFz~fo} z`!f&T>E?HR%9M4o#TFB~@UPZ$6W*pAtZ_6oK zYaHDIPEE-a7~Rm%U4~-?Ua>^E7w5>McwKi#Hi*~>!_=VXcE)2GANs^Y=Dk`T%Fs3> zyR|*m9szFIdr&5F{Bdo*_-0Z}a?Y4~@>~>G+u>*o4|JINV1Ucy8EtU#dH{(H5(_G; zr7lbxBejlE@|UmOIbf&9Xc;Z;HkEG0CtK|y+uB2i_%fqaKNHU}=^k_LR*rQ!JB$nK z_!ZYQe&oJuuGSqkaAdzn=k@4XIJd-wlQGqEzVto~xd2=TTTVFQX3Zl=9F3XcL#7(1 zu9^+c`q+MCSbNS%jb8u|XVf(?{1#Vnu;$kz&5gtJOrBqjA32BL*n^9&{L&95_jzDR z7ucE5AsW4<3mb6NR@^Ugg@X)<3!H7hkgzqtAiNU5mJ=Hn{GFeU#`fHr!3=&e(d?~p zDb)N2;y@>_VH}>ru^1*r(Tc$!MO-VxWfr-<6F-2ZzK9ZJ-Aim=W6n6{uo{3-y8PU2NTO6?Z2D>6EF{SC@2X* zz>7Y;8DbApmROs$PjXFY$Qx^NV|*2}GEVO_dFz)4;UuYO4fEm&@xQv5fg$VRy<^l6YU0XgK?CnEi zQV-jk*XTEn*mcjuQIf58yRUPH`{>CdIYxxa@nWc2>IIO5{Wz#rb$lhJqN&2{NFj_XA}%KNYd8@?;9 z`Q^+(Ov{70>VjFGnWmmNsiuCy29AC%@Ft!=C&{%3jNc@gN@rdUXmBIz>NT~`xWJDDE5RHlnit< z6DT;g1xrV4`!S%EgYJWFy5WBIr3U^L2CexgPYLirr`}kK(_T_H^M@Zg{bXf#S6SdM zYv8J3-_Xi|acaRx^0^qoxOprb52(o$W#VH?%Y{VYVKz^I7Ka?H!2RG^wv@eA375y9aSmRnu4y_Y8)94w| zo2!L7OzP`JxkMNF;=`l0`1*cGdGbQp_j%4uI{i}TpZ&Rid;HA5{rAVm|G7UcTU@fv zcBVWKaKmTnBEygT$PYW7{k(5D9;}N-#974)v~enH9l5yk>eu|z@vmR;lm3@EUifk$ z=ca4?`!E-G<{}o}3FXLrjPFH)uIHj=Z<}5m9QUfx@O!`m?tgsCH@~nH9UJGKR~=J( zUOdu@=XGIiC=NA}ey@}D2FUuMtUiAfy5V@_Bi_gF?%{J4d%p*k3AHEej4N7%!B@LU z7t5$0`^ca3B^m2#ZgOs18|%Ql>tWdfPAERp>`#95M;$-{2)A?hLZ z3;UVw6N%XWtHDQmKJU?s=w(P*r&%(wPj^;gb zk59jj3g`O;+2BeoU;XM=AJ_CwQ|<%SJ5U99&pme?zy9m5JKj%w(J#;nG`-hSpy0T{ zV;VUHKz@>NgrnxisREo{dwh~(Zo<5%5+Vk1C&?C2YB*&v+OeaM$yaKY(xA1{-r z@q)HpG3al*?RH%ddzb6t7ohr9>0F4*J|_$IKh#oB&9%oKqknz=Tc7b){YMa=w*?)t z-PaY9OgXX^x7~jG@qOR_qsOzK^PJ;;I@gM=GubXFMb4w&e8U@$zw>v$;CP-c0)EH` ze}K<#jIdd8G}iAqx53CXe!v56JwENP{KexDk9frKMUQ*j@%DGVLw~T*bAWm5fi^Y0 zaR3=}9`#(>*N%ndA(M6k_lcs+MSjawoAUBwnkKxSu4gUVT)LhVuxF5F}>xZ`UDHfWtTz0CJ@vC9Z4~4^?T4u<^IxBjRE^5}} z3YKgfhdRy^)az`Ee-g7#`D4FFMP3Xvb?ac|0&T75(w8s2V+O?r8se9mD(q_x;oiq$ z2L&vI0GYbQu5XYa-?F+KC#)GMme1rXcmjl-8?|IjcZ_Hxky&0lO0r`}e(s^zIsfyp zINL9P$q6Xdj|8D{1-GWiy$0rn&(cH=0w)p*J)ga4{_w{6zOeNNIm43(c7t_cbHWmE zarAfzP@o%}i*e64T$>Q4?9=o%=RV1Nnb8JwMtIVEehpXZM6=GZXCkp{SSMS}VduE` z)G&BH3VS_|2UeSyYV8#3RruYnK7Z1fI&dK-)vXVF_4TO$QZ+b3s{5G-galyP?R6o zFyX~d?3vdugtJEjj(Jb$_O(BG3IY`H8Nd@ip5!?mo$^Mn5Pm@{eIEE(E9rX=gTMH| zA6DV3USr-#0AB6EvqnnvZXRc54mM6Td|Z5zg>7(L08=C1FOZW%2HR$XeL6@A-`pF( zh(RgRoQYCczuqKp*}y!w|Fe2JZM zFu>E6;b>jXw^7PA+`Mj+2Y&G~fNjVTS#!&M2W#)7(YSeSTaQTWvgF1gfY3JIIZYOZ zdC3L_x!`M++{7ca%xgWkh+Xyqb{e?|lCdbVPkefA;S2@jFZpwx@Xj0R=J>o@>E4_C?l*f#eghXRAc4WwyaM%ATp+$s9X&0!M5 zp+xoOoAp^{H$QmeXDW;BK5%9_y!{=w9Z!AQ*Xr}mZq|he-Wk^yNm&cFh}Yb;fMevu zbp4b=)(-UO{SLEB772|{M$Ty93kuhR-z9(|nJQiuv7oC*{BU_doAiupWLM>I*#g>f+a}#}E9| zA2~kyPw*Lbx)9y^5SaT8qB~C3ktwlhh|%NMwsPH#P%<~pjFJ6*^Xjmp$^Tpn#+6Bh z&PH?4q-j)c`%$C2`>uPAm%aR_eEacP`ogBSz3r`jO!7rMF5s~~ zIuO?*6{+XaC?`*=W7EgYd928ujXz`B{s#o+mZKvM4@bij9W7oOpq~jJ#lSS&ba91q z+}0d&ACu?ZCx5>yblJq$VI>3B`&@AjeuU=8X@YluvwZPXYK?1K&5bK_WBAEAR^hikwqW)VM!g2$ zI!S(wf0waBh!iVCiO+n1!Bj(+7i&@U^LDJmSdHA037u9|Z=+cMB7lxy9NmFukC8SR05Pwt-Ta6Qi!2HS9q zZRLD^N?5>iM?~%y%;V`j66g5Hm=}BG3r}qENt_t2ygc2}qu*_bzWB>Ad0y{Bjrq;V z?tleb{T|m(Dzp#y7bigZ;djVtaPG_XLJTCwU=Y*r$-GDBYph7%)N={R$>WSs-st$N zZeA2AE8oxN9Z113T>}R*wFu@OXU1M%d}>Dx^X}R5a^6DpgKEr-&mrfgo`Oq$@^Agp1j!Kk`P2YGXgy(b>ZVCpEC~ zf#caS*?BuRi@OId9ORmu!e{p{>a3Mc2V3fJ;>%`Uf8d!~ijz6=PK0wGm#@$ovH{#% z&Iq;0*j}LQ8+5)ej&eO1pBIz#c6Hu~b;HdeH^{CHq_3ttdgOVM7y&Ww(!Z3RAGi+} zedd|+>5C!GLDtki%Fkn+H;u)d)^#7`lNWr{$O()#<|XQPh4C&h#kxz_H@-{fQl6!` zE@1OQ>h8Od-+26&*T3U<$+vyi@p%19AKxTN9$>p}f-N^&tT8G%M%WL{DMC`=)JzM(}gOpb2&_0bMF&K$)=Eq6e72}7$pBNVJBFNw)tG$Oz(kK%- zt(r=V7I`1fVvV7GiE~9C2MML?K11d`Z)M~?w`OT z>fm0sv{8FJqRSXt%vC3>F_!c1{_fk4|MaWBq6>j{jZk7pa=Yus<7GeoW5=7`_^9I% z`i9}NO{5htSYVpZ9J;s9b+5zN7tr>apuRfR-1QmTeGk+fpT=^1g<~ikIxp_RA`bcm zk^%N_dF$Jb7rpRB$DMcFK7aJlE|mN(eLO)uc)ai*DFn!ujXd`ozw!8^fB3_5!L#ul zVuGl1u<6ZXf|Ju*-uk=x*7L7Fe&k1g*!OwYZn`_~1_uLUk@Zn=T?5Jd4dJ?fyhA)c z=kq@Ac>Xs%@A#;X`pDyceEE!wox417TzeG|nqpVad31w({*W#p{_qccpFUski;n;B zAAep3G3MhKV$0b$&Hg+;o5w57ZOuVrzlj4=Kau2NJdYQdQ_u5e2YDl7%wu9;Qg_2h zP`;P;3PG|HmuG^h6jp5O> z0;4tq=+5s7Qiy!fOxC7+=xM~GHNVV?ms*~jY^g(>d^7-d;0>5T$aQ(WZ`<|a6r5}2&Q}P|53{R|bOig?mDi2?1^jhaRI_KD#FF0rO z&KF%`T;g8Z#sB000`|17NBrxzDQ#Ha>?_W1YMxqN@d+%*Ys4V0Z6ZzVt_|A6J2^ET zI$Gz!%$oYet2}idIW}Eu*WR_{dItAhyvDVW6K2T$;k%T4xs29K&ao8Kn?+1 zwYBl!&wA(jRe*~LR>nZh{+QSx>RshP_d!HUg%R9Fu91J}?Khj>gk*#LezPCGbF&i& z_}Mo)t^DAfwt1*C20)x>qXpp0f)afx-J5gn>QX0Fs?IT z8$Nj{0Ht3Ku#G*s;G||97n}ST_fD6w*{TmuG8j(6CARgm(QFR}@wspzJtsrt)1zDV zlh@=s(8k2KIXI3voIE0%JcI|PZ;F4~2Va6<65BaSKK4?qy^BA#J;CiaH6efNh6ipm za_b_h)9bvHHJUDlMgYuvA=2cwb-?te9;LnG4V*8onP~_cJhI9 z^Ti)~u-#`M%<>z|WjFQ{8-Bf5-CTm1+7aKi67J&Zc;NG358l`ypZMs)2OH`6S$<<@ z*=60DLof|uzi^>Pcr=I7wm%o4LeS;gY<;xwR16htw2H0bZ z9Z%Te?>~B&R{VpxVS7JkZY<%o-i?nu=i|ATy$&#)wYiE5LwKFEW6ODzIPFGm9>d>$ zmw9(B%E|j<_7k#}!8O%9IS@bRz-e=yA3moS2Jv#s3y@qqHZFN8L>}O#{u02!-yq8U ztj3P%USqKI$4g0&57auxb z4z8i(sUQ2}#^WX5@vX-%|H^+d5B&Bgn9QjI{%BTXh)6e=jJa;^X3PYBzx~^9IiC8| zrz)m1RX)^6YoK=mx=!jDyGC#xd3x+(AKTQ!e`Fal<~H=!WBtx;?96lIBND|SEdfss zoj{d?nM5PB3Hhl}5{6{8YJktxHui{f!K4P)8-vBKI|7V}<@l>Uul?m;(>r=z;&t`& z4(jSP*FMv?EdRg{evkgO`{!IYM;eS^hA+g6SvJ+L`3&~lybWq_r4AM&m13@+@v-xa;aaij&&1vCUn4;_rq{;uC@=Uov{bg&94lKokS=KHzUZI+ z$p5K}#QFjdo&P+E=f02gj@tOXQm;P$#minHT`qwVvt=Q5mAI&Zk| z=6*+eiHn2Se!wH&|MXe=b_Cy?i*G_4)R56l&whN^T_vm`0ZVy zSP{I*+Ndj0zTd+>_ub98bsJKJ8?4Lt0V2YFW?cyhFC z^6GryX^``>HG@}f_A{qVhYGB*Nyj^{5ubj^L34{Dx8K;r9J$(X_MSH1$f?np7x1&r z*mGYr9J)ul=H`KIYcZJC`+N?6AE$PwFKe6it!&j49Db%R{(e7{I>?{2CVu*?OJx)+ z_vj!z3}r)nTt6 zuGGrOgij9{F+mJZY;^-0ALH2{vv|R7`QtNlM%>VIb1eF4%MTw-;;A0|iBVgW#5RwK zVbHNNR~bhiTYQOq{#|oL? zw04ebTPGU3`fdT^F={i-x2mZ`mwvB4#VHnILiBpoyad5@QJ|hI*bx+_# z#|B^9#E>*BBLpJrW8?LiSdNWYw~KYghc>-yMWzDY?Had#O$&`WkH5IMF3u%!7WSq? zW^PvMu)$o!b#DA6M+O?lutb?l;`0o=%&|+#sg>i{U5c?;u|4Oo>jr1SEZmA)PlLs% zoUyeU$YsZ{b^(F&Qak*tz16Wrzw-=sbjc(48$-)@?4Rb_T$40lc>zNyjV;I8dd=ZM zb6k32IT1t}m1F8ktTV4Ta~>8(F5}Ip4WzRk*;By*4~Js5K9-M9bt?fmReq z)LuViGDPjx`-H@ot)ytdV$5Gz`OHhdV+ap{*+zOUq}XrpHdrjxh?V%x!Cdf0JUY*K zg2oeyJ^^VC>AUver#6@MhtNeH!pw!K#BvBZpAWW)7hKsGIJV@Icm0M7@pvJ4)*JGA zGN66?m0$e~eW?F!lL7J>PS^_{RGXfVZt9!6^_p`b;`@H^M~~n6o!^;SC!|Z}7d3e@ zQhVp`0;fLY#f|vq6FkWjkQdD~bAxAjSc*cTO~x4Lfw$hKBVTSSXG!co0;ZZ@U`j?^ zm+Ukx++bvy9oNtebec97GOTUK)Zwjfd;9U|$9&Oolisz;zi|_ny7 zFFaoN`Zq}L1kRj?FM0K%oTJAqL^P&Gu5$B1KjX@?E^)!Za;BT@Xf5&>oo>ajtTC93 z-f|tfR;@2{_Vmp+-{Rb;7cMkl{Fii$M;LuA#t7rKQ5k>vMA?`%?Rt1#KLxswLvZr3 zN$u!I4mXA?w+b3aea`t9)vo=*5s2XdV=yT#b#^*dc>o3Rb5&p0+dqw9Cd*+4T z{3883`8(yTW>OhmcZBn%zt4Kv38S=Czluz@_gA7j^TuUPE!i86RlVS>180-jwjju* zUv?97g_izz^zOf}f9A6Sv1%ek0{edV9{n+ocZq$>pZ+t)cYNEo9v}39kM!e3_p8Gd zlr;A-)0uNW`1;>`UZ!FG*_j=EFKc4jsPdgs0+iB#n$=8iUl# z^O2*+4&C0@kDmNPKlx-5)E;d!SixDED;&rZoW!gZef$zD@rT!L|A64&uHMmJKQL&ZS9_RH9y z>-m$lk2S~QB~W5i>vMZZ*p4DI*Nxb;y$|EbBg!!kTRYfC5Z(ltlrLC}xfR9XF(WVi z&|kL5j8Vndlym#o{2us3yb6tA2+-{}o0c4$8@mJYQ!C$C5FnHXDGssA6O2f(zQU78PDHr+ zamFjx(IKr&wmXO5hMRFD*ln%H$MRU$pPBjV2?Yb>t6o-6i;;}pIa!9m_#1yX+RrKy zbxyH6vB{H^6EwDa+hldB6pMzvIct_yo?lwhd>W zeN7>W5*M1MLo!o~%K`l6l~@zfRykb8D#(esna)tU{4UjX)(YBOpqeM0HHWmu?otbK za#|%Tef{Hg838XGV!pr@Mtq5!7Q1uq;3ANXZ1T7c0pm;D<(!t+`2L zxmBJd=fGqfkl1NlKg5_huKjbr(t4P4NG%+E_ZmBlxV1NoFnKYyoEXA?Zpg9s{HFiJ#UxQ_jDdU*tcr;DOxhELZ#c08n1gkrR6I ze%0i<9TH641gIRNEc`ztZ_y4h7&D{l!rMpnihv`60f8s4sVj6LP)&&)X| zXYUbgz|y`+eHg#%c*|SfeEiJ6`FDC}*|_lM;BwzvK6@${!Z>-g>8dZT^7;69&k7Umtv z`0`G+oAeo_pYb<7^LX~NzV>+U_kNH0H({usd!6trEOF#qPQ>P2*1xK6U;gyJ{x^@G z*T38H<#4P)FNAe}dM)J6oOfxX$Df{eWyYS!`NNHt?s)7MKIZsm|Hr>LZolKsRgdO> zT@N?4#nofJz~#Zm;~)D4$1|Sswa2Zu-fC`6!qz#!sg2s?78w`Wv+t3naWR<-nS8NR zmPftRaPNhK)VveKBs#;%+fc9`bIX8e`*RSK~k`*ont9 zoantC%Ny%H|F%%pkR(DRz4zk zeC%jdVrp|NE*mBewFc3juTR3IP}Vw-Boz+6rv{6vIk=zDelV_It=ckCFB6p1$m^WID( zC&Gy^!G(YNkJd302t@GsAt8P}Hnzs~caMJJfI(Al4D`~uIHU78R^fmx50G&i4Ump} zF&`@C;~Evac$(A7+kYLaY`FAYjN_7Au!(EMnIbE14VQ^!lbfW?uZ&l%7TSWb#G7Zp z$lF65z;Wde%zT3&G1sXwaBMF@H)Q5=9^SU-n4TCj2ZwRj#N)~5w1G?-Se^}O#;2Xe zHB{ePAY|Nm`Py`GG0Wv$Tk~=dXltNf``6DRS!>=L46+mH7+dT%E{spx?<9Sv9+9v zb)8)R*7~Mu5+(|mspRyCkx5Y=AYfBFb49CP`xBe74Sig;1{plRLpo|32k*qS?G}`J z@?#75pd%nM_05T};lv1N0%~AR30PSAz}U*R>>plG^&J+$n0H)w0^S)93Oz4S8&HS* z3`N2wv~l}{-0#Mcvtuf**U)|6?Iw2mA3Um ze7+V@3b*@0=M#l^}`}#&FPF4IPI1P2I-Da&)mgSuW3|t7J^a zVP+F6c}lj$zTknE)Q%$?bC>AC2DyHaQG<8tU0N^t)|Vd7eeQGS-#2xEfITI@yLnfa zzWBobLdd@p>Y@h!TKI2&_Gk3DV?XB4474F}r-7?qA2Md|YYt$0^!L@UQ44PwlZ$Tl zXWB7#Ge7Ndj196jLV8U`U@XQcrk#3rYvK{Ydq+V%rk~po42G2$pSKmGGO6}>wzX*P zC%0UPj~fT_tt}$jswKM3&$hNW{XMPCj>UKy@vdu&#Hr!jebvvs`uNG0zswh+h|M`) zew?>{9;y364aDsx{fp{9IR5Dm{a?r1^+gry0#5my&>mgnTCS6nIhJ#Ec)iWZt zX4mwkCQtg3Cmm1t;>YN-{~oM&0_GiZC)z|-x1u20^5G9CuYS#Ik5B*fzj6H6H@-m^ z8}fOqxwuDc9*43=Ie+sp2)adzFa8$g`fiFp|C)y{gv4f{ENm9Ri!Enc3s>N$;GQT8~*paRMe`#{g67HOb zgy-;g4w|K>mYQrf-s{ZCQ3}`mtTIBkg<~DY0WqF^BdQ%vaA)ouY{LN#NH!83sy80a zjx#Ydb3M_cX=0hI(aU6}uIvCiV#Yi1zzAmJV4gPo&qj_rRhOC3?irYE96u$E~=xBZ?XPfa3 zV$3I|^J6am$k8PQj*;Sda>I6yRX%XAbBvP{xV=dkzvK%)+NwvUmR)y{Exc&$GYePa z5?{?QgU!4-(1)um;DnN+cZ?}Ew!@zxIOXHHY%N1)Kcv_pjeQD|xrK9Z*v3|$ zxfsvY+%>;&l60UNkG^?g%7d=wHjOb@+{z2PEb1r*bk7YN8nXJ)uej)RYkswf6wi&< z5O4?HR;$*xI0NdKi=%|T07N=Tq`}oAI2$+<&T$sT9&^m$+}d?~=0OH(m+-?KFDdkU z+6&81AZCH^-G}hH$`u1+#P-C(IgRkHjcn~N7wqoK)*k4|wYcM#`VJ_VGqkB}7dHaZ z;5y^@Dx!=Xx6Zc1c4F>wbZU#|!iu~)b6sqr8&kzQ{mrj&24Znuai>Pu8B@nqiy7Ou z5du_>*n*jNUr9G+*KwS9R>P4Pn0I|qjE!HMS_xbLZJxv2S4mz>V_+BCxYiFNI3{NC zY$M^&xWcDc^3AWh1Piy|fk7VC*#xtR5V{GFb}jb|dC$#?CN2Ei{ubl+UYQ^GnCS_?Q2DJtjei7rNw* z)B4e4mfewIBw}=p;okOZ+{RE~=DpSpmVByiJ2xb~poIf5Ct3 zK{3`g_deDfjEd`zJuUd^&k#}6Ax75o@Akj(8?QV5;XizWV&&qMKkHOijfmAf(;9KH zk9~6Iop&E!_T(=+zUXmZa{T6RzQON=BQ5hywBM6o6d?ny0DnM$zvnx*ABB`*E|MD&$*2sYeY}uTV`oYzv`=>e0syeEivu`4h(rU-$xlxk%zTBfIC&y?JuBT!E-d?&ox$qt`9IKUPd$!{qUZcxoG` zJid_w!YEu*gT{X6I0)A6cFl9|gGTHHJ^2nFP!=H# zz&4!6ailbH)-ids0?ET_JAya7>=C<_o^0%|p4cmz%?2w$49Q_}mkqw2kH5!~lLO<{ zigbbOym||T!Q8h-k^S-qPd}N*fU$Y+eCoUPP*1G#9wOsc+~u+gg9OI)%h+@H^0Vc5 zYVBgK<%h&^hOs%#H3nNR=is7W&BmEQjlA26ugds`7&NUSR*c#huDw)vT#aCn@Q4M+Ujl3QlzX}dm> zIgVTP&6E3FRAxWe#-(oD(Mx$obO4BuoPo>Jxs2tKQ7c8$J(=}TLkoV0TYbdpc zV_Q7)vTQeGZSq)l@uxFPmg5HKfW&VyB+XpyEYwLJUBcBW^UhMRY!WbxHrUMFm{x)wF zR|Gi)FV+`FDMDFZ3|tS;2AO$-UN~ZU)((4ajZM6ahud%fqPC1LFtCBcG{*c8{$v!~ z=nQb-W7jR#!eVejubj#^_{p{TS|%Un9uJS^8Sc!|j5RUBJdBf$4@97Hu1(jB^E;spg=3OC&*mYk;-aNkf5UGWPya>E#1V$w|4$xM&l*qMLcZduNW&%_N(PO&|B9B z@p+MjFG_Sg2;cLrJN-_n`k{sg`IHY7*PJm%YRKO7rZ*pV+^!d6VCmKuI*vUtGZt6$ zx)lJN+=5s;gFC<)odcOJUh>ArL~Mal1IZlc^jdSTsePivj&tjK{>k?ozwisc;vB32 zr!DkMmbKee7Zcg>ZlbSz%2V}XFyC6qYmDSPYc;X+=T zSg|>el`r|_K1%>#+(&EOQT>0My=~A&?ONA$?fpEPEpG*EG|S<|1cLhDhn%sQY{r+&RDUzB+f;b2F!bvZH#pjgNHXg4yBhxg^vao_iU{H@35r5@k+{onU^ zmH(a_7ti&)7wEuwCF6jB_FPziiO)f-8BmW*PQ4&jVY1qWDk0{*OMs{y+OBe?Wi@ zUpBZ6zY1m>8F;5<%zu9Wr#|zS`?qoBl=JHGJ-l!05NG+vh-lPfj{!e`kpNz!EfwP3 zVq(iz|1*6t?tC_jTH%=eVGLTAPFB`@I_tKzBYb&cKV<5v+~I=2$T3ly6F%0XijI#C z@9w8vzp+I;m{YpUBXCm1z+ovZ@^ugeSU#wTy|IUPA!3(r#%0O$o)>WfWyhc|Mf1ae za%-FzXnS=VQ_b1PnxHhgj5oIxHx>qa-c3~PZr3>3ug>*4rfKl+zSHp%y666sqp%@y z1+Krm5Z|~p*#>f(+U6Lsb`&M8`%@--?;1HRugYR*NP@I&{g?ZZ5}%xOKw4~iWSKQi z2Ko-<)>DJntd96QruPN*;+($NRk(e!t%NjW28rse*TfiH(U(^UZ4Kb)TEP8gOHMD_ z`aH=8Q`ytFlRFOMU3azFSO=WpPW*gt9OjOXEC~6|)JE^M;N*+281>U1S@hM^6HIwA zr2uxspH@+AR-|1O#(0vy;2fPX;pNvDPuKHkH1OcOyiJ;AQEG`d7;Cb8&wK_jBj!-o z@5*PSldCRKS_L?~0P_PR$GJ*4Q^sx?rR_p3sh2={#BLW^f=dDja566%h#AdfPnghL z94u@EitpvuIlAsJXl~jJm&0H}w;PamB%itQf-^-K{=G8jO`|pH2NDjecv0jdBBK!(?oq zhH|oVcYS#hw5*2DZ(9gKV7cFVybh3Qn!>asz^WgSh*&{;7@ixm&#m{(?;N zzuVuKGQ8PiyD^tYJ!h?lHxyZ=wcdVZ=xLQ_n>f5&*{AU6DrkPOA-}v%d}_A2x= zg9XQ8vu^cYjQS3)x;r{AxX=1aeksa0nuUDsS3I>~YTDF= zHts8O zml^-zmw!b*N%U@iB~6^jcL^_?6GG&>oWG^@m%sCWef*st{DJ(c9iOr>6Laa!Y%gY7 zhbs?wXi1OHnKzlrS}WsiP#9pMJ-+1IH6U@gi-0|IOci91#{Aya7r$rSIIS;w3^Lbt z#*0;5_zuXf9sSCd2f6X2|@x71Vp9g=R`|Cb0fAi~G^Y9eZxA^HS zF1+}}8+G2A(8GfpYr`b~7mYl0<@F;!`ooXU|BYYx_?oZ#hWr=Q|0fTtKiL0T9UPCt zIQ4j(oLVtL_CyFzw!8jFZ@mUwC5knuiNuqW%+N&{m-)g6A5m<*$?t{ z=Ik2@vd5$2`0(KD-~G}ref*|><4Ydj^F80|KM3JLp!=6eO4D4WN8Tj%ltgXLc5^~- zw_5`+umL7X?tZBH7qZ8AT94QM3j9CFe;ogdc@XzI{+%y<{KhZ(;>Ua6_j6+Q@6+{x zxDP|kzsE-F@cQHZ`5T)(dTv1(CKNfE=LVH)b^q|^-~ae?fBrAzAMbqG;{zYcpF4=| zwGZ<*72{!Iq|NcjoLaV?8&`C78Kq~fXs#KtBkMzd{PQ3%Ys@iCo&L)|{>_iC`P#34 z{LIh1*SI>ei5+eCG3zc?8Q5*k5~KW(UAp_s^Ou~;{cfii zx8ZtW9T8Az5TD+8MGl{+#xCFc1Ca}l{bu5ieX71lgWY{=g}k?0OZ_Tsm9kxo$xEo^ zaqk^xW6wGVjBhZ3oO!7<{@OI+t-ksY`nd&nPkiwk+wBPbJph0Q$EC+xZ(+t z(b`{-hEko4K8ed*uFHMqq=tSPwxd zjw6jXuM4w0HC~=Cc&)9x#0oZlThFi2-h7P)#uBp*7l$E2V24|OW;?!f7HTk#e!^}H z(b))j0$T?v_Nt2fN3AJe-Dh(y_!Tf-odIKO&IETnJ5R>6W9#gNV0{&j<>0u`ck{tpnAVBeR-~H#$}@4V z{QZ;#{$Ni#wHTQ>M0e0)3I-m$#cHo@^2-{!j9>*<9b>GQr+|&uEvne&0)ZeRXRIu< zjlU3Uhh7;L7!f5r{CgQr6cw+~n9D=+*fQ^ywE>1+D=fv^I1NM0Sd0nr)B_lqV?A{q zTI6uUZJ@@L#?`*K(;c#MfUVELg+4{a^aJ1Pv7g$~eTHgsU-QvO>yu~oLF7Sr4 zFUnxuF~;9)MqbAtZk@>KzHZBr-kLV&7+t(2U!>gzPLtMqY@@V}Dya<+{8St==$_m0 zMI2-Hn~Ud5+vhnodTM|pdJ2-sIT&gUC)z!ZE@$%s*NlK(`3C9D*ydJLk+T78?K8?X zH+C0B<6~`UVk&AT`=ICk)7On5=aZm>^+!(qjB)SXCq-6A5IMw^bs|Qwhi^>-#f^bR z<{~~4L~uk$OJ_Y77ZFC_OvAT-KSv{dmgky*=lBu`+-bJ2=OCDn+`cgU&<8*8_^YMSGD3BasT85O!9|&Gk-k~ z0p9iF?|S^^-|{7o-~GG3{PAo5#ee?saUb*1a=kAFR8EVZbNo}2fACX3^Z2T-{_5h# zjvKINhIjw~AOJ~3K~&Y=(`FKz(`GF06JWTB4{)q|-)H{h4j!)k$19U#ADOgVWex>K^%eb>GCdvCx<6;2~cpOaj7#n)klN{=3Jge(I+@{<&ZMs~`WuulY5PU-8R7)qf1|(fQTKx8;*iemW;O zJ#Jesa@^+e=6idjiKzwlyvJ*lEdvWNUhtivbymNURS>f>V`O}-386mncuyX5{F`6$ zZ|A`m4?MD8Y2RS@FrvoqfB*aal}!FqvMn+i7*oNRMW(;1v$>caQ3myo!^y+JCB97V zZj5`v#`SpPW{#-kq#A1*ncO1XqF2wao4)H^?|%HDum0M{cmDP7>?dt49S@6o9^P7R zkLQkw&9O@UyKd}bufLuLSJ_|w&JX?2<4b-jh#2{!f4YTOZ%}&EJ%Nmh%s@ z53}z4^Ne68PvY>k7^TqntVjM~4*DPXTR)Kf>V1#5=O1IxY73jJ#yVzJeqiSBoPOW~ zIi~)H$Jc({*T&30e(o`8P2z+f=RVHO$UN@=p2qy@u?;`hptjZVu#j^=fWXVhTiQ9i z>Hf^O{F%r5@~gmK@#SCk_(wke<6~A^{Ao;2QBjGaN)CK_{X_f{80&MbMJ>ixjOL?V zKRcQ8}8|DPYG)AMjBqsHAZbVpAUGj39O^Ydnb? zLoNaHea{#zBEG=G$&O)K&n?TNJfHFj^odoMY^f1JE?udYhh|NqHpHH&+k37*c*N~w z`SYILd)L+s?b-m9y-Z%&Ok3ogV?RndlD_xH3`NxK9Iu-WXVRKq@idL!$eA84;1!VC z(X<2W7HD;c+aain`CfjNX(CMH9*kfww0S4|y&~mBNnB^W2at6m#(`(pzp`RZJJ zvNO{SE={6Dx=MQ5%vZepw8mUTjok(l*rj-n4VIUJMIP|s*XY$iF1B>{wz2QByAiRr z!B32l$qgmk@+B_}4nU;DTntssW!$VGdg)C)jT4!e(QMrpM@DV}2nr4Uzf{HL<6e zlS8-zIx!ah$tSin3pwk5Xif0`Ie9W#9wM`h%8p@T@Y|HHkp^W&9*)he{+)X#MhS# zq!iU1!xbwS_QJ&9*ofa`{K~6u(}6$AjRQN>+MC)2)@n8-@l>0Qw`MQr$Xj26;CcaD z4-7Uh#YVW_;YjWL2`xI!V)&x< zP`dJTeu82@F_d*qOS`NXI*K=($la;TJb z>w0r+e>k5?xLVg#jw6Kk!M_ZTcCnY&$#inu9PFnCc1y|BHqWNnPb@{jF`in!mQPSn zlP8QY*!_R;YSS7a^ClJ{-}JY(Gvwty506l!*4Kk1J_(p#1^d)bfBe?p`dc3F_~du^ zX&R%)uIm*ieDVk4d1&*e{^Xx}y!$8LrJWbry6KLu~xc%vMj zeLrHoXr?_mzmoUGS6<2cPE;H_=1gsGdVI(k|E&ot-v>z{G8aO>;q!j|8Ef3z_+lM9b z{XZe#Z4_V}j&E!N-e2Z1Na|q48N ze%s?SKI5N!;(;lA;OqX#dd-|zhckNC81%_)9|$n#1ugpBuRE`7eemx-F&qbLG0?x+ zcxCj9@AY%+`MYpG^20y!_^aRfm-7Ib>$YNV&E=`6$AtM!mBF*NAvc`)|&2M<*D(`Q@jT_f`3VYAnd94q0<`H1rnJzQBc#vE6?Z^D&d z&*ho~TtBIowKcxQ>9NNg{8zaSbiJV42Y4UMuiyUkPyclEbBq#q><7YJcAugKH1KGY5x)LS;o2|;)@#~$O>=WTDxxi!~9iRlm807|n4)AP?fKI^kS%ku%h z+RJgxxsvxZ>cQGkXKMLM@_r@f&ky9k3IC<<_=}Hs|M-uKMcknxv8@%c&{4BJzh&4) z0^!Yhh&6^|o$E7JcJiJ5l|bARi8*miAIo4GZv@MuHFITdutB(qtS4L}tbMRdiuLAC zQe?m3TF4l)2GOrA^;cu!I zYg4SKY_4U&p7|T2+}DQpaqr1;{^D=4LSo~IO`PFJf9e9^8{W&-AOamZQdlyLc<3F2rff=0T3M&BOK1 zG0qRNc|WLrDmMNHZw4=8-}NtRc-Nir@`|8UIhxxbnp_O;GoV-V-{xV(bqwcI>vFSR zPEY+W!2jl!7_HIiN4a^dEww$^uKR6mn5S{va8KYpIJh+YSnC*GK1 zALo73)}5g{g^O45A-hDd>l@=F5j%+nNSo*h+>3}T^6S?_t0i(0AKMcz^J7E&m-Cg; zZ@iUFBIWnSC#{PG%U#T@v~Ru?gf|;_FBj_pcAACr4f8hdaEhG^u_S+d;7z-V+p+Ri zl7qmy#Z2pK(|7_jHYXqI$;DXCKBhvvB8}m#Gl#(&CdU>Z+t$ky?yOxD=DPgXH2vZr zdKtHvIr-!Q$cvxb3G$`Ak>YS_HF($&W3hU&$n1qJcCXOA7&h2O0nb*n8m<@ns2wogTUp*Id5&vGRZxx1iBn#+j^ zS#tr%xzvlvyxDlOMJ;VuwAJtV6C+m+ce}8Ft8ob!f2YmaIgl%pT;~<<+Kpp`Wm@iU z$r}xGFM4@H%op{fX?IakHY$0n?d}_zZhZaOM0(?1xOjR4G90p&?vFWt5?wrM*oawg zZm9ac!^0eobIzUTSRYF;`Wi<&WDHi<+{&vm$FU$@OT9J25Bbz2uKSRuIZrm};PWcy z$^h`jy${PQNBp?)1Bu>?rQ|H9@M3>9aBx27Ge7(BjodT1YN=zJXxi<=E#z?DwZ*6VH4m*m z>hYJp?OPwe*av_&2!;(Ur_tDf^}&34>i2);*F3)UTmR?Ch{={4cbqZ_Z|jwRqCrjI z$4yQj29k#^+IF2|kE6bX#-Nk?nyr4k=O!(Y=K_!%`!FX6sUHs~Rel z=4Sr`A9#QKIhJ_Xd9UXv); zf3{HvUU+akaA6Mq{i3(}^+;83l*L5#Bx6ml>Km;gpscOyIdKt+b1-I*4iwe_$zB^< z%RJP1EuZS7M)2L%{gIi@`jwn4D$n-HsdD_*qWeMoLyG-oZQ-ysj^>k25qBz2-gkO# z@qti|8{T_*>}VRRAk>}A0kbic%q{iDa5`>zoT&~+;4q%pRYk?N^EVd6{e`vCBx z@+rL!^jw;FYNJEHb-9&FJ#zWDWm9glQPp$w@>GORUf^C2k`YA?P1RJQHgX(%9tg1( zAIUMPZRFZ_bMqNI^TVjEd>K!mkvGRoDsqkDaWq*KA01BPorJ z-80gx_k@;@4p=?Mx%-{*U~h~q<#EMO9_a(O*XTS9>ORAd) z3A~`s&K#FqYjAw)@#ObxduA`7$cV-o@ZH)@t;D+=y03P<%t_<=fpHzHgDLl%hxP{` zUZdaE8>eZm%?XtOzSg@2XCKKl8ufcjIgbc;#tU=Sdj;w`x#vO+;7E);b^zJhFh1|S zEzpUyF*f(R-^Pfsq)f@l^X8r#nrkg~9Uu^Cj_tj_J=c}t)9atGg&TIlXPfWkb+1*d z-q5ygaskuprB*=QVBZ~)Zv%6ApZi<)%iDTr9It%Ki+T7iH`N;l4iqfyiLY|5?dHBt z3n4E%Fle|-Y-`fE#O$$?=NY_zuf0yQK}LLa1C%*mxsxbRu}hLIFMm3LL7Hv@?pPtC zw!P?)uHX~1tZQ^gjMES+N<4KQ-FEoUHi-<~85KUexDdp?`3M-~Iqahl!@-5fGTm9#a(4%=5+wv>GOlfe6i;zN zU)jpxOn1LqTzG;rIYVbj2kO2ai%Fcj&7m_?+7VDNKs3R}5)A3hi>cV%(GGUR+Jk4^ zfy|5R^X28Q^+e2t-15VoCP&iCS7WEoK?rW!HCoB`gwhc1Of=m=Wr{*xZUZHUjG2m& zk?|*Iq!)v=0)v3@U@h==jx4P}N47CgVf!@SH>DOe1_6qHZ_*Ehe)!YGz=)W1@qshn z$kgY}Sh>YO`&?7k70dV%i(4pU{;K_8e&i$ExYW~Mqs#e@5H#M0I6wL;O2mZs(GnXg zHw>nAUR7g1Y3g*jWv*>v&B${qao}2R?ag%_;N+TTFlx8CgT)IpD{P(!%7yQovq(}T4pfVlr8VD%ueFSqr=x-(GuJAdT2F}!hG;0&(GXI8O4`dt&(@ji2mtqn>syjx)uMn8>m+)n{v#qf_X@H(#?cAvi_GxUQ3F zMgB+AVDRb3J<;)ctT#STA6cQO8>Z6) zJ2j5oVKzP=PR_>5zC@3nW^Jao4zVXc+gr+EX4=+fy{o~TFz{tV--p=axqqHQ12B2t z2}gh6R4nGMNBYAFIDn@yhPw~peq z)Rsuf2{UfX6LWFs)4-si&-c|fmV7$d{YBf$az@X- zxA9CNlU=8SiowfvPdBl&M9dBIg+^Q`gDYrp6UvJ+{mEzK*yOvlN6z=f4f~Dp6dx{QyD`DQmv7GhZD29#zSmLh zjJalGbKD0@E{i)wwr-PKRKCX@g4Ip6fyL{GhsSHbiXCiBK5wIUy! z8|FzlvW>f_&aoYTLo(i2^w2S{bX;?!HD$(~D;ot^;BZ}2TxLMjb~)}Gofhs8&rHDY z!@qkZ9E#a32yH}I*@-iJ4pVrHKgP>L-{DBU)b@O`j`0a{_wPQVuaRqYY~ka*!%A-Y zi6P_gcslMdBa!8^S;2d`q098ukzD{P`et@^%1J=njEmhQ$;&thHxR=#BNGO@qAP2;z69XHX7Js$LS){%rIs zyRm`!;32>3?))ncJKnut##|`-3&A?wbh3``?tDa>RT0#kcflztE%gDh2XtiAj0-1~ z%QqHR*kT%|ep}0}8i*H%0lIZ!6CX@*3w}aia3<^zbMgX%od=HT#pzUY>v8;mh)quZ zh!&EY1ln?5-APy-VUo=_t*teR?75CJA5Ma2bNB^V9ur`BY<+fp?zLmAKbYho_9D}R zLPJ}9I;N_`GRTjpWG&6i)nGdwMYH1*eQEP_{WNCYB}SD4WSa;m9IF z>KfkVs*T|2$_oy~kxYUT&zdP7-IGbsO+HZaPdvy@{suUk)Hh#X9^(wPKbV)zSqtD! zEiQcYax+Vr`mo?5+;|hhrn6IPhz#AtTdphHn7dEZ9K5ogQ0b;NLJ@=S+Mq*A2ye&$ z;U{0pV+ZeW%r8#)@CX}>y|0ZaIY~c#b3_IR8**mc*8DRDQ&D^~z>oOWt!^0A!g;ML z0kg3*7$oflx$rhObkvg46D{Zjc5v3%h=@glsBiI2_q z|IHtKyz?)9dwx>Pry^^^10mw9&BlDb-^UnYnw`A%#=g8^(wx2NP5^!#@TtYb+-^O3 zJiB4gYUFWZM!CTbj_;YHk#%I{nLGmMCoy>7G7XyKzZaf}ncK(L^+%Smgl6ms6#|%dQ$9eAUV7N@TIyQs z5i{zN7~<#{QTlOlP_!K12%(t`^_d3`jQOn+DX`CKRvS6u0tSIjTYVqa_z)@po?l~z zrL`Ite3_$Pl+!P6^xDN2-HuP< z=B@4J+i=T96l=>Z9{XwQw)xDwX3?`hW>chsPLmF*ZfRDA&gwrarIoPFZwq1P{~(5vvYBy zcIL2_4drnik=f#xcwCEh4ajZkkByXA_fVUC=t8?>aQDLn!{ctlcwJA0hnif-Em@jB zL!&)o6MM>oX&M_?W%P6%iG(QKrxvr8=-O6J?Y2a6985ZVDmz{y0NbY~tH3g^c;K*r zXywWvO+dO?Q)JP%u4`KEsA%q=I$m~L2luVfZ5;Q7_>rIEiNEvad+C-cX7`$%dLRF# zU%_IFJ4L$3fX`j0J^91eG7az%sl0e2r#+i>8ku;tkT=7tuEkPD9l{STM?aXz|9sPwZ3v~+{Q02y%IzGC{BEE(m1~I`w)%`hF|2vt2VXWzhCb@5|LR1XY*ek z<~BSxZUe+A-1mp`%6vbbV0WHD_5Tz|fLM#Lqv(+mhZBCftrHfG2X8s7{P->A)FMvT z`A6ffl`*ho?Zh5FljEpHQyuY*GwaEUjnL}WOo7kaw193+YC!UeJ5a{zANnHu_hBj!6 zrOU|bsRtO4JVhFN3;z;xk|Fq6nDO7@bSmn_b#RD|AG`auT@dGq?ZObJ#G2q5zvwZ+ zUt@fR`^Le1wAIP~9vh1`und`APOvNm$ zuBXk&&1Vd|Ck)ap*E{&eSq{RGL%_?U78h1@;4kcr#5rkBeAi5J_{5>#>A+UuW@8&~ z@dh8KBL{}iN0)i_5n3_SI$zoPZZ4b0TH<$f<{i&fZsFzKpvzT!Gl~D&;Tm7~Z5-@W z<9=|JI@b<|yFPZydo1mWUA`+9QunRXYSJU$x=)^P=mlV7a1rW_b!?Y=&zmA`#3uL}z^8V*A0T$UMy~CwuVgg`{K??taAJwGHq?J(UODu*7qEGO8bWaE{x|-E z)dv+l?xtjQ9zQ1L0X<%JI8W@6(-CK^CzGr)cIU~HPC!Wbej!uQ=yhrxt!cRPum*HC z)N*rDhS-})bR)^lA76FCv2W_k-48Mc+x-yb0qS7mYCky!ytNQ^#*v6Yee*guiit1& z@NwU=fZ6YIZZI%;oi;mZhmbjkOviNbB*x1bS8y7pmPEbckY6ISg zHsL8cxmL%G_3Z{(bJiJ@*3SojgX5?AScA;>6O*Ne0g2-{#twHJ=92?C@+rO3GM1J- z0JHz5Z#^zs=HR^UK6izBD?68%(It6m%9=E0FjdDFOmVZZMH0L2A3&h36*Z#yE1(c$ z95ushACHjZRhM!TA6Gb`&fkRO*Zc8fzxr_I{m-a?@r`pD`WS(|4IgLTT;J-VIr&x3 zF#>mNk!{xxoI7UPwTts*O1mY4@c@KvY@_v?*|^}9 z3B3540GBpO^tSY3Wa7H;{+haOEv#*H;@oVDOPm53DWQgKv4;^R8v})m>ZaZEzPqkE z#c$?x@D|farir?BX%JK0l=YNfLG&490OUb>$IO(Jlg;OAOJ~3 zK~y=5pN%1$a9p>EmtkYP(Qnhg;>P$E=A_9$!r%^C=J$urxcTFYCDL&6a$k+#d3ZR1 zfxNeSPS$j}V3cv8d(L8fc!P7B>pR%4)lFx-=nhTCF#uwOon83MeMP_f9QIq1jWMM` zH7#EA{=oG+zTn93vdE~q#a*6{+p*dL&{h_DGk5>=*A&pV3Zvc?KwYo-tEy2cTv2Lcn@NhA2aW~;p zq}Uisd{Q&RVn*gg79ue2Z`xSI5pQ(`dE$bo;5RW?`c>wI22sI(HoML4!&%bUZ!V3K z?O>oeyzZ{(wBuJ?9N?u&khE14x0+Coe=TNYwb`HFFI0mqTORF(ikrUij6% zv5}8|`Xkf);<~le7<=*79?IoYZU!h;?8Ka2|KvxC)YLAUvMAocSfy6opyQkP`rPmr zL(8#gpo?!${;^nnhFiz0FOS}xJ)ssZzGa+&<4&Jy7ZJZAwRO@XP}e#Y!6XKQawHvJ z+MlW*<5E3s=&Y^rm8CMiv9&I(59bUnp)O1S;3?F>BCwLK6_b2--KMWcc*OPq?ieM) z=SD8d9q82gn1R3RX&?Fi1ez>X21e)OXm{;bgg*AY#{(&^a+-9tK{Qfrq{wLYBi&}6 zPB}ddzs2$<73aK^P2Zf${cwuG#lI1c55W5F@iRdpyYD-VTbn5G!oZH3_z!1YmS5L* z@HS8^v496|t%0GH1JoE90LvsVeYoj9O7x;&lvmo|`9@pGQp;>3jRHybNgo2mk6*#_ z6Id}Tx50)3ulRrQ7yahP=l!dn`*?f)Aq29$F})*85Br-C?7#9?zx(my?|v6S<1H|U2EbmNba)=@`rb{3;fNWyr_E5P z2(3vBIh-30SiIUZd?g!2F#;P@Y%adF1$up3(BjlN65{B8vs;ofUHQd#)o`opU`nv| zsrEd$;Lf!9))slOUgy_1b>$%qH<_&uFv}Hg%Wt_p9~ZaS@$Xs{0l%%Eh!9UBmi5n+ zp0EBYXq15i5nuBxX=`j;=HlpV{-Bm4oX^iNJX^@Y^EC8OfFtOe14y;=TFkAP|F=vDfU&gf7Z*RbmR>Kw@ zylX@K8V7Ex2ZL5V9bk`v>mr6)v5LR^W1oUpU;5aJix?tEk&JIH=8(K8Ria1|rR6zB5n4eD4c+VVJf5{S3@Ier7+aSRZn@x}kXZ<~_B zY@BS4>GdhQLYr+7BRJ;lC)2K6DtI~;7CI`pb8I3Sis>>_P~Wp95CrZ?C}98VI%$h9 zc7s6-AJEwk;pm7X)QHLv8CdL7eb3msFCN%_`bUI&um*)D&v`8j{Zos8H)!25#+b1! zvF&f`gdqBILXUr~^*g?`Q-=(*wHtH0krfZS)_(V)?bp}ZagL`A4|;2-sP)Q~h_jdc zTH~&b^-aD@nYs3OQin83L$l*}p6i!_8Ut+DlhvQYc48y<1R7Nix0_}^_ze%4Qn8}FNsJfc4sz@SGK3?UIr+v}^%W97$Rhs^TL zzgo)o0L4;Z`{#gn;g$8?P=Y@-KnW1r}8 z#M@Y0tbqX(c(*nY-l&L853rl@48f4s%(s5a!?=fQan@&dOnJbYmOPdp$ZI3n$)kpp zVthv6!R7_nJ%!qs@4dEn-3)~Y=rj%B?(y#==g1tkLnmji+)%xfh_d zDtAEOU=9?2+TzNQ98YDMpJvvJQR{awBFFD#94*NU@Cmgx=xoTzB}NSQrs~vm9ILGh zTQ5rJk7(AEIegMy*6-wv*ErtmcpP;elg;Z^E5XCrpN0lIa{{OvI_}KJ;uoMVmm3hYMj6m~P`AZ(Z!!Jhx)5?0G*HBcQuJVwH$AHXeC3<=~$XjjcRTUVh%fxEE5+?uL zzI={Dj; zy8@<2B9IpofMBNXvX_JEIx+WQPp7eOEsEU$^S8$1SS}-U%znD|oXZ^;+9t=G9 z6Jy2MT!sWTlS_tY{TQ!&<8Qwl?{j3-unz_1CX@V9?A-@Q13SLFUkrs!O*xdTNem&b zEljr6L0*WCzO`RoQH(>^2wue8rd@mq&ibp%l=WDy5ZHaQey!VLt=-lDE?0wm_pg<0 zp3J-M;Bmb0%74I(fq9YBa66dPFnsuG8jQwHJYqM;>llCou`XVgv{L2eje$gJOG-MsJk z_z@=?94{B5t8?u~f9;R8H@fGl@&L#6A%s}0?_eWh9L2K@S=4{mWzRR8dk zP&mNz>%aZs4P(F6gTI*6Sqg~^J`FV6@xeO#7~HZSm0!k4sj& zHn#U`5Pa3+kmje)(>D*IZflG^x2^*IZOwym*^YQRImfgp^{FrUY`hw;t)vxAp1E%; zLeIIVuKb-@%as%5j{OE+8Wh7HxS0zK@w?@&sLac4YQMQ-%R2D2ewlBU0!D|#2XujI z6g08QWY1I0rw^^_hslKmetn2`wT(}IDe9;Fm@3hc&0wYP`7Dxye_@+QOqb))h=b2{ zKdo-%7m@gOKZW(~Z)PwQsLiP@93Yx*W#DQb`IBv=&bc74F}pV%s(}Nbzd*ak+W60= z_y%t}`yEHK{`U`2;JXXIM06ucMuOqV|R zq$Ymk%UKV2a~_QiUvE-j&BPFAEGGVPEmnez-1%&l+se!#cI9(2dh$J7#*PFxp9`mw zA`ITme(mU|JQY!k2`fT;_-fn!svmOvTgT)d`z^S#(OV02FPajLqkLh1a=Y76Bz)CR zyy%H*?0fbIXz_XZz*PC0a6sOF0x}m1K2eNtKO#9%NO z(n7?H_grinWc(zPaiVUX(<9qAO`KK_3hm4}i}1}!6u$TwBwoXx`DI^!wkx+U!Ar?V z+)^IMlNWLLdTQ2%9Ls6rYcqLoUGUrs(&}|Cr{Hc5@?PAn%{^@0N;2{ZQ(Dp(<5e8r zsTDiGnlNOnXgM5#HC|i#&2QW%IDx?7LS*ZXto71pFv@F}@vT73AEZ0B4*Ci0s9a)pgtHRJ1r?cbdY?j6ZhV zIHq}%mIqm%`)mKD$Jc$$AAEf5$9}YOHq_f%DG6rmj9*u!&?l?2HurhPujOX`Pk+mQ z|9H=Pe%hP9=G&SAun$KpL*^%5PV{i|2KQua$d=n7IF-3Zvwt{_g8y{;REe=AiIulb zSG5)o8*r`%8temnLT!JgGPAs|9lmmh3qh9GmFKS9c?i0JlBYLoacd2OJ9NOVS#g;6 zp!tVLlYFT9g5no#yK;YAdJyEg7@z)BRTmA?m zibszhlgS2du9YEnA3=)(q;1r$_Vl!?mN@rVUzr9-;U_kI263)?=hg$Be7X-{^xTN# zfo6SmJ{XJZxf713ja|_B0C7N$zw$gO-^4j}AToCJyl9Kj<2cr8%KiYP zrFBg@?)<`P9U4APcfU^M2S<5s&=@v$jK~IR|G(PgzrMl&#-Q!2pR&d}^@G=(>&`wA zyZ#&xj)SxOcAE24>q5<_54dYIftSM4Nq_KS(t2)KC$YNl`O%wk!>qYsPmr7e?QFI8qdl(DIP0| zXCi2iEq=VbuiW#-Bd59Jt=wEswRiWflld(!8OW@>Q!b-k3n*$D5`VL{E!AK8&ps{mn)?$u~N& z#;)Vxy1MBx=_-LG7^3kX9|;=@qhqhR+-tWqk9={6m9OTsRz$hlJ$IztS1#M-2=-h} zoH`zR)|EIKH3xJy{PZ2{u~in;RaeZ)cVl9+{IqjkShm^?RYHK^G@Ei^hGrbooi(C1 z;j(khfJnQ`xKU#nwmELFXl6jq@Md?}zYu0>TnwgO+UlQ;^+6KD=$l8mo*ED2FA!2! zY~hNHac;!L9E_@qf9DT1T_P+89Ad}Zt4fi_CS&bmL(Jj*%$Hep+HAoJ3k};<)0HIN z#6@y0OdXelTVrY=r(3!RjqSj3F-rjgw>JEhWGubSEkbfr#&~$q^KgZWMfcOxC+~yD zxtVis9y$JpTzRy<=vHB z5FSO31p$9>$#3~z!?i=DYhq}<_5TQ0^J8Sjn0~$9T-vdu5}G`Yp98r{H+K2w0q3@v9^u7 zTr?!|(D1*;W=`9fa-@#@r_==GZ<&=}u$KSiEP3MxgL72mPqC&BFht%+*^WPQWK5T{ z{@BLX`)&-x>6#V4ar`8h$4%{HdGa8~;u1y0ZpsCNY4R`zg3R*0$CH!EvGta*{5Dtq zHYbU6ACc~OWSm-l#I>TrQy)TD1Lg_I^$q}XK#*6)*+Zgly!P-*!_@xT&z|a6t>?eW z!EEcO4?u~-ry03kW1Ts6)Mrz!@iEqNU=56%ez`Lo{tVAU`9>noV)g+?$860`w6Tbc z2G%xWG>*6Z7OOgrRhF>&K|nxrS7{9A@WYmg7}#;_rgPXB>yDv_(fLD}aob- zU2r>&t-gqJ?JpP^9V@@#tuJ7CdEIl*T=}8rzz3fP2e(b2d|hkd*dO7sHcXjxe*NG= z2F($@?ArI_ggrtWRl6bcvAT=p)BSC1F^Zox6rY-0T(Dvh{^8__G8dUXWRW0 z|JDQr^Z-10PrS;}!Lw`K`^m*l&ydtc&s-cZMu-2YhvpMtC)^mb zQ;U!K)M#)HuD-GLySW_Q#vcFHh}gBib21MyBrY*x#hL-Jyfd^R!`^=ZGBL=-HI6^N zIM#m*^wd#I4Qi&wc!qvqFP}@cT*_>D7QCF1l}n{>zP%79zcE`}cAg8bxo9#w4?5O} zIvgJzKn{ak$5>NrbnNEXj%;m?NidMrDLwD&FA$84!{L$`_(k-jo7$acS!0hSc56>h z+uj;yUaUwb&-09pob&3wQa%1BHgOe9?lC$4p06jG-AN{-L_;8cFF_kLx(Nt0V=ogn z(8#1<>}(*3er+;${9^9TG8g@l1}iUN)`xlgWDD+*BVl>8HYOv~h|C4Q2ndU%Z92xv zZu80}ekRn58dMU)iH(FtkGuWoN&w+lMZO`{c5z}=-6$4!q^Qf^0g$m6u&D?JhX-b@ zX?GB7r7WBHsT+V@Yim0gSm49HK%<**&Ud3t>{hwB8}DVTV!*S&jS1RftVL{i6Ui60 zYmcQq*xwrQu(98IuO3s%1<$l&Ys~oN;9hxqwn3X3mqUEzqs7r-r+(LC3birhga!QW zynxFWIU>gHYLNBH_J|Ib$>Cs)+nL{TJ+eDK_AP_`;+{a`FP-7Iy5;DN{#$Yb5sFrQ zeE6?%dtTZ6^&O6j<6_{sTyFYP%Qap;nLCtImB(#>C=GG_N1OLrYuJ6Go{i0o)^fu#(cTOW_Xt0;7!aLvmJZDZuZUZvXahHV#38M?M`otk9^dwgL85s zM04%zW*6l`VdvX(xL$TV)WDuinm8SHJMv86e$CKQtv2(DZzY!<@?FP6BrfpZ_ex@8 zqc1!J&5ZL+Zk~VVm;U>Ycf8}1igN-sZsQG!=;VhDFysO@fa7T$y)eNsH-S55_*VWyfMJp=!yM$h27ZzXW%%=<%-*CTz>kMiaN@mkZ%WA( z>NLsEMZ__&X3+#2el~B&FFm za`ggl&sjGN<`B2OgGYDe$9^#N*N+)_HA%VU0Wqaqo)~B~GPqq2*{ZVyC~gg0PqcSg z$ecXkIYln*IX;znb#}OMwYD+9bv?6=`wvyjn{ce(Jm7Ae%?lEE@~Z4C@J)n|Q;+$v z!=7Q<{=LCF?`mQ&`wnv>`H+cyD|2u;Ck~D>ZoEgg7^H4Hb!ok@SC`G20LB=`J`|3P zpYV!t-6KH1F=C9pwTjRhVwl(Kb6i7wKM_hTcmL8eJ|MC+ZYGTrVH0WnGo|&|b5uKI zU;rYLC^O31bF&I|xX|-WAb9Y8_tyN!bdDL<01$NXkt5UdP`r+=(SY>*Y9K?}^$^1V z5R#XS?i`K5W3TZ|P&w+6nAENbMFTE4Soa)Iv&QQ<`rCWs6|`D>%bR$`F>>8%a79tXJ#w2ZhabmYcfUNccvZKfV2y~lgX;peSPMmT9ry7|==R+oA zjbZSHu#`JZ6#OKj1fztbCaTgmkJHDZdYo!Qe;}geJju8Ws~`x$NIO=3SollfWrljU^ieN_On!)EIt4NN6Imf zuk~?k;E4hz=P`4N3?O*rYvW;g{LVPC*e)(MG=1cEC!=EY_$1zPI{o+(JiumZ4!Rjj zA9nP^53JFrBL&y6qH=pNc0CC}gu&X}7K`4z#y6$_<5a&)*5~A_x^;e%QKU4n9Rsdt zA;kiFo3%$8dw9rslw50yl{qjyqQYz9U}g=Xyvu~o24BV-1A7Gf;EXA_#?FX5->iuN zH_i(c&9#CX#LxZPU_`VZrn~ZS&u`9v>D&E)vG4N%vtH=a zfIiL1ND}-!0NjM{XoaP-|>#G8bc zhd!8x+8m=-p&~XmOspz4!-@`{e37ryu~|8=_f{L;&8c;FFvr>83bYmSjf_#-psQQH zTVpJ+Tx)UtX)#`WZSDd0TzD+YfqEE5c3SVr3Q}6Lsd{)LuiqX7uGxupsvC&L<3JjS zvTaZ!@5VfDa!VceBCOTb54+$t%k8mH7f0&^MsV+XZ0>V&Mrd9y@F0LcYbIu_$OnQq zzME@nvy~V0#3CkbZN;cb-xp1~0xStlTf6NmC48*bXm}#J)(~g>y~avhZq)LX`L^c5 zJNx7`I48$3+`Qxvo1Lq^;Suj}dO1clao{LM$ea`?@H9Ejw02cI)d2m(s5}C>40fg) z9_SS4~B;XsL7+|yY zz{jRM;~3Sf`+&@__Y3A~#la7`GR4`HKrypjrrzX(%@uc=55Bl|n0*ay))PdIBdEl# z{66ydCI8qjIljt=_R@)A+x8gI#F@$HmM<4z-~Ts%;PLJ6{5E6ePTz~dO@SKmIuGvx z!<=R<-9+XMUTx7fXP9Sn^8x3TSGr%iPw>8mFW=%WX+yX<@7Jqp0Bc1Araq*jhii_J z%F!UKytIiO{m4|tq;;a9aFR6Hc_bo`--VVw+}=u>xs|-YvX?4 z`7)mfof8q5yn53*PU%*zj(J|NmBAAaSzL}uWseOslf=aYNGI+903ZNKL_t)2-Vec9 z?C#IOA1?4*8KJgHq}_4(lw*K)zY-K3gSM>&%$K&>U4+D<)~x>)eZW`j z=E<=EHbhyoD~`Uz<(SUI`gN&6RT8cIJI8}5I^d?>J@_hfyZX&e;mIK^@eVQvZTamD zDH?Q|pq*GGS=}W=<#`|qBFbjo69?twiyddmbG@TI^{6Gj<{ClDl{&JY#KYV)Dwib@ zJcHs+iwXYLBp9tX^7@X6hDz9oQN6z5?t2WzY1h6K8{($hdhBtpP+L5JBq!q6mJoOO z0=d>|CO!@mgE)JvE`RLBj!!w1UA+YwG$aKw23#Z#W?u5cWIWe=cv=%Y;^)n;zN;)W zRB2$5KVPz0EVa?NknkSGSz>L{=l5R+qlI2qQYjNdoe<*!qnN zAEGD*mBZs$raF*}#gvZM6_{zo~n|pp82f?6d%Z;AvROR}{ zU^wBkyv+$?+LFs~^@x|BGJMSdO=HRLChNRBcUi}#=3GikO(-!B{<1UX$`Ni#^|!8J*+B#EdkC-?6*R!D!nam#en! zGq4?>hNi0*#G^hV6!Xsh2hDhefLRU5i>Ko+I&a14eg$ycivxC8HN`aMu#Q9osuS+z4?HZ+wVteUJIpxU8xx zHYUq;%#Dxi!mbsH!!4pqKl=iN?sBawVSB~d*zB8aU@hLAyUE7r@)fAo$=MP?>ZyTR zWc-eAjEzFA7PDq!Gn05*&P?~96CNMevcXn&N>6~=VkBXEtx8H>RLN30``Vsr~?TG2o8)(l^6?APIn z{w(K)BX0<#3&vT4IGp`slU1j2TxwWqjHz=^t-DO*!-b?= zYHq{>H?={f6`PKxX3JNFZkL_Y!?74)e0X2{k!=l;F83l_`$w^}4cO4V1}55y;E zLp;IB_N-MznKxb|>BKNTMH>6e@ncQBah{vSK7c~ks_4&TVtD=?Ut(?S@&Ye1Xt)M9 zeH7lH%CCHm4^y17S((@oksvDCXgk5c)>>}7avIgKDG&U)+rjgAFZfNi-pBA}v${#I z)98Qh=YIb2d%oiL&fiMoKZ@oU@;*9I$tyOkZNa#KRd%wwaoivnP_G2@*G46sRm_}M zam`AUS6|J~8BGp-KzKErh_1gFH~Hul!;`P{*vb#tyx32!{o|4wYa*PR^8gl_?GVwh z>*ao=&sZe}H_xpL*nOA-3D@@E!Ts_PxzmXws2g)Q1&C$h`<33DqGO*lFLPr}8kgSj zasrXK#tuK$&nS$GQ_RhKAIMyF%E%l`KTP1kaTfuy=2H7b0n>K!XFfJKkP&(L+E481 z5zN|RGhUZ3T$1Ai)z*h96L0GyFL)NxT*W^-s^k z3xf?lwaI)R=DA;}zp&z9s9|dcr<){pYgaF=8F$@0vuaoZXn*z zdGO5peEg>V)L=Q`%f3bLcw%5MkuJZ%P=lYgm#+yC8cu9(?b<&s+%+k$TRp~gcNchJ zd#Z7RZr%vCni;uYu_pD?ITp#t(4;Yphyeloylfm1nGJCOfx}nRfIe zu!i_fvu4Z}Z}ZS$kFkTM*ZSQ#gu^$6U9l7%asvtIW^aEt-cz04lg2KoNL=SBc+R*xYRCwYmXh=KwaL8jZB|t>l3er z-z0E9+8+-vAevIiVsJimVYrWt4p`ovm#TW;7+ReT=~4<0{`g!=`}rh zVLx#hZ#z8Wz*t>2ev&Ol7mw(vm%vd>3dLeuLFbG9?Ei#?2NxjY@RUo0Tf_AMvHUh- z`dK3)ZyxoG0ItZj!Pe=qGce(`yB!}jt6y~b-+I-7liv-yI2VudX;6PcpNq)NRSZ<7 zP#czJG32VgI_#In#zod&yIgr=rQK_>c~^sOjB2@Jv9sa(P2*tbc@RB0_=Pm>1__OO zJq=gT=h(<}y>ZvKaIU{Us_FXlr#1w_@!prua?lUpG-(aDt+V+@Cdc8>9|?_NG+U$f zA>=VVX3JyZPek(Hf`HSXoUap_jPrfsj|$sUSBfUkD8s9of_H<9({Vw!~GSb(Zv8i*E55zHTgCLtgNpXPjH>ECHEMXQ?Ee~$_aIF z?+;zpww#AM5WGhSHq7A_2?Wv5SKak#TGdGbbq1KZZkg0S^BFWh^u!gEP1}Mni|#yJ zYIL0RW4|FPUpvmw8gdg&40ZIzy?7ZkytY;lzJ~=zA8XM(m1Wa4ntg6?eNZ&N_Dym4 zTYK2;t2vU;I+*la>kLmtFve;LQ-jFhlUE=SXv1h*eKNy#;;tX|jC)9BP1*NSJ8=L_ ztm!oMsGou%Cl`NpvB8(w#Ue&|%=&oi>w}2~*l9g>nS^}5!(np4kQba|(J9KF6S^-J zXu^*TGU8xcM`I>Q&!1vyf*}=%ZAoDc7QU_}0H_uxLj+pSh74i29DGQVU+>;yN0-?s z69RsKvai$^3?R>b$5@HMUAvgb#~2dLJT^Vwm8RPOJ=Zpl$x+Jj*!9L6AsV2Ui#)2h z@j*KK<8YO6@(`DL4+=<)7hU4EmIl)HEgn0ySsnWw`;+`z@uzfZYq6rGrf)tZ&N zrMcx;QV_XXYumYF;#e9pC@vqWm{?j$yZYryz25o?58=t}DeHBy*Iul}q{lu8=GZl1 zSgCFWbpIf?`nXV0FSqlFO?>4D#F7c_EeD3pVDLfBPSE<*O^g>l{;GZ!U}L=jWq2!> zhsjC9fY1r2-t(4cf=-*IWhZm&9S`Ki3LnBFEqiS8(iaPeVt|W%o($0_ptJ9ju zFmrHrtxL}p#0rI-waj?ed@=4U!IO7{cR2*`BXH%jr2}X>rBzKWYOs}*BM4)IU%3M_ zF%YI5z8NjAz0Z*2*c{s-a$g=q_ZpuVF=|HGvPDD4Z#tI-fleT%@^AZc? ztzOy#HODS<>j_}mg+{mr)FwWKiVVU`Mt`>>*1^L)c3m$AIMpBSTcFtOr#Nv~|GVD2 z+`opCMcc?=@*Q9Ma^GBr2GlS!uA!N%Qi%Bb70>0S$t`B)Pi^z4iK@G=wIT?0iFV^3 zjLBo~iILa8YilMiVu10oE%xSUAi~D*4fYsKQ$HNwN(K17e5Q&Kup81&e#5J9F)(=7 zZ_i_7y;$^}708dNbmtJ2xLy1ZIpfGqTck|@;`zpQRgxHoh8ZQcwCPF07>WSt4ry|0 zJ8=$<0QXHbyBn_E8@OytTp3%a;!s{L(3Z~~UAN^R*^NNX8IKOkbCEZ(`iY;I%&&w! z<6gN7J4(3FHp=Fu@FWE$u}P&kU4vMM&yk_mksiB69pS{O{pcp2<3}#O5)SNxcVS$x z#2(NQuFr`qUzik2Ud9~kV_5uShu_#?V*WP@49)MP(SiQ*C(@1muTMvcYNcI>wvaI_X9-X4!ujLfM8(QO)Pt_SS zJ+>Wy)z+HVZ*?(G;I+61*?f>`vjh<|ta9^0mew(z&8KTz-KA$lL~8L88{h%Y==l=> z7^B561HTpMvNGI_8x_qwhC4Q$qbY_X^kd(1@JvvqJ6{zh;QIbqLL9Tp-y*tAsCKiMz@0dWY0Xelt9%JBES9+PX-)p2$?#K0-qkgaBFfL; z8lCkTErIc}D?0Pyw66)e)t^2CXqLzJ5t!@7?^GN3`g$Rw?jy&m`ytWO2A^R|m_1~7 zfW?bZ*SGtY`x~-+G3G_Rtc&|wK!e6j^z!O{nQ+l#_u6auFT&aqIgPbQJRY1EGrrjJ z4>IV7|Hi-;@|&0JBa@r9Bqnkbo0=^S?(6G5t!<338y%C@WN;#9zEWJczTA3IHhC3% zoZ4+X!0#N9$LZMCe|-qpx~#u_Fixz*WV{@z$Xr+%x_06Miy}=N>wnd-wYg%}@t(14t}L) zjdwr7x0yiJA=C0!fNbJLfyt>~#0f=d2L=Op78x7Km2Yn#f!Ubdn^)=co%<^HCVH9(y0FU%?N+=NW@q(`n;OQ7~eYM z>a~T4aT*v;e4?gy8;9}M&mOmlvuhzxHu}_iMK?Z^!T94V|FL`n@6tOsflV7>#`u?G z`8i+Sa;Q!Hj;DA-!T9DU?(8#Bo|;e?qRMsf0Ca(h8&L*p5pg^1 z=0}Dki{0`)G;j#!%H^;!=yxe_HLejqJwo z#&@+BH*2+g;@6n;jzrgs`hXj5H1yB$>x+JMy~x)5`0fEVAVyhSeTg$M#FtwTb8)o3 z%_3XuZY0PV8@_0cZ}T$5rA}*!-O6-i6O+ZF2Xb*;AMzCYc$CZ9O)sb6Lte%qJidcF z>dq%N@uv(1(DtSaeA^7*wzbE%Svt@TtP^j5vG-=dSmYDa1s<7eu`-c%L%iX*90ch4 zuhkvzjE^#diK8euQj4rPq>rq1-ymAneCv0N@y8jU%kDnz3XF11*OuP#3cv%Wu^D$X zTf!j3M!Q>&TN*WAO1nm*5X_Qkhi&nWeZy$Jo92hZN*LxV&fH9HTddA!ZC4q4a9Y{$R8&?tg;pO{c)2rKkdy|v*`+oi(KHmA4zU%P?U-(6j z_q^vl)`DMgH1^=_0|5FRThGaX${;_rm>A>-$ARe@$c1$&DQt+;*?x(ClFyP|r)UJGZXwXRHJ)hv> zp$|ZE04iT%0NOD0avXk2vG+AL>6{aT^&b7{7i#^86FYopO^^8)rpwHUxXx;S<{Jk) z9qR#CnsynVUSVPxY~|gbKkP$19^_pQOIypic!~vpGWdAQ`@3M1ivt(zjz5iW>1sH*akT}@_M2D6y z2}Bq7klcWV0U#4fIjj#D3d-zY>@Z)}h-M#6L+cin;N7P^ zBZB#A1XrFwEht>igz6B2N;B9DJa>aHB8nepXeB)UD5NlU2d2_4Y z15|_fn9el-EnKAHtQ@jb}uJ@Ue4*@}wr*mu_ z&3)%%{_don395D=7;4;pDd&mSd#=T(n0k#K`_P^!_!+VfW^H?3zrY-{!###RZ&ULk zim~3{tcT-Q!N3!A^K^b_1`m%4T^}z6-h?yz-V(>b#XBNb8_C_Nu~%!!$aFN1&dpA| zUjS-Ig1(@`zmQAfZ0!&sXhbnrG=A>K-uQDI`&no5W`e`@>0ln+x^PHHCH5^I)X<+P$sgCA4bNf=;lTyQSFZR@2_&FYZ zBne5i9QSuMujguQjMoG4x!sYut+WEoyAO-`SgTR!sJaBCg{_zRS{OW3*u$oU@SI!2G_X8v@WEe(>~Yjx=BiQ zeB{kt{LxY|)@pp$9Esjs1g>u8;Eh?m6YxkDkDI!e|MjEAk@I`Kn{$&5rttt9B1W

    2. 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 z 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